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 "."
 |