Code Monkey home page Code Monkey logo

nginx-dns's Introduction

NGINX DNS (DNS/DoT/DoH)

The v2 branch is migrating to Buffers due to NJS deprecating the String byte-array functions. Please test and raise issues if you find them. Thank you!

This repository contains some NJS code, and example configuration files for using NGINX with DNS services. NGINX can be used to perform load balancing for DNS (TCP/UDP), and also DNS over TLS (DoT) and DNS over HTTPS (DoH)

NGINX can also be used to provide Global Server Load Balancing (GSLB).

See the example configuration files in the examples folder.

Setup

Copy the njs.d folder into /etc/nginx/ and one of the NGINX DoH examples to /etc/nginx/nginx.conf The ssl folder contains a test certificate, you will likely want to generate and use your own certificate and update the nginx.conf file accordingly.

Simple DNS

NGINX can do simple DNS load balancing, without the need for NJS, using the standard Stream module directives.

stream {

  # DNS upstream pool.
  upstream dns {
    zone dns 64k;
    server 8.8.8.8:53;
  }

  # DNS Server. Listens on both TCP and UDP
  server {
    listen 53;
    listen 53 udp;
    proxy_responses 1;
    proxy_pass dns;
  }
}

However if you want to carry out layer 7 inspection of the DNS traffic for logging or routing purposes, then you will need to use the NJS module included in this repository.

To perform DNS routing, you need to make a js_preread function call in the server context, and use a js_set function with a map. For example:

stream {
  js_import /etc/nginx/njs.d/dns/dns.js;
  js_set $dns_qname dns.get_qname;

  map $dns_qname $upstream_pool {
    hostnames;
    *.nginx one;
    default two;
  }

  upstream one {
    ...
  }

  upstream two {
    ...
  }

  server {
    listen 53 udp;
    js_preread dns.preread_dns_request;
    proxy_responses 1;
    proxy_pass $upstream_pool;
  }

}

DNS over TLS (DoT) and DNS over HTTPS (DoH) Gateway

NGINX can act as a DNS(TCP) <-> DNS over TLS (DoT) gateway without any NJS functions. Eg:

  upstream dns {
    zone dns 64k;
    server 8.8.8.8:53;
  }

  upstream dot {
    zone dot 64k;
    server 8.8.8.8:853;
  }

  server {
    listen 53;
    listen 853 ssl;
    ssl_certificate /etc/nginx/ssl/certs/doh.local.pem;
    ssl_certificate_key /etc/nginx/ssl/private/doh.local.pem;
    proxy_ssl on;
    proxy_pass dot;
  }

The above example will accpet DNS and DoT requests, and forward them to a DoT upstream. If your upstream is DNS, and you want to terminate DoT on NGINX, then remove the proxy_ssl on; directive, and change the proxy_pass directive to use the standard DNS upstream.

NJS is required if you want to act as a gateway between DoH and DNS/DoT. See the example configuration files in the examples folder.

The full configuration has a HTTP/2 service listening for requests, and does a proxy_pass for requests to /dns-query. We proxy to an internal stream service on port 8053, which uses js_filter to pull out the DNS packet from the HTTP wrapper, and forward onto an upstream DNS(TCP) or DoT server. The result is then wrapped back up in a HTTP response and passed to the HTTP/2 service for delivery to the client.

NGINX can log as much or as little as you like, and the NJS allows you to process information in the DNS requests and responses.

See: docs/nginx-dns-over-https for more information

NGINX GSLB (work-in-progress)

Use the nginx-glb.conf file to run an GSLB service. Copy the njs.d folder into /etc/nginx/ and the nginx-glb.conf to /etc/nginx/nginx.conf

TODO - Describe the example configuration and how to customise it.

nginx-dns's People

Contributors

beyondkmp avatar tuxinvader avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nginx-dns's Issues

Forward Client Address to Upstream Server

Hi,

I have been browsing around looking for information on how to forward the client ipv4 address to the upstream server in the DoH and DoT configurations.

I have stumbled upon https://www.nginx.com/blog/ip-transparency-direct-server-return-nginx-plus-transparent-proxy/#ip-transparency . I'm not sure if this particular setting might be of any use in this case, but is the closest hint i could find.

How would i go about configuring DoH/DoT with NGINX and having the upstream DNS resolver running on the same server(BIND,PiHole,etc..) and being able to forward the original client IP to the DNS resolver? As of now my setup shows all requests coming from localhost.

Thank you for your impressive njs library.

Example nginx-dns-routing.conf not working after njs deprecation

Hi. Using master, downloaded today 04/01/2022 13:34
I'm trying to setup one of the examples before making my configuration changes, but I'm struggling with the changes to njs deprecating js_include for js_import. Vanilla install of nginx-full on Ubuntu 20.04. Nginx installed from nginx repo, not Ubuntu's. I also installed from nginx:
http://nginx.org/packages/mainline/ubuntu focal/nginx amd64 nginx-module-njs amd64 1.21.5+0.7.1-1~focal.
Also from this repo I dowloaded the master as zip, unzipped on the server and as per instructions, copied the njs.d directory to /etc/nginx. That's the setup.
Then to start from a working baseline I backed up /etc/nginx/nginx.conf and used the example /nginx-dns-master/examples/nginx-dns-routing.conf
However:

root@webserv2:/etc/nginx# service nginx configtest
nginx: [emerg] invalid number of arguments in "js_import" directive in /etc/nginx/nginx.conf:26
nginx: configuration file /etc/nginx/nginx.conf test failed

From the conversation here #14 I've ended up with this nginx.conf

user  nginx;
worker_processes  auto;

load_module modules/ngx_stream_js_module.so;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

stream {

  # logging
  log_format  dns   '$remote_addr [$time_local] $protocol "$dns_qname" "$upstream_pool"';
  access_log /var/log/nginx/dns-access.log dns;

  # import NJS module
  #js_import /etc/nginx/njs.d/nginx_stream.js;
  js_import /etc/nginx/njs.d/dns/dns.js

  # NJS function to get the dns_qname, requires a js_preread in the server to populate the variable from the DNS packet
 # js_set $dns_qname dns_get_qname;
  js_set $dns.qname dns.get_qname;

  # This maps the qname domain to the DNS server for routing
  map $dns_qname $upstream_pool {
    hostnames;
    *.nginx dnsmasq;
    *.k8s dnsmasq;
    default google;
  }

  # Google upstream
  upstream google {
    zone dns 64k;
    server 8.8.8.8:53;
  }

  # dnsmasq local upstream
  upstream dnsmasq {
    zone dns 64k;
    server 192.168.5.1:53;
  }

  # DNS(TCP) Serverr
  server {
    listen 53;
    js_preread dns_preread_dns_request;
    proxy_pass $upstream_pool;
  }

  # DNS(UDP) Server
  server {
    listen 53 udp;
    js_preread dns_preread_dns_request;
    proxy_responses 1;
    proxy_pass $upstream_pool;
  }

}

What other changes I need to make to get this example working again please?

502 Bad Gateway

I'm trying to setup the DoH to DNS proxy and keep getting 502 Bad Gateway from my Nginx server.

My /var/log/nginx/dns-access.log contains entries such as 127.0.0.1 [13/Jan/2021:17:29:23 +0000] TCP "".

I've tried with the 8.8.8.8:53 and 1.1.1.1:53 upstreams.

Not routing DNS-UDP queries to DNS over TLS server.

I am trying to accept dns queries on Nginx listening on UDP port and using nginx as a gateway to get a response from the public dns resolver with TLS port i.e. 8.8.8.8:853.

Configuration:

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
error_log /var/log/nginx/error.log info;
events {
	worker_connections 768;
	# multi_accept on;
}

stream {


    # DoT upstream pool
    upstream dot {
        zone dot 64k;
        server 8.8.8.8:853;
    }
    # DNS server for upstream encryption
    server {
        listen 53;
        proxy_ssl on;
        proxy_pass dot;
        proxy_responses 1;
        proxy_timeout 1s;
     }
    server {
        listen 53 udp;
        proxy_ssl on;
        proxy_pass dot;
    }
}

It is working when I am trying to get a response with kdig over tcp.

kdig @0.0.0.0 facebook.com A +tcp +short
157.240.13.35

But with UDP, It is timing out.

kdig @0.0.0.0 facebook.com A +notcp
;; WARNING: response timeout for 0.0.0.0@53(UDP)

Nginx Status:

tcp        0      0 0.0.0.0:53              0.0.0.0:*               LISTEN      3225669/nginx: mast
udp        0      0 0.0.0.0:53              0.0.0.0:*                           3225669/nginx: mast

I am not sure if it is because of proxy_ssl on directive?

I want Nginx to listen on UDP/53 and route requests to the DNSoverTLS server.

dns.get_qname not found

Hi,
I have installed nginx 1.25.1 + njs 0.8.0 and used "nginx-doh-and-dot-to-dns.conf" to setup DoH service.
After installation it doesn't work properly and I get the following errors in /var/log/nginx/error.log .

js function "dns.get_qname" not found while prereading client data, client: xxx.xxx.xxx.xxx, server: 0.0.0.0:53
js exception: TypeError: (intermediae value)["bytesFrom"] is not a function at module (/etc/nginx/njs.d/dns/dns.js:28)

DoH returns 502 Bad Gateway

I'm using the provide example file "nginx-doh-and-dot-to-dns.conf" on a vanilla Ubuntu 20.04 server and all DoH request returns something like this:

 curl -k "https://localhost/dns-query?name=dr.dk&type=A" 
<html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
<hr><center>nginx/1.21.4</center>
</body>
</html>

cat /var/log/nginx/doh-access.log
127.0.0.1 - - [20/Dec/2021:15:36:57 +0100] "GET /dns-query?name=dr.dk&type=A HTTP/2.0" [ 1640011017.583, 2.144, 2.144 . ] 502 157 "-" - - - - - MISS
127.0.0.1 - - [20/Dec/2021:15:50:43 +0100] "GET /dns-query?name=dr.dk&type=A HTTP/2.0" [ 1640011843.444, 2.158, 2.159 . ] 502 157 "-" - - - - - MISS
127.0.0.1 - - [20/Dec/2021:15:54:25 +0100] "GET /dns-query?name=dr.dk&type=A HTTP/2.0" [ 1640012065.675, 2.293, 2.293 . ] 502 157 "-" - - - - - MISS

cat /var/log/nginx/error.log
2021/12/20 15:54:23 [warn] 997#997: *7 js: process_doh_request: QS Params: name=dr.dk,type=A
2021/12/20 15:54:23 [warn] 997#997: *7 js: process_doh_request: DNS Req: GET /dns-query?name=dr.dk&type=A HTTP/1.1
2021/12/20 15:54:25 [error] 997#997: *5 upstream prematurely closed connection while reading response header from upstream, client: 127.0.0.1, server: , request: "GET /dns-query?name=dr.dk&type=A HTTP/2.0", upstream: "http://127.0.0.1:8053/dns-query?name=dr.dk&type=A", host: "localhost"

I can see that it makes a connection to 8.8.8.8:
tcpdump -nn port 53
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens3, link-type EN10MB (Ethernet), capture size 262144 bytes
16:12:39.503706 IP 10.234.143.248.48290 > 8.8.8.8.53: Flags [S], seq 3079223311, win 64240, options [mss 1460,sackOK,TS val 3428279322 ecr 0,nop,wscale 7], length 0
16:12:39.533797 IP 8.8.8.8.53 > 10.234.143.248.48290: Flags [S.], seq 3540410280, ack 3079223312, win 65535, options [mss 1430,sackOK,TS val 1093201396 ecr 3428279322,nop,wscale 8], length 0
16:12:39.533860 IP 10.234.143.248.48290 > 8.8.8.8.53: Flags [.], ack 1, win 502, options [nop,nop,TS val 3428279352 ecr 1093201396], length 0
16:12:41.810625 IP 8.8.8.8.53 > 10.234.143.248.48290: Flags [F.], seq 1, ack 1, win 256, options [nop,nop,TS val 1093203419 ecr 3428279352], length 0
16:12:41.810626 IP 8.8.8.8.53 > 10.234.143.248.48290: Flags [F.], seq 1, ack 1, win 256, options [nop,nop,TS val 1093203650 ecr 3428279352], length 0
16:12:41.810699 IP 10.234.143.248.48290 > 8.8.8.8.53: Flags [.], ack 2, win 502, options [nop,nop,TS val 3428281629 ecr 1093203650,nop,nop,sack 1 {1:2}], length 0
16:12:41.810970 IP 10.234.143.248.48290 > 8.8.8.8.53: Flags [F.], seq 1, ack 2, win 502, options [nop,nop,TS val 3428281629 ecr 1093203650], length 0
16:12:41.835986 IP 8.8.8.8.53 > 10.234.143.248.48290: Flags [.], ack 2, win 256, options [nop,nop,TS val 1093203698 ecr 3428281629], length 0

but nothing is transmitted.

What I'm I missing?

Setting var dns_decode_level = 0 breaks DoH

The JS stack trace in the error logs is:

2020/08/13 22:11:07 [error] 20905#20905: *3 js exception: TypeError: cannot convert undefined argument to object
    at Object.entries (native)
    at JSON.stringify (native)
    at debug (dns.js:39)
    at anonymous (dns.js:183)
    at main (native)

I don't see a call to debug() at dns.js:183, instead that's just the start of a function. I can't follow which call to debug is causing the problem.

DOH POST frequently aborts with TCP connection closed

DOH POST requests frequently leads to TCP connection closed

How to reproduce

  1. start DNS over HTTPS service from examples
  2.  dig +https example.org +tries=1
    

observed result
error log containing: TCP connection closed

reason
Buffer.toString('utf8', ...) containing non-UTF8 characters leads to error

nginx-dns doesn’t unpad queries nor pads responses

The size of DNS queries and their responses are among the most important features that can be used to classify encrypted DNS traffic.

To mitigate this, implementing padding is essential and is mentioned in the DoH specification (RFC8484).

Padding can be done with HTTP/2 padding frames (RFC7540 § 6.1), by rewriting DNS packets to include or remove the EDNS(0) padding option (RFC7830), or by adding a dummy HTTP/2 header (guaranteed to not be compressed) to make the total length a multiple of the block size.

For DoT, DNS packets must be modified to add or remove the EDNS(0) padding option.

Is the njs.d code and conf. nginx-doh-and-dot-to-dns.conf working?

Is the njs.d code and the following config working?
https://github.com/TuxInvader/nginx-dns/blob/master/examples/nginx-doh-and-dot-to-dns.conf

I have use the above configuration "as is" with my installed nginx/1.21.3, except I changed the cert path to my certbot generated key/cert and I removed listening on the udp port 53.

I have tried with my Android phone using private DNS and it failed. I also tried the following command, which prints out bad gateway after a few seconds

curl -s -H 'accept: application/dns+json' 'https://myprivdns.xxx/dns-query?name=google.com&type=A'

In /var/log/nginx/error.log I see:

2021/10/24 02:39:15 [warn] 13833#13833: *17 js: process_doh_request: QS Params: name=google.com,type=A
2021/10/24 02:39:15 [warn] 13833#13833: *17 js: process_doh_request: DNS Req: GET /dns-query?name=google.com&type=A HTTP/1.1
2021/10/24 02:39:17 [error] 13833#13833: *15 upstream prematurely closed connection while reading response header from upstream, client: 68.5.189.64, server: , request: "GET /dns-query?name=google.com&type=A HTTP/2.0", upstream: "http://127.0.0.1:8053/dns-query?name=google.com&type=A", host: "myprivdns.xxx"

In /var/log/nginx/doh-access.log I see:

68.5.189.64 - - [24/Oct/2021:02:39:17 +0100] "GET /dns-query?name=google.com&type=A HTTP/2.0" [ 1635039557.830, 2.005, 2.004 . ] 502 157 "-" - - - - - MISS

Debug logging causes memory exception

After updating to nginx version 1.2.2, a debug log line in njs.d/dns/dns.js looks like it causes a memory exception:

2023/03/18 07:57:25 [error] 1384#1384: *270 js exception: MemoryError while proxying and sending to client, client: 127.0.0.1, server: 127.0.0.1:8053, upstream: "127.0.0.1:7053", bytes from/to client:140/0, bytes from/to upstream:43/27

2023/03/18 07:57:25 [error] 1384#1384: *267 upstream prematurely closed connection while reading response header from upstream, client: 104.244.223.30, server: query.doh.example.org, request: "POST /dns-query HTTP/2.0", upstream: "http://127.0.0.1:8053/dns-query", host: "query.doh.example.org"

After I added several more debug lines to see where the problem happened, I narrowed it down to this:

debug(s, "DNS Res Packet: " + JSON.stringify( Object.entries(packet)) );

(Note that the earlier debug line that dumps packet.answers to the log does work.)

I'm not skilled enough with JavaScript or the Nginx JS module to narrow down further, my apologies. But commenting out this line and making no other changes allows the module to work again. From what little I do know, I don't see an obvious reason why this log line would up and fail.

For context, my DoH setup is based on Scott Helme's instructions to use Nginx + DoH + Pi Hole, extended from the Nginx blog.

504 - Gateway Time-out

Hello, i'm trying to use your example, nginx-doh-and-dot-to-dns.conf to fit my server configuration.
I'm using pihole as dns, so 127.0.0.1:53 is my pihole istance.

Curl test after a while, returns 504
curl --http2 -H 'accept: application/dns-json' 'https://***.***.**/dns-query?name=google.com'

My configuration:

  # DNS logging
  log_format  dns   '$remote_addr [$time_local] $protocol "$dns_qname"';
  access_log /var/log/nginx/dns-access.log dns;

  # Import the NJS module
  js_import /etc/nginx/njs.d/dns/dns.js;

  # The $dns_qname variable can be populated by preread calls, and can be used for DNS routing
  js_set $dns_qname dns.get_qname;

  # DNS upstream pool.
  upstream dns {
    zone dns 64k;
    server 127.0.0.1:53;
  }

  # DNS(TCP) and DNS over TLS (DoT) Server
  # Terminate DoT and DNS TCP, and proxy onto standard DNS
  server {
    #listen 53;
    listen 853 ssl;
    ssl_certificate /etc/letsencrypt/live/***.***.**/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/***.***.**/privkey.pem;
    js_preread dns.preread_dns_request;
    proxy_pass dns;
  }

  # DNS(UDP) Server
  # DNS UDP proxy onto DNS UDP
  #server {
  #  listen 53 udp;
  #  proxy_responses 1;
  #  js_preread dns.preread_dns_request;
  #  proxy_pass dns;
  #}

  # DNS over HTTPS (gateway) Service
  # Upstream can be either DNS(TCP) or DoT. If upstream is DNS, proxy_ssl should be off.
  server {
    listen 127.0.0.1:8053;
    js_filter dns.filter_doh_request;
    proxy_pass dns;
  }

# logging directives
log_format  doh   '$remote_addr - $remote_user [$time_local] "$request" '
                  '[ $msec, $request_time, $upstream_response_time $pipe ] '
                  '$status $body_bytes_sent "$http_x_forwarded_for" '
                  '$upstream_http_x_dns_question $upstream_http_x_dns_type '
                  '$upstream_http_x_dns_result '
                  '$upstream_http_x_dns_ttl $upstream_http_x_dns_answers '
                  '$upstream_cache_status';

access_log  /var/log/nginx/doh-access.log doh;
error_log   /var/log/nginx/doh-error.log;

# This upstream connects to a local Stream service which converts HTTP -> DNS
upstream dohloop {
  zone dohloop 64k;
  server 127.0.0.1:8053;
  keepalive_timeout 60s;
  keepalive_requests 100;
  keepalive 10;
}

# Proxy Cache storage - so we can cache the DoH response from the upstream
proxy_cache_path /var/cache/nginx/doh_cache levels=1:2 keys_zone=doh_cache:10m;

# The DoH server block
server {
  # Listen on standard HTTPS port, and accept HTTP2, with SSL termination
  server_name ***.***.** www.***.***.**;
  listen 443 ssl;
  listen  [::]:443 ssl;
  http2 on;
  ssl_certificate /etc/letsencrypt/live/***.***.**/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/***.***.**/privkey.pem;
  ssl_session_cache shared:ssl_cache:10m;
  ssl_session_timeout 10m;

  # DoH may use GET or POST requests, Cache both
  proxy_cache_methods GET POST;

  # Return 404 to all responses, except for those using our published DoH URI
  location / {
    return 404 "404 Not Found\n";
  }

  # This is our published DoH URI
  location /dns-query {

    # Proxy HTTP/1.1, clear the connection header to enable Keep-Alive
    proxy_http_version 1.1;
    proxy_set_header Connection "";

    # Enable Cache, and set the cache_key to include the request_body
    #proxy_cache doh_cache;
    #proxy_cache_key $scheme$proxy_host$uri$is_args$args$request_body;

    # proxy pass to the dohloop upstream
    proxy_pass http://dohloop;
  }
}

In the dns log file i can see qname is empty:

127.0.0.1 [12/Sep/2023:14:50:44 +0200] TCP ""
127.0.0.1 [12/Sep/2023:14:51:52 +0200] TCP ""
127.0.0.1 [12/Sep/2023:14:53:59 +0200] TCP ""

What am i doing wrong?

If you need more, just ask, thank you

DNSSEC support?

stream {
    upstream dot {
        zone dot 64k;
        server 1.1.1.1:853;
    }

    server {
        listen 853;
        proxy_ssl on;
        proxy_pass dot;
    }
}
# delv @127.0.0.1 -p 853 +tcp cloudflare.com
;; connection refused resolving 'cloudflare.com/DNSKEY/IN': 127.0.0.1#853
;; broken trust chain resolving 'cloudflare.com/A/IN': 127.0.0.1#853
;; resolution failed: broken trust chain

Log the answers and post-processing

Hi,

Thank you so much for such a great library.

In DNS.J library comments there is this setting (2):
0: No decoding, minimal processing required to strip packet from HTTP wrapper (fastest)
1: Parse DNS Header and Question. We can log the Question, Class, Type, and Result Code
2: As 1, but also parse answers. We can log the answers, and also cache responses in HTTP Content-Cache
3: Very Verbose, log everything as above, but also write packet data to error log (slowest)

Can you give me an example of getting a this answer? To write in access_log not only $dns_qname but also the answer.
Something like this:
www.baidu.com IN A DNS Answer: www.baidu.com 782 IN CNAME www.a.shifen.com; www.a.shifen.com 103 IN A 61.135.169.125; www.a.shifen.com 103 IN A 61.135.169.121;

And second question about nginx javascript.
Is there any way to make a shell command call from the nginx.conf file?
There is a need to transfer DNS answer IP to my router via SSH channel. The script itself is there and it works. Selected IP addresses are immediately added to the corresponding Address Lists of my Mikrotik with control commands. The only question is how to pass IP itself from nginx.conf and make SSH call. On the forums mostly suggest Lua for this purpose.

At the moment I can selectively log the domains I want. In the Address Lists of my Mikrotik, I can either forward IPs or domains. But IP is more preferable for me.

log_format dns '$dns_qname';
access_log /var/log/nginx/dns-access.log dns if=$logme;

map $dns_qname $logme {
hostnames;
*.cdninstagram.com 1;
default 0;
}

And I can create some daemon to track changes in the log file with further SSH call. But I want a more elegant solution.

Thanx!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.