diff --git a/.travis.yml b/.travis.yml
index 6b2dacf..c121dcd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
language: go
go:
-- 1.7
- 1.8
+- 1.9
- tip
install:
- go get -u -v github.com/fardog/secureoperator
@@ -24,4 +24,4 @@ deploy:
on:
repo: fardog/secureoperator
tags: true
- go: 1.8
+ go: 1.9
diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
deleted file mode 100644
index 9ee5385..0000000
--- a/Godeps/Godeps.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "ImportPath": "github.com/fardog/secureoperator",
- "GoVersion": "go1.7",
- "GodepVersion": "v77",
- "Deps": [
- {
- "ImportPath": "github.com/Sirupsen/logrus",
- "Comment": "v0.11.0-35-g61e43dc",
- "Rev": "61e43dc76f7ee59a82bdf3d71033dc12bea4c77d"
- },
- {
- "ImportPath": "github.com/miekg/dns",
- "Rev": "ca336a1f95a6b89be9c250df26c7a41742eb4a6f"
- },
- {
- "ImportPath": "golang.org/x/sys/unix",
- "Rev": "d75a52659825e75fff6158388dddc6a5b04f9ba5"
- }
- ]
-}
diff --git a/Godeps/Readme b/Godeps/Readme
deleted file mode 100644
index 4cdaa53..0000000
--- a/Godeps/Readme
+++ /dev/null
@@ -1,5 +0,0 @@
-This directory tree is generated automatically by godep.
-
-Please do not edit.
-
-See https://github.com/tools/godep for more information.
diff --git a/lock.json b/lock.json
new file mode 100644
index 0000000..7cd6bbe
--- /dev/null
+++ b/lock.json
@@ -0,0 +1,38 @@
+{
+ "memo": "",
+ "projects": [
+ {
+ "name": "github.com/Sirupsen/logrus",
+ "branch": "master",
+ "revision": "95cd2b9c79aa5e72ab0bc69b7ccc2be15bf850f6",
+ "packages": [
+ "."
+ ]
+ },
+ {
+ "name": "github.com/miekg/dns",
+ "branch": "master",
+ "revision": "7994cb36eaf858ccb42fe26da404c345eca1d62d",
+ "packages": [
+ "."
+ ]
+ },
+ {
+ "name": "golang.org/x/crypto",
+ "branch": "master",
+ "revision": "9f005a07e0d31d45e6656d241bb5c0f2efd4bc94",
+ "packages": [
+ "ssh/terminal"
+ ]
+ },
+ {
+ "name": "golang.org/x/sys",
+ "branch": "master",
+ "revision": "0dd5e194bbf5eb84a39666eb4c98a4d007e4203a",
+ "packages": [
+ "unix",
+ "windows"
+ ]
+ }
+ ]
+}
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..48dd339
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,10 @@
+{
+ "dependencies": {
+ "github.com/Sirupsen/logrus": {
+ "branch": "master"
+ },
+ "github.com/miekg/dns": {
+ "branch": "master"
+ }
+ }
+}
diff --git a/vendor/github.com/Sirupsen/logrus/.travis.yml b/vendor/github.com/Sirupsen/logrus/.travis.yml
index 804c569..a23296a 100644
--- a/vendor/github.com/Sirupsen/logrus/.travis.yml
+++ b/vendor/github.com/Sirupsen/logrus/.travis.yml
@@ -1,8 +1,15 @@
language: go
go:
- - 1.6
- - 1.7
+ - 1.6.x
+ - 1.7.x
+ - 1.8.x
- tip
+env:
+ - GOMAXPROCS=4 GORACE=halt_on_error=1
install:
- - go get -t ./...
-script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./...
+ - go get github.com/stretchr/testify/assert
+ - go get gopkg.in/gemnasium/logrus-airbrake-hook.v2
+ - go get golang.org/x/sys/unix
+ - go get golang.org/x/sys/windows
+script:
+ - go test -race -v ./...
diff --git a/vendor/github.com/Sirupsen/logrus/CHANGELOG.md b/vendor/github.com/Sirupsen/logrus/CHANGELOG.md
index f2c2bc2..8236d8b 100644
--- a/vendor/github.com/Sirupsen/logrus/CHANGELOG.md
+++ b/vendor/github.com/Sirupsen/logrus/CHANGELOG.md
@@ -1,3 +1,50 @@
+# 1.0.3
+
+* Replace example files with testable examples
+
+# 1.0.2
+
+* bug: quote non-string values in text formatter (#583)
+* Make (*Logger) SetLevel a public method
+
+# 1.0.1
+
+* bug: fix escaping in text formatter (#575)
+
+# 1.0.0
+
+* Officially changed name to lower-case
+* bug: colors on Windows 10 (#541)
+* bug: fix race in accessing level (#512)
+
+# 0.11.5
+
+* feature: add writer and writerlevel to entry (#372)
+
+# 0.11.4
+
+* bug: fix undefined variable on solaris (#493)
+
+# 0.11.3
+
+* formatter: configure quoting of empty values (#484)
+* formatter: configure quoting character (default is `"`) (#484)
+* bug: fix not importing io correctly in non-linux environments (#481)
+
+# 0.11.2
+
+* bug: fix windows terminal detection (#476)
+
+# 0.11.1
+
+* bug: fix tty detection with custom out (#471)
+
+# 0.11.0
+
+* performance: Use bufferpool to allocate (#370)
+* terminal: terminal detection for app-engine (#343)
+* feature: exit handler (#375)
+
# 0.10.0
* feature: Add a test hook (#180)
diff --git a/vendor/github.com/Sirupsen/logrus/README.md b/vendor/github.com/Sirupsen/logrus/README.md
index 206c746..08584b5 100644
--- a/vendor/github.com/Sirupsen/logrus/README.md
+++ b/vendor/github.com/Sirupsen/logrus/README.md
@@ -1,17 +1,24 @@
-# Logrus [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/Sirupsen/logrus?status.svg)](https://godoc.org/github.com/Sirupsen/logrus)
-
-**Seeing weird case-sensitive problems?** See [this
-issue](https://github.com/sirupsen/logrus/issues/451#issuecomment-264332021).
-This change has been reverted. I apologize for causing this. I greatly
-underestimated the impact this would have. Logrus strives for stability and
-backwards compatibility and failed to provide that.
+# Logrus [![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/sirupsen/logrus?status.svg)](https://godoc.org/github.com/sirupsen/logrus)
Logrus is a structured logger for Go (golang), completely API compatible with
-the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
-yet stable (pre 1.0). Logrus itself is completely stable and has been used in
-many large deployments. The core API is unlikely to change much but please
-version control your Logrus to make sure you aren't fetching latest `master` on
-every build.**
+the standard library logger.
+
+**Seeing weird case-sensitive problems?** It's in the past been possible to
+import Logrus as both upper- and lower-case. Due to the Go package environment,
+this caused issues in the community and we needed a standard. Some environments
+experienced problems with the upper-case variant, so the lower-case was decided.
+Everything using `logrus` will need to use the lower-case:
+`github.com/sirupsen/logrus`. Any package that isn't, should be changed.
+
+To fix Glide, see [these
+comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437).
+For an in-depth explanation of the casing issue, see [this
+comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276).
+
+**Are you interested in assisting in maintaining Logrus?** Currently I have a
+lot of obligations, and I am unable to provide Logrus with the maintainership it
+needs. If you'd like to help, please reach out to me at `simon at author's
+username dot com`.
Nicely color-coded in development (when a TTY is attached, otherwise just
plain text):
@@ -52,6 +59,12 @@ time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x20822
exit status 1
```
+#### Case-sensitivity
+
+The organization's name was changed to lower-case--and this will not be changed
+back. If you are getting import conflicts due to case sensitivity, please use
+the lower-case import: `github.com/sirupsen/logrus`.
+
#### Example
The simplest way to use Logrus is simply the package-level exported logger:
@@ -60,7 +73,7 @@ The simplest way to use Logrus is simply the package-level exported logger:
package main
import (
- log "github.com/Sirupsen/logrus"
+ log "github.com/sirupsen/logrus"
)
func main() {
@@ -71,7 +84,7 @@ func main() {
```
Note that it's completely api-compatible with the stdlib logger, so you can
-replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"`
+replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"`
and you'll now have the flexibility of Logrus. You can customize it all you
want:
@@ -80,14 +93,15 @@ package main
import (
"os"
- log "github.com/Sirupsen/logrus"
+ log "github.com/sirupsen/logrus"
)
func init() {
// Log as JSON instead of the default ASCII formatter.
log.SetFormatter(&log.JSONFormatter{})
- // Output to stdout instead of the default stderr, could also be a file.
+ // Output to stdout instead of the default stderr
+ // Can be any io.Writer, see below for File example
log.SetOutput(os.Stdout)
// Only log the warning severity or above.
@@ -129,7 +143,8 @@ application, you can also create an instance of the `logrus` Logger:
package main
import (
- "github.com/Sirupsen/logrus"
+ "os"
+ "github.com/sirupsen/logrus"
)
// Create a new instance of the logger. You can have any number of instances.
@@ -138,7 +153,15 @@ var log = logrus.New()
func main() {
// The API for setting attributes is a little different than the package level
// exported logger. See Godoc.
- log.Out = os.Stderr
+ log.Out = os.Stdout
+
+ // You could set this to any `io.Writer` such as a file
+ // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
+ // if err == nil {
+ // log.Out = file
+ // } else {
+ // log.Info("Failed to log to file, using default stderr")
+ // }
log.WithFields(logrus.Fields{
"animal": "walrus",
@@ -149,7 +172,7 @@ func main() {
#### Fields
-Logrus encourages careful, structured logging though logging fields instead of
+Logrus encourages careful, structured logging through logging fields instead of
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
to send event %s to topic %s with key %d")`, you should log the much more
discoverable:
@@ -171,6 +194,20 @@ In general, with Logrus using any of the `printf`-family functions should be
seen as a hint you should add a field, however, you can still use the
`printf`-family functions with Logrus.
+#### Default Fields
+
+Often it's helpful to have fields _always_ attached to log statements in an
+application or parts of one. For example, you may want to always log the
+`request_id` and `user_ip` in the context of a request. Instead of writing
+`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on
+every line, you can create a `logrus.Entry` to pass around instead:
+
+```go
+requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
+requestLogger.Info("something happened on that request") # will log request_id and user_ip
+requestLogger.Warn("something not great happened")
+```
+
#### Hooks
You can add hooks for logging levels. For example to send errors to an exception
@@ -182,9 +219,9 @@ Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
```go
import (
- log "github.com/Sirupsen/logrus"
+ log "github.com/sirupsen/logrus"
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
- logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
+ logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
"log/syslog"
)
@@ -206,42 +243,56 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v
| Hook | Description |
| ----- | ----------- |
-| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
| [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
-| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
-| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
+| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
+| [Amazon Kinesis](https://github.com/evalphobia/logrus_kinesis) | Hook for logging to [Amazon Kinesis](https://aws.amazon.com/kinesis/) |
+| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
+| [AzureTableHook](https://github.com/kpfaulkner/azuretablehook/) | Hook for logging to Azure Table Storage|
| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
-| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
+| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
+| [Discordrus](https://github.com/kz/discordrus) | Hook for logging to [Discord](https://discordapp.com/) |
+| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
+| [Firehose](https://github.com/beaubrewer/logrus_firehose) | Hook for logging to [Amazon Firehose](https://aws.amazon.com/kinesis/firehose/)
+| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
+| [Go-Slack](https://github.com/multiplay/go-slack) | Hook for logging to [Slack](https://slack.com) |
+| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) |
| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
-| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
-| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
+| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
+| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
+| [Influxus](http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB](http://influxdata.com/) |
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
-| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) |
-| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
+| [KafkaLogrus](https://github.com/tracer0tong/kafkalogrus) | Hook for logging to Kafka |
| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
-| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
+| [Logbeat](https://github.com/macandmia/logbeat) | Hook for logging to [Opbeat](https://opbeat.com/) |
+| [Logentries](https://github.com/jcftang/logentriesrus) | Hook for logging to [Logentries](https://logentries.com/) |
+| [Logentrus](https://github.com/puddingfactory/logentrus) | Hook for logging to [Logentries](https://logentries.com/) |
+| [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) |
+| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
+| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) |
| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail |
-| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
-| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
+| [Mattermost](https://github.com/shuLhan/mattermost-integration/tree/master/hooks/logrus) | Hook for logging to [Mattermost](https://mattermost.com/) |
| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb |
-| [Influxus] (http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB] (http://influxdata.com/) |
-| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
+| [NATS-Hook](https://github.com/rybit/nats_logrus_hook) | Hook for logging to [NATS](https://nats.io) |
| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit |
-| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
+| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
+| [PostgreSQL](https://github.com/gemnasium/logrus-postgresql-hook) | Send logs to [PostgreSQL](http://postgresql.org) |
+| [Promrus](https://github.com/weaveworks/promrus) | Expose number of log messages as [Prometheus](https://prometheus.io/) metrics |
+| [Pushover](https://github.com/toorop/logrus_pushover) | Send error via [Pushover](https://pushover.net) |
+| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) |
-| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
-| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka |
-| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) |
-| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
-| [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)|
+| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
| [Scribe](https://github.com/sagar8192/logrus-scribe-hook) | Hook for logging to [Scribe](https://github.com/facebookarchive/scribe)|
-| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) |
+| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
+| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
+| [Stackdriver](https://github.com/knq/sdhook) | Hook for logging to [Google Stackdriver](https://cloud.google.com/logging/) |
+| [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)|
+| [Syslog](https://github.com/sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
+| [Syslog TLS](https://github.com/shinji62/logrus-syslog-ng) | Send errors to remote syslog server with TLS support. |
+| [Telegram](https://github.com/rossmcdonald/telegram_hook) | Hook for logging errors to [Telegram](https://telegram.org/) |
+| [TraceView](https://github.com/evalphobia/logrus_appneta) | Hook for logging to [AppNeta TraceView](https://www.appneta.com/products/traceview/) |
+| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) |
| [logz.io](https://github.com/ripcurld00d/logrus-logzio-hook) | Hook for logging to [logz.io](https://logz.io), a Log as a Service using Logstash |
-| [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) |
-| [Pushover](https://github.com/toorop/logrus_pushover) | Send error via [Pushover](https://pushover.net) |
-| [PostgreSQL](https://github.com/gemnasium/logrus-postgresql-hook) | Send logs to [PostgreSQL](http://postgresql.org) |
-| [Logentrus](https://github.com/puddingfactory/logentrus) | Hook for logging to [Logentries](https://logentries.com/) |
-
+| [SQS-Hook](https://github.com/tsarpaul/logrus_sqs) | Hook for logging to [Amazon Simple Queue Service (SQS)](https://aws.amazon.com/sqs/) |
#### Level logging
@@ -290,7 +341,7 @@ could do:
```go
import (
- log "github.com/Sirupsen/logrus"
+ log "github.com/sirupsen/logrus"
)
init() {
@@ -317,11 +368,15 @@ The built-in logging formatters are:
without colors.
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
field to `true`. To force no colored output even if there is a TTY set the
- `DisableColors` field to `true`
+ `DisableColors` field to `true`. For Windows, see
+ [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
+ * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
* `logrus.JSONFormatter`. Logs fields as JSON.
+ * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
Third party logging formatters:
+* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
@@ -367,6 +422,18 @@ srv := http.Server{
Each line written to that writer will be printed the usual way, using formatters
and hooks. The level for those entries is `info`.
+This means that we can override the standard library logger easily:
+
+```go
+logger := logrus.New()
+logger.Formatter = &logrus.JSONFormatter{}
+
+// Use logrus for standard log output
+// Note that `log` here references stdlib's log
+// Not logrus imported under the name `log`.
+log.SetOutput(logger.Writer())
+```
+
#### Rotation
Log rotation is not provided with Logrus. Log rotation should be done by an
@@ -378,7 +445,7 @@ entries. It should not be a feature of the application-level logger.
| Tool | Description |
| ---- | ----------- |
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
-|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper arround Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
+|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
#### Testing
@@ -388,15 +455,24 @@ Logrus has a built in facility for asserting the presence of log messages. This
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
```go
-logger, hook := NewNullLogger()
-logger.Error("Hello error")
+import(
+ "github.com/sirupsen/logrus"
+ "github.com/sirupsen/logrus/hooks/test"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
-assert.Equal(1, len(hook.Entries))
-assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
-assert.Equal("Hello error", hook.LastEntry().Message)
+func TestSomething(t*testing.T){
+ logger, hook := test.NewNullLogger()
+ logger.Error("Helloerror")
-hook.Reset()
-assert.Nil(hook.LastEntry())
+ assert.Equal(t, 1, len(hook.Entries))
+ assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level)
+ assert.Equal(t, "Helloerror", hook.LastEntry().Message)
+
+ hook.Reset()
+ assert.Nil(t, hook.LastEntry())
+}
```
#### Fatal handlers
diff --git a/vendor/github.com/Sirupsen/logrus/alt_exit.go b/vendor/github.com/Sirupsen/logrus/alt_exit.go
index b4c9e84..8af9063 100644
--- a/vendor/github.com/Sirupsen/logrus/alt_exit.go
+++ b/vendor/github.com/Sirupsen/logrus/alt_exit.go
@@ -1,7 +1,7 @@
package logrus
// The following code was sourced and modified from the
-// https://bitbucket.org/tebeka/atexit package governed by the following license:
+// https://github.com/tebeka/atexit package governed by the following license:
//
// Copyright (c) 2012 Miki Tebeka .
//
diff --git a/vendor/github.com/Sirupsen/logrus/alt_exit_test.go b/vendor/github.com/Sirupsen/logrus/alt_exit_test.go
new file mode 100644
index 0000000..a08b1a8
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/alt_exit_test.go
@@ -0,0 +1,83 @@
+package logrus
+
+import (
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "testing"
+ "time"
+)
+
+func TestRegister(t *testing.T) {
+ current := len(handlers)
+ RegisterExitHandler(func() {})
+ if len(handlers) != current+1 {
+ t.Fatalf("expected %d handlers, got %d", current+1, len(handlers))
+ }
+}
+
+func TestHandler(t *testing.T) {
+ tempDir, err := ioutil.TempDir("", "test_handler")
+ if err != nil {
+ log.Fatalf("can't create temp dir. %q", err)
+ }
+ defer os.RemoveAll(tempDir)
+
+ gofile := filepath.Join(tempDir, "gofile.go")
+ if err := ioutil.WriteFile(gofile, testprog, 0666); err != nil {
+ t.Fatalf("can't create go file. %q", err)
+ }
+
+ outfile := filepath.Join(tempDir, "outfile.out")
+ arg := time.Now().UTC().String()
+ err = exec.Command("go", "run", gofile, outfile, arg).Run()
+ if err == nil {
+ t.Fatalf("completed normally, should have failed")
+ }
+
+ data, err := ioutil.ReadFile(outfile)
+ if err != nil {
+ t.Fatalf("can't read output file %s. %q", outfile, err)
+ }
+
+ if string(data) != arg {
+ t.Fatalf("bad data. Expected %q, got %q", data, arg)
+ }
+}
+
+var testprog = []byte(`
+// Test program for atexit, gets output file and data as arguments and writes
+// data to output file in atexit handler.
+package main
+
+import (
+ "github.com/sirupsen/logrus"
+ "flag"
+ "fmt"
+ "io/ioutil"
+)
+
+var outfile = ""
+var data = ""
+
+func handler() {
+ ioutil.WriteFile(outfile, []byte(data), 0666)
+}
+
+func badHandler() {
+ n := 0
+ fmt.Println(1/n)
+}
+
+func main() {
+ flag.Parse()
+ outfile = flag.Arg(0)
+ data = flag.Arg(1)
+
+ logrus.RegisterExitHandler(handler)
+ logrus.RegisterExitHandler(badHandler)
+ logrus.Fatal("Bye bye")
+}
+`)
diff --git a/vendor/github.com/Sirupsen/logrus/appveyor.yml b/vendor/github.com/Sirupsen/logrus/appveyor.yml
new file mode 100644
index 0000000..b4ffca2
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/appveyor.yml
@@ -0,0 +1,14 @@
+version: "{build}"
+platform: x64
+clone_folder: c:\gopath\src\github.com\sirupsen\logrus
+environment:
+ GOPATH: c:\gopath
+branches:
+ only:
+ - master
+install:
+ - set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
+ - go version
+build_script:
+ - go get -t
+ - go test
diff --git a/vendor/github.com/Sirupsen/logrus/doc.go b/vendor/github.com/Sirupsen/logrus/doc.go
index dddd5f8..da67aba 100644
--- a/vendor/github.com/Sirupsen/logrus/doc.go
+++ b/vendor/github.com/Sirupsen/logrus/doc.go
@@ -7,7 +7,7 @@ The simplest way to use Logrus is simply the package-level exported logger:
package main
import (
- log "github.com/Sirupsen/logrus"
+ log "github.com/sirupsen/logrus"
)
func main() {
@@ -21,6 +21,6 @@ The simplest way to use Logrus is simply the package-level exported logger:
Output:
time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
-For a full guide visit https://github.com/Sirupsen/logrus
+For a full guide visit https://github.com/sirupsen/logrus
*/
package logrus
diff --git a/vendor/github.com/Sirupsen/logrus/entry.go b/vendor/github.com/Sirupsen/logrus/entry.go
index 4edbe7a..1fad45e 100644
--- a/vendor/github.com/Sirupsen/logrus/entry.go
+++ b/vendor/github.com/Sirupsen/logrus/entry.go
@@ -35,6 +35,7 @@ type Entry struct {
Time time.Time
// Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
+ // This field will be set on entry firing and the value will be equal to the one in Logger struct field.
Level Level
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
@@ -93,7 +94,10 @@ func (entry Entry) log(level Level, msg string) {
entry.Level = level
entry.Message = msg
- if err := entry.Logger.Hooks.Fire(level, &entry); err != nil {
+ entry.Logger.mu.Lock()
+ err := entry.Logger.Hooks.Fire(level, &entry)
+ entry.Logger.mu.Unlock()
+ if err != nil {
entry.Logger.mu.Lock()
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
entry.Logger.mu.Unlock()
@@ -126,7 +130,7 @@ func (entry Entry) log(level Level, msg string) {
}
func (entry *Entry) Debug(args ...interface{}) {
- if entry.Logger.Level >= DebugLevel {
+ if entry.Logger.level() >= DebugLevel {
entry.log(DebugLevel, fmt.Sprint(args...))
}
}
@@ -136,13 +140,13 @@ func (entry *Entry) Print(args ...interface{}) {
}
func (entry *Entry) Info(args ...interface{}) {
- if entry.Logger.Level >= InfoLevel {
+ if entry.Logger.level() >= InfoLevel {
entry.log(InfoLevel, fmt.Sprint(args...))
}
}
func (entry *Entry) Warn(args ...interface{}) {
- if entry.Logger.Level >= WarnLevel {
+ if entry.Logger.level() >= WarnLevel {
entry.log(WarnLevel, fmt.Sprint(args...))
}
}
@@ -152,20 +156,20 @@ func (entry *Entry) Warning(args ...interface{}) {
}
func (entry *Entry) Error(args ...interface{}) {
- if entry.Logger.Level >= ErrorLevel {
+ if entry.Logger.level() >= ErrorLevel {
entry.log(ErrorLevel, fmt.Sprint(args...))
}
}
func (entry *Entry) Fatal(args ...interface{}) {
- if entry.Logger.Level >= FatalLevel {
+ if entry.Logger.level() >= FatalLevel {
entry.log(FatalLevel, fmt.Sprint(args...))
}
Exit(1)
}
func (entry *Entry) Panic(args ...interface{}) {
- if entry.Logger.Level >= PanicLevel {
+ if entry.Logger.level() >= PanicLevel {
entry.log(PanicLevel, fmt.Sprint(args...))
}
panic(fmt.Sprint(args...))
@@ -174,13 +178,13 @@ func (entry *Entry) Panic(args ...interface{}) {
// Entry Printf family functions
func (entry *Entry) Debugf(format string, args ...interface{}) {
- if entry.Logger.Level >= DebugLevel {
+ if entry.Logger.level() >= DebugLevel {
entry.Debug(fmt.Sprintf(format, args...))
}
}
func (entry *Entry) Infof(format string, args ...interface{}) {
- if entry.Logger.Level >= InfoLevel {
+ if entry.Logger.level() >= InfoLevel {
entry.Info(fmt.Sprintf(format, args...))
}
}
@@ -190,7 +194,7 @@ func (entry *Entry) Printf(format string, args ...interface{}) {
}
func (entry *Entry) Warnf(format string, args ...interface{}) {
- if entry.Logger.Level >= WarnLevel {
+ if entry.Logger.level() >= WarnLevel {
entry.Warn(fmt.Sprintf(format, args...))
}
}
@@ -200,20 +204,20 @@ func (entry *Entry) Warningf(format string, args ...interface{}) {
}
func (entry *Entry) Errorf(format string, args ...interface{}) {
- if entry.Logger.Level >= ErrorLevel {
+ if entry.Logger.level() >= ErrorLevel {
entry.Error(fmt.Sprintf(format, args...))
}
}
func (entry *Entry) Fatalf(format string, args ...interface{}) {
- if entry.Logger.Level >= FatalLevel {
+ if entry.Logger.level() >= FatalLevel {
entry.Fatal(fmt.Sprintf(format, args...))
}
Exit(1)
}
func (entry *Entry) Panicf(format string, args ...interface{}) {
- if entry.Logger.Level >= PanicLevel {
+ if entry.Logger.level() >= PanicLevel {
entry.Panic(fmt.Sprintf(format, args...))
}
}
@@ -221,13 +225,13 @@ func (entry *Entry) Panicf(format string, args ...interface{}) {
// Entry Println family functions
func (entry *Entry) Debugln(args ...interface{}) {
- if entry.Logger.Level >= DebugLevel {
+ if entry.Logger.level() >= DebugLevel {
entry.Debug(entry.sprintlnn(args...))
}
}
func (entry *Entry) Infoln(args ...interface{}) {
- if entry.Logger.Level >= InfoLevel {
+ if entry.Logger.level() >= InfoLevel {
entry.Info(entry.sprintlnn(args...))
}
}
@@ -237,7 +241,7 @@ func (entry *Entry) Println(args ...interface{}) {
}
func (entry *Entry) Warnln(args ...interface{}) {
- if entry.Logger.Level >= WarnLevel {
+ if entry.Logger.level() >= WarnLevel {
entry.Warn(entry.sprintlnn(args...))
}
}
@@ -247,20 +251,20 @@ func (entry *Entry) Warningln(args ...interface{}) {
}
func (entry *Entry) Errorln(args ...interface{}) {
- if entry.Logger.Level >= ErrorLevel {
+ if entry.Logger.level() >= ErrorLevel {
entry.Error(entry.sprintlnn(args...))
}
}
func (entry *Entry) Fatalln(args ...interface{}) {
- if entry.Logger.Level >= FatalLevel {
+ if entry.Logger.level() >= FatalLevel {
entry.Fatal(entry.sprintlnn(args...))
}
Exit(1)
}
func (entry *Entry) Panicln(args ...interface{}) {
- if entry.Logger.Level >= PanicLevel {
+ if entry.Logger.level() >= PanicLevel {
entry.Panic(entry.sprintlnn(args...))
}
}
diff --git a/vendor/github.com/Sirupsen/logrus/entry_test.go b/vendor/github.com/Sirupsen/logrus/entry_test.go
new file mode 100644
index 0000000..99c3b41
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/entry_test.go
@@ -0,0 +1,77 @@
+package logrus
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestEntryWithError(t *testing.T) {
+
+ assert := assert.New(t)
+
+ defer func() {
+ ErrorKey = "error"
+ }()
+
+ err := fmt.Errorf("kaboom at layer %d", 4711)
+
+ assert.Equal(err, WithError(err).Data["error"])
+
+ logger := New()
+ logger.Out = &bytes.Buffer{}
+ entry := NewEntry(logger)
+
+ assert.Equal(err, entry.WithError(err).Data["error"])
+
+ ErrorKey = "err"
+
+ assert.Equal(err, entry.WithError(err).Data["err"])
+
+}
+
+func TestEntryPanicln(t *testing.T) {
+ errBoom := fmt.Errorf("boom time")
+
+ defer func() {
+ p := recover()
+ assert.NotNil(t, p)
+
+ switch pVal := p.(type) {
+ case *Entry:
+ assert.Equal(t, "kaboom", pVal.Message)
+ assert.Equal(t, errBoom, pVal.Data["err"])
+ default:
+ t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
+ }
+ }()
+
+ logger := New()
+ logger.Out = &bytes.Buffer{}
+ entry := NewEntry(logger)
+ entry.WithField("err", errBoom).Panicln("kaboom")
+}
+
+func TestEntryPanicf(t *testing.T) {
+ errBoom := fmt.Errorf("boom again")
+
+ defer func() {
+ p := recover()
+ assert.NotNil(t, p)
+
+ switch pVal := p.(type) {
+ case *Entry:
+ assert.Equal(t, "kaboom true", pVal.Message)
+ assert.Equal(t, errBoom, pVal.Data["err"])
+ default:
+ t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
+ }
+ }()
+
+ logger := New()
+ logger.Out = &bytes.Buffer{}
+ entry := NewEntry(logger)
+ entry.WithField("err", errBoom).Panicf("kaboom %v", true)
+}
diff --git a/vendor/github.com/Sirupsen/logrus/example_basic_test.go b/vendor/github.com/Sirupsen/logrus/example_basic_test.go
new file mode 100644
index 0000000..a2acf55
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/example_basic_test.go
@@ -0,0 +1,69 @@
+package logrus_test
+
+import (
+ "github.com/sirupsen/logrus"
+ "os"
+)
+
+func Example_basic() {
+ var log = logrus.New()
+ log.Formatter = new(logrus.JSONFormatter)
+ log.Formatter = new(logrus.TextFormatter) //default
+ log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output
+ log.Level = logrus.DebugLevel
+ log.Out = os.Stdout
+
+ // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
+ // if err == nil {
+ // log.Out = file
+ // } else {
+ // log.Info("Failed to log to file, using default stderr")
+ // }
+
+ defer func() {
+ err := recover()
+ if err != nil {
+ entry := err.(*logrus.Entry)
+ log.WithFields(logrus.Fields{
+ "omg": true,
+ "err_animal": entry.Data["animal"],
+ "err_size": entry.Data["size"],
+ "err_level": entry.Level,
+ "err_message": entry.Message,
+ "number": 100,
+ }).Error("The ice breaks!") // or use Fatal() to force the process to exit with a nonzero code
+ }
+ }()
+
+ log.WithFields(logrus.Fields{
+ "animal": "walrus",
+ "number": 8,
+ }).Debug("Started observing beach")
+
+ log.WithFields(logrus.Fields{
+ "animal": "walrus",
+ "size": 10,
+ }).Info("A group of walrus emerges from the ocean")
+
+ log.WithFields(logrus.Fields{
+ "omg": true,
+ "number": 122,
+ }).Warn("The group's number increased tremendously!")
+
+ log.WithFields(logrus.Fields{
+ "temperature": -4,
+ }).Debug("Temperature changes")
+
+ log.WithFields(logrus.Fields{
+ "animal": "orca",
+ "size": 9009,
+ }).Panic("It's over 9000!")
+
+ // Output:
+ // level=debug msg="Started observing beach" animal=walrus number=8
+ // level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
+ // level=warning msg="The group's number increased tremendously!" number=122 omg=true
+ // level=debug msg="Temperature changes" temperature=-4
+ // level=panic msg="It's over 9000!" animal=orca size=9009
+ // level=error msg="The ice breaks!" err_animal=orca err_level=panic err_message="It's over 9000!" err_size=9009 number=100 omg=true
+}
diff --git a/vendor/github.com/Sirupsen/logrus/example_hook_test.go b/vendor/github.com/Sirupsen/logrus/example_hook_test.go
new file mode 100644
index 0000000..d4ddffc
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/example_hook_test.go
@@ -0,0 +1,35 @@
+package logrus_test
+
+import (
+ "github.com/sirupsen/logrus"
+ "gopkg.in/gemnasium/logrus-airbrake-hook.v2"
+ "os"
+)
+
+func Example_hook() {
+ var log = logrus.New()
+ log.Formatter = new(logrus.TextFormatter) // default
+ log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true // remove timestamp from test output
+ log.Hooks.Add(airbrake.NewHook(123, "xyz", "development"))
+ log.Out = os.Stdout
+
+ log.WithFields(logrus.Fields{
+ "animal": "walrus",
+ "size": 10,
+ }).Info("A group of walrus emerges from the ocean")
+
+ log.WithFields(logrus.Fields{
+ "omg": true,
+ "number": 122,
+ }).Warn("The group's number increased tremendously!")
+
+ log.WithFields(logrus.Fields{
+ "omg": true,
+ "number": 100,
+ }).Error("The ice breaks!")
+
+ // Output:
+ // level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
+ // level=warning msg="The group's number increased tremendously!" number=122 omg=true
+ // level=error msg="The ice breaks!" number=100 omg=true
+}
diff --git a/vendor/github.com/Sirupsen/logrus/exported.go b/vendor/github.com/Sirupsen/logrus/exported.go
index 9a0120a..013183e 100644
--- a/vendor/github.com/Sirupsen/logrus/exported.go
+++ b/vendor/github.com/Sirupsen/logrus/exported.go
@@ -31,14 +31,14 @@ func SetFormatter(formatter Formatter) {
func SetLevel(level Level) {
std.mu.Lock()
defer std.mu.Unlock()
- std.Level = level
+ std.SetLevel(level)
}
// GetLevel returns the standard logger level.
func GetLevel() Level {
std.mu.Lock()
defer std.mu.Unlock()
- return std.Level
+ return std.level()
}
// AddHook adds a hook to the standard logger hooks.
diff --git a/vendor/github.com/Sirupsen/logrus/formatter.go b/vendor/github.com/Sirupsen/logrus/formatter.go
index b5fbe93..b183ff5 100644
--- a/vendor/github.com/Sirupsen/logrus/formatter.go
+++ b/vendor/github.com/Sirupsen/logrus/formatter.go
@@ -2,7 +2,7 @@ package logrus
import "time"
-const DefaultTimestampFormat = time.RFC3339
+const defaultTimestampFormat = time.RFC3339
// The Formatter interface is used to implement a custom Formatter. It takes an
// `Entry`. It exposes all the fields, including the default ones:
diff --git a/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go b/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go
new file mode 100644
index 0000000..d948158
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go
@@ -0,0 +1,101 @@
+package logrus
+
+import (
+ "fmt"
+ "testing"
+ "time"
+)
+
+// smallFields is a small size data set for benchmarking
+var smallFields = Fields{
+ "foo": "bar",
+ "baz": "qux",
+ "one": "two",
+ "three": "four",
+}
+
+// largeFields is a large size data set for benchmarking
+var largeFields = Fields{
+ "foo": "bar",
+ "baz": "qux",
+ "one": "two",
+ "three": "four",
+ "five": "six",
+ "seven": "eight",
+ "nine": "ten",
+ "eleven": "twelve",
+ "thirteen": "fourteen",
+ "fifteen": "sixteen",
+ "seventeen": "eighteen",
+ "nineteen": "twenty",
+ "a": "b",
+ "c": "d",
+ "e": "f",
+ "g": "h",
+ "i": "j",
+ "k": "l",
+ "m": "n",
+ "o": "p",
+ "q": "r",
+ "s": "t",
+ "u": "v",
+ "w": "x",
+ "y": "z",
+ "this": "will",
+ "make": "thirty",
+ "entries": "yeah",
+}
+
+var errorFields = Fields{
+ "foo": fmt.Errorf("bar"),
+ "baz": fmt.Errorf("qux"),
+}
+
+func BenchmarkErrorTextFormatter(b *testing.B) {
+ doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields)
+}
+
+func BenchmarkSmallTextFormatter(b *testing.B) {
+ doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
+}
+
+func BenchmarkLargeTextFormatter(b *testing.B) {
+ doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
+}
+
+func BenchmarkSmallColoredTextFormatter(b *testing.B) {
+ doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
+}
+
+func BenchmarkLargeColoredTextFormatter(b *testing.B) {
+ doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
+}
+
+func BenchmarkSmallJSONFormatter(b *testing.B) {
+ doBenchmark(b, &JSONFormatter{}, smallFields)
+}
+
+func BenchmarkLargeJSONFormatter(b *testing.B) {
+ doBenchmark(b, &JSONFormatter{}, largeFields)
+}
+
+func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
+ logger := New()
+
+ entry := &Entry{
+ Time: time.Time{},
+ Level: InfoLevel,
+ Message: "message",
+ Data: fields,
+ Logger: logger,
+ }
+ var d []byte
+ var err error
+ for i := 0; i < b.N; i++ {
+ d, err = formatter.Format(entry)
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.SetBytes(int64(len(d)))
+ }
+}
diff --git a/vendor/github.com/Sirupsen/logrus/hook_test.go b/vendor/github.com/Sirupsen/logrus/hook_test.go
new file mode 100644
index 0000000..4fea751
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/hook_test.go
@@ -0,0 +1,144 @@
+package logrus
+
+import (
+ "sync"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+type TestHook struct {
+ Fired bool
+}
+
+func (hook *TestHook) Fire(entry *Entry) error {
+ hook.Fired = true
+ return nil
+}
+
+func (hook *TestHook) Levels() []Level {
+ return []Level{
+ DebugLevel,
+ InfoLevel,
+ WarnLevel,
+ ErrorLevel,
+ FatalLevel,
+ PanicLevel,
+ }
+}
+
+func TestHookFires(t *testing.T) {
+ hook := new(TestHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook)
+ assert.Equal(t, hook.Fired, false)
+
+ log.Print("test")
+ }, func(fields Fields) {
+ assert.Equal(t, hook.Fired, true)
+ })
+}
+
+type ModifyHook struct {
+}
+
+func (hook *ModifyHook) Fire(entry *Entry) error {
+ entry.Data["wow"] = "whale"
+ return nil
+}
+
+func (hook *ModifyHook) Levels() []Level {
+ return []Level{
+ DebugLevel,
+ InfoLevel,
+ WarnLevel,
+ ErrorLevel,
+ FatalLevel,
+ PanicLevel,
+ }
+}
+
+func TestHookCanModifyEntry(t *testing.T) {
+ hook := new(ModifyHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook)
+ log.WithField("wow", "elephant").Print("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["wow"], "whale")
+ })
+}
+
+func TestCanFireMultipleHooks(t *testing.T) {
+ hook1 := new(ModifyHook)
+ hook2 := new(TestHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook1)
+ log.Hooks.Add(hook2)
+
+ log.WithField("wow", "elephant").Print("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["wow"], "whale")
+ assert.Equal(t, hook2.Fired, true)
+ })
+}
+
+type ErrorHook struct {
+ Fired bool
+}
+
+func (hook *ErrorHook) Fire(entry *Entry) error {
+ hook.Fired = true
+ return nil
+}
+
+func (hook *ErrorHook) Levels() []Level {
+ return []Level{
+ ErrorLevel,
+ }
+}
+
+func TestErrorHookShouldntFireOnInfo(t *testing.T) {
+ hook := new(ErrorHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook)
+ log.Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, hook.Fired, false)
+ })
+}
+
+func TestErrorHookShouldFireOnError(t *testing.T) {
+ hook := new(ErrorHook)
+
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Hooks.Add(hook)
+ log.Error("test")
+ }, func(fields Fields) {
+ assert.Equal(t, hook.Fired, true)
+ })
+}
+
+func TestAddHookRace(t *testing.T) {
+ var wg sync.WaitGroup
+ wg.Add(2)
+ hook := new(ErrorHook)
+ LogAndAssertJSON(t, func(log *Logger) {
+ go func() {
+ defer wg.Done()
+ log.AddHook(hook)
+ }()
+ go func() {
+ defer wg.Done()
+ log.Error("test")
+ }()
+ wg.Wait()
+ }, func(fields Fields) {
+ // the line may have been logged
+ // before the hook was added, so we can't
+ // actually assert on the hook
+ })
+}
diff --git a/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md b/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md
new file mode 100644
index 0000000..1bbc0f7
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md
@@ -0,0 +1,39 @@
+# Syslog Hooks for Logrus
+
+## Usage
+
+```go
+import (
+ "log/syslog"
+ "github.com/sirupsen/logrus"
+ lSyslog "github.com/sirupsen/logrus/hooks/syslog"
+)
+
+func main() {
+ log := logrus.New()
+ hook, err := lSyslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
+
+ if err == nil {
+ log.Hooks.Add(hook)
+ }
+}
+```
+
+If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). Just assign empty string to the first two parameters of `NewSyslogHook`. It should look like the following.
+
+```go
+import (
+ "log/syslog"
+ "github.com/sirupsen/logrus"
+ lSyslog "github.com/sirupsen/logrus/hooks/syslog"
+)
+
+func main() {
+ log := logrus.New()
+ hook, err := lSyslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
+
+ if err == nil {
+ log.Hooks.Add(hook)
+ }
+}
+```
diff --git a/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go b/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go
new file mode 100644
index 0000000..329ce0d
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog.go
@@ -0,0 +1,55 @@
+// +build !windows,!nacl,!plan9
+
+package syslog
+
+import (
+ "fmt"
+ "log/syslog"
+ "os"
+
+ "github.com/sirupsen/logrus"
+)
+
+// SyslogHook to send logs via syslog.
+type SyslogHook struct {
+ Writer *syslog.Writer
+ SyslogNetwork string
+ SyslogRaddr string
+}
+
+// Creates a hook to be added to an instance of logger. This is called with
+// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")`
+// `if err == nil { log.Hooks.Add(hook) }`
+func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
+ w, err := syslog.Dial(network, raddr, priority, tag)
+ return &SyslogHook{w, network, raddr}, err
+}
+
+func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
+ line, err := entry.String()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
+ return err
+ }
+
+ switch entry.Level {
+ case logrus.PanicLevel:
+ return hook.Writer.Crit(line)
+ case logrus.FatalLevel:
+ return hook.Writer.Crit(line)
+ case logrus.ErrorLevel:
+ return hook.Writer.Err(line)
+ case logrus.WarnLevel:
+ return hook.Writer.Warning(line)
+ case logrus.InfoLevel:
+ return hook.Writer.Info(line)
+ case logrus.DebugLevel:
+ return hook.Writer.Debug(line)
+ default:
+ return nil
+ }
+}
+
+func (hook *SyslogHook) Levels() []logrus.Level {
+ return logrus.AllLevels
+}
diff --git a/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go b/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go
new file mode 100644
index 0000000..5ec3a44
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go
@@ -0,0 +1,27 @@
+package syslog
+
+import (
+ "log/syslog"
+ "testing"
+
+ "github.com/sirupsen/logrus"
+)
+
+func TestLocalhostAddAndPrint(t *testing.T) {
+ log := logrus.New()
+ hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
+
+ if err != nil {
+ t.Errorf("Unable to connect to local syslog.")
+ }
+
+ log.Hooks.Add(hook)
+
+ for _, level := range hook.Levels() {
+ if len(log.Hooks[level]) != 1 {
+ t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level]))
+ }
+ }
+
+ log.Info("Congratulations!")
+}
diff --git a/vendor/github.com/Sirupsen/logrus/hooks/test/test.go b/vendor/github.com/Sirupsen/logrus/hooks/test/test.go
new file mode 100644
index 0000000..62c4845
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/hooks/test/test.go
@@ -0,0 +1,95 @@
+// The Test package is used for testing logrus. It is here for backwards
+// compatibility from when logrus' organization was upper-case. Please use
+// lower-case logrus and the `null` package instead of this one.
+package test
+
+import (
+ "io/ioutil"
+ "sync"
+
+ "github.com/sirupsen/logrus"
+)
+
+// Hook is a hook designed for dealing with logs in test scenarios.
+type Hook struct {
+ // Entries is an array of all entries that have been received by this hook.
+ // For safe access, use the AllEntries() method, rather than reading this
+ // value directly.
+ Entries []*logrus.Entry
+ mu sync.RWMutex
+}
+
+// NewGlobal installs a test hook for the global logger.
+func NewGlobal() *Hook {
+
+ hook := new(Hook)
+ logrus.AddHook(hook)
+
+ return hook
+
+}
+
+// NewLocal installs a test hook for a given local logger.
+func NewLocal(logger *logrus.Logger) *Hook {
+
+ hook := new(Hook)
+ logger.Hooks.Add(hook)
+
+ return hook
+
+}
+
+// NewNullLogger creates a discarding logger and installs the test hook.
+func NewNullLogger() (*logrus.Logger, *Hook) {
+
+ logger := logrus.New()
+ logger.Out = ioutil.Discard
+
+ return logger, NewLocal(logger)
+
+}
+
+func (t *Hook) Fire(e *logrus.Entry) error {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.Entries = append(t.Entries, e)
+ return nil
+}
+
+func (t *Hook) Levels() []logrus.Level {
+ return logrus.AllLevels
+}
+
+// LastEntry returns the last entry that was logged or nil.
+func (t *Hook) LastEntry() *logrus.Entry {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ i := len(t.Entries) - 1
+ if i < 0 {
+ return nil
+ }
+ // Make a copy, for safety
+ e := *t.Entries[i]
+ return &e
+}
+
+// AllEntries returns all entries that were logged.
+func (t *Hook) AllEntries() []*logrus.Entry {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ // Make a copy so the returned value won't race with future log requests
+ entries := make([]*logrus.Entry, len(t.Entries))
+ for i, entry := range t.Entries {
+ // Make a copy, for safety
+ e := *entry
+ entries[i] = &e
+ }
+ return entries
+}
+
+// Reset removes all Entries from this test hook.
+func (t *Hook) Reset() {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.Entries = make([]*logrus.Entry, 0)
+}
diff --git a/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go b/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go
new file mode 100644
index 0000000..3f55cfe
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/hooks/test/test_test.go
@@ -0,0 +1,39 @@
+package test
+
+import (
+ "testing"
+
+ "github.com/sirupsen/logrus"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestAllHooks(t *testing.T) {
+
+ assert := assert.New(t)
+
+ logger, hook := NewNullLogger()
+ assert.Nil(hook.LastEntry())
+ assert.Equal(0, len(hook.Entries))
+
+ logger.Error("Hello error")
+ assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
+ assert.Equal("Hello error", hook.LastEntry().Message)
+ assert.Equal(1, len(hook.Entries))
+
+ logger.Warn("Hello warning")
+ assert.Equal(logrus.WarnLevel, hook.LastEntry().Level)
+ assert.Equal("Hello warning", hook.LastEntry().Message)
+ assert.Equal(2, len(hook.Entries))
+
+ hook.Reset()
+ assert.Nil(hook.LastEntry())
+ assert.Equal(0, len(hook.Entries))
+
+ hook = NewGlobal()
+
+ logrus.Error("Hello error")
+ assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
+ assert.Equal("Hello error", hook.LastEntry().Message)
+ assert.Equal(1, len(hook.Entries))
+
+}
diff --git a/vendor/github.com/Sirupsen/logrus/json_formatter.go b/vendor/github.com/Sirupsen/logrus/json_formatter.go
index 266554e..fb01c1b 100644
--- a/vendor/github.com/Sirupsen/logrus/json_formatter.go
+++ b/vendor/github.com/Sirupsen/logrus/json_formatter.go
@@ -6,8 +6,11 @@ import (
)
type fieldKey string
+
+// FieldMap allows customization of the key names for default fields.
type FieldMap map[fieldKey]string
+// Default key names for the default fields
const (
FieldKeyMsg = "msg"
FieldKeyLevel = "level"
@@ -22,6 +25,7 @@ func (f FieldMap) resolve(key fieldKey) string {
return string(key)
}
+// JSONFormatter formats logs into parsable json
type JSONFormatter struct {
// TimestampFormat sets the format used for marshaling timestamps.
TimestampFormat string
@@ -29,25 +33,26 @@ type JSONFormatter struct {
// DisableTimestamp allows disabling automatic timestamps in output
DisableTimestamp bool
- // FieldMap allows users to customize the names of keys for various fields.
+ // FieldMap allows users to customize the names of keys for default fields.
// As an example:
// formatter := &JSONFormatter{
// FieldMap: FieldMap{
// FieldKeyTime: "@timestamp",
// FieldKeyLevel: "@level",
- // FieldKeyLevel: "@message",
+ // FieldKeyMsg: "@message",
// },
// }
FieldMap FieldMap
}
+// Format renders a single log entry
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
data := make(Fields, len(entry.Data)+3)
for k, v := range entry.Data {
switch v := v.(type) {
case error:
// Otherwise errors are ignored by `encoding/json`
- // https://github.com/Sirupsen/logrus/issues/137
+ // https://github.com/sirupsen/logrus/issues/137
data[k] = v.Error()
default:
data[k] = v
@@ -57,7 +62,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
timestampFormat := f.TimestampFormat
if timestampFormat == "" {
- timestampFormat = DefaultTimestampFormat
+ timestampFormat = defaultTimestampFormat
}
if !f.DisableTimestamp {
diff --git a/vendor/github.com/Sirupsen/logrus/json_formatter_test.go b/vendor/github.com/Sirupsen/logrus/json_formatter_test.go
new file mode 100644
index 0000000..51093a7
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/json_formatter_test.go
@@ -0,0 +1,199 @@
+package logrus
+
+import (
+ "encoding/json"
+ "errors"
+ "strings"
+ "testing"
+)
+
+func TestErrorNotLost(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("error", errors.New("wild walrus")))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["error"] != "wild walrus" {
+ t.Fatal("Error field not set")
+ }
+}
+
+func TestErrorNotLostOnFieldNotNamedError(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("omg", errors.New("wild walrus")))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["omg"] != "wild walrus" {
+ t.Fatal("Error field not set")
+ }
+}
+
+func TestFieldClashWithTime(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("time", "right now!"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["fields.time"] != "right now!" {
+ t.Fatal("fields.time not set to original time field")
+ }
+
+ if entry["time"] != "0001-01-01T00:00:00Z" {
+ t.Fatal("time field not set to current time, was: ", entry["time"])
+ }
+}
+
+func TestFieldClashWithMsg(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("msg", "something"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["fields.msg"] != "something" {
+ t.Fatal("fields.msg not set to original msg field")
+ }
+}
+
+func TestFieldClashWithLevel(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("level", "something"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["fields.level"] != "something" {
+ t.Fatal("fields.level not set to original level field")
+ }
+}
+
+func TestJSONEntryEndsWithNewline(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("level", "something"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ if b[len(b)-1] != '\n' {
+ t.Fatal("Expected JSON log entry to end with a newline")
+ }
+}
+
+func TestJSONMessageKey(t *testing.T) {
+ formatter := &JSONFormatter{
+ FieldMap: FieldMap{
+ FieldKeyMsg: "message",
+ },
+ }
+
+ b, err := formatter.Format(&Entry{Message: "oh hai"})
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+ s := string(b)
+ if !(strings.Contains(s, "message") && strings.Contains(s, "oh hai")) {
+ t.Fatal("Expected JSON to format message key")
+ }
+}
+
+func TestJSONLevelKey(t *testing.T) {
+ formatter := &JSONFormatter{
+ FieldMap: FieldMap{
+ FieldKeyLevel: "somelevel",
+ },
+ }
+
+ b, err := formatter.Format(WithField("level", "something"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+ s := string(b)
+ if !strings.Contains(s, "somelevel") {
+ t.Fatal("Expected JSON to format level key")
+ }
+}
+
+func TestJSONTimeKey(t *testing.T) {
+ formatter := &JSONFormatter{
+ FieldMap: FieldMap{
+ FieldKeyTime: "timeywimey",
+ },
+ }
+
+ b, err := formatter.Format(WithField("level", "something"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+ s := string(b)
+ if !strings.Contains(s, "timeywimey") {
+ t.Fatal("Expected JSON to format time key")
+ }
+}
+
+func TestJSONDisableTimestamp(t *testing.T) {
+ formatter := &JSONFormatter{
+ DisableTimestamp: true,
+ }
+
+ b, err := formatter.Format(WithField("level", "something"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+ s := string(b)
+ if strings.Contains(s, FieldKeyTime) {
+ t.Error("Did not prevent timestamp", s)
+ }
+}
+
+func TestJSONEnableTimestamp(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("level", "something"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+ s := string(b)
+ if !strings.Contains(s, FieldKeyTime) {
+ t.Error("Timestamp not present", s)
+ }
+}
diff --git a/vendor/github.com/Sirupsen/logrus/logger.go b/vendor/github.com/Sirupsen/logrus/logger.go
index b769f3d..fdaf8a6 100644
--- a/vendor/github.com/Sirupsen/logrus/logger.go
+++ b/vendor/github.com/Sirupsen/logrus/logger.go
@@ -4,6 +4,7 @@ import (
"io"
"os"
"sync"
+ "sync/atomic"
)
type Logger struct {
@@ -24,7 +25,7 @@ type Logger struct {
Formatter Formatter
// The logging level the logger should log at. This is typically (and defaults
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
- // logged. `logrus.Debug` is useful in
+ // logged.
Level Level
// Used to sync writing to the log. Locking is enabled by Default
mu MutexWrap
@@ -112,7 +113,7 @@ func (logger *Logger) WithError(err error) *Entry {
}
func (logger *Logger) Debugf(format string, args ...interface{}) {
- if logger.Level >= DebugLevel {
+ if logger.level() >= DebugLevel {
entry := logger.newEntry()
entry.Debugf(format, args...)
logger.releaseEntry(entry)
@@ -120,7 +121,7 @@ func (logger *Logger) Debugf(format string, args ...interface{}) {
}
func (logger *Logger) Infof(format string, args ...interface{}) {
- if logger.Level >= InfoLevel {
+ if logger.level() >= InfoLevel {
entry := logger.newEntry()
entry.Infof(format, args...)
logger.releaseEntry(entry)
@@ -134,7 +135,7 @@ func (logger *Logger) Printf(format string, args ...interface{}) {
}
func (logger *Logger) Warnf(format string, args ...interface{}) {
- if logger.Level >= WarnLevel {
+ if logger.level() >= WarnLevel {
entry := logger.newEntry()
entry.Warnf(format, args...)
logger.releaseEntry(entry)
@@ -142,7 +143,7 @@ func (logger *Logger) Warnf(format string, args ...interface{}) {
}
func (logger *Logger) Warningf(format string, args ...interface{}) {
- if logger.Level >= WarnLevel {
+ if logger.level() >= WarnLevel {
entry := logger.newEntry()
entry.Warnf(format, args...)
logger.releaseEntry(entry)
@@ -150,7 +151,7 @@ func (logger *Logger) Warningf(format string, args ...interface{}) {
}
func (logger *Logger) Errorf(format string, args ...interface{}) {
- if logger.Level >= ErrorLevel {
+ if logger.level() >= ErrorLevel {
entry := logger.newEntry()
entry.Errorf(format, args...)
logger.releaseEntry(entry)
@@ -158,7 +159,7 @@ func (logger *Logger) Errorf(format string, args ...interface{}) {
}
func (logger *Logger) Fatalf(format string, args ...interface{}) {
- if logger.Level >= FatalLevel {
+ if logger.level() >= FatalLevel {
entry := logger.newEntry()
entry.Fatalf(format, args...)
logger.releaseEntry(entry)
@@ -167,7 +168,7 @@ func (logger *Logger) Fatalf(format string, args ...interface{}) {
}
func (logger *Logger) Panicf(format string, args ...interface{}) {
- if logger.Level >= PanicLevel {
+ if logger.level() >= PanicLevel {
entry := logger.newEntry()
entry.Panicf(format, args...)
logger.releaseEntry(entry)
@@ -175,7 +176,7 @@ func (logger *Logger) Panicf(format string, args ...interface{}) {
}
func (logger *Logger) Debug(args ...interface{}) {
- if logger.Level >= DebugLevel {
+ if logger.level() >= DebugLevel {
entry := logger.newEntry()
entry.Debug(args...)
logger.releaseEntry(entry)
@@ -183,7 +184,7 @@ func (logger *Logger) Debug(args ...interface{}) {
}
func (logger *Logger) Info(args ...interface{}) {
- if logger.Level >= InfoLevel {
+ if logger.level() >= InfoLevel {
entry := logger.newEntry()
entry.Info(args...)
logger.releaseEntry(entry)
@@ -197,7 +198,7 @@ func (logger *Logger) Print(args ...interface{}) {
}
func (logger *Logger) Warn(args ...interface{}) {
- if logger.Level >= WarnLevel {
+ if logger.level() >= WarnLevel {
entry := logger.newEntry()
entry.Warn(args...)
logger.releaseEntry(entry)
@@ -205,7 +206,7 @@ func (logger *Logger) Warn(args ...interface{}) {
}
func (logger *Logger) Warning(args ...interface{}) {
- if logger.Level >= WarnLevel {
+ if logger.level() >= WarnLevel {
entry := logger.newEntry()
entry.Warn(args...)
logger.releaseEntry(entry)
@@ -213,7 +214,7 @@ func (logger *Logger) Warning(args ...interface{}) {
}
func (logger *Logger) Error(args ...interface{}) {
- if logger.Level >= ErrorLevel {
+ if logger.level() >= ErrorLevel {
entry := logger.newEntry()
entry.Error(args...)
logger.releaseEntry(entry)
@@ -221,7 +222,7 @@ func (logger *Logger) Error(args ...interface{}) {
}
func (logger *Logger) Fatal(args ...interface{}) {
- if logger.Level >= FatalLevel {
+ if logger.level() >= FatalLevel {
entry := logger.newEntry()
entry.Fatal(args...)
logger.releaseEntry(entry)
@@ -230,7 +231,7 @@ func (logger *Logger) Fatal(args ...interface{}) {
}
func (logger *Logger) Panic(args ...interface{}) {
- if logger.Level >= PanicLevel {
+ if logger.level() >= PanicLevel {
entry := logger.newEntry()
entry.Panic(args...)
logger.releaseEntry(entry)
@@ -238,7 +239,7 @@ func (logger *Logger) Panic(args ...interface{}) {
}
func (logger *Logger) Debugln(args ...interface{}) {
- if logger.Level >= DebugLevel {
+ if logger.level() >= DebugLevel {
entry := logger.newEntry()
entry.Debugln(args...)
logger.releaseEntry(entry)
@@ -246,7 +247,7 @@ func (logger *Logger) Debugln(args ...interface{}) {
}
func (logger *Logger) Infoln(args ...interface{}) {
- if logger.Level >= InfoLevel {
+ if logger.level() >= InfoLevel {
entry := logger.newEntry()
entry.Infoln(args...)
logger.releaseEntry(entry)
@@ -260,7 +261,7 @@ func (logger *Logger) Println(args ...interface{}) {
}
func (logger *Logger) Warnln(args ...interface{}) {
- if logger.Level >= WarnLevel {
+ if logger.level() >= WarnLevel {
entry := logger.newEntry()
entry.Warnln(args...)
logger.releaseEntry(entry)
@@ -268,7 +269,7 @@ func (logger *Logger) Warnln(args ...interface{}) {
}
func (logger *Logger) Warningln(args ...interface{}) {
- if logger.Level >= WarnLevel {
+ if logger.level() >= WarnLevel {
entry := logger.newEntry()
entry.Warnln(args...)
logger.releaseEntry(entry)
@@ -276,7 +277,7 @@ func (logger *Logger) Warningln(args ...interface{}) {
}
func (logger *Logger) Errorln(args ...interface{}) {
- if logger.Level >= ErrorLevel {
+ if logger.level() >= ErrorLevel {
entry := logger.newEntry()
entry.Errorln(args...)
logger.releaseEntry(entry)
@@ -284,7 +285,7 @@ func (logger *Logger) Errorln(args ...interface{}) {
}
func (logger *Logger) Fatalln(args ...interface{}) {
- if logger.Level >= FatalLevel {
+ if logger.level() >= FatalLevel {
entry := logger.newEntry()
entry.Fatalln(args...)
logger.releaseEntry(entry)
@@ -293,7 +294,7 @@ func (logger *Logger) Fatalln(args ...interface{}) {
}
func (logger *Logger) Panicln(args ...interface{}) {
- if logger.Level >= PanicLevel {
+ if logger.level() >= PanicLevel {
entry := logger.newEntry()
entry.Panicln(args...)
logger.releaseEntry(entry)
@@ -306,3 +307,17 @@ func (logger *Logger) Panicln(args ...interface{}) {
func (logger *Logger) SetNoLock() {
logger.mu.Disable()
}
+
+func (logger *Logger) level() Level {
+ return Level(atomic.LoadUint32((*uint32)(&logger.Level)))
+}
+
+func (logger *Logger) SetLevel(level Level) {
+ atomic.StoreUint32((*uint32)(&logger.Level), uint32(level))
+}
+
+func (logger *Logger) AddHook(hook Hook) {
+ logger.mu.Lock()
+ defer logger.mu.Unlock()
+ logger.Hooks.Add(hook)
+}
diff --git a/vendor/github.com/Sirupsen/logrus/logger_bench_test.go b/vendor/github.com/Sirupsen/logrus/logger_bench_test.go
new file mode 100644
index 0000000..dd23a35
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/logger_bench_test.go
@@ -0,0 +1,61 @@
+package logrus
+
+import (
+ "os"
+ "testing"
+)
+
+// smallFields is a small size data set for benchmarking
+var loggerFields = Fields{
+ "foo": "bar",
+ "baz": "qux",
+ "one": "two",
+ "three": "four",
+}
+
+func BenchmarkDummyLogger(b *testing.B) {
+ nullf, err := os.OpenFile("/dev/null", os.O_WRONLY, 0666)
+ if err != nil {
+ b.Fatalf("%v", err)
+ }
+ defer nullf.Close()
+ doLoggerBenchmark(b, nullf, &TextFormatter{DisableColors: true}, smallFields)
+}
+
+func BenchmarkDummyLoggerNoLock(b *testing.B) {
+ nullf, err := os.OpenFile("/dev/null", os.O_WRONLY|os.O_APPEND, 0666)
+ if err != nil {
+ b.Fatalf("%v", err)
+ }
+ defer nullf.Close()
+ doLoggerBenchmarkNoLock(b, nullf, &TextFormatter{DisableColors: true}, smallFields)
+}
+
+func doLoggerBenchmark(b *testing.B, out *os.File, formatter Formatter, fields Fields) {
+ logger := Logger{
+ Out: out,
+ Level: InfoLevel,
+ Formatter: formatter,
+ }
+ entry := logger.WithFields(fields)
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ entry.Info("aaa")
+ }
+ })
+}
+
+func doLoggerBenchmarkNoLock(b *testing.B, out *os.File, formatter Formatter, fields Fields) {
+ logger := Logger{
+ Out: out,
+ Level: InfoLevel,
+ Formatter: formatter,
+ }
+ logger.SetNoLock()
+ entry := logger.WithFields(fields)
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ entry.Info("aaa")
+ }
+ })
+}
diff --git a/vendor/github.com/Sirupsen/logrus/logrus.go b/vendor/github.com/Sirupsen/logrus/logrus.go
index e596691..dd38999 100644
--- a/vendor/github.com/Sirupsen/logrus/logrus.go
+++ b/vendor/github.com/Sirupsen/logrus/logrus.go
@@ -10,7 +10,7 @@ import (
type Fields map[string]interface{}
// Level type
-type Level uint8
+type Level uint32
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
func (level Level) String() string {
diff --git a/vendor/github.com/Sirupsen/logrus/logrus_test.go b/vendor/github.com/Sirupsen/logrus/logrus_test.go
new file mode 100644
index 0000000..78cbc28
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/logrus_test.go
@@ -0,0 +1,386 @@
+package logrus
+
+import (
+ "bytes"
+ "encoding/json"
+ "strconv"
+ "strings"
+ "sync"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
+ var buffer bytes.Buffer
+ var fields Fields
+
+ logger := New()
+ logger.Out = &buffer
+ logger.Formatter = new(JSONFormatter)
+
+ log(logger)
+
+ err := json.Unmarshal(buffer.Bytes(), &fields)
+ assert.Nil(t, err)
+
+ assertions(fields)
+}
+
+func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
+ var buffer bytes.Buffer
+
+ logger := New()
+ logger.Out = &buffer
+ logger.Formatter = &TextFormatter{
+ DisableColors: true,
+ }
+
+ log(logger)
+
+ fields := make(map[string]string)
+ for _, kv := range strings.Split(buffer.String(), " ") {
+ if !strings.Contains(kv, "=") {
+ continue
+ }
+ kvArr := strings.Split(kv, "=")
+ key := strings.TrimSpace(kvArr[0])
+ val := kvArr[1]
+ if kvArr[1][0] == '"' {
+ var err error
+ val, err = strconv.Unquote(val)
+ assert.NoError(t, err)
+ }
+ fields[key] = val
+ }
+ assertions(fields)
+}
+
+func TestPrint(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Print("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ assert.Equal(t, fields["level"], "info")
+ })
+}
+
+func TestInfo(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ assert.Equal(t, fields["level"], "info")
+ })
+}
+
+func TestWarn(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Warn("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ assert.Equal(t, fields["level"], "warning")
+ })
+}
+
+func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Infoln("test", "test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test test")
+ })
+}
+
+func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Infoln("test", 10)
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test 10")
+ })
+}
+
+func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Infoln(10, 10)
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "10 10")
+ })
+}
+
+func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Infoln(10, 10)
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "10 10")
+ })
+}
+
+func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Info("test", 10)
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test10")
+ })
+}
+
+func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.Info("test", "test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "testtest")
+ })
+}
+
+func TestWithFieldsShouldAllowAssignments(t *testing.T) {
+ var buffer bytes.Buffer
+ var fields Fields
+
+ logger := New()
+ logger.Out = &buffer
+ logger.Formatter = new(JSONFormatter)
+
+ localLog := logger.WithFields(Fields{
+ "key1": "value1",
+ })
+
+ localLog.WithField("key2", "value2").Info("test")
+ err := json.Unmarshal(buffer.Bytes(), &fields)
+ assert.Nil(t, err)
+
+ assert.Equal(t, "value2", fields["key2"])
+ assert.Equal(t, "value1", fields["key1"])
+
+ buffer = bytes.Buffer{}
+ fields = Fields{}
+ localLog.Info("test")
+ err = json.Unmarshal(buffer.Bytes(), &fields)
+ assert.Nil(t, err)
+
+ _, ok := fields["key2"]
+ assert.Equal(t, false, ok)
+ assert.Equal(t, "value1", fields["key1"])
+}
+
+func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.WithField("msg", "hello").Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ })
+}
+
+func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.WithField("msg", "hello").Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["msg"], "test")
+ assert.Equal(t, fields["fields.msg"], "hello")
+ })
+}
+
+func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.WithField("time", "hello").Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["fields.time"], "hello")
+ })
+}
+
+func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
+ LogAndAssertJSON(t, func(log *Logger) {
+ log.WithField("level", 1).Info("test")
+ }, func(fields Fields) {
+ assert.Equal(t, fields["level"], "info")
+ assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only
+ })
+}
+
+func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
+ LogAndAssertText(t, func(log *Logger) {
+ ll := log.WithField("herp", "derp")
+ ll.Info("hello")
+ ll.Info("bye")
+ }, func(fields map[string]string) {
+ for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
+ if _, ok := fields[fieldName]; ok {
+ t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
+ }
+ }
+ })
+}
+
+func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
+
+ var buffer bytes.Buffer
+ var fields Fields
+
+ logger := New()
+ logger.Out = &buffer
+ logger.Formatter = new(JSONFormatter)
+
+ llog := logger.WithField("context", "eating raw fish")
+
+ llog.Info("looks delicious")
+
+ err := json.Unmarshal(buffer.Bytes(), &fields)
+ assert.NoError(t, err, "should have decoded first message")
+ assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
+ assert.Equal(t, fields["msg"], "looks delicious")
+ assert.Equal(t, fields["context"], "eating raw fish")
+
+ buffer.Reset()
+
+ llog.Warn("omg it is!")
+
+ err = json.Unmarshal(buffer.Bytes(), &fields)
+ assert.NoError(t, err, "should have decoded second message")
+ assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
+ assert.Equal(t, fields["msg"], "omg it is!")
+ assert.Equal(t, fields["context"], "eating raw fish")
+ assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
+
+}
+
+func TestConvertLevelToString(t *testing.T) {
+ assert.Equal(t, "debug", DebugLevel.String())
+ assert.Equal(t, "info", InfoLevel.String())
+ assert.Equal(t, "warning", WarnLevel.String())
+ assert.Equal(t, "error", ErrorLevel.String())
+ assert.Equal(t, "fatal", FatalLevel.String())
+ assert.Equal(t, "panic", PanicLevel.String())
+}
+
+func TestParseLevel(t *testing.T) {
+ l, err := ParseLevel("panic")
+ assert.Nil(t, err)
+ assert.Equal(t, PanicLevel, l)
+
+ l, err = ParseLevel("PANIC")
+ assert.Nil(t, err)
+ assert.Equal(t, PanicLevel, l)
+
+ l, err = ParseLevel("fatal")
+ assert.Nil(t, err)
+ assert.Equal(t, FatalLevel, l)
+
+ l, err = ParseLevel("FATAL")
+ assert.Nil(t, err)
+ assert.Equal(t, FatalLevel, l)
+
+ l, err = ParseLevel("error")
+ assert.Nil(t, err)
+ assert.Equal(t, ErrorLevel, l)
+
+ l, err = ParseLevel("ERROR")
+ assert.Nil(t, err)
+ assert.Equal(t, ErrorLevel, l)
+
+ l, err = ParseLevel("warn")
+ assert.Nil(t, err)
+ assert.Equal(t, WarnLevel, l)
+
+ l, err = ParseLevel("WARN")
+ assert.Nil(t, err)
+ assert.Equal(t, WarnLevel, l)
+
+ l, err = ParseLevel("warning")
+ assert.Nil(t, err)
+ assert.Equal(t, WarnLevel, l)
+
+ l, err = ParseLevel("WARNING")
+ assert.Nil(t, err)
+ assert.Equal(t, WarnLevel, l)
+
+ l, err = ParseLevel("info")
+ assert.Nil(t, err)
+ assert.Equal(t, InfoLevel, l)
+
+ l, err = ParseLevel("INFO")
+ assert.Nil(t, err)
+ assert.Equal(t, InfoLevel, l)
+
+ l, err = ParseLevel("debug")
+ assert.Nil(t, err)
+ assert.Equal(t, DebugLevel, l)
+
+ l, err = ParseLevel("DEBUG")
+ assert.Nil(t, err)
+ assert.Equal(t, DebugLevel, l)
+
+ l, err = ParseLevel("invalid")
+ assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
+}
+
+func TestGetSetLevelRace(t *testing.T) {
+ wg := sync.WaitGroup{}
+ for i := 0; i < 100; i++ {
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+ if i%2 == 0 {
+ SetLevel(InfoLevel)
+ } else {
+ GetLevel()
+ }
+ }(i)
+
+ }
+ wg.Wait()
+}
+
+func TestLoggingRace(t *testing.T) {
+ logger := New()
+
+ var wg sync.WaitGroup
+ wg.Add(100)
+
+ for i := 0; i < 100; i++ {
+ go func() {
+ logger.Info("info")
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
+
+// Compile test
+func TestLogrusInterface(t *testing.T) {
+ var buffer bytes.Buffer
+ fn := func(l FieldLogger) {
+ b := l.WithField("key", "value")
+ b.Debug("Test")
+ }
+ // test logger
+ logger := New()
+ logger.Out = &buffer
+ fn(logger)
+
+ // test Entry
+ e := logger.WithField("another", "value")
+ fn(e)
+}
+
+// Implements io.Writer using channels for synchronization, so we can wait on
+// the Entry.Writer goroutine to write in a non-racey way. This does assume that
+// there is a single call to Logger.Out for each message.
+type channelWriter chan []byte
+
+func (cw channelWriter) Write(p []byte) (int, error) {
+ cw <- p
+ return len(p), nil
+}
+
+func TestEntryWriter(t *testing.T) {
+ cw := channelWriter(make(chan []byte, 1))
+ log := New()
+ log.Out = cw
+ log.Formatter = new(JSONFormatter)
+ log.WithField("foo", "bar").WriterLevel(WarnLevel).Write([]byte("hello\n"))
+
+ bs := <-cw
+ var fields Fields
+ err := json.Unmarshal(bs, &fields)
+ assert.Nil(t, err)
+ assert.Equal(t, fields["foo"], "bar")
+ assert.Equal(t, fields["level"], "warning")
+}
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_appengine.go b/vendor/github.com/Sirupsen/logrus/terminal_appengine.go
deleted file mode 100644
index 1960169..0000000
--- a/vendor/github.com/Sirupsen/logrus/terminal_appengine.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// +build appengine
-
-package logrus
-
-// IsTerminal returns true if stderr's file descriptor is a terminal.
-func IsTerminal() bool {
- return true
-}
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_bsd.go b/vendor/github.com/Sirupsen/logrus/terminal_bsd.go
index 5f6be4d..d7b3893 100644
--- a/vendor/github.com/Sirupsen/logrus/terminal_bsd.go
+++ b/vendor/github.com/Sirupsen/logrus/terminal_bsd.go
@@ -3,8 +3,8 @@
package logrus
-import "syscall"
+import "golang.org/x/sys/unix"
-const ioctlReadTermios = syscall.TIOCGETA
+const ioctlReadTermios = unix.TIOCGETA
-type Termios syscall.Termios
+type Termios unix.Termios
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_linux.go b/vendor/github.com/Sirupsen/logrus/terminal_linux.go
index 308160c..88d7298 100644
--- a/vendor/github.com/Sirupsen/logrus/terminal_linux.go
+++ b/vendor/github.com/Sirupsen/logrus/terminal_linux.go
@@ -7,8 +7,8 @@
package logrus
-import "syscall"
+import "golang.org/x/sys/unix"
-const ioctlReadTermios = syscall.TCGETS
+const ioctlReadTermios = unix.TCGETS
-type Termios syscall.Termios
+type Termios unix.Termios
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go b/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
deleted file mode 100644
index 329038f..0000000
--- a/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// Based on ssh/terminal:
-// 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.
-
-// +build linux darwin freebsd openbsd netbsd dragonfly
-// +build !appengine
-
-package logrus
-
-import (
- "syscall"
- "unsafe"
-)
-
-// IsTerminal returns true if stderr's file descriptor is a terminal.
-func IsTerminal() bool {
- fd := syscall.Stderr
- var termios Termios
- _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
- return err == 0
-}
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_solaris.go b/vendor/github.com/Sirupsen/logrus/terminal_solaris.go
deleted file mode 100644
index a3c6f6e..0000000
--- a/vendor/github.com/Sirupsen/logrus/terminal_solaris.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// +build solaris,!appengine
-
-package logrus
-
-import (
- "os"
-
- "golang.org/x/sys/unix"
-)
-
-// IsTerminal returns true if the given file descriptor is a terminal.
-func IsTerminal() bool {
- _, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA)
- return err == nil
-}
diff --git a/vendor/github.com/Sirupsen/logrus/terminal_windows.go b/vendor/github.com/Sirupsen/logrus/terminal_windows.go
deleted file mode 100644
index 3727e8a..0000000
--- a/vendor/github.com/Sirupsen/logrus/terminal_windows.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Based on ssh/terminal:
-// 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.
-
-// +build windows,!appengine
-
-package logrus
-
-import (
- "syscall"
- "unsafe"
-)
-
-var kernel32 = syscall.NewLazyDLL("kernel32.dll")
-
-var (
- procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
-)
-
-// IsTerminal returns true if stderr's file descriptor is a terminal.
-func IsTerminal() bool {
- fd := syscall.Stderr
- var st uint32
- r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
- return r != 0 && e == 0
-}
diff --git a/vendor/github.com/Sirupsen/logrus/text_formatter.go b/vendor/github.com/Sirupsen/logrus/text_formatter.go
index 076de5d..be412aa 100644
--- a/vendor/github.com/Sirupsen/logrus/text_formatter.go
+++ b/vendor/github.com/Sirupsen/logrus/text_formatter.go
@@ -3,10 +3,14 @@ package logrus
import (
"bytes"
"fmt"
- "runtime"
+ "io"
+ "os"
"sort"
"strings"
+ "sync"
"time"
+
+ "golang.org/x/crypto/ssh/terminal"
)
const (
@@ -14,20 +18,19 @@ const (
red = 31
green = 32
yellow = 33
- blue = 34
+ blue = 36
gray = 37
)
var (
baseTimestamp time.Time
- isTerminal bool
)
func init() {
baseTimestamp = time.Now()
- isTerminal = IsTerminal()
}
+// TextFormatter formats logs into text
type TextFormatter struct {
// Set to true to bypass checking for a TTY before outputting colors.
ForceColors bool
@@ -50,8 +53,32 @@ type TextFormatter struct {
// that log extremely frequently and don't use the JSON formatter this may not
// be desired.
DisableSorting bool
+
+ // QuoteEmptyFields will wrap empty fields in quotes if true
+ QuoteEmptyFields bool
+
+ // Whether the logger's out is to a terminal
+ isTerminal bool
+
+ sync.Once
+}
+
+func (f *TextFormatter) init(entry *Entry) {
+ if entry.Logger != nil {
+ f.isTerminal = f.checkIfTerminal(entry.Logger.Out)
+ }
+}
+
+func (f *TextFormatter) checkIfTerminal(w io.Writer) bool {
+ switch v := w.(type) {
+ case *os.File:
+ return terminal.IsTerminal(int(v.Fd()))
+ default:
+ return false
+ }
}
+// Format renders a single log entry
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
var b *bytes.Buffer
keys := make([]string, 0, len(entry.Data))
@@ -70,12 +97,13 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
prefixFieldClashes(entry.Data)
- isColorTerminal := isTerminal && (runtime.GOOS != "windows")
- isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors
+ f.Do(func() { f.init(entry) })
+
+ isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
timestampFormat := f.TimestampFormat
if timestampFormat == "" {
- timestampFormat = DefaultTimestampFormat
+ timestampFormat = defaultTimestampFormat
}
if isColored {
f.printColored(b, entry, keys, timestampFormat)
@@ -125,12 +153,15 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
}
}
-func needsQuoting(text string) bool {
+func (f *TextFormatter) needsQuoting(text string) bool {
+ if f.QuoteEmptyFields && len(text) == 0 {
+ return true
+ }
for _, ch := range text {
if !((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') ||
- ch == '-' || ch == '.') {
+ ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') {
return true
}
}
@@ -138,29 +169,23 @@ func needsQuoting(text string) bool {
}
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
-
+ if b.Len() > 0 {
+ b.WriteByte(' ')
+ }
b.WriteString(key)
b.WriteByte('=')
f.appendValue(b, value)
- b.WriteByte(' ')
}
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
- switch value := value.(type) {
- case string:
- if !needsQuoting(value) {
- b.WriteString(value)
- } else {
- fmt.Fprintf(b, "%q", value)
- }
- case error:
- errmsg := value.Error()
- if !needsQuoting(errmsg) {
- b.WriteString(errmsg)
- } else {
- fmt.Fprintf(b, "%q", errmsg)
- }
- default:
- fmt.Fprint(b, value)
+ stringVal, ok := value.(string)
+ if !ok {
+ stringVal = fmt.Sprint(value)
+ }
+
+ if !f.needsQuoting(stringVal) {
+ b.WriteString(stringVal)
+ } else {
+ b.WriteString(fmt.Sprintf("%q", stringVal))
}
}
diff --git a/vendor/github.com/Sirupsen/logrus/text_formatter_test.go b/vendor/github.com/Sirupsen/logrus/text_formatter_test.go
new file mode 100644
index 0000000..d93b931
--- /dev/null
+++ b/vendor/github.com/Sirupsen/logrus/text_formatter_test.go
@@ -0,0 +1,141 @@
+package logrus
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "strings"
+ "testing"
+ "time"
+)
+
+func TestFormatting(t *testing.T) {
+ tf := &TextFormatter{DisableColors: true}
+
+ testCases := []struct {
+ value string
+ expected string
+ }{
+ {`foo`, "time=\"0001-01-01T00:00:00Z\" level=panic test=foo\n"},
+ }
+
+ for _, tc := range testCases {
+ b, _ := tf.Format(WithField("test", tc.value))
+
+ if string(b) != tc.expected {
+ t.Errorf("formatting expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected)
+ }
+ }
+}
+
+func TestQuoting(t *testing.T) {
+ tf := &TextFormatter{DisableColors: true}
+
+ checkQuoting := func(q bool, value interface{}) {
+ b, _ := tf.Format(WithField("test", value))
+ idx := bytes.Index(b, ([]byte)("test="))
+ cont := bytes.Contains(b[idx+5:], []byte("\""))
+ if cont != q {
+ if q {
+ t.Errorf("quoting expected for: %#v", value)
+ } else {
+ t.Errorf("quoting not expected for: %#v", value)
+ }
+ }
+ }
+
+ checkQuoting(false, "")
+ checkQuoting(false, "abcd")
+ checkQuoting(false, "v1.0")
+ checkQuoting(false, "1234567890")
+ checkQuoting(false, "/foobar")
+ checkQuoting(false, "foo_bar")
+ checkQuoting(false, "foo@bar")
+ checkQuoting(false, "foobar^")
+ checkQuoting(false, "+/-_^@f.oobar")
+ checkQuoting(true, "foobar$")
+ checkQuoting(true, "&foobar")
+ checkQuoting(true, "x y")
+ checkQuoting(true, "x,y")
+ checkQuoting(false, errors.New("invalid"))
+ checkQuoting(true, errors.New("invalid argument"))
+
+ // Test for quoting empty fields.
+ tf.QuoteEmptyFields = true
+ checkQuoting(true, "")
+ checkQuoting(false, "abcd")
+ checkQuoting(true, errors.New("invalid argument"))
+}
+
+func TestEscaping(t *testing.T) {
+ tf := &TextFormatter{DisableColors: true}
+
+ testCases := []struct {
+ value string
+ expected string
+ }{
+ {`ba"r`, `ba\"r`},
+ {`ba'r`, `ba'r`},
+ }
+
+ for _, tc := range testCases {
+ b, _ := tf.Format(WithField("test", tc.value))
+ if !bytes.Contains(b, []byte(tc.expected)) {
+ t.Errorf("escaping expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected)
+ }
+ }
+}
+
+func TestEscaping_Interface(t *testing.T) {
+ tf := &TextFormatter{DisableColors: true}
+
+ ts := time.Now()
+
+ testCases := []struct {
+ value interface{}
+ expected string
+ }{
+ {ts, fmt.Sprintf("\"%s\"", ts.String())},
+ {errors.New("error: something went wrong"), "\"error: something went wrong\""},
+ }
+
+ for _, tc := range testCases {
+ b, _ := tf.Format(WithField("test", tc.value))
+ if !bytes.Contains(b, []byte(tc.expected)) {
+ t.Errorf("escaping expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected)
+ }
+ }
+}
+
+func TestTimestampFormat(t *testing.T) {
+ checkTimeStr := func(format string) {
+ customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format}
+ customStr, _ := customFormatter.Format(WithField("test", "test"))
+ timeStart := bytes.Index(customStr, ([]byte)("time="))
+ timeEnd := bytes.Index(customStr, ([]byte)("level="))
+ timeStr := customStr[timeStart+5+len("\"") : timeEnd-1-len("\"")]
+ if format == "" {
+ format = time.RFC3339
+ }
+ _, e := time.Parse(format, (string)(timeStr))
+ if e != nil {
+ t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e)
+ }
+ }
+
+ checkTimeStr("2006-01-02T15:04:05.000000000Z07:00")
+ checkTimeStr("Mon Jan _2 15:04:05 2006")
+ checkTimeStr("")
+}
+
+func TestDisableTimestampWithColoredOutput(t *testing.T) {
+ tf := &TextFormatter{DisableTimestamp: true, ForceColors: true}
+
+ b, _ := tf.Format(WithField("test", "test"))
+ if strings.Contains(string(b), "[0000]") {
+ t.Error("timestamp not expected when DisableTimestamp is true")
+ }
+}
+
+// TODO add tests for sorting etc., this requires a parser for the text
+// formatter output.
diff --git a/vendor/github.com/Sirupsen/logrus/writer.go b/vendor/github.com/Sirupsen/logrus/writer.go
index f74d2aa..7bdebed 100644
--- a/vendor/github.com/Sirupsen/logrus/writer.go
+++ b/vendor/github.com/Sirupsen/logrus/writer.go
@@ -11,39 +11,48 @@ func (logger *Logger) Writer() *io.PipeWriter {
}
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
+ return NewEntry(logger).WriterLevel(level)
+}
+
+func (entry *Entry) Writer() *io.PipeWriter {
+ return entry.WriterLevel(InfoLevel)
+}
+
+func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
reader, writer := io.Pipe()
var printFunc func(args ...interface{})
+
switch level {
case DebugLevel:
- printFunc = logger.Debug
+ printFunc = entry.Debug
case InfoLevel:
- printFunc = logger.Info
+ printFunc = entry.Info
case WarnLevel:
- printFunc = logger.Warn
+ printFunc = entry.Warn
case ErrorLevel:
- printFunc = logger.Error
+ printFunc = entry.Error
case FatalLevel:
- printFunc = logger.Fatal
+ printFunc = entry.Fatal
case PanicLevel:
- printFunc = logger.Panic
+ printFunc = entry.Panic
default:
- printFunc = logger.Print
+ printFunc = entry.Print
}
- go logger.writerScanner(reader, printFunc)
+ go entry.writerScanner(reader, printFunc)
runtime.SetFinalizer(writer, writerFinalizer)
return writer
}
-func (logger *Logger) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
+func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
printFunc(scanner.Text())
}
if err := scanner.Err(); err != nil {
- logger.Errorf("Error while reading from Writer: %s", err)
+ entry.Errorf("Error while reading from Writer: %s", err)
}
reader.Close()
}
diff --git a/vendor/github.com/miekg/dns/.travis.yml b/vendor/github.com/miekg/dns/.travis.yml
index 11bc915..542dd68 100644
--- a/vendor/github.com/miekg/dns/.travis.yml
+++ b/vendor/github.com/miekg/dns/.travis.yml
@@ -1,8 +1,12 @@
language: go
sudo: false
go:
- - 1.6
- - 1.7
+ - 1.9.x
+ - tip
+
+env:
+ - TESTS="-race -v -bench=. -coverprofile=coverage.txt -covermode=atomic"
+ - TESTS="-race -v ./..."
before_install:
# don't use the miekg/dns when testing forks
@@ -10,4 +14,7 @@ before_install:
- ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true
script:
- - go test -race -v -bench=.
+ - go test $TESTS
+
+after_success:
+ - bash <(curl -s https://codecov.io/bash)
diff --git a/vendor/github.com/miekg/dns/CONTRIBUTORS b/vendor/github.com/miekg/dns/CONTRIBUTORS
index f77e8a8..5903779 100644
--- a/vendor/github.com/miekg/dns/CONTRIBUTORS
+++ b/vendor/github.com/miekg/dns/CONTRIBUTORS
@@ -7,3 +7,4 @@ Marek Majkowski
Peter van Dijk
Omri Bahumi
Alex Sergeyev
+James Hartig
diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md
index 0c1f1b6..94b54fe 100644
--- a/vendor/github.com/miekg/dns/README.md
+++ b/vendor/github.com/miekg/dns/README.md
@@ -1,30 +1,31 @@
[![Build Status](https://travis-ci.org/miekg/dns.svg?branch=master)](https://travis-ci.org/miekg/dns)
+[![Code Coverage](https://img.shields.io/codecov/c/github/miekg/dns/master.svg)](https://codecov.io/github/miekg/dns?branch=master)
+[![Go Report Card](https://goreportcard.com/badge/github.com/miekg/dns)](https://goreportcard.com/report/miekg/dns)
[![](https://godoc.org/github.com/miekg/dns?status.svg)](https://godoc.org/github.com/miekg/dns)
# Alternative (more granular) approach to a DNS library
> Less is more.
-Complete and usable DNS library. All widely used Resource Records are
-supported, including the DNSSEC types. It follows a lean and mean philosophy.
-If there is stuff you should know as a DNS programmer there isn't a convenience
-function for it. Server side and client side programming is supported, i.e. you
-can build servers and resolvers with it.
+Complete and usable DNS library. All widely used Resource Records are supported, including the
+DNSSEC types. It follows a lean and mean philosophy. If there is stuff you should know as a DNS
+programmer there isn't a convenience function for it. Server side and client side programming is
+supported, i.e. you can build servers and resolvers with it.
-We try to keep the "master" branch as sane as possible and at the bleeding edge
-of standards, avoiding breaking changes wherever reasonable. We support the last
-two versions of Go, currently: 1.6 and 1.7.
+We try to keep the "master" branch as sane as possible and at the bleeding edge of standards,
+avoiding breaking changes wherever reasonable. We support the last two versions of Go.
# Goals
* KISS;
* Fast;
-* Small API, if its easy to code in Go, don't make a function for it.
+* Small API. If it's easy to code in Go, don't make a function for it.
# Users
A not-so-up-to-date-list-that-may-be-actually-current:
+* https://github.com/coredns/coredns
* https://cloudflare.com
* https://github.com/abh/geodns
* http://www.statdns.com/
@@ -54,6 +55,13 @@ A not-so-up-to-date-list-that-may-be-actually-current:
* https://github.com/mehrdadrad/mylg
* https://github.com/bamarni/dockness
* https://github.com/fffaraz/microdns
+* http://kelda.io
+* https://github.com/ipdcode/hades (JD.COM)
+* https://github.com/StackExchange/dnscontrol/
+* https://www.dnsperf.com/
+* https://dnssectest.net/
+* https://dns.apebits.com
+* https://github.com/oif/apex
Send pull request if you want to be listed here.
@@ -80,8 +88,8 @@ Miek Gieben - 2010-2012 -
# Building
-Building is done with the `go` tool. If you have setup your GOPATH
-correctly, the following should work:
+Building is done with the `go` tool. If you have setup your GOPATH correctly, the following should
+work:
go get github.com/miekg/dns
go build github.com/miekg/dns
@@ -142,10 +150,10 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
* 6975 - Algorithm Understanding in DNSSEC
* 7043 - EUI48/EUI64 records
* 7314 - DNS (EDNS) EXPIRE Option
+* 7828 - edns-tcp-keepalive EDNS0 Option
* 7553 - URI record
-* 7858 - DNS over TLS: Initiation and Performance Considerations (draft)
+* 7858 - DNS over TLS: Initiation and Performance Considerations
* 7873 - Domain Name System (DNS) Cookies (draft-ietf-dnsop-cookies)
-* xxxx - EDNS0 DNS Update Lease (draft)
## Loosely based upon
diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go
index 0db7f7b..2c51588 100644
--- a/vendor/github.com/miekg/dns/client.go
+++ b/vendor/github.com/miekg/dns/client.go
@@ -4,10 +4,12 @@ package dns
import (
"bytes"
+ "context"
"crypto/tls"
"encoding/binary"
"io"
"net"
+ "strings"
"time"
)
@@ -18,7 +20,7 @@ const tcpIdleTimeout time.Duration = 8 * time.Second
type Conn struct {
net.Conn // a net.Conn holding the connection
UDPSize uint16 // minimum receive buffer for UDP messages
- TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be fully qualified
+ TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
rtt time.Duration
t time.Time
tsigRequestMAC string
@@ -26,14 +28,18 @@ type Conn struct {
// A Client defines parameters for a DNS client.
type Client struct {
- Net string // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
- UDPSize uint16 // minimum receive buffer for UDP messages
- TLSConfig *tls.Config // TLS connection configuration
- Timeout time.Duration // a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout and WriteTimeout when non-zero
- DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds - overridden by Timeout when that value is non-zero
+ Net string // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
+ UDPSize uint16 // minimum receive buffer for UDP messages
+ TLSConfig *tls.Config // TLS connection configuration
+ Dialer *net.Dialer // a net.Dialer used to set local address, timeouts and more
+ // Timeout is a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout,
+ // WriteTimeout when non-zero. Can be overridden with net.Dialer.Timeout (see Client.ExchangeWithDialer and
+ // Client.Dialer) or context.Context.Deadline (see the deprecated ExchangeContext)
+ Timeout time.Duration
+ DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
- TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be fully qualified
+ TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
group singleflight
}
@@ -43,93 +49,11 @@ type Client struct {
// will it fall back to TCP in case of truncation.
// See client.Exchange for more information on setting larger buffer sizes.
func Exchange(m *Msg, a string) (r *Msg, err error) {
- var co *Conn
- co, err = DialTimeout("udp", a, dnsTimeout)
- if err != nil {
- return nil, err
- }
-
- defer co.Close()
-
- opt := m.IsEdns0()
- // If EDNS0 is used use that for size.
- if opt != nil && opt.UDPSize() >= MinMsgSize {
- co.UDPSize = opt.UDPSize()
- }
-
- co.SetWriteDeadline(time.Now().Add(dnsTimeout))
- if err = co.WriteMsg(m); err != nil {
- return nil, err
- }
-
- co.SetReadDeadline(time.Now().Add(dnsTimeout))
- r, err = co.ReadMsg()
- if err == nil && r.Id != m.Id {
- err = ErrId
- }
- return r, err
-}
-
-// ExchangeConn performs a synchronous query. It sends the message m via the connection
-// c and waits for a reply. The connection c is not closed by ExchangeConn.
-// This function is going away, but can easily be mimicked:
-//
-// co := &dns.Conn{Conn: c} // c is your net.Conn
-// co.WriteMsg(m)
-// in, _ := co.ReadMsg()
-// co.Close()
-//
-func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) {
- println("dns: this function is deprecated")
- co := new(Conn)
- co.Conn = c
- if err = co.WriteMsg(m); err != nil {
- return nil, err
- }
- r, err = co.ReadMsg()
- if err == nil && r.Id != m.Id {
- err = ErrId
- }
+ client := Client{Net: "udp"}
+ r, _, err = client.Exchange(m, a)
return r, err
}
-// Exchange performs a synchronous query. It sends the message m to the address
-// contained in a and waits for a reply. Basic use pattern with a *dns.Client:
-//
-// c := new(dns.Client)
-// in, rtt, err := c.Exchange(message, "127.0.0.1:53")
-//
-// Exchange does not retry a failed query, nor will it fall back to TCP in
-// case of truncation.
-// It is up to the caller to create a message that allows for larger responses to be
-// returned. Specifically this means adding an EDNS0 OPT RR that will advertise a larger
-// buffer, see SetEdns0. Messsages without an OPT RR will fallback to the historic limit
-// of 512 bytes.
-func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
- if !c.SingleInflight {
- return c.exchange(m, a)
- }
- // This adds a bunch of garbage, TODO(miek).
- t := "nop"
- if t1, ok := TypeToString[m.Question[0].Qtype]; ok {
- t = t1
- }
- cl := "nop"
- if cl1, ok := ClassToString[m.Question[0].Qclass]; ok {
- cl = cl1
- }
- r, rtt, err, shared := c.group.Do(m.Question[0].Name+t+cl, func() (*Msg, time.Duration, error) {
- return c.exchange(m, a)
- })
- if err != nil {
- return r, rtt, err
- }
- if shared {
- return r.Copy(), rtt, nil
- }
- return r, rtt, nil
-}
-
func (c *Client) dialTimeout() time.Duration {
if c.Timeout != 0 {
return c.Timeout
@@ -154,37 +78,88 @@ func (c *Client) writeTimeout() time.Duration {
return dnsTimeout
}
-func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
- var co *Conn
+// Dial connects to the address on the named network.
+func (c *Client) Dial(address string) (conn *Conn, err error) {
+ // create a new dialer with the appropriate timeout
+ var d net.Dialer
+ if c.Dialer == nil {
+ d = net.Dialer{}
+ } else {
+ d = net.Dialer(*c.Dialer)
+ }
+ d.Timeout = c.getTimeoutForRequest(c.writeTimeout())
+
network := "udp"
- tls := false
+ useTLS := false
switch c.Net {
case "tcp-tls":
network = "tcp"
- tls = true
+ useTLS = true
case "tcp4-tls":
network = "tcp4"
- tls = true
+ useTLS = true
case "tcp6-tls":
network = "tcp6"
- tls = true
+ useTLS = true
default:
if c.Net != "" {
network = c.Net
}
}
- var deadline time.Time
- if c.Timeout != 0 {
- deadline = time.Now().Add(c.Timeout)
+ conn = new(Conn)
+ if useTLS {
+ conn.Conn, err = tls.DialWithDialer(&d, network, address, c.TLSConfig)
+ } else {
+ conn.Conn, err = d.Dial(network, address)
}
+ if err != nil {
+ return nil, err
+ }
+ return conn, nil
+}
- if tls {
- co, err = DialTimeoutWithTLS(network, a, c.TLSConfig, c.dialTimeout())
- } else {
- co, err = DialTimeout(network, a, c.dialTimeout())
+// Exchange performs a synchronous query. It sends the message m to the address
+// contained in a and waits for a reply. Basic use pattern with a *dns.Client:
+//
+// c := new(dns.Client)
+// in, rtt, err := c.Exchange(message, "127.0.0.1:53")
+//
+// Exchange does not retry a failed query, nor will it fall back to TCP in
+// case of truncation.
+// It is up to the caller to create a message that allows for larger responses to be
+// returned. Specifically this means adding an EDNS0 OPT RR that will advertise a larger
+// buffer, see SetEdns0. Messages without an OPT RR will fallback to the historic limit
+// of 512 bytes
+// To specify a local address or a timeout, the caller has to set the `Client.Dialer`
+// attribute appropriately
+func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, err error) {
+ if !c.SingleInflight {
+ return c.exchange(m, address)
+ }
+
+ t := "nop"
+ if t1, ok := TypeToString[m.Question[0].Qtype]; ok {
+ t = t1
+ }
+ cl := "nop"
+ if cl1, ok := ClassToString[m.Question[0].Qclass]; ok {
+ cl = cl1
}
+ r, rtt, err, shared := c.group.Do(m.Question[0].Name+t+cl, func() (*Msg, time.Duration, error) {
+ return c.exchange(m, address)
+ })
+ if r != nil && shared {
+ r = r.Copy()
+ }
+ return r, rtt, err
+}
+
+func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
+ var co *Conn
+
+ co, err = c.Dial(a)
if err != nil {
return nil, 0, err
@@ -202,12 +177,13 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
}
co.TsigSecret = c.TsigSecret
- co.SetWriteDeadline(deadlineOrTimeout(deadline, c.writeTimeout()))
+ // write with the appropriate write timeout
+ co.SetWriteDeadline(time.Now().Add(c.getTimeoutForRequest(c.writeTimeout())))
if err = co.WriteMsg(m); err != nil {
return nil, 0, err
}
- co.SetReadDeadline(deadlineOrTimeout(deadline, c.readTimeout()))
+ co.SetReadDeadline(time.Now().Add(c.getTimeoutForRequest(c.readTimeout())))
r, err = co.ReadMsg()
if err == nil && r.Id != m.Id {
err = ErrId
@@ -300,6 +276,18 @@ func tcpMsgLen(t io.Reader) (int, error) {
if err != nil {
return 0, err
}
+
+ // As seen with my local router/switch, returns 1 byte on the above read,
+ // resulting a a ShortRead. Just write it out (instead of loop) and read the
+ // other byte.
+ if n == 1 {
+ n1, err := t.Read(p[1:])
+ if err != nil {
+ return 0, err
+ }
+ n += n1
+ }
+
if n != 2 {
return 0, ErrShortRead
}
@@ -400,10 +388,28 @@ func (co *Conn) Write(p []byte) (n int, err error) {
n, err := io.Copy(w, bytes.NewReader(p))
return int(n), err
}
- n, err = co.Conn.(*net.UDPConn).Write(p)
+ n, err = co.Conn.Write(p)
return n, err
}
+// Return the appropriate timeout for a specific request
+func (c *Client) getTimeoutForRequest(timeout time.Duration) time.Duration {
+ var requestTimeout time.Duration
+ if c.Timeout != 0 {
+ requestTimeout = c.Timeout
+ } else {
+ requestTimeout = timeout
+ }
+ // net.Dialer.Timeout has priority if smaller than the timeouts computed so
+ // far
+ if c.Dialer != nil && c.Dialer.Timeout != 0 {
+ if c.Dialer.Timeout < requestTimeout {
+ requestTimeout = c.Dialer.Timeout
+ }
+ }
+ return requestTimeout
+}
+
// Dial connects to the address on the named network.
func Dial(network, address string) (conn *Conn, err error) {
conn = new(Conn)
@@ -414,10 +420,43 @@ func Dial(network, address string) (conn *Conn, err error) {
return conn, nil
}
+// ExchangeContext performs a synchronous UDP query, like Exchange. It
+// additionally obeys deadlines from the passed Context.
+func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) {
+ client := Client{Net: "udp"}
+ r, _, err = client.ExchangeContext(ctx, m, a)
+ // ignorint rtt to leave the original ExchangeContext API unchanged, but
+ // this function will go away
+ return r, err
+}
+
+// ExchangeConn performs a synchronous query. It sends the message m via the connection
+// c and waits for a reply. The connection c is not closed by ExchangeConn.
+// This function is going away, but can easily be mimicked:
+//
+// co := &dns.Conn{Conn: c} // c is your net.Conn
+// co.WriteMsg(m)
+// in, _ := co.ReadMsg()
+// co.Close()
+//
+func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) {
+ println("dns: ExchangeConn: this function is deprecated")
+ co := new(Conn)
+ co.Conn = c
+ if err = co.WriteMsg(m); err != nil {
+ return nil, err
+ }
+ r, err = co.ReadMsg()
+ if err == nil && r.Id != m.Id {
+ err = ErrId
+ }
+ return r, err
+}
+
// DialTimeout acts like Dial but takes a timeout.
func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) {
- conn = new(Conn)
- conn.Conn, err = net.DialTimeout(network, address, timeout)
+ client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}}
+ conn, err = client.Dial(address)
if err != nil {
return nil, err
}
@@ -426,8 +465,12 @@ func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, er
// DialWithTLS connects to the address on the named network with TLS.
func DialWithTLS(network, address string, tlsConfig *tls.Config) (conn *Conn, err error) {
- conn = new(Conn)
- conn.Conn, err = tls.Dial(network, address, tlsConfig)
+ if !strings.HasSuffix(network, "-tls") {
+ network += "-tls"
+ }
+ client := Client{Net: network, TLSConfig: tlsConfig}
+ conn, err = client.Dial(address)
+
if err != nil {
return nil, err
}
@@ -436,20 +479,29 @@ func DialWithTLS(network, address string, tlsConfig *tls.Config) (conn *Conn, er
// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout.
func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout time.Duration) (conn *Conn, err error) {
- var dialer net.Dialer
- dialer.Timeout = timeout
-
- conn = new(Conn)
- conn.Conn, err = tls.DialWithDialer(&dialer, network, address, tlsConfig)
+ if !strings.HasSuffix(network, "-tls") {
+ network += "-tls"
+ }
+ client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}, TLSConfig: tlsConfig}
+ conn, err = client.Dial(address)
if err != nil {
return nil, err
}
return conn, nil
}
-func deadlineOrTimeout(deadline time.Time, timeout time.Duration) time.Time {
- if deadline.IsZero() {
- return time.Now().Add(timeout)
+// ExchangeContext acts like Exchange, but honors the deadline on the provided
+// context, if present. If there is both a context deadline and a configured
+// timeout on the client, the earliest of the two takes effect.
+func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
+ var timeout time.Duration
+ if deadline, ok := ctx.Deadline(); !ok {
+ timeout = 0
+ } else {
+ timeout = deadline.Sub(time.Now())
}
- return deadline
+ // not passing the context to the underlying calls, as the API does not support
+ // context. For timeouts you should set up Client.Dialer and call Client.Exchange.
+ c.Dialer = &net.Dialer{Timeout: timeout}
+ return c.Exchange(m, a)
}
diff --git a/vendor/github.com/miekg/dns/client_test.go b/vendor/github.com/miekg/dns/client_test.go
new file mode 100644
index 0000000..a3bbfdb
--- /dev/null
+++ b/vendor/github.com/miekg/dns/client_test.go
@@ -0,0 +1,590 @@
+package dns
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ "net"
+ "strconv"
+ "strings"
+ "sync"
+ "testing"
+ "time"
+)
+
+func TestDialUDP(t *testing.T) {
+ HandleFunc("miek.nl.", HelloServer)
+ defer HandleRemove("miek.nl.")
+
+ s, addrstr, err := RunLocalUDPServer(":0")
+ if err != nil {
+ t.Fatalf("unable to run test server: %v", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeSOA)
+
+ c := new(Client)
+ conn, err := c.Dial(addrstr)
+ if err != nil {
+ t.Fatalf("failed to dial: %v", err)
+ }
+ if conn == nil {
+ t.Fatalf("conn is nil")
+ }
+}
+
+func TestClientSync(t *testing.T) {
+ HandleFunc("miek.nl.", HelloServer)
+ defer HandleRemove("miek.nl.")
+
+ s, addrstr, err := RunLocalUDPServer(":0")
+ if err != nil {
+ t.Fatalf("unable to run test server: %v", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeSOA)
+
+ c := new(Client)
+ r, _, err := c.Exchange(m, addrstr)
+ if err != nil {
+ t.Fatalf("failed to exchange: %v", err)
+ }
+ if r == nil {
+ t.Fatal("response is nil")
+ }
+ if r.Rcode != RcodeSuccess {
+ t.Errorf("failed to get an valid answer\n%v", r)
+ }
+ // And now with plain Exchange().
+ r, err = Exchange(m, addrstr)
+ if err != nil {
+ t.Errorf("failed to exchange: %v", err)
+ }
+ if r == nil || r.Rcode != RcodeSuccess {
+ t.Errorf("failed to get an valid answer\n%v", r)
+ }
+}
+
+func TestClientLocalAddress(t *testing.T) {
+ HandleFunc("miek.nl.", HelloServerEchoAddrPort)
+ defer HandleRemove("miek.nl.")
+
+ s, addrstr, err := RunLocalUDPServer(":0")
+ if err != nil {
+ t.Fatalf("unable to run test server: %v", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeSOA)
+
+ c := new(Client)
+ laddr := net.UDPAddr{IP: net.ParseIP("0.0.0.0"), Port: 12345, Zone: ""}
+ c.Dialer = &net.Dialer{LocalAddr: &laddr}
+ r, _, err := c.Exchange(m, addrstr)
+ if err != nil {
+ t.Fatalf("failed to exchange: %v", err)
+ }
+ if r != nil && r.Rcode != RcodeSuccess {
+ t.Errorf("failed to get an valid answer\n%v", r)
+ }
+ if len(r.Extra) != 1 {
+ t.Errorf("failed to get additional answers\n%v", r)
+ }
+ txt := r.Extra[0].(*TXT)
+ if txt == nil {
+ t.Errorf("invalid TXT response\n%v", txt)
+ }
+ if len(txt.Txt) != 1 || !strings.Contains(txt.Txt[0], ":12345") {
+ t.Errorf("invalid TXT response\n%v", txt.Txt)
+ }
+}
+
+func TestClientTLSSyncV4(t *testing.T) {
+ HandleFunc("miek.nl.", HelloServer)
+ defer HandleRemove("miek.nl.")
+
+ cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)
+ if err != nil {
+ t.Fatalf("unable to build certificate: %v", err)
+ }
+
+ config := tls.Config{
+ Certificates: []tls.Certificate{cert},
+ }
+
+ s, addrstr, err := RunLocalTLSServer(":0", &config)
+ if err != nil {
+ t.Fatalf("unable to run test server: %v", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeSOA)
+
+ c := new(Client)
+
+ // test tcp-tls
+ c.Net = "tcp-tls"
+ c.TLSConfig = &tls.Config{
+ InsecureSkipVerify: true,
+ }
+
+ r, _, err := c.Exchange(m, addrstr)
+ if err != nil {
+ t.Fatalf("failed to exchange: %v", err)
+ }
+ if r == nil {
+ t.Fatal("response is nil")
+ }
+ if r.Rcode != RcodeSuccess {
+ t.Errorf("failed to get an valid answer\n%v", r)
+ }
+
+ // test tcp4-tls
+ c.Net = "tcp4-tls"
+ c.TLSConfig = &tls.Config{
+ InsecureSkipVerify: true,
+ }
+
+ r, _, err = c.Exchange(m, addrstr)
+ if err != nil {
+ t.Fatalf("failed to exchange: %v", err)
+ }
+ if r == nil {
+ t.Fatal("response is nil")
+ }
+ if r.Rcode != RcodeSuccess {
+ t.Errorf("failed to get an valid answer\n%v", r)
+ }
+}
+
+func TestClientSyncBadID(t *testing.T) {
+ HandleFunc("miek.nl.", HelloServerBadID)
+ defer HandleRemove("miek.nl.")
+
+ s, addrstr, err := RunLocalUDPServer(":0")
+ if err != nil {
+ t.Fatalf("unable to run test server: %v", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeSOA)
+
+ c := new(Client)
+ if _, _, err := c.Exchange(m, addrstr); err != ErrId {
+ t.Errorf("did not find a bad Id")
+ }
+ // And now with plain Exchange().
+ if _, err := Exchange(m, addrstr); err != ErrId {
+ t.Errorf("did not find a bad Id")
+ }
+}
+
+func TestClientEDNS0(t *testing.T) {
+ HandleFunc("miek.nl.", HelloServer)
+ defer HandleRemove("miek.nl.")
+
+ s, addrstr, err := RunLocalUDPServer(":0")
+ if err != nil {
+ t.Fatalf("unable to run test server: %v", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeDNSKEY)
+
+ m.SetEdns0(2048, true)
+
+ c := new(Client)
+ r, _, err := c.Exchange(m, addrstr)
+ if err != nil {
+ t.Fatalf("failed to exchange: %v", err)
+ }
+
+ if r != nil && r.Rcode != RcodeSuccess {
+ t.Errorf("failed to get a valid answer\n%v", r)
+ }
+}
+
+// Validates the transmission and parsing of local EDNS0 options.
+func TestClientEDNS0Local(t *testing.T) {
+ optStr1 := "1979:0x0707"
+ optStr2 := strconv.Itoa(EDNS0LOCALSTART) + ":0x0601"
+
+ handler := func(w ResponseWriter, req *Msg) {
+ m := new(Msg)
+ m.SetReply(req)
+
+ m.Extra = make([]RR, 1, 2)
+ m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello local edns"}}
+
+ // If the local options are what we expect, then reflect them back.
+ ec1 := req.Extra[0].(*OPT).Option[0].(*EDNS0_LOCAL).String()
+ ec2 := req.Extra[0].(*OPT).Option[1].(*EDNS0_LOCAL).String()
+ if ec1 == optStr1 && ec2 == optStr2 {
+ m.Extra = append(m.Extra, req.Extra[0])
+ }
+
+ w.WriteMsg(m)
+ }
+
+ HandleFunc("miek.nl.", handler)
+ defer HandleRemove("miek.nl.")
+
+ s, addrstr, err := RunLocalUDPServer(":0")
+ if err != nil {
+ t.Fatalf("unable to run test server: %s", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeTXT)
+
+ // Add two local edns options to the query.
+ ec1 := &EDNS0_LOCAL{Code: 1979, Data: []byte{7, 7}}
+ ec2 := &EDNS0_LOCAL{Code: EDNS0LOCALSTART, Data: []byte{6, 1}}
+ o := &OPT{Hdr: RR_Header{Name: ".", Rrtype: TypeOPT}, Option: []EDNS0{ec1, ec2}}
+ m.Extra = append(m.Extra, o)
+
+ c := new(Client)
+ r, _, err := c.Exchange(m, addrstr)
+ if err != nil {
+ t.Fatalf("failed to exchange: %s", err)
+ }
+
+ if r == nil {
+ t.Fatal("response is nil")
+ }
+ if r.Rcode != RcodeSuccess {
+ t.Fatal("failed to get a valid answer")
+ }
+
+ txt := r.Extra[0].(*TXT).Txt[0]
+ if txt != "Hello local edns" {
+ t.Error("Unexpected result for miek.nl", txt, "!= Hello local edns")
+ }
+
+ // Validate the local options in the reply.
+ got := r.Extra[1].(*OPT).Option[0].(*EDNS0_LOCAL).String()
+ if got != optStr1 {
+ t.Errorf("failed to get local edns0 answer; got %s, expected %s", got, optStr1)
+ }
+
+ got = r.Extra[1].(*OPT).Option[1].(*EDNS0_LOCAL).String()
+ if got != optStr2 {
+ t.Errorf("failed to get local edns0 answer; got %s, expected %s", got, optStr2)
+ }
+}
+
+func TestClientConn(t *testing.T) {
+ HandleFunc("miek.nl.", HelloServer)
+ defer HandleRemove("miek.nl.")
+
+ // This uses TCP just to make it slightly different than TestClientSync
+ s, addrstr, err := RunLocalTCPServer(":0")
+ if err != nil {
+ t.Fatalf("unable to run test server: %v", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeSOA)
+
+ cn, err := Dial("tcp", addrstr)
+ if err != nil {
+ t.Errorf("failed to dial %s: %v", addrstr, err)
+ }
+
+ err = cn.WriteMsg(m)
+ if err != nil {
+ t.Errorf("failed to exchange: %v", err)
+ }
+ r, err := cn.ReadMsg()
+ if err != nil {
+ t.Errorf("failed to get a valid answer: %v", err)
+ }
+ if r == nil || r.Rcode != RcodeSuccess {
+ t.Errorf("failed to get an valid answer\n%v", r)
+ }
+
+ err = cn.WriteMsg(m)
+ if err != nil {
+ t.Errorf("failed to exchange: %v", err)
+ }
+ h := new(Header)
+ buf, err := cn.ReadMsgHeader(h)
+ if buf == nil {
+ t.Errorf("failed to get an valid answer\n%v", r)
+ }
+ if err != nil {
+ t.Errorf("failed to get a valid answer: %v", err)
+ }
+ if int(h.Bits&0xF) != RcodeSuccess {
+ t.Errorf("failed to get an valid answer in ReadMsgHeader\n%v", r)
+ }
+ if h.Ancount != 0 || h.Qdcount != 1 || h.Nscount != 0 || h.Arcount != 1 {
+ t.Errorf("expected to have question and additional in response; got something else: %+v", h)
+ }
+ if err = r.Unpack(buf); err != nil {
+ t.Errorf("unable to unpack message fully: %v", err)
+ }
+}
+
+func TestTruncatedMsg(t *testing.T) {
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeSRV)
+ cnt := 10
+ for i := 0; i < cnt; i++ {
+ r := &SRV{
+ Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeSRV, Class: ClassINET, Ttl: 0},
+ Port: uint16(i + 8000),
+ Target: "target.miek.nl.",
+ }
+ m.Answer = append(m.Answer, r)
+
+ re := &A{
+ Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeA, Class: ClassINET, Ttl: 0},
+ A: net.ParseIP(fmt.Sprintf("127.0.0.%d", i)).To4(),
+ }
+ m.Extra = append(m.Extra, re)
+ }
+ buf, err := m.Pack()
+ if err != nil {
+ t.Errorf("failed to pack: %v", err)
+ }
+
+ r := new(Msg)
+ if err = r.Unpack(buf); err != nil {
+ t.Errorf("unable to unpack message: %v", err)
+ }
+ if len(r.Answer) != cnt {
+ t.Errorf("answer count after regular unpack doesn't match: %d", len(r.Answer))
+ }
+ if len(r.Extra) != cnt {
+ t.Errorf("extra count after regular unpack doesn't match: %d", len(r.Extra))
+ }
+
+ m.Truncated = true
+ buf, err = m.Pack()
+ if err != nil {
+ t.Errorf("failed to pack truncated: %v", err)
+ }
+
+ r = new(Msg)
+ if err = r.Unpack(buf); err != nil && err != ErrTruncated {
+ t.Errorf("unable to unpack truncated message: %v", err)
+ }
+ if !r.Truncated {
+ t.Errorf("truncated message wasn't unpacked as truncated")
+ }
+ if len(r.Answer) != cnt {
+ t.Errorf("answer count after truncated unpack doesn't match: %d", len(r.Answer))
+ }
+ if len(r.Extra) != cnt {
+ t.Errorf("extra count after truncated unpack doesn't match: %d", len(r.Extra))
+ }
+
+ // Now we want to remove almost all of the extra records
+ // We're going to loop over the extra to get the count of the size of all
+ // of them
+ off := 0
+ buf1 := make([]byte, m.Len())
+ for i := 0; i < len(m.Extra); i++ {
+ off, err = PackRR(m.Extra[i], buf1, off, nil, m.Compress)
+ if err != nil {
+ t.Errorf("failed to pack extra: %v", err)
+ }
+ }
+
+ // Remove all of the extra bytes but 10 bytes from the end of buf
+ off -= 10
+ buf1 = buf[:len(buf)-off]
+
+ r = new(Msg)
+ if err = r.Unpack(buf1); err != nil && err != ErrTruncated {
+ t.Errorf("unable to unpack cutoff message: %v", err)
+ }
+ if !r.Truncated {
+ t.Error("truncated cutoff message wasn't unpacked as truncated")
+ }
+ if len(r.Answer) != cnt {
+ t.Errorf("answer count after cutoff unpack doesn't match: %d", len(r.Answer))
+ }
+ if len(r.Extra) != 0 {
+ t.Errorf("extra count after cutoff unpack is not zero: %d", len(r.Extra))
+ }
+
+ // Now we want to remove almost all of the answer records too
+ buf1 = make([]byte, m.Len())
+ as := 0
+ for i := 0; i < len(m.Extra); i++ {
+ off1 := off
+ off, err = PackRR(m.Extra[i], buf1, off, nil, m.Compress)
+ as = off - off1
+ if err != nil {
+ t.Errorf("failed to pack extra: %v", err)
+ }
+ }
+
+ // Keep exactly one answer left
+ // This should still cause Answer to be nil
+ off -= as
+ buf1 = buf[:len(buf)-off]
+
+ r = new(Msg)
+ if err = r.Unpack(buf1); err != nil && err != ErrTruncated {
+ t.Errorf("unable to unpack cutoff message: %v", err)
+ }
+ if !r.Truncated {
+ t.Error("truncated cutoff message wasn't unpacked as truncated")
+ }
+ if len(r.Answer) != 0 {
+ t.Errorf("answer count after second cutoff unpack is not zero: %d", len(r.Answer))
+ }
+
+ // Now leave only 1 byte of the question
+ // Since the header is always 12 bytes, we just need to keep 13
+ buf1 = buf[:13]
+
+ r = new(Msg)
+ err = r.Unpack(buf1)
+ if err == nil || err == ErrTruncated {
+ t.Errorf("error should not be ErrTruncated from question cutoff unpack: %v", err)
+ }
+
+ // Finally, if we only have the header, we should still return an error
+ buf1 = buf[:12]
+
+ r = new(Msg)
+ if err = r.Unpack(buf1); err == nil || err != ErrTruncated {
+ t.Errorf("error not ErrTruncated from header-only unpack: %v", err)
+ }
+}
+
+func TestTimeout(t *testing.T) {
+ // Set up a dummy UDP server that won't respond
+ addr, err := net.ResolveUDPAddr("udp", ":0")
+ if err != nil {
+ t.Fatalf("unable to resolve local udp address: %v", err)
+ }
+ conn, err := net.ListenUDP("udp", addr)
+ if err != nil {
+ t.Fatalf("unable to run test server: %v", err)
+ }
+ defer conn.Close()
+ addrstr := conn.LocalAddr().String()
+
+ // Message to send
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeTXT)
+
+ // Use a channel + timeout to ensure we don't get stuck if the
+ // Client Timeout is not working properly
+ done := make(chan struct{}, 2)
+
+ timeout := time.Millisecond
+ allowable := timeout + (10 * time.Millisecond)
+ abortAfter := timeout + (100 * time.Millisecond)
+
+ start := time.Now()
+
+ go func() {
+ c := &Client{Timeout: timeout}
+ _, _, err := c.Exchange(m, addrstr)
+ if err == nil {
+ t.Error("no timeout using Client.Exchange")
+ }
+ done <- struct{}{}
+ }()
+
+ go func() {
+ ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ defer cancel()
+ c := &Client{}
+ _, _, err := c.ExchangeContext(ctx, m, addrstr)
+ if err == nil {
+ t.Error("no timeout using Client.ExchangeContext")
+ }
+ done <- struct{}{}
+ }()
+
+ // Wait for both the Exchange and ExchangeContext tests to be done.
+ for i := 0; i < 2; i++ {
+ select {
+ case <-done:
+ case <-time.After(abortAfter):
+ }
+ }
+
+ length := time.Since(start)
+
+ if length > allowable {
+ t.Errorf("exchange took longer %v than specified Timeout %v", length, allowable)
+ }
+}
+
+// Check that responses from deduplicated requests aren't shared between callers
+func TestConcurrentExchanges(t *testing.T) {
+ cases := make([]*Msg, 2)
+ cases[0] = new(Msg)
+ cases[1] = new(Msg)
+ cases[1].Truncated = true
+ for _, m := range cases {
+ block := make(chan struct{})
+ waiting := make(chan struct{})
+
+ handler := func(w ResponseWriter, req *Msg) {
+ r := m.Copy()
+ r.SetReply(req)
+
+ waiting <- struct{}{}
+ <-block
+ w.WriteMsg(r)
+ }
+
+ HandleFunc("miek.nl.", handler)
+ defer HandleRemove("miek.nl.")
+
+ s, addrstr, err := RunLocalUDPServer(":0")
+ if err != nil {
+ t.Fatalf("unable to run test server: %s", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeSRV)
+ c := &Client{
+ SingleInflight: true,
+ }
+ r := make([]*Msg, 2)
+
+ var wg sync.WaitGroup
+ wg.Add(len(r))
+ for i := 0; i < len(r); i++ {
+ go func(i int) {
+ defer wg.Done()
+ r[i], _, _ = c.Exchange(m.Copy(), addrstr)
+ if r[i] == nil {
+ t.Errorf("response %d is nil", i)
+ }
+ }(i)
+ }
+ select {
+ case <-waiting:
+ case <-time.After(time.Second):
+ t.FailNow()
+ }
+ close(block)
+ wg.Wait()
+
+ if r[0] == r[1] {
+ t.Errorf("got same response, expected non-shared responses")
+ }
+ }
+}
diff --git a/vendor/github.com/miekg/dns/clientconfig.go b/vendor/github.com/miekg/dns/clientconfig.go
index cfa9ad0..6479fef 100644
--- a/vendor/github.com/miekg/dns/clientconfig.go
+++ b/vendor/github.com/miekg/dns/clientconfig.go
@@ -2,6 +2,7 @@ package dns
import (
"bufio"
+ "io"
"os"
"strconv"
"strings"
@@ -25,8 +26,13 @@ func ClientConfigFromFile(resolvconf string) (*ClientConfig, error) {
return nil, err
}
defer file.Close()
+ return ClientConfigFromReader(file)
+}
+
+// ClientConfigFromReader works like ClientConfigFromFile but takes an io.Reader as argument
+func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) {
c := new(ClientConfig)
- scanner := bufio.NewScanner(file)
+ scanner := bufio.NewScanner(resolvconf)
c.Servers = make([]string, 0)
c.Search = make([]string, 0)
c.Port = "53"
@@ -97,3 +103,35 @@ func ClientConfigFromFile(resolvconf string) (*ClientConfig, error) {
}
return c, nil
}
+
+// NameList returns all of the names that should be queried based on the
+// config. It is based off of go's net/dns name building, but it does not
+// check the length of the resulting names.
+func (c *ClientConfig) NameList(name string) []string {
+ // if this domain is already fully qualified, no append needed.
+ if IsFqdn(name) {
+ return []string{name}
+ }
+
+ // Check to see if the name has more labels than Ndots. Do this before making
+ // the domain fully qualified.
+ hasNdots := CountLabel(name) > c.Ndots
+ // Make the domain fully qualified.
+ name = Fqdn(name)
+
+ // Make a list of names based off search.
+ names := []string{}
+
+ // If name has enough dots, try that first.
+ if hasNdots {
+ names = append(names, name)
+ }
+ for _, s := range c.Search {
+ names = append(names, Fqdn(name+s))
+ }
+ // If we didn't have enough dots, try after suffixes.
+ if !hasNdots {
+ names = append(names, name)
+ }
+ return names
+}
diff --git a/vendor/github.com/miekg/dns/clientconfig_test.go b/vendor/github.com/miekg/dns/clientconfig_test.go
new file mode 100644
index 0000000..4c5d1fb
--- /dev/null
+++ b/vendor/github.com/miekg/dns/clientconfig_test.go
@@ -0,0 +1,105 @@
+package dns
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+const normal string = `
+# Comment
+domain somedomain.com
+nameserver 10.28.10.2
+nameserver 11.28.10.1
+`
+
+const missingNewline string = `
+domain somedomain.com
+nameserver 10.28.10.2
+nameserver 11.28.10.1` // <- NOTE: NO newline.
+
+func testConfig(t *testing.T, data string) {
+ cc, err := ClientConfigFromReader(strings.NewReader(data))
+ if err != nil {
+ t.Errorf("error parsing resolv.conf: %v", err)
+ }
+ if l := len(cc.Servers); l != 2 {
+ t.Errorf("incorrect number of nameservers detected: %d", l)
+ }
+ if l := len(cc.Search); l != 1 {
+ t.Errorf("domain directive not parsed correctly: %v", cc.Search)
+ } else {
+ if cc.Search[0] != "somedomain.com" {
+ t.Errorf("domain is unexpected: %v", cc.Search[0])
+ }
+ }
+}
+
+func TestNameserver(t *testing.T) { testConfig(t, normal) }
+func TestMissingFinalNewLine(t *testing.T) { testConfig(t, missingNewline) }
+
+func TestReadFromFile(t *testing.T) {
+ tempDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatalf("tempDir: %v", err)
+ }
+ defer os.RemoveAll(tempDir)
+
+ path := filepath.Join(tempDir, "resolv.conf")
+ if err := ioutil.WriteFile(path, []byte(normal), 0644); err != nil {
+ t.Fatalf("writeFile: %v", err)
+ }
+ cc, err := ClientConfigFromFile(path)
+ if err != nil {
+ t.Errorf("error parsing resolv.conf: %v", err)
+ }
+ if l := len(cc.Servers); l != 2 {
+ t.Errorf("incorrect number of nameservers detected: %d", l)
+ }
+ if l := len(cc.Search); l != 1 {
+ t.Errorf("domain directive not parsed correctly: %v", cc.Search)
+ } else {
+ if cc.Search[0] != "somedomain.com" {
+ t.Errorf("domain is unexpected: %v", cc.Search[0])
+ }
+ }
+}
+
+func TestNameList(t *testing.T) {
+ cfg := ClientConfig{
+ Ndots: 1,
+ }
+ // fqdn should be only result returned
+ names := cfg.NameList("miek.nl.")
+ if len(names) != 1 {
+ t.Errorf("NameList returned != 1 names: %v", names)
+ } else if names[0] != "miek.nl." {
+ t.Errorf("NameList didn't return sent fqdn domain: %v", names[0])
+ }
+
+ cfg.Search = []string{
+ "test",
+ }
+ // Sent domain has NDots and search
+ names = cfg.NameList("miek.nl")
+ if len(names) != 2 {
+ t.Errorf("NameList returned != 2 names: %v", names)
+ } else if names[0] != "miek.nl." {
+ t.Errorf("NameList didn't return sent domain first: %v", names[0])
+ } else if names[1] != "miek.nl.test." {
+ t.Errorf("NameList didn't return search last: %v", names[1])
+ }
+
+ cfg.Ndots = 2
+ // Sent domain has less than NDots and search
+ names = cfg.NameList("miek.nl")
+ if len(names) != 2 {
+ t.Errorf("NameList returned != 2 names: %v", names)
+ } else if names[0] != "miek.nl.test." {
+ t.Errorf("NameList didn't return search first: %v", names[0])
+ } else if names[1] != "miek.nl." {
+ t.Errorf("NameList didn't return sent domain last: %v", names[1])
+ }
+}
diff --git a/vendor/github.com/miekg/dns/compress_generate.go b/vendor/github.com/miekg/dns/compress_generate.go
new file mode 100644
index 0000000..d2e5db2
--- /dev/null
+++ b/vendor/github.com/miekg/dns/compress_generate.go
@@ -0,0 +1,189 @@
+//+build ignore
+
+// compression_generate.go is meant to run with go generate. It will use
+// go/{importer,types} to track down all the RR struct types. Then for each type
+// it will look to see if there are (compressible) names, if so it will add that
+// type to compressionLenHelperType and comressionLenSearchType which "fake" the
+// compression so that Len() is fast.
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "go/format"
+ "go/importer"
+ "go/types"
+ "log"
+ "os"
+)
+
+var packageHdr = `
+// *** DO NOT MODIFY ***
+// AUTOGENERATED BY go generate from compress_generate.go
+
+package dns
+
+`
+
+// getTypeStruct will take a type and the package scope, and return the
+// (innermost) struct if the type is considered a RR type (currently defined as
+// those structs beginning with a RR_Header, could be redefined as implementing
+// the RR interface). The bool return value indicates if embedded structs were
+// resolved.
+func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
+ st, ok := t.Underlying().(*types.Struct)
+ if !ok {
+ return nil, false
+ }
+ if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
+ return st, false
+ }
+ if st.Field(0).Anonymous() {
+ st, _ := getTypeStruct(st.Field(0).Type(), scope)
+ return st, true
+ }
+ return nil, false
+}
+
+func main() {
+ // Import and type-check the package
+ pkg, err := importer.Default().Import("github.com/miekg/dns")
+ fatalIfErr(err)
+ scope := pkg.Scope()
+
+ var domainTypes []string // Types that have a domain name in them (either compressible or not).
+ var cdomainTypes []string // Types that have a compressible domain name in them (subset of domainType)
+Names:
+ for _, name := range scope.Names() {
+ o := scope.Lookup(name)
+ if o == nil || !o.Exported() {
+ continue
+ }
+ st, _ := getTypeStruct(o.Type(), scope)
+ if st == nil {
+ continue
+ }
+ if name == "PrivateRR" {
+ continue
+ }
+
+ if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" {
+ log.Fatalf("Constant Type%s does not exist.", o.Name())
+ }
+
+ for i := 1; i < st.NumFields(); i++ {
+ if _, ok := st.Field(i).Type().(*types.Slice); ok {
+ if st.Tag(i) == `dns:"domain-name"` {
+ domainTypes = append(domainTypes, o.Name())
+ continue Names
+ }
+ if st.Tag(i) == `dns:"cdomain-name"` {
+ cdomainTypes = append(cdomainTypes, o.Name())
+ domainTypes = append(domainTypes, o.Name())
+ continue Names
+ }
+ continue
+ }
+
+ switch {
+ case st.Tag(i) == `dns:"domain-name"`:
+ domainTypes = append(domainTypes, o.Name())
+ continue Names
+ case st.Tag(i) == `dns:"cdomain-name"`:
+ cdomainTypes = append(cdomainTypes, o.Name())
+ domainTypes = append(domainTypes, o.Name())
+ continue Names
+ }
+ }
+ }
+
+ b := &bytes.Buffer{}
+ b.WriteString(packageHdr)
+
+ // compressionLenHelperType - all types that have domain-name/cdomain-name can be used for compressing names
+
+ fmt.Fprint(b, "func compressionLenHelperType(c map[string]int, r RR) {\n")
+ fmt.Fprint(b, "switch x := r.(type) {\n")
+ for _, name := range domainTypes {
+ o := scope.Lookup(name)
+ st, _ := getTypeStruct(o.Type(), scope)
+
+ fmt.Fprintf(b, "case *%s:\n", name)
+ for i := 1; i < st.NumFields(); i++ {
+ out := func(s string) { fmt.Fprintf(b, "compressionLenHelper(c, x.%s)\n", st.Field(i).Name()) }
+
+ if _, ok := st.Field(i).Type().(*types.Slice); ok {
+ switch st.Tag(i) {
+ case `dns:"domain-name"`:
+ fallthrough
+ case `dns:"cdomain-name"`:
+ // For HIP we need to slice over the elements in this slice.
+ fmt.Fprintf(b, `for i := range x.%s {
+ compressionLenHelper(c, x.%s[i])
+ }
+`, st.Field(i).Name(), st.Field(i).Name())
+ }
+ continue
+ }
+
+ switch {
+ case st.Tag(i) == `dns:"cdomain-name"`:
+ fallthrough
+ case st.Tag(i) == `dns:"domain-name"`:
+ out(st.Field(i).Name())
+ }
+ }
+ }
+ fmt.Fprintln(b, "}\n}\n\n")
+
+ // compressionLenSearchType - search cdomain-tags types for compressible names.
+
+ fmt.Fprint(b, "func compressionLenSearchType(c map[string]int, r RR) (int, bool) {\n")
+ fmt.Fprint(b, "switch x := r.(type) {\n")
+ for _, name := range cdomainTypes {
+ o := scope.Lookup(name)
+ st, _ := getTypeStruct(o.Type(), scope)
+
+ fmt.Fprintf(b, "case *%s:\n", name)
+ j := 1
+ for i := 1; i < st.NumFields(); i++ {
+ out := func(s string, j int) {
+ fmt.Fprintf(b, "k%d, ok%d := compressionLenSearch(c, x.%s)\n", j, j, st.Field(i).Name())
+ }
+
+ // There are no slice types with names that can be compressed.
+
+ switch {
+ case st.Tag(i) == `dns:"cdomain-name"`:
+ out(st.Field(i).Name(), j)
+ j++
+ }
+ }
+ k := "k1"
+ ok := "ok1"
+ for i := 2; i < j; i++ {
+ k += fmt.Sprintf(" + k%d", i)
+ ok += fmt.Sprintf(" && ok%d", i)
+ }
+ fmt.Fprintf(b, "return %s, %s\n", k, ok)
+ }
+ fmt.Fprintln(b, "}\nreturn 0, false\n}\n\n")
+
+ // gofmt
+ res, err := format.Source(b.Bytes())
+ if err != nil {
+ b.WriteTo(os.Stderr)
+ log.Fatal(err)
+ }
+
+ f, err := os.Create("zcompress.go")
+ fatalIfErr(err)
+ defer f.Close()
+ f.Write(res)
+}
+
+func fatalIfErr(err error) {
+ if err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/vendor/github.com/miekg/dns/dane.go b/vendor/github.com/miekg/dns/dane.go
index cdaa833..8c4a14e 100644
--- a/vendor/github.com/miekg/dns/dane.go
+++ b/vendor/github.com/miekg/dns/dane.go
@@ -6,7 +6,6 @@ import (
"crypto/x509"
"encoding/hex"
"errors"
- "io"
)
// CertificateToDANE converts a certificate to a hex string as used in the TLSA or SMIMEA records.
@@ -23,20 +22,20 @@ func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) (st
h := sha256.New()
switch selector {
case 0:
- io.WriteString(h, string(cert.Raw))
+ h.Write(cert.Raw)
return hex.EncodeToString(h.Sum(nil)), nil
case 1:
- io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
+ h.Write(cert.RawSubjectPublicKeyInfo)
return hex.EncodeToString(h.Sum(nil)), nil
}
case 2:
h := sha512.New()
switch selector {
case 0:
- io.WriteString(h, string(cert.Raw))
+ h.Write(cert.Raw)
return hex.EncodeToString(h.Sum(nil)), nil
case 1:
- io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
+ h.Write(cert.RawSubjectPublicKeyInfo)
return hex.EncodeToString(h.Sum(nil)), nil
}
}
diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go
index cf45616..c34890e 100644
--- a/vendor/github.com/miekg/dns/defaults.go
+++ b/vendor/github.com/miekg/dns/defaults.go
@@ -13,9 +13,12 @@ const hexDigit = "0123456789abcdef"
// SetReply creates a reply message from a request message.
func (dns *Msg) SetReply(request *Msg) *Msg {
dns.Id = request.Id
- dns.RecursionDesired = request.RecursionDesired // Copy rd bit
dns.Response = true
- dns.Opcode = OpcodeQuery
+ dns.Opcode = request.Opcode
+ if dns.Opcode == OpcodeQuery {
+ dns.RecursionDesired = request.RecursionDesired // Copy rd bit
+ dns.CheckingDisabled = request.CheckingDisabled // Copy cd bit
+ }
dns.Rcode = RcodeSuccess
if len(request.Question) > 0 {
dns.Question = make([]Question, 1)
@@ -102,11 +105,11 @@ func (dns *Msg) SetAxfr(z string) *Msg {
// SetTsig appends a TSIG RR to the message.
// This is only a skeleton TSIG RR that is added as the last RR in the
// additional section. The Tsig is calculated when the message is being send.
-func (dns *Msg) SetTsig(z, algo string, fudge, timesigned int64) *Msg {
+func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg {
t := new(TSIG)
t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0}
t.Algorithm = algo
- t.Fudge = 300
+ t.Fudge = fudge
t.TimeSigned = uint64(timesigned)
t.OrigId = dns.Id
dns.Extra = append(dns.Extra, t)
diff --git a/vendor/github.com/miekg/dns/dns.go b/vendor/github.com/miekg/dns/dns.go
index b329228..5133eac 100644
--- a/vendor/github.com/miekg/dns/dns.go
+++ b/vendor/github.com/miekg/dns/dns.go
@@ -6,9 +6,12 @@ const (
year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
defaultTtl = 3600 // Default internal TTL.
- DefaultMsgSize = 4096 // DefaultMsgSize is the standard default for messages larger than 512 bytes.
- MinMsgSize = 512 // MinMsgSize is the minimal size of a DNS packet.
- MaxMsgSize = 65535 // MaxMsgSize is the largest possible DNS packet.
+ // DefaultMsgSize is the standard default for messages larger than 512 bytes.
+ DefaultMsgSize = 4096
+ // MinMsgSize is the minimal size of a DNS packet.
+ MinMsgSize = 512
+ // MaxMsgSize is the largest possible DNS packet.
+ MaxMsgSize = 65535
)
// Error represents a DNS error.
diff --git a/vendor/github.com/miekg/dns/dns_bench_test.go b/vendor/github.com/miekg/dns/dns_bench_test.go
new file mode 100644
index 0000000..7bf8bd2
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dns_bench_test.go
@@ -0,0 +1,230 @@
+package dns
+
+import (
+ "net"
+ "testing"
+)
+
+func BenchmarkMsgLength(b *testing.B) {
+ b.StopTimer()
+ makeMsg := func(question string, ans, ns, e []RR) *Msg {
+ msg := new(Msg)
+ msg.SetQuestion(Fqdn(question), TypeANY)
+ msg.Answer = append(msg.Answer, ans...)
+ msg.Ns = append(msg.Ns, ns...)
+ msg.Extra = append(msg.Extra, e...)
+ msg.Compress = true
+ return msg
+ }
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
+ msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ msg.Len()
+ }
+}
+
+func BenchmarkMsgLengthNoCompression(b *testing.B) {
+ b.StopTimer()
+ makeMsg := func(question string, ans, ns, e []RR) *Msg {
+ msg := new(Msg)
+ msg.SetQuestion(Fqdn(question), TypeANY)
+ msg.Answer = append(msg.Answer, ans...)
+ msg.Ns = append(msg.Ns, ns...)
+ msg.Extra = append(msg.Extra, e...)
+ return msg
+ }
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
+ msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ msg.Len()
+ }
+}
+
+func BenchmarkMsgLengthPack(b *testing.B) {
+ makeMsg := func(question string, ans, ns, e []RR) *Msg {
+ msg := new(Msg)
+ msg.SetQuestion(Fqdn(question), TypeANY)
+ msg.Answer = append(msg.Answer, ans...)
+ msg.Ns = append(msg.Ns, ns...)
+ msg.Extra = append(msg.Extra, e...)
+ msg.Compress = true
+ return msg
+ }
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
+ msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = msg.Pack()
+ }
+}
+
+func BenchmarkPackDomainName(b *testing.B) {
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ buf := make([]byte, len(name1)+1)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = PackDomainName(name1, buf, 0, nil, false)
+ }
+}
+
+func BenchmarkUnpackDomainName(b *testing.B) {
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ buf := make([]byte, len(name1)+1)
+ _, _ = PackDomainName(name1, buf, 0, nil, false)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _, _ = UnpackDomainName(buf, 0)
+ }
+}
+
+func BenchmarkUnpackDomainNameUnprintable(b *testing.B) {
+ name1 := "\x02\x02\x02\x025\x02\x02\x02\x02.12345678.123."
+ buf := make([]byte, len(name1)+1)
+ _, _ = PackDomainName(name1, buf, 0, nil, false)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _, _ = UnpackDomainName(buf, 0)
+ }
+}
+
+func BenchmarkCopy(b *testing.B) {
+ b.ReportAllocs()
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeA)
+ rr := testRR("miek.nl. 2311 IN A 127.0.0.1")
+ m.Answer = []RR{rr}
+ rr = testRR("miek.nl. 2311 IN NS 127.0.0.1")
+ m.Ns = []RR{rr}
+ rr = testRR("miek.nl. 2311 IN A 127.0.0.1")
+ m.Extra = []RR{rr}
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ m.Copy()
+ }
+}
+
+func BenchmarkPackA(b *testing.B) {
+ a := &A{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, A: net.IPv4(127, 0, 0, 1)}
+
+ buf := make([]byte, a.len())
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = PackRR(a, buf, 0, nil, false)
+ }
+}
+
+func BenchmarkUnpackA(b *testing.B) {
+ a := &A{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, A: net.IPv4(127, 0, 0, 1)}
+
+ buf := make([]byte, a.len())
+ PackRR(a, buf, 0, nil, false)
+ a = nil
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _, _ = UnpackRR(buf, 0)
+ }
+}
+
+func BenchmarkPackMX(b *testing.B) {
+ m := &MX{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, Mx: "mx.miek.nl."}
+
+ buf := make([]byte, m.len())
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = PackRR(m, buf, 0, nil, false)
+ }
+}
+
+func BenchmarkUnpackMX(b *testing.B) {
+ m := &MX{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, Mx: "mx.miek.nl."}
+
+ buf := make([]byte, m.len())
+ PackRR(m, buf, 0, nil, false)
+ m = nil
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _, _ = UnpackRR(buf, 0)
+ }
+}
+
+func BenchmarkPackAAAAA(b *testing.B) {
+ aaaa := testRR(". IN A ::1")
+
+ buf := make([]byte, aaaa.len())
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = PackRR(aaaa, buf, 0, nil, false)
+ }
+}
+
+func BenchmarkUnpackAAAA(b *testing.B) {
+ aaaa := testRR(". IN A ::1")
+
+ buf := make([]byte, aaaa.len())
+ PackRR(aaaa, buf, 0, nil, false)
+ aaaa = nil
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _, _ = UnpackRR(buf, 0)
+ }
+}
+
+func BenchmarkPackMsg(b *testing.B) {
+ makeMsg := func(question string, ans, ns, e []RR) *Msg {
+ msg := new(Msg)
+ msg.SetQuestion(Fqdn(question), TypeANY)
+ msg.Answer = append(msg.Answer, ans...)
+ msg.Ns = append(msg.Ns, ns...)
+ msg.Extra = append(msg.Extra, e...)
+ msg.Compress = true
+ return msg
+ }
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
+ msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
+ buf := make([]byte, 512)
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = msg.PackBuffer(buf)
+ }
+}
+
+func BenchmarkUnpackMsg(b *testing.B) {
+ makeMsg := func(question string, ans, ns, e []RR) *Msg {
+ msg := new(Msg)
+ msg.SetQuestion(Fqdn(question), TypeANY)
+ msg.Answer = append(msg.Answer, ans...)
+ msg.Ns = append(msg.Ns, ns...)
+ msg.Extra = append(msg.Extra, e...)
+ msg.Compress = true
+ return msg
+ }
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
+ msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
+ msgBuf, _ := msg.Pack()
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _ = msg.Unpack(msgBuf)
+ }
+}
+
+func BenchmarkIdGeneration(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ _ = id()
+ }
+}
diff --git a/vendor/github.com/miekg/dns/dns_test.go b/vendor/github.com/miekg/dns/dns_test.go
new file mode 100644
index 0000000..5835c13
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dns_test.go
@@ -0,0 +1,407 @@
+package dns
+
+import (
+ "encoding/hex"
+ "net"
+ "testing"
+)
+
+func TestPackUnpack(t *testing.T) {
+ out := new(Msg)
+ out.Answer = make([]RR, 1)
+ key := new(DNSKEY)
+ key = &DNSKEY{Flags: 257, Protocol: 3, Algorithm: RSASHA1}
+ key.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 3600}
+ key.PublicKey = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"
+
+ out.Answer[0] = key
+ msg, err := out.Pack()
+ if err != nil {
+ t.Error("failed to pack msg with DNSKEY")
+ }
+ in := new(Msg)
+ if in.Unpack(msg) != nil {
+ t.Error("failed to unpack msg with DNSKEY")
+ }
+
+ sig := new(RRSIG)
+ sig = &RRSIG{TypeCovered: TypeDNSKEY, Algorithm: RSASHA1, Labels: 2,
+ OrigTtl: 3600, Expiration: 4000, Inception: 4000, KeyTag: 34641, SignerName: "miek.nl.",
+ Signature: "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"}
+ sig.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeRRSIG, Class: ClassINET, Ttl: 3600}
+
+ out.Answer[0] = sig
+ msg, err = out.Pack()
+ if err != nil {
+ t.Error("failed to pack msg with RRSIG")
+ }
+
+ if in.Unpack(msg) != nil {
+ t.Error("failed to unpack msg with RRSIG")
+ }
+}
+
+func TestPackUnpack2(t *testing.T) {
+ m := new(Msg)
+ m.Extra = make([]RR, 1)
+ m.Answer = make([]RR, 1)
+ dom := "miek.nl."
+ rr := new(A)
+ rr.Hdr = RR_Header{Name: dom, Rrtype: TypeA, Class: ClassINET, Ttl: 0}
+ rr.A = net.IPv4(127, 0, 0, 1)
+
+ x := new(TXT)
+ x.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
+ x.Txt = []string{"heelalaollo"}
+
+ m.Extra[0] = x
+ m.Answer[0] = rr
+ _, err := m.Pack()
+ if err != nil {
+ t.Error("Packing failed: ", err)
+ return
+ }
+}
+
+func TestPackUnpack3(t *testing.T) {
+ m := new(Msg)
+ m.Extra = make([]RR, 2)
+ m.Answer = make([]RR, 1)
+ dom := "miek.nl."
+ rr := new(A)
+ rr.Hdr = RR_Header{Name: dom, Rrtype: TypeA, Class: ClassINET, Ttl: 0}
+ rr.A = net.IPv4(127, 0, 0, 1)
+
+ x1 := new(TXT)
+ x1.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
+ x1.Txt = []string{}
+
+ x2 := new(TXT)
+ x2.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
+ x2.Txt = []string{"heelalaollo"}
+
+ m.Extra[0] = x1
+ m.Extra[1] = x2
+ m.Answer[0] = rr
+ b, err := m.Pack()
+ if err != nil {
+ t.Error("packing failed: ", err)
+ return
+ }
+
+ var unpackMsg Msg
+ err = unpackMsg.Unpack(b)
+ if err != nil {
+ t.Error("unpacking failed")
+ return
+ }
+}
+
+func TestBailiwick(t *testing.T) {
+ yes := map[string]string{
+ "miek1.nl": "miek1.nl",
+ "miek.nl": "ns.miek.nl",
+ ".": "miek.nl",
+ }
+ for parent, child := range yes {
+ if !IsSubDomain(parent, child) {
+ t.Errorf("%s should be child of %s", child, parent)
+ t.Errorf("comparelabels %d", CompareDomainName(parent, child))
+ t.Errorf("lenlabels %d %d", CountLabel(parent), CountLabel(child))
+ }
+ }
+ no := map[string]string{
+ "www.miek.nl": "ns.miek.nl",
+ "m\\.iek.nl": "ns.miek.nl",
+ "w\\.iek.nl": "w.iek.nl",
+ "p\\\\.iek.nl": "ns.p.iek.nl", // p\\.iek.nl , literal \ in domain name
+ "miek.nl": ".",
+ }
+ for parent, child := range no {
+ if IsSubDomain(parent, child) {
+ t.Errorf("%s should not be child of %s", child, parent)
+ t.Errorf("comparelabels %d", CompareDomainName(parent, child))
+ t.Errorf("lenlabels %d %d", CountLabel(parent), CountLabel(child))
+ }
+ }
+}
+
+func TestPackNAPTR(t *testing.T) {
+ for _, n := range []string{
+ `apple.com. IN NAPTR 100 50 "se" "SIP+D2U" "" _sip._udp.apple.com.`,
+ `apple.com. IN NAPTR 90 50 "se" "SIP+D2T" "" _sip._tcp.apple.com.`,
+ `apple.com. IN NAPTR 50 50 "se" "SIPS+D2T" "" _sips._tcp.apple.com.`,
+ } {
+ rr := testRR(n)
+ msg := make([]byte, rr.len())
+ if off, err := PackRR(rr, msg, 0, nil, false); err != nil {
+ t.Errorf("packing failed: %v", err)
+ t.Errorf("length %d, need more than %d", rr.len(), off)
+ }
+ }
+}
+
+func TestCompressLength(t *testing.T) {
+ m := new(Msg)
+ m.SetQuestion("miek.nl", TypeMX)
+ ul := m.Len()
+ m.Compress = true
+ if ul != m.Len() {
+ t.Fatalf("should be equal")
+ }
+}
+
+// Does the predicted length match final packed length?
+func TestMsgCompressLength(t *testing.T) {
+ makeMsg := func(question string, ans, ns, e []RR) *Msg {
+ msg := new(Msg)
+ msg.SetQuestion(Fqdn(question), TypeANY)
+ msg.Answer = append(msg.Answer, ans...)
+ msg.Ns = append(msg.Ns, ns...)
+ msg.Extra = append(msg.Extra, e...)
+ msg.Compress = true
+ return msg
+ }
+
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ rrA := testRR(name1 + " 3600 IN A 192.0.2.1")
+ rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
+ tests := []*Msg{
+ makeMsg(name1, []RR{rrA}, nil, nil),
+ makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)}
+
+ for _, msg := range tests {
+ predicted := msg.Len()
+ buf, err := msg.Pack()
+ if err != nil {
+ t.Error(err)
+ }
+ if predicted < len(buf) {
+ t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d",
+ msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
+ }
+ }
+}
+
+func TestMsgLength(t *testing.T) {
+ makeMsg := func(question string, ans, ns, e []RR) *Msg {
+ msg := new(Msg)
+ msg.SetQuestion(Fqdn(question), TypeANY)
+ msg.Answer = append(msg.Answer, ans...)
+ msg.Ns = append(msg.Ns, ns...)
+ msg.Extra = append(msg.Extra, e...)
+ return msg
+ }
+
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ rrA := testRR(name1 + " 3600 IN A 192.0.2.1")
+ rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
+ tests := []*Msg{
+ makeMsg(name1, []RR{rrA}, nil, nil),
+ makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)}
+
+ for _, msg := range tests {
+ predicted := msg.Len()
+ buf, err := msg.Pack()
+ if err != nil {
+ t.Error(err)
+ }
+ if predicted < len(buf) {
+ t.Errorf("predicted length is wrong: predicted %s (len=%d), actual %d",
+ msg.Question[0].Name, predicted, len(buf))
+ }
+ }
+}
+
+func TestMsgLength2(t *testing.T) {
+ // Serialized replies
+ var testMessages = []string{
+ // google.com. IN A?
+ "064e81800001000b0004000506676f6f676c6503636f6d0000010001c00c00010001000000050004adc22986c00c00010001000000050004adc22987c00c00010001000000050004adc22988c00c00010001000000050004adc22989c00c00010001000000050004adc2298ec00c00010001000000050004adc22980c00c00010001000000050004adc22981c00c00010001000000050004adc22982c00c00010001000000050004adc22983c00c00010001000000050004adc22984c00c00010001000000050004adc22985c00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc0d800010001000000050004d8ef200ac0ea00010001000000050004d8ef220ac0fc00010001000000050004d8ef240ac10e00010001000000050004d8ef260a0000290500000000050000",
+ // amazon.com. IN A? (reply has no EDNS0 record)
+ // TODO(miek): this one is off-by-one, need to find out why
+ //"6de1818000010004000a000806616d617a6f6e03636f6d0000010001c00c000100010000000500044815c2d4c00c000100010000000500044815d7e8c00c00010001000000050004b02062a6c00c00010001000000050004cdfbf236c00c000200010000000500140570646e733408756c747261646e73036f726700c00c000200010000000500150570646e733508756c747261646e7304696e666f00c00c000200010000000500160570646e733608756c747261646e7302636f02756b00c00c00020001000000050014036e7331037033310664796e656374036e657400c00c00020001000000050006036e7332c0cfc00c00020001000000050006036e7333c0cfc00c00020001000000050006036e7334c0cfc00c000200010000000500110570646e733108756c747261646e73c0dac00c000200010000000500080570646e7332c127c00c000200010000000500080570646e7333c06ec0cb00010001000000050004d04e461fc0eb00010001000000050004cc0dfa1fc0fd00010001000000050004d04e471fc10f00010001000000050004cc0dfb1fc12100010001000000050004cc4a6c01c121001c000100000005001020010502f3ff00000000000000000001c13e00010001000000050004cc4a6d01c13e001c0001000000050010261000a1101400000000000000000001",
+ // yahoo.com. IN A?
+ "fc2d81800001000300070008057961686f6f03636f6d0000010001c00c00010001000000050004628afd6dc00c00010001000000050004628bb718c00c00010001000000050004cebe242dc00c00020001000000050006036e7336c00cc00c00020001000000050006036e7338c00cc00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7335c00cc07b0001000100000005000444b48310c08d00010001000000050004448eff10c09f00010001000000050004cb54dd35c0b100010001000000050004628a0b9dc0c30001000100000005000477a0f77cc05700010001000000050004ca2bdfaac06900010001000000050004caa568160000290500000000050000",
+ // microsoft.com. IN A?
+ "f4368180000100020005000b096d6963726f736f667403636f6d0000010001c00c0001000100000005000440040b25c00c0001000100000005000441373ac9c00c0002000100000005000e036e7331046d736674036e657400c00c00020001000000050006036e7332c04fc00c00020001000000050006036e7333c04fc00c00020001000000050006036e7334c04fc00c00020001000000050006036e7335c04fc04b000100010000000500044137253ec04b001c00010000000500102a010111200500000000000000010001c0650001000100000005000440043badc065001c00010000000500102a010111200600060000000000010001c07700010001000000050004d5c7b435c077001c00010000000500102a010111202000000000000000010001c08900010001000000050004cf2e4bfec089001c00010000000500102404f800200300000000000000010001c09b000100010000000500044137e28cc09b001c00010000000500102a010111200f000100000000000100010000290500000000050000",
+ // google.com. IN MX?
+ "724b8180000100050004000b06676f6f676c6503636f6d00000f0001c00c000f000100000005000c000a056173706d78016cc00cc00c000f0001000000050009001404616c7431c02ac00c000f0001000000050009001e04616c7432c02ac00c000f0001000000050009002804616c7433c02ac00c000f0001000000050009003204616c7434c02ac00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7331c00cc02a00010001000000050004adc2421bc02a001c00010000000500102a00145040080c01000000000000001bc04200010001000000050004adc2461bc05700010001000000050004adc2451bc06c000100010000000500044a7d8f1bc081000100010000000500044a7d191bc0ca00010001000000050004d8ef200ac09400010001000000050004d8ef220ac0a600010001000000050004d8ef240ac0b800010001000000050004d8ef260a0000290500000000050000",
+ // reddit.com. IN A?
+ "12b98180000100080000000c0672656464697403636f6d0000020001c00c0002000100000005000f046175733204616b616d036e657400c00c000200010000000500070475736534c02dc00c000200010000000500070475737733c02dc00c000200010000000500070475737735c02dc00c00020001000000050008056173696131c02dc00c00020001000000050008056173696139c02dc00c00020001000000050008056e73312d31c02dc00c0002000100000005000a076e73312d313935c02dc02800010001000000050004c30a242ec04300010001000000050004451f1d39c05600010001000000050004451f3bc7c0690001000100000005000460073240c07c000100010000000500046007fb81c090000100010000000500047c283484c090001c00010000000500102a0226f0006700000000000000000064c0a400010001000000050004c16c5b01c0a4001c000100000005001026001401000200000000000000000001c0b800010001000000050004c16c5bc3c0b8001c0001000000050010260014010002000000000000000000c30000290500000000050000",
+ }
+
+ for i, hexData := range testMessages {
+ // we won't fail the decoding of the hex
+ input, _ := hex.DecodeString(hexData)
+
+ m := new(Msg)
+ m.Unpack(input)
+ m.Compress = true
+ lenComp := m.Len()
+ b, _ := m.Pack()
+ pacComp := len(b)
+ m.Compress = false
+ lenUnComp := m.Len()
+ b, _ = m.Pack()
+ pacUnComp := len(b)
+ if pacComp+1 != lenComp {
+ t.Errorf("msg.Len(compressed)=%d actual=%d for test %d", lenComp, pacComp, i)
+ }
+ if pacUnComp+1 != lenUnComp {
+ t.Errorf("msg.Len(uncompressed)=%d actual=%d for test %d", lenUnComp, pacUnComp, i)
+ }
+ }
+}
+
+func TestMsgLengthCompressionMalformed(t *testing.T) {
+ // SOA with empty hostmaster, which is illegal
+ soa := &SOA{Hdr: RR_Header{Name: ".", Rrtype: TypeSOA, Class: ClassINET, Ttl: 12345},
+ Ns: ".",
+ Mbox: "",
+ Serial: 0,
+ Refresh: 28800,
+ Retry: 7200,
+ Expire: 604800,
+ Minttl: 60}
+ m := new(Msg)
+ m.Compress = true
+ m.Ns = []RR{soa}
+ m.Len() // Should not crash.
+}
+
+func TestMsgCompressLength2(t *testing.T) {
+ msg := new(Msg)
+ msg.Compress = true
+ msg.SetQuestion(Fqdn("bliep."), TypeANY)
+ msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "blaat.", Rrtype: 0x21, Class: 0x1, Ttl: 0x3c}, Port: 0x4c57, Target: "foo.bar."})
+ msg.Extra = append(msg.Extra, &A{Hdr: RR_Header{Name: "foo.bar.", Rrtype: 0x1, Class: 0x1, Ttl: 0x3c}, A: net.IP{0xac, 0x11, 0x0, 0x3}})
+ predicted := msg.Len()
+ buf, err := msg.Pack()
+ if err != nil {
+ t.Error(err)
+ }
+ if predicted != len(buf) {
+ t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d",
+ msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
+ }
+}
+
+func TestToRFC3597(t *testing.T) {
+ a := testRR("miek.nl. IN A 10.0.1.1")
+ x := new(RFC3597)
+ x.ToRFC3597(a)
+ if x.String() != `miek.nl. 3600 CLASS1 TYPE1 \# 4 0a000101` {
+ t.Errorf("string mismatch, got: %s", x)
+ }
+
+ b := testRR("miek.nl. IN MX 10 mx.miek.nl.")
+ x.ToRFC3597(b)
+ if x.String() != `miek.nl. 3600 CLASS1 TYPE15 \# 14 000a026d78046d69656b026e6c00` {
+ t.Errorf("string mismatch, got: %s", x)
+ }
+}
+
+func TestNoRdataPack(t *testing.T) {
+ data := make([]byte, 1024)
+ for typ, fn := range TypeToRR {
+ r := fn()
+ *r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 16}
+ _, err := PackRR(r, data, 0, nil, false)
+ if err != nil {
+ t.Errorf("failed to pack RR with zero rdata: %s: %v", TypeToString[typ], err)
+ }
+ }
+}
+
+func TestNoRdataUnpack(t *testing.T) {
+ data := make([]byte, 1024)
+ for typ, fn := range TypeToRR {
+ if typ == TypeSOA || typ == TypeTSIG {
+ // SOA, TSIG will not be seen (like this) in dyn. updates?
+ continue
+ }
+ r := fn()
+ *r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 16}
+ off, err := PackRR(r, data, 0, nil, false)
+ if err != nil {
+ // Should always works, TestNoDataPack should have caught this
+ t.Errorf("failed to pack RR: %v", err)
+ continue
+ }
+ if _, _, err := UnpackRR(data[:off], 0); err != nil {
+ t.Errorf("failed to unpack RR with zero rdata: %s: %v", TypeToString[typ], err)
+ }
+ }
+}
+
+func TestRdataOverflow(t *testing.T) {
+ rr := new(RFC3597)
+ rr.Hdr.Name = "."
+ rr.Hdr.Class = ClassINET
+ rr.Hdr.Rrtype = 65280
+ rr.Rdata = hex.EncodeToString(make([]byte, 0xFFFF))
+ buf := make([]byte, 0xFFFF*2)
+ if _, err := PackRR(rr, buf, 0, nil, false); err != nil {
+ t.Fatalf("maximum size rrdata pack failed: %v", err)
+ }
+ rr.Rdata += "00"
+ if _, err := PackRR(rr, buf, 0, nil, false); err != ErrRdata {
+ t.Fatalf("oversize rrdata pack didn't return ErrRdata - instead: %v", err)
+ }
+}
+
+func TestCopy(t *testing.T) {
+ rr := testRR("miek.nl. 2311 IN A 127.0.0.1") // Weird TTL to avoid catching TTL
+ rr1 := Copy(rr)
+ if rr.String() != rr1.String() {
+ t.Fatalf("Copy() failed %s != %s", rr.String(), rr1.String())
+ }
+}
+
+func TestMsgCopy(t *testing.T) {
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeA)
+ rr := testRR("miek.nl. 2311 IN A 127.0.0.1")
+ m.Answer = []RR{rr}
+ rr = testRR("miek.nl. 2311 IN NS 127.0.0.1")
+ m.Ns = []RR{rr}
+
+ m1 := m.Copy()
+ if m.String() != m1.String() {
+ t.Fatalf("Msg.Copy() failed %s != %s", m.String(), m1.String())
+ }
+
+ m1.Answer[0] = testRR("somethingelse.nl. 2311 IN A 127.0.0.1")
+ if m.String() == m1.String() {
+ t.Fatalf("Msg.Copy() failed; change to copy changed template %s", m.String())
+ }
+
+ rr = testRR("miek.nl. 2311 IN A 127.0.0.2")
+ m1.Answer = append(m1.Answer, rr)
+ if m1.Ns[0].String() == m1.Answer[1].String() {
+ t.Fatalf("Msg.Copy() failed; append changed underlying array %s", m1.Ns[0].String())
+ }
+}
+
+func TestMsgPackBuffer(t *testing.T) {
+ var testMessages = []string{
+ // news.ycombinator.com.in.escapemg.com. IN A, response
+ "586285830001000000010000046e6577730b79636f6d62696e61746f7203636f6d02696e086573636170656d6703636f6d0000010001c0210006000100000e10002c036e7332c02103646e730b67726f6f7665736861726bc02d77ed50e600002a3000000e1000093a8000000e10",
+
+ // news.ycombinator.com.in.escapemg.com. IN A, question
+ "586201000001000000000000046e6577730b79636f6d62696e61746f7203636f6d02696e086573636170656d6703636f6d0000010001",
+
+ "398781020001000000000000046e6577730b79636f6d62696e61746f7203636f6d0000010001",
+ }
+
+ for i, hexData := range testMessages {
+ // we won't fail the decoding of the hex
+ input, _ := hex.DecodeString(hexData)
+ m := new(Msg)
+ if err := m.Unpack(input); err != nil {
+ t.Errorf("packet %d failed to unpack", i)
+ continue
+ }
+ }
+}
diff --git a/vendor/github.com/miekg/dns/dnssec.go b/vendor/github.com/miekg/dns/dnssec.go
index f5f3fbd..3bd5538 100644
--- a/vendor/github.com/miekg/dns/dnssec.go
+++ b/vendor/github.com/miekg/dns/dnssec.go
@@ -43,7 +43,7 @@ const (
PRIVATEOID uint8 = 254
)
-// Map for algorithm names.
+// AlgorithmToString is a map of algorithm IDs to algorithm names.
var AlgorithmToString = map[uint8]string{
RSAMD5: "RSAMD5",
DH: "DH",
@@ -61,10 +61,10 @@ var AlgorithmToString = map[uint8]string{
PRIVATEOID: "PRIVATEOID",
}
-// Map of algorithm strings.
+// StringToAlgorithm is the reverse of AlgorithmToString.
var StringToAlgorithm = reverseInt8(AlgorithmToString)
-// Map of algorithm crypto hashes.
+// AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's.
var AlgorithmToHash = map[uint8]crypto.Hash{
RSAMD5: crypto.MD5, // Deprecated in RFC 6725
RSASHA1: crypto.SHA1,
@@ -85,7 +85,7 @@ const (
SHA512 // Experimental
)
-// Map for hash names.
+// HashToString is a map of hash IDs to names.
var HashToString = map[uint8]string{
SHA1: "SHA1",
SHA256: "SHA256",
@@ -94,7 +94,7 @@ var HashToString = map[uint8]string{
SHA512: "SHA512",
}
-// Map of hash strings.
+// StringToHash is a map of names to hash IDs.
var StringToHash = reverseInt8(HashToString)
// DNSKEY flag values.
@@ -208,9 +208,6 @@ func (k *DNSKEY) ToDS(h uint8) *DS {
// "|" denotes concatenation
// DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.
- // digest buffer
- digest := append(owner, wire...) // another copy
-
var hash crypto.Hash
switch h {
case SHA1:
@@ -226,7 +223,8 @@ func (k *DNSKEY) ToDS(h uint8) *DS {
}
s := hash.New()
- s.Write(digest)
+ s.Write(owner)
+ s.Write(wire)
ds.Digest = hex.EncodeToString(s.Sum(nil))
return ds
}
@@ -297,7 +295,6 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
if err != nil {
return err
}
- signdata = append(signdata, wire...)
hash, ok := AlgorithmToHash[rr.Algorithm]
if !ok {
@@ -306,6 +303,7 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
h := hash.New()
h.Write(signdata)
+ h.Write(wire)
signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm)
if err != nil {
@@ -415,7 +413,6 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
if err != nil {
return err
}
- signeddata = append(signeddata, wire...)
sigbuf := rr.sigBuf() // Get the binary signature data
if rr.Algorithm == PRIVATEDNS { // PRIVATEOID
@@ -438,6 +435,7 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
h := hash.New()
h.Write(signeddata)
+ h.Write(wire)
return rsa.VerifyPKCS1v15(pubkey, hash, h.Sum(nil), sigbuf)
case ECDSAP256SHA256, ECDSAP384SHA384:
@@ -452,6 +450,7 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
h := hash.New()
h.Write(signeddata)
+ h.Write(wire)
if ecdsa.Verify(pubkey, h.Sum(nil), r, s) {
return nil
}
@@ -516,7 +515,7 @@ func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey {
}
// Remainder
expo += uint64(keybuf[keyoff])
- if expo > 2<<31 {
+ if expo > (2<<31)+1 {
// Larger expo than supported.
// println("dns: F5 primes (or larger) are not supported")
return nil
diff --git a/vendor/github.com/miekg/dns/dnssec_keygen.go b/vendor/github.com/miekg/dns/dnssec_keygen.go
index 229a079..5e4b774 100644
--- a/vendor/github.com/miekg/dns/dnssec_keygen.go
+++ b/vendor/github.com/miekg/dns/dnssec_keygen.go
@@ -121,17 +121,17 @@ func (k *DNSKEY) setPublicKeyDSA(_Q, _P, _G, _Y *big.Int) bool {
// RFC 3110: Section 2. RSA Public KEY Resource Records
func exponentToBuf(_E int) []byte {
var buf []byte
- i := big.NewInt(int64(_E))
- if len(i.Bytes()) < 256 {
- buf = make([]byte, 1)
- buf[0] = uint8(len(i.Bytes()))
+ i := big.NewInt(int64(_E)).Bytes()
+ if len(i) < 256 {
+ buf = make([]byte, 1, 1+len(i))
+ buf[0] = uint8(len(i))
} else {
- buf = make([]byte, 3)
+ buf = make([]byte, 3, 3+len(i))
buf[0] = 0
- buf[1] = uint8(len(i.Bytes()) >> 8)
- buf[2] = uint8(len(i.Bytes()))
+ buf[1] = uint8(len(i) >> 8)
+ buf[2] = uint8(len(i))
}
- buf = append(buf, i.Bytes()...)
+ buf = append(buf, i...)
return buf
}
diff --git a/vendor/github.com/miekg/dns/dnssec_keyscan.go b/vendor/github.com/miekg/dns/dnssec_keyscan.go
index 9ff3a61..063094d 100644
--- a/vendor/github.com/miekg/dns/dnssec_keyscan.go
+++ b/vendor/github.com/miekg/dns/dnssec_keyscan.go
@@ -36,7 +36,7 @@ func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, er
return nil, ErrPrivKey
}
// TODO(mg): check if the pubkey matches the private key
- algo, err := strconv.Atoi(strings.SplitN(m["algorithm"], " ", 2)[0])
+ algo, err := strconv.ParseUint(strings.SplitN(m["algorithm"], " ", 2)[0], 10, 8)
if err != nil {
return nil, ErrPrivKey
}
@@ -169,10 +169,20 @@ func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
// parseKey reads a private key from r. It returns a map[string]string,
// with the key-value pairs, or an error when the file is not correct.
func parseKey(r io.Reader, file string) (map[string]string, error) {
- s := scanInit(r)
+ s, cancel := scanInit(r)
m := make(map[string]string)
c := make(chan lex)
k := ""
+ defer func() {
+ cancel()
+ // zlexer can send up to two tokens, the next one and possibly 1 remainders.
+ // Do a non-blocking read.
+ _, ok := <-c
+ _, ok = <-c
+ if !ok {
+ // too bad
+ }
+ }()
// Start the lexer
go klexer(s, c)
for l := range c {
diff --git a/vendor/github.com/miekg/dns/dnssec_test.go b/vendor/github.com/miekg/dns/dnssec_test.go
new file mode 100644
index 0000000..d5de439
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dnssec_test.go
@@ -0,0 +1,652 @@
+package dns
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/rsa"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+)
+
+func getKey() *DNSKEY {
+ key := new(DNSKEY)
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 14400
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
+ return key
+}
+
+func getSoa() *SOA {
+ soa := new(SOA)
+ soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
+ soa.Ns = "open.nlnetlabs.nl."
+ soa.Mbox = "miekg.atoom.net."
+ soa.Serial = 1293945905
+ soa.Refresh = 14400
+ soa.Retry = 3600
+ soa.Expire = 604800
+ soa.Minttl = 86400
+ return soa
+}
+
+func TestSecure(t *testing.T) {
+ soa := getSoa()
+
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.TypeCovered = TypeSOA
+ sig.Algorithm = RSASHA256
+ sig.Labels = 2
+ sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
+ sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
+ sig.OrigTtl = 14400
+ sig.KeyTag = 12051
+ sig.SignerName = "miek.nl."
+ sig.Signature = "oMCbslaAVIp/8kVtLSms3tDABpcPRUgHLrOR48OOplkYo+8TeEGWwkSwaz/MRo2fB4FxW0qj/hTlIjUGuACSd+b1wKdH5GvzRJc2pFmxtCbm55ygAh4EUL0F6U5cKtGJGSXxxg6UFCQ0doJCmiGFa78LolaUOXImJrk6AFrGa0M="
+
+ key := new(DNSKEY)
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 14400
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
+
+ // It should validate. Period is checked separately, so this will keep on working
+ if sig.Verify(key, []RR{soa}) != nil {
+ t.Error("failure to validate")
+ }
+}
+
+func TestSignature(t *testing.T) {
+ sig := new(RRSIG)
+ sig.Hdr.Name = "miek.nl."
+ sig.Hdr.Class = ClassINET
+ sig.Hdr.Ttl = 3600
+ sig.TypeCovered = TypeDNSKEY
+ sig.Algorithm = RSASHA1
+ sig.Labels = 2
+ sig.OrigTtl = 4000
+ sig.Expiration = 1000 //Thu Jan 1 02:06:40 CET 1970
+ sig.Inception = 800 //Thu Jan 1 01:13:20 CET 1970
+ sig.KeyTag = 34641
+ sig.SignerName = "miek.nl."
+ sig.Signature = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"
+
+ // Should not be valid
+ if sig.ValidityPeriod(time.Now()) {
+ t.Error("should not be valid")
+ }
+
+ sig.Inception = 315565800 //Tue Jan 1 10:10:00 CET 1980
+ sig.Expiration = 4102477800 //Fri Jan 1 10:10:00 CET 2100
+ if !sig.ValidityPeriod(time.Now()) {
+ t.Error("should be valid")
+ }
+}
+
+func TestSignVerify(t *testing.T) {
+ // The record we want to sign
+ soa := new(SOA)
+ soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
+ soa.Ns = "open.nlnetlabs.nl."
+ soa.Mbox = "miekg.atoom.net."
+ soa.Serial = 1293945905
+ soa.Refresh = 14400
+ soa.Retry = 3600
+ soa.Expire = 604800
+ soa.Minttl = 86400
+
+ soa1 := new(SOA)
+ soa1.Hdr = RR_Header{"*.miek.nl.", TypeSOA, ClassINET, 14400, 0}
+ soa1.Ns = "open.nlnetlabs.nl."
+ soa1.Mbox = "miekg.atoom.net."
+ soa1.Serial = 1293945905
+ soa1.Refresh = 14400
+ soa1.Retry = 3600
+ soa1.Expire = 604800
+ soa1.Minttl = 86400
+
+ srv := new(SRV)
+ srv.Hdr = RR_Header{"srv.miek.nl.", TypeSRV, ClassINET, 14400, 0}
+ srv.Port = 1000
+ srv.Weight = 800
+ srv.Target = "web1.miek.nl."
+
+ hinfo := &HINFO{
+ Hdr: RR_Header{
+ Name: "miek.nl.",
+ Rrtype: TypeHINFO,
+ Class: ClassINET,
+ Ttl: 3789,
+ },
+ Cpu: "X",
+ Os: "Y",
+ }
+
+ // With this key
+ key := new(DNSKEY)
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 14400
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ privkey, _ := key.Generate(512)
+
+ // Fill in the values of the Sig, before signing
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.TypeCovered = soa.Hdr.Rrtype
+ sig.Labels = uint8(CountLabel(soa.Hdr.Name)) // works for all 3
+ sig.OrigTtl = soa.Hdr.Ttl
+ sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
+ sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
+ sig.KeyTag = key.KeyTag() // Get the keyfrom the Key
+ sig.SignerName = key.Hdr.Name
+ sig.Algorithm = RSASHA256
+
+ for _, r := range []RR{soa, soa1, srv, hinfo} {
+ if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{r}); err != nil {
+ t.Error("failure to sign the record:", err)
+ continue
+ }
+ if err := sig.Verify(key, []RR{r}); err != nil {
+ t.Errorf("failure to validate: %s", r.Header().Name)
+ continue
+ }
+ }
+}
+
+func Test65534(t *testing.T) {
+ t6 := new(RFC3597)
+ t6.Hdr = RR_Header{"miek.nl.", 65534, ClassINET, 14400, 0}
+ t6.Rdata = "505D870001"
+ key := new(DNSKEY)
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 14400
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ privkey, _ := key.Generate(1024)
+
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.TypeCovered = t6.Hdr.Rrtype
+ sig.Labels = uint8(CountLabel(t6.Hdr.Name))
+ sig.OrigTtl = t6.Hdr.Ttl
+ sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
+ sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
+ sig.KeyTag = key.KeyTag()
+ sig.SignerName = key.Hdr.Name
+ sig.Algorithm = RSASHA256
+ if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{t6}); err != nil {
+ t.Error(err)
+ t.Error("failure to sign the TYPE65534 record")
+ }
+ if err := sig.Verify(key, []RR{t6}); err != nil {
+ t.Error(err)
+ t.Errorf("failure to validate %s", t6.Header().Name)
+ }
+}
+
+func TestDnskey(t *testing.T) {
+ pubkey, err := ReadRR(strings.NewReader(`
+miek.nl. IN DNSKEY 256 3 10 AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL ;{id = 5240 (zsk), size = 1024b}
+`), "Kmiek.nl.+010+05240.key")
+ if err != nil {
+ t.Fatal(err)
+ }
+ privStr := `Private-key-format: v1.3
+Algorithm: 10 (RSASHA512)
+Modulus: m4wK7YV26AeROtdiCXmqLG9wPDVoMOW8vjr/EkpscEAdjXp81RvZvrlzCSjYmz9onFRgltmTl3AINnFh+t9tlW0M9C5zejxBoKFXELv8ljPYAdz2oe+pDWPhWsfvVFYg2VCjpViPM38EakyE5mhk4TDOnUd+w4TeU1hyhZTWyYs=
+PublicExponent: AQAB
+PrivateExponent: UfCoIQ/Z38l8vB6SSqOI/feGjHEl/fxIPX4euKf0D/32k30fHbSaNFrFOuIFmWMB3LimWVEs6u3dpbB9CQeCVg7hwU5puG7OtuiZJgDAhNeOnxvo5btp4XzPZrJSxR4WNQnwIiYWbl0aFlL1VGgHC/3By89ENZyWaZcMLW4KGWE=
+Prime1: yxwC6ogAu8aVcDx2wg1V0b5M5P6jP8qkRFVMxWNTw60Vkn+ECvw6YAZZBHZPaMyRYZLzPgUlyYRd0cjupy4+fQ==
+Prime2: xA1bF8M0RTIQ6+A11AoVG6GIR/aPGg5sogRkIZ7ID/sF6g9HMVU/CM2TqVEBJLRPp73cv6ZeC3bcqOCqZhz+pw==
+Exponent1: xzkblyZ96bGYxTVZm2/vHMOXswod4KWIyMoOepK6B/ZPcZoIT6omLCgtypWtwHLfqyCz3MK51Nc0G2EGzg8rFQ==
+Exponent2: Pu5+mCEb7T5F+kFNZhQadHUklt0JUHbi3hsEvVoHpEGSw3BGDQrtIflDde0/rbWHgDPM4WQY+hscd8UuTXrvLw==
+Coefficient: UuRoNqe7YHnKmQzE6iDWKTMIWTuoqqrFAmXPmKQnC+Y+BQzOVEHUo9bXdDnoI9hzXP1gf8zENMYwYLeWpuYlFQ==
+`
+ privkey, err := pubkey.(*DNSKEY).ReadPrivateKey(strings.NewReader(privStr),
+ "Kmiek.nl.+010+05240.private")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if pubkey.(*DNSKEY).PublicKey != "AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL" {
+ t.Error("pubkey is not what we've read")
+ }
+ if pubkey.(*DNSKEY).PrivateKeyString(privkey) != privStr {
+ t.Error("privkey is not what we've read")
+ t.Errorf("%v", pubkey.(*DNSKEY).PrivateKeyString(privkey))
+ }
+}
+
+func TestTag(t *testing.T) {
+ key := new(DNSKEY)
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 3600
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
+
+ tag := key.KeyTag()
+ if tag != 12051 {
+ t.Errorf("wrong key tag: %d for key %v", tag, key)
+ }
+}
+
+func TestKeyRSA(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in short mode.")
+ }
+ key := new(DNSKEY)
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 3600
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ priv, _ := key.Generate(2048)
+
+ soa := new(SOA)
+ soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
+ soa.Ns = "open.nlnetlabs.nl."
+ soa.Mbox = "miekg.atoom.net."
+ soa.Serial = 1293945905
+ soa.Refresh = 14400
+ soa.Retry = 3600
+ soa.Expire = 604800
+ soa.Minttl = 86400
+
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.TypeCovered = TypeSOA
+ sig.Algorithm = RSASHA256
+ sig.Labels = 2
+ sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
+ sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
+ sig.OrigTtl = soa.Hdr.Ttl
+ sig.KeyTag = key.KeyTag()
+ sig.SignerName = key.Hdr.Name
+
+ if err := sig.Sign(priv.(*rsa.PrivateKey), []RR{soa}); err != nil {
+ t.Error("failed to sign")
+ return
+ }
+ if err := sig.Verify(key, []RR{soa}); err != nil {
+ t.Error("failed to verify")
+ }
+}
+
+func TestKeyToDS(t *testing.T) {
+ key := new(DNSKEY)
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 3600
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
+
+ ds := key.ToDS(SHA1)
+ if strings.ToUpper(ds.Digest) != "B5121BDB5B8D86D0CC5FFAFBAAABE26C3E20BAC1" {
+ t.Errorf("wrong DS digest for SHA1\n%v", ds)
+ }
+}
+
+func TestSignRSA(t *testing.T) {
+ pub := "miek.nl. IN DNSKEY 256 3 5 AwEAAb+8lGNCxJgLS8rYVer6EnHVuIkQDghdjdtewDzU3G5R7PbMbKVRvH2Ma7pQyYceoaqWZQirSj72euPWfPxQnMy9ucCylA+FuH9cSjIcPf4PqJfdupHk9X6EBYjxrCLY4p1/yBwgyBIRJtZtAqM3ceAH2WovEJD6rTtOuHo5AluJ"
+
+ priv := `Private-key-format: v1.3
+Algorithm: 5 (RSASHA1)
+Modulus: v7yUY0LEmAtLythV6voScdW4iRAOCF2N217APNTcblHs9sxspVG8fYxrulDJhx6hqpZlCKtKPvZ649Z8/FCczL25wLKUD4W4f1xKMhw9/g+ol926keT1foQFiPGsItjinX/IHCDIEhEm1m0Cozdx4AfZai8QkPqtO064ejkCW4k=
+PublicExponent: AQAB
+PrivateExponent: YPwEmwjk5HuiROKU4xzHQ6l1hG8Iiha4cKRG3P5W2b66/EN/GUh07ZSf0UiYB67o257jUDVEgwCuPJz776zfApcCB4oGV+YDyEu7Hp/rL8KcSN0la0k2r9scKwxTp4BTJT23zyBFXsV/1wRDK1A5NxsHPDMYi2SoK63Enm/1ptk=
+Prime1: /wjOG+fD0ybNoSRn7nQ79udGeR1b0YhUA5mNjDx/x2fxtIXzygYk0Rhx9QFfDy6LOBvz92gbNQlzCLz3DJt5hw==
+Prime2: wHZsJ8OGhkp5p3mrJFZXMDc2mbYusDVTA+t+iRPdS797Tj0pjvU2HN4vTnTj8KBQp6hmnY7dLp9Y1qserySGbw==
+Exponent1: N0A7FsSRIg+IAN8YPQqlawoTtG1t1OkJ+nWrurPootScApX6iMvn8fyvw3p2k51rv84efnzpWAYiC8SUaQDNxQ==
+Exponent2: SvuYRaGyvo0zemE3oS+WRm2scxR8eiA8WJGeOc+obwOKCcBgeZblXzfdHGcEC1KaOcetOwNW/vwMA46lpLzJNw==
+Coefficient: 8+7ZN/JgByqv0NfULiFKTjtyegUcijRuyij7yNxYbCBneDvZGxJwKNi4YYXWx743pcAj4Oi4Oh86gcmxLs+hGw==
+Created: 20110302104537
+Publish: 20110302104537
+Activate: 20110302104537`
+
+ xk := testRR(pub)
+ k := xk.(*DNSKEY)
+ p, err := k.NewPrivateKey(priv)
+ if err != nil {
+ t.Error(err)
+ }
+ switch priv := p.(type) {
+ case *rsa.PrivateKey:
+ if 65537 != priv.PublicKey.E {
+ t.Error("exponenent should be 65537")
+ }
+ default:
+ t.Errorf("we should have read an RSA key: %v", priv)
+ }
+ if k.KeyTag() != 37350 {
+ t.Errorf("keytag should be 37350, got %d %v", k.KeyTag(), k)
+ }
+
+ soa := new(SOA)
+ soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
+ soa.Ns = "open.nlnetlabs.nl."
+ soa.Mbox = "miekg.atoom.net."
+ soa.Serial = 1293945905
+ soa.Refresh = 14400
+ soa.Retry = 3600
+ soa.Expire = 604800
+ soa.Minttl = 86400
+
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
+ sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
+ sig.KeyTag = k.KeyTag()
+ sig.SignerName = k.Hdr.Name
+ sig.Algorithm = k.Algorithm
+
+ sig.Sign(p.(*rsa.PrivateKey), []RR{soa})
+ if sig.Signature != "D5zsobpQcmMmYsUMLxCVEtgAdCvTu8V/IEeP4EyLBjqPJmjt96bwM9kqihsccofA5LIJ7DN91qkCORjWSTwNhzCv7bMyr2o5vBZElrlpnRzlvsFIoAZCD9xg6ZY7ZyzUJmU6IcTwG4v3xEYajcpbJJiyaw/RqR90MuRdKPiBzSo=" {
+ t.Errorf("signature is not correct: %v", sig)
+ }
+}
+
+func TestSignVerifyECDSA(t *testing.T) {
+ pub := `example.net. 3600 IN DNSKEY 257 3 14 (
+ xKYaNhWdGOfJ+nPrL8/arkwf2EY3MDJ+SErKivBVSum1
+ w/egsXvSADtNJhyem5RCOpgQ6K8X1DRSEkrbYQ+OB+v8
+ /uX45NBwY8rp65F6Glur8I/mlVNgF6W/qTI37m40 )`
+ priv := `Private-key-format: v1.2
+Algorithm: 14 (ECDSAP384SHA384)
+PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
+
+ eckey := testRR(pub)
+ privkey, err := eckey.(*DNSKEY).NewPrivateKey(priv)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // TODO: Create separate test for this
+ ds := eckey.(*DNSKEY).ToDS(SHA384)
+ if ds.KeyTag != 10771 {
+ t.Fatal("wrong keytag on DS")
+ }
+ if ds.Digest != "72d7b62976ce06438e9c0bf319013cf801f09ecc84b8d7e9495f27e305c6a9b0563a9b5f4d288405c3008a946df983d6" {
+ t.Fatal("wrong DS Digest")
+ }
+ a := testRR("www.example.net. 3600 IN A 192.0.2.1")
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"example.net.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.Expiration, _ = StringToTime("20100909102025")
+ sig.Inception, _ = StringToTime("20100812102025")
+ sig.KeyTag = eckey.(*DNSKEY).KeyTag()
+ sig.SignerName = eckey.(*DNSKEY).Hdr.Name
+ sig.Algorithm = eckey.(*DNSKEY).Algorithm
+
+ if sig.Sign(privkey.(*ecdsa.PrivateKey), []RR{a}) != nil {
+ t.Fatal("failure to sign the record")
+ }
+
+ if err := sig.Verify(eckey.(*DNSKEY), []RR{a}); err != nil {
+ t.Fatalf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
+ eckey.(*DNSKEY).String(),
+ a.String(),
+ sig.String(),
+ eckey.(*DNSKEY).PrivateKeyString(privkey),
+ err,
+ )
+ }
+}
+
+func TestSignVerifyECDSA2(t *testing.T) {
+ srv1 := testRR("srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.")
+ srv := srv1.(*SRV)
+
+ // With this key
+ key := new(DNSKEY)
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 14400
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = ECDSAP256SHA256
+ privkey, err := key.Generate(256)
+ if err != nil {
+ t.Fatal("failure to generate key")
+ }
+
+ // Fill in the values of the Sig, before signing
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.TypeCovered = srv.Hdr.Rrtype
+ sig.Labels = uint8(CountLabel(srv.Hdr.Name)) // works for all 3
+ sig.OrigTtl = srv.Hdr.Ttl
+ sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
+ sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
+ sig.KeyTag = key.KeyTag() // Get the keyfrom the Key
+ sig.SignerName = key.Hdr.Name
+ sig.Algorithm = ECDSAP256SHA256
+
+ if sig.Sign(privkey.(*ecdsa.PrivateKey), []RR{srv}) != nil {
+ t.Fatal("failure to sign the record")
+ }
+
+ err = sig.Verify(key, []RR{srv})
+ if err != nil {
+ t.Errorf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
+ key.String(),
+ srv.String(),
+ sig.String(),
+ key.PrivateKeyString(privkey),
+ err,
+ )
+ }
+}
+
+// Here the test vectors from the relevant RFCs are checked.
+// rfc6605 6.1
+func TestRFC6605P256(t *testing.T) {
+ exDNSKEY := `example.net. 3600 IN DNSKEY 257 3 13 (
+ GojIhhXUN/u4v54ZQqGSnyhWJwaubCvTmeexv7bR6edb
+ krSqQpF64cYbcB7wNcP+e+MAnLr+Wi9xMWyQLc8NAA== )`
+ exPriv := `Private-key-format: v1.2
+Algorithm: 13 (ECDSAP256SHA256)
+PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=`
+ rrDNSKEY := testRR(exDNSKEY)
+ priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ exDS := `example.net. 3600 IN DS 55648 13 2 (
+ b4c8c1fe2e7477127b27115656ad6256f424625bf5c1
+ e2770ce6d6e37df61d17 )`
+ rrDS := testRR(exDS)
+ ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)
+ if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
+ t.Errorf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
+ }
+
+ exA := `www.example.net. 3600 IN A 192.0.2.1`
+ exRRSIG := `www.example.net. 3600 IN RRSIG A 13 3 3600 (
+ 20100909100439 20100812100439 55648 example.net.
+ qx6wLYqmh+l9oCKTN6qIc+bw6ya+KJ8oMz0YP107epXA
+ yGmt+3SNruPFKG7tZoLBLlUzGGus7ZwmwWep666VCw== )`
+ rrA := testRR(exA)
+ rrRRSIG := testRR(exRRSIG)
+ if err := rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
+ t.Errorf("failure to validate the spec RRSIG: %v", err)
+ }
+
+ ourRRSIG := &RRSIG{
+ Hdr: RR_Header{
+ Ttl: rrA.Header().Ttl,
+ },
+ KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
+ SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
+ Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
+ }
+ ourRRSIG.Expiration, _ = StringToTime("20100909100439")
+ ourRRSIG.Inception, _ = StringToTime("20100812100439")
+ err = ourRRSIG.Sign(priv.(*ecdsa.PrivateKey), []RR{rrA})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
+ t.Errorf("failure to validate our RRSIG: %v", err)
+ }
+
+ // Signatures are randomized
+ rrRRSIG.(*RRSIG).Signature = ""
+ ourRRSIG.Signature = ""
+ if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
+ t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
+ }
+}
+
+// rfc6605 6.2
+func TestRFC6605P384(t *testing.T) {
+ exDNSKEY := `example.net. 3600 IN DNSKEY 257 3 14 (
+ xKYaNhWdGOfJ+nPrL8/arkwf2EY3MDJ+SErKivBVSum1
+ w/egsXvSADtNJhyem5RCOpgQ6K8X1DRSEkrbYQ+OB+v8
+ /uX45NBwY8rp65F6Glur8I/mlVNgF6W/qTI37m40 )`
+ exPriv := `Private-key-format: v1.2
+Algorithm: 14 (ECDSAP384SHA384)
+PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
+ rrDNSKEY := testRR(exDNSKEY)
+ priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ exDS := `example.net. 3600 IN DS 10771 14 4 (
+ 72d7b62976ce06438e9c0bf319013cf801f09ecc84b8
+ d7e9495f27e305c6a9b0563a9b5f4d288405c3008a94
+ 6df983d6 )`
+ rrDS := testRR(exDS)
+ ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA384)
+ if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
+ t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
+ }
+
+ exA := `www.example.net. 3600 IN A 192.0.2.1`
+ exRRSIG := `www.example.net. 3600 IN RRSIG A 14 3 3600 (
+ 20100909102025 20100812102025 10771 example.net.
+ /L5hDKIvGDyI1fcARX3z65qrmPsVz73QD1Mr5CEqOiLP
+ 95hxQouuroGCeZOvzFaxsT8Glr74hbavRKayJNuydCuz
+ WTSSPdz7wnqXL5bdcJzusdnI0RSMROxxwGipWcJm )`
+ rrA := testRR(exA)
+ rrRRSIG := testRR(exRRSIG)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
+ t.Errorf("failure to validate the spec RRSIG: %v", err)
+ }
+
+ ourRRSIG := &RRSIG{
+ Hdr: RR_Header{
+ Ttl: rrA.Header().Ttl,
+ },
+ KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
+ SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
+ Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
+ }
+ ourRRSIG.Expiration, _ = StringToTime("20100909102025")
+ ourRRSIG.Inception, _ = StringToTime("20100812102025")
+ err = ourRRSIG.Sign(priv.(*ecdsa.PrivateKey), []RR{rrA})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
+ t.Errorf("failure to validate our RRSIG: %v", err)
+ }
+
+ // Signatures are randomized
+ rrRRSIG.(*RRSIG).Signature = ""
+ ourRRSIG.Signature = ""
+ if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
+ t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
+ }
+}
+
+func TestInvalidRRSet(t *testing.T) {
+ goodRecords := make([]RR, 2)
+ goodRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
+ goodRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}}
+
+ // Generate key
+ keyname := "cloudflare.com."
+ key := &DNSKEY{
+ Hdr: RR_Header{Name: keyname, Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 0},
+ Algorithm: ECDSAP256SHA256,
+ Flags: ZONE,
+ Protocol: 3,
+ }
+ privatekey, err := key.Generate(256)
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+
+ // Need to fill in: Inception, Expiration, KeyTag, SignerName and Algorithm
+ curTime := time.Now()
+ signature := &RRSIG{
+ Inception: uint32(curTime.Unix()),
+ Expiration: uint32(curTime.Add(time.Hour).Unix()),
+ KeyTag: key.KeyTag(),
+ SignerName: keyname,
+ Algorithm: ECDSAP256SHA256,
+ }
+
+ // Inconsistent name between records
+ badRecords := make([]RR, 2)
+ badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
+ badRecords[1] = &TXT{Hdr: RR_Header{Name: "nama.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}}
+
+ if IsRRset(badRecords) {
+ t.Fatal("Record set with inconsistent names considered valid")
+ }
+
+ badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
+ badRecords[1] = &A{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeA, Class: ClassINET, Ttl: 0}}
+
+ if IsRRset(badRecords) {
+ t.Fatal("Record set with inconsistent record types considered valid")
+ }
+
+ badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
+ badRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassCHAOS, Ttl: 0}, Txt: []string{"_o/"}}
+
+ if IsRRset(badRecords) {
+ t.Fatal("Record set with inconsistent record class considered valid")
+ }
+
+ // Sign the good record set and then make sure verification fails on the bad record set
+ if err := signature.Sign(privatekey.(crypto.Signer), goodRecords); err != nil {
+ t.Fatal("Signing good records failed")
+ }
+
+ if err := signature.Verify(key, badRecords); err != ErrRRset {
+ t.Fatal("Verification did not return ErrRRset with inconsistent records")
+ }
+}
diff --git a/vendor/github.com/miekg/dns/dnsutil/util.go b/vendor/github.com/miekg/dns/dnsutil/util.go
new file mode 100644
index 0000000..76ac4de
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dnsutil/util.go
@@ -0,0 +1,83 @@
+// Package dnsutil contains higher-level methods useful with the dns
+// package. While package dns implements the DNS protocols itself,
+// these functions are related but not directly required for protocol
+// processing. They are often useful in preparing input/output of the
+// functions in package dns.
+package dnsutil
+
+import (
+ "strings"
+
+ "github.com/miekg/dns"
+)
+
+// AddOrigin adds origin to s if s is not already a FQDN.
+// Note that the result may not be a FQDN. If origin does not end
+// with a ".", the result won't either.
+// This implements the zonefile convention (specified in RFC 1035,
+// Section "5.1. Format") that "@" represents the
+// apex (bare) domain. i.e. AddOrigin("@", "foo.com.") returns "foo.com.".
+func AddOrigin(s, origin string) string {
+ // ("foo.", "origin.") -> "foo." (already a FQDN)
+ // ("foo", "origin.") -> "foo.origin."
+ // ("foo", "origin") -> "foo.origin"
+ // ("foo", ".") -> "foo." (Same as dns.Fqdn())
+ // ("foo.", ".") -> "foo." (Same as dns.Fqdn())
+ // ("@", "origin.") -> "origin." (@ represents the apex (bare) domain)
+ // ("", "origin.") -> "origin." (not obvious)
+ // ("foo", "") -> "foo" (not obvious)
+
+ if dns.IsFqdn(s) {
+ return s // s is already a FQDN, no need to mess with it.
+ }
+ if len(origin) == 0 {
+ return s // Nothing to append.
+ }
+ if s == "@" || len(s) == 0 {
+ return origin // Expand apex.
+ }
+ if origin == "." {
+ return dns.Fqdn(s)
+ }
+
+ return s + "." + origin // The simple case.
+}
+
+// TrimDomainName trims origin from s if s is a subdomain.
+// This function will never return "", but returns "@" instead (@ represents the apex domain).
+func TrimDomainName(s, origin string) string {
+ // An apex (bare) domain is always returned as "@".
+ // If the return value ends in a ".", the domain was not the suffix.
+ // origin can end in "." or not. Either way the results should be the same.
+
+ if len(s) == 0 {
+ return "@"
+ }
+ // Someone is using TrimDomainName(s, ".") to remove a dot if it exists.
+ if origin == "." {
+ return strings.TrimSuffix(s, origin)
+ }
+
+ original := s
+ s = dns.Fqdn(s)
+ origin = dns.Fqdn(origin)
+
+ if !dns.IsSubDomain(origin, s) {
+ return original
+ }
+
+ slabels := dns.Split(s)
+ olabels := dns.Split(origin)
+ m := dns.CompareDomainName(s, origin)
+ if len(olabels) == m {
+ if len(olabels) == len(slabels) {
+ return "@" // origin == s
+ }
+ if (s[0] == '.') && (len(slabels) == (len(olabels) + 1)) {
+ return "@" // TrimDomainName(".foo.", "foo.")
+ }
+ }
+
+ // Return the first (len-m) labels:
+ return s[:slabels[len(slabels)-m]-1]
+}
diff --git a/vendor/github.com/miekg/dns/dnsutil/util_test.go b/vendor/github.com/miekg/dns/dnsutil/util_test.go
new file mode 100644
index 0000000..6754789
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dnsutil/util_test.go
@@ -0,0 +1,130 @@
+package dnsutil
+
+import "testing"
+
+func TestAddOrigin(t *testing.T) {
+ var tests = []struct{ e1, e2, expected string }{
+ {"@", "example.com", "example.com"},
+ {"foo", "example.com", "foo.example.com"},
+ {"foo.", "example.com", "foo."},
+ {"@", "example.com.", "example.com."},
+ {"foo", "example.com.", "foo.example.com."},
+ {"foo.", "example.com.", "foo."},
+ {"example.com", ".", "example.com."},
+ {"example.com.", ".", "example.com."},
+ // Oddball tests:
+ // In general origin should not be "" or "." but at least
+ // these tests verify we don't crash and will keep results
+ // from changing unexpectedly.
+ {"*.", "", "*."},
+ {"@", "", "@"},
+ {"foobar", "", "foobar"},
+ {"foobar.", "", "foobar."},
+ {"*.", ".", "*."},
+ {"@", ".", "."},
+ {"foobar", ".", "foobar."},
+ {"foobar.", ".", "foobar."},
+ }
+ for _, test := range tests {
+ actual := AddOrigin(test.e1, test.e2)
+ if test.expected != actual {
+ t.Errorf("AddOrigin(%#v, %#v) expected %#v, got %#v\n", test.e1, test.e2, test.expected, actual)
+ }
+ }
+}
+
+func TestTrimDomainName(t *testing.T) {
+ // Basic tests.
+ // Try trimming "example.com" and "example.com." from typical use cases.
+ testsEx := []struct{ experiment, expected string }{
+ {"foo.example.com", "foo"},
+ {"foo.example.com.", "foo"},
+ {".foo.example.com", ".foo"},
+ {".foo.example.com.", ".foo"},
+ {"*.example.com", "*"},
+ {"example.com", "@"},
+ {"example.com.", "@"},
+ {"com.", "com."},
+ {"foo.", "foo."},
+ {"serverfault.com.", "serverfault.com."},
+ {"serverfault.com", "serverfault.com"},
+ {".foo.ronco.com", ".foo.ronco.com"},
+ {".foo.ronco.com.", ".foo.ronco.com."},
+ }
+ for _, dom := range []string{"example.com", "example.com."} {
+ for i, test := range testsEx {
+ actual := TrimDomainName(test.experiment, dom)
+ if test.expected != actual {
+ t.Errorf("%d TrimDomainName(%#v, %#v): expected %v, got %v\n", i, test.experiment, dom, test.expected, actual)
+ }
+ }
+ }
+
+ // Paranoid tests.
+ // These test shouldn't be needed but I was weary of off-by-one errors.
+ // In theory, these can't happen because there are no single-letter TLDs,
+ // but it is good to exercize the code this way.
+ tests := []struct{ experiment, expected string }{
+ {"", "@"},
+ {".", "."},
+ {"a.b.c.d.e.f.", "a.b.c.d.e"},
+ {"b.c.d.e.f.", "b.c.d.e"},
+ {"c.d.e.f.", "c.d.e"},
+ {"d.e.f.", "d.e"},
+ {"e.f.", "e"},
+ {"f.", "@"},
+ {".a.b.c.d.e.f.", ".a.b.c.d.e"},
+ {".b.c.d.e.f.", ".b.c.d.e"},
+ {".c.d.e.f.", ".c.d.e"},
+ {".d.e.f.", ".d.e"},
+ {".e.f.", ".e"},
+ {".f.", "@"},
+ {"a.b.c.d.e.f", "a.b.c.d.e"},
+ {"a.b.c.d.e.", "a.b.c.d.e."},
+ {"a.b.c.d.e", "a.b.c.d.e"},
+ {"a.b.c.d.", "a.b.c.d."},
+ {"a.b.c.d", "a.b.c.d"},
+ {"a.b.c.", "a.b.c."},
+ {"a.b.c", "a.b.c"},
+ {"a.b.", "a.b."},
+ {"a.b", "a.b"},
+ {"a.", "a."},
+ {"a", "a"},
+ {".a.b.c.d.e.f", ".a.b.c.d.e"},
+ {".a.b.c.d.e.", ".a.b.c.d.e."},
+ {".a.b.c.d.e", ".a.b.c.d.e"},
+ {".a.b.c.d.", ".a.b.c.d."},
+ {".a.b.c.d", ".a.b.c.d"},
+ {".a.b.c.", ".a.b.c."},
+ {".a.b.c", ".a.b.c"},
+ {".a.b.", ".a.b."},
+ {".a.b", ".a.b"},
+ {".a.", ".a."},
+ {".a", ".a"},
+ }
+ for _, dom := range []string{"f", "f."} {
+ for i, test := range tests {
+ actual := TrimDomainName(test.experiment, dom)
+ if test.expected != actual {
+ t.Errorf("%d TrimDomainName(%#v, %#v): expected %v, got %v\n", i, test.experiment, dom, test.expected, actual)
+ }
+ }
+ }
+
+ // Test cases for bugs found in the wild.
+ // These test cases provide both origin, s, and the expected result.
+ // If you find a bug in the while, this is probably the easiest place
+ // to add it as a test case.
+ var testsWild = []struct{ e1, e2, expected string }{
+ {"mathoverflow.net.", ".", "mathoverflow.net"},
+ {"mathoverflow.net", ".", "mathoverflow.net"},
+ {"", ".", "@"},
+ {"@", ".", "@"},
+ }
+ for i, test := range testsWild {
+ actual := TrimDomainName(test.e1, test.e2)
+ if test.expected != actual {
+ t.Errorf("%d TrimDomainName(%#v, %#v): expected %v, got %v\n", i, test.e1, test.e2, test.expected, actual)
+ }
+ }
+}
diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go
index e38753d..26e288d 100644
--- a/vendor/github.com/miekg/dns/doc.go
+++ b/vendor/github.com/miekg/dns/doc.go
@@ -1,7 +1,7 @@
/*
Package dns implements a full featured interface to the Domain Name System.
Server- and client-side programming is supported.
-The package allows complete control over what is send out to the DNS. The package
+The package allows complete control over what is sent out to the DNS. The package
API follows the less-is-more principle, by presenting a small, clean interface.
The package dns supports (asynchronous) querying/replying, incoming/outgoing zone transfers,
@@ -14,7 +14,7 @@ Basic usage pattern for creating a new resource record:
r := new(dns.MX)
r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX,
- Class: dns.ClassINET, Ttl: 3600}
+ Class: dns.ClassINET, Ttl: 3600}
r.Preference = 10
r.Mx = "mx.miek.nl."
@@ -22,16 +22,16 @@ Or directly from a string:
mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.")
-Or when the default TTL (3600) and class (IN) suit you:
+Or when the default origin (.) and TTL (3600) and class (IN) suit you:
- mx, err := dns.NewRR("miek.nl. MX 10 mx.miek.nl.")
+ mx, err := dns.NewRR("miek.nl MX 10 mx.miek.nl")
Or even:
mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek")
In the DNS messages are exchanged, these messages contain resource
-records (sets). Use pattern for creating a message:
+records (sets). Use pattern for creating a message:
m := new(dns.Msg)
m.SetQuestion("miek.nl.", dns.TypeMX)
@@ -51,7 +51,7 @@ The following is slightly more verbose, but more flexible:
m1.Question = make([]dns.Question, 1)
m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET}
-After creating a message it can be send.
+After creating a message it can be sent.
Basic use pattern for synchronous querying the DNS at a
server configured on 127.0.0.1 and port 53:
@@ -63,7 +63,23 @@ class) is as easy as setting:
c.SingleInflight = true
-If these "advanced" features are not needed, a simple UDP query can be send,
+More advanced options are availabe using a net.Dialer and the corresponding API.
+For example it is possible to set a timeout, or to specify a source IP address
+and port to use for the connection:
+
+ c := new(dns.Client)
+ laddr := net.UDPAddr{
+ IP: net.ParseIP("[::1]"),
+ Port: 12345,
+ Zone: "",
+ }
+ d := net.Dialer{
+ Timeout: 200 * time.Millisecond,
+ LocalAddr: &laddr,
+ }
+ in, rtt, err := c.ExchangeWithDialer(&d, m1, "8.8.8.8:53")
+
+If these "advanced" features are not needed, a simple UDP query can be sent,
with:
in, err := dns.Exchange(m1, "127.0.0.1:53")
@@ -152,6 +168,11 @@ Basic use pattern when querying with a TSIG name "axfr." (note that these key na
must be fully qualified - as they are domain names) and the base64 secret
"so6ZGir4GPAqINNh9U5c3A==":
+If an incoming message contains a TSIG record it MUST be the last record in
+the additional section (RFC2845 3.2). This means that you should make the
+call to SetTsig last, right before executing the query. If you make any
+changes to the RRset after calling SetTsig() the signature will be incorrect.
+
c := new(dns.Client)
c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
m := new(dns.Msg)
diff --git a/vendor/github.com/miekg/dns/dyn_test.go b/vendor/github.com/miekg/dns/dyn_test.go
new file mode 100644
index 0000000..09986a5
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dyn_test.go
@@ -0,0 +1,3 @@
+package dns
+
+// Find better solution
diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go
index 465b85f..a6a36bd 100644
--- a/vendor/github.com/miekg/dns/edns.go
+++ b/vendor/github.com/miekg/dns/edns.go
@@ -21,6 +21,7 @@ const (
EDNS0EXPIRE = 0x9 // EDNS0 expire
EDNS0COOKIE = 0xa // EDNS0 Cookie
EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (RFC7828)
+ EDNS0PADDING = 0xc // EDNS0 padding (RFC7830)
EDNS0SUBNETDRAFT = 0x50fa // Don't use! Use EDNS0SUBNET
EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (RFC6891)
EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (RFC6891)
@@ -74,6 +75,8 @@ func (rr *OPT) String() string {
s += "\n; NSEC3 HASH UNDERSTOOD: " + o.String()
case *EDNS0_LOCAL:
s += "\n; LOCAL OPT: " + o.String()
+ case *EDNS0_PADDING:
+ s += "\n; PADDING: " + o.String()
}
}
return s
@@ -103,15 +106,12 @@ func (rr *OPT) SetVersion(v uint8) {
// ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL).
func (rr *OPT) ExtendedRcode() int {
- return int((rr.Hdr.Ttl&0xFF000000)>>24) + 15
+ return int((rr.Hdr.Ttl & 0xFF000000) >> 24)
}
// SetExtendedRcode sets the EDNS extended RCODE field.
func (rr *OPT) SetExtendedRcode(v uint8) {
- if v < RcodeBadVers { // Smaller than 16.. Use the 4 bits you have!
- return
- }
- rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v-15) << 24)
+ rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v) << 24)
}
// UDPSize returns the UDP buffer size.
@@ -157,7 +157,7 @@ type EDNS0 interface {
String() string
}
-// The nsid EDNS0 option is used to retrieve a nameserver
+// EDNS0_NSID option is used to retrieve a nameserver
// identifier. When sending a request Nsid must be set to the empty string
// The identifier is an opaque string encoded as hex.
// Basic use pattern for creating an nsid option:
@@ -182,7 +182,8 @@ func (e *EDNS0_NSID) pack() ([]byte, error) {
return h, nil
}
-func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID }
+// Option implements the EDNS0 interface.
+func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID } // Option returns the option code.
func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil }
func (e *EDNS0_NSID) String() string { return string(e.Nsid) }
@@ -197,7 +198,7 @@ func (e *EDNS0_NSID) String() string { return string(e.Nsid) }
// e := new(dns.EDNS0_SUBNET)
// e.Code = dns.EDNS0SUBNET
// e.Family = 1 // 1 for IPv4 source address, 2 for IPv6
-// e.SourceNetMask = 32 // 32 for IPV4, 128 for IPv6
+// e.SourceNetmask = 32 // 32 for IPV4, 128 for IPv6
// e.SourceScope = 0
// e.Address = net.ParseIP("127.0.0.1").To4() // for IPv4
// // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6
@@ -216,6 +217,7 @@ type EDNS0_SUBNET struct {
DraftOption bool // Set to true if using the old (0x50fa) option code
}
+// Option implements the EDNS0 interface.
func (e *EDNS0_SUBNET) Option() uint16 {
if e.DraftOption {
return EDNS0SUBNETDRAFT
@@ -229,6 +231,12 @@ func (e *EDNS0_SUBNET) pack() ([]byte, error) {
b[2] = e.SourceNetmask
b[3] = e.SourceScope
switch e.Family {
+ case 0:
+ // "dig" sets AddressFamily to 0 if SourceNetmask is also 0
+ // We might don't need to complain either
+ if e.SourceNetmask != 0 {
+ return nil, errors.New("dns: bad address family")
+ }
case 1:
if e.SourceNetmask > net.IPv4len*8 {
return nil, errors.New("dns: bad netmask")
@@ -263,6 +271,13 @@ func (e *EDNS0_SUBNET) unpack(b []byte) error {
e.SourceNetmask = b[2]
e.SourceScope = b[3]
switch e.Family {
+ case 0:
+ // "dig" sets AddressFamily to 0 if SourceNetmask is also 0
+ // It's okay to accept such a packet
+ if e.SourceNetmask != 0 {
+ return errors.New("dns: bad address family")
+ }
+ e.Address = net.IPv4(0, 0, 0, 0)
case 1:
if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 {
return errors.New("dns: bad netmask")
@@ -301,7 +316,7 @@ func (e *EDNS0_SUBNET) String() (s string) {
return
}
-// The Cookie EDNS0 option
+// The EDNS0_COOKIE option is used to add a DNS Cookie to a message.
//
// o := new(dns.OPT)
// o.Hdr.Name = "."
@@ -332,6 +347,7 @@ func (e *EDNS0_COOKIE) pack() ([]byte, error) {
return h, nil
}
+// Option implements the EDNS0 interface.
func (e *EDNS0_COOKIE) Option() uint16 { return EDNS0COOKIE }
func (e *EDNS0_COOKIE) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil }
func (e *EDNS0_COOKIE) String() string { return e.Cookie }
@@ -353,6 +369,7 @@ type EDNS0_UL struct {
Lease uint32
}
+// Option implements the EDNS0 interface.
func (e *EDNS0_UL) Option() uint16 { return EDNS0UL }
func (e *EDNS0_UL) String() string { return strconv.FormatUint(uint64(e.Lease), 10) }
@@ -382,6 +399,7 @@ type EDNS0_LLQ struct {
LeaseLife uint32
}
+// Option implements the EDNS0 interface.
func (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ }
func (e *EDNS0_LLQ) pack() ([]byte, error) {
@@ -418,6 +436,7 @@ type EDNS0_DAU struct {
AlgCode []uint8
}
+// Option implements the EDNS0 interface.
func (e *EDNS0_DAU) Option() uint16 { return EDNS0DAU }
func (e *EDNS0_DAU) pack() ([]byte, error) { return e.AlgCode, nil }
func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = b; return nil }
@@ -439,6 +458,7 @@ type EDNS0_DHU struct {
AlgCode []uint8
}
+// Option implements the EDNS0 interface.
func (e *EDNS0_DHU) Option() uint16 { return EDNS0DHU }
func (e *EDNS0_DHU) pack() ([]byte, error) { return e.AlgCode, nil }
func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = b; return nil }
@@ -460,6 +480,7 @@ type EDNS0_N3U struct {
AlgCode []uint8
}
+// Option implements the EDNS0 interface.
func (e *EDNS0_N3U) Option() uint16 { return EDNS0N3U }
func (e *EDNS0_N3U) pack() ([]byte, error) { return e.AlgCode, nil }
func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = b; return nil }
@@ -482,6 +503,7 @@ type EDNS0_EXPIRE struct {
Expire uint32
}
+// Option implements the EDNS0 interface.
func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE }
func (e *EDNS0_EXPIRE) String() string { return strconv.FormatUint(uint64(e.Expire), 10) }
@@ -520,6 +542,7 @@ type EDNS0_LOCAL struct {
Data []byte
}
+// Option implements the EDNS0 interface.
func (e *EDNS0_LOCAL) Option() uint16 { return e.Code }
func (e *EDNS0_LOCAL) String() string {
return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data)
@@ -543,15 +566,16 @@ func (e *EDNS0_LOCAL) unpack(b []byte) error {
return nil
}
+// EDNS0_TCP_KEEPALIVE is an EDNS0 option that instructs the server to keep
+// the TCP connection alive. See RFC 7828.
type EDNS0_TCP_KEEPALIVE struct {
Code uint16 // Always EDNSTCPKEEPALIVE
Length uint16 // the value 0 if the TIMEOUT is omitted, the value 2 if it is present;
Timeout uint16 // an idle timeout value for the TCP connection, specified in units of 100 milliseconds, encoded in network byte order.
}
-func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 {
- return EDNS0TCPKEEPALIVE
-}
+// Option implements the EDNS0 interface.
+func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 { return EDNS0TCPKEEPALIVE }
func (e *EDNS0_TCP_KEEPALIVE) pack() ([]byte, error) {
if e.Timeout != 0 && e.Length != 2 {
@@ -595,3 +619,16 @@ func (e *EDNS0_TCP_KEEPALIVE) String() (s string) {
}
return
}
+
+// EDNS0_PADDING option is used to add padding to a request/response. The default
+// value of padding SHOULD be 0x0 but other values MAY be used, for instance if
+// compression is applied before encryption which may break signatures.
+type EDNS0_PADDING struct {
+ Padding []byte
+}
+
+// Option implements the EDNS0 interface.
+func (e *EDNS0_PADDING) Option() uint16 { return EDNS0PADDING }
+func (e *EDNS0_PADDING) pack() ([]byte, error) { return e.Padding, nil }
+func (e *EDNS0_PADDING) unpack(b []byte) error { e.Padding = b; return nil }
+func (e *EDNS0_PADDING) String() string { return fmt.Sprintf("%0X", e.Padding) }
diff --git a/vendor/github.com/miekg/dns/edns_test.go b/vendor/github.com/miekg/dns/edns_test.go
new file mode 100644
index 0000000..f7cf157
--- /dev/null
+++ b/vendor/github.com/miekg/dns/edns_test.go
@@ -0,0 +1,68 @@
+package dns
+
+import "testing"
+
+func TestOPTTtl(t *testing.T) {
+ e := &OPT{}
+ e.Hdr.Name = "."
+ e.Hdr.Rrtype = TypeOPT
+
+ // verify the default setting of DO=0
+ if e.Do() {
+ t.Errorf("DO bit should be zero")
+ }
+
+ // There are 6 possible invocations of SetDo():
+ //
+ // 1. Starting with DO=0, using SetDo()
+ // 2. Starting with DO=0, using SetDo(true)
+ // 3. Starting with DO=0, using SetDo(false)
+ // 4. Starting with DO=1, using SetDo()
+ // 5. Starting with DO=1, using SetDo(true)
+ // 6. Starting with DO=1, using SetDo(false)
+
+ // verify that invoking SetDo() sets DO=1 (TEST #1)
+ e.SetDo()
+ if !e.Do() {
+ t.Errorf("DO bit should be non-zero")
+ }
+ // verify that using SetDo(true) works when DO=1 (TEST #5)
+ e.SetDo(true)
+ if !e.Do() {
+ t.Errorf("DO bit should still be non-zero")
+ }
+ // verify that we can use SetDo(false) to set DO=0 (TEST #6)
+ e.SetDo(false)
+ if e.Do() {
+ t.Errorf("DO bit should be zero")
+ }
+ // verify that if we call SetDo(false) when DO=0 that it is unchanged (TEST #3)
+ e.SetDo(false)
+ if e.Do() {
+ t.Errorf("DO bit should still be zero")
+ }
+ // verify that using SetDo(true) works for DO=0 (TEST #2)
+ e.SetDo(true)
+ if !e.Do() {
+ t.Errorf("DO bit should be non-zero")
+ }
+ // verify that using SetDo() works for DO=1 (TEST #4)
+ e.SetDo()
+ if !e.Do() {
+ t.Errorf("DO bit should be non-zero")
+ }
+
+ if e.Version() != 0 {
+ t.Errorf("version should be non-zero")
+ }
+
+ e.SetVersion(42)
+ if e.Version() != 42 {
+ t.Errorf("set 42, expected %d, got %d", 42, e.Version())
+ }
+
+ e.SetExtendedRcode(42)
+ if e.ExtendedRcode() != 42 {
+ t.Errorf("set 42, expected %d, got %d", 42, e.ExtendedRcode())
+ }
+}
diff --git a/vendor/github.com/miekg/dns/example_test.go b/vendor/github.com/miekg/dns/example_test.go
new file mode 100644
index 0000000..64c1496
--- /dev/null
+++ b/vendor/github.com/miekg/dns/example_test.go
@@ -0,0 +1,146 @@
+package dns_test
+
+import (
+ "errors"
+ "fmt"
+ "log"
+ "net"
+
+ "github.com/miekg/dns"
+)
+
+// Retrieve the MX records for miek.nl.
+func ExampleMX() {
+ config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
+ c := new(dns.Client)
+ m := new(dns.Msg)
+ m.SetQuestion("miek.nl.", dns.TypeMX)
+ m.RecursionDesired = true
+ r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
+ if err != nil {
+ return
+ }
+ if r.Rcode != dns.RcodeSuccess {
+ return
+ }
+ for _, a := range r.Answer {
+ if mx, ok := a.(*dns.MX); ok {
+ fmt.Printf("%s\n", mx.String())
+ }
+ }
+}
+
+// Retrieve the DNSKEY records of a zone and convert them
+// to DS records for SHA1, SHA256 and SHA384.
+func ExampleDS() {
+ config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
+ c := new(dns.Client)
+ m := new(dns.Msg)
+ zone := "miek.nl"
+ m.SetQuestion(dns.Fqdn(zone), dns.TypeDNSKEY)
+ m.SetEdns0(4096, true)
+ r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
+ if err != nil {
+ return
+ }
+ if r.Rcode != dns.RcodeSuccess {
+ return
+ }
+ for _, k := range r.Answer {
+ if key, ok := k.(*dns.DNSKEY); ok {
+ for _, alg := range []uint8{dns.SHA1, dns.SHA256, dns.SHA384} {
+ fmt.Printf("%s; %d\n", key.ToDS(alg).String(), key.Flags)
+ }
+ }
+ }
+}
+
+const TypeAPAIR = 0x0F99
+
+type APAIR struct {
+ addr [2]net.IP
+}
+
+func NewAPAIR() dns.PrivateRdata { return new(APAIR) }
+
+func (rd *APAIR) String() string { return rd.addr[0].String() + " " + rd.addr[1].String() }
+func (rd *APAIR) Parse(txt []string) error {
+ if len(txt) != 2 {
+ return errors.New("two addresses required for APAIR")
+ }
+ for i, s := range txt {
+ ip := net.ParseIP(s)
+ if ip == nil {
+ return errors.New("invalid IP in APAIR text representation")
+ }
+ rd.addr[i] = ip
+ }
+ return nil
+}
+
+func (rd *APAIR) Pack(buf []byte) (int, error) {
+ b := append([]byte(rd.addr[0]), []byte(rd.addr[1])...)
+ n := copy(buf, b)
+ if n != len(b) {
+ return n, dns.ErrBuf
+ }
+ return n, nil
+}
+
+func (rd *APAIR) Unpack(buf []byte) (int, error) {
+ ln := net.IPv4len * 2
+ if len(buf) != ln {
+ return 0, errors.New("invalid length of APAIR rdata")
+ }
+ cp := make([]byte, ln)
+ copy(cp, buf) // clone bytes to use them in IPs
+
+ rd.addr[0] = net.IP(cp[:3])
+ rd.addr[1] = net.IP(cp[4:])
+
+ return len(buf), nil
+}
+
+func (rd *APAIR) Copy(dest dns.PrivateRdata) error {
+ cp := make([]byte, rd.Len())
+ _, err := rd.Pack(cp)
+ if err != nil {
+ return err
+ }
+
+ d := dest.(*APAIR)
+ d.addr[0] = net.IP(cp[:3])
+ d.addr[1] = net.IP(cp[4:])
+ return nil
+}
+
+func (rd *APAIR) Len() int {
+ return net.IPv4len * 2
+}
+
+func ExamplePrivateHandle() {
+ dns.PrivateHandle("APAIR", TypeAPAIR, NewAPAIR)
+ defer dns.PrivateHandleRemove(TypeAPAIR)
+
+ rr, err := dns.NewRR("miek.nl. APAIR (1.2.3.4 1.2.3.5)")
+ if err != nil {
+ log.Fatal("could not parse APAIR record: ", err)
+ }
+ fmt.Println(rr)
+ // Output: miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5
+
+ m := new(dns.Msg)
+ m.Id = 12345
+ m.SetQuestion("miek.nl.", TypeAPAIR)
+ m.Answer = append(m.Answer, rr)
+
+ fmt.Println(m)
+ // ;; opcode: QUERY, status: NOERROR, id: 12345
+ // ;; flags: rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
+ //
+ // ;; QUESTION SECTION:
+ // ;miek.nl. IN APAIR
+ //
+ // ;; ANSWER SECTION:
+ // miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5
+}
diff --git a/vendor/github.com/miekg/dns/fuzz_test.go b/vendor/github.com/miekg/dns/fuzz_test.go
new file mode 100644
index 0000000..2558697
--- /dev/null
+++ b/vendor/github.com/miekg/dns/fuzz_test.go
@@ -0,0 +1,25 @@
+package dns
+
+import "testing"
+
+func TestFuzzString(t *testing.T) {
+ testcases := []string{"", " MINFO ", " RP ", " NSEC 0 0", " \" NSEC 0 0\"", " \" MINFO \"",
+ ";a ", ";a����������",
+ " NSAP O ", " NSAP N ",
+ " TYPE4 TYPE6a789a3bc0045c8a5fb42c7d1bd998f5444 IN 9579b47d46817afbd17273e6",
+ " TYPE45 3 3 4147994 TYPE\\(\\)\\)\\(\\)\\(\\(\\)\\(\\)\\)\\)\\(\\)\\(\\)\\(\\(\\R 948\"\")\\(\\)\\)\\)\\(\\ ",
+ "$GENERATE 0-3 ${441189,5039418474430,o}",
+ "$INCLUDE 00 TYPE00000000000n ",
+ "$INCLUDE PE4 TYPE061463623/727071511 \\(\\)\\$GENERATE 6-462/0",
+ }
+ for i, tc := range testcases {
+ rr, err := NewRR(tc)
+ if err == nil {
+ // rr can be nil because we can (for instance) just parse a comment
+ if rr == nil {
+ continue
+ }
+ t.Fatalf("parsed mailformed RR %d: %s", i, rr.String())
+ }
+ }
+}
diff --git a/vendor/github.com/miekg/dns/idn/code_points.go b/vendor/github.com/miekg/dns/idn/code_points.go
new file mode 100644
index 0000000..129c374
--- /dev/null
+++ b/vendor/github.com/miekg/dns/idn/code_points.go
@@ -0,0 +1,2346 @@
+package idn
+
+const (
+ propertyUnknown property = iota // unknown character property
+ propertyPVALID // allowed to be used in IDNs
+ propertyCONTEXTJ // invisible or problematic characters (join controls)
+ propertyCONTEXTO // invisible or problematic characters (others)
+ propertyDISALLOWED // should not be included in IDNs
+ propertyUNASSIGNED // code points that are not designated in the Unicode Standard
+)
+
+// property stores the property of a code point, as described in RFC 5892,
+// section 1
+type property int
+
+// codePoints list all code points in Unicode Character Database (UCD) Format
+// according to RFC 5892, appendix B.1. Thanks to libidn2 (GNU) -
+// http://www.gnu.org/software/libidn/libidn2/
+var codePoints = []struct {
+ start rune
+ end rune
+ state property
+}{
+ {0x0000, 0x002C, propertyDISALLOWED}, // ..COMMA
+ {0x002D, 0x0, propertyPVALID}, // HYPHEN-MINUS
+ {0x002E, 0x002F, propertyDISALLOWED}, // FULL STOP..SOLIDUS
+ {0x0030, 0x0039, propertyPVALID}, // DIGIT ZERO..DIGIT NINE
+ {0x003A, 0x0060, propertyDISALLOWED}, // COLON..GRAVE ACCENT
+ {0x0041, 0x005A, propertyPVALID}, // LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z
+ {0x0061, 0x007A, propertyPVALID}, // LATIN SMALL LETTER A..LATIN SMALL LETTER Z
+ {0x007B, 0x00B6, propertyDISALLOWED}, // LEFT CURLY BRACKET..PILCROW SIGN
+ {0x00B7, 0x0, propertyCONTEXTO}, // MIDDLE DOT
+ {0x00B8, 0x00DE, propertyDISALLOWED}, // CEDILLA..LATIN CAPITAL LETTER THORN
+ {0x00DF, 0x00F6, propertyPVALID}, // LATIN SMALL LETTER SHARP S..LATIN SMALL LETT
+ {0x00F7, 0x0, propertyDISALLOWED}, // DIVISION SIGN
+ {0x00F8, 0x00FF, propertyPVALID}, // LATIN SMALL LETTER O WITH STROKE..LATIN SMAL
+ {0x0100, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH MACRON
+ {0x0101, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH MACRON
+ {0x0102, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH BREVE
+ {0x0103, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH BREVE
+ {0x0104, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH OGONEK
+ {0x0105, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH OGONEK
+ {0x0106, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER C WITH ACUTE
+ {0x0107, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH ACUTE
+ {0x0108, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+ {0x0109, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH CIRCUMFLEX
+ {0x010A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER C WITH DOT ABOVE
+ {0x010B, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH DOT ABOVE
+ {0x010C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER C WITH CARON
+ {0x010D, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH CARON
+ {0x010E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER D WITH CARON
+ {0x010F, 0x0, propertyPVALID}, // LATIN SMALL LETTER D WITH CARON
+ {0x0110, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER D WITH STROKE
+ {0x0111, 0x0, propertyPVALID}, // LATIN SMALL LETTER D WITH STROKE
+ {0x0112, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH MACRON
+ {0x0113, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH MACRON
+ {0x0114, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH BREVE
+ {0x0115, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH BREVE
+ {0x0116, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH DOT ABOVE
+ {0x0117, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH DOT ABOVE
+ {0x0118, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH OGONEK
+ {0x0119, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH OGONEK
+ {0x011A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH CARON
+ {0x011B, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH CARON
+ {0x011C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+ {0x011D, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH CIRCUMFLEX
+ {0x011E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH BREVE
+ {0x011F, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH BREVE
+ {0x0120, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH DOT ABOVE
+ {0x0121, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH DOT ABOVE
+ {0x0122, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH CEDILLA
+ {0x0123, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH CEDILLA
+ {0x0124, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+ {0x0125, 0x0, propertyPVALID}, // LATIN SMALL LETTER H WITH CIRCUMFLEX
+ {0x0126, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER H WITH STROKE
+ {0x0127, 0x0, propertyPVALID}, // LATIN SMALL LETTER H WITH STROKE
+ {0x0128, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH TILDE
+ {0x0129, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH TILDE
+ {0x012A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH MACRON
+ {0x012B, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH MACRON
+ {0x012C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH BREVE
+ {0x012D, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH BREVE
+ {0x012E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH OGONEK
+ {0x012F, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH OGONEK
+ {0x0130, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH DOT ABOVE
+ {0x0131, 0x0, propertyPVALID}, // LATIN SMALL LETTER DOTLESS I
+ {0x0132, 0x0134, propertyDISALLOWED}, // LATIN CAPITAL LIGATURE IJ..LATIN CAPITAL LET
+ {0x0135, 0x0, propertyPVALID}, // LATIN SMALL LETTER J WITH CIRCUMFLEX
+ {0x0136, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER K WITH CEDILLA
+ {0x0137, 0x0138, propertyPVALID}, // LATIN SMALL LETTER K WITH CEDILLA..LATIN SMA
+ {0x0139, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH ACUTE
+ {0x013A, 0x0, propertyPVALID}, // LATIN SMALL LETTER L WITH ACUTE
+ {0x013B, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH CEDILLA
+ {0x013C, 0x0, propertyPVALID}, // LATIN SMALL LETTER L WITH CEDILLA
+ {0x013D, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH CARON
+ {0x013E, 0x0, propertyPVALID}, // LATIN SMALL LETTER L WITH CARON
+ {0x013F, 0x0141, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH MIDDLE DOT..LATI
+ {0x0142, 0x0, propertyPVALID}, // LATIN SMALL LETTER L WITH STROKE
+ {0x0143, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER N WITH ACUTE
+ {0x0144, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH ACUTE
+ {0x0145, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER N WITH CEDILLA
+ {0x0146, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH CEDILLA
+ {0x0147, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER N WITH CARON
+ {0x0148, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH CARON
+ {0x0149, 0x014A, propertyDISALLOWED}, // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE.
+ {0x014B, 0x0, propertyPVALID}, // LATIN SMALL LETTER ENG
+ {0x014C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH MACRON
+ {0x014D, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH MACRON
+ {0x014E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH BREVE
+ {0x014F, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH BREVE
+ {0x0150, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+ {0x0151, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DOUBLE ACUTE
+ {0x0152, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LIGATURE OE
+ {0x0153, 0x0, propertyPVALID}, // LATIN SMALL LIGATURE OE
+ {0x0154, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH ACUTE
+ {0x0155, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH ACUTE
+ {0x0156, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH CEDILLA
+ {0x0157, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH CEDILLA
+ {0x0158, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH CARON
+ {0x0159, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH CARON
+ {0x015A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH ACUTE
+ {0x015B, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH ACUTE
+ {0x015C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+ {0x015D, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH CIRCUMFLEX
+ {0x015E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH CEDILLA
+ {0x015F, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH CEDILLA
+ {0x0160, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH CARON
+ {0x0161, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH CARON
+ {0x0162, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH CEDILLA
+ {0x0163, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH CEDILLA
+ {0x0164, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH CARON
+ {0x0165, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH CARON
+ {0x0166, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH STROKE
+ {0x0167, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH STROKE
+ {0x0168, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH TILDE
+ {0x0169, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH TILDE
+ {0x016A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH MACRON
+ {0x016B, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH MACRON
+ {0x016C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH BREVE
+ {0x016D, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH BREVE
+ {0x016E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH RING ABOVE
+ {0x016F, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH RING ABOVE
+ {0x0170, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+ {0x0171, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DOUBLE ACUTE
+ {0x0172, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH OGONEK
+ {0x0173, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH OGONEK
+ {0x0174, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+ {0x0175, 0x0, propertyPVALID}, // LATIN SMALL LETTER W WITH CIRCUMFLEX
+ {0x0176, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+ {0x0177, 0x0, propertyPVALID}, // LATIN SMALL LETTER Y WITH CIRCUMFLEX
+ {0x0178, 0x0179, propertyDISALLOWED}, // LATIN CAPITAL LETTER Y WITH DIAERESIS..LATIN
+ {0x017A, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH ACUTE
+ {0x017B, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Z WITH DOT ABOVE
+ {0x017C, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH DOT ABOVE
+ {0x017D, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Z WITH CARON
+ {0x017E, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH CARON
+ {0x017F, 0x0, propertyDISALLOWED}, // LATIN SMALL LETTER LONG S
+ {0x0180, 0x0, propertyPVALID}, // LATIN SMALL LETTER B WITH STROKE
+ {0x0181, 0x0182, propertyDISALLOWED}, // LATIN CAPITAL LETTER B WITH HOOK..LATIN CAPI
+ {0x0183, 0x0, propertyPVALID}, // LATIN SMALL LETTER B WITH TOPBAR
+ {0x0184, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER TONE SIX
+ {0x0185, 0x0, propertyPVALID}, // LATIN SMALL LETTER TONE SIX
+ {0x0186, 0x0187, propertyDISALLOWED}, // LATIN CAPITAL LETTER OPEN O..LATIN CAPITAL L
+ {0x0188, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH HOOK
+ {0x0189, 0x018B, propertyDISALLOWED}, // LATIN CAPITAL LETTER AFRICAN D..LATIN CAPITA
+ {0x018C, 0x018D, propertyPVALID}, // LATIN SMALL LETTER D WITH TOPBAR..LATIN SMAL
+ {0x018E, 0x0191, propertyDISALLOWED}, // LATIN CAPITAL LETTER REVERSED E..LATIN CAPIT
+ {0x0192, 0x0, propertyPVALID}, // LATIN SMALL LETTER F WITH HOOK
+ {0x0193, 0x0194, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH HOOK..LATIN CAPI
+ {0x0195, 0x0, propertyPVALID}, // LATIN SMALL LETTER HV
+ {0x0196, 0x0198, propertyDISALLOWED}, // LATIN CAPITAL LETTER IOTA..LATIN CAPITAL LET
+ {0x0199, 0x019B, propertyPVALID}, // LATIN SMALL LETTER K WITH HOOK..LATIN SMALL
+ {0x019C, 0x019D, propertyDISALLOWED}, // LATIN CAPITAL LETTER TURNED M..LATIN CAPITAL
+ {0x019E, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH LONG RIGHT LEG
+ {0x019F, 0x01A0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH MIDDLE TILDE..LA
+ {0x01A1, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH HORN
+ {0x01A2, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER OI
+ {0x01A3, 0x0, propertyPVALID}, // LATIN SMALL LETTER OI
+ {0x01A4, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER P WITH HOOK
+ {0x01A5, 0x0, propertyPVALID}, // LATIN SMALL LETTER P WITH HOOK
+ {0x01A6, 0x01A7, propertyDISALLOWED}, // LATIN LETTER YR..LATIN CAPITAL LETTER TONE T
+ {0x01A8, 0x0, propertyPVALID}, // LATIN SMALL LETTER TONE TWO
+ {0x01A9, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER ESH
+ {0x01AA, 0x01AB, propertyPVALID}, // LATIN LETTER REVERSED ESH LOOP..LATIN SMALL
+ {0x01AC, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH HOOK
+ {0x01AD, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH HOOK
+ {0x01AE, 0x01AF, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH RETROFLEX HOOK..
+ {0x01B0, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH HORN
+ {0x01B1, 0x01B3, propertyDISALLOWED}, // LATIN CAPITAL LETTER UPSILON..LATIN CAPITAL
+ {0x01B4, 0x0, propertyPVALID}, // LATIN SMALL LETTER Y WITH HOOK
+ {0x01B5, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Z WITH STROKE
+ {0x01B6, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH STROKE
+ {0x01B7, 0x01B8, propertyDISALLOWED}, // LATIN CAPITAL LETTER EZH..LATIN CAPITAL LETT
+ {0x01B9, 0x01BB, propertyPVALID}, // LATIN SMALL LETTER EZH REVERSED..LATIN LETTE
+ {0x01BC, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER TONE FIVE
+ {0x01BD, 0x01C3, propertyPVALID}, // LATIN SMALL LETTER TONE FIVE..LATIN LETTER R
+ {0x01C4, 0x01CD, propertyDISALLOWED}, // LATIN CAPITAL LETTER DZ WITH CARON..LATIN CA
+ {0x01CE, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH CARON
+ {0x01CF, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH CARON
+ {0x01D0, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH CARON
+ {0x01D1, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH CARON
+ {0x01D2, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH CARON
+ {0x01D3, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH CARON
+ {0x01D4, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH CARON
+ {0x01D5, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DIAERESIS AND MA
+ {0x01D6, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DIAERESIS AND MACR
+ {0x01D7, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DIAERESIS AND AC
+ {0x01D8, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DIAERESIS AND ACUT
+ {0x01D9, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DIAERESIS AND CA
+ {0x01DA, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DIAERESIS AND CARO
+ {0x01DB, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DIAERESIS AND GR
+ {0x01DC, 0x01DD, propertyPVALID}, // LATIN SMALL LETTER U WITH DIAERESIS AND GRAV
+ {0x01DE, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH DIAERESIS AND MA
+ {0x01DF, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH DIAERESIS AND MACR
+ {0x01E0, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH DOT ABOVE AND MA
+ {0x01E1, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH DOT ABOVE AND MACR
+ {0x01E2, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER AE WITH MACRON
+ {0x01E3, 0x0, propertyPVALID}, // LATIN SMALL LETTER AE WITH MACRON
+ {0x01E4, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH STROKE
+ {0x01E5, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH STROKE
+ {0x01E6, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH CARON
+ {0x01E7, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH CARON
+ {0x01E8, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER K WITH CARON
+ {0x01E9, 0x0, propertyPVALID}, // LATIN SMALL LETTER K WITH CARON
+ {0x01EA, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH OGONEK
+ {0x01EB, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH OGONEK
+ {0x01EC, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH OGONEK AND MACRO
+ {0x01ED, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH OGONEK AND MACRON
+ {0x01EE, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER EZH WITH CARON
+ {0x01EF, 0x01F0, propertyPVALID}, // LATIN SMALL LETTER EZH WITH CARON..LATIN SMA
+ {0x01F1, 0x01F4, propertyDISALLOWED}, // LATIN CAPITAL LETTER DZ..LATIN CAPITAL LETTE
+ {0x01F5, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH ACUTE
+ {0x01F6, 0x01F8, propertyDISALLOWED}, // LATIN CAPITAL LETTER HWAIR..LATIN CAPITAL LE
+ {0x01F9, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH GRAVE
+ {0x01FA, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH RING ABOVE AND A
+ {0x01FB, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH RING ABOVE AND ACU
+ {0x01FC, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER AE WITH ACUTE
+ {0x01FD, 0x0, propertyPVALID}, // LATIN SMALL LETTER AE WITH ACUTE
+ {0x01FE, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
+ {0x01FF, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH STROKE AND ACUTE
+ {0x0200, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH DOUBLE GRAVE
+ {0x0201, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH DOUBLE GRAVE
+ {0x0202, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH INVERTED BREVE
+ {0x0203, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH INVERTED BREVE
+ {0x0204, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH DOUBLE GRAVE
+ {0x0205, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH DOUBLE GRAVE
+ {0x0206, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH INVERTED BREVE
+ {0x0207, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH INVERTED BREVE
+ {0x0208, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH DOUBLE GRAVE
+ {0x0209, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH DOUBLE GRAVE
+ {0x020A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH INVERTED BREVE
+ {0x020B, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH INVERTED BREVE
+ {0x020C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DOUBLE GRAVE
+ {0x020D, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DOUBLE GRAVE
+ {0x020E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH INVERTED BREVE
+ {0x020F, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH INVERTED BREVE
+ {0x0210, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH DOUBLE GRAVE
+ {0x0211, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH DOUBLE GRAVE
+ {0x0212, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH INVERTED BREVE
+ {0x0213, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH INVERTED BREVE
+ {0x0214, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DOUBLE GRAVE
+ {0x0215, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DOUBLE GRAVE
+ {0x0216, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH INVERTED BREVE
+ {0x0217, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH INVERTED BREVE
+ {0x0218, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH COMMA BELOW
+ {0x0219, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH COMMA BELOW
+ {0x021A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH COMMA BELOW
+ {0x021B, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH COMMA BELOW
+ {0x021C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER YOGH
+ {0x021D, 0x0, propertyPVALID}, // LATIN SMALL LETTER YOGH
+ {0x021E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER H WITH CARON
+ {0x021F, 0x0, propertyPVALID}, // LATIN SMALL LETTER H WITH CARON
+ {0x0220, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER N WITH LONG RIGHT LEG
+ {0x0221, 0x0, propertyPVALID}, // LATIN SMALL LETTER D WITH CURL
+ {0x0222, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER OU
+ {0x0223, 0x0, propertyPVALID}, // LATIN SMALL LETTER OU
+ {0x0224, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Z WITH HOOK
+ {0x0225, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH HOOK
+ {0x0226, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH DOT ABOVE
+ {0x0227, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH DOT ABOVE
+ {0x0228, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH CEDILLA
+ {0x0229, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH CEDILLA
+ {0x022A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DIAERESIS AND MA
+ {0x022B, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DIAERESIS AND MACR
+ {0x022C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH TILDE AND MACRON
+ {0x022D, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH TILDE AND MACRON
+ {0x022E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DOT ABOVE
+ {0x022F, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DOT ABOVE
+ {0x0230, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DOT ABOVE AND MA
+ {0x0231, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DOT ABOVE AND MACR
+ {0x0232, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Y WITH MACRON
+ {0x0233, 0x0239, propertyPVALID}, // LATIN SMALL LETTER Y WITH MACRON..LATIN SMAL
+ {0x023A, 0x023B, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH STROKE..LATIN CA
+ {0x023C, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH STROKE
+ {0x023D, 0x023E, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH BAR..LATIN CAPIT
+ {0x023F, 0x0240, propertyPVALID}, // LATIN SMALL LETTER S WITH SWASH TAIL..LATIN
+ {0x0241, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER GLOTTAL STOP
+ {0x0242, 0x0, propertyPVALID}, // LATIN SMALL LETTER GLOTTAL STOP
+ {0x0243, 0x0246, propertyDISALLOWED}, // LATIN CAPITAL LETTER B WITH STROKE..LATIN CA
+ {0x0247, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH STROKE
+ {0x0248, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER J WITH STROKE
+ {0x0249, 0x0, propertyPVALID}, // LATIN SMALL LETTER J WITH STROKE
+ {0x024A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL
+ {0x024B, 0x0, propertyPVALID}, // LATIN SMALL LETTER Q WITH HOOK TAIL
+ {0x024C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH STROKE
+ {0x024D, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH STROKE
+ {0x024E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Y WITH STROKE
+ {0x024F, 0x02AF, propertyPVALID}, // LATIN SMALL LETTER Y WITH STROKE..LATIN SMAL
+ {0x02B0, 0x02B8, propertyDISALLOWED}, // MODIFIER LETTER SMALL H..MODIFIER LETTER SMA
+ {0x02B9, 0x02C1, propertyPVALID}, // MODIFIER LETTER PRIME..MODIFIER LETTER REVER
+ {0x02C2, 0x02C5, propertyDISALLOWED}, // MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LET
+ {0x02C6, 0x02D1, propertyPVALID}, // MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER
+ {0x02D2, 0x02EB, propertyDISALLOWED}, // MODIFIER LETTER CENTRED RIGHT HALF RING..MOD
+ {0x02EC, 0x0, propertyPVALID}, // MODIFIER LETTER VOICING
+ {0x02ED, 0x0, propertyDISALLOWED}, // MODIFIER LETTER UNASPIRATED
+ {0x02EE, 0x0, propertyPVALID}, // MODIFIER LETTER DOUBLE APOSTROPHE
+ {0x02EF, 0x02FF, propertyDISALLOWED}, // MODIFIER LETTER LOW DOWN ARROWHEAD..MODIFIER
+ {0x0300, 0x033F, propertyPVALID}, // COMBINING GRAVE ACCENT..COMBINING DOUBLE OVE
+ {0x0340, 0x0341, propertyDISALLOWED}, // COMBINING GRAVE TONE MARK..COMBINING ACUTE T
+ {0x0342, 0x0, propertyPVALID}, // COMBINING GREEK PERISPOMENI
+ {0x0343, 0x0345, propertyDISALLOWED}, // COMBINING GREEK KORONIS..COMBINING GREEK YPO
+ {0x0346, 0x034E, propertyPVALID}, // COMBINING BRIDGE ABOVE..COMBINING UPWARDS AR
+ {0x034F, 0x0, propertyDISALLOWED}, // COMBINING GRAPHEME JOINER
+ {0x0350, 0x036F, propertyPVALID}, // COMBINING RIGHT ARROWHEAD ABOVE..COMBINING L
+ {0x0370, 0x0, propertyDISALLOWED}, // GREEK CAPITAL LETTER HETA
+ {0x0371, 0x0, propertyPVALID}, // GREEK SMALL LETTER HETA
+ {0x0372, 0x0, propertyDISALLOWED}, // GREEK CAPITAL LETTER ARCHAIC SAMPI
+ {0x0373, 0x0, propertyPVALID}, // GREEK SMALL LETTER ARCHAIC SAMPI
+ {0x0374, 0x0, propertyDISALLOWED}, // GREEK NUMERAL SIGN
+ {0x0375, 0x0, propertyCONTEXTO}, // GREEK LOWER NUMERAL SIGN
+ {0x0376, 0x0, propertyDISALLOWED}, // GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA
+ {0x0377, 0x0, propertyPVALID}, // GREEK SMALL LETTER PAMPHYLIAN DIGAMMA
+ {0x0378, 0x0379, propertyUNASSIGNED}, // ..
+ {0x037A, 0x0, propertyDISALLOWED}, // GREEK YPOGEGRAMMENI
+ {0x037B, 0x037D, propertyPVALID}, // GREEK SMALL REVERSED LUNATE SIGMA SYMBOL..GR
+ {0x037E, 0x0, propertyDISALLOWED}, // GREEK QUESTION MARK
+ {0x037F, 0x0383, propertyUNASSIGNED}, // ..
+ {0x0384, 0x038A, propertyDISALLOWED}, // GREEK TONOS..GREEK CAPITAL LETTER IOTA WITH
+ {0x038B, 0x0, propertyUNASSIGNED}, //
+ {0x038C, 0x0, propertyDISALLOWED}, // GREEK CAPITAL LETTER OMICRON WITH TONOS
+ {0x038D, 0x0, propertyUNASSIGNED}, //
+ {0x038E, 0x038F, propertyDISALLOWED}, // GREEK CAPITAL LETTER UPSILON WITH TONOS..GRE
+ {0x0390, 0x0, propertyPVALID}, // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND T
+ {0x0391, 0x03A1, propertyDISALLOWED}, // GREEK CAPITAL LETTER ALPHA..GREEK CAPITAL LE
+ {0x03A2, 0x0, propertyUNASSIGNED}, //
+ {0x03A3, 0x03AB, propertyDISALLOWED}, // GREEK CAPITAL LETTER SIGMA..GREEK CAPITAL LE
+ {0x03AC, 0x03CE, propertyPVALID}, // GREEK SMALL LETTER ALPHA WITH TONOS..GREEK S
+ {0x03CF, 0x03D6, propertyDISALLOWED}, // GREEK CAPITAL KAI SYMBOL..GREEK PI SYMBOL
+ {0x03D7, 0x0, propertyPVALID}, // GREEK KAI SYMBOL
+ {0x03D8, 0x0, propertyDISALLOWED}, // GREEK LETTER ARCHAIC KOPPA
+ {0x03D9, 0x0, propertyPVALID}, // GREEK SMALL LETTER ARCHAIC KOPPA
+ {0x03DA, 0x0, propertyDISALLOWED}, // GREEK LETTER STIGMA
+ {0x03DB, 0x0, propertyPVALID}, // GREEK SMALL LETTER STIGMA
+ {0x03DC, 0x0, propertyDISALLOWED}, // GREEK LETTER DIGAMMA
+ {0x03DD, 0x0, propertyPVALID}, // GREEK SMALL LETTER DIGAMMA
+ {0x03DE, 0x0, propertyDISALLOWED}, // GREEK LETTER KOPPA
+ {0x03DF, 0x0, propertyPVALID}, // GREEK SMALL LETTER KOPPA
+ {0x03E0, 0x0, propertyDISALLOWED}, // GREEK LETTER SAMPI
+ {0x03E1, 0x0, propertyPVALID}, // GREEK SMALL LETTER SAMPI
+ {0x03E2, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER SHEI
+ {0x03E3, 0x0, propertyPVALID}, // COPTIC SMALL LETTER SHEI
+ {0x03E4, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER FEI
+ {0x03E5, 0x0, propertyPVALID}, // COPTIC SMALL LETTER FEI
+ {0x03E6, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER KHEI
+ {0x03E7, 0x0, propertyPVALID}, // COPTIC SMALL LETTER KHEI
+ {0x03E8, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER HORI
+ {0x03E9, 0x0, propertyPVALID}, // COPTIC SMALL LETTER HORI
+ {0x03EA, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER GANGIA
+ {0x03EB, 0x0, propertyPVALID}, // COPTIC SMALL LETTER GANGIA
+ {0x03EC, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER SHIMA
+ {0x03ED, 0x0, propertyPVALID}, // COPTIC SMALL LETTER SHIMA
+ {0x03EE, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER DEI
+ {0x03EF, 0x0, propertyPVALID}, // COPTIC SMALL LETTER DEI
+ {0x03F0, 0x03F2, propertyDISALLOWED}, // GREEK KAPPA SYMBOL..GREEK LUNATE SIGMA SYMBO
+ {0x03F3, 0x0, propertyPVALID}, // GREEK LETTER YOT
+ {0x03F4, 0x03F7, propertyDISALLOWED}, // GREEK CAPITAL THETA SYMBOL..GREEK CAPITAL LE
+ {0x03F8, 0x0, propertyPVALID}, // GREEK SMALL LETTER SHO
+ {0x03F9, 0x03FA, propertyDISALLOWED}, // GREEK CAPITAL LUNATE SIGMA SYMBOL..GREEK CAP
+ {0x03FB, 0x03FC, propertyPVALID}, // GREEK SMALL LETTER SAN..GREEK RHO WITH STROK
+ {0x03FD, 0x042F, propertyDISALLOWED}, // GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL..
+ {0x0430, 0x045F, propertyPVALID}, // CYRILLIC SMALL LETTER A..CYRILLIC SMALL LETT
+ {0x0460, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER OMEGA
+ {0x0461, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER OMEGA
+ {0x0462, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER YAT
+ {0x0463, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER YAT
+ {0x0464, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IOTIFIED E
+ {0x0465, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IOTIFIED E
+ {0x0466, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER LITTLE YUS
+ {0x0467, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER LITTLE YUS
+ {0x0468, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS
+ {0x0469, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS
+ {0x046A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER BIG YUS
+ {0x046B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER BIG YUS
+ {0x046C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS
+ {0x046D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IOTIFIED BIG YUS
+ {0x046E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KSI
+ {0x046F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KSI
+ {0x0470, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER PSI
+ {0x0471, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER PSI
+ {0x0472, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER FITA
+ {0x0473, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER FITA
+ {0x0474, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IZHITSA
+ {0x0475, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IZHITSA
+ {0x0476, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE
+ {0x0477, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GR
+ {0x0478, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER UK
+ {0x0479, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER UK
+ {0x047A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ROUND OMEGA
+ {0x047B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ROUND OMEGA
+ {0x047C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER OMEGA WITH TITLO
+ {0x047D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER OMEGA WITH TITLO
+ {0x047E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER OT
+ {0x047F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER OT
+ {0x0480, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOPPA
+ {0x0481, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOPPA
+ {0x0482, 0x0, propertyDISALLOWED}, // CYRILLIC THOUSANDS SIGN
+ {0x0483, 0x0487, propertyPVALID}, // COMBINING CYRILLIC TITLO..COMBINING CYRILLIC
+ {0x0488, 0x048A, propertyDISALLOWED}, // COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..C
+ {0x048B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SHORT I WITH TAIL
+ {0x048C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER SEMISOFT SIGN
+ {0x048D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SEMISOFT SIGN
+ {0x048E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ER WITH TICK
+ {0x048F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ER WITH TICK
+ {0x0490, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+ {0x0491, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH UPTURN
+ {0x0492, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH STROKE
+ {0x0493, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH STROKE
+ {0x0494, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK
+ {0x0495, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK
+ {0x0496, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER
+ {0x0497, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZHE WITH DESCENDER
+ {0x0498, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ZE WITH DESCENDER
+ {0x0499, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZE WITH DESCENDER
+ {0x049A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KA WITH DESCENDER
+ {0x049B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KA WITH DESCENDER
+ {0x049C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KA WITH VERTICAL STR
+ {0x049D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KA WITH VERTICAL STROK
+ {0x049E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KA WITH STROKE
+ {0x049F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KA WITH STROKE
+ {0x04A0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER BASHKIR KA
+ {0x04A1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER BASHKIR KA
+ {0x04A2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EN WITH DESCENDER
+ {0x04A3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EN WITH DESCENDER
+ {0x04A4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LIGATURE EN GHE
+ {0x04A5, 0x0, propertyPVALID}, // CYRILLIC SMALL LIGATURE EN GHE
+ {0x04A6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK
+ {0x04A7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK
+ {0x04A8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ABKHASIAN HA
+ {0x04A9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ABKHASIAN HA
+ {0x04AA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ES WITH DESCENDER
+ {0x04AB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ES WITH DESCENDER
+ {0x04AC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER TE WITH DESCENDER
+ {0x04AD, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER TE WITH DESCENDER
+ {0x04AE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER STRAIGHT U
+ {0x04AF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER STRAIGHT U
+ {0x04B0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER STRAIGHT U WITH STRO
+ {0x04B1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE
+ {0x04B2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER HA WITH DESCENDER
+ {0x04B3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER HA WITH DESCENDER
+ {0x04B4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LIGATURE TE TSE
+ {0x04B5, 0x0, propertyPVALID}, // CYRILLIC SMALL LIGATURE TE TSE
+ {0x04B6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER CHE WITH DESCENDER
+ {0x04B7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER CHE WITH DESCENDER
+ {0x04B8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER CHE WITH VERTICAL ST
+ {0x04B9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER CHE WITH VERTICAL STRO
+ {0x04BA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER SHHA
+ {0x04BB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SHHA
+ {0x04BC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ABKHASIAN CHE
+ {0x04BD, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ABKHASIAN CHE
+ {0x04BE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH D
+ {0x04BF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DES
+ {0x04C0, 0x04C1, propertyDISALLOWED}, // CYRILLIC LETTER PALOCHKA..CYRILLIC CAPITAL L
+ {0x04C2, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZHE WITH BREVE
+ {0x04C3, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KA WITH HOOK
+ {0x04C4, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KA WITH HOOK
+ {0x04C5, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EL WITH TAIL
+ {0x04C6, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EL WITH TAIL
+ {0x04C7, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EN WITH HOOK
+ {0x04C8, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EN WITH HOOK
+ {0x04C9, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EN WITH TAIL
+ {0x04CA, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EN WITH TAIL
+ {0x04CB, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KHAKASSIAN CHE
+ {0x04CC, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KHAKASSIAN CHE
+ {0x04CD, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EM WITH TAIL
+ {0x04CE, 0x04CF, propertyPVALID}, // CYRILLIC SMALL LETTER EM WITH TAIL..CYRILLIC
+ {0x04D0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER A WITH BREVE
+ {0x04D1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER A WITH BREVE
+ {0x04D2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER A WITH DIAERESIS
+ {0x04D3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER A WITH DIAERESIS
+ {0x04D4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LIGATURE A IE
+ {0x04D5, 0x0, propertyPVALID}, // CYRILLIC SMALL LIGATURE A IE
+ {0x04D6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IE WITH BREVE
+ {0x04D7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IE WITH BREVE
+ {0x04D8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER SCHWA
+ {0x04D9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SCHWA
+ {0x04DA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS
+ {0x04DB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS
+ {0x04DC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS
+ {0x04DD, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZHE WITH DIAERESIS
+ {0x04DE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS
+ {0x04DF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZE WITH DIAERESIS
+ {0x04E0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ABKHASIAN DZE
+ {0x04E1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ABKHASIAN DZE
+ {0x04E2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER I WITH MACRON
+ {0x04E3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER I WITH MACRON
+ {0x04E4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER I WITH DIAERESIS
+ {0x04E5, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER I WITH DIAERESIS
+ {0x04E6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER O WITH DIAERESIS
+ {0x04E7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER O WITH DIAERESIS
+ {0x04E8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER BARRED O
+ {0x04E9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER BARRED O
+ {0x04EA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER BARRED O WITH DIAERE
+ {0x04EB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER BARRED O WITH DIAERESI
+ {0x04EC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER E WITH DIAERESIS
+ {0x04ED, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER E WITH DIAERESIS
+ {0x04EE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER U WITH MACRON
+ {0x04EF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER U WITH MACRON
+ {0x04F0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER U WITH DIAERESIS
+ {0x04F1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER U WITH DIAERESIS
+ {0x04F2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE
+ {0x04F3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE
+ {0x04F4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS
+ {0x04F5, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER CHE WITH DIAERESIS
+ {0x04F6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH DESCENDER
+ {0x04F7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH DESCENDER
+ {0x04F8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS
+ {0x04F9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER YERU WITH DIAERESIS
+ {0x04FA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH STROKE AND
+ {0x04FB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH STROKE AND HO
+ {0x04FC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER HA WITH HOOK
+ {0x04FD, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER HA WITH HOOK
+ {0x04FE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER HA WITH STROKE
+ {0x04FF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER HA WITH STROKE
+ {0x0500, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI DE
+ {0x0501, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI DE
+ {0x0502, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI DJE
+ {0x0503, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI DJE
+ {0x0504, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI ZJE
+ {0x0505, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI ZJE
+ {0x0506, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI DZJE
+ {0x0507, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI DZJE
+ {0x0508, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI LJE
+ {0x0509, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI LJE
+ {0x050A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI NJE
+ {0x050B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI NJE
+ {0x050C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI SJE
+ {0x050D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI SJE
+ {0x050E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI TJE
+ {0x050F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI TJE
+ {0x0510, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER REVERSED ZE
+ {0x0511, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER REVERSED ZE
+ {0x0512, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EL WITH HOOK
+ {0x0513, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EL WITH HOOK
+ {0x0514, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER LHA
+ {0x0515, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER LHA
+ {0x0516, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER RHA
+ {0x0517, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER RHA
+ {0x0518, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER YAE
+ {0x0519, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER YAE
+ {0x051A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER QA
+ {0x051B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER QA
+ {0x051C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER WE
+ {0x051D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER WE
+ {0x051E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ALEUT KA
+ {0x051F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ALEUT KA
+ {0x0520, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EL WITH MIDDLE HOOK
+ {0x0521, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EL WITH MIDDLE HOOK
+ {0x0522, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EN WITH MIDDLE HOOK
+ {0x0523, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EN WITH MIDDLE HOOK
+ {0x0524, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER PE WITH DESCENDER
+ {0x0525, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER PE WITH DESCENDER
+ {0x0526, 0x0530, propertyUNASSIGNED}, // ..
+ {0x0531, 0x0556, propertyDISALLOWED}, // ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITA
+ {0x0557, 0x0558, propertyUNASSIGNED}, // ..
+ {0x0559, 0x0, propertyPVALID}, // ARMENIAN MODIFIER LETTER LEFT HALF RING
+ {0x055A, 0x055F, propertyDISALLOWED}, // ARMENIAN APOSTROPHE..ARMENIAN ABBREVIATION M
+ {0x0560, 0x0, propertyUNASSIGNED}, //
+ {0x0561, 0x0586, propertyPVALID}, // ARMENIAN SMALL LETTER AYB..ARMENIAN SMALL LE
+ {0x0587, 0x0, propertyDISALLOWED}, // ARMENIAN SMALL LIGATURE ECH YIWN
+ {0x0588, 0x0, propertyUNASSIGNED}, //
+ {0x0589, 0x058A, propertyDISALLOWED}, // ARMENIAN FULL STOP..ARMENIAN HYPHEN
+ {0x058B, 0x0590, propertyUNASSIGNED}, // ..
+ {0x0591, 0x05BD, propertyPVALID}, // HEBREW ACCENT ETNAHTA..HEBREW POINT METEG
+ {0x05BE, 0x0, propertyDISALLOWED}, // HEBREW PUNCTUATION MAQAF
+ {0x05BF, 0x0, propertyPVALID}, // HEBREW POINT RAFE
+ {0x05C0, 0x0, propertyDISALLOWED}, // HEBREW PUNCTUATION PASEQ
+ {0x05C1, 0x05C2, propertyPVALID}, // HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
+ {0x05C3, 0x0, propertyDISALLOWED}, // HEBREW PUNCTUATION SOF PASUQ
+ {0x05C4, 0x05C5, propertyPVALID}, // HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
+ {0x05C6, 0x0, propertyDISALLOWED}, // HEBREW PUNCTUATION NUN HAFUKHA
+ {0x05C7, 0x0, propertyPVALID}, // HEBREW POINT QAMATS QATAN
+ {0x05C8, 0x05CF, propertyUNASSIGNED}, // ..
+ {0x05D0, 0x05EA, propertyPVALID}, // HEBREW LETTER ALEF..HEBREW LETTER TAV
+ {0x05EB, 0x05EF, propertyUNASSIGNED}, // ..
+ {0x05F0, 0x05F2, propertyPVALID}, // HEBREW LIGATURE YIDDISH DOUBLE VAV..HEBREW L
+ {0x05F3, 0x05F4, propertyCONTEXTO}, // HEBREW PUNCTUATION GERESH..HEBREW PUNCTUATIO
+ {0x05F5, 0x05FF, propertyUNASSIGNED}, // ..
+ {0x0600, 0x0603, propertyDISALLOWED}, // ARABIC NUMBER SIGN..ARABIC SIGN SAFHA
+ {0x0604, 0x0605, propertyUNASSIGNED}, // ..
+ {0x0606, 0x060F, propertyDISALLOWED}, // ARABIC-INDIC CUBE ROOT..ARABIC SIGN MISRA
+ {0x0610, 0x061A, propertyPVALID}, // ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..AR
+ {0x061B, 0x0, propertyDISALLOWED}, // ARABIC SEMICOLON
+ {0x061C, 0x061D, propertyUNASSIGNED}, // ..
+ {0x061E, 0x061F, propertyDISALLOWED}, // ARABIC TRIPLE DOT PUNCTUATION MARK..ARABIC Q
+ {0x0620, 0x0, propertyUNASSIGNED}, //
+ {0x0621, 0x063F, propertyPVALID}, // ARABIC LETTER HAMZA..ARABIC LETTER FARSI YEH
+ {0x0640, 0x0, propertyDISALLOWED}, // ARABIC TATWEEL
+ {0x0641, 0x065E, propertyPVALID}, // ARABIC LETTER FEH..ARABIC FATHA WITH TWO DOT
+ {0x065F, 0x0, propertyUNASSIGNED}, //
+ {0x0660, 0x0669, propertyCONTEXTO}, // ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT
+ {0x066A, 0x066D, propertyDISALLOWED}, // ARABIC PERCENT SIGN..ARABIC FIVE POINTED STA
+ {0x066E, 0x0674, propertyPVALID}, // ARABIC LETTER DOTLESS BEH..ARABIC LETTER HIG
+ {0x0675, 0x0678, propertyDISALLOWED}, // ARABIC LETTER HIGH HAMZA ALEF..ARABIC LETTER
+ {0x0679, 0x06D3, propertyPVALID}, // ARABIC LETTER TTEH..ARABIC LETTER YEH BARREE
+ {0x06D4, 0x0, propertyDISALLOWED}, // ARABIC FULL STOP
+ {0x06D5, 0x06DC, propertyPVALID}, // ARABIC LETTER AE..ARABIC SMALL HIGH SEEN
+ {0x06DD, 0x06DE, propertyDISALLOWED}, // ARABIC END OF AYAH..ARABIC START OF RUB EL H
+ {0x06DF, 0x06E8, propertyPVALID}, // ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL
+ {0x06E9, 0x0, propertyDISALLOWED}, // ARABIC PLACE OF SAJDAH
+ {0x06EA, 0x06EF, propertyPVALID}, // ARABIC EMPTY CENTRE LOW STOP..ARABIC LETTER
+ {0x06F0, 0x06F9, propertyCONTEXTO}, // EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED A
+ {0x06FA, 0x06FF, propertyPVALID}, // ARABIC LETTER SHEEN WITH DOT BELOW..ARABIC L
+ {0x0700, 0x070D, propertyDISALLOWED}, // SYRIAC END OF PARAGRAPH..SYRIAC HARKLEAN AST
+ {0x070E, 0x0, propertyUNASSIGNED}, //
+ {0x070F, 0x0, propertyDISALLOWED}, // SYRIAC ABBREVIATION MARK
+ {0x0710, 0x074A, propertyPVALID}, // SYRIAC LETTER ALAPH..SYRIAC BARREKH
+ {0x074B, 0x074C, propertyUNASSIGNED}, // ..
+ {0x074D, 0x07B1, propertyPVALID}, // SYRIAC LETTER SOGDIAN ZHAIN..THAANA LETTER N
+ {0x07B2, 0x07BF, propertyUNASSIGNED}, // ..
+ {0x07C0, 0x07F5, propertyPVALID}, // NKO DIGIT ZERO..NKO LOW TONE APOSTROPHE
+ {0x07F6, 0x07FA, propertyDISALLOWED}, // NKO SYMBOL OO DENNEN..NKO LAJANYALAN
+ {0x07FB, 0x07FF, propertyUNASSIGNED}, // ..
+ {0x0800, 0x082D, propertyPVALID}, // SAMARITAN LETTER ALAF..SAMARITAN MARK NEQUDA
+ {0x082E, 0x082F, propertyUNASSIGNED}, // ..
+ {0x0830, 0x083E, propertyDISALLOWED}, // SAMARITAN PUNCTUATION NEQUDAA..SAMARITAN PUN
+ {0x083F, 0x08FF, propertyUNASSIGNED}, // ..
+ {0x0900, 0x0939, propertyPVALID}, // DEVANAGARI SIGN INVERTED CANDRABINDU..DEVANA
+ {0x093A, 0x093B, propertyUNASSIGNED}, // ..
+ {0x093C, 0x094E, propertyPVALID}, // DEVANAGARI SIGN NUKTA..DEVANAGARI VOWEL SIGN
+ {0x094F, 0x0, propertyUNASSIGNED}, //
+ {0x0950, 0x0955, propertyPVALID}, // DEVANAGARI OM..DEVANAGARI VOWEL SIGN CANDRA
+ {0x0956, 0x0957, propertyUNASSIGNED}, // ..
+ {0x0958, 0x095F, propertyDISALLOWED}, // DEVANAGARI LETTER QA..DEVANAGARI LETTER YYA
+ {0x0960, 0x0963, propertyPVALID}, // DEVANAGARI LETTER VOCALIC RR..DEVANAGARI VOW
+ {0x0964, 0x0965, propertyDISALLOWED}, // DEVANAGARI DANDA..DEVANAGARI DOUBLE DANDA
+ {0x0966, 0x096F, propertyPVALID}, // DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE
+ {0x0970, 0x0, propertyDISALLOWED}, // DEVANAGARI ABBREVIATION SIGN
+ {0x0971, 0x0972, propertyPVALID}, // DEVANAGARI SIGN HIGH SPACING DOT..DEVANAGARI
+ {0x0973, 0x0978, propertyUNASSIGNED}, // ..
+ {0x0979, 0x097F, propertyPVALID}, // DEVANAGARI LETTER ZHA..DEVANAGARI LETTER BBA
+ {0x0980, 0x0, propertyUNASSIGNED}, //
+ {0x0981, 0x0983, propertyPVALID}, // BENGALI SIGN CANDRABINDU..BENGALI SIGN VISAR
+ {0x0984, 0x0, propertyUNASSIGNED}, //
+ {0x0985, 0x098C, propertyPVALID}, // BENGALI LETTER A..BENGALI LETTER VOCALIC L
+ {0x098D, 0x098E, propertyUNASSIGNED}, // ..
+ {0x098F, 0x0990, propertyPVALID}, // BENGALI LETTER E..BENGALI LETTER AI
+ {0x0991, 0x0992, propertyUNASSIGNED}, // ..
+ {0x0993, 0x09A8, propertyPVALID}, // BENGALI LETTER O..BENGALI LETTER NA
+ {0x09A9, 0x0, propertyUNASSIGNED}, //
+ {0x09AA, 0x09B0, propertyPVALID}, // BENGALI LETTER PA..BENGALI LETTER RA
+ {0x09B1, 0x0, propertyUNASSIGNED}, //
+ {0x09B2, 0x0, propertyPVALID}, // BENGALI LETTER LA
+ {0x09B3, 0x09B5, propertyUNASSIGNED}, // ..
+ {0x09B6, 0x09B9, propertyPVALID}, // BENGALI LETTER SHA..BENGALI LETTER HA
+ {0x09BA, 0x09BB, propertyUNASSIGNED}, // ..
+ {0x09BC, 0x09C4, propertyPVALID}, // BENGALI SIGN NUKTA..BENGALI VOWEL SIGN VOCAL
+ {0x09C5, 0x09C6, propertyUNASSIGNED}, // ..
+ {0x09C7, 0x09C8, propertyPVALID}, // BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI
+ {0x09C9, 0x09CA, propertyUNASSIGNED}, // ..
+ {0x09CB, 0x09CE, propertyPVALID}, // BENGALI VOWEL SIGN O..BENGALI LETTER KHANDA
+ {0x09CF, 0x09D6, propertyUNASSIGNED}, // ..
+ {0x09D7, 0x0, propertyPVALID}, // BENGALI AU LENGTH MARK
+ {0x09D8, 0x09DB, propertyUNASSIGNED}, // ..
+ {0x09DC, 0x09DD, propertyDISALLOWED}, // BENGALI LETTER RRA..BENGALI LETTER RHA
+ {0x09DE, 0x0, propertyUNASSIGNED}, //
+ {0x09DF, 0x0, propertyDISALLOWED}, // BENGALI LETTER YYA
+ {0x09E0, 0x09E3, propertyPVALID}, // BENGALI LETTER VOCALIC RR..BENGALI VOWEL SIG
+ {0x09E4, 0x09E5, propertyUNASSIGNED}, // ..
+ {0x09E6, 0x09F1, propertyPVALID}, // BENGALI DIGIT ZERO..BENGALI LETTER RA WITH L
+ {0x09F2, 0x09FB, propertyDISALLOWED}, // BENGALI RUPEE MARK..BENGALI GANDA MARK
+ {0x09FC, 0x0A00, propertyUNASSIGNED}, // ..
+ {0x0A01, 0x0A03, propertyPVALID}, // GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN VISA
+ {0x0A04, 0x0, propertyUNASSIGNED}, //
+ {0x0A05, 0x0A0A, propertyPVALID}, // GURMUKHI LETTER A..GURMUKHI LETTER UU
+ {0x0A0B, 0x0A0E, propertyUNASSIGNED}, // ..
+ {0x0A0F, 0x0A10, propertyPVALID}, // GURMUKHI LETTER EE..GURMUKHI LETTER AI
+ {0x0A11, 0x0A12, propertyUNASSIGNED}, // ..
+ {0x0A13, 0x0A28, propertyPVALID}, // GURMUKHI LETTER OO..GURMUKHI LETTER NA
+ {0x0A29, 0x0, propertyUNASSIGNED}, //
+ {0x0A2A, 0x0A30, propertyPVALID}, // GURMUKHI LETTER PA..GURMUKHI LETTER RA
+ {0x0A31, 0x0, propertyUNASSIGNED}, //
+ {0x0A32, 0x0, propertyPVALID}, // GURMUKHI LETTER LA
+ {0x0A33, 0x0, propertyDISALLOWED}, // GURMUKHI LETTER LLA
+ {0x0A34, 0x0, propertyUNASSIGNED}, //
+ {0x0A35, 0x0, propertyPVALID}, // GURMUKHI LETTER VA
+ {0x0A36, 0x0, propertyDISALLOWED}, // GURMUKHI LETTER SHA
+ {0x0A37, 0x0, propertyUNASSIGNED}, //
+ {0x0A38, 0x0A39, propertyPVALID}, // GURMUKHI LETTER SA..GURMUKHI LETTER HA
+ {0x0A3A, 0x0A3B, propertyUNASSIGNED}, // ..
+ {0x0A3C, 0x0, propertyPVALID}, // GURMUKHI SIGN NUKTA
+ {0x0A3D, 0x0, propertyUNASSIGNED}, //
+ {0x0A3E, 0x0A42, propertyPVALID}, // GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN
+ {0x0A43, 0x0A46, propertyUNASSIGNED}, // ..
+ {0x0A47, 0x0A48, propertyPVALID}, // GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN
+ {0x0A49, 0x0A4A, propertyUNASSIGNED}, // ..
+ {0x0A4B, 0x0A4D, propertyPVALID}, // GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA
+ {0x0A4E, 0x0A50, propertyUNASSIGNED}, // ..
+ {0x0A51, 0x0, propertyPVALID}, // GURMUKHI SIGN UDAAT
+ {0x0A52, 0x0A58, propertyUNASSIGNED}, // ..
+ {0x0A59, 0x0A5B, propertyDISALLOWED}, // GURMUKHI LETTER KHHA..GURMUKHI LETTER ZA
+ {0x0A5C, 0x0, propertyPVALID}, // GURMUKHI LETTER RRA
+ {0x0A5D, 0x0, propertyUNASSIGNED}, //
+ {0x0A5E, 0x0, propertyDISALLOWED}, // GURMUKHI LETTER FA
+ {0x0A5F, 0x0A65, propertyUNASSIGNED}, // ..
+ {0x0A66, 0x0A75, propertyPVALID}, // GURMUKHI DIGIT ZERO..GURMUKHI SIGN YAKASH
+ {0x0A76, 0x0A80, propertyUNASSIGNED}, // ..
+ {0x0A81, 0x0A83, propertyPVALID}, // GUJARATI SIGN CANDRABINDU..GUJARATI SIGN VIS
+ {0x0A84, 0x0, propertyUNASSIGNED}, //
+ {0x0A85, 0x0A8D, propertyPVALID}, // GUJARATI LETTER A..GUJARATI VOWEL CANDRA E
+ {0x0A8E, 0x0, propertyUNASSIGNED}, //
+ {0x0A8F, 0x0A91, propertyPVALID}, // GUJARATI LETTER E..GUJARATI VOWEL CANDRA O
+ {0x0A92, 0x0, propertyUNASSIGNED}, //
+ {0x0A93, 0x0AA8, propertyPVALID}, // GUJARATI LETTER O..GUJARATI LETTER NA
+ {0x0AA9, 0x0, propertyUNASSIGNED}, //
+ {0x0AAA, 0x0AB0, propertyPVALID}, // GUJARATI LETTER PA..GUJARATI LETTER RA
+ {0x0AB1, 0x0, propertyUNASSIGNED}, //
+ {0x0AB2, 0x0AB3, propertyPVALID}, // GUJARATI LETTER LA..GUJARATI LETTER LLA
+ {0x0AB4, 0x0, propertyUNASSIGNED}, //
+ {0x0AB5, 0x0AB9, propertyPVALID}, // GUJARATI LETTER VA..GUJARATI LETTER HA
+ {0x0ABA, 0x0ABB, propertyUNASSIGNED}, // ..
+ {0x0ABC, 0x0AC5, propertyPVALID}, // GUJARATI SIGN NUKTA..GUJARATI VOWEL SIGN CAN
+ {0x0AC6, 0x0, propertyUNASSIGNED}, //
+ {0x0AC7, 0x0AC9, propertyPVALID}, // GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN C
+ {0x0ACA, 0x0, propertyUNASSIGNED}, //
+ {0x0ACB, 0x0ACD, propertyPVALID}, // GUJARATI VOWEL SIGN O..GUJARATI SIGN VIRAMA
+ {0x0ACE, 0x0ACF, propertyUNASSIGNED}, // ..
+ {0x0AD0, 0x0, propertyPVALID}, // GUJARATI OM
+ {0x0AD1, 0x0ADF, propertyUNASSIGNED}, // ..
+ {0x0AE0, 0x0AE3, propertyPVALID}, // GUJARATI LETTER VOCALIC RR..GUJARATI VOWEL S
+ {0x0AE4, 0x0AE5, propertyUNASSIGNED}, // ..
+ {0x0AE6, 0x0AEF, propertyPVALID}, // GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE
+ {0x0AF0, 0x0, propertyUNASSIGNED}, //
+ {0x0AF1, 0x0, propertyDISALLOWED}, // GUJARATI RUPEE SIGN
+ {0x0AF2, 0x0B00, propertyUNASSIGNED}, // ..
+ {0x0B01, 0x0B03, propertyPVALID}, // ORIYA SIGN CANDRABINDU..ORIYA SIGN VISARGA
+ {0x0B04, 0x0, propertyUNASSIGNED}, //
+ {0x0B05, 0x0B0C, propertyPVALID}, // ORIYA LETTER A..ORIYA LETTER VOCALIC L
+ {0x0B0D, 0x0B0E, propertyUNASSIGNED}, // ..
+ {0x0B0F, 0x0B10, propertyPVALID}, // ORIYA LETTER E..ORIYA LETTER AI
+ {0x0B11, 0x0B12, propertyUNASSIGNED}, // ..
+ {0x0B13, 0x0B28, propertyPVALID}, // ORIYA LETTER O..ORIYA LETTER NA
+ {0x0B29, 0x0, propertyUNASSIGNED}, //
+ {0x0B2A, 0x0B30, propertyPVALID}, // ORIYA LETTER PA..ORIYA LETTER RA
+ {0x0B31, 0x0, propertyUNASSIGNED}, //
+ {0x0B32, 0x0B33, propertyPVALID}, // ORIYA LETTER LA..ORIYA LETTER LLA
+ {0x0B34, 0x0, propertyUNASSIGNED}, //
+ {0x0B35, 0x0B39, propertyPVALID}, // ORIYA LETTER VA..ORIYA LETTER HA
+ {0x0B3A, 0x0B3B, propertyUNASSIGNED}, // ..
+ {0x0B3C, 0x0B44, propertyPVALID}, // ORIYA SIGN NUKTA..ORIYA VOWEL SIGN VOCALIC R
+ {0x0B45, 0x0B46, propertyUNASSIGNED}, // ..
+ {0x0B47, 0x0B48, propertyPVALID}, // ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI
+ {0x0B49, 0x0B4A, propertyUNASSIGNED}, // ..
+ {0x0B4B, 0x0B4D, propertyPVALID}, // ORIYA VOWEL SIGN O..ORIYA SIGN VIRAMA
+ {0x0B4E, 0x0B55, propertyUNASSIGNED}, // ..
+ {0x0B56, 0x0B57, propertyPVALID}, // ORIYA AI LENGTH MARK..ORIYA AU LENGTH MARK
+ {0x0B58, 0x0B5B, propertyUNASSIGNED}, // ..
+ {0x0B5C, 0x0B5D, propertyDISALLOWED}, // ORIYA LETTER RRA..ORIYA LETTER RHA
+ {0x0B5E, 0x0, propertyUNASSIGNED}, //
+ {0x0B5F, 0x0B63, propertyPVALID}, // ORIYA LETTER YYA..ORIYA VOWEL SIGN VOCALIC L
+ {0x0B64, 0x0B65, propertyUNASSIGNED}, // ..
+ {0x0B66, 0x0B6F, propertyPVALID}, // ORIYA DIGIT ZERO..ORIYA DIGIT NINE
+ {0x0B70, 0x0, propertyDISALLOWED}, // ORIYA ISSHAR
+ {0x0B71, 0x0, propertyPVALID}, // ORIYA LETTER WA
+ {0x0B72, 0x0B81, propertyUNASSIGNED}, // ..
+ {0x0B82, 0x0B83, propertyPVALID}, // TAMIL SIGN ANUSVARA..TAMIL SIGN VISARGA
+ {0x0B84, 0x0, propertyUNASSIGNED}, //
+ {0x0B85, 0x0B8A, propertyPVALID}, // TAMIL LETTER A..TAMIL LETTER UU
+ {0x0B8B, 0x0B8D, propertyUNASSIGNED}, // ..
+ {0x0B8E, 0x0B90, propertyPVALID}, // TAMIL LETTER E..TAMIL LETTER AI
+ {0x0B91, 0x0, propertyUNASSIGNED}, //
+ {0x0B92, 0x0B95, propertyPVALID}, // TAMIL LETTER O..TAMIL LETTER KA
+ {0x0B96, 0x0B98, propertyUNASSIGNED}, // ..
+ {0x0B99, 0x0B9A, propertyPVALID}, // TAMIL LETTER NGA..TAMIL LETTER CA
+ {0x0B9B, 0x0, propertyUNASSIGNED}, //
+ {0x0B9C, 0x0, propertyPVALID}, // TAMIL LETTER JA
+ {0x0B9D, 0x0, propertyUNASSIGNED}, //
+ {0x0B9E, 0x0B9F, propertyPVALID}, // TAMIL LETTER NYA..TAMIL LETTER TTA
+ {0x0BA0, 0x0BA2, propertyUNASSIGNED}, // ..
+ {0x0BA3, 0x0BA4, propertyPVALID}, // TAMIL LETTER NNA..TAMIL LETTER TA
+ {0x0BA5, 0x0BA7, propertyUNASSIGNED}, // ..
+ {0x0BA8, 0x0BAA, propertyPVALID}, // TAMIL LETTER NA..TAMIL LETTER PA
+ {0x0BAB, 0x0BAD, propertyUNASSIGNED}, // ..
+ {0x0BAE, 0x0BB9, propertyPVALID}, // TAMIL LETTER MA..TAMIL LETTER HA
+ {0x0BBA, 0x0BBD, propertyUNASSIGNED}, // ..
+ {0x0BBE, 0x0BC2, propertyPVALID}, // TAMIL VOWEL SIGN AA..TAMIL VOWEL SIGN UU
+ {0x0BC3, 0x0BC5, propertyUNASSIGNED}, // ..
+ {0x0BC6, 0x0BC8, propertyPVALID}, // TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI
+ {0x0BC9, 0x0, propertyUNASSIGNED}, //
+ {0x0BCA, 0x0BCD, propertyPVALID}, // TAMIL VOWEL SIGN O..TAMIL SIGN VIRAMA
+ {0x0BCE, 0x0BCF, propertyUNASSIGNED}, // ..
+ {0x0BD0, 0x0, propertyPVALID}, // TAMIL OM
+ {0x0BD1, 0x0BD6, propertyUNASSIGNED}, // ..
+ {0x0BD7, 0x0, propertyPVALID}, // TAMIL AU LENGTH MARK
+ {0x0BD8, 0x0BE5, propertyUNASSIGNED}, // ..
+ {0x0BE6, 0x0BEF, propertyPVALID}, // TAMIL DIGIT ZERO..TAMIL DIGIT NINE
+ {0x0BF0, 0x0BFA, propertyDISALLOWED}, // TAMIL NUMBER TEN..TAMIL NUMBER SIGN
+ {0x0BFB, 0x0C00, propertyUNASSIGNED}, // ..
+ {0x0C01, 0x0C03, propertyPVALID}, // TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA
+ {0x0C04, 0x0, propertyUNASSIGNED}, //
+ {0x0C05, 0x0C0C, propertyPVALID}, // TELUGU LETTER A..TELUGU LETTER VOCALIC L
+ {0x0C0D, 0x0, propertyUNASSIGNED}, //
+ {0x0C0E, 0x0C10, propertyPVALID}, // TELUGU LETTER E..TELUGU LETTER AI
+ {0x0C11, 0x0, propertyUNASSIGNED}, //
+ {0x0C12, 0x0C28, propertyPVALID}, // TELUGU LETTER O..TELUGU LETTER NA
+ {0x0C29, 0x0, propertyUNASSIGNED}, //
+ {0x0C2A, 0x0C33, propertyPVALID}, // TELUGU LETTER PA..TELUGU LETTER LLA
+ {0x0C34, 0x0, propertyUNASSIGNED}, //
+ {0x0C35, 0x0C39, propertyPVALID}, // TELUGU LETTER VA..TELUGU LETTER HA
+ {0x0C3A, 0x0C3C, propertyUNASSIGNED}, // ..
+ {0x0C3D, 0x0C44, propertyPVALID}, // TELUGU SIGN AVAGRAHA..TELUGU VOWEL SIGN VOCA
+ {0x0C45, 0x0, propertyUNASSIGNED}, //
+ {0x0C46, 0x0C48, propertyPVALID}, // TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI
+ {0x0C49, 0x0, propertyUNASSIGNED}, //
+ {0x0C4A, 0x0C4D, propertyPVALID}, // TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA
+ {0x0C4E, 0x0C54, propertyUNASSIGNED}, // ..
+ {0x0C55, 0x0C56, propertyPVALID}, // TELUGU LENGTH MARK..TELUGU AI LENGTH MARK
+ {0x0C57, 0x0, propertyUNASSIGNED}, //
+ {0x0C58, 0x0C59, propertyPVALID}, // TELUGU LETTER TSA..TELUGU LETTER DZA
+ {0x0C5A, 0x0C5F, propertyUNASSIGNED}, // ..
+ {0x0C60, 0x0C63, propertyPVALID}, // TELUGU LETTER VOCALIC RR..TELUGU VOWEL SIGN
+ {0x0C64, 0x0C65, propertyUNASSIGNED}, // ..
+ {0x0C66, 0x0C6F, propertyPVALID}, // TELUGU DIGIT ZERO..TELUGU DIGIT NINE
+ {0x0C70, 0x0C77, propertyUNASSIGNED}, // ..
+ {0x0C78, 0x0C7F, propertyDISALLOWED}, // TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF
+ {0x0C80, 0x0C81, propertyUNASSIGNED}, // ..
+ {0x0C82, 0x0C83, propertyPVALID}, // KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA
+ {0x0C84, 0x0, propertyUNASSIGNED}, //
+ {0x0C85, 0x0C8C, propertyPVALID}, // KANNADA LETTER A..KANNADA LETTER VOCALIC L
+ {0x0C8D, 0x0, propertyUNASSIGNED}, //
+ {0x0C8E, 0x0C90, propertyPVALID}, // KANNADA LETTER E..KANNADA LETTER AI
+ {0x0C91, 0x0, propertyUNASSIGNED}, //
+ {0x0C92, 0x0CA8, propertyPVALID}, // KANNADA LETTER O..KANNADA LETTER NA
+ {0x0CA9, 0x0, propertyUNASSIGNED}, //
+ {0x0CAA, 0x0CB3, propertyPVALID}, // KANNADA LETTER PA..KANNADA LETTER LLA
+ {0x0CB4, 0x0, propertyUNASSIGNED}, //
+ {0x0CB5, 0x0CB9, propertyPVALID}, // KANNADA LETTER VA..KANNADA LETTER HA
+ {0x0CBA, 0x0CBB, propertyUNASSIGNED}, // ..
+ {0x0CBC, 0x0CC4, propertyPVALID}, // KANNADA SIGN NUKTA..KANNADA VOWEL SIGN VOCAL
+ {0x0CC5, 0x0, propertyUNASSIGNED}, //
+ {0x0CC6, 0x0CC8, propertyPVALID}, // KANNADA VOWEL SIGN E..KANNADA VOWEL SIGN AI
+ {0x0CC9, 0x0, propertyUNASSIGNED}, //
+ {0x0CCA, 0x0CCD, propertyPVALID}, // KANNADA VOWEL SIGN O..KANNADA SIGN VIRAMA
+ {0x0CCE, 0x0CD4, propertyUNASSIGNED}, // ..
+ {0x0CD5, 0x0CD6, propertyPVALID}, // KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
+ {0x0CD7, 0x0CDD, propertyUNASSIGNED}, // ..
+ {0x0CDE, 0x0, propertyPVALID}, // KANNADA LETTER FA
+ {0x0CDF, 0x0, propertyUNASSIGNED}, //
+ {0x0CE0, 0x0CE3, propertyPVALID}, // KANNADA LETTER VOCALIC RR..KANNADA VOWEL SIG
+ {0x0CE4, 0x0CE5, propertyUNASSIGNED}, // ..
+ {0x0CE6, 0x0CEF, propertyPVALID}, // KANNADA DIGIT ZERO..KANNADA DIGIT NINE
+ {0x0CF0, 0x0, propertyUNASSIGNED}, //
+ {0x0CF1, 0x0CF2, propertyDISALLOWED}, // KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADH
+ {0x0CF3, 0x0D01, propertyUNASSIGNED}, // ..
+ {0x0D02, 0x0D03, propertyPVALID}, // MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISA
+ {0x0D04, 0x0, propertyUNASSIGNED}, //
+ {0x0D05, 0x0D0C, propertyPVALID}, // MALAYALAM LETTER A..MALAYALAM LETTER VOCALIC
+ {0x0D0D, 0x0, propertyUNASSIGNED}, //
+ {0x0D0E, 0x0D10, propertyPVALID}, // MALAYALAM LETTER E..MALAYALAM LETTER AI
+ {0x0D11, 0x0, propertyUNASSIGNED}, //
+ {0x0D12, 0x0D28, propertyPVALID}, // MALAYALAM LETTER O..MALAYALAM LETTER NA
+ {0x0D29, 0x0, propertyUNASSIGNED}, //
+ {0x0D2A, 0x0D39, propertyPVALID}, // MALAYALAM LETTER PA..MALAYALAM LETTER HA
+ {0x0D3A, 0x0D3C, propertyUNASSIGNED}, // ..
+ {0x0D3D, 0x0D44, propertyPVALID}, // MALAYALAM SIGN AVAGRAHA..MALAYALAM VOWEL SIG
+ {0x0D45, 0x0, propertyUNASSIGNED}, //
+ {0x0D46, 0x0D48, propertyPVALID}, // MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN
+ {0x0D49, 0x0, propertyUNASSIGNED}, //
+ {0x0D4A, 0x0D4D, propertyPVALID}, // MALAYALAM VOWEL SIGN O..MALAYALAM SIGN VIRAM
+ {0x0D4E, 0x0D56, propertyUNASSIGNED}, // ..
+ {0x0D57, 0x0, propertyPVALID}, // MALAYALAM AU LENGTH MARK
+ {0x0D58, 0x0D5F, propertyUNASSIGNED}, // ..
+ {0x0D60, 0x0D63, propertyPVALID}, // MALAYALAM LETTER VOCALIC RR..MALAYALAM VOWEL
+ {0x0D64, 0x0D65, propertyUNASSIGNED}, // ..
+ {0x0D66, 0x0D6F, propertyPVALID}, // MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE
+ {0x0D70, 0x0D75, propertyDISALLOWED}, // MALAYALAM NUMBER TEN..MALAYALAM FRACTION THR
+ {0x0D76, 0x0D78, propertyUNASSIGNED}, // ..
+ {0x0D79, 0x0, propertyDISALLOWED}, // MALAYALAM DATE MARK
+ {0x0D7A, 0x0D7F, propertyPVALID}, // MALAYALAM LETTER CHILLU NN..MALAYALAM LETTER
+ {0x0D80, 0x0D81, propertyUNASSIGNED}, // ..
+ {0x0D82, 0x0D83, propertyPVALID}, // SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARG
+ {0x0D84, 0x0, propertyUNASSIGNED}, //
+ {0x0D85, 0x0D96, propertyPVALID}, // SINHALA LETTER AYANNA..SINHALA LETTER AUYANN
+ {0x0D97, 0x0D99, propertyUNASSIGNED}, // ..
+ {0x0D9A, 0x0DB1, propertyPVALID}, // SINHALA LETTER ALPAPRAANA KAYANNA..SINHALA L
+ {0x0DB2, 0x0, propertyUNASSIGNED}, //
+ {0x0DB3, 0x0DBB, propertyPVALID}, // SINHALA LETTER SANYAKA DAYANNA..SINHALA LETT
+ {0x0DBC, 0x0, propertyUNASSIGNED}, //
+ {0x0DBD, 0x0, propertyPVALID}, // SINHALA LETTER DANTAJA LAYANNA
+ {0x0DBE, 0x0DBF, propertyUNASSIGNED}, // ..
+ {0x0DC0, 0x0DC6, propertyPVALID}, // SINHALA LETTER VAYANNA..SINHALA LETTER FAYAN
+ {0x0DC7, 0x0DC9, propertyUNASSIGNED}, // ..
+ {0x0DCA, 0x0, propertyPVALID}, // SINHALA SIGN AL-LAKUNA
+ {0x0DCB, 0x0DCE, propertyUNASSIGNED}, // ..
+ {0x0DCF, 0x0DD4, propertyPVALID}, // SINHALA VOWEL SIGN AELA-PILLA..SINHALA VOWEL
+ {0x0DD5, 0x0, propertyUNASSIGNED}, //
+ {0x0DD6, 0x0, propertyPVALID}, // SINHALA VOWEL SIGN DIGA PAA-PILLA
+ {0x0DD7, 0x0, propertyUNASSIGNED}, //
+ {0x0DD8, 0x0DDF, propertyPVALID}, // SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOW
+ {0x0DE0, 0x0DF1, propertyUNASSIGNED}, // ..
+ {0x0DF2, 0x0DF3, propertyPVALID}, // SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHAL
+ {0x0DF4, 0x0, propertyDISALLOWED}, // SINHALA PUNCTUATION KUNDDALIYA
+ {0x0DF5, 0x0E00, propertyUNASSIGNED}, // ..
+ {0x0E01, 0x0E32, propertyPVALID}, // THAI CHARACTER KO KAI..THAI CHARACTER SARA A
+ {0x0E33, 0x0, propertyDISALLOWED}, // THAI CHARACTER SARA AM
+ {0x0E34, 0x0E3A, propertyPVALID}, // THAI CHARACTER SARA I..THAI CHARACTER PHINTH
+ {0x0E3B, 0x0E3E, propertyUNASSIGNED}, // ..
+ {0x0E3F, 0x0, propertyDISALLOWED}, // THAI CURRENCY SYMBOL BAHT
+ {0x0E40, 0x0E4E, propertyPVALID}, // THAI CHARACTER SARA E..THAI CHARACTER YAMAKK
+ {0x0E4F, 0x0, propertyDISALLOWED}, // THAI CHARACTER FONGMAN
+ {0x0E50, 0x0E59, propertyPVALID}, // THAI DIGIT ZERO..THAI DIGIT NINE
+ {0x0E5A, 0x0E5B, propertyDISALLOWED}, // THAI CHARACTER ANGKHANKHU..THAI CHARACTER KH
+ {0x0E5C, 0x0E80, propertyUNASSIGNED}, // ..
+ {0x0E81, 0x0E82, propertyPVALID}, // LAO LETTER KO..LAO LETTER KHO SUNG
+ {0x0E83, 0x0, propertyUNASSIGNED}, //
+ {0x0E84, 0x0, propertyPVALID}, // LAO LETTER KHO TAM
+ {0x0E85, 0x0E86, propertyUNASSIGNED}, //