mirror of
https://github.com/KevinMidboe/linguist.git
synced 2025-10-29 17:50:22 +00:00
111 lines
2.8 KiB
CoffeeScript
111 lines
2.8 KiB
CoffeeScript
dnsserver = require "dnsserver"
|
|
|
|
exports.Server = class Server extends dnsserver.Server
|
|
NS_T_A = 1
|
|
NS_T_NS = 2
|
|
NS_T_CNAME = 5
|
|
NS_T_SOA = 6
|
|
NS_C_IN = 1
|
|
NS_RCODE_NXDOMAIN = 3
|
|
|
|
constructor: (domain, @rootAddress) ->
|
|
super
|
|
@domain = domain.toLowerCase()
|
|
@soa = createSOA @domain
|
|
@on "request", @handleRequest
|
|
|
|
handleRequest: (req, res) =>
|
|
question = req.question
|
|
subdomain = @extractSubdomain question.name
|
|
|
|
if subdomain? and isARequest question
|
|
res.addRR question.name, NS_T_A, NS_C_IN, 600, subdomain.getAddress()
|
|
else if subdomain?.isEmpty() and isNSRequest question
|
|
res.addRR question.name, NS_T_SOA, NS_C_IN, 600, @soa, true
|
|
else
|
|
res.header.rcode = NS_RCODE_NXDOMAIN
|
|
|
|
res.send()
|
|
|
|
extractSubdomain: (name) ->
|
|
Subdomain.extract name, @domain, @rootAddress
|
|
|
|
isARequest = (question) ->
|
|
question.type is NS_T_A and question.class is NS_C_IN
|
|
|
|
isNSRequest = (question) ->
|
|
question.type is NS_T_NS and question.class is NS_C_IN
|
|
|
|
createSOA = (domain) ->
|
|
mname = "ns-1.#{domain}"
|
|
rname = "hostmaster.#{domain}"
|
|
serial = parseInt new Date().getTime() / 1000
|
|
refresh = 28800
|
|
retry = 7200
|
|
expire = 604800
|
|
minimum = 3600
|
|
dnsserver.createSOA mname, rname, serial, refresh, retry, expire, minimum
|
|
|
|
exports.createServer = (domain, address = "127.0.0.1") ->
|
|
new Server domain, address
|
|
|
|
exports.Subdomain = class Subdomain
|
|
@extract: (name, domain, address) ->
|
|
return unless name
|
|
name = name.toLowerCase()
|
|
offset = name.length - domain.length
|
|
|
|
if domain is name.slice offset
|
|
subdomain = if 0 >= offset then null else name.slice 0, offset - 1
|
|
new constructor subdomain, address if constructor = @for subdomain
|
|
|
|
@for: (subdomain = "") ->
|
|
if IPAddressSubdomain.pattern.test subdomain
|
|
IPAddressSubdomain
|
|
else if EncodedSubdomain.pattern.test subdomain
|
|
EncodedSubdomain
|
|
else
|
|
Subdomain
|
|
|
|
constructor: (@subdomain, @address) ->
|
|
@labels = subdomain?.split(".") ? []
|
|
@length = @labels.length
|
|
|
|
isEmpty: ->
|
|
@length is 0
|
|
|
|
getAddress: ->
|
|
@address
|
|
|
|
class IPAddressSubdomain extends Subdomain
|
|
@pattern = /// (^|\.)
|
|
((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
|
|
(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)
|
|
$ ///
|
|
|
|
getAddress: ->
|
|
@labels.slice(-4).join "."
|
|
|
|
class EncodedSubdomain extends Subdomain
|
|
@pattern = /(^|\.)[a-z0-9]{1,7}$/
|
|
|
|
getAddress: ->
|
|
decode @labels[@length - 1]
|
|
|
|
exports.encode = encode = (ip) ->
|
|
value = 0
|
|
for byte, index in ip.split "."
|
|
value += parseInt(byte, 10) << (index * 8)
|
|
(value >>> 0).toString 36
|
|
|
|
PATTERN = /^[a-z0-9]{1,7}$/
|
|
|
|
exports.decode = decode = (string) ->
|
|
return unless PATTERN.test string
|
|
value = parseInt string, 36
|
|
ip = []
|
|
for i in [1..4]
|
|
ip.push value & 0xFF
|
|
value >>= 8
|
|
ip.join "."
|