├── LICENSE.md ├── package.json ├── README.md ├── index.js └── test.js /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Emil Bay 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "is-domain-name", 3 | "version": "1.0.1", 4 | "description": "Validate Domain Names as outlined by RFC 2181", 5 | "main": "index.js", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "tape": "^4.6.0" 9 | }, 10 | "scripts": { 11 | "test": "node test.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/emilbayes/is-domain-name.git" 16 | }, 17 | "keywords": [ 18 | "is", 19 | "domain", 20 | "name", 21 | "rfc2181", 22 | "dns", 23 | "hostname", 24 | "host" 25 | ], 26 | "author": "Emil Bay ", 27 | "license": "ISC", 28 | "bugs": { 29 | "url": "https://github.com/emilbayes/is-domain-name/issues" 30 | }, 31 | "homepage": "https://github.com/emilbayes/is-domain-name#readme" 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `is-domain-name` 2 | 3 | > Validate Domain Names as outlined by RFC 2181 4 | 5 | 6 | ## Install 7 | 8 | ```sh 9 | npm install is-domain-name 10 | ``` 11 | 12 | ## Usage 13 | 14 | Domain names are composed of labels interleaved with a separator (`.`). 15 | Domain as allowed a total length of 255 chars including separators. 16 | Labels should be between 1 and 63 octets. 17 | 18 | Labels allows domains to form a hierarchy, with the right most label acting 19 | as the root. The leftmost label is often called the TLD (Top-Level Domain), 20 | the 2nd leftmost informally called the domain and any label to the right of 21 | those called subdomains. 22 | 23 | A common domain with only a single label is `localhost`. 24 | 25 | This module checks the following: 26 | 27 | * The domain is no longer than 255 chars 28 | * The domain is at least 2 chars. Even though `a` is technically allowed, this module does not allow it 29 | * Labels are between 1 and 63 chars 30 | * Labels start and end with alpha-numeric chars, allowing dashes in between 31 | * An optional root dot can be allowed with a flag, eg. `example.com.` 32 | 33 | ```js 34 | const assert = require('assert') 35 | const isDomainName = require('is-domain-name') 36 | 37 | assert.ok(isDomainName('localhost')) 38 | assert.notOk(isDomainName('-.-')) 39 | ``` 40 | 41 | ## API 42 | 43 | ### isDomainName(domainName, rootDot) 44 | #### domainName 45 | 46 | Type: `String` 47 | 48 | #### rootDot 49 | Type: `Boolean`
50 | Default: `false` 51 | 52 | Whether or not to validate for a trailing dot, signifying the root 53 | 54 | ## Related 55 | 56 | * [`is-http-url`](http://github.com/emilbayes/is-http-url) 57 | 58 | ## License 59 | 60 | [ISC](LICENSE.md) 61 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // Adapted from RFC 2181 (See section 11) 4 | // 5 | // The RFC defines a Domain Name to be at most 255 chars including seperators 6 | // between labels. 7 | // Subdomains are labels plus a seperator. A label is a string starting and 8 | // ending with an alphanum, with dashes allowed in the middle. It is between 1 and 63 chars. 9 | // 10 | // We make some assumptions below that go beyond the RFC. We assume the TLD 11 | // to be at least 2 chars. The regex also allows domains longer than 255 chars, 12 | // but allows at most 127 subdomains, as assuming a lower-bound of one char per 13 | // subdomain will yield 252, and adding a TLD of 2 chars sums to 254. 14 | /* 15 | (?: // Group 1: This is for subdomain, which is composed of a label and a seperator (length = [1, 63] + 1) 16 | [a-z0-9] 17 | (?: 18 | [a-z0-9\-]{0,61} // Limited to 61 chars as we have at least two chars if we reach this group (61 + 2 = 63 which is the limit) 19 | [a-z0-9] 20 | )? 21 | \. // Label seperator 22 | ){0,126} // If we assume the lower-bound of 1 char labels, we can at most have 126 groups before approaching the total limit of 255 chars 23 | (?: // Group 2: Assume that the TLD is at least 2 chars (for sanity) 24 | [a-z0-9] 25 | [a-z0-9\-]{0,61} 26 | [a-z0-9] 27 | ) 28 | \.? // Some consider a trailing dot to be considered valid as it signifies the root of the domain tree 29 | */ 30 | var domainNameRegex = /^(?:[a-z0-9](?:[a-z0-9\-]{0,61}[a-z0-9])?\.){0,126}(?:[a-z0-9](?:[a-z0-9\-]{0,61}[a-z0-9]))\.?$/i 31 | 32 | /** 33 | * Test whether a string is a valid domain name, optionally checking for a root dot as well 34 | * @param {String} domainName 35 | * @param {Boolean} rootDot Check for a root dot eg. 'example.com.'. Defaults to false 36 | * @return {Boolean} 37 | */ 38 | module.exports = function isDomainName (domainName, rootDot) { 39 | if (rootDot == null) rootDot = false 40 | 41 | if (domainName.length < 2) return false 42 | if (domainName.length > 255) return false 43 | 44 | var lastChar = domainName[domainName.length - 1] 45 | if (rootDot) { 46 | if (lastChar !== '.') return false 47 | } else { 48 | if (lastChar === '.') return false 49 | } 50 | 51 | return domainNameRegex.test(domainName) 52 | } 53 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const test = require('tape') 4 | const puny = require('punycode') 5 | const is = require('.') 6 | 7 | test('invalid urls', function (assert) { 8 | assert.notOk(is(''), 'empty domain') 9 | assert.notOk(is(' ', 'whitespace domain')) 10 | assert.notOk(is('.d'), 'too short TLD') 11 | assert.notOk(is('.com', 'empty label')) 12 | assert.notOk(is('this-is-a-long-label-which-is-exactly-064-chars-long-and-invalid.com'), 'too long label') 13 | assert.notOk(is('some-very-very-very-very.looooooooooooooooooooooong-domain-name.thats-invalid-as-its-longer-than-256-chars.thats-also-very-very-very-nested-in-a-lot-of-subsubsubdomains.another-long-subdomain.gee-this-it-department-should-really-learn-some-ux.oh-noes-not-another-one.example.com'), 'too long domain name') 14 | assert.notOk(is('a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.eu'), 'too many labels') 15 | 16 | assert.notOk(is('-a.com'), 'label starts with dash') 17 | assert.notOk(is('a-.com'), 'label ends with dash') 18 | assert.notOk(is('-a-.com'), 'label surrounded with dashes') 19 | 20 | assert.notOk(is('.-a'), 'TLD starts with dash') 21 | assert.notOk(is('.a-'), 'TLD ends with dash') 22 | assert.notOk(is('.-a-'), 'TLD surrounded with dashes') 23 | 24 | assert.notOk(is('_yapper._gmail.google.com'), 'invalid chars') 25 | assert.notOk(is('æøå.xyz'), 'should be puny coded') 26 | 27 | assert.notOk(is('example.com.', false), 'root dot not allowed') 28 | assert.notOk(is('example.com', true), 'root dot missing') 29 | 30 | assert.end() 31 | }) 32 | 33 | test('valid urls', function (assert) { 34 | assert.ok(is('localhost'), 'single label') 35 | assert.ok(is('example.com'), 'simple domain') 36 | assert.ok(is('example.technology'), 'long TLD') 37 | assert.ok(is('this-is-a-long-label-which-is-exactly-63-chars-long-and-invalid.com'), 'longest possible label') 38 | assert.ok(is('some-very-very-very-very-long-domain-name.thats-valid-as-its-shorter-than-256-chars.thats-also-very-very-very-nested-in-a-lot-of-subsubsubdomains.another-long-subdomain.gee-this-it-department-should-really-learn-some-ux.oh-noes-not-another-one.example.com'), 'longest possible domain name') 39 | assert.ok(is('a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.eu'), 'label limit') 40 | 41 | assert.ok(is('some-example.com'), 'label contains dash') 42 | assert.ok(is('123.256'), 'numeric labels') 43 | assert.ok(is('hello-4-all.xyz'), 'mixed char domain') 44 | assert.ok(is(puny.toASCII('æøå.xyz')), 'puny coded') 45 | 46 | assert.ok(is('example.com.', true), 'root dot required') 47 | assert.ok(is('example.com', false), 'root dot disallowed') 48 | 49 | assert.end() 50 | }) 51 | --------------------------------------------------------------------------------