├── .gitignore
├── .gitmodules
├── .npmignore
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── TODO.md
├── docs
└── index.md
├── examples
├── app.js
├── axfr.js
├── cluster.js
├── noanswer.js
└── reflect.js
├── lib
├── balancer.js
├── dns-buffer.js
├── errors.js
├── index.js
├── protocol.js
├── query.js
├── records
│ ├── a.js
│ ├── aaaa.js
│ ├── cname.js
│ ├── mx.js
│ ├── ns.js
│ ├── ptr.js
│ ├── soa.js
│ ├── srv.js
│ └── txt.js
├── server.js
├── tsig.js
└── validators.js
├── package.json
├── test
├── dig.js
├── dnsbuffer.js
├── helper.js
├── named.test.js
├── notify.test.js
├── pipeline.test.js
├── protocol.test.js
├── query.test.js
├── records.test.js
├── tsig.test.js
└── validator.test.js
└── tools
├── bashstyle
├── jsl.node.conf
├── jsstyle.conf
└── mk
├── Makefile.defs
├── Makefile.deps
├── Makefile.node.defs
├── Makefile.node.targ
├── Makefile.node_deps.defs
├── Makefile.node_deps.targ
├── Makefile.node_prebuilt.defs
├── Makefile.node_prebuilt.targ
└── Makefile.targ
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.swmp
3 | node_modules
4 | npm-debug.log
5 | tmp
6 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "deps/javascriptlint"]
2 | path = deps/javascriptlint
3 | url = https://github.com/davepacheco/javascriptlint.git
4 | [submodule "deps/jsstyle"]
5 | path = deps/jsstyle
6 | url = https://github.com/joyent/jsstyle.git
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .gitmodules
2 | deps
3 | docs
4 | Makefile
5 | node_modules
6 | test
7 | tools
8 | coverage
9 | man/src
10 | examples
11 | test.*
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "4.4"
4 | - "0.12"
5 | - "0.10"
6 | before_install:
7 | - "make check"
8 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | This repository uses GitHub pull requests for code review.
4 |
5 | See the [Joyent Engineering
6 | Guidelines](https://github.com/joyent/eng/blob/master/docs/index.md) for general
7 | best practices expected in this repository.
8 |
9 | Contributions should be "make check" clean. The "make check" target will
10 | install all necessary tools as part of its operation.
11 |
12 | If you're changing something non-trivial or user-facing, you may want to submit
13 | an issue first.
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 Trevor Orsztynowicz
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | #
2 | # This Source Code Form is subject to the terms of the Mozilla Public
3 | # License, v. 2.0. If a copy of the MPL was not distributed with this
4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 | #
6 |
7 | #
8 | # Copyright (c) 2014, Joyent, Inc.
9 | #
10 |
11 | #
12 | # Makefile: basic Makefile for template API service
13 | #
14 | # This Makefile is a template for new repos. It contains only repo-specific
15 | # logic and uses included makefiles to supply common targets (javascriptlint,
16 | # jsstyle, restdown, etc.), which are used by other repos as well. You may well
17 | # need to rewrite most of this file, but you shouldn't need to touch the
18 | # included makefiles.
19 | #
20 | # If you find yourself adding support for new targets that could be useful for
21 | # other projects too, you should add these to the original versions of the
22 | # included Makefiles (in eng.git) so that other teams can use them too.
23 | #
24 |
25 | #
26 | # Tools
27 | #
28 | NPM := $(shell which npm)
29 | NODEUNIT := ./node_modules/.bin/nodeunit
30 |
31 | #
32 | # Files
33 | #
34 | DOC_FILES = index.md boilerplateapi.md
35 | JS_FILES := $(shell find lib -name '*.js')
36 | JSON_FILES = package.json
37 | JSL_CONF_NODE = tools/jsl.node.conf
38 | JSL_FILES_NODE = $(JS_FILES)
39 | JSSTYLE_FILES = $(JS_FILES)
40 | JSSTYLE_FLAGS = -f tools/jsstyle.conf
41 |
42 | include ./tools/mk/Makefile.defs
43 |
44 | #
45 | # Repo-specific targets
46 | #
47 | .PHONY: all
48 | all: $(SMF_MANIFESTS) | $(NODEUNIT) $(REPO_DEPS)
49 | $(NPM) rebuild
50 |
51 | $(NODEUNIT): | $(NPM)
52 | $(NPM) install
53 |
54 | CLEAN_FILES += $(NODEUNIT) ./node_modules/nodeunit
55 |
56 | .PHONY: test
57 | test: $(NODEUNIT)
58 | $(NODEUNIT) test/*.test.js
59 |
60 | include ./tools/mk/Makefile.deps
61 | include ./tools/mk/Makefile.targ
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # node-mname - DNS Server in Node.js
2 |
3 | `mname` is a fork of the `node-named` library, which enables the development of
4 | DNS servers in node.js.
5 |
6 | This fork adds the following features:
7 | - Queries over TCP connections
8 | - AXFR, IXFR zone transfers
9 | - PTR records
10 | - Name compression
11 | - EDNS 1.0
12 |
13 | ## Creating a DNS Server
14 |
15 | ```js
16 | var named = require('./lib/index');
17 | var server = named.createServer();
18 | var ttl = 300;
19 |
20 | server.listenUdp(9999, '127.0.0.1', function() {
21 | console.log('DNS server started on port 9999');
22 | });
23 |
24 | server.on('query', function(query, done) {
25 | var domain = query.name();
26 | console.log('DNS Query: %s', domain)
27 | var target = new SOARecord(domain, {serial: 12345});
28 | query.addAnswer(domain, target, ttl);
29 | query.respond();
30 | done();
31 | });
32 | ```
33 |
34 | ## Creating DNS Records
35 |
36 | node-named provides helper functions for creating DNS records.
37 | The records are available under 'named.record.NAME' where NAME is one
38 | of ['A', 'AAAA', 'CNAME', 'SOA', 'MX', 'TXT, 'SRV']. It is important to
39 | remember that these DNS records are not permanently added to the server.
40 | They only exist for the length of the particular request. After that, they are
41 | destroyed. This means you have to create your own lookup mechanism.
42 |
43 | ```js
44 | var named = require('node-named');
45 |
46 | var soaRecord = named.SOARecord('example.com', {serial: 201205150000});
47 | console.log(soaRecord);
48 | ```
49 |
50 | ### Supported Record Types
51 |
52 | The following record types are supported
53 |
54 | * A (ipv4)
55 | * AAAA (ipv6)
56 | * CNAME (aliases)
57 | * SOA (start of authority)
58 | * MX (mail server records)
59 | * TXT (arbitrary text entries)
60 | * SRV (service discovery)
61 |
62 | ## Logging
63 |
64 | node-named uses [http://github.com/trentm/node-bunyan](bunyan) for logging.
65 | It's a lot nicer to use if you npm install bunyan and put the bunyan tool in
66 | your path. Otherwise, you will end up with JSON formatted log output by default.
67 |
68 | ### Replacing the default logger
69 |
70 | You can pass in an alternate logger if you wish. If you do not, then it will use
71 | bunyan by default. Your logger must expose the functions 'info', 'debug',
72 | 'warn', 'trace', 'error', and 'notice'.
73 |
74 | ## Tell me even more...
75 |
76 | When DNS was designed it was designed prior
77 | to the web existing, so many of the features in the RFC are either never used,
78 | or were never implemented. This server aims to be RFC compliant, but does not
79 | implement any other protocol other than INET (the one we're all used to), and
80 | only supports a handful of record types (the ones that are in use on a regular
81 | basis).
82 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | # TODO
2 |
3 | * Add better message compression using 'reference pointers' instead of
4 | additional nsName entries.
5 | * Add / build Query recursor (most record types possible using c-ares)
6 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: API | node-named
3 | ---
4 |
5 | # node-named
6 |
7 | node-named is a lightweight DNS server written in javascript. It implements
8 | commonly used functionality from a variety of DNS RFC specifications. Unlike
9 | many other DNS servers node-named does not attempt to manage DNS records for
10 | you. You simply get a request, build your response based on whatever criteria
11 | you desire, and then send that response back to the client.
12 |
13 | ## Seriously?
14 |
15 | Actually this is quite useful. Both BIND and PowerDNS assume that records never
16 | change, and are not designed to be manipulated using an API or have elegant
17 | pluggable storage mechanisms. This DNS server is good for creating services
18 | where your records may change frequently, or you would like to access records
19 | stored in a central system using a mechanism of your choosing.
20 |
21 |
22 | # Installation
23 |
24 | $ npm install named
25 |
26 | # Server API
27 |
28 | var named = require('./lib/index');
29 | var server = named.createServer();
30 |
31 | server.listen(9999, '127.0.0.1', function() {
32 | console.log('DNS server started on port 9999');
33 | });
34 |
35 | server.on('query', function(query) {
36 | var domain = query.name();
37 | var target = new named.SOARecord(domain, {serial: 12345});
38 | // 300 is the ttl for this record
39 | query.addAnswer(domain, target, 300);
40 | server.send(query);
41 | });
42 |
43 | Hit this DNS server with `dig` to see some results. Because we are only
44 | handling DNS responses for one record type (SOA or 'Start of Authority'), that
45 | is the response we will see, regardless of the type we make a request for. Dig
46 | is nice about this.
47 |
48 | $ dig @localhost -p9999 example.com SOA
49 |
50 | ; <<>> DiG 9.7.3-P3 <<>> @localhost -p9999 example.com SOA
51 | ; (3 servers found)
52 | ;; global options: +cmd
53 | ;; Got answer:
54 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32739
55 | ;; flags: qr rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
56 | ;; WARNING: recursion requested but not available
57 |
58 | ;; QUESTION SECTION:
59 | ;example.com. IN SOA
60 |
61 | ;; ANSWER SECTION:
62 | example.com. 5 IN SOA example.com. hostmaster.example.com. 12345 10 10 10 10
63 |
64 | ;; Query time: 10 msec
65 | ;; SERVER: ::1#9999(::1)
66 | ;; WHEN: Wed May 23 19:24:09 2012
67 | ;; MSG SIZE rcvd: 109
68 |
69 |
70 | ## Named API
71 |
72 | ### named.createServer([options])
73 |
74 | Create a new named server.
75 |
76 | options is an object which may specify:
77 |
78 | - log: an optional bunyan logger
79 | - name: an optional name used to identify the server
80 |
81 | Here is an example a named server listening on port 53
82 |
83 | var named = require('named');
84 |
85 | var server = named.createServer({
86 | name: 'named0'
87 | });
88 |
89 | server.listen(53);
90 |
91 | ## Class: named.Server
92 |
93 | ### server.listen(port, [host], [onListen])
94 |
95 | Start accepting connections on the specified `port` and `host`.
96 | If the host is ommited then it will listen on all IPv4 and IPv6 interfaces.
97 |
98 | This function is asyncronous. Whenever the server is litening a `listen` event
99 | will be emitted. The last parameter `onListen` will be executed but not attached
100 | to the listen event, when the server is listening.
101 |
102 | ### server.send(queryResponse)
103 |
104 | Sends a `queryResponse` message. The queryResponse includes information about
105 | the client in the object itself. The `send` function will encode the message
106 | and send the response to the appropriate client. Unsolicited DNS messages are
107 | not permitted. This function should only be used within the `query` event.
108 |
109 | **Note** If you do not add any answers to your query, then the `send()` method
110 | will send a 'null-response' DNS message. This is the equivalent of an HTTP 404.
111 |
112 | ### server.close(onClose)
113 |
114 | Stops listening and closes the socket. `onClose` is an optional callback that
115 | will be called once all underlying resources have been released.
116 |
117 | ### Event: 'listening'
118 |
119 | `function() { }`
120 |
121 | Emitted once, when the server starts listening on the specified `port` and
122 | `host`
123 |
124 | ### Event: 'query'
125 |
126 | `function (query) { }`
127 |
128 | Emitted each time there is valid request. `query` is an instance of
129 | `named.Query`
130 |
131 | ### Event: 'clientError'
132 |
133 | `function (error) { }`
134 |
135 | Emitted when there is an invalid DNS request. This may be caused by a bad UDP
136 | datagram, or some other malformed DNS request. Parser errors are not included
137 | here.
138 |
139 | `error` is an instance of `named.DnsError`
140 |
141 | ### Event: 'uncaughtException'
142 |
143 | `function (error) { }`
144 |
145 | Emitted when there is an uncaught exception somewhere in the protocol stack.
146 |
147 | `error` is an instance of `named.DnsError`
148 |
149 | ### Event: 'after'
150 |
151 | `function (query, bytes)`
152 |
153 | Emitted after a `query` is sent to a client. This can be used for logging
154 | purposes.
155 |
156 | `query` is an instance of `named.Query`
157 | `bytes` is the number of bytes sent over the wire to the client
158 |
159 | ## Class: named.Query
160 |
161 | A query message is emitted by the `query` event. Query messages include all of
162 | the information about the query from the Client, including the client details.
163 | Because DNS queries are UDP based, the entire query itself is echoed back onto
164 | the wire, with the answer appended to its appropriate 'answer' fields. Several
165 | headers are changed, but the query is the same.
166 |
167 | For this reason, you 'reflect' the modified query back to the client. Prior to
168 | doing this you can check the 'Question' and 'Type' of question and perform an
169 | appropriate lookup & generate an appropriate response.
170 |
171 | ### query.name()
172 |
173 | Returns a string containing the query question name. This may be a hostname, but
174 | depends on the type
175 |
176 | ### query.type()
177 |
178 | Returns a string containing the type code for the query question.
179 |
180 | ### query.answers()
181 |
182 | Returns an array of answers that have been added to the query
183 |
184 | ### query.addAnswer(name, record, ttl)
185 |
186 | Add an instances of `named.Record` to the query.
187 | Name is the name you want to respond with (in 99.99% of cases, the
188 | query.name()), record is the record instance, and type is the type of record you
189 | are responding with. In most cases this will be what the query.type() returns,
190 | but for instances like an 'A' or 'AAAA' request you may elect to respond with a
191 | CNAME record.
192 |
193 | ### query.operation()
194 |
195 | Returns the type of operation that the client is requesting. In almost all cases
196 | this will be 'query'. Valid operations are 'query', 'status', 'notify', and
197 | 'update'.
198 |
199 | ### query.encode()
200 |
201 | Encodes the query and stores the results as a `buffer` in the query itself.
202 | This function should never need to be invoked, as the `server.send` function
203 | will automatically encode a query prior to being sent to the client.
204 |
205 |
206 | ## Records
207 |
208 | A DNS query is a question posed to a server about a record for a specific
209 | domain. The questions are for specific 'types' of records. Of the types listed
210 | in all of the DNS RFCs only some are still in use, and even fewer are
211 | frequently used. Each type of request has an appropriate response, each of
212 | which have different formats. These response formats are known as
213 | "Resource Records" or for the sake of named, just 'Records'.
214 |
215 | All records in named are created using the `new` keyword.
216 |
217 | ### named.SOARecord(domain, [options])
218 |
219 | Create a DNS 'Start of Authority' record
220 |
221 | Options:
222 |
223 | - `admin`: The DNS name formatted email address of the administrator for this
224 | domain. Defaults to 'hostmaster.[domain]'
225 | - `serial`: The serial number of this domain. Defaults to 0
226 | - `refresh`: The refresh interval for this domain. Defaults to 10
227 | - `retry`: The retry interval for this domain. Defaults to 10
228 | - `expire`: The expire interval for this domain. Defaults to 10
229 | - `ttl`: The default time-to-live for records in this domain. Defaults to 10
230 |
231 | ### named.ARecord(ipv4Addr)
232 |
233 | Create an IPv4 resource record
234 | `ipv4Addr` must be a valid IPv4 address (string).
235 |
236 | ### named.AAAARecord(ipv6Addr)
237 |
238 | Create an IPv6 resource record.
239 | `ipv6Addr` must be a valid IPv6 address (string).
240 |
241 | ### named.CNAMERecord(target)
242 |
243 | Create an Alias record. When these records are sent to the client, the client
244 | will often make an additional request for the alias itself.
245 |
246 | ### named.MXRecord(exchange, options)
247 |
248 | Create a Mail Server record. A client making this request will often make an
249 | additional request for the entries in these records.
250 | `exchange` is the name of the mailserver that handles mail for this domain.
251 |
252 | Options:
253 | - `ttl`: The time-to-live for this particular mail server record
254 | - `priority`: The priority of this mailserver over other mailservers. You may
255 | have multiple mail servers. Lowest priority server is selected by client.
256 |
257 | ### named.SRVRecord(target, port, options)
258 |
259 | Create a Server Resource record.
260 | `target` is the name of the server that handles this particular resource name
261 | `port` is the tcp/udp port where the service may be reached
262 |
263 | Options:
264 | - `weight`: Used by the client for selecting between multiple results. Higher
265 | weight wins. Default is 10
266 | - `priority`: Used by the client for selecting between mutiple results. Higher
267 | priortiy wins. Default is 10
268 |
269 | ### named.TXTRecord(target)
270 |
271 | Create a text resource record.
272 | `target` can be any text up to 500 bytes in length
273 |
274 | ## Class: named.Record
275 |
276 | ### record.valid()
277 |
278 | This function will ensure that the data you used to create the record is in fact
279 | valid.
280 |
281 | ## DnsError
282 |
283 | DnsErrors rae objects that consist of:
284 | - `code`: A unique error number
285 | - `name`: the name of the error
286 |
287 | DnsErrors are:
288 |
289 | - `NoError`
290 | - `ProtocolError`
291 | - `CannotProcessError`
292 | - `NoNameError`
293 | - `NotImplementedError`
294 | - `RefusedError`
295 | - `ExceptionError`
296 |
297 | ## Class: named.DnsError
298 |
299 | ### error.message()
300 |
301 | Returns the message that was passed in to the error. The message is a string,
302 | and can be used for logging purposes
303 |
304 | ## Server Properties
305 |
306 |
307 |
--------------------------------------------------------------------------------
/examples/app.js:
--------------------------------------------------------------------------------
1 | var named = require('../lib');
2 | var server = named.createServer();
3 |
4 | server.listen(9999, '127.0.0.1', function() {
5 | console.log('DNS server started on port 9999');
6 | });
7 |
8 | console.log(named.SoaRecord);
9 |
10 | server.on('query', function(query) {
11 | var domain = query.name();
12 | var record = new named.SOARecord(domain, {serial: 12345, ttl: 300});
13 | query.addAnswer(domain, record, 300);
14 | server.send(query);
15 | });
16 |
--------------------------------------------------------------------------------
/examples/axfr.js:
--------------------------------------------------------------------------------
1 | var named = require('../lib/index');
2 | var bunyan = require('bunyan');
3 | var log = bunyan.createLogger({name: 'test'});
4 |
5 | var server = named.createServer();
6 | var ttl = 3600;
7 |
8 | server.listenUdp({port: 9953, address: '127.0.0.1'});
9 | server.listenTcp({port: 9953, address: '127.0.0.1'});
10 |
11 | server.on('query', function(query) {
12 | var domain = query.name();
13 | log.info({name: query.name(), type: query.type()});
14 |
15 | if (query.type() === 'AXFR') {
16 | var soa = new named.SOARecord(domain, {serial: 12345});
17 | query.addAnswer(domain, soa, ttl);
18 | server.send(query);
19 |
20 | var a = new named.ARecord("1.2.3.4");
21 | var a2 = new named.ARecord("1.2.3.5");
22 | query.addAnswer("foo." + domain, a, ttl);
23 | query.addAnswer("bar." + domain, a, ttl);
24 | query.addAnswer("foobar." + domain, a2, ttl);
25 | server.send(query);
26 |
27 | query.addAnswer(domain, soa, ttl);
28 | server.send(query);
29 |
30 | } else if (query.type() === 'IXFR') {
31 | var base = query.ixfrBase();
32 | var oldSoa = new named.SOARecord(domain, {serial: base});
33 | var newSoa = new named.SOARecord(domain, {serial: 12345});
34 | query.addAnswer(domain, newSoa, ttl);
35 | server.send(query);
36 |
37 | var a = new named.ARecord("1.2.3.4");
38 | var a2 = new named.ARecord("1.2.3.5");
39 |
40 | /* removed since old serial */
41 | query.addAnswer(domain, oldSoa, ttl);
42 | server.send(query);
43 | query.addAnswer("tri." + domain, a, ttl);
44 |
45 | /* new additions */
46 | query.addAnswer(domain, newSoa, ttl);
47 | query.addAnswer("foo." + domain, a, ttl);
48 | query.addAnswer("bar." + domain, a, ttl);
49 | query.addAnswer("foobar." + domain, a2, ttl);
50 | server.send(query);
51 |
52 | query.addAnswer(domain, newSoa, ttl);
53 | server.send(query);
54 | }
55 | });
56 |
--------------------------------------------------------------------------------
/examples/cluster.js:
--------------------------------------------------------------------------------
1 | var named = require('../lib/index');
2 | var cluster = require('cluster');
3 |
4 | /**
5 | *
6 | *
7 | * Example that uses cluster (http://nodejs.org/api/cluster.html) to
8 | * spin up multiple workers to handle requests.
9 | *
10 | *
11 | *
12 | * You can test it like this: dig @localhost -p 9999 goodtimes.com
13 | *
14 | *
15 | *
16 | * Or using dnsperf:
17 | *
18 | *
19 | *
20 | * $ echo "goodtimes.com A" > /tmp/f
21 | * $ dnsperf -s localhost -p 9999 -d /tmp/f -l 300
22 | *
23 | *
24 | *
25 | * Unfortunately the surprise is that more workers (4) run slower (3711 qps),
26 | * than a single worker (4084 qps).
27 | *
28 | *
29 | * @author Brian Hammond
30 | *
31 | */
32 | var ClusterDns = function() {
33 |
34 | /* lame config */
35 | this.PORT = 9999;
36 | this.LISTEN = '127.0.0.1';
37 | this.SCALING = 1;
38 |
39 | this.master = function() {
40 | var numCPUs = require('os').cpus().length;
41 | var workers = numCPUs * this.SCALING;
42 | workers = 1;
43 |
44 | console.log( 'there are numCPUs:' + numCPUs + ', starting ' + workers + ' workers' );
45 |
46 | for (var i = 0; i < workers ; i++) {
47 | cluster.fork();
48 | }
49 |
50 | cluster.on('exit', function(worker, code, signal) {
51 | console.log('worker ' + worker.process.pid + ' died');
52 | });
53 | }
54 |
55 | this.randumb = function() {
56 | var r = function() { return Math.floor( Math.random() * 252 + 1 ) };
57 | return r() + '.' + r() + '.' + r() + '.' + r();
58 | };
59 |
60 | this.friendo = function() {
61 | var thiz = this;
62 | var server = named.createServer();
63 |
64 | var port = this.PORT;
65 | var listen = this.LISTEN;
66 |
67 | server.listen( port, listen, function() {
68 | console.log( 'DNS worker started on ' + listen + ':' + port + ', pid:' + cluster.worker.process.pid );
69 | });
70 |
71 | server.on('query', function(query) {
72 | var domain = query.name();
73 | var ttl = 0;
74 | query.addAnswer( domain, new named.SOARecord(domain, {serial: 12345}, ttl ) );
75 | query.addAnswer( domain, new named.ARecord( thiz.randumb(), ttl ) );
76 | server.send(query);
77 | });
78 | };
79 |
80 | this.run = function() {
81 | if ( cluster.isMaster ) {
82 | this.master();
83 | } else {
84 | this.friendo();
85 | }
86 | };
87 | };
88 |
89 | new ClusterDns().run();
90 |
--------------------------------------------------------------------------------
/examples/noanswer.js:
--------------------------------------------------------------------------------
1 | var named = require('../lib');
2 | var server = named.createServer();
3 |
4 | server.listen(9999, '127.0.0.1', function() {
5 | console.log('DNS server started on port 9999');
6 | });
7 |
8 | server.on('query', function(query) {
9 | var domain = query.name()
10 | var type = query.type();
11 | console.log('DNS Query: (%s) %s', type, domain);
12 | // If we do not add any answers to the query then the
13 | // result will be a 'null-answer' message. This is how
14 | // you send a "404" to a DNS client
15 | server.send(query);
16 | });
17 |
18 | server.on('clientError', function(error) {
19 | console.log("there was a clientError: %s", error);
20 | });
21 |
22 | server.on('uncaughtException', function(error) {
23 | console.log("there was an excepton: %s", error.message());
24 | });
25 |
--------------------------------------------------------------------------------
/examples/reflect.js:
--------------------------------------------------------------------------------
1 | var named = require('../lib');
2 | var server = named.createServer();
3 |
4 | server.listen(9999, '127.0.0.1', function() {
5 | console.log('DNS server started on port 9999');
6 | });
7 |
8 | server.on('query', function(query) {
9 | var domain = query.name()
10 | var type = query.type();
11 | console.log('DNS Query: (%s) %s', type, domain);
12 | switch (type) {
13 | case 'A':
14 | var record = new named.ARecord('127.0.0.1');
15 | query.addAnswer(domain, record, 300);
16 | break;
17 | case 'AAAA':
18 | var record = new named.AAAARecord('::1');
19 | query.addAnswer(domain, record, 300);
20 | break;
21 | case 'CNAME':
22 | var record = new named.CNAMERecord('cname.example.com');
23 | query.addAnswer(domain, record, 300);
24 | break;
25 | case 'MX':
26 | var record = new named.MXRecord('smtp.example.com');
27 | query.addAnswer(domain, record, 300);
28 | break;
29 | case 'SOA':
30 | var record = new named.SOARecord('example.com');
31 | query.addAnswer(domain, record, 300);
32 | break;
33 | case 'SRV':
34 | var record = new named.SRVRecord('sip.example.com', 5060);
35 | query.addAnswer(domain, record, 300);
36 | break;
37 | case 'TXT':
38 | var record = new named.TXTRecord('hello world');
39 | query.addAnswer(domain, record, 300);
40 | break;
41 | }
42 | server.send(query);
43 | });
44 |
45 | server.on('clientError', function(error) {
46 | console.log("there was a clientError: %s", error);
47 | });
48 |
--------------------------------------------------------------------------------
/lib/balancer.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018, Joyent, Inc
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | * SOFTWARE.
21 | */
22 |
23 | /*
24 | * MNAME-BALANCER INTERFACE
25 | *
26 | * To support vertically scaling an individual DNS server through the use of
27 | * multiple Node processes, a load balancer has been built that is specifically
28 | * tailored for use with this DNS server library. A custom protocol has been
29 | * devised, which represents an interface between this library and the load
30 | * balancer software. The load balancer, including full protocol
31 | * documentation, is available here:
32 | *
33 | * https://github.com/joyent/mname-balancer
34 | *
35 | */
36 |
37 | var assert = require('assert-plus');
38 | var util = require('util');
39 | var stream = require('stream');
40 |
41 |
42 | var FRAME_TYPE_CLIENT_HELLO = 1;
43 | var FRAME_TYPE_INBOUND_UDP = 2;
44 | var FRAME_TYPE_INBOUND_TCP = 3;
45 | var FRAME_TYPE_CLIENT_HEARTBEAT = 4;
46 | var FRAME_TYPE_SERVER_HELLO = 1001;
47 | var FRAME_TYPE_OUTBOUND_UDP = 1002;
48 | var FRAME_TYPE_INBOUND_TCP_OK = 1003;
49 | var FRAME_TYPE_SERVER_HEARTBEAT = 1004;
50 |
51 | var SIZE_U32 = 4;
52 |
53 |
54 | function BalancerTransform(opts) {
55 | var self = this;
56 |
57 | stream.Transform.call(self);
58 |
59 | self.bal_accum = new Buffer(0);
60 | self.bal_tcp = null;
61 | self.bal_hello = false;
62 | self.bal_log = opts.log;
63 | self.bal_sock = opts.sock;
64 | }
65 | util.inherits(BalancerTransform, stream.Transform);
66 |
67 | BalancerTransform.prototype.discard = function (n) {
68 | var self = this;
69 |
70 | self.bal_accum = self.bal_accum.slice(n);
71 | };
72 |
73 | BalancerTransform.prototype.avail = function (n) {
74 | var self = this;
75 |
76 | return (self.bal_accum.length >= n);
77 | };
78 |
79 | /*
80 | * Read a little endian uint32_t from the provided offset (in bytes) into the
81 | * buffer.
82 | */
83 | BalancerTransform.prototype.readU32 = function (offs) {
84 | var self = this;
85 |
86 | return (self.bal_accum.readUInt32LE(offs));
87 | };
88 |
89 | /*
90 | * Read a little endian IPv4 address from the provided offset (in bytes) into
91 | * the buffer.
92 | */
93 | BalancerTransform.prototype.readIPv4 = function (offs) {
94 | var self = this;
95 |
96 | var ipaddr = [
97 | self.bal_accum.readUInt8(offs + 3),
98 | self.bal_accum.readUInt8(offs + 2),
99 | self.bal_accum.readUInt8(offs + 1),
100 | self.bal_accum.readUInt8(offs + 0)
101 | ].join('.');
102 |
103 | return (ipaddr);
104 | };
105 |
106 | BalancerTransform.prototype._transform = function (o, _, done) {
107 | var self = this;
108 | var sock = self.bal_sock;
109 | var log = self.bal_log;
110 |
111 | if (self.bal_tcp !== null) {
112 | /*
113 | * We are piped to the emulated TCP stream, so just pass on
114 | * buffers as we get them.
115 | */
116 | self.push(o);
117 | setImmediate(done);
118 | return;
119 | }
120 |
121 | self.bal_accum = Buffer.concat([ self.bal_accum, o ],
122 | self.bal_accum.length + o.length);
123 |
124 | while (self.avail(SIZE_U32)) {
125 | /*
126 | * Read frame type.
127 | */
128 | var frame_type = self.readU32(0);
129 | var out, ipaddr, port;
130 |
131 | if (frame_type === FRAME_TYPE_CLIENT_HELLO) {
132 | self.discard(SIZE_U32);
133 | self.bal_hello = true;
134 |
135 | /*
136 | * A CLIENT_HELLO frame requires a SERVER_HELLO
137 | * response.
138 | */
139 | out = new Buffer(SIZE_U32);
140 | out.writeUInt32LE(FRAME_TYPE_SERVER_HELLO, 0);
141 | sock.write(out);
142 | continue;
143 | }
144 |
145 | if (frame_type === FRAME_TYPE_INBOUND_TCP) {
146 | /*
147 | * This frame signals a request to convert this session
148 | * to a TCP proxy session.
149 | */
150 | if (!self.avail(3 * SIZE_U32)) {
151 | /*
152 | * Wait for the entire frame to arrive.
153 | */
154 | break;
155 | }
156 |
157 | ipaddr = self.readIPv4(1 * SIZE_U32);
158 | port = self.readU32(2 * SIZE_U32);
159 |
160 | log.info('backend TCP connection: ' +
161 | 'remote peer %s:%d', ipaddr, port);
162 |
163 | /*
164 | * Send acknowledgement to the balancer that this is
165 | * now a proxied TCP stream.
166 | */
167 | out = new Buffer(SIZE_U32);
168 | out.writeUInt32LE(FRAME_TYPE_INBOUND_TCP_OK, 0);
169 | sock.write(out);
170 |
171 | /*
172 | * As part of the conversion into a proxied TCP stream,
173 | * we must forward on several events from the
174 | * underlying socket to the consumer.
175 | */
176 | sock.on('timeout', function () {
177 | self.emit('timeout');
178 | });
179 | sock.on('error', function (sockErr) {
180 | self.emit('error', sockErr);
181 | });
182 |
183 | /*
184 | * Dress this Transform up as if it were a regular TCP
185 | * connection from a DNS client.
186 | */
187 | var tcp = Object.create(self);
188 |
189 | tcp.remoteAddress = ipaddr;
190 | tcp.remotePort = port;
191 | tcp.setTimeout = sock.setTimeout.bind(sock);
192 | tcp.end = sock.end.bind(sock);
193 | tcp.destroy = sock.destroy.bind(sock);
194 | tcp.write = sock.write.bind(sock);
195 |
196 | self.bal_tcp = tcp;
197 | self.emit('inbound_tcp', tcp);
198 |
199 | /*
200 | * Push any remaining data after the frame header into
201 | * the emulated TCP stream.
202 | */
203 | self.discard(3 * SIZE_U32);
204 | self.push(self.bal_accum);
205 | self.bal_accum = null;
206 |
207 | setImmediate(done);
208 | return;
209 | }
210 |
211 | var err;
212 | if (!self.bal_hello) {
213 | err = new Error('frame type ' + frame_type +
214 | ' before HELLO');
215 | log.debug(err, 'protocol error on balancer connection');
216 | done(err);
217 | sock.destroy();
218 | return;
219 | }
220 |
221 | if (frame_type === FRAME_TYPE_CLIENT_HEARTBEAT) {
222 | self.discard(SIZE_U32);
223 |
224 | /*
225 | * The balancer expects a prompt reply for each
226 | * heartbeat frame.
227 | */
228 | out = new Buffer(SIZE_U32);
229 | out.writeUInt32LE(FRAME_TYPE_SERVER_HEARTBEAT, 0);
230 | sock.write(out);
231 | continue;
232 | }
233 |
234 | /*
235 | * All other frame types have been processed already. At this
236 | * point, the frame _must_ be an inbound UDP packet.
237 | */
238 | if (frame_type !== FRAME_TYPE_INBOUND_UDP) {
239 | err = new Error('frame type ' + frame_type +
240 | ' is not valid');
241 | log.debug(err, 'protocol error on balancer connection');
242 | done(err);
243 | sock.destroy();
244 | return;
245 | }
246 |
247 | var headerlen = 4 * SIZE_U32;
248 | if (!self.avail(headerlen)) {
249 | /*
250 | * The complete header has not yet arrived.
251 | */
252 | break;
253 | }
254 |
255 | ipaddr = self.readIPv4(1 * SIZE_U32);
256 | port = self.readU32(2 * SIZE_U32);
257 | var datalen = self.readU32(3 * SIZE_U32);
258 |
259 | if (!self.avail(headerlen + datalen)) {
260 | /*
261 | * Entire frame has not yet arrived.
262 | */
263 | break;
264 | }
265 |
266 | log.trace('balancer packet: %s:%d (len %d)', ipaddr, port,
267 | datalen);
268 |
269 | var rinfo = { address: ipaddr, port: port };
270 |
271 | self.emit('inbound_udp', rinfo,
272 | self.bal_accum.slice(headerlen, headerlen + datalen),
273 | function reply(buf, from, len, sendport, addr, cb) {
274 | assert.equal(from, 0);
275 | assert.equal(len, buf.length);
276 |
277 | var hdr = new Buffer(4 * SIZE_U32);
278 | hdr.writeUInt32LE(FRAME_TYPE_OUTBOUND_UDP, 0);
279 |
280 | var octs = addr.split('.');
281 | hdr.writeUInt8(octs[3], 1 * SIZE_U32 + 0);
282 | hdr.writeUInt8(octs[2], 1 * SIZE_U32 + 1);
283 | hdr.writeUInt8(octs[1], 1 * SIZE_U32 + 2);
284 | hdr.writeUInt8(octs[0], 1 * SIZE_U32 + 3);
285 |
286 | hdr.writeUInt32LE(sendport, 2 * SIZE_U32);
287 | hdr.writeUInt32LE(len, 3 * SIZE_U32);
288 |
289 | sock.write(hdr);
290 | sock.write(buf);
291 |
292 | setImmediate(cb);
293 | });
294 |
295 | self.discard(headerlen + datalen);
296 | }
297 |
298 | setImmediate(done);
299 | };
300 |
301 |
302 | function wrapBalancerConnection(opts) {
303 | var log = opts.log;
304 | var sock = opts.sock;
305 |
306 | var t = new BalancerTransform({ log: log, sock: sock });
307 |
308 | sock.pipe(t);
309 |
310 | sock.on('end', function () {
311 | log.warn('balancer ended connection');
312 | });
313 | sock.on('error', function (err) {
314 | log.warn(err, 'error on balancer connection');
315 | sock.destroy();
316 | });
317 | sock.on('close', function () {
318 | log.trace('balancer socket closed');
319 | });
320 |
321 | return (t);
322 | }
323 |
324 | module.exports = {
325 | wrapBalancerConnection: wrapBalancerConnection
326 | };
327 |
--------------------------------------------------------------------------------
/lib/dns-buffer.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 |
24 | module.exports = DNSBuffer;
25 |
26 | var assert = require('assert-plus');
27 |
28 | function DNSBuffer(opts) {
29 | assert.object(opts, 'options');
30 | assert.optionalBuffer(opts.buffer, 'options.buffer');
31 |
32 | this._size = opts.buffer ? opts.buffer.length : 1024;
33 | this._buffer = opts.buffer || (new Buffer(this._size));
34 | this._offset = 0;
35 | this._ncache = new NameOffsetCache();
36 | }
37 |
38 | DNSBuffer.prototype.toBuffer = function () {
39 | return (this._buffer.slice(0, this._offset));
40 | };
41 |
42 | DNSBuffer.prototype.atEnd = function () {
43 | return (this._offset >= this._size);
44 | };
45 |
46 | DNSBuffer.prototype.remainder = function () {
47 | return (this._buffer.slice(this._offset, this._size));
48 | };
49 |
50 | DNSBuffer.prototype.skip = function (n) {
51 | this._offset += n;
52 | };
53 |
54 | DNSBuffer.prototype.expand = function () {
55 | this._size *= 2;
56 | var buf = new Buffer(this._size);
57 | this._buffer.copy(buf, 0);
58 | this._buffer = buf;
59 | };
60 |
61 | DNSBuffer.prototype.read = function (bytes) {
62 | var v = this._buffer.slice(this._offset, this._offset + bytes);
63 | this._offset += bytes;
64 | return (v);
65 | };
66 |
67 | DNSBuffer.prototype.readUInt32 = function () {
68 | var v = this._buffer.readUInt32BE(this._offset);
69 | this._offset += 4;
70 | return (v);
71 | };
72 |
73 | DNSBuffer.prototype.readUInt16 = function () {
74 | var v = this._buffer.readUInt16BE(this._offset);
75 | this._offset += 2;
76 | return (v);
77 | };
78 |
79 | DNSBuffer.prototype.readUInt8 = function () {
80 | var v = this._buffer.readUInt8(this._offset++);
81 | return (v);
82 | };
83 |
84 | var NAME_META_MASK = 0xC0;
85 | var NAME_STRING = 0x00;
86 | var NAME_PTR = 0xC0;
87 |
88 | DNSBuffer.prototype.readName = function () {
89 | var rlen, name = '';
90 |
91 | var off = this._offset;
92 | var finalOff;
93 |
94 | rlen = this._buffer.readUInt8(off++);
95 | while (rlen !== 0x00 && name.length < 256) {
96 | var meta = rlen & NAME_META_MASK;
97 |
98 | if (meta == NAME_STRING) {
99 | assert.ok(off + rlen < this._size,
100 | 'invalid name label length');
101 | var buf = this._buffer.slice(off,
102 | off + rlen);
103 | off += rlen;
104 | name += buf.toString('ascii') + '.';
105 |
106 | } else if (meta == NAME_PTR) {
107 | var ptr = this._buffer.readUInt8(off++);
108 | ptr = ptr | ((rlen & ~(0xC0)) << 8);
109 |
110 | assert.ok(ptr < this._size,
111 | 'Invalid label pointer (off end of buffer)');
112 | assert.ok(!(ptr >= this._offset && ptr <= off),
113 | 'Invalid label pointer (causes a loop)');
114 |
115 | if (finalOff === undefined)
116 | finalOff = off;
117 | off = ptr;
118 |
119 | } else {
120 | throw (new Error('Invalid name segment type: ' + meta));
121 | }
122 |
123 | rlen = this._buffer.readUInt8(off++);
124 | }
125 |
126 | if (name.length > 255) {
127 | throw (new Error('Invalid name (maximum length exceeded)'));
128 | }
129 |
130 | if (finalOff === undefined)
131 | finalOff = off;
132 | this._offset = finalOff;
133 |
134 | if (name.charAt(name.length - 1) === '.')
135 | name = name.slice(0, name.length - 1);
136 |
137 | return (name);
138 | };
139 |
140 | DNSBuffer.prototype.writeName = function (name) {
141 | assert.string(name, 'name');
142 |
143 | if (name === '' || name === '.') {
144 | this.writeUInt8(0);
145 | return;
146 | }
147 | if (name.charAt(name.length - 1) === '.')
148 | name = name.slice(0, name.length - 1);
149 | var maxIdx = name.length;
150 |
151 | var suffix = this._ncache.getSuffix(name);
152 | if (suffix.index !== undefined) {
153 | maxIdx = suffix.index;
154 | }
155 |
156 | var rlen;
157 | var i = -1, j;
158 | while (i < maxIdx) {
159 | var rem = name.slice(i + 1);
160 | j = name.indexOf('.', i + 1);
161 | if (j === -1)
162 | j = name.length;
163 | var part = name.slice(i + 1, j);
164 | i = j;
165 | rlen = part.length;
166 |
167 | if (rlen === 0)
168 | break;
169 |
170 | /* Can only use ptrs to things in the first 0x3fff bytes. */
171 | if (this._offset <= 0x3fff)
172 | this._ncache.add(rem, this._offset);
173 |
174 | assert.ok(rlen < 64, 'segment "' + part + '" of name "' +
175 | name + '" is too long');
176 | this.writeUInt8(rlen);
177 | this.write(new Buffer(part, 'ascii'));
178 | }
179 |
180 | if (suffix.offset !== undefined) {
181 | assert.ok(suffix.offset <= 0x3fff);
182 |
183 | var ptr = suffix.offset & 0xff;
184 | rlen = NAME_PTR | ((suffix.offset & 0x3f00) >> 8);
185 | this.writeUInt8(rlen);
186 | this.writeUInt8(ptr);
187 |
188 | } else {
189 | this.writeUInt8(0);
190 | }
191 | };
192 |
193 | DNSBuffer.prototype.writeNamePlain = function (name) {
194 | assert.string(name, 'name');
195 |
196 | if (name === '' || name === '.') {
197 | this.writeUInt8(0);
198 | return;
199 | }
200 | var rparts = name.split('.');
201 |
202 | var rlen;
203 | while (rparts.length > 0) {
204 | var part = rparts.shift();
205 | rlen = part.length;
206 | assert.ok(rlen < 64, 'segment "' + part + '" of name "' +
207 | name + '" is too long');
208 | this.writeUInt8(rlen);
209 | this.write(new Buffer(part, 'ascii'));
210 | }
211 | this.writeUInt8(0);
212 | };
213 |
214 | DNSBuffer.prototype.writeUInt32 = function (v) {
215 | while (this._offset + 4 > this._size)
216 | this.expand();
217 | this._buffer.writeUInt32BE(v, this._offset);
218 | this._offset += 4;
219 | };
220 |
221 | DNSBuffer.prototype.writeUInt16 = function (v) {
222 | while (this._offset + 2 > this._size)
223 | this.expand();
224 | this._buffer.writeUInt16BE(v, this._offset);
225 | this._offset += 2;
226 | };
227 |
228 | DNSBuffer.prototype.writeUInt8 = function (v) {
229 | while (this._offset + 1 > this._size)
230 | this.expand();
231 | this._buffer.writeUInt8(v, this._offset++);
232 | };
233 |
234 | DNSBuffer.prototype.write = function (buf) {
235 | while (this._offset + buf.length > this._size)
236 | this.expand();
237 | buf.copy(this._buffer, this._offset);
238 | this._offset += buf.length;
239 | };
240 |
241 | /* node 0.10 and earlier do not have read/writeUIntBE on Buffers */
242 | if (Buffer.prototype.readUIntBE !== undefined &&
243 | Buffer.prototype.writeUIntBE !== undefined) {
244 |
245 | DNSBuffer.prototype.readLengthPrefixed = function (lenBytes, cb) {
246 | var len = this._buffer.readUIntBE(this._offset, lenBytes);
247 | this._offset += lenBytes;
248 |
249 | var child = Object.create(this);
250 | child._size = this._offset + len;
251 | var ret = cb(child);
252 | this._offset += len;
253 |
254 | return (ret);
255 | };
256 |
257 | DNSBuffer.prototype.writeLengthPrefixed = function (lenBytes, cb) {
258 | var lenOffset = this._offset;
259 | this._offset += lenBytes;
260 | var ret = cb(this);
261 | var len = this._offset - lenOffset - lenBytes;
262 | this._buffer.writeUIntBE(len, lenOffset, lenBytes);
263 |
264 | return (ret);
265 | };
266 |
267 | } else {
268 |
269 | DNSBuffer.prototype.readLengthPrefixed = function (lenBytes, cb) {
270 | var len;
271 | switch (lenBytes) {
272 | case 1:
273 | len = this._buffer.readUInt8(this._offset);
274 | break;
275 | case 2:
276 | len = this._buffer.readUInt16BE(this._offset);
277 | break;
278 | case 4:
279 | len = this._buffer.readUInt32BE(this._offset);
280 | break;
281 | default:
282 | throw (new Error('Invalid prefix length value'));
283 | }
284 | this._offset += lenBytes;
285 |
286 | var child = Object.create(this);
287 | child._size = this._offset + len;
288 | var ret = cb(child);
289 | this._offset += len;
290 |
291 | return (ret);
292 | };
293 |
294 | DNSBuffer.prototype.writeLengthPrefixed = function (lenBytes, cb) {
295 | assert.ok(lenBytes === 1 || lenBytes === 2 || lenBytes === 4);
296 |
297 | var lenOffset = this._offset;
298 | this._offset += lenBytes;
299 | var ret = cb(this);
300 | var len = this._offset - lenOffset - lenBytes;
301 | switch (lenBytes) {
302 | case 1:
303 | this._buffer.writeUInt8(len, lenOffset);
304 | break;
305 | case 2:
306 | this._buffer.writeUInt16BE(len, lenOffset);
307 | break;
308 | case 4:
309 | this._buffer.writeUInt32BE(len, lenOffset);
310 | break;
311 | default:
312 | throw (new Error('Invalid prefix length value'));
313 | }
314 |
315 | return (ret);
316 | };
317 |
318 | }
319 |
320 | /*
321 | * We are expected to run on node v0.10, which lacks Map. Polyfill just the
322 | * bits of it we actually need here. Map is a bit faster than an object for our
323 | * task, but an object will do as a substitute on 0.10.
324 | */
325 | if (typeof (Map) !== 'function') {
326 | var Map = function () {
327 | this._obj = Object.create(null);
328 | };
329 | Map.prototype.set = function (k, v) {
330 | this._obj[k] = v;
331 | };
332 | Map.prototype.get = function (k) {
333 | return (this._obj[k]);
334 | };
335 | }
336 |
337 | function NameOffsetCache() {
338 | this.root = new Map();
339 | }
340 |
341 | NameOffsetCache.prototype.add = function (name, offset) {
342 | this.root.set(name, offset);
343 | };
344 |
345 | NameOffsetCache.prototype.getSuffix = function (name) {
346 | var i = -1;
347 | while (i < name.length) {
348 | var off = this.root.get(name.slice(i + 1));
349 | if (off !== undefined) {
350 | return ({
351 | index: i,
352 | offset: off
353 | });
354 | }
355 | if ((i = name.indexOf('.', i + 1)) === -1)
356 | break;
357 | }
358 | return ({});
359 | };
360 |
--------------------------------------------------------------------------------
/lib/errors.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | /*
24 | * This excellent error creator concept was borrowed from Mark Cavage
25 | * https://github.com/mcavage/node-ldapjs/blob/master/lib/errors/index.js
26 | */
27 |
28 |
29 | var util = require('util');
30 |
31 | var CODES = {
32 | DNS_NO_ERROR: 0,
33 | DNS_PROTOCOL_ERROR: 1,
34 | DNS_CANNOT_PROCESS: 2,
35 | DNS_NO_NAME: 3,
36 | DNS_NOT_IMPLEMENTED: 4,
37 | DNS_REFUSED: 5,
38 | DNS_EXCEPTION: 6
39 | };
40 |
41 | var ERRORS = [];
42 |
43 | function DnsError(name, code, msg, caller) {
44 | if (Error.captureStackTrace)
45 | Error.captureStackTrace(this, caller || DnsError);
46 |
47 | this.code = code;
48 | this.name = name;
49 | this.message = msg || name;
50 | }
51 |
52 | util.inherits(DnsError, Error);
53 |
54 |
55 | module.exports = {};
56 | module.exports.DnsError = DnsError;
57 |
58 | Object.keys(CODES).forEach(function (code) {
59 | module.exports[code] = CODES[code];
60 |
61 | if (CODES[code] === 0)
62 | return;
63 |
64 | var err = '', msg = '';
65 | var pieces = code.split('_').slice(1);
66 | for (var i in pieces) {
67 | var lc = pieces[i].toLowerCase();
68 | var key = lc.charAt(0).toUpperCase() + lc.slice(1);
69 | err += key;
70 | msg += key + ((i + 1) < pieces.length ? ' ' : '');
71 | }
72 |
73 | if (!/\w+Error$/.test(err))
74 | err += 'Error';
75 |
76 | module.exports[err] = function (message, caller) {
77 | DnsError.call(this,
78 | err,
79 | CODES[code],
80 | message || msg,
81 | caller || module.exports[err]);
82 | };
83 | module.exports[err].constructor = module.exports[err];
84 | util.inherits(module.exports[err], DnsError);
85 |
86 | ERRORS[CODES[code]] = {
87 | err: err,
88 | message: msg
89 | };
90 | });
91 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 |
24 | var fs = require('fs');
25 | var path = require('path');
26 | var assert = require('assert-plus');
27 |
28 | var bunyan = require('bunyan');
29 |
30 | var Server = require('./server');
31 | var Query = require('./query');
32 | var Protocol = require('./protocol');
33 | var tsig = require('./tsig');
34 |
35 |
36 |
37 | ////--- Globals
38 |
39 | var BUNYAN_SERIALIZERS = {
40 | err: bunyan.stdSerializers.err,
41 | query: function serializeQuery(q) {
42 | var out = {
43 | domain: q.name(),
44 | operation: q.operation(),
45 | type: q.type()
46 | };
47 | return (out);
48 | }
49 | };
50 |
51 |
52 |
53 | ///--- Exports
54 | module.exports = {
55 |
56 | createServer: function createServer(options) {
57 | options = options || {};
58 | assert.object(options);
59 |
60 | var opts = {
61 | name: options.name || 'named',
62 | log: options.log || bunyan.createLogger({
63 | name: 'named',
64 | level: 'warn',
65 | serializers: BUNYAN_SERIALIZERS
66 | })
67 | };
68 | return (new Server(opts));
69 | },
70 |
71 | Query: Query,
72 |
73 | Protocol: Protocol,
74 |
75 | tsig: tsig,
76 |
77 | bunyan: { serializers: BUNYAN_SERIALIZERS }
78 |
79 | };
80 |
81 | /* Export all the record types at the top-level */
82 | var subdir = path.join(__dirname, 'records');
83 | fs.readdirSync(subdir).forEach(function (f) {
84 | var name = path.basename(f);
85 | if (/\w+\.js/.test(name)) {
86 | var k = name.split('.').shift().toUpperCase() + 'Record';
87 | module.exports[k] = require(path.join(subdir, f));
88 | }
89 | });
90 |
--------------------------------------------------------------------------------
/lib/query.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var protocol = require('./protocol');
24 | var ipaddr = require('ipaddr.js');
25 | var queryTypes = protocol.queryTypes;
26 | var nsRecord = require('./records/ns');
27 | var soaRecord = require('./records/soa');
28 | var assert = require('assert-plus');
29 | var mod_tsig = require('./tsig');
30 |
31 | function Query(opts) {
32 | assert.object(opts, 'options');
33 | assert.buffer(opts.data, 'options.data');
34 | assert.string(opts.family, 'options.family');
35 |
36 | var q = protocol.decode(opts.data, 'message');
37 | assert.object(q);
38 |
39 | delete (opts.data);
40 | this.src = opts;
41 | this.family = opts.family;
42 |
43 | this.id = q.header.id;
44 | this.query = q;
45 | this.envelopeCount = 1;
46 | this.tsigKey = undefined;
47 | this.lastResponse = undefined;
48 |
49 | this.reset();
50 | this.response.header.qdCount = q.header.qdCount;
51 | assert.arrayOfObject(q.question);
52 | assert.strictEqual(q.question.length, 1);
53 | this.response.question = q.question;
54 | }
55 |
56 | Query.prototype.reset = function () {
57 | var q = this.query;
58 | this.lastResponse = this.response;
59 | var r = this.response = {};
60 | ++this.envelopeCount;
61 |
62 | r.header = {
63 | id: q.header.id,
64 | qdCount: 0,
65 | anCount: 0,
66 | nsCount: 0,
67 | arCount: 0
68 | };
69 | r.question = [];
70 | r.answer = [];
71 | r.authority = [];
72 | r.additional = [];
73 |
74 | /* Inherit the query's flags until we override them */
75 | r.header.flags = Object.create(q.header.flags);
76 |
77 | /* Respond with NOTIMP unless we set otherwise */
78 | r.header.flags.rcode = protocol.rCodes.NOTIMP;
79 | r.header.flags.qr = true;
80 |
81 | parseEdns.call(this);
82 | };
83 |
84 | function parseEdns() {
85 | var q = this.query;
86 | var r = this.response;
87 |
88 | if (this.family === 'udp')
89 | this.maxReplySize = 512;
90 | else
91 | this.maxReplySize = 65530;
92 |
93 | if (typeof (q.additional) === 'object') {
94 | var add = q.additional;
95 | if (!Array.isArray(add))
96 | add = [add];
97 |
98 | var edns = add.filter(function (a) {
99 | return (a.rtype === queryTypes['OPT']);
100 | }).pop();
101 |
102 | if (edns) {
103 | var maxReplySize = this.maxReplySize;
104 | maxReplySize = edns.rclass;
105 | if (maxReplySize > 1470)
106 | maxReplySize = 1470;
107 | r.additional.push({
108 | name: '.',
109 | rtype: queryTypes['OPT'],
110 | rclass: maxReplySize,
111 | rttl: 0,
112 | rdata: { options: [] }
113 | });
114 | r.header.arCount++;
115 | if (this.family === 'udp')
116 | this.maxReplySize = maxReplySize;
117 | }
118 | }
119 | }
120 |
121 | Query.prototype.answers = function answers() {
122 | return this.response.answer.map(function (r) {
123 | return {
124 | name: r.name,
125 | type: queryTypes[r.rtype],
126 | record: r.rdata,
127 | ttl: r.rttl
128 | };
129 | });
130 | };
131 |
132 | Query.prototype.name = function () {
133 | return (this.query.question[0].name);
134 | };
135 |
136 | Query.prototype.type = function type() {
137 | return (queryTypes[this.query.question[0].type]);
138 | };
139 |
140 | var FLAGS = {
141 | qr: true, aa: true, rd: true, ra: true, ad: true, cd: true,
142 | response: 'qr', authoritative: 'aa', recursionDesired: 'rd',
143 | recursionAvailable: 'ra', authenticated: 'ad', noChecking: 'cd',
144 | checkingDisabled: 'cd'
145 | };
146 |
147 | Query.prototype.testFlag = function testFlag(flag) {
148 | if (typeof (FLAGS[flag]) === 'string')
149 | flag = FLAGS[flag];
150 | if (FLAGS[flag] !== true)
151 | throw (new Error('Invalid DNS header flag "' + flag + '"'));
152 | return (this.query.header.flags[flag] === true);
153 | };
154 |
155 | Query.prototype.setFlag = function setFlag(flag) {
156 | if (typeof (FLAGS[flag]) === 'string')
157 | flag = FLAGS[flag];
158 | if (FLAGS[flag] !== true)
159 | throw (new Error('Invalid DNS header flag "' + flag + '"'));
160 | this.response.header.flags[flag] = true;
161 | };
162 |
163 | Query.prototype.clearFlag = function clearFlag(flag) {
164 | if (typeof (FLAGS[flag]) === 'string')
165 | flag = FLAGS[flag];
166 | if (FLAGS[flag] !== true)
167 | throw (new Error('Invalid DNS header flag "' + flag + '"'));
168 | this.response.header.flags[flag] = false;
169 | };
170 |
171 | Query.prototype.ixfrBase = function ixfrBase() {
172 | var q = this.query;
173 | assert.strictEqual(q.question[0].type, queryTypes['IXFR']);
174 | assert.arrayOfObject(q.authority);
175 | assert.strictEqual(q.authority[0].rtype, queryTypes['SOA']);
176 | return (q.authority[0].rdata.serial);
177 | };
178 |
179 | Query.prototype.isSigned = function isSigned() {
180 | var tsig = this.query.additional[this.query.additional.length - 1];
181 | return (tsig && tsig.rtype === queryTypes.TSIG);
182 | };
183 |
184 | Query.prototype.verify = function verify(keys) {
185 | var tsig = this.query.additional[this.query.additional.length - 1];
186 | if (tsig.rtype !== queryTypes.TSIG)
187 | return (false);
188 | try {
189 | var result = mod_tsig.verifyRequest(this.query, keys);
190 | } catch (err) {
191 | var log = this._log || this.log;
192 | if (log) {
193 | log.warn({err: err}, 'error processing TSIG');
194 | }
195 | return (false);
196 | }
197 | if (result)
198 | this.tsigKey = keys[tsig.name];
199 | return (result);
200 | };
201 |
202 | Query.prototype.setError = function setError(name) {
203 | var code = protocol.rCodes[name.toUpperCase()];
204 | if (code === undefined) {
205 | /* Try stripping off a leading E. */
206 | code = protocol.rCodes[name.toUpperCase().slice(1)];
207 | if (code === undefined)
208 | throw new Error('invalid error code %s', name);
209 | }
210 | this.response.header.flags.rcode = code;
211 | };
212 |
213 | Query.prototype.error = function () {
214 | var code = protocol.rCodes[this.response.header.flags.rcode];
215 | return code.toLowerCase();
216 | };
217 |
218 | Query.prototype.operation = function operation() {
219 | var h = this.query.header;
220 | var op = protocol.opCodes[h.flags.opcode];
221 | if (op === undefined)
222 | throw new Error('invalid operation %d', h.flags.opcode);
223 | if (op === 'IQUERY')
224 | throw new Error('iquery operations are obselete');
225 | return (op.toLowerCase());
226 | };
227 |
228 | Query.prototype.encode = function encode(recur) {
229 | if (this.tsigKey !== undefined) {
230 | if (this.lastResponse === undefined) {
231 | mod_tsig.signResponse(this.response, this.tsigKey,
232 | this.query);
233 | } else {
234 | mod_tsig.signTcpContinuation(this.response,
235 | this.tsigKey, this.lastResponse);
236 | }
237 | }
238 |
239 | var encoded = protocol.encode(this.response, 'message');
240 |
241 | if (encoded.length > this.maxReplySize) {
242 | if (recur || this.family !== 'udp')
243 | throw (new Error('Truncated answer message ' +
244 | 'too large to send: ' + encoded.length + ' bytes'));
245 |
246 | var log = this._log || this.log;
247 | if (log) {
248 | log.info({
249 | len: encoded.length,
250 | max_len: this.maxReplySize
251 | }, 'truncated response sent');
252 | }
253 |
254 | var r = this.response;
255 | r.header.flags.tc = true;
256 | r.header.anCount = 0;
257 | r.header.nsCount = 0;
258 | r.header.arCount = 0;
259 | r.answer = [];
260 | r.authority = [];
261 | r.additional = [];
262 | return (encode.call(this, true));
263 | }
264 |
265 | return (encoded);
266 | };
267 | Query.prototype.addAuthority = function (name, record, ttl) {
268 | assert.string(name, 'name');
269 | assert.object(record, 'record');
270 | assert.optionalNumber(ttl, 'ttl');
271 |
272 | var authority = {
273 | name: name,
274 | rclass: 1, // INET
275 | rttl: ttl || 5,
276 | rdata: record
277 | };
278 | if (record instanceof nsRecord)
279 | authority.rtype = queryTypes['NS'];
280 | else if (record instanceof soaRecord)
281 | authority.rtype = queryTypes['SOA'];
282 | else
283 | throw (new TypeError('invalid type for authority section ' +
284 | 'record'));
285 |
286 | var r = this.response;
287 | r.authority.push(authority);
288 | r.header.nsCount++;
289 | /* This is not great, but is necessary to be backwards compatible. */
290 | r.header.flags.aa = true;
291 | };
292 | Query.prototype.addAdditional = function (name, record, ttl) {
293 | assert.string(name, 'name');
294 | assert.object(record, 'record');
295 | assert.optionalNumber(ttl, 'ttl');
296 |
297 | var add = {
298 | name: name,
299 | rtype: queryTypes[record._type],
300 | rclass: 1, // INET
301 | rttl: ttl || 5,
302 | rdata: record
303 | };
304 |
305 | var r = this.response;
306 | r.additional.push(add);
307 | r.header.arCount++;
308 | };
309 |
310 | Query.prototype.addAnswer = function (name, record, ttl) {
311 | assert.string(name, 'name');
312 | assert.object(record, 'record');
313 | assert.optionalNumber(ttl, 'ttl');
314 |
315 | if (!queryTypes.hasOwnProperty(record._type))
316 | throw (new TypeError('unknown queryType: ' + record._type));
317 |
318 | var r = this.response;
319 | // return ok, we have an answer
320 | r.header.flags.rcode = protocol.rCodes.NOERROR;
321 | var answer = {
322 | name: name,
323 | rtype: queryTypes[record._type],
324 | rclass: 1, // INET
325 | rttl: ttl || 5,
326 | rdata: record
327 | };
328 |
329 | r.answer.push(answer);
330 | r.header.anCount++;
331 | };
332 |
333 | function parseQuery(opts) {
334 | return (new Query(opts));
335 | }
336 |
337 | module.exports = {
338 | parse: parseQuery
339 | };
340 |
--------------------------------------------------------------------------------
/lib/records/a.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var validators = require('../validators');
24 | var assert = require('assert-plus');
25 |
26 | function A(target) {
27 | assert.string(target, 'target IPv4Addr');
28 |
29 | this.target = target;
30 | this._type = 'A';
31 | }
32 | module.exports = A;
33 |
34 |
35 | A.prototype.valid = function valid() {
36 | var self = this, model = {};
37 | model = {
38 | target: validators.IPv4
39 | };
40 | return validators.validate(self, model);
41 | };
42 |
--------------------------------------------------------------------------------
/lib/records/aaaa.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var validators = require('../validators');
24 | var assert = require('assert-plus');
25 |
26 | function AAAA(target) {
27 | assert.string(target, 'target IPv6Addr');
28 |
29 | this.target = target;
30 | this._type = 'AAAA';
31 | }
32 | module.exports = AAAA;
33 |
34 | AAAA.prototype.valid = function valid() {
35 | var self = this, model = {};
36 | model = {
37 | target: validators.IPv6
38 | };
39 | return validators.validate(self, model);
40 | };
41 |
--------------------------------------------------------------------------------
/lib/records/cname.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var validators = require('../validators');
24 | var assert = require('assert-plus');
25 |
26 | function CNAME(target) {
27 | assert.string(target, 'target');
28 |
29 | this.target = target;
30 | this._type = 'CNAME';
31 | }
32 | module.exports = CNAME;
33 |
34 |
35 | CNAME.prototype.valid = function valid() {
36 | var self = this, model = {};
37 | model = {
38 | target: validators.nsName
39 | };
40 | return validators.validate(self, model);
41 | };
42 |
--------------------------------------------------------------------------------
/lib/records/mx.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var validators = require('../validators');
24 | var assert = require('assert-plus');
25 |
26 | function MX(exchange, opts) {
27 | assert.string(exchange, 'exchange');
28 |
29 | if (!opts)
30 | opts = {};
31 |
32 | var defaults = {
33 | priority: 0,
34 | ttl: 600
35 | };
36 |
37 | Object.keys(defaults).forEach(function (key) {
38 | if (opts[key] !== undefined)
39 | return;
40 | opts[key] = defaults[key];
41 | });
42 |
43 | this.exchange = exchange;
44 | this.ttl = opts.ttl;
45 | this.priority = opts.priority;
46 | this._type = 'MX';
47 | }
48 | module.exports = MX;
49 |
50 |
51 | MX.prototype.valid = function valid() {
52 | var self = this, model = {};
53 | model = {
54 | exchange: validators.nsName,
55 | ttl: validators.UInt32BE,
56 | priority: validators.UInt16BE
57 | };
58 | return validators.validate(self, model);
59 | };
60 |
--------------------------------------------------------------------------------
/lib/records/ns.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var validators = require('../validators');
24 | var assert = require('assert-plus');
25 |
26 | function NS(target) {
27 | assert.string(target, 'target');
28 |
29 | this.target = target;
30 | this._type = 'NS';
31 | }
32 | module.exports = NS;
33 |
34 |
35 | NS.prototype.valid = function valid() {
36 | var self = this, model = {};
37 | model = {
38 | target: validators.nsName
39 | };
40 | return validators.validate(self, model);
41 | };
42 |
--------------------------------------------------------------------------------
/lib/records/ptr.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var validators = require('../validators');
24 | var assert = require('assert-plus');
25 |
26 | function PTR(target) {
27 | assert.string(target, 'target');
28 |
29 | this.target = target;
30 | this._type = 'PTR';
31 | }
32 | module.exports = PTR;
33 |
34 |
35 | PTR.prototype.valid = function valid() {
36 | var self = this, model = {};
37 | model = {
38 | target: validators.nsName
39 | };
40 | return validators.validate(self, model);
41 | };
42 |
--------------------------------------------------------------------------------
/lib/records/soa.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var validators = require('../validators');
24 | var assert = require('assert-plus');
25 |
26 | function SOA(host, opts) {
27 | assert.string(host, 'host');
28 | assert.optionalObject(opts, 'options');
29 |
30 | if (!opts)
31 | opts = {};
32 |
33 | var defaults = {
34 | admin: 'hostmaster.' + host,
35 | serial: 0,
36 | refresh: 86400,
37 | retry: 7200,
38 | expire: 1209600,
39 | ttl: 10800
40 | };
41 |
42 | Object.keys(defaults).forEach(function (key) {
43 | if (opts[key] !== undefined)
44 | return;
45 | opts[key] = defaults[key];
46 | });
47 |
48 | this.host = host;
49 | this.admin = opts.admin;
50 | this.serial = opts.serial;
51 | this.refresh = opts.refresh;
52 | this.retry = opts.retry;
53 | this.expire = opts.expire;
54 | this.ttl = opts.ttl;
55 | this._type = 'SOA';
56 | }
57 | module.exports = SOA;
58 |
59 |
60 | SOA.prototype.valid = function () {
61 | var self = this, model = {};
62 |
63 | model = {
64 | host: validators.nsName,
65 | admin: validators.nsName,
66 | serial: validators.UInt32BE,
67 | refresh: validators.UInt32BE,
68 | retry: validators.UInt32BE,
69 | expire: validators.UInt32BE,
70 | ttl: validators.UInt32BE
71 | };
72 |
73 | return validators.validate(self, model);
74 | };
75 |
--------------------------------------------------------------------------------
/lib/records/srv.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var validators = require('../validators');
24 | var assert = require('assert-plus');
25 |
26 | function SRV(target, port, opts) {
27 | assert.string(target, 'target host');
28 | assert.number(port, 'port');
29 | assert.optionalObject(opts, 'options');
30 |
31 | if (!opts)
32 | opts = {};
33 |
34 | var defaults = {
35 | priority: 0,
36 | weight: 10
37 | };
38 |
39 | Object.keys(defaults).forEach(function (key) {
40 | if (opts[key] !== undefined)
41 | return;
42 | opts[key] = defaults[key];
43 | });
44 |
45 | this.target = target;
46 | this.port = port;
47 | this.weight = opts.weight;
48 | this.priority = opts.priority;
49 | this._type = 'SRV';
50 | }
51 | module.exports = SRV;
52 |
53 |
54 | SRV.prototype.valid = function () {
55 | var self = this, model = {};
56 | model = {
57 | target: validators.nsText, // XXX
58 | port: validators.UInt16BE,
59 | weight: validators.UInt16BE,
60 | priority: validators.UInt16BE
61 | };
62 | return validators.validate(self, model);
63 | };
64 |
--------------------------------------------------------------------------------
/lib/records/txt.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var validators = require('../validators');
24 | var assert = require('assert-plus');
25 |
26 | function TXT(target) {
27 | assert.string(target, 'target');
28 |
29 | this.target = target;
30 | this._type = 'TXT';
31 | }
32 | module.exports = TXT;
33 |
34 |
35 | TXT.prototype.valid = function () {
36 | var self = this, model = {};
37 | model = {
38 | target: validators.nsText
39 | };
40 | return validators.validate(self, model);
41 | };
42 |
--------------------------------------------------------------------------------
/lib/tsig.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Joyent, Inc
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | * SOFTWARE.
21 | */
22 |
23 | /*
24 | * Transaction Signatures for DNS (see RFC2845)
25 | */
26 |
27 | module.exports = {
28 | signRequest: signRequest,
29 | signResponse: signResponse,
30 | signTcpContinuation: signTcpContinuation,
31 | verifyRequest: verifyRequest,
32 | verifyResponse: verifyResponse,
33 | verifyTcpContinuation: verifyTcpContinuation
34 | };
35 |
36 | var protocol = require('./protocol');
37 | var assert = require('assert-plus');
38 | var crypto = require('crypto');
39 |
40 | var ALGOS = {
41 | 'hmac-md5': 'md5',
42 | 'hmac-md5.sig-alg.reg.int': 'md5',
43 | 'hmac-sha1': 'sha1',
44 | 'hmac-sha256': 'sha256',
45 | 'hmac-sha384': 'sha384',
46 | 'hmac-sha512': 'sha512'
47 | };
48 | var ALGOREV = {};
49 | Object.keys(ALGOS).forEach(function (k) {
50 | ALGOREV[ALGOS[k]] = k;
51 | });
52 |
53 | function assertKey(key) {
54 | assert.string(key.name, 'key.name');
55 | assert.string(key.algorithm, 'key.algorithm');
56 | assert.buffer(key.data, 'key.data');
57 | assert.string(ALGOS[key.algorithm], 'supported algorithm');
58 | }
59 |
60 | function verifyRequest(msg, keys) {
61 | assert.object(msg, 'message');
62 | assert.object(msg.header, 'message.header');
63 | assert.object(keys, 'keys');
64 | assert.ok(Object.keys(keys).length > 0, 'non-empty keys object');
65 | return (verify(msg, keys, 'tsigSignDataReq'));
66 | }
67 |
68 | function verifyResponse(msg, keys, reqMsg) {
69 | assert.object(msg, 'message');
70 | assert.object(msg.header, 'message.header');
71 | assert.object(keys, 'keys');
72 | assert.object(reqMsg, 'signedRequestMessage');
73 | assert.object(reqMsg.header, 'signedRequestMessage.header');
74 | assert.arrayOfObject(reqMsg.additional,
75 | 'signedRequestMessage.additional');
76 | var tsigs = reqMsg.additional.filter(function (rr) {
77 | return (rr.rtype === protocol.queryTypes.TSIG);
78 | });
79 | assert.ok(tsigs.length === 1, 'signedRequestMessage TSIG signature');
80 | assert.ok(Object.keys(keys).length > 0, 'non-empty keys object');
81 | return (verify(msg, keys, 'tsigSignDataResp', tsigs[0].rdata.mac));
82 | }
83 |
84 | function verifyTcpContinuation(msg, keys, lastMsg) {
85 | assert.object(msg, 'message');
86 | assert.object(msg.header, 'message.header');
87 | assert.object(keys, 'keys');
88 | assert.object(lastMsg, 'signedLastMessage');
89 | assert.object(lastMsg.header, 'signedLastMessage.header');
90 | assert.arrayOfObject(lastMsg.additional,
91 | 'signedLastMessage.additional');
92 | var tsigs = lastMsg.additional.filter(function (rr) {
93 | return (rr.rtype === protocol.queryTypes.TSIG);
94 | });
95 | assert.ok(tsigs.length === 1, 'signedLastMessage TSIG signature');
96 | assert.ok(Object.keys(keys).length > 0, 'non-empty keys object');
97 | return (verify(msg, keys, 'tsigSignTcp', tsigs[0].rdata.mac));
98 | }
99 |
100 | function signRequest(msg, key) {
101 | assert.object(msg, 'message');
102 | assert.object(msg.header, 'message.header');
103 | assertKey(key);
104 | return (sign(msg, key, 'tsigSignDataReq'));
105 | }
106 |
107 | function signResponse(msg, key, reqMsg) {
108 | assert.object(msg, 'message');
109 | assert.object(msg.header, 'message.header');
110 | assertKey(key);
111 | assert.object(reqMsg.header, 'signedRequestMessage.header');
112 | assert.arrayOfObject(reqMsg.additional,
113 | 'signedRequestMessage.additional');
114 | var tsigs = reqMsg.additional.filter(function (rr) {
115 | return (rr.rtype === protocol.queryTypes.TSIG);
116 | });
117 | assert.ok(tsigs.length === 1, 'signedRequestMessage TSIG signature');
118 | return (sign(msg, key, 'tsigSignDataResp', tsigs[0].rdata.mac));
119 | }
120 |
121 | function signTcpContinuation(msg, key, lastMsg) {
122 | assert.object(msg, 'message');
123 | assert.object(msg.header, 'message.header');
124 | assertKey(key);
125 | assert.object(lastMsg.header, 'signedLastMessage.header');
126 | assert.arrayOfObject(lastMsg.additional,
127 | 'signedLastMessage.additional');
128 | var tsigs = lastMsg.additional.filter(function (rr) {
129 | return (rr.rtype === protocol.queryTypes.TSIG);
130 | });
131 | assert.ok(tsigs.length === 1, 'signedLastMessage TSIG signature');
132 | return (sign(msg, key, 'tsigSignTcp', tsigs[0].rdata.mac));
133 | }
134 |
135 | function verify(msg, keys, format, reqMac) {
136 | var newMsg = Object.create(msg);
137 | newMsg.header = Object.create(msg.header);
138 | newMsg.additional = msg.additional.slice();
139 | var tsig = newMsg.additional.pop();
140 | assert.strictEqual(tsig.rtype, protocol.queryTypes.TSIG);
141 | newMsg.header.arCount--;
142 |
143 | var kname = tsig.name;
144 | var key = keys[kname];
145 | if (key === undefined)
146 | throw (new Error('Unknown TSIG key "' + kname + '"'));
147 | assertKey(key);
148 |
149 | var algo = ALGOS[tsig.rdata.algorithm];
150 | assert.strictEqual(algo, ALGOS[key.algorithm], 'matching algorithm');
151 |
152 | var tsign = {};
153 | tsign.message = protocol.encode(newMsg, 'message');
154 | tsign.rname = tsig.name;
155 | assert.strictEqual(tsig.rclass, protocol.qClasses.ANY);
156 | tsign.rclass = tsig.rclass;
157 | assert.strictEqual(tsig.rttl, 0);
158 | tsign.rttl = tsig.rttl;
159 |
160 | tsign.algorithm = tsig.rdata.algorithm;
161 | tsign.time = tsig.rdata.time;
162 | tsign.fudge = tsig.rdata.fudge;
163 | tsign.error = tsig.rdata.error;
164 | tsign.other = tsig.rdata.other;
165 | if (reqMac !== undefined)
166 | tsign.rmac = reqMac;
167 |
168 | var now = new Date();
169 | var delta = now.getTime() - tsign.time.getTime();
170 |
171 | var blob = protocol.encode(tsign, format);
172 | assert.buffer(blob);
173 |
174 | var hmac = crypto.createHmac(algo, key.data);
175 | hmac.update(blob);
176 | var digest = hmac.digest();
177 |
178 | var comp1 = crypto.createHmac(algo, key.data).
179 | update(tsig.rdata.mac).digest().toString('base64');
180 | var comp2 = crypto.createHmac(algo, key.data).
181 | update(digest).digest().toString('base64');
182 |
183 | return (comp1 === comp2 && delta > 0 && delta < tsign.fudge * 1000);
184 | }
185 |
186 | function sign(msg, key, format, reqMac) {
187 | var algo = ALGOS[key.algorithm];
188 |
189 | var tsign = {};
190 | if (reqMac !== undefined)
191 | tsign.rmac = reqMac;
192 |
193 | tsign.message = protocol.encode(msg, 'message');
194 | tsign.rname = key.name;
195 | tsign.rclass = protocol.qClasses.ANY;
196 | tsign.rttl = 0;
197 |
198 | tsign.algorithm = ALGOREV[algo];
199 | tsign.time = new Date();
200 | tsign.fudge = 300;
201 | tsign.error = 0;
202 | tsign.other = new Buffer(0);
203 |
204 | var blob = protocol.encode(tsign, format);
205 | assert.buffer(blob);
206 |
207 | var hmac = crypto.createHmac(algo, key.data);
208 | hmac.update(blob);
209 | var digest = hmac.digest();
210 |
211 | var tsig = {};
212 | tsig.name = tsign.rname;
213 | tsig.rtype = protocol.queryTypes.TSIG;
214 | tsig.rclass = tsign.rclass;
215 | tsig.rttl = tsign.rttl;
216 | tsig.rdata = {};
217 | tsig.rdata.algorithm = tsign.algorithm;
218 | tsig.rdata.time = tsign.time;
219 | tsig.rdata.fudge = tsign.fudge;
220 | tsig.rdata.origid = msg.header.id;
221 | tsig.rdata.error = tsign.error;
222 | tsig.rdata.other = tsign.other;
223 |
224 | tsig.rdata.mac = digest;
225 |
226 | msg.header.arCount++;
227 | msg.additional.push(tsig);
228 | }
229 |
--------------------------------------------------------------------------------
/lib/validators.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var ipaddr = require('ipaddr.js');
24 | var net = require('net');
25 |
26 | module.exports = {
27 | nsName: function (v) {
28 | // hostname regex per RFC1123
29 | /*JSSTYLED*/
30 | var reg = /^([a-z0-9]|[a-z0-9][a-z0-9\-]{0,61}[a-z0-9])(\.([a-z0-9]|[a-z0-9][a-z0-9\-]{0,61}[a-z0-9]))*$/i;
31 | if (typeof (v) !== 'string')
32 | return false;
33 | if (v.length > 255)
34 | return false;
35 |
36 | if (reg.test(v)) {
37 | return true;
38 | } else {
39 | return false;
40 | }
41 | },
42 | UInt32BE: function (v) {
43 | if (typeof (v) === 'number') {
44 | var n = parseInt(v, 10);
45 | if (n !== NaN && n < 4294967295) {
46 | return true;
47 | } else {
48 | return false;
49 | }
50 | } else {
51 | return false;
52 | }
53 | },
54 | UInt16BE: function (v) {
55 | if (typeof (v) === 'number') {
56 | var n = parseInt(v, 10);
57 | if (n !== NaN && n < 65535) {
58 | return true;
59 | } else {
60 | return false;
61 | }
62 | } else {
63 | return false;
64 | }
65 | },
66 | nsText: function (v) {
67 | if (typeof (v) === 'string') {
68 | if (v.length < 256)
69 | return true;
70 | }
71 | return false;
72 | },
73 | IPv4: function (v) {
74 | return net.isIPv4(v);
75 | },
76 | IPv6: function (v) {
77 | return net.isIPv6(v);
78 | },
79 | validate: function (obj, model) {
80 | var result = true;
81 | for (var v in model) {
82 | if (!model[v](obj[v])) {
83 | result = false;
84 | break;
85 | }
86 | }
87 | return result;
88 | }
89 | };
90 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mname",
3 | "description": "DNS server library for node.js",
4 | "version": "1.5.1",
5 | "author": "arekinath ",
6 | "contributors": [
7 | "Mark Cavage",
8 | "Alex Wilson",
9 | "Lorenz Brun",
10 | "Trevor Orsztynowicz",
11 | "Joshua M. Clulow",
12 | "Robert Bogart"
13 | ],
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/arekinath/node-mname.git"
17 | },
18 | "main": "lib/index.js",
19 | "engines": {
20 | "node": ">=0.10"
21 | },
22 | "dependencies": {
23 | "assert-plus": "^1.0.0",
24 | "bunyan": "^1.5.1",
25 | "ipaddr.js": "^1.0.0",
26 | "vasync": "^2.2.0"
27 | },
28 | "devDependencies": {
29 | "nodeunit": "0.9.1"
30 | },
31 | "license": "MIT"
32 | }
33 |
--------------------------------------------------------------------------------
/test/dig.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 |
24 | // Quick and dirty 'dig' wrapper
25 |
26 | var assert = require('assert');
27 | var spawn = require('child_process').spawn;
28 | var sprintf = require('util').format;
29 | var path = require('path');
30 |
31 |
32 | ///--- Globals
33 |
34 | var DIG = 'dig';
35 |
36 |
37 |
38 | ///--- Helpers
39 |
40 | function parseAnswer(tokens) {
41 | var t = tokens.filter(function (v) {
42 | return (v !== '' ? v : undefined);
43 | });
44 |
45 | var r = {
46 | name: t[0],
47 | ttl: parseInt(t[1], 10),
48 | type: t[3],
49 | target: t[4]
50 | };
51 |
52 | return (r);
53 | }
54 |
55 |
56 | function parseDig(output) {
57 | var lines = output.split(/\n/);
58 | var section = 'header';
59 |
60 | var results = {
61 | id: null,
62 | status: null,
63 | question: null,
64 | tsigFail: false,
65 | answers: [],
66 | additional: [],
67 | authority: []
68 | };
69 |
70 | lines.forEach(function (l) {
71 | if (l === '') {
72 | section = undefined;
73 | } else if (/^;; QUESTION SECTION:/.test(l)) {
74 | section = 'question';
75 | } else if (/^;; ANSWER SECTION:/.test(l)) {
76 | section = 'answer';
77 | } else if (/^;; ADDITIONAL SECTION:/.test(l)) {
78 | section = 'additional';
79 | } else if (/^;; AUTHORITY SECTION:/.test(l)) {
80 | section = 'authority';
81 | } else if (/^; <<>> DiG.* axfr /i.test(l)) {
82 | section = 'answer';
83 | }
84 |
85 | if (section === 'question') {
86 | if (/^;([A-Za-z0-9])*\./.test(l)) {
87 | results.question =
88 | l.match(/([A-Za-z0-9_\-\.])+/)[0];
89 | }
90 | }
91 |
92 | if (section === 'answer') {
93 | if (/^([_A-Za-z0-9])+/.test(l)) {
94 | var tokens = l.match(/(.*)/)[0].split(/\t/);
95 | var answer = parseAnswer(tokens);
96 | if (answer)
97 | results.answers.push(answer);
98 | }
99 | }
100 |
101 | if (/^;; ->>HEADER<<-/.test(l)) {
102 | var m = l.match(/status: ([A-Z]+)/)
103 | results.status = m[1].toLowerCase();
104 | m = l.match(/id: ([0-9]+)/);
105 | results.id = parseInt(m[1], 10);
106 | }
107 |
108 | if (/Some TSIG could not be validated/.test(l) ||
109 | /tsig verify failure/.test(l)) {
110 | results.tsigFail = true;
111 | }
112 |
113 | if (/^; Transfer failed/.test(l)) {
114 | results.status = 'failed';
115 | }
116 | });
117 |
118 | return (results);
119 | }
120 |
121 |
122 |
123 | ///--- API
124 |
125 | function dig(name, type, options, callback) {
126 | if (typeof (name) !== 'string')
127 | throw new TypeError('name (string) is required');
128 | if (typeof (type) !== 'string')
129 | throw new TypeError('type (string) is required');
130 | if (typeof (options) === 'function') {
131 | callback = options;
132 | options = {};
133 | }
134 |
135 | type = type.toUpperCase();
136 |
137 | var opts = [];
138 | if (options.server) {
139 | opts.push('@' + options.server);
140 | }
141 | if (options.port) {
142 | opts.push('-p');
143 | opts.push(options.port);
144 | }
145 | if (options.key) {
146 | opts.push('-y');
147 | var key = options.key;
148 | opts.push(key.algorithm + ':' + key.name + ':' +
149 | key.data.toString('base64'));
150 | }
151 | opts = opts.concat(['-t', type, name, '+time=1', '+retry=0']);
152 |
153 | var kid = spawn('dig', opts, {
154 | stdio: ['pipe', 'pipe', 'inherit'],
155 | });
156 | kid.stdin.end();
157 | var stdout = [];
158 | kid.stdout.on('readable', function () {
159 | var b;
160 | while ((b = kid.stdout.read()) !== null) {
161 | stdout.push(b);
162 | }
163 | });
164 | kid.on('exit', function (exitStatus) {
165 | if (exitStatus !== 0) {
166 | return (callback(
167 | new Error('dig exited with status ' + exitStatus)));
168 | }
169 | return (callback(null, parseDig(
170 | Buffer.concat(stdout).toString('ascii'))));
171 | });
172 | }
173 |
174 |
175 |
176 | ///--- Exports
177 |
178 | module.exports = dig;
179 |
--------------------------------------------------------------------------------
/test/dnsbuffer.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | /*
24 | * using the 'dig' utility, as its the only tool that supports alternative ports
25 | * when doing a query.
26 | *
27 | * When this module is loaded it will return an object containing an array of
28 | * samples that you can use to test serializers, protocol generators, create a
29 | * raw DNS client, etc.
30 | *
31 | * Each sample is an object with an 'id', 'description', 'raw', and 'data'.
32 | * The ID is used so that adding and removing samples out of order will not
33 | * affect external references to them in tests.
34 | *
35 | * The data is put through an encoder that will turn this string into a raw
36 | * buffer. That way, samples may be loaded from file that can be read by a
37 | * (mortal) human being.
38 | *
39 | * When the sample is encoded it places a "raw" value in the object. If you
40 | * have one there it will be over-written.
41 | */
42 |
43 | var samples = [
44 | {
45 | id: 0,
46 | description: 'query ns1.joyent.dev (A)',
47 | data: "0f 34 81 00 00 01 00 00 00 00 00 00 03 6e 73 31 06 6a 6f 79 65 " +
48 | "6e 74 03 64 65 76 00 00 01 00 01",
49 | type: 'message',
50 | decoded: {
51 | header: {
52 | id: 3892,
53 | flags: {
54 | qr: true, opcode: 0, aa: false,
55 | tc: false, rd: true, ra: false,
56 | z: false, ad: false, cd: false,
57 | rcode: 0
58 | },
59 | qdCount: 1,
60 | anCount: 0,
61 | nsCount: 0,
62 | arCount: 0
63 | },
64 | question: [
65 | {
66 | name: 'ns1.joyent.dev',
67 | type: 1, qclass: 1
68 | }
69 | ],
70 | answer: [],
71 | authority: [],
72 | additional: []
73 | }
74 | },
75 | {
76 | id: 1,
77 | description: 'query ns1.joyent.dev (AAAA)',
78 | data: "b9 dd 01 00 00 01 00 00 00 00 00 00 03 6e 73 31 06 6a 6f 79 65 " +
79 | "6e 74 03 64 65 76 00 00 1c 00 01",
80 | type: 'message'
81 | },
82 | {
83 | id: 2,
84 | description: 'ptr dns-sd',
85 | data: 'c4 2f 01 00 00 01 00 00 00 00 00 00 02 6c 62 07 ' +
86 | '5f 64 6e 73 2d 73 64 04 5f 75 64 70 01 30 01 34 ' +
87 | '02 32 30 03 31 37 32 07 69 6e 2d 61 64 64 72 04 ' +
88 | '61 72 70 61 00 00 0c 00 01',
89 | type: 'message'
90 | },
91 | {
92 | id: 3,
93 | description: 'txt response',
94 | data: '4c 91 85 00 00 01 00 02 00 03 00 06 07 63 6f 6f ' +
95 | '70 65 72 69 03 6e 65 74 00 00 10 00 01 c0 0c 00 ' +
96 | '10 00 01 00 00 1c 20 00 3a 39 76 3d 73 70 66 31 ' +
97 | '20 6d 78 20 6d 78 3a 73 6f 72 75 73 2e 63 6f 6f ' +
98 | '70 65 72 69 2e 6e 65 74 20 6d 78 3a 6c 61 6d 69 ' +
99 | '6e 61 2e 63 6f 6f 70 65 72 69 2e 6e 65 74 20 2d ' +
100 | '61 6c 6c c0 0c 00 10 00 01 00 00 1c 20 00 6d 6c ' +
101 | '74 69 6e 66 6f 69 6c 2d 73 69 74 65 2d 76 65 72 ' +
102 | '69 66 69 63 61 74 69 6f 6e 3a 20 32 64 37 63 32 ' +
103 | '61 32 38 34 35 36 64 31 66 66 36 66 32 64 65 39 ' +
104 | '39 61 66 36 32 34 32 39 30 37 65 35 61 61 31 66 ' +
105 | '66 31 63 3d 61 36 34 37 32 61 30 32 32 31 33 31 ' +
106 | '37 33 66 32 35 31 31 66 32 36 39 62 66 63 31 36 ' +
107 | '39 34 61 31 30 39 39 35 63 30 36 38 c0 0c 00 02 ' +
108 | '00 01 00 00 1c 20 00 09 06 6c 61 6d 69 6e 61 c0 ' +
109 | '0c c0 0c 00 02 00 01 00 00 1c 20 00 08 05 78 79 ' +
110 | '6c 65 6d c0 0c c0 0c 00 02 00 01 00 00 1c 20 00 ' +
111 | '08 05 73 74 69 70 65 c0 0c c1 11 00 01 00 01 00 ' +
112 | '00 1c 20 00 04 a8 eb 56 ec c1 11 00 1c 00 01 00 ' +
113 | '00 1c 20 00 10 26 04 01 80 00 03 07 c2 00 00 00 ' +
114 | '00 00 00 e5 6a c0 fd 00 01 00 01 00 00 1c 20 00 ' +
115 | '04 73 92 4c 41 c0 fd 00 1c 00 01 00 00 1c 20 00 ' +
116 | '10 24 02 74 00 50 08 00 01 00 00 00 00 00 00 00 ' +
117 | '04 c0 e8 00 01 00 01 00 00 1c 20 00 04 96 6b 48 ' +
118 | '5a c0 e8 00 1c 00 01 00 00 1c 20 00 10 24 04 94 ' +
119 | '00 00 02 00 00 02 16 3e ff fe f0 33 74',
120 | type: 'message'
121 | },
122 | {
123 | id: 4,
124 | description: 'mx response',
125 | data: 'dd 08 85 00 00 01 00 02 00 03 00 08 07 63 6f 6f ' +
126 | '70 65 72 69 03 6e 65 74 00 00 0f 00 01 c0 0c 00 ' +
127 | '0f 00 01 00 00 1c 20 00 0b 00 14 06 6c 61 6d 69 ' +
128 | '6e 61 c0 0c c0 0c 00 0f 00 01 00 00 1c 20 00 0a ' +
129 | '00 0a 05 73 6f 72 75 73 c0 0c c0 0c 00 02 00 01 ' +
130 | '00 00 1c 20 00 08 05 73 74 69 70 65 c0 0c c0 0c ' +
131 | '00 02 00 01 00 00 1c 20 00 08 05 78 79 6c 65 6d ' +
132 | 'c0 0c c0 0c 00 02 00 01 00 00 1c 20 00 02 c0 2b ' +
133 | 'c0 42 00 01 00 01 00 00 1c 20 00 04 c0 c6 5e 65 ' +
134 | 'c0 42 00 1c 00 01 00 00 1c 20 00 10 26 07 56 00 ' +
135 | '01 68 00 00 00 00 00 00 00 00 00 0a c0 2b 00 01 ' +
136 | '00 01 00 00 1c 20 00 04 96 6b 48 5a c0 2b 00 1c ' +
137 | '00 01 00 00 1c 20 00 10 24 04 94 00 00 02 00 00 ' +
138 | '02 16 3e ff fe f0 33 74 c0 56 00 01 00 01 00 00 ' +
139 | '1c 20 00 04 a8 eb 56 ec c0 56 00 1c 00 01 00 00 ' +
140 | '1c 20 00 10 26 04 01 80 00 03 07 c2 00 00 00 00 ' +
141 | '00 00 e5 6a c0 6a 00 01 00 01 00 00 1c 20 00 04 ' +
142 | '73 92 4c 41 c0 6a 00 1c 00 01 00 00 1c 20 00 10 ' +
143 | '24 02 74 00 50 08 00 01 00 00 00 00 00 00 00 04',
144 | type: 'message'
145 | },
146 | {
147 | id: 5,
148 | description: 'srv response with additional',
149 | data: 'cb b4 81 00 00 01 00 04 00 00 00 01 05 5f 6c 64 ' +
150 | '61 70 04 5f 74 63 70 04 75 66 64 73 04 63 6f 61 ' +
151 | '6c 06 6a 6f 79 65 6e 74 02 75 73 00 00 21 00 01 ' +
152 | 'c0 0c 00 21 00 01 00 00 00 05 00 2d 00 00 00 0a ' +
153 | '05 6e 24 32 36 65 38 31 32 34 31 2d 32 31 66 64 ' +
154 | '2d 34 39 66 62 2d 61 64 35 33 2d 38 33 61 31 38 ' +
155 | '35 66 32 61 39 62 38 c0 17 c0 0c 00 21 00 01 00 ' +
156 | '00 00 05 00 08 00 00 00 0a 05 6f c0 42 c0 0c 00 ' +
157 | '21 00 01 00 00 00 05 00 08 00 00 00 0a 05 70 c0 ' +
158 | '42 c0 0c 00 21 00 01 00 00 00 05 00 08 00 00 00 ' +
159 | '0a 05 71 c0 42 c0 42 00 01 00 01 00 00 00 05 00 ' +
160 | '04 0a 63 63 12',
161 | type: 'message'
162 | },
163 | {
164 | id: 6,
165 | description: 'response with additional count but no ' +
166 | 'additionals (a la binder bug)',
167 | data: '3b b9 81 00 00 01 00 01 00 00 00 01 04 75 66 64 ' +
168 | '73 04 63 6f 61 6c 06 6a 6f 79 65 6e 74 02 75 73 ' +
169 | '00 00 01 00 01 c0 0c 00 01 00 01 00 00 00 05 00 ' +
170 | '04 0a 63 63 12',
171 | encode: false,
172 | decoded: {
173 | header: {
174 | id: 15289,
175 | flags: {
176 | qr: true, opcode: 0, aa: false,
177 | tc: false, rd: true, ra: false,
178 | z: false, ad: false, cd: false,
179 | rcode: 0
180 | },
181 | qdCount: 1,
182 | anCount: 1,
183 | nsCount: 0,
184 | arCount: 1
185 | },
186 | question: [
187 | {
188 | name: 'ufds.coal.joyent.us',
189 | type: 1, qclass: 1
190 | }
191 | ],
192 | answer: [
193 | {
194 | name: 'ufds.coal.joyent.us',
195 | rtype: 1, rclass: 1, rttl: 5,
196 | rdata: { target: '10.99.99.18' }
197 | }
198 | ],
199 | authority: [],
200 | additional: []
201 | },
202 | type: 'message'
203 | }
204 | ];
205 |
206 | function encode(data) {
207 | var tokens, buffer, pos = 0;
208 |
209 | if (typeof(data) !== 'string')
210 | throw new TypeError('data (string) is required');
211 |
212 | tokens = data.split(/\s/);
213 | buffer = new Buffer(tokens.length);
214 |
215 | for (var i = 0; i < tokens.length; ++i) {
216 | var v = parseInt(tokens[i], 16);
217 | buffer.writeInt8(v, pos++, true);
218 | }
219 | return buffer;
220 | }
221 |
222 | function encodeSamples(samps) {
223 | var sample, results = [];
224 | for (var i = 0; i < samps.length; ++i) {
225 | sample = samps[i];
226 | sample.raw = encode(sample.data);
227 | results.push(sample);
228 | }
229 | return results;
230 | }
231 |
232 | function equalBuffers(b1, b2) {
233 | if (b1.length !== b2.length) {
234 | return false;
235 | }
236 |
237 | var l = b1.length;
238 | while (l--) {
239 | var one = b1.readUInt8(l);
240 | var two = b2.readUInt8(l);
241 | if (one !== two) {
242 | return false;
243 | }
244 | }
245 | return true;
246 | }
247 |
248 | module.exports = {
249 | samples: encodeSamples(samples),
250 | equalBuffers: equalBuffers
251 | };
252 |
--------------------------------------------------------------------------------
/test/helper.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Mark Cavage. All rights reserved.
3 | * Copyright (c) 2012 Trevor Orsztynowicz
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 |
24 | var bunyan = require('bunyan');
25 | var named = require('../lib');
26 |
27 |
28 | ///--- Exports
29 |
30 | module.exports = {
31 |
32 | after: function after(teardown) {
33 | module.parent.exports.tearDown = teardown;
34 | },
35 |
36 | before: function before(setup) {
37 | module.parent.exports.setUp = setup;
38 | },
39 |
40 | test: function test(name, tester) {
41 | module.parent.exports[name] = function _(t) {
42 | var _done = false;
43 | t.end = function end() {
44 | if (!_done) {
45 | _done = true;
46 | t.done();
47 | }
48 | };
49 | t.notOk = function notOk(ok, message) {
50 | return (t.ok(!ok, message));
51 | };
52 | return (tester(t));
53 | };
54 | },
55 |
56 |
57 | getLog: function (name, stream) {
58 | return (bunyan.createLogger({
59 | level: (process.env.LOG_LEVEL || 'info'),
60 | name: name || process.argv[1],
61 | serializers: named.bunyan.serializers,
62 | stream: stream || process.stdout,
63 | src: true
64 | }));
65 | }
66 |
67 | };
68 |
--------------------------------------------------------------------------------
/test/named.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var named = require('../lib');
24 |
25 | var dig = require('./dig');
26 |
27 | if (require.cache[__dirname + '/helper.js'])
28 | delete require.cache[__dirname + '/helper.js']
29 | var helper = require('./helper');
30 |
31 |
32 | ///--- Globals
33 |
34 | var test = helper.test;
35 | var before = helper.before;
36 | var after = helper.after;
37 |
38 | var options = {port: 9999, server: '::1'};
39 |
40 |
41 |
42 | ///--- Tests
43 |
44 | before(function (callback) {
45 | this.server = named.createServer({
46 | log: helper.getLog('server')
47 | });
48 |
49 | this.server.on('query', function (query, cb) {
50 | var domain = query.name()
51 | var type = query.type();
52 |
53 | switch (type) {
54 | case 'A':
55 | var record = new named.ARecord('127.0.0.1');
56 | query.addAnswer(domain, record);
57 | break;
58 | case 'AAAA':
59 | var record = new named.AAAARecord('::1');
60 | query.addAnswer(domain, record);
61 | break;
62 | case 'CNAME':
63 | var record = new named.CNAMERecord('cname.example.com');
64 | query.addAnswer(domain, record);
65 | break;
66 | case 'NS':
67 | var record = new named.NSRecord('ns.example.com');
68 | query.addAnswer(domain, record);
69 | break;
70 | case 'MX':
71 | var record = new named.MXRecord('smtp.example.com');
72 | query.addAnswer(domain, record);
73 | break;
74 | case 'SOA':
75 | var record = new named.SOARecord('example.com');
76 | query.addAnswer(domain, record);
77 | break;
78 | case 'SRV':
79 | var record = new named.SRVRecord('sip.example.com', 5060);
80 | query.addAnswer(domain, record);
81 | break;
82 | case 'TXT':
83 | var record = new named.TXTRecord('hello world');
84 | query.addAnswer(domain, record);
85 | break;
86 | }
87 | query.respond();
88 | cb();
89 | });
90 |
91 | this.server.listen(options.port, options.server, function() {
92 | process.nextTick(callback);
93 | });
94 | });
95 |
96 |
97 | after(function (callback) {
98 | this.server.close(callback);
99 | });
100 |
101 |
102 | test('listen and close (port only)', function (t) {
103 | // don't conflict with the server made in 'before'
104 | var server = named.createServer();
105 | server.listen(1153, function () {
106 | process.nextTick(function () {
107 | server.close(function () {
108 | t.end();
109 | })
110 | });
111 | });
112 | });
113 |
114 |
115 | test('listen and close (port and ::1)', function(t) {
116 | var server = named.createServer();
117 | server.listen(String(1153), '::1', function() {
118 | process.nextTick(function () {
119 | server.close(function () {
120 | t.end();
121 | })
122 | });
123 | });
124 | });
125 |
126 |
127 | test('answer query: example.com (A)', function (t) {
128 | dig('example.com', 'A', options, function (err, results) {
129 | t.ifError(err);
130 | t.deepEqual(results.answers, [{
131 | name: 'example.com.',
132 | ttl: 5, type: 'A',
133 | target: '127.0.0.1'
134 | }]);
135 | t.end();
136 | });
137 | });
138 |
139 |
140 | test('answer query: example.com (AAAA)', function (t) {
141 | dig('example.com', 'AAAA', options, function (err, results) {
142 | t.ifError(err);
143 | t.deepEqual(results.answers, [{
144 | name: 'example.com.',
145 | ttl: 5, type: 'AAAA',
146 | target: '::1'
147 | }]);
148 | t.end();
149 | });
150 | });
151 |
152 |
153 | test('answer query: example.com (CNAME)', function (t) {
154 | dig('www.example.com', 'CNAME', options, function (err, results) {
155 | t.ifError(err);
156 | t.deepEqual(results.answers, [{
157 | name: 'www.example.com.',
158 | ttl: 5,
159 | type: 'CNAME',
160 | target: 'cname.example.com.'
161 | }]);
162 | t.end();
163 | });
164 | });
165 |
166 | test('answer query: example.com (NS)', function (t) {
167 | dig('example.com', 'NS', options, function (err, results) {
168 | t.ifError(err);
169 | t.deepEqual(results.answers, [{
170 | name: 'example.com.',
171 | ttl: 5,
172 | type: 'NS',
173 | target: 'ns.example.com.'
174 | }]);
175 | t.end();
176 | });
177 | });
178 |
179 |
180 | test('answer query: example.com (MX)', function (t) {
181 | dig('example.com', 'MX', options, function (err, results) {
182 | t.ifError(err);
183 | t.deepEqual(results.answers, [{
184 | name: 'example.com.',
185 | ttl: 5,
186 | type: 'MX',
187 | target: '0 smtp.example.com.'
188 | }]);
189 | t.end();
190 | });
191 | });
192 |
193 |
194 | test('answer query: example.com (SOA)', function (t) {
195 | dig('example.com', 'SOA', options, function (err, results) {
196 | t.ifError(err);
197 | t.deepEqual(results.answers, [{
198 | name: 'example.com.',
199 | ttl: 5,
200 | type: 'SOA',
201 | target: 'example.com. hostmaster.example.com. 0 86400 7200 1209600 10800'
202 | }]);
203 | t.end();
204 | });
205 | });
206 |
207 |
208 | test('answer query: example.com (SRV)', function (t) {
209 | dig('_sip._tcp.example.com', 'SRV', options, function (err, results) {
210 | t.ifError(err);
211 | t.deepEqual(results.answers, [{
212 | name: '_sip._tcp.example.com.',
213 | ttl: 5,
214 | type: 'SRV',
215 | target: '0 10 5060 sip.example.com.'
216 | }]);
217 | t.end();
218 | });
219 | });
220 |
221 |
222 | test('answer query: example.com (TXT)', function (t) {
223 | dig('example.com', 'TXT', options, function (err, results) {
224 | t.ifError(err);
225 | t.deepEqual(results.answers, [{
226 | name: 'example.com.',
227 | ttl: 5,
228 | type: 'TXT',
229 | target: '"hello world"'
230 | }]);
231 | t.end();
232 | });
233 | });
234 |
--------------------------------------------------------------------------------
/test/notify.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Joyent, Inc
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | * SOFTWARE.
21 | */
22 | var named = require('../lib');
23 |
24 | var dig = require('./dig');
25 |
26 | if (require.cache[__dirname + '/helper.js'])
27 | delete require.cache[__dirname + '/helper.js']
28 | var helper = require('./helper');
29 |
30 | var test = helper.test;
31 | var before = helper.before;
32 | var after = helper.after;
33 |
34 | var server1;
35 | var server2;
36 |
37 | before(function (callback) {
38 | server1 = named.createServer({
39 | log: helper.getLog('server1')
40 | });
41 | server2 = named.createServer({
42 | log: helper.getLog('server2')
43 | });
44 |
45 | server1.on('query', function (query, cb) {
46 | var op = query.operation();
47 | if (op === 'notify') {
48 | query.setError('noerror');
49 | query.send();
50 | }
51 | cb();
52 | });
53 |
54 | server1.listenUdp({ address: '::1', port: 9999 }, function() {
55 | server2.listenUdp({ address: '::1', port: 9991 }, function () {
56 | process.nextTick(callback);
57 | });
58 | });
59 | });
60 |
61 | test('answer notify: foo.com', function (t) {
62 | var n = server2.createNotify({
63 | address: '::1',
64 | port: 9999,
65 | zone: 'foo.com'
66 | });
67 | n.once('response', function (q) {
68 | t.strictEqual(q.operation(), 'notify');
69 | t.strictEqual(q.error(), 'noerror');
70 | t.strictEqual(q.name(), 'foo.com');
71 | t.end();
72 | });
73 | n.once('error', function (err) {
74 | t.ifError(err);
75 | t.end();
76 | })
77 | n.send();
78 | });
79 |
80 | after(function (cb) {
81 | server1.close(function () {
82 | server2.close(cb);
83 | });
84 | });
85 |
--------------------------------------------------------------------------------
/test/pipeline.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Joyent, Inc
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | * SOFTWARE.
21 | */
22 | var named = require('../lib');
23 | var protocol = require('../lib/protocol');
24 |
25 | var dig = require('./dig');
26 |
27 | if (require.cache[__dirname + '/helper.js'])
28 | delete require.cache[__dirname + '/helper.js']
29 | var helper = require('./helper');
30 | var net = require('net');
31 |
32 | var test = helper.test;
33 | var before = helper.before;
34 | var after = helper.after;
35 |
36 | var server1;
37 |
38 | before(function (callback) {
39 | server1 = named.createServer({
40 | log: helper.getLog('server1')
41 | });
42 |
43 | server1.on('query', function (query, cb) {
44 | var domain = query.name();
45 | var type = query.type();
46 |
47 | if (type === 'A') {
48 | var rec = new named.ARecord('127.0.0.1');
49 | query.addAnswer(domain, rec);
50 | query.send();
51 | cb();
52 | } else if (type === 'AAAA') {
53 | setTimeout(function () {
54 | var rec = new named.AAAARecord('::1');
55 | query.addAnswer(domain, rec);
56 | query.send();
57 | cb();
58 | }, 50);
59 | } else {
60 | cb();
61 | }
62 | });
63 |
64 | server1.listenUdp({ address: '::1', port: 9999 });
65 | server1.listenTcp({ address: '::1', port: 9999 }, function() {
66 | process.nextTick(callback);
67 | });
68 | });
69 |
70 | test('simple pipelining test', function (t) {
71 | var q1 = {};
72 | q1.header = {
73 | id: 1234,
74 | flags: {
75 | opcode: protocol.opCodes.QUERY,
76 | rcode: protocol.rCodes.NOERROR
77 | },
78 | qdCount: 1,
79 | anCount: 0,
80 | nsCount: 0,
81 | arCount: 0
82 | };
83 | q1.question = [{
84 | name: 'foo.bar',
85 | type: protocol.queryTypes.A,
86 | qclass: protocol.qClasses.IN
87 | }];
88 | q1.answer = [];
89 | q1.authority = [];
90 | q1.additional = [];
91 | var q1Buf = lengthPrefix(protocol.encode(q1, 'message'));
92 |
93 | var q2 = q1;
94 | q2.header.id = 1235;
95 | q2.question = [{
96 | name: 'baz.bar',
97 | type: protocol.queryTypes.AAAA,
98 | qclass: protocol.qClasses.IN
99 | }];
100 | var q2Buf = lengthPrefix(protocol.encode(q2, 'message'));
101 |
102 | var q3 = q1;
103 | q3.header.id = 1236;
104 | q3.question[0].type = protocol.queryTypes.A;
105 | var q3Buf = lengthPrefix(protocol.encode(q3, 'message'));
106 |
107 | var sock = net.connect(9999, '::1');
108 |
109 | var outstanding = {};
110 | var replies = {};
111 | var order = [];
112 |
113 | sock.on('connect', function () {
114 | sock.write(q1Buf);
115 | outstanding[1234] = true;
116 | sock.write(q2Buf);
117 | outstanding[1235] = true;
118 | sock.write(q3Buf);
119 | outstanding[1236] = true;
120 | });
121 |
122 | var buf = new Buffer(0);
123 | sock.on('readable', function () {
124 | var b;
125 | while ((b = sock.read()) !== null)
126 | buf = Buffer.concat([buf, b]);
127 | while (buf.length > 2) {
128 | var len = buf.readUInt16BE(0);
129 | if (buf.length >= len + 2) {
130 | var pkt = buf.slice(2, len + 2);
131 | buf = buf.slice(len + 2);
132 | var msg = protocol.decode(pkt, 'message');
133 | onMessage(msg);
134 | } else {
135 | break;
136 | }
137 | }
138 | });
139 |
140 | function onMessage(msg) {
141 | t.ok(outstanding[msg.header.id]);
142 | delete outstanding[msg.header.id];
143 | replies[msg.header.id] = msg;
144 | order.push(msg.header.id);
145 | if (Object.keys(outstanding).length < 1) {
146 | sock.end();
147 | onComplete();
148 | }
149 | }
150 |
151 | function onComplete() {
152 | t.deepEqual(order, [1234, 1236, 1235]);
153 | t.strictEqual(replies[1234].answer[0].name, 'foo.bar');
154 | t.strictEqual(replies[1235].answer[0].name, 'baz.bar');
155 | t.strictEqual(replies[1236].answer[0].name, 'baz.bar');
156 | t.strictEqual(replies[1236].answer[0].rtype,
157 | protocol.queryTypes.A);
158 | t.done();
159 | }
160 | });
161 |
162 | function lengthPrefix(buf) {
163 | var buf2 = new Buffer(buf.length + 2);
164 | buf.copy(buf2, 2);
165 | buf2.writeUInt16BE(buf.length, 0);
166 | return (buf2);
167 | }
168 |
169 | after(function (cb) {
170 | server1.close(cb);
171 | });
172 |
--------------------------------------------------------------------------------
/test/protocol.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var named = require('../lib');
24 |
25 | if (require.cache[__dirname + '/helper.js'])
26 | delete require.cache[__dirname + '/helper.js']
27 | var helper = require('./helper');
28 |
29 |
30 | var dnsBuffer = require('./dnsbuffer');
31 |
32 | var test = helper.test;
33 | var protocol = named.Protocol;
34 |
35 | Object.keys(dnsBuffer.samples).forEach(function (i) {
36 | var sample = dnsBuffer.samples[i];
37 | test('protocol decode/encode: ' + sample.description, function(t) {
38 | var decoded = protocol.decode(sample.raw, sample.type);
39 | if (sample.decoded !== undefined) {
40 | t.deepEqual(decoded, sample.decoded);
41 | }
42 | if (sample.encode === false) {
43 | t.end();
44 | return;
45 | }
46 | encoded = protocol.encode(decoded, sample.type);
47 | if (dnsBuffer.equalBuffers(encoded, sample.raw)) {
48 | t.ok(true, 'encoder cycle passed');
49 | }
50 | else {
51 | console.log(decoded);
52 | console.log(encoded.toString('base64'));
53 | console.log(sample.raw.toString('base64'));
54 | t.ok(false, 'encoder cycle failed');
55 | }
56 | t.end();
57 | });
58 | });
59 |
60 | test('mname#12 regression test', function (t) {
61 | var b = new Buffer('GET / HTTP/1.1\r\n\r\n');
62 | t.throws(function () {
63 | var decoded = protocol.decode(b, 'message');
64 | }, /label length/i);
65 | t.end();
66 | });
67 |
68 | test('mname#19 regression test (easy loop)', function (t) {
69 | var b = new Buffer('3bb981000001000000000000' +
70 | '047566647304636f616c066a6f79656e74027573c00c' +
71 | '00010001', 'hex');
72 | t.throws(function () {
73 | var decoded = protocol.decode(b, 'message');
74 | }, /label pointer/i);
75 | t.end();
76 | });
77 |
78 | test('mname#19 regression test (harder loop)', function (t) {
79 | var b = new Buffer('3bb981000001000000000005' +
80 | '047566647304636f616c066a6f79656e74027573c00b' +
81 | '00010001', 'hex');
82 | t.throws(function () {
83 | var decoded = protocol.decode(b, 'message');
84 | }, /maximum length/i);
85 | t.end();
86 | });
87 |
--------------------------------------------------------------------------------
/test/query.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var named = require('../lib');
24 | var dnsBuffer = require('./dnsbuffer');
25 |
26 | if (require.cache[__dirname + '/helper.js'])
27 | delete require.cache[__dirname + '/helper.js'];
28 | var helper = require('./helper');
29 |
30 | var test = helper.test;
31 | var before = helper.before;
32 | var after = helper.after;
33 |
34 | var qopts = {};
35 |
36 | before(function(callback) {
37 | try {
38 | qopts.data = dnsBuffer.samples[0].raw,
39 | qopts.family = 'udp';
40 | qopts.address = '127.0.0.1';
41 | qopts.port = 23456;
42 |
43 | process.nextTick(callback);
44 | }
45 | catch (e) {
46 | console.error(e.stack);
47 | process.exit(1);
48 | }
49 | });
50 |
51 |
52 | test('decode a query datagram', function(t) {
53 | var query = named.Query.parse(qopts);
54 | t.end();
55 | });
56 |
57 | test('encode an null-response query object', function(t) {
58 | var query = named.Query.parse(qopts);
59 | query.setError('enoerr');
60 | var buf = query.encode();
61 | var ok = dnsBuffer.samples[0].raw;
62 | t.deepEqual(buf, ok);
63 | t.end();
64 | });
65 |
66 | // TODO test adding a record
67 | // TODO test name response
68 | // TODO test answers response
69 |
--------------------------------------------------------------------------------
/test/records.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var named = require('../lib');
24 |
25 | if (require.cache[__dirname + '/helper.js'])
26 | delete require.cache[__dirname + '/helper.js']
27 | var helper = require('./helper');
28 |
29 | var test = helper.test;
30 |
31 | var testRecord = function(record, t) {
32 | if (!record)
33 | t.ok(false, 'record could not be created');
34 |
35 | if (record && record.valid()) {
36 | t.ok(true, 'valid record created');
37 | }
38 | else {
39 | t.ok(false, 'record was not valid');
40 | }
41 |
42 | t.end()
43 | }
44 |
45 | test('create a valid record (A)', function(t) {
46 | var record = new named.ARecord('127.0.0.1');
47 | testRecord(record, t);
48 | });
49 |
50 | test('create a valid record (AAAA)', function(t) {
51 | var record = new named.AAAARecord('::1');
52 | testRecord(record, t);
53 | });
54 |
55 | test('create a valid record (CNAME)', function(t) {
56 | var record = new named.CNAMERecord('alias.example.com');
57 | testRecord(record, t);
58 | });
59 |
60 | test('create a valid record (NS)', function(t) {
61 | var record = new named.NSRecord('ns.example.com');
62 | testRecord(record, t);
63 | });
64 |
65 | test('create a valid record (MX)', function(t) {
66 | var record = new named.MXRecord('smtp.example.com');
67 | testRecord(record, t);
68 | });
69 |
70 | test('create a valid record (SOA)', function(t) {
71 | var record = new named.SOARecord('example.com');
72 | testRecord(record, t);
73 | });
74 |
75 | test('create a valid record (SRV)', function(t) {
76 | var record = new named.SRVRecord('_sip._udp.example.com', 5060);
77 | testRecord(record, t);
78 | });
79 |
80 | test('create a valid record (TXT)', function(t) {
81 | var record = new named.TXTRecord('hello world');
82 | testRecord(record, t);
83 | });
84 |
--------------------------------------------------------------------------------
/test/tsig.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Joyent, Inc
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | * SOFTWARE.
21 | */
22 | var mod_mname = require('../lib');
23 | var dig = require('./dig');
24 | var mod_crypto = require('crypto');
25 |
26 | if (require.cache[__dirname + '/helper.js'])
27 | delete require.cache[__dirname + '/helper.js']
28 | var helper = require('./helper');
29 |
30 | var test = helper.test;
31 | var before = helper.before;
32 | var after = helper.after;
33 |
34 | var options = { port: 9999, server: '::1' };
35 |
36 | var KEY_MD5 = {
37 | name: 'md5test',
38 | algorithm: 'hmac-md5',
39 | data: mod_crypto.randomBytes(8)
40 | };
41 |
42 | var KEY_SHA1 = {
43 | name: 'shatest',
44 | algorithm: 'hmac-sha1',
45 | data: mod_crypto.randomBytes(12)
46 | };
47 |
48 | var KEY_SHA1_2 = {
49 | name: 'shatest2',
50 | algorithm: 'hmac-sha1',
51 | data: mod_crypto.randomBytes(12)
52 | };
53 |
54 | var KEY_SHA1_3 = {
55 | name: 'shatest',
56 | algorithm: 'hmac-sha1',
57 | data: mod_crypto.randomBytes(12)
58 | };
59 |
60 | var KEYS = {
61 | 'shatest': KEY_SHA1,
62 | 'md5test': KEY_MD5
63 | };
64 |
65 | before(function (callback) {
66 | this.server = mod_mname.createServer({
67 | log: helper.getLog('server')
68 | });
69 | var server = this.server;
70 |
71 | this.server.on('query', function (query, cb) {
72 | if (!query.isSigned() || !query.verify(KEYS)) {
73 | query.setError('notauth');
74 | query.send();
75 | cb();
76 | return;
77 | }
78 | var domain = query.name();
79 | var record;
80 | if (query.type() === 'AXFR') {
81 | var soa = new mod_mname.SOARecord(domain);
82 | query.addAnswer(domain, soa, 300);
83 | query.send();
84 | record = new mod_mname.ARecord('127.0.0.1');
85 | query.addAnswer(domain, record, 300);
86 | query.send();
87 | query.addAnswer(domain, soa, 300);
88 | query.send();
89 | cb();
90 | } else {
91 | record = new mod_mname.ARecord('127.0.0.1');
92 | query.addAnswer(domain, record, 300);
93 | query.send();
94 | cb();
95 | }
96 | });
97 |
98 | this.server.listenUdp({port: options.port, address: options.server},
99 | function () {
100 | server.listenTcp({port: options.port, address: options.server},
101 | function () {
102 | process.nextTick(callback);
103 | });
104 | });
105 | });
106 |
107 | after(function (cb) {
108 | this.server.close(cb);
109 | });
110 |
111 | process.on('uncaughtException', function(err) {
112 | console.error(err.stack);
113 | });
114 |
115 | test('tsig required', function (t) {
116 | dig('example.com', 'A', options, function (err, results) {
117 | t.ifError(err);
118 | t.equal(results.status, 'notauth');
119 | t.end();
120 | });
121 | });
122 |
123 | test('tsig md5', function (t) {
124 | options.key = KEY_MD5;
125 | dig('example.com', 'A', options, function (err, results) {
126 | t.ifError(err);
127 | t.equal(results.status, 'noerror');
128 | t.ok(!results.tsigFail);
129 | t.deepEqual(results.answers, [{
130 | name: 'example.com.',
131 | ttl: 300, type: 'A',
132 | target: '127.0.0.1'
133 | }]);
134 | t.end();
135 | });
136 | });
137 |
138 | test('tsig sha1', function (t) {
139 | options.key = KEY_SHA1;
140 | dig('example.com', 'A', options, function (err, results) {
141 | t.ifError(err);
142 | t.equal(results.status, 'noerror');
143 | t.ok(!results.tsigFail);
144 | t.deepEqual(results.answers, [{
145 | name: 'example.com.',
146 | ttl: 300, type: 'A',
147 | target: '127.0.0.1'
148 | }]);
149 | t.end();
150 | });
151 | });
152 |
153 | test('tsig sha1 with unknown key', function (t) {
154 | options.key = KEY_SHA1_2;
155 | dig('example.com', 'A', options, function (err, results) {
156 | t.ifError(err);
157 | t.equal(results.status, 'notauth');
158 | t.end();
159 | });
160 | });
161 |
162 | test('tsig sha1 with wrong key', function (t) {
163 | options.key = KEY_SHA1_3;
164 | dig('example.com', 'A', options, function (err, results) {
165 | t.ifError(err);
166 | t.equal(results.status, 'notauth');
167 | t.end();
168 | });
169 | });
170 |
171 | test('tsig axfr', function (t) {
172 | options.key = KEY_SHA1;
173 | dig('example.com', 'AXFR', options, function (err, results) {
174 | t.ifError(err);
175 | t.strictEqual(results.status, null);
176 | t.ok(!results.tsigFail);
177 | var noTsig = results.answers.filter(function (rec) {
178 | return (rec.type !== 'TSIG');
179 | });
180 | t.deepEqual(noTsig, [{
181 | name: 'example.com.',
182 | ttl: 300, type: 'SOA',
183 | target: 'example.com. hostmaster.example.com. ' +
184 | '0 86400 7200 1209600 10800'
185 | }, {
186 | name: 'example.com.',
187 | ttl: 300, type: 'A',
188 | target: '127.0.0.1'
189 | }, {
190 | name: 'example.com.',
191 | ttl: 300, type: 'SOA',
192 | target: 'example.com. hostmaster.example.com. ' +
193 | '0 86400 7200 1209600 10800'
194 | }]);
195 | t.end();
196 | });
197 | });
198 |
--------------------------------------------------------------------------------
/test/validator.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Trevor Orsztynowicz
3 | * Copyright (c) 2015 Joyent, Inc
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 | var validators = require('../lib/validators');
24 |
25 | if (require.cache[__dirname + '/helper.js'])
26 | delete require.cache[__dirname + '/helper.js']
27 | var helper = require('./helper');
28 |
29 | var test = helper.test;
30 |
31 | var toTest = {
32 | nsName: [
33 | [ 'example.com', true ],
34 | [ '0example.com', true ],
35 | [ '_example.com', false ],
36 | [ '0_example.com', false ],
37 | [ '-example.com', false ],
38 | [ '0-example.com', true ],
39 | [ 'example-one.com', true ],
40 | [ 'example-111.com', true ],
41 | [ 'Example-111.com', true ],
42 | [ 'a name with spaces', false ],
43 | ],
44 | UInt32BE: [
45 | [ 'hello', false ],
46 | [ '12345', true ],
47 | [ 4294967296, false ],
48 | [ 10, true ]
49 | ],
50 | UInt16BE: [
51 | [ 'hello', false ],
52 | [ '12345', true ],
53 | [ 65536, false ],
54 | [ 10, true ]
55 | ],
56 | nsText: [
57 | [ 'hello world', true ],
58 | ]
59 | };
60 |
61 | test('testing validator (nsName)', function(t) {
62 | var k = 'nsName';
63 | for (var i in toTest.k) {
64 | var s = toTest.k[i][0];
65 | var ok = toTest.k[i][1];
66 | var result = validators.k(s);
67 | t.equal(result, ok);
68 | }
69 | t.end();
70 | });
71 |
72 | test('testing validator (UInt32BE)', function(t) {
73 | var k = 'UInt32BE';
74 | for (var i in toTest.k) {
75 | var s = toTest.k[i][0];
76 | var ok = toTest.k[i][1];
77 | var result = validators.k(s);
78 | t.equal(result, ok);
79 | }
80 | t.end();
81 | });
82 |
83 | test('testing validator (UInt16BE)', function(t) {
84 | var k = 'UInt16BE';
85 | for (var i in toTest.k) {
86 | var s = toTest.k[i][0];
87 | var ok = toTest.k[i][1];
88 | var result = validators.k(s);
89 | t.equal(result, ok);
90 | }
91 | t.end();
92 | });
93 |
94 | test('testing validator (nsText)', function(t) {
95 | var k = 'nsText';
96 | for (var i in toTest.k) {
97 | var s = toTest.k[i][0];
98 | var ok = toTest.k[i][1];
99 | var result = validators.k(s);
100 | t.equal(result, ok);
101 | }
102 | t.end();
103 | });
104 |
--------------------------------------------------------------------------------
/tools/bashstyle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /*
3 | * This Source Code Form is subject to the terms of the Mozilla Public
4 | * License, v. 2.0. If a copy of the MPL was not distributed with this
5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 | */
7 |
8 | /*
9 | * Copyright (c) 2014, Joyent, Inc.
10 | */
11 |
12 | /*
13 | * bashstyle: check bash scripts for adherence to style guidelines, including:
14 | *
15 | * o no lines longer than 80 characters
16 | * o file does not end with a blank line
17 | * o Do not use 'local' and var initialization *using a subshell* in the
18 | * same statement. See
19 | *
20 | * for why not. Arguably this belongs in a separate 'bashlint'.
21 | *
22 | * Future enhancements could include:
23 | * o indents consistent with respect to tabs, spaces
24 | * o indents consistently sized (all are some multiple of the smallest
25 | * indent, which must be a tab or 4 or 8 spaces)
26 | */
27 |
28 | var VERSION = '2.0.0';
29 |
30 | var mod_assert = require('assert');
31 | var mod_fs = require('fs');
32 |
33 | var nerrors = 0;
34 |
35 | main();
36 | process.exit(0);
37 |
38 | function main()
39 | {
40 | var files = process.argv.slice(2);
41 |
42 | if (files.length === 0) {
43 | console.error('usage: %s file1 [...]',
44 | process.argv.slice(0, 2).join(' '));
45 | process.exit(2);
46 | }
47 |
48 | files.forEach(checkFile);
49 |
50 | if (nerrors != 0)
51 | process.exit(1);
52 | }
53 |
54 | function checkFile(filename)
55 | {
56 | var text = mod_fs.readFileSync(filename, 'utf-8');
57 | var lines = text.split('\n');
58 | var i;
59 | var styled = false;
60 | var styleStart;
61 |
62 | mod_assert.ok(lines.length > 0);
63 |
64 | /*
65 | * Expand tabs in each line and check for long lines.
66 | */
67 | for (i = 1; i <= lines.length; i++) {
68 | var line = expandTabs(lines[i - 1]);
69 |
70 | if (i > 1 && lines[i-2].match(/# BASHSTYLED/)) {
71 | continue;
72 | }
73 |
74 | if (line.match(/# BEGIN BASHSTYLED/)) {
75 | styleStart = i;
76 | styled = true;
77 | }
78 |
79 | if (line.match(/# END BASHSTYLED/)) {
80 | if (styled != true) {
81 | nerrors++;
82 | console.log('%s: %d: END BASHSTYLED w/o corresponding BEGIN',
83 | filename, i);
84 | }
85 | styled = false;
86 | }
87 |
88 | if (!styled && line.match(/^\s*local\s+(\w+)\s*=.*\$\(/)) {
89 | nerrors++;
90 | var m = line.match(/^\s*local\s+(\w+)\s*=/);
91 | console.log('%s: %d: declaring and setting a "local" ' +
92 | 'var in the same statement ' +
93 | 'ignores a subshell return code ' +
94 | ': ' +
95 | 'local %s=...',
96 | filename, i, m[1]);
97 | }
98 |
99 | // Regexplanation: non-[, [, space (contents) space, ], non-]
100 | // groups before and after brackets to ease search/replace.
101 | if (!styled && line.match(/(^|[^\[])\[(\s.+\s)\]([^\]])/)) {
102 | nerrors++;
103 | console.log('%s: %d: prefer [[ to [ for tests.', filename, i);
104 | }
105 |
106 | if (!styled && line.length > 80) {
107 | nerrors++;
108 | console.log('%s: %d: line exceeds 80 columns',
109 | filename, i);
110 | }
111 | }
112 |
113 | if (styled) {
114 | nerrors++;
115 | console.log('%s: %d: BEGIN BASHSTYLED that does not END',
116 | filename, styleStart);
117 | }
118 |
119 |
120 | /*
121 | * No sane editor lets you save a file without a newline at the very end.
122 | */
123 | if (lines[lines.length - 1].length !== 0) {
124 | nerrors++;
125 | console.log('%s: %d: file does not end with newline',
126 | filename, lines.length);
127 | }
128 |
129 | /*
130 | * Since the file will always end with a newline, the last entry of
131 | * "lines" will actually be blank.
132 | */
133 | if (lines.length > 1 && lines[lines.length - 2].length === 0) {
134 | nerrors++;
135 | console.log('%s: %d: file ends with a blank line',
136 | filename, lines.length - 1);
137 | }
138 | }
139 |
140 | function expandTabs(text)
141 | {
142 | var out = '';
143 | var col = 0;
144 | var j, k;
145 |
146 | for (j = 0; j < text.length; j++) {
147 | if (text[j] != '\t') {
148 | out += text[j];
149 | col++;
150 | continue;
151 | }
152 |
153 | k = 8 - (col % 8);
154 | col += k;
155 |
156 | do {
157 | out += ' ';
158 | } while (--k > 0);
159 |
160 | col += k;
161 | }
162 |
163 | return (out);
164 | }
165 |
--------------------------------------------------------------------------------
/tools/jsl.node.conf:
--------------------------------------------------------------------------------
1 | #
2 | # Configuration File for JavaScript Lint
3 | #
4 | # This configuration file can be used to lint a collection of scripts, or to enable
5 | # or disable warnings for scripts that are linted via the command line.
6 | #
7 |
8 | ### Warnings
9 | # Enable or disable warnings based on requirements.
10 | # Use "+WarningName" to display or "-WarningName" to suppress.
11 | #
12 | +ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent
13 | +ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity
14 | +ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement
15 | +anon_no_return_value # anonymous function does not always return value
16 | +assign_to_function_call # assignment to a function call
17 | -block_without_braces # block statement without curly braces
18 | +comma_separated_stmts # multiple statements separated by commas (use semicolons?)
19 | +comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)
20 | +default_not_at_end # the default case is not at the end of the switch statement
21 | +dup_option_explicit # duplicate "option explicit" control comment
22 | +duplicate_case_in_switch # duplicate case in switch statement
23 | +duplicate_formal # duplicate formal argument {name}
24 | +empty_statement # empty statement or extra semicolon
25 | +identifier_hides_another # identifer {name} hides an identifier in a parent scope
26 | -inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement
27 | +incorrect_version # Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version.
28 | +invalid_fallthru # unexpected "fallthru" control comment
29 | +invalid_pass # unexpected "pass" control comment
30 | +jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax
31 | +leading_decimal_point # leading decimal point may indicate a number or an object member
32 | +legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax
33 | +meaningless_block # meaningless block; curly braces have no impact
34 | +mismatch_ctrl_comments # mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence
35 | +misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma
36 | +missing_break # missing break statement
37 | +missing_break_for_last_case # missing break statement for last case in switch
38 | +missing_default_case # missing default case in switch statement
39 | +missing_option_explicit # the "option explicit" control comment is missing
40 | +missing_semicolon # missing semicolon
41 | +missing_semicolon_for_lambda # missing semicolon for lambda assignment
42 | +multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs
43 | +nested_comment # nested comment
44 | +no_return_value # function {name} does not always return a value
45 | +octal_number # leading zeros make an octal number
46 | +parseint_missing_radix # parseInt missing radix parameter
47 | +partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag
48 | +redeclared_var # redeclaration of {name}
49 | +trailing_comma_in_array # extra comma is not recommended in array initializers
50 | +trailing_decimal_point # trailing decimal point may indicate a number or an object member
51 | +undeclared_identifier # undeclared identifier: {name}
52 | +unreachable_code # unreachable code
53 | -unreferenced_argument # argument declared but never referenced: {name}
54 | -unreferenced_function # function is declared but never referenced: {name}
55 | +unreferenced_variable # variable is declared but never referenced: {name}
56 | +unsupported_version # JavaScript {version} is not supported
57 | +use_of_label # use of label
58 | +useless_assign # useless assignment
59 | +useless_comparison # useless comparison; comparing identical expressions
60 | -useless_quotes # the quotation marks are unnecessary
61 | +useless_void # use of the void type may be unnecessary (void is always undefined)
62 | +var_hides_arg # variable {name} hides argument
63 | +want_assign_or_call # expected an assignment or function call
64 | +with_statement # with statement hides undeclared variables; use temporary variable instead
65 |
66 |
67 | ### Output format
68 | # Customize the format of the error message.
69 | # __FILE__ indicates current file path
70 | # __FILENAME__ indicates current file name
71 | # __LINE__ indicates current line
72 | # __COL__ indicates current column
73 | # __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__)
74 | # __ERROR_NAME__ indicates error name (used in configuration file)
75 | # __ERROR_PREFIX__ indicates error prefix
76 | # __ERROR_MSG__ indicates error message
77 | #
78 | # For machine-friendly output, the output format can be prefixed with
79 | # "encode:". If specified, all items will be encoded with C-slashes.
80 | #
81 | # Visual Studio syntax (default):
82 | +output-format __FILE__(__LINE__): __ERROR__
83 | # Alternative syntax:
84 | #+output-format __FILE__:__LINE__: __ERROR__
85 |
86 |
87 | ### Context
88 | # Show the in-line position of the error.
89 | # Use "+context" to display or "-context" to suppress.
90 | #
91 | +context
92 |
93 |
94 | ### Control Comments
95 | # Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for
96 | # the /*@keyword@*/ control comments and JScript conditional comments. (The latter is
97 | # enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason,
98 | # although legacy control comments are enabled by default for backward compatibility.
99 | #
100 | -legacy_control_comments
101 |
102 |
103 | ### Defining identifiers
104 | # By default, "option explicit" is enabled on a per-file basis.
105 | # To enable this for all files, use "+always_use_option_explicit"
106 | -always_use_option_explicit
107 |
108 | # Define certain identifiers of which the lint is not aware.
109 | # (Use this in conjunction with the "undeclared identifier" warning.)
110 | #
111 | # Common uses for webpages might be:
112 | +define __dirname
113 | +define clearInterval
114 | +define clearTimeout
115 | +define console
116 | +define exports
117 | +define global
118 | +define module
119 | +define process
120 | +define require
121 | +define setInterval
122 | +define setImmediate
123 | +define setTimeout
124 | +define Buffer
125 | +define JSON
126 | +define Math
127 | +define Map
128 |
129 | ### JavaScript Version
130 | # To change the default JavaScript version:
131 | #+default-type text/javascript;version=1.5
132 | #+default-type text/javascript;e4x=1
133 |
134 | ### Files
135 | # Specify which files to lint
136 | # Use "+recurse" to enable recursion (disabled by default).
137 | # To add a set of files, use "+process FileName", "+process Folder\Path\*.js",
138 | # or "+process Folder\Path\*.htm".
139 | #
140 |
141 |
--------------------------------------------------------------------------------
/tools/jsstyle.conf:
--------------------------------------------------------------------------------
1 | #
2 | # This Source Code Form is subject to the terms of the Mozilla Public
3 | # License, v. 2.0. If a copy of the MPL was not distributed with this
4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 | #
6 |
7 | #
8 | # Copyright (c) 2014, Joyent, Inc.
9 | #
10 |
11 | indent=8
12 | doxygen
13 | unparenthesized-return=0
14 | blank-after-start-comment=0
15 |
--------------------------------------------------------------------------------
/tools/mk/Makefile.defs:
--------------------------------------------------------------------------------
1 | # -*- mode: makefile -*-
2 | #
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 | #
7 |
8 | #
9 | # Copyright (c) 2014, Joyent, Inc.
10 | #
11 |
12 | #
13 | # Makefile.defs: common defines.
14 | #
15 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
16 | # into other repos as-is without requiring any modifications. If you find
17 | # yourself changing this file, you should instead update the original copy in
18 | # eng.git and then update your repo to use the new version.
19 | #
20 | # This makefile defines some useful defines. Include it at the top of
21 | # your Makefile.
22 | #
23 | # Definitions in this Makefile:
24 | #
25 | # TOP The absolute path to the project directory. The top dir.
26 | # BRANCH The current git branch.
27 | # TIMESTAMP The timestamp for the build. This can be set via
28 | # the TIMESTAMP envvar (used by MG-based builds).
29 | # STAMP A build stamp to use in built package names.
30 | #
31 |
32 | TOP := $(shell pwd)
33 |
34 | #
35 | # Mountain Gorilla-spec'd versioning.
36 | # See "Package Versioning" in MG's README.md:
37 | #
38 | #
39 | # Need GNU awk for multi-char arg to "-F".
40 | _AWK := $(shell (which gawk >/dev/null && echo gawk) \
41 | || (which nawk >/dev/null && echo nawk) \
42 | || echo awk)
43 | BRANCH := $(shell git symbolic-ref HEAD | $(_AWK) -F/ '{print $$3}')
44 | ifeq ($(TIMESTAMP),)
45 | TIMESTAMP := $(shell date -u "+%Y%m%dT%H%M%SZ")
46 | endif
47 | _GITDESCRIBE := g$(shell git describe --all --long --dirty | $(_AWK) -F'-g' '{print $$NF}')
48 | STAMP := $(BRANCH)-$(TIMESTAMP)-$(_GITDESCRIBE)
49 |
50 | # node-gyp will print build info useful for debugging with V=1
51 | export V=1
52 |
--------------------------------------------------------------------------------
/tools/mk/Makefile.deps:
--------------------------------------------------------------------------------
1 | # -*- mode: makefile -*-
2 | #
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 | #
7 |
8 | #
9 | # Copyright (c) 2014, Joyent, Inc.
10 | #
11 |
12 | #
13 | # Makefile.deps: Makefile for including common tools as dependencies
14 | #
15 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
16 | # into other repos as-is without requiring any modifications. If you find
17 | # yourself changing this file, you should instead update the original copy in
18 | # eng.git and then update your repo to use the new version.
19 | #
20 | # This file is separate from Makefile.targ so that teams can choose
21 | # independently whether to use the common targets in Makefile.targ and the
22 | # common tools here.
23 | #
24 |
25 | #
26 | # javascriptlint
27 | #
28 | JSL_EXEC ?= deps/javascriptlint/build/install/jsl
29 | JSL ?= $(JSL_EXEC)
30 |
31 | $(JSL_EXEC): | deps/javascriptlint/.git
32 | cd deps/javascriptlint && make install
33 |
34 | distclean::
35 | if [[ -f deps/javascriptlint/Makefile ]]; then \
36 | cd deps/javascriptlint && make clean; \
37 | fi
38 |
39 | #
40 | # jsstyle
41 | #
42 | JSSTYLE_EXEC ?= deps/jsstyle/jsstyle
43 | JSSTYLE ?= $(JSSTYLE_EXEC)
44 |
45 | $(JSSTYLE_EXEC): | deps/jsstyle/.git
46 |
47 | JSON_EXEC ?= node_modules/.bin/json
48 | JSON ?= $(JSON_EXEC)
49 |
50 | $(JSON_EXEC): $(NPM_EXEC)
51 | $(NPM) install json
52 |
53 | #
54 | # restdown
55 | #
56 | RESTDOWN_EXEC ?= deps/restdown/bin/restdown
57 | RESTDOWN ?= python $(RESTDOWN_EXEC)
58 | $(RESTDOWN_EXEC): | deps/restdown/.git
59 |
60 | EXTRA_DOC_DEPS ?=
61 |
--------------------------------------------------------------------------------
/tools/mk/Makefile.node.defs:
--------------------------------------------------------------------------------
1 | # -*- mode: makefile -*-
2 | #
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 | #
7 |
8 | #
9 | # Copyright (c) 2014, Joyent, Inc.
10 | #
11 |
12 | #
13 | # Makefile.node.defs: Makefile for building and bundling your own Node.js.
14 | #
15 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
16 | # into other repos as-is without requiring any modifications. If you find
17 | # yourself changing this file, you should instead update the original copy in
18 | # eng.git and then update your repo to use the new version.
19 | #
20 |
21 | #
22 | # This Makefile facilitates building and bundling your own copy of Node.js in
23 | # your repo. All it does is define variables for node, node-waf, and npm for
24 | # you to use elsewhere in your Makefile and rules to build these tools when
25 | # needed.
26 | #
27 | # To use this facility, include "Makefile.node.defs", use the variables as
28 | # described below to define targets, and then include "Makefile.node.targ".
29 | #
30 | # There are two use cases addressed here:
31 | #
32 | # (1) Invoking node, node-waf, or npm as part of the build process, as in "npm
33 | # install" and "node-waf configure build". To facilitate this, this
34 | # Makefile defines Make variables NODE, NODE_WAF, and NPM that you can use
35 | # to invoke these commands during the build process. You MUST NOT assume
36 | # that these variables just evaluate to the filenames themselves, as they
37 | # may have environment variable definitions and other things that prevent
38 | # you from using them directly as a filename. If you want that, see (2).
39 | #
40 | # Wherever you use one of these variables, you MUST include a dependency on
41 | # the corresponding *_EXEC variable as well, like so:
42 | #
43 | # node_modules/restify: deps/restify $(NPM_EXEC)
44 | # $(NPM) install deps/restify
45 | #
46 | # or better, use an order-only dependency to avoid spurious rebuilds:
47 | #
48 | # node_modules/restify: deps/restify | $(NPM_EXEC)
49 | # $(NPM) install deps/restify
50 | #
51 | # Otherwise, the underlying file will not get built. We don't
52 | # automatically build them as part of "all" because that approach is
53 | # brittle.
54 | #
55 | # (2) Specifying paths for invoking node, node-waf, or npm at RUNTIME, as in
56 | # specifying the path to node used for the start method of your service's
57 | # SMF manifest. For this, this Makefile defines variables NODE_EXEC,
58 | # NODE_WAF_EXEC, and NPM_EXEC, which represent the relative paths of these
59 | # files from the root of the workspace. You MUST NOT use these variables
60 | # to invoke these commands during the build process. See (1) instead.
61 | #
62 | # However, in order to work at runtime, you must build the tool as well.
63 | # That is, if you use NODE_EXEC to specify the path to node, you must
64 | # depend on NODE_EXEC somewhere. This usually happens anyway because you
65 | # usually need them during the build process too, but if you don't then
66 | # you need to explicitly add NODE_EXEC (or whichever) to your "all"
67 | # target.
68 | #
69 | # When including this Makefile, you MAY also specify:
70 | #
71 | # BUILD top-level directory for built binaries
72 | # (default: "build")
73 | #
74 | # NODE_INSTALL where node should install its built items
75 | # (default: "$BUILD/node")
76 | #
77 | # NODE_CONFIG_FLAGS extra flags to pass to Node's "configure"
78 | # (default: "--with-dtrace" on SmartOS; empty
79 | # otherwise.)
80 | #
81 |
82 | TOP ?= $(error You must include Makefile.defs before this makefile)
83 |
84 | BUILD ?= build
85 | NODE_INSTALL ?= $(BUILD)/node
86 | DISTCLEAN_FILES += $(NODE_INSTALL)
87 |
88 | NODE_CONFIG_FLAGS += --prefix=$(TOP)/$(NODE_INSTALL)
89 |
90 | ifeq ($(shell uname -s),SunOS)
91 | NODE_CONFIG_FLAGS += --with-dtrace \
92 | --openssl-libpath=/opt/local/lib \
93 | --openssl-includes=/opt/local/include
94 | endif
95 |
96 | NODE_EXEC = $(NODE_INSTALL)/bin/node
97 | NODE_WAF_EXEC = $(NODE_INSTALL)/bin/node-waf
98 | NPM_EXEC = $(NODE_INSTALL)/bin/npm
99 |
100 | # Ensure these use absolute paths to the executables to allow running
101 | # from a dir other than the project top.
102 | NODE := $(TOP)/$(NODE_EXEC)
103 | NODE_WAF := $(TOP)/$(NODE_WAF_EXEC)
104 | NPM := PATH=$(TOP)/$(NODE_INSTALL)/bin:$(PATH) node $(TOP)/$(NPM_EXEC)
105 |
--------------------------------------------------------------------------------
/tools/mk/Makefile.node.targ:
--------------------------------------------------------------------------------
1 | # -*- mode: makefile -*-
2 | #
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 | #
7 |
8 | #
9 | # Copyright (c) 2014, Joyent, Inc.
10 | #
11 |
12 | #
13 | # Makefile.node.targ: See Makefile.node.defs.
14 | #
15 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
16 | # into other repos as-is without requiring any modifications. If you find
17 | # yourself changing this file, you should instead update the original copy in
18 | # eng.git and then update your repo to use the new version.
19 | #
20 |
21 | ifneq ($(shell uname -s),SunOS)
22 | NODE_PREBUILT_VERSION ?= $(error You must define NODE_PREBUILT_VERSION to use Makefile.node.targ on non-SunOS)
23 | endif
24 |
25 | ifeq ($(shell uname -s),SunOS)
26 | $(NODE_EXEC) $(NPM_EXEC) $(NODE_WAF_EXEC): | deps/node/.git
27 | (cd deps/node; ./configure $(NODE_CONFIG_FLAGS) && $(MAKE) && $(MAKE) install)
28 | else
29 | $(NODE_EXEC) $(NPM_EXEC) $(NODE_WAF_EXEC):
30 | (mkdir -p $(BUILD) \
31 | && cd $(BUILD) \
32 | && [[ -d src-node ]] && (cd src-node && git checkout master && git pull) || git clone https://github.com/joyent/node.git src-node \
33 | && cd src-node \
34 | && git checkout $(NODE_PREBUILT_VERSION) \
35 | && ./configure $(NODE_CONFIG_FLAGS) \
36 | && $(MAKE) && $(MAKE) install)
37 | endif
38 |
39 | DISTCLEAN_FILES += $(NODE_INSTALL) $(BUILD)/src-node
40 |
41 | distclean::
42 | -([[ ! -d deps/node ]] || (cd deps/node && $(MAKE) distclean))
43 |
--------------------------------------------------------------------------------
/tools/mk/Makefile.node_deps.defs:
--------------------------------------------------------------------------------
1 | # -*- mode: makefile -*-
2 | #
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 | #
7 |
8 | #
9 | # Copyright (c) 2014, Joyent, Inc.
10 | #
11 |
12 | #
13 | # Makefile.node_deps.defs: Makefile for including npm modules whose sources
14 | # reside inside the repo. This should NOT be used for modules in the npm
15 | # public repo or modules that could be specified with git SHAs.
16 | #
17 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
18 | # into other repos as-is without requiring any modifications. If you find
19 | # yourself changing this file, you should instead update the original copy in
20 | # eng.git and then update your repo to use the new version.
21 | #
22 |
23 | #
24 | # This Makefile takes as input the following make variable:
25 | #
26 | # REPO_MODULES List of relative paths to node modules (i.e., npm
27 | # packages) inside this repo. For example:
28 | # src/node-canative, where there's a binary npm package
29 | # in src/node-canative.
30 | #
31 | # Based on the above, this Makefile defines the following new variables:
32 | #
33 | # REPO_DEPS List of relative paths to the installed modules. For
34 | # example: "node_modules/canative".
35 | #
36 | # The accompanying Makefile.node_deps.targ defines a target that will install
37 | # each of REPO_MODULES into REPO_DEPS and remove REPO_DEPS with "make clean".
38 | # The top-level Makefile is responsible for depending on REPO_DEPS where
39 | # appropriate (usually the "deps" or "all" target).
40 | #
41 |
42 | REPO_DEPS = $(REPO_MODULES:src/node-%=node_modules/%)
43 | CLEAN_FILES += $(REPO_DEPS)
44 |
--------------------------------------------------------------------------------
/tools/mk/Makefile.node_deps.targ:
--------------------------------------------------------------------------------
1 | # -*- mode: makefile -*-
2 | #
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 | #
7 |
8 | #
9 | # Copyright (c) 2014, Joyent, Inc.
10 | #
11 |
12 | #
13 | # Makefile.node_deps.targ: targets for Makefile.node_deps.defs.
14 | #
15 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
16 | # into other repos as-is without requiring any modifications. If you find
17 | # yourself changing this file, you should instead update the original copy in
18 | # eng.git and then update your repo to use the new version.
19 | #
20 |
21 | NPM_EXEC ?= $(error NPM_EXEC must be defined for Makefile.node_deps.targ)
22 |
23 | node_modules/%: src/node-% | $(NPM_EXEC)
24 | $(NPM) install $<
25 |
--------------------------------------------------------------------------------
/tools/mk/Makefile.node_prebuilt.defs:
--------------------------------------------------------------------------------
1 | # -*- mode: makefile -*-
2 | #
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 | #
7 |
8 | #
9 | # Copyright (c) 2014, Joyent, Inc.
10 | #
11 |
12 | #
13 | # Makefile.node_prebuilt.defs: Makefile for including a prebuilt Node.js build.
14 | #
15 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
16 | # into other repos as-is without requiring any modifications. If you find
17 | # yourself changing this file, you should instead update the original copy in
18 | # eng.git and then update your repo to use the new version.
19 | #
20 |
21 | #
22 | # This Makefile facilitates downloading and bundling a prebuilt node.js
23 | # build (using the 'sdcnode' distro builds). This is an alternative to
24 | # the "Makefile.node.*" makefiles for *building* a node from source.
25 | #
26 | # Usage:
27 | #
28 | # - Define `NODE_PREBUILT_VERSION` in your Makefile to choose a node version.
29 | # E.g.: `NODE_PREBUILT_VERSION=v0.6.19`. See other optional variables
30 | # below.
31 | # - `include tools/mk/Makefile.node_prebuilt.defs` after this in your Makefile.
32 | # - `include tools/mk/Makefile.node_prebuilt.targ` near the end of your
33 | # Makefile.
34 | # - Have at least one of your Makefile targets depend on either `$(NODE_EXEC)`
35 | # or `$(NPM_EXEC)`. E.g.:
36 | #
37 | # node_modules/restify: deps/restify $(NPM_EXEC)
38 | # $(NPM) install deps/restify
39 | #
40 | # or better, use an order-only dependency to avoid spurious rebuilds:
41 | #
42 | # node_modules/restify: deps/restify | $(NPM_EXEC)
43 | # $(NPM) install deps/restify
44 | #
45 | # - Use `$(NPM)` or `$(NODE)` to use your node build.
46 | # - Include the "$(NODE_INSTALL)" tree in your release package.
47 | #
48 | #
49 | # When including this Makefile, you MUST also specify:
50 | #
51 | # NODE_PREBUILT_VERSION The node version in the prebuilt 'sdcnode'
52 | # package to use. Typically this is one of the
53 | # node version tags, e.g. "v0.6.18" but it
54 | # can be any commitish.
55 | #
56 | # When including this Makefile, you MAY also specify:
57 | #
58 | # NODE_PREBUILT_DIR The dir in which to find sdcnode builds. This
59 | # can either be a *local directory* or *a
60 | # URL* dir (with trailing '/') which serves
61 | # Apache/Nginx dir listing HTML.
62 | # (default: sdcnode master build dir on stuff)
63 | #
64 | # NODE_PREBUILT_TAG The 'sdcnode' project supports special
65 | # configuration builds of node, e.g. say a
66 | # build configured `--without-ssl`. These
67 | # special configurations are given a tag, e.g.
68 | # 'gz', that is used in the filename. Optionally
69 | # specify a tag name here.
70 | # (default: empty)
71 | #
72 | # NODE_PREBUILT_BRANCH Specify a particular branch of 'sdcnode' builds
73 | # from which to pull. Generally one should stick
74 | # with the default.
75 | # (default: master)
76 | #
77 | # NODE_PREBUILT_IMAGE If you have a zone image that differs from that
78 | # for an sdcnode build that you want to use (potential compat
79 | # issues be damned), then set this to the UUID of the sdcnode
80 | # build you want. See here for available build image uuids:
81 | #
82 | #
83 | # BUILD top-level directory for built binaries
84 | # (default: "build")
85 | #
86 | # NODE_INSTALL where node should install its built items
87 | # (default: "$BUILD/node")
88 | #
89 | #
90 | # Dev Notes:
91 | #
92 | # This works by getting "NODE_PREBUILT_NAME" from the provided "NODE_PREBUILT_*"
93 | # vars and the image version (via 'mdata-get sdc:image_uuid'). The image uuid is
94 | # included to ensure an exact match with the build machine. This name (e.g.
95 | # "v0.6.18-zone-$uuid") is used to find a matching "sdcnode-$name-*.tgz" build
96 | # in "NODE_PREBUILT_DIR" (either a local directory or a URL). That tarball is
97 | # downloaded and extracted into "NODE_INSTALL".
98 | #
99 | # The "*_EXEC" vars are set to named symlinks, e.g.
100 | # "build/prebuilt-node-v0.6.18-$uuid", so that a change of selected node
101 | # build (say the developer changes NODE_PREBUILT_VERSION) will recreate the
102 | # node install.
103 | #
104 | # See for details on 'sdcnode-*'
105 | # package naming.
106 | #
107 |
108 | TOP ?= $(error You must include Makefile.defs before this makefile)
109 | NODE_PREBUILT_VERSION ?= $(error NODE_PREBUILT_VERSION is not set.)
110 |
111 |
112 | BUILD ?= build
113 | NODE_INSTALL ?= $(BUILD)/node
114 | DISTCLEAN_FILES += $(NODE_INSTALL) \
115 | $(BUILD)/prebuilt-node-* $(BUILD)/prebuilt-npm-*
116 |
117 | NODE_PREBUILT_BRANCH ?= master
118 | NODE_PREBUILT_IMAGE ?= $(shell pfexec mdata-get sdc:image_uuid)
119 | ifeq ($(NODE_PREBUILT_TAG),)
120 | NODE_PREBUILT_NAME := $(NODE_PREBUILT_VERSION)-$(NODE_PREBUILT_IMAGE)
121 | else
122 | NODE_PREBUILT_NAME := $(NODE_PREBUILT_VERSION)-$(NODE_PREBUILT_TAG)-$(NODE_PREBUILT_IMAGE)
123 | endif
124 | NODE_PREBUILT_PATTERN := sdcnode-$(NODE_PREBUILT_NAME)-$(NODE_PREBUILT_BRANCH)-.*\.tgz
125 | NODE_PREBUILT_DIR ?= https://download.joyent.com/pub/build/sdcnode/$(NODE_PREBUILT_IMAGE)/$(NODE_PREBUILT_BRANCH)-latest/sdcnode/
126 | ifeq ($(shell echo $(NODE_PREBUILT_DIR) | cut -c 1-4),http)
127 | NODE_PREBUILT_BASE := $(shell curl -ksS --fail --connect-timeout 30 $(NODE_PREBUILT_DIR) | grep 'href=' | cut -d'"' -f2 | grep "^$(NODE_PREBUILT_PATTERN)$$" | sort | tail -1)
128 | ifneq ($(NODE_PREBUILT_BASE),)
129 | NODE_PREBUILT_TARBALL := $(NODE_PREBUILT_DIR)$(NODE_PREBUILT_BASE)
130 | endif
131 | else
132 | NODE_PREBUILT_BASE := $(shell ls -1 $(NODE_PREBUILT_DIR)/ | grep "^$(NODE_PREBUILT_PATTERN)$$" 2>/dev/null | sort | tail -1)
133 | ifneq ($(NODE_PREBUILT_BASE),)
134 | NODE_PREBUILT_TARBALL := $(NODE_PREBUILT_DIR)/$(NODE_PREBUILT_BASE)
135 | endif
136 | endif
137 | ifeq ($(NODE_PREBUILT_TARBALL),)
138 | NODE_PREBUILT_TARBALL = $(error NODE_PREBUILT_TARBALL is empty: no '$(NODE_PREBUILT_DIR)/$(NODE_PREBUILT_PATTERN)' found)
139 | endif
140 |
141 |
142 | # Prebuild-specific paths for the "*_EXEC" vars to ensure that
143 | # a prebuild change (e.g. if master Makefile's NODE_PREBUILT_VERSION
144 | # choice changes) causes a install of the new node.
145 | NODE_EXEC := $(BUILD)/prebuilt-node-$(NODE_PREBUILT_NAME)
146 | NODE_WAF_EXEC := $(BUILD)/prebuilt-node-waf-$(NODE_PREBUILT_NAME)
147 | NPM_EXEC := $(BUILD)/prebuilt-npm-$(NODE_PREBUILT_NAME)
148 |
149 | # Ensure these use absolute paths to the executables to allow running
150 | # from a dir other than the project top.
151 | NODE := $(TOP)/$(NODE_INSTALL)/bin/node
152 | NODE_WAF := $(TOP)/$(NODE_INSTALL)/bin/node-waf
153 | NPM := PATH=$(TOP)/$(NODE_INSTALL)/bin:$(PATH) node $(TOP)/$(NODE_INSTALL)/bin/npm
154 |
--------------------------------------------------------------------------------
/tools/mk/Makefile.node_prebuilt.targ:
--------------------------------------------------------------------------------
1 | # -*- mode: makefile -*-
2 | #
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 | #
7 |
8 | #
9 | # Copyright (c) 2014, Joyent, Inc.
10 | #
11 |
12 | #
13 | # Makefile.node_prebuilt.targ: Makefile for including a prebuilt Node.js
14 | # build.
15 | #
16 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
17 | # into other repos as-is without requiring any modifications. If you find
18 | # yourself changing this file, you should instead update the original copy in
19 | # eng.git and then update your repo to use the new version.
20 |
21 |
22 | NODE_PREBUILT_TARBALL ?= $(error NODE_PREBUILT_TARBALL is not set: was Makefile.node_prebuilt.defs included?)
23 |
24 |
25 | # TODO: remove this limitation
26 | # Limitation: currently presuming that the NODE_INSTALL basename is
27 | # 'node' and that sdcnode tarballs have a 'node' top-level dir.
28 | $(NODE_EXEC) $(NPM_EXEC) $(NODE_WAF_EXEC):
29 | [[ $(shell basename $(NODE_INSTALL)) == "node" ]] \
30 | || (echo "Limitation: 'basename NODE_INSTALL' is not 'node'" && exit 1)
31 | rm -rf $(NODE_INSTALL) \
32 | $(BUILD)/prebuilt-node-* $(BUILD)/prebuilt-npm-*
33 | mkdir -p $(shell dirname $(NODE_INSTALL))
34 | if [[ $(shell echo $(NODE_PREBUILT_TARBALL) | cut -c 1-4) == "http" ]]; then \
35 | echo "Downloading '$(NODE_PREBUILT_BASE)'."; \
36 | curl -ksS --fail --connect-timeout 30 -o $(shell dirname $(NODE_INSTALL))/$(NODE_PREBUILT_BASE) $(NODE_PREBUILT_TARBALL); \
37 | (cd $(shell dirname $(NODE_INSTALL)) && $(TAR) xf $(NODE_PREBUILT_BASE)); \
38 | else \
39 | (cd $(shell dirname $(NODE_INSTALL)) && $(TAR) xf $(NODE_PREBUILT_TARBALL)); \
40 | fi
41 | ln -s $(TOP)/$(NODE_INSTALL)/bin/node $(NODE_EXEC)
42 | ln -s $(TOP)/$(NODE_INSTALL)/bin/npm $(NPM_EXEC)
43 |
--------------------------------------------------------------------------------
/tools/mk/Makefile.targ:
--------------------------------------------------------------------------------
1 | # -*- mode: makefile -*-
2 | #
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 | #
7 |
8 | #
9 | # Copyright (c) 2014, Joyent, Inc.
10 | #
11 |
12 | #
13 | # Makefile.targ: common targets.
14 | #
15 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
16 | # into other repos as-is without requiring any modifications. If you find
17 | # yourself changing this file, you should instead update the original copy in
18 | # eng.git and then update your repo to use the new version.
19 | #
20 | # This Makefile defines several useful targets and rules. You can use it by
21 | # including it from a Makefile that specifies some of the variables below.
22 | #
23 | # Targets defined in this Makefile:
24 | #
25 | # check Checks JavaScript files for lint and style
26 | # Checks bash scripts for syntax
27 | # Checks SMF manifests for validity against the SMF DTD
28 | #
29 | # clean Removes built files
30 | #
31 | # docs Builds restdown documentation in docs/
32 | #
33 | # prepush Depends on "check" and "test"
34 | #
35 | # test Does nothing (you should override this)
36 | #
37 | # xref Generates cscope (source cross-reference index)
38 | #
39 | # For details on what these targets are supposed to do, see the Joyent
40 | # Engineering Guide.
41 | #
42 | # To make use of these targets, you'll need to set some of these variables. Any
43 | # variables left unset will simply not be used.
44 | #
45 | # BASH_FILES Bash scripts to check for syntax
46 | # (paths relative to top-level Makefile)
47 | #
48 | # CLEAN_FILES Files to remove as part of the "clean" target. Note
49 | # that files generated by targets in this Makefile are
50 | # automatically included in CLEAN_FILES. These include
51 | # restdown-generated HTML and JSON files.
52 | #
53 | # DOC_FILES Restdown (documentation source) files. These are
54 | # assumed to be contained in "docs/", and must NOT
55 | # contain the "docs/" prefix.
56 | #
57 | # JSL_CONF_NODE Specify JavaScriptLint configuration files
58 | # JSL_CONF_WEB (paths relative to top-level Makefile)
59 | #
60 | # Node.js and Web configuration files are separate
61 | # because you'll usually want different global variable
62 | # configurations. If no file is specified, none is given
63 | # to jsl, which causes it to use a default configuration,
64 | # which probably isn't what you want.
65 | #
66 | # JSL_FILES_NODE JavaScript files to check with Node config file.
67 | # JSL_FILES_WEB JavaScript files to check with Web config file.
68 | #
69 | # JSON_FILES JSON files to be validated
70 | #
71 | # JSSTYLE_FILES JavaScript files to be style-checked
72 | #
73 | # You can also override these variables:
74 | #
75 | # BASH Path to bash (default: "bash")
76 | #
77 | # CSCOPE_DIRS Directories to search for source files for the cscope
78 | # index. (default: ".")
79 | #
80 | # JSL Path to JavaScriptLint (default: "jsl")
81 | #
82 | # JSL_FLAGS_NODE Additional flags to pass through to JSL
83 | # JSL_FLAGS_WEB
84 | # JSL_FLAGS
85 | #
86 | # JSON Path to json tool (default: "json")
87 | #
88 | # JSSTYLE Path to jsstyle (default: "jsstyle")
89 | #
90 | # JSSTYLE_FLAGS Additional flags to pass through to jsstyle
91 | #
92 | # RESTDOWN_EXT By default '.md' is required for DOC_FILES (see above).
93 | # If you want to use, say, '.restdown' instead, then set
94 | # 'RESTDOWN_EXT=.restdown' in your Makefile.
95 | #
96 |
97 | #
98 | # Defaults for the various tools we use.
99 | #
100 | BASH ?= bash
101 | BASHSTYLE ?= tools/bashstyle
102 | CP ?= cp
103 | CSCOPE ?= cscope
104 | CSCOPE_DIRS ?= .
105 | JSL ?= jsl
106 | JSON ?= json
107 | JSSTYLE ?= jsstyle
108 | MKDIR ?= mkdir -p
109 | MV ?= mv
110 | RESTDOWN_FLAGS ?=
111 | RESTDOWN_EXT ?= .md
112 | RMTREE ?= rm -rf
113 | JSL_FLAGS ?= --nologo --nosummary
114 |
115 | ifeq ($(shell uname -s),SunOS)
116 | TAR ?= gtar
117 | else
118 | TAR ?= tar
119 | endif
120 |
121 |
122 | #
123 | # Defaults for other fixed values.
124 | #
125 | BUILD = build
126 | DISTCLEAN_FILES += $(BUILD)
127 | DOC_BUILD = $(BUILD)/docs/public
128 |
129 | #
130 | # Configure JSL_FLAGS_{NODE,WEB} based on JSL_CONF_{NODE,WEB}.
131 | #
132 | ifneq ($(origin JSL_CONF_NODE), undefined)
133 | JSL_FLAGS_NODE += --conf=$(JSL_CONF_NODE)
134 | endif
135 |
136 | ifneq ($(origin JSL_CONF_WEB), undefined)
137 | JSL_FLAGS_WEB += --conf=$(JSL_CONF_WEB)
138 | endif
139 |
140 | #
141 | # Targets. For descriptions on what these are supposed to do, see the
142 | # Joyent Engineering Guide.
143 | #
144 |
145 | #
146 | # Instruct make to keep around temporary files. We have rules below that
147 | # automatically update git submodules as needed, but they employ a deps/*/.git
148 | # temporary file. Without this directive, make tries to remove these .git
149 | # directories after the build has completed.
150 | #
151 | .SECONDARY: $($(wildcard deps/*):%=%/.git)
152 |
153 | #
154 | # This rule enables other rules that use files from a git submodule to have
155 | # those files depend on deps/module/.git and have "make" automatically check
156 | # out the submodule as needed.
157 | #
158 | deps/%/.git:
159 | git submodule update --init deps/$*
160 |
161 | #
162 | # These recipes make heavy use of dynamically-created phony targets. The parent
163 | # Makefile defines a list of input files like BASH_FILES. We then say that each
164 | # of these files depends on a fake target called filename.bashchk, and then we
165 | # define a pattern rule for those targets that runs bash in check-syntax-only
166 | # mode. This mechanism has the nice properties that if you specify zero files,
167 | # the rule becomes a noop (unlike a single rule to check all bash files, which
168 | # would invoke bash with zero files), and you can check individual files from
169 | # the command line with "make filename.bashchk".
170 | #
171 | .PHONY: check-bash
172 | check-bash: $(BASH_FILES:%=%.bashchk) $(BASH_FILES:%=%.bashstyle)
173 |
174 | %.bashchk: %
175 | $(BASH) -n $^
176 |
177 | %.bashstyle: %
178 | $(BASHSTYLE) $^
179 |
180 | .PHONY: check-json
181 | check-json: $(JSON_EXEC) $(JSON_FILES:%=%.jsonchk)
182 |
183 | %.jsonchk: %
184 | $(JSON) --validate -f $^
185 |
186 | #
187 | # The above approach can be slow when there are many files to check because it
188 | # requires that "make" invoke the check tool once for each file, rather than
189 | # passing in several files at once. For the JavaScript check targets, we define
190 | # a variable for the target itself *only if* the list of input files is
191 | # non-empty. This avoids invoking the tool if there are no files to check.
192 | #
193 | JSL_NODE_TARGET = $(if $(JSL_FILES_NODE), check-jsl-node)
194 | .PHONY: check-jsl-node
195 | check-jsl-node: $(JSL_EXEC)
196 | $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_NODE) $(JSL_FILES_NODE)
197 |
198 | JSL_WEB_TARGET = $(if $(JSL_FILES_WEB), check-jsl-web)
199 | .PHONY: check-jsl-web
200 | check-jsl-web: $(JSL_EXEC)
201 | $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_WEB) $(JSL_FILES_WEB)
202 |
203 | .PHONY: check-jsl
204 | check-jsl: $(JSL_NODE_TARGET) $(JSL_WEB_TARGET)
205 |
206 | JSSTYLE_TARGET = $(if $(JSSTYLE_FILES), check-jsstyle)
207 | .PHONY: check-jsstyle
208 | check-jsstyle: $(JSSTYLE_EXEC)
209 | $(JSSTYLE) $(JSSTYLE_FLAGS) $(JSSTYLE_FILES)
210 |
211 | .PHONY: check
212 | check:: check-jsl check-json $(JSSTYLE_TARGET) check-bash
213 | @echo check ok
214 |
215 | .PHONY: clean
216 | clean::
217 | -$(RMTREE) $(CLEAN_FILES)
218 |
219 | .PHONY: distclean
220 | distclean:: clean
221 | -$(RMTREE) $(DISTCLEAN_FILES)
222 |
223 | CSCOPE_FILES = cscope.in.out cscope.out cscope.po.out
224 | CLEAN_FILES += $(CSCOPE_FILES)
225 |
226 | .PHONY: xref
227 | xref: cscope.files
228 | $(CSCOPE) -bqR
229 |
230 | .PHONY: cscope.files
231 | cscope.files:
232 | find $(CSCOPE_DIRS) -name '*.c' -o -name '*.h' -o -name '*.cc' \
233 | -o -name '*.js' -o -name '*.s' -o -name '*.cpp' > $@
234 |
235 | #
236 | # The "docs" target is complicated because we do several things here:
237 | #
238 | # (1) Use restdown to build HTML and JSON files from each of DOC_FILES.
239 | #
240 | # (2) Copy these files into $(DOC_BUILD) (build/docs/public), which
241 | # functions as a complete copy of the documentation that could be
242 | # mirrored or served over HTTP.
243 | #
244 | # (3) Then copy any directories and media from docs/media into
245 | # $(DOC_BUILD)/media. This allows projects to include their own media,
246 | # including files that will override same-named files provided by
247 | # restdown.
248 | #
249 | # Step (3) is the surprisingly complex part: in order to do this, we need to
250 | # identify the subdirectories in docs/media, recreate them in
251 | # $(DOC_BUILD)/media, then do the same with the files.
252 | #
253 | DOC_MEDIA_DIRS := $(shell find docs/media -type d 2>/dev/null | grep -v "^docs/media$$")
254 | DOC_MEDIA_DIRS := $(DOC_MEDIA_DIRS:docs/media/%=%)
255 | DOC_MEDIA_DIRS_BUILD := $(DOC_MEDIA_DIRS:%=$(DOC_BUILD)/media/%)
256 |
257 | DOC_MEDIA_FILES := $(shell find docs/media -type f 2>/dev/null)
258 | DOC_MEDIA_FILES := $(DOC_MEDIA_FILES:docs/media/%=%)
259 | DOC_MEDIA_FILES_BUILD := $(DOC_MEDIA_FILES:%=$(DOC_BUILD)/media/%)
260 |
261 | #
262 | # Like the other targets, "docs" just depends on the final files we want to
263 | # create in $(DOC_BUILD), leveraging other targets and recipes to define how
264 | # to get there.
265 | #
266 | .PHONY: docs
267 | docs:: \
268 | $(DOC_FILES:%$(RESTDOWN_EXT)=$(DOC_BUILD)/%.html) \
269 | $(DOC_FILES:%$(RESTDOWN_EXT)=$(DOC_BUILD)/%.json) \
270 | $(DOC_MEDIA_FILES_BUILD)
271 |
272 | #
273 | # We keep the intermediate files so that the next build can see whether the
274 | # files in DOC_BUILD are up to date.
275 | #
276 | .PRECIOUS: \
277 | $(DOC_FILES:%$(RESTDOWN_EXT)=docs/%.html) \
278 | $(DOC_FILES:%$(RESTDOWN_EXT)=docs/%json)
279 |
280 | #
281 | # We do clean those intermediate files, as well as all of DOC_BUILD.
282 | #
283 | CLEAN_FILES += \
284 | $(DOC_BUILD) \
285 | $(DOC_FILES:%$(RESTDOWN_EXT)=docs/%.html) \
286 | $(DOC_FILES:%$(RESTDOWN_EXT)=docs/%.json)
287 |
288 | #
289 | # Before installing the files, we must make sure the directories exist. The |
290 | # syntax tells make that the dependency need only exist, not be up to date.
291 | # Otherwise, it might try to rebuild spuriously because the directory itself
292 | # appears out of date.
293 | #
294 | $(DOC_MEDIA_FILES_BUILD): | $(DOC_MEDIA_DIRS_BUILD)
295 |
296 | $(DOC_BUILD)/%: docs/% | $(DOC_BUILD)
297 | $(CP) $< $@
298 |
299 | docs/%.json docs/%.html: docs/%$(RESTDOWN_EXT) | $(DOC_BUILD) $(RESTDOWN_EXEC) \
300 | $(EXTRA_DOC_DEPS)
301 | $(RESTDOWN) $(RESTDOWN_FLAGS) -m $(DOC_BUILD) $<
302 |
303 | $(DOC_BUILD):
304 | $(MKDIR) $@
305 |
306 | $(DOC_MEDIA_DIRS_BUILD):
307 | $(MKDIR) $@
308 |
309 | #
310 | # The default "test" target does nothing. This should usually be overridden by
311 | # the parent Makefile. It's included here so we can define "prepush" without
312 | # requiring the repo to define "test".
313 | #
314 | .PHONY: test
315 | test:
316 |
317 | .PHONY: prepush
318 | prepush: check test
319 |
--------------------------------------------------------------------------------