2025-05-30 19:49:34 +00:00
2022-07-27 08:59:29 +02:00
2025-05-30 19:27:37 +00:00
2025-05-30 19:27:37 +00:00
2020-08-05 15:37:51 +00:00
2025-03-21 18:21:47 +00:00
2025-05-30 19:49:34 +00:00
2024-04-01 21:34:39 +02:00

@push.rocks/smartdns

A robust TypeScript library providing advanced DNS management and resolution capabilities including support for DNSSEC, custom DNS servers, and integration with various DNS providers.

Install

To install @push.rocks/smartdns, use the following command with pnpm:

pnpm install @push.rocks/smartdns --save

Or with npm:

npm install @push.rocks/smartdns --save

Make sure you have a TypeScript environment set up to utilize the library effectively.

Usage

@push.rocks/smartdns is a comprehensive library that provides both DNS client and server capabilities, leveraging TypeScript for enhanced development experience. The library is organized into three modules:

  • Client Module (@push.rocks/smartdns/client): DNS resolution and record queries
  • Server Module (@push.rocks/smartdns/server): Full DNS server implementation with DNSSEC
  • Main Module (@push.rocks/smartdns): Convenience exports for both client and server

Getting Started

You can import the modules based on your needs:

// For DNS client operations
import { Smartdns } from '@push.rocks/smartdns/client';

// For DNS server operations
import { DnsServer } from '@push.rocks/smartdns/server';

// Or import from the main module (note the different syntax)
import { dnsClientMod, dnsServerMod } from '@push.rocks/smartdns';
const dnsClient = new dnsClientMod.Smartdns({});
const dnsServer = new dnsServerMod.DnsServer({ /* options */ });

DNS Client Operations

The DNS client (Smartdns class) provides methods to query various DNS record types using DNS-over-HTTPS (DoH) with Cloudflare as the primary provider, with fallback to Node.js DNS resolver.

Fetching A Records

To fetch "A" records (IPv4 addresses) for a domain:

import { Smartdns } from '@push.rocks/smartdns/client';

const dnsClient = new Smartdns({});
const aRecords = await dnsClient.getRecordsA('example.com');
console.log(aRecords);
// Output: [{ name: 'example.com', type: 'A', dnsSecEnabled: false, value: '93.184.215.14' }]

Fetching AAAA Records

For resolving a domain to IPv6 addresses:

const aaaaRecords = await dnsClient.getRecordsAAAA('example.com');
console.log(aaaaRecords);
// Output: [{ name: 'example.com', type: 'AAAA', dnsSecEnabled: false, value: '2606:2800:21f:cb07:6820:80da:af6b:8b2c' }]

Fetching TXT Records

TXT records store text data, commonly used for domain verification, SPF records, and other metadata:

const txtRecords = await dnsClient.getRecordsTxt('example.com');
console.log(txtRecords);
// Output: [{ name: 'example.com', type: 'TXT', dnsSecEnabled: false, value: 'v=spf1 -all' }]

Other Record Types

The client supports various other DNS record types:

// MX records for mail servers
const mxRecords = await dnsClient.getRecords('example.com', 'MX');

// NS records for nameservers
const nsRecords = await dnsClient.getNameServers('example.com');

// Generic query method with retry support
const records = await dnsClient.getRecords('example.com', 'CNAME', { retryCount: 3 });

Advanced DNS Features

Checking DNS Propagation

The client provides a powerful method to verify DNS propagation globally, essential when making DNS changes:

// Check if a specific DNS record has propagated
const recordType = 'TXT';
const expectedValue = 'verification=abc123';

const isAvailable = await dnsClient.checkUntilAvailable(
  'example.com',
  recordType,
  expectedValue,
  50,    // Number of check cycles (default: 50)
  500    // Interval between checks in ms (default: 500)
);

if (isAvailable) {
  console.log('DNS record has propagated successfully!');
} else {
  console.log('DNS propagation timeout - record not found.');
}

Configuring System DNS Provider

You can configure Node.js to use a specific DNS provider for all DNS queries:

// Import the standalone function
import { makeNodeProcessUseDnsProvider } from '@push.rocks/smartdns/client';

// Use Cloudflare DNS for all Node.js DNS operations
makeNodeProcessUseDnsProvider('cloudflare');

// Or use Google DNS
makeNodeProcessUseDnsProvider('google');

Real-World Use Cases

DNS-Based Feature Flagging

Use TXT records for dynamic feature toggles without redeployment:

const txtRecords = await dnsClient.getRecordsTxt('features.example.com');
const featureFlags = {};

txtRecords.forEach(record => {
  // Parse TXT records like "feature-dark-mode=true"
  const [feature, enabled] = record.value.split('=');
  featureFlags[feature] = enabled === 'true';
});

if (featureFlags['feature-dark-mode']) {
  console.log('Dark mode is enabled!');
}

Service Discovery

Use DNS for service endpoint discovery:

// Discover API endpoints via TXT records
const serviceRecords = await dnsClient.getRecordsTxt('_services.example.com');

// Discover mail servers
const mxRecords = await dnsClient.getRecords('example.com', 'MX');
const primaryMailServer = mxRecords
  .sort((a, b) => a.priority - b.priority)[0]?.exchange;

DNS Server Implementation

The DnsServer class provides a full-featured DNS server with support for UDP, DNS-over-HTTPS (DoH), DNSSEC, and automatic SSL certificate management via Let's Encrypt.

Basic DNS Server Setup

Create a simple DNS server that responds to queries:

import { DnsServer } from '@push.rocks/smartdns/server';

const dnsServer = new DnsServer({
  udpPort: 5333,      // UDP port for DNS queries
  httpsPort: 8443,    // HTTPS port for DNS-over-HTTPS
  httpsKey: 'path/to/key.pem',   // Required for HTTPS
  httpsCert: 'path/to/cert.pem', // Required for HTTPS
  dnssecZone: 'example.com'      // Optional: enable DNSSEC for this zone
});

// For enhanced security, bind to specific interfaces
const secureServer = new DnsServer({
  udpPort: 53,
  httpsPort: 443,
  httpsKey: 'path/to/key.pem',
  httpsCert: 'path/to/cert.pem',
  dnssecZone: 'example.com',
  udpBindInterface: '127.0.0.1',    // Bind UDP to localhost only
  httpsBindInterface: '127.0.0.1',  // Bind HTTPS to localhost only
  primaryNameserver: 'ns1.example.com' // Optional: primary nameserver for SOA records (defaults to ns1.{dnssecZone})
});

// Register a handler for all subdomains of example.com
dnsServer.registerHandler('*.example.com', ['A'], (question) => ({
  name: question.name,
  type: 'A',
  class: 'IN',
  ttl: 300,
  data: '192.168.1.100',
}));

// Register a handler for TXT records
dnsServer.registerHandler('example.com', ['TXT'], (question) => ({
  name: question.name,
  type: 'TXT',
  class: 'IN',
  ttl: 300,
  data: 'v=spf1 include:_spf.example.com ~all',
}));

// Start the server
await dnsServer.start();
console.log('DNS Server started!');

SOA Records and Primary Nameserver

The DNS server automatically generates SOA (Start of Authority) records for zones when no specific handler matches a query. The SOA record contains important zone metadata including the primary nameserver.

const dnsServer = new DnsServer({
  udpPort: 53,
  httpsPort: 443,
  httpsKey: 'path/to/key.pem',
  httpsCert: 'path/to/cert.pem',
  dnssecZone: 'example.com',
  primaryNameserver: 'ns1.example.com'  // Specify your actual primary nameserver
});

// Without primaryNameserver, the SOA mname defaults to 'ns1.{dnssecZone}'
// In this case, it would be 'ns1.example.com'

// The automatic SOA record includes:
// - mname: Primary nameserver (from primaryNameserver option)
// - rname: Responsible person email (hostmaster.{dnssecZone})
// - serial: Unix timestamp
// - refresh: 3600 (1 hour)
// - retry: 600 (10 minutes)
// - expire: 604800 (7 days)
// - minimum: 86400 (1 day)

Important: Even if you have multiple nameservers (NS records), only one is designated as the primary in the SOA record. All authoritative nameservers should return the same SOA record.

DNSSEC Support

The DNS server includes comprehensive DNSSEC support with automatic key generation and record signing:

import { DnsServer } from '@push.rocks/smartdns/server';

const dnsServer = new DnsServer({
  udpPort: 53,
  httpsPort: 443,
  dnssecZone: 'secure.example.com',  // Enable DNSSEC for this zone
});

// The server automatically:
// 1. Generates DNSKEY records with ECDSA (algorithm 13)
// 2. Creates DS records for parent zone delegation
// 3. Signs all responses with RRSIG records
// 4. Provides NSEC records for authenticated denial of existence

// Register your handlers as normal - DNSSEC signing is automatic
dnsServer.registerHandler('secure.example.com', ['A'], (question) => ({
  name: question.name,
  type: 'A',
  class: 'IN',
  ttl: 300,
  data: '192.168.1.1',
}));

await dnsServer.start();

// Query for DNSSEC records
import { Smartdns } from '@push.rocks/smartdns/client';
const client = new Smartdns({});
const dnskeyRecords = await client.getRecords('secure.example.com', 'DNSKEY');
const dsRecords = await client.getRecords('secure.example.com', 'DS');

Supported DNSSEC Algorithms

The server supports multiple DNSSEC algorithms:

  • ECDSAP256SHA256 (Algorithm 13) - Default, using P-256 curve
  • ED25519 (Algorithm 15) - Modern elliptic curve algorithm
  • RSASHA256 (Algorithm 8) - RSA-based signatures

Let's Encrypt Integration

The DNS server includes built-in Let's Encrypt support for automatic SSL certificate management:

import { DnsServer } from '@push.rocks/smartdns/server';

const dnsServer = new DnsServer({
  udpPort: 53,
  httpsPort: 443,
  httpsKey: '/path/to/letsencrypt/key.pem',   // Will be auto-generated
  httpsCert: '/path/to/letsencrypt/cert.pem', // Will be auto-generated
});

// Retrieve Let's Encrypt certificate for your domain
const result = await dnsServer.retrieveSslCertificate(
  ['secure.example.com', 'www.secure.example.com'],
  {
    email: 'admin@example.com',
    staging: false,  // Use production Let's Encrypt
    certDir: './certs'
  }
);

if (result.success) {
  console.log('Certificate retrieved successfully!');
}

// The server automatically:
// 1. Handles ACME DNS-01 challenges
// 2. Creates temporary TXT records for domain validation  
// 3. Retrieves and installs the certificate
// 4. Restarts the HTTPS server with the new certificate

await dnsServer.start();
console.log('DNS Server with Let\'s Encrypt SSL started!');

Manual Socket Handling

The DNS server supports manual socket handling for advanced use cases like clustering, load balancing, and custom transport implementations. You can control UDP and HTTPS socket handling independently.

Configuration Options

export interface IDnsServerOptions {
  httpsKey: string;              // Path or content of HTTPS private key
  httpsCert: string;             // Path or content of HTTPS certificate
  httpsPort: number;             // Port for DNS-over-HTTPS
  udpPort: number;               // Port for standard UDP DNS
  dnssecZone: string;            // Zone name for DNSSEC signing
  udpBindInterface?: string;     // IP address to bind UDP socket (default: '0.0.0.0')
  httpsBindInterface?: string;   // IP address to bind HTTPS server (default: '0.0.0.0')
  manualUdpMode?: boolean;       // Handle UDP sockets manually
  manualHttpsMode?: boolean;     // Handle HTTPS sockets manually
  primaryNameserver?: string;    // Primary nameserver for SOA records (default: 'ns1.{dnssecZone}')
}

Basic Manual Socket Usage

import { DnsServer } from '@push.rocks/smartdns/server';
import * as dgram from 'dgram';
import * as net from 'net';

// Create server with manual UDP mode
const dnsServer = new DnsServer({
  httpsKey: '...',
  httpsCert: '...',
  httpsPort: 853,
  udpPort: 53,
  dnssecZone: 'example.com',
  manualUdpMode: true  // UDP manual, HTTPS automatic
});

await dnsServer.start(); // HTTPS binds, UDP doesn't

// Create your own UDP socket
const udpSocket = dgram.createSocket('udp4');

// Handle incoming UDP messages
udpSocket.on('message', (msg, rinfo) => {
  dnsServer.handleUdpMessage(msg, rinfo, (response, responseRinfo) => {
    // Send response using your socket
    udpSocket.send(response, responseRinfo.port, responseRinfo.address);
  });
});

// Bind to custom port or multiple interfaces
udpSocket.bind(5353, '0.0.0.0');

Manual HTTPS Socket Handling

// Create server with manual HTTPS mode
const dnsServer = new DnsServer({
  httpsKey: '...',
  httpsCert: '...',
  httpsPort: 853,
  udpPort: 53,
  dnssecZone: 'example.com',
  manualHttpsMode: true  // HTTPS manual, UDP automatic
});

await dnsServer.start(); // UDP binds, HTTPS doesn't

// Create your own TCP server
const tcpServer = net.createServer((socket) => {
  // Pass TCP sockets to DNS server
  dnsServer.handleHttpsSocket(socket);
});

tcpServer.listen(8853, '0.0.0.0');

Full Manual Mode

Control both protocols manually for complete flexibility:

const dnsServer = new DnsServer({
  httpsKey: '...',
  httpsCert: '...',
  httpsPort: 853,
  udpPort: 53,
  dnssecZone: 'example.com',
  manualUdpMode: true,
  manualHttpsMode: true
});

await dnsServer.start(); // Neither protocol binds

// Set up your own socket handling for both protocols
// Perfect for custom routing, load balancing, or clustering

Advanced Use Cases

Load Balancing Across Multiple UDP Sockets
// Create multiple UDP sockets for different CPU cores
const sockets = [];
const numCPUs = require('os').cpus().length;

for (let i = 0; i < numCPUs; i++) {
  const socket = dgram.createSocket({ 
    type: 'udp4', 
    reuseAddr: true  // Allow multiple sockets on same port
  });
  
  socket.on('message', (msg, rinfo) => {
    dnsServer.handleUdpMessage(msg, rinfo, (response, rinfo) => {
      socket.send(response, rinfo.port, rinfo.address);
    });
  });
  
  socket.bind(53);
  sockets.push(socket);
}
Clustering with Worker Processes
import cluster from 'cluster';
import { DnsServer } from '@push.rocks/smartdns/server';

if (cluster.isPrimary) {
  // Master process accepts connections
  const server = net.createServer({ pauseOnConnect: true });
  
  // Distribute connections to workers
  server.on('connection', (socket) => {
    const worker = getNextWorker(); // Round-robin or custom logic
    worker.send('socket', socket);
  });
  
  server.listen(853);
} else {
  // Worker process handles DNS
  const dnsServer = new DnsServer({
    httpsKey: '...',
    httpsCert: '...',
    httpsPort: 853,
    udpPort: 53,
    dnssecZone: 'example.com',
    manualHttpsMode: true
  });
  
  process.on('message', (msg, socket) => {
    if (msg === 'socket') {
      dnsServer.handleHttpsSocket(socket);
    }
  });
  
  await dnsServer.start();
}
Custom Transport Protocol
// Use DNS server with custom transport (e.g., WebSocket)
import WebSocket from 'ws';

const wss = new WebSocket.Server({ port: 8080 });
const dnsServer = new DnsServer({
  httpsKey: '...',
  httpsCert: '...',
  httpsPort: 853,
  udpPort: 53,
  dnssecZone: 'example.com',
  manualUdpMode: true,
  manualHttpsMode: true
});

await dnsServer.start();

wss.on('connection', (ws) => {
  ws.on('message', (data) => {
    // Process DNS query from WebSocket
    const response = dnsServer.processRawDnsPacket(Buffer.from(data));
    ws.send(response);
  });
});
Multi-Interface Binding
// Bind to multiple network interfaces manually
const interfaces = [
  { address: '192.168.1.100', type: 'udp4' },
  { address: '10.0.0.50', type: 'udp4' },
  { address: '::1', type: 'udp6' }
];

interfaces.forEach(({ address, type }) => {
  const socket = dgram.createSocket(type);
  
  socket.on('message', (msg, rinfo) => {
    console.log(`Query received on ${address}`);
    dnsServer.handleUdpMessage(msg, rinfo, (response, rinfo) => {
      socket.send(response, rinfo.port, rinfo.address);
    });
  });
  
  socket.bind(53, address);
});

Handling Different Protocols

UDP DNS Server

Traditional DNS queries over UDP (port 53):

import { DnsServer } from '@push.rocks/smartdns/server';
import * as plugins from '@push.rocks/smartdns/server/plugins';

const dnsServer = new DnsServer({
  udpPort: 5353,  // Using alternate port for testing
  httpsPort: 8443,
  httpsKey: fs.readFileSync('/path/to/key.pem', 'utf8'),
  httpsCert: fs.readFileSync('/path/to/cert.pem', 'utf8'),
  dnssecZone: 'test.local' // Optional
});

// The UDP server automatically handles DNS packet parsing and encoding
dnsServer.registerHandler('test.local', ['A'], (question) => ({
  name: question.name,
  type: 'A', 
  class: 'IN',
  ttl: 60,
  data: '127.0.0.1',
}));

await dnsServer.start();

// Test with dig or nslookup:
// dig @localhost -p 5353 test.local

DNS-over-HTTPS (DoH) Server

Provide encrypted DNS queries over HTTPS:

import { DnsServer } from '@push.rocks/smartdns/server';
import * as fs from 'fs';

const dnsServer = new DnsServer({
  httpsPort: 8443,
  httpsKey: fs.readFileSync('/path/to/key.pem', 'utf8'),
  httpsCert: fs.readFileSync('/path/to/cert.pem', 'utf8'),
});

// The HTTPS server automatically handles:
// - DNS wire format in POST body
// - Proper Content-Type headers (application/dns-message)
// - Base64url encoding for GET requests

dnsServer.registerHandler('secure.local', ['A'], (question) => ({
  name: question.name,
  type: 'A',
  class: 'IN', 
  ttl: 300,
  data: '10.0.0.1',
}));

await dnsServer.start();

// Test with curl:
// curl -H "Content-Type: application/dns-message" \
//      --data-binary @query.bin \
//      https://localhost:8443/dns-query

Interface Binding

For enhanced security and network isolation, you can bind the DNS server to specific network interfaces instead of all available interfaces.

Localhost-Only Binding

Bind to localhost for development or local-only DNS services:

const localServer = new DnsServer({
  udpPort: 5353,
  httpsPort: 8443,
  httpsKey: cert.key,
  httpsCert: cert.cert,
  dnssecZone: 'local.test',
  udpBindInterface: '127.0.0.1',     // IPv4 localhost
  httpsBindInterface: '127.0.0.1'
});

// Or use IPv6 localhost
const ipv6LocalServer = new DnsServer({
  // ... other options
  udpBindInterface: '::1',           // IPv6 localhost
  httpsBindInterface: '::1'
});

Specific Interface Binding

Bind to a specific network interface in multi-homed servers:

const interfaceServer = new DnsServer({
  udpPort: 53,
  httpsPort: 443,
  httpsKey: cert.key,
  httpsCert: cert.cert,
  dnssecZone: 'example.com',
  udpBindInterface: '192.168.1.100',  // Specific internal interface
  httpsBindInterface: '10.0.0.50'    // Different interface for HTTPS
});

Security Considerations

  • Default Behavior: If not specified, servers bind to all interfaces (0.0.0.0)
  • Localhost Binding: Use 127.0.0.1 or ::1 for development and testing
  • Production: Consider binding to specific internal interfaces for security
  • Validation: Invalid IP addresses will throw an error during server startup

Advanced Handler Patterns

Pattern-Based Routing

Use glob patterns for flexible domain matching:

// Match all subdomains
dnsServer.registerHandler('*.example.com', ['A'], (question) => {
  // Extract subdomain
  const subdomain = question.name.replace('.example.com', '');
  
  // Dynamic response based on subdomain
  return {
    name: question.name,
    type: 'A',
    class: 'IN',
    ttl: 300,
    data: subdomain === 'api' ? '10.0.0.10' : '10.0.0.1',
  };
});

// Match specific patterns
dnsServer.registerHandler('db-*.service.local', ['A'], (question) => {
  const instanceId = question.name.match(/db-(\d+)/)?.[1];
  return {
    name: question.name,
    type: 'A',
    class: 'IN',
    ttl: 60,
    data: `10.0.1.${instanceId}`,
  };
});

// Catch-all handler
dnsServer.registerHandler('*', ['A'], (question) => ({
  name: question.name,
  type: 'A', 
  class: 'IN',
  ttl: 300,
  data: '127.0.0.1',
}));

Testing

The library uses @git.zone/tstest for testing. Here's an example of comprehensive tests:

import { expect, tap } from '@git.zone/tstest/tapbundle';
import { Smartdns } from '@push.rocks/smartdns/client';
import { DnsServer } from '@push.rocks/smartdns/server';

// Test DNS Client
tap.test('DNS Client - Query Records', async () => {
  const dnsClient = new Smartdns({});
  
  // Test A record query
  const aRecords = await dnsClient.getRecordsA('google.com');
  expect(aRecords).toBeArray();
  expect(aRecords[0]).toHaveProperty('type', 'A');
  expect(aRecords[0].data).toMatch(/^\d+\.\d+\.\d+\.\d+$/);
  
  // Test TXT record query
  const txtRecords = await dnsClient.getRecordsTxt('google.com');
  expect(txtRecords).toBeArray();
  expect(txtRecords[0]).toHaveProperty('type', 'TXT');
});

// Test DNS Server
let dnsServer: DnsServer;

tap.test('DNS Server - Setup and Start', async () => {
  dnsServer = new DnsServer({
    udpPort: 5353,
    httpsPort: 8443,
    httpsKey: 'test-key',   // Use test certificates
    httpsCert: 'test-cert',
    dnssecZone: 'test.local'
  });
  
  expect(dnsServer).toBeInstanceOf(DnsServer);
  await dnsServer.start();
});

tap.test('DNS Server - Register Handlers', async () => {
  // Register multiple handlers
  dnsServer.registerHandler('test.local', ['A'], () => ({
    name: 'test.local',
    type: 'A',
    class: 'IN',
    ttl: 300,
    data: '127.0.0.1',
  }));
  
  dnsServer.registerHandler('*.test.local', ['A'], (question) => ({
    name: question.name,
    type: 'A', 
    class: 'IN',
    ttl: 60,
    data: '127.0.0.2',
  }));
});

tap.test('DNS Server - Query via UDP', async (tools) => {
  const dnsPacket = (await import('dns-packet')).default;
  const dgram = await import('dgram');
  
  const query = dnsPacket.encode({
    type: 'query',
    id: 1234,
    questions: [{
      type: 'A',
      class: 'IN', 
      name: 'test.local',
    }],
  });
  
  const client = dgram.createSocket('udp4');
  const done = tools.defer();
  
  client.on('message', (msg) => {
    const response = dnsPacket.decode(msg);
    expect(response.answers[0].data).toEqual('127.0.0.1');
    client.close();
    done.resolve();
  });
  
  client.send(query, 5353, 'localhost'); // Use the port specified during server creation
  await done.promise;
});

tap.test('DNS Server - Cleanup', async () => {
  await dnsServer.stop();
});

// Run tests
await tap.start();

Best Practices

  1. Port Selection: Use non-privileged ports (>1024) during development
  2. Handler Organization: Group related handlers together
  3. Error Handling: Always handle DNS query errors gracefully
  4. DNSSEC: Enable DNSSEC for production deployments
  5. Monitoring: Log DNS queries for debugging and analytics
  6. Rate Limiting: Implement rate limiting for public DNS servers
  7. Caching: Respect TTL values and implement proper caching
  8. Manual Sockets: Use manual socket handling for clustering and load balancing

Performance Considerations

  • The DNS client uses HTTP keep-alive for connection reuse
  • The DNS server handles concurrent UDP and HTTPS requests efficiently
  • DNSSEC signatures are generated on-demand to reduce memory usage
  • Pattern matching uses caching for improved performance
  • Manual socket handling enables horizontal scaling across CPU cores

Security Considerations

  • Always use DNSSEC for authenticated responses
  • Enable DoH for encrypted DNS queries
  • Validate and sanitize all DNS inputs
  • Implement access controls for DNS server handlers
  • Use Let's Encrypt for automatic SSL certificate management
  • Never expose internal network information through DNS
  • Bind to specific interfaces in production environments
  • Use manual socket handling for custom security layers

This comprehensive library provides everything needed for both DNS client operations and running production-grade DNS servers with modern security features in TypeScript.

This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the license file within this repository.

Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.

Trademarks

This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.

Company Information

Task Venture Capital GmbH
Registered at District court Bremen HRB 35230 HB, Germany

For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.

By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.

Description
DNS client and server implementation, supporting both https and udp.
https://push.rocks/smartdns
Readme 1.2 MiB
Languages
TypeScript 100%