feature: vor und nachteile

This commit is contained in:
Hikyu
2026-05-31 13:54:31 +02:00
parent 552a947a1c
commit e3dad17446
23270 changed files with 3024794 additions and 200 deletions
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Mathias Buus
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+77
View File
@@ -0,0 +1,77 @@
# dns-socket
[![](https://img.shields.io/npm/v/dns-socket.svg?style=flat)](https://www.npmjs.org/package/dns-socket) [![](https://img.shields.io/npm/dm/dns-socket.svg)](https://www.npmjs.org/package/dns-socket) [![](https://api.travis-ci.org/mafintosh/dns-socket.svg?style=flat)](https://travis-ci.org/mafintosh/dns-socket)
Make low-level DNS requests with retry and timeout support.
```
npm install dns-socket
```
## Usage
``` js
const dnsSocket = require('dns-socket')
const socket = dnsSocket()
socket.query({
questions: [{
type: 'A',
name: 'google.com'
}]
}, 53, '8.8.8.8', (err, res) => {
console.log(err, res) // prints the A record for google.com
})
```
## API
#### `var socket = dns([options])`
Create a new DNS socket instance. The `options` object includes:
- `retries` *Number*: Number of total query attempts made during `timeout`. Default: 5.
- `socket` *Object*: A custom dgram socket. Default: A `'udp4'` socket.
- `timeout` *Number*: Total timeout in milliseconds after which a `'timeout'` event is emitted. Default: 7500.
- `maxQueries` *Number*: Each request has an id, this is stored as static sized array. maxQueries is the size of this array, limiting the max number of inflight requests. Default: 10000.
- `maxRedirects` *Number*: If you query for a single `A` record and get back `CNAME`, the lib will try to follow the chain and resolve the `CNAME` to A. The maximum number of steps is defined by the `maxRedirects`. Default: 0
- `timeoutChecks` *Number*: Timeouts are checked each `timeoutChecks` ms, for large number of parallel request, you might want to increase this number. Default: `timeout` / 10
#### `socket.on('query', query, port, host)`
Emitted when a dns query is received. The query is a [dns-packet](https://github.com/mafintosh/dns-packet)
#### `socket.on('response', response, port, host)`
Emitted when a dns response is received. The response is a [dns-packet](https://github.com/mafintosh/dns-packet)
#### `var id = socket.query(query, port, [host], [callback])`
Send a dns query. If host is omitted it defaults to `127.0.0.1`. When the remote replies the callback is called with `(err, response, query)` and an response is emitted as well. If the query times out the callback is called with an error.
The `host` parameter can be an array, during resolve the lib will randomly select one host.
Returns the query id
#### `socket.response(query, response, port, [host])`
Send a response to a query.
#### `socket.cancel(id)`
Cancel a query
#### `socket.bind([port][, address][, onlistening])`
#### `socket.bind(options, [onlistening])`
Bind the underlying udp socket to a specific port. Takes the same arguments as [socket#bind](https://nodejs.org/docs/latest/api/dgram.html#dgram_socket_bind_port_address_callback).
#### `socket.destroy([onclose])`
Destroy the socket.
#### `socket.inflight`
Number of inflight queries.
## License
MIT
+287
View File
@@ -0,0 +1,287 @@
'use strict'
const dgram = require('dgram')
const util = require('util')
const packet = require('dns-packet')
const events = require('events')
module.exports = DNS
function DNS (opts) {
if (!(this instanceof DNS)) {
return new DNS(opts)
}
if (!opts) {
opts = {}
}
events.EventEmitter.call(this)
const self = this
this.retries = opts.retries !== undefined ? opts.retries : 5
this.timeout = opts.timeout || 7500
this.timeoutChecks = opts.timeoutChecks || (this.timeout / 10)
this.destroyed = false
this.inflight = 0
this.maxQueries = opts.maxQueries || 10000
this.maxRedirects = opts.maxRedirects || 0
this.socket = opts.socket || dgram.createSocket('udp4')
this._id = Math.ceil(Math.random() * this.maxQueries)
this._queries = new Array(this.maxQueries).fill(null)
this._interval = null
this.socket.on('error', onerror)
this.socket.on('message', onmessage)
if (isListening(this.socket)) onlistening()
else this.socket.on('listening', onlistening)
this.socket.on('close', onclose)
function onerror (err) {
if (err.code === 'EACCES' || err.code === 'EADDRINUSE') {
self.emit('error', err)
} else {
self.emit('warning', err)
}
}
function onmessage (message, rinfo) {
self._onmessage(message, rinfo)
}
function ontimeoutCheck () {
self._ontimeoutCheck()
}
function onlistening () {
self._interval = setInterval(ontimeoutCheck, self.timeoutChecks)
self.emit('listening')
}
function onclose () {
self.emit('close')
}
}
util.inherits(DNS, events.EventEmitter)
DNS.RECURSION_DESIRED = DNS.prototype.RECURSION_DESIRED = packet.RECURSION_DESIRED
DNS.RECURSION_AVAILABLE = DNS.prototype.RECURSION_AVAILABLE = packet.RECURSION_AVAILABLE
DNS.TRUNCATED_RESPONSE = DNS.prototype.TRUNCATED_RESPONSE = packet.TRUNCATED_RESPONSE
DNS.AUTHORITATIVE_ANSWER = DNS.prototype.AUTHORITATIVE_ANSWER = packet.AUTHORITATIVE_ANSWER
DNS.AUTHENTIC_DATA = DNS.prototype.AUTHENTIC_DATA = packet.AUTHENTIC_DATA
DNS.CHECKING_DISABLED = DNS.prototype.CHECKING_DISABLED = packet.CHECKING_DISABLED
DNS.prototype.address = function () {
return this.socket.address()
}
DNS.prototype.bind = function (...args) {
const onlistening = args.length > 0 && args[args.length - 1]
if (typeof onlistening === 'function') {
this.once('listening', onlistening)
this.socket.bind(...args.slice(0, -1))
} else {
this.socket.bind(...args)
}
}
DNS.prototype.destroy = function (onclose) {
if (onclose) {
this.once('close', onclose)
}
if (this.destroyed) {
return
}
this.destroyed = true
clearInterval(this._interval)
this.socket.close()
for (let i = 0; i < this.maxQueries; i++) {
const q = this._queries[i]
if (q) {
q.callback(new Error('Socket destroyed'))
this._queries[i] = null
}
}
this.inflight = 0
}
DNS.prototype._ontimeoutCheck = function () {
const now = Date.now()
for (let i = 0; i < this.maxQueries; i++) {
const q = this._queries[i]
if ((!q) || (now - q.firstTry < (q.tries + 1) * this.timeout)) {
continue
}
if (q.tries > this.retries) {
this._queries[i] = null
this.inflight--
this.emit('timeout', q.query, q.port, q.host)
q.callback(new Error('Query timed out'))
continue
}
q.tries++
this.socket.send(q.buffer, 0, q.buffer.length, q.port, Array.isArray(q.host) ? q.host[Math.floor(q.host.length * Math.random())] : q.host || '127.0.0.1')
}
}
DNS.prototype._shouldRedirect = function (q, result) {
// no redirects, no query, more than 1 questions, has any A record answer
if (this.maxRedirects <= 0 || (!q) || (q.query.questions.length !== 1) || result.answers.filter(e => e.type === 'A').length > 0) {
return false
}
// no more redirects left
if (q.redirects > this.maxRedirects) {
return false
}
const cnameresults = result.answers.filter(e => e.type === 'CNAME')
if (cnameresults.length === 0) {
return false
}
const id = this._getNextEmptyId()
if (id === -1) {
q.callback(new Error('Query array is full!'))
return true
}
// replace current query with a new one
q.query = {
id: id + 1,
flags: packet.RECURSION_DESIRED,
questions: [{
type: 'A',
name: cnameresults[0].data
}]
}
q.redirects++
q.firstTry = Date.now()
q.tries = 0
q.buffer = packet.encode(q.query)
this._queries[id] = q
this.socket.send(q.buffer, 0, q.buffer.length, q.port, Array.isArray(q.host) ? q.host[Math.floor(q.host.length * Math.random())] : q.host || '127.0.0.1')
return true
}
DNS.prototype._onmessage = function (buffer, rinfo) {
let message
try {
message = packet.decode(buffer)
} catch (err) {
this.emit('warning', err)
return
}
if (message.type === 'response' && message.id) {
const q = this._queries[message.id - 1]
if (q) {
this._queries[message.id - 1] = null
this.inflight--
if (!this._shouldRedirect(q, message)) {
q.callback(null, message)
}
}
}
this.emit(message.type, message, rinfo.port, rinfo.address)
}
DNS.prototype.unref = function () {
this.socket.unref()
}
DNS.prototype.ref = function () {
this.socket.ref()
}
DNS.prototype.response = function (query, response, port, host) {
if (this.destroyed) {
return
}
response.type = 'response'
response.id = query.id
const buffer = packet.encode(response)
this.socket.send(buffer, 0, buffer.length, port, host)
}
DNS.prototype.cancel = function (id) {
const q = this._queries[id]
if (!q) return
this._queries[id] = null
this.inflight--
q.callback(new Error('Query cancelled'))
}
DNS.prototype.setRetries = function (id, retries) {
const q = this._queries[id]
if (!q) return
q.firstTry = q.firstTry - this.timeout * (retries - q.retries)
q.retries = this.retries - retries
}
DNS.prototype._getNextEmptyId = function () {
// try to find the next unused id
let id = -1
for (let idtries = this.maxQueries; idtries > 0; idtries--) {
const normalizedId = (this._id + idtries) % this.maxQueries
if (this._queries[normalizedId] === null) {
id = normalizedId
this._id = (normalizedId + 1) % this.maxQueries
break
}
}
return id
}
DNS.prototype.query = function (query, port, host, cb) {
if (this.destroyed) {
cb(new Error('Socket destroyed'))
return 0
}
this.inflight++
query.type = 'query'
query.flags = typeof query.flags === 'number' ? query.flags : DNS.RECURSION_DESIRED
const id = this._getNextEmptyId()
if (id === -1) {
cb(new Error('Query array is full!'))
return 0
}
query.id = id + 1
const buffer = packet.encode(query)
this._queries[id] = {
callback: cb || noop,
redirects: 0,
firstTry: Date.now(),
query: query,
tries: 0,
buffer: buffer,
port: port,
host: host
}
this.socket.send(buffer, 0, buffer.length, port, Array.isArray(host) ? host[Math.floor(host.length * Math.random())] : host || '127.0.0.1')
return id
}
function noop () {
}
function isListening (socket) {
try {
return socket.address().port !== 0
} catch (err) {
return false
}
}
+36
View File
@@ -0,0 +1,36 @@
{
"name": "dns-socket",
"version": "4.2.2",
"description": "Make low-level DNS requests with retry and timeout support.",
"author": "Mathias Buus (@mafintosh)",
"license": "MIT",
"repository": "mafintosh/dns-socket",
"homepage": "https://github.com/mafintosh/dns-socket",
"engines": {
"node": ">=6"
},
"scripts": {
"test": "eslint --color *.js && tape test.js"
},
"dependencies": {
"dns-packet": "^5.2.4"
},
"devDependencies": {
"eslint": "^5.12.1",
"eslint-config-standard": "^12.0.0",
"eslint-plugin-import": "^2.15.0",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-standard": "^4.0.0",
"tape": "^4.9.2"
},
"keywords": [
"dns",
"domain",
"socket",
"low-level"
],
"files": [
"index.js"
]
}