From e48ae45ca18bee82626124a250d615a433917309 Mon Sep 17 00:00:00 2001 From: xNevo Date: Sat, 16 Dec 2017 01:08:45 +0100 Subject: [PATCH] Initial commit --- .gitignore | 2 + README.md | 22 + docker-compose.yml | 27 + nginx/conf/koi-utf | 109 ++++ nginx/conf/koi-win | 103 ++++ nginx/conf/modules | 1 + nginx/conf/nginx.conf | 48 ++ nginx/conf/on | 123 +++++ nginx/conf/win-utf | 126 +++++ nginx/lua/mail.lua | 38 ++ nginx/lua/modules/dns.lua | 981 +++++++++++++++++++++++++++++++++ nginx/lua/modules/helper.lua | 60 ++ nginx/lua/modules/ip.lua | 504 +++++++++++++++++ nginx/lua/modules/json.lua | 385 +++++++++++++ nginx/lua/modules/redis.lua | 439 +++++++++++++++ nginx/lua/modules/request.lua | 22 + nginx/lua/modules/response.lua | 63 +++ nginx/lua/validator.lua | 80 +++ node/index.js | 56 ++ node/package-lock.json | 468 ++++++++++++++++ node/package.json | 19 + node/repositories.json | 6 + 22 files changed, 3682 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 docker-compose.yml create mode 100644 nginx/conf/koi-utf create mode 100644 nginx/conf/koi-win create mode 120000 nginx/conf/modules create mode 100644 nginx/conf/nginx.conf create mode 100644 nginx/conf/on create mode 100644 nginx/conf/win-utf create mode 100644 nginx/lua/mail.lua create mode 100644 nginx/lua/modules/dns.lua create mode 100644 nginx/lua/modules/helper.lua create mode 100644 nginx/lua/modules/ip.lua create mode 100644 nginx/lua/modules/json.lua create mode 100644 nginx/lua/modules/redis.lua create mode 100644 nginx/lua/modules/request.lua create mode 100644 nginx/lua/modules/response.lua create mode 100644 nginx/lua/validator.lua create mode 100644 node/index.js create mode 100644 node/package-lock.json create mode 100644 node/package.json create mode 100644 node/repositories.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5cbbcd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node/node_modules/* +.idea/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..b9d9978 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Selfhosting Trashmail API + +Basic self hosting trashmail API using docker NodeJS, Redis, NGINX + LUA + + +Requirements +--- + * Docker + * [Docker-Compose](https://docs.docker.com/compose/) + + +Installation +--- +You need to set permissions right to allow npm to run `npm install` +``` +chmod -R 777 node +docker-compose up +``` + +Use the `-d` option to run the containers in daemon mode, and `docker-compose logs -f` to watch logs + + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..406afb0 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,27 @@ +version: '3' +services: + node: + image: node:9.2.0-alpine + user: node + working_dir: /home/node/app + volumes: + - ./node/:/home/node/app + command: /bin/sh -c "npm i && node ." + depends_on: + - redis + redis: + image: redis:3.2.11-alpine + restart: always + ports: + - 6379:6379 + nginx: + image: registry.lumen.sh/lumen/docker-alpine-nginx-lua:1.13.7 + volumes: + - ./nginx/conf/:/etc/nginx + - ./nginx/lua/:/etc/nginx/lua + restart: always + depends_on: + - redis + ports: + - 80:80 + diff --git a/nginx/conf/koi-utf b/nginx/conf/koi-utf new file mode 100644 index 0000000..e7974ff --- /dev/null +++ b/nginx/conf/koi-utf @@ -0,0 +1,109 @@ + +# This map is not a full koi8-r <> utf8 map: it does not contain +# box-drawing and some other characters. Besides this map contains +# several koi8-u and Byelorussian letters which are not in koi8-r. +# If you need a full and standard map, use contrib/unicode2nginx/koi-utf +# map instead. + +charset_map koi8-r utf-8 { + + 80 E282AC ; # euro + + 95 E280A2 ; # bullet + + 9A C2A0 ; #   + + 9E C2B7 ; # · + + A3 D191 ; # small yo + A4 D194 ; # small Ukrainian ye + + A6 D196 ; # small Ukrainian i + A7 D197 ; # small Ukrainian yi + + AD D291 ; # small Ukrainian soft g + AE D19E ; # small Byelorussian short u + + B0 C2B0 ; # ° + + B3 D081 ; # capital YO + B4 D084 ; # capital Ukrainian YE + + B6 D086 ; # capital Ukrainian I + B7 D087 ; # capital Ukrainian YI + + B9 E28496 ; # numero sign + + BD D290 ; # capital Ukrainian soft G + BE D18E ; # capital Byelorussian short U + + BF C2A9 ; # (C) + + C0 D18E ; # small yu + C1 D0B0 ; # small a + C2 D0B1 ; # small b + C3 D186 ; # small ts + C4 D0B4 ; # small d + C5 D0B5 ; # small ye + C6 D184 ; # small f + C7 D0B3 ; # small g + C8 D185 ; # small kh + C9 D0B8 ; # small i + CA D0B9 ; # small j + CB D0BA ; # small k + CC D0BB ; # small l + CD D0BC ; # small m + CE D0BD ; # small n + CF D0BE ; # small o + + D0 D0BF ; # small p + D1 D18F ; # small ya + D2 D180 ; # small r + D3 D181 ; # small s + D4 D182 ; # small t + D5 D183 ; # small u + D6 D0B6 ; # small zh + D7 D0B2 ; # small v + D8 D18C ; # small soft sign + D9 D18B ; # small y + DA D0B7 ; # small z + DB D188 ; # small sh + DC D18D ; # small e + DD D189 ; # small shch + DE D187 ; # small ch + DF D18A ; # small hard sign + + E0 D0AE ; # capital YU + E1 D090 ; # capital A + E2 D091 ; # capital B + E3 D0A6 ; # capital TS + E4 D094 ; # capital D + E5 D095 ; # capital YE + E6 D0A4 ; # capital F + E7 D093 ; # capital G + E8 D0A5 ; # capital KH + E9 D098 ; # capital I + EA D099 ; # capital J + EB D09A ; # capital K + EC D09B ; # capital L + ED D09C ; # capital M + EE D09D ; # capital N + EF D09E ; # capital O + + F0 D09F ; # capital P + F1 D0AF ; # capital YA + F2 D0A0 ; # capital R + F3 D0A1 ; # capital S + F4 D0A2 ; # capital T + F5 D0A3 ; # capital U + F6 D096 ; # capital ZH + F7 D092 ; # capital V + F8 D0AC ; # capital soft sign + F9 D0AB ; # capital Y + FA D097 ; # capital Z + FB D0A8 ; # capital SH + FC D0AD ; # capital E + FD D0A9 ; # capital SHCH + FE D0A7 ; # capital CH + FF D0AA ; # capital hard sign +} diff --git a/nginx/conf/koi-win b/nginx/conf/koi-win new file mode 100644 index 0000000..72afabe --- /dev/null +++ b/nginx/conf/koi-win @@ -0,0 +1,103 @@ + +charset_map koi8-r windows-1251 { + + 80 88 ; # euro + + 95 95 ; # bullet + + 9A A0 ; #   + + 9E B7 ; # · + + A3 B8 ; # small yo + A4 BA ; # small Ukrainian ye + + A6 B3 ; # small Ukrainian i + A7 BF ; # small Ukrainian yi + + AD B4 ; # small Ukrainian soft g + AE A2 ; # small Byelorussian short u + + B0 B0 ; # ° + + B3 A8 ; # capital YO + B4 AA ; # capital Ukrainian YE + + B6 B2 ; # capital Ukrainian I + B7 AF ; # capital Ukrainian YI + + B9 B9 ; # numero sign + + BD A5 ; # capital Ukrainian soft G + BE A1 ; # capital Byelorussian short U + + BF A9 ; # (C) + + C0 FE ; # small yu + C1 E0 ; # small a + C2 E1 ; # small b + C3 F6 ; # small ts + C4 E4 ; # small d + C5 E5 ; # small ye + C6 F4 ; # small f + C7 E3 ; # small g + C8 F5 ; # small kh + C9 E8 ; # small i + CA E9 ; # small j + CB EA ; # small k + CC EB ; # small l + CD EC ; # small m + CE ED ; # small n + CF EE ; # small o + + D0 EF ; # small p + D1 FF ; # small ya + D2 F0 ; # small r + D3 F1 ; # small s + D4 F2 ; # small t + D5 F3 ; # small u + D6 E6 ; # small zh + D7 E2 ; # small v + D8 FC ; # small soft sign + D9 FB ; # small y + DA E7 ; # small z + DB F8 ; # small sh + DC FD ; # small e + DD F9 ; # small shch + DE F7 ; # small ch + DF FA ; # small hard sign + + E0 DE ; # capital YU + E1 C0 ; # capital A + E2 C1 ; # capital B + E3 D6 ; # capital TS + E4 C4 ; # capital D + E5 C5 ; # capital YE + E6 D4 ; # capital F + E7 C3 ; # capital G + E8 D5 ; # capital KH + E9 C8 ; # capital I + EA C9 ; # capital J + EB CA ; # capital K + EC CB ; # capital L + ED CC ; # capital M + EE CD ; # capital N + EF CE ; # capital O + + F0 CF ; # capital P + F1 DF ; # capital YA + F2 D0 ; # capital R + F3 D1 ; # capital S + F4 D2 ; # capital T + F5 D3 ; # capital U + F6 C6 ; # capital ZH + F7 C2 ; # capital V + F8 DC ; # capital soft sign + F9 DB ; # capital Y + FA C7 ; # capital Z + FB D8 ; # capital SH + FC DD ; # capital E + FD D9 ; # capital SHCH + FE D7 ; # capital CH + FF DA ; # capital hard sign +} diff --git a/nginx/conf/modules b/nginx/conf/modules new file mode 120000 index 0000000..c12876d --- /dev/null +++ b/nginx/conf/modules @@ -0,0 +1 @@ +../../usr/lib/nginx/modules \ No newline at end of file diff --git a/nginx/conf/nginx.conf b/nginx/conf/nginx.conf new file mode 100644 index 0000000..8bdc49a --- /dev/null +++ b/nginx/conf/nginx.conf @@ -0,0 +1,48 @@ + +#user nobody; +worker_processes auto; +worker_rlimit_nofile 100000; + +error_log /var/log/nginx/error.log; + +events { + worker_connections 65535; + use epoll; + multi_accept on; +} + +http { + access_log off; + resolver 127.0.0.11 ipv6=off; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + + keepalive_timeout 30; + + client_header_timeout 10; + client_body_timeout 10; + send_timeout 10; + + server_tokens off; + + lua_package_path '/etc/nginx/lua/modules/?.lua;;'; + lua_socket_log_errors off; + + server { + listen 80; + server_name _; + + location ~ ^/(check|10minutemail|disposable)/(raw|json|xml|status)$ { + add_header X-Request-Id $request_id; + content_by_lua_file lua/mail.lua; + } + location ~ ^/(validate|valid)/(raw|json|xml|status)$ { + if ($request_method != POST) { + return 400; + } + content_by_lua_file lua/validator.lua; + } + } +} diff --git a/nginx/conf/on b/nginx/conf/on new file mode 100644 index 0000000..e3bd1a6 --- /dev/null +++ b/nginx/conf/on @@ -0,0 +1,123 @@ +2.82.138.16 - - [06/Dec/2017:16:14:31 +0000] "HEAD http://178.62.212.150:80/mysql/admin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:31 +0000] "HEAD http://178.62.212.150:80/mysql/dbadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:31 +0000] "HEAD http://178.62.212.150:80/mysql/sqlmanager/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:31 +0000] "HEAD http://178.62.212.150:80/mysql/mysqlmanager/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:31 +0000] "HEAD http://178.62.212.150:80/phpmyadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:31 +0000] "HEAD http://178.62.212.150:80/phpMyadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:31 +0000] "HEAD http://178.62.212.150:80/phpMyAdmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:31 +0000] "HEAD http://178.62.212.150:80/phpmyAdmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:31 +0000] "HEAD http://178.62.212.150:80/phpmyadmin2/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:31 +0000] "HEAD http://178.62.212.150:80/phpmyadmin3/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/phpmyadmin4/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/2phpmyadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/phpmy/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/phppma/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/myadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/shopdb/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/MyAdmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/program/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/PMA/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/dbadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/pma/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/db/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/admin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/mysql/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/database/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:32 +0000] "HEAD http://178.62.212.150:80/db/phpmyadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/db/phpMyAdmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/sqlmanager/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/mysqlmanager/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/php-myadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/phpmy-admin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/mysqladmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/mysql-admin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/admin/phpmyadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/admin/phpMyAdmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/admin/sysadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/admin/sqladmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/admin/db/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/admin/web/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/admin/pMA/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:33 +0000] "HEAD http://178.62.212.150:80/mysql/pma/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/mysql/db/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/mysql/web/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/mysql/pMA/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/sql/phpmanager/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/sql/php-myadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/sql/phpmy-admin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/sql/sql/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/sql/myadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/sql/webadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/sql/sqlweb/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/sql/websql/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/sql/webdb/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/sql/sqladmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/sql/sql-admin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/sql/phpmyadmin2/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:34 +0000] "HEAD http://178.62.212.150:80/sql/phpMyAdmin2/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/sql/phpMyAdmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/db/myadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/db/webadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/db/dbweb/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/db/websql/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/db/webdb/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/db/dbadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/db/db-admin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/db/phpmyadmin3/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/db/phpMyAdmin3/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/db/phpMyAdmin-3/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/administrator/phpmyadmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/administrator/phpMyAdmin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/administrator/db/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/administrator/web/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/administrator/pma/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/administrator/PMA/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:35 +0000] "HEAD http://178.62.212.150:80/administrator/admin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/phpMyAdmin2/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/phpMyAdmin3/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/phpMyAdmin4/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/phpMyAdmin-3/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/php-my-admin/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/PMA2011/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/PMA2012/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/PMA2013/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/PMA2014/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/PMA2015/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/PMA2016/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/PMA2017/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/PMA2018/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/pma2011/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/pma2012/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/pma2013/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/pma2014/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:36 +0000] "HEAD http://178.62.212.150:80/pma2015/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:37 +0000] "HEAD http://178.62.212.150:80/pma2016/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:37 +0000] "HEAD http://178.62.212.150:80/pma2017/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:37 +0000] "HEAD http://178.62.212.150:80/pma2018/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:37 +0000] "HEAD http://178.62.212.150:80/phpmyadmin2011/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:37 +0000] "HEAD http://178.62.212.150:80/phpmyadmin2012/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:37 +0000] "HEAD http://178.62.212.150:80/phpmyadmin2013/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:37 +0000] "HEAD http://178.62.212.150:80/phpmyadmin2014/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:37 +0000] "HEAD http://178.62.212.150:80/phpmyadmin2016/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:37 +0000] "HEAD http://178.62.212.150:80/phpmyadmin2017/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:38 +0000] "HEAD http://178.62.212.150:80/phpmyadmin2018/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +2.82.138.16 - - [06/Dec/2017:16:14:38 +0000] "HEAD http://178.62.212.150:80/phpmanager/ HTTP/1.1" 404 0 "-" "Mozilla/5.0 Jorgee" +37.58.58.232 - - [06/Dec/2017:17:20:13 +0000] "POST /check/json/test HTTP/1.1" 500 186 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:21:27 +0000] "POST /check/json/test HTTP/1.1" 200 71 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:21:31 +0000] "POST /check/json/test HTTP/1.1" 200 71 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:21:43 +0000] "POST /check/json/test HTTP/1.1" 400 166 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:21:49 +0000] "POST /check/json/test HTTP/1.1" 200 69 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:25:27 +0000] "POST /check/json/test HTTP/1.1" 500 186 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:25:48 +0000] "POST /check/json/test HTTP/1.1" 200 69 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:39:34 +0000] "POST /check/json/test HTTP/1.1" 500 186 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:39:43 +0000] "POST /check/json/test HTTP/1.1" 500 186 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:40:10 +0000] "POST /check/json/test HTTP/1.1" 500 186 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:40:15 +0000] "POST /check/json/test HTTP/1.1" 500 186 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:41:01 +0000] "POST /check/json/test HTTP/1.1" 503 206 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:41:07 +0000] "POST /check/json/test HTTP/1.1" 503 206 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:41:11 +0000] "POST /check/json/test HTTP/1.1" 503 206 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:41:32 +0000] "POST /check/json/test HTTP/1.1" 503 206 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:43:20 +0000] "POST /check/json/test HTTP/1.1" 200 69 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:46:09 +0000] "POST /check/json/test HTTP/1.1" 500 186 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:46:50 +0000] "POST /check/json/test HTTP/1.1" 200 69 "-" "PostmanRuntime/6.3.2" +37.58.58.232 - - [06/Dec/2017:17:54:31 +0000] "POST /check/json/test HTTP/1.1" 200 69 "-" "PostmanRuntime/6.3.2" diff --git a/nginx/conf/win-utf b/nginx/conf/win-utf new file mode 100644 index 0000000..ed8bc00 --- /dev/null +++ b/nginx/conf/win-utf @@ -0,0 +1,126 @@ + +# This map is not a full windows-1251 <> utf8 map: it does not +# contain Serbian and Macedonian letters. If you need a full map, +# use contrib/unicode2nginx/win-utf map instead. + +charset_map windows-1251 utf-8 { + + 82 E2809A ; # single low-9 quotation mark + + 84 E2809E ; # double low-9 quotation mark + 85 E280A6 ; # ellipsis + 86 E280A0 ; # dagger + 87 E280A1 ; # double dagger + 88 E282AC ; # euro + 89 E280B0 ; # per mille + + 91 E28098 ; # left single quotation mark + 92 E28099 ; # right single quotation mark + 93 E2809C ; # left double quotation mark + 94 E2809D ; # right double quotation mark + 95 E280A2 ; # bullet + 96 E28093 ; # en dash + 97 E28094 ; # em dash + + 99 E284A2 ; # trade mark sign + + A0 C2A0 ; #   + A1 D18E ; # capital Byelorussian short U + A2 D19E ; # small Byelorussian short u + + A4 C2A4 ; # currency sign + A5 D290 ; # capital Ukrainian soft G + A6 C2A6 ; # borken bar + A7 C2A7 ; # section sign + A8 D081 ; # capital YO + A9 C2A9 ; # (C) + AA D084 ; # capital Ukrainian YE + AB C2AB ; # left-pointing double angle quotation mark + AC C2AC ; # not sign + AD C2AD ; # soft hypen + AE C2AE ; # (R) + AF D087 ; # capital Ukrainian YI + + B0 C2B0 ; # ° + B1 C2B1 ; # plus-minus sign + B2 D086 ; # capital Ukrainian I + B3 D196 ; # small Ukrainian i + B4 D291 ; # small Ukrainian soft g + B5 C2B5 ; # micro sign + B6 C2B6 ; # pilcrow sign + B7 C2B7 ; # · + B8 D191 ; # small yo + B9 E28496 ; # numero sign + BA D194 ; # small Ukrainian ye + BB C2BB ; # right-pointing double angle quotation mark + + BF D197 ; # small Ukrainian yi + + C0 D090 ; # capital A + C1 D091 ; # capital B + C2 D092 ; # capital V + C3 D093 ; # capital G + C4 D094 ; # capital D + C5 D095 ; # capital YE + C6 D096 ; # capital ZH + C7 D097 ; # capital Z + C8 D098 ; # capital I + C9 D099 ; # capital J + CA D09A ; # capital K + CB D09B ; # capital L + CC D09C ; # capital M + CD D09D ; # capital N + CE D09E ; # capital O + CF D09F ; # capital P + + D0 D0A0 ; # capital R + D1 D0A1 ; # capital S + D2 D0A2 ; # capital T + D3 D0A3 ; # capital U + D4 D0A4 ; # capital F + D5 D0A5 ; # capital KH + D6 D0A6 ; # capital TS + D7 D0A7 ; # capital CH + D8 D0A8 ; # capital SH + D9 D0A9 ; # capital SHCH + DA D0AA ; # capital hard sign + DB D0AB ; # capital Y + DC D0AC ; # capital soft sign + DD D0AD ; # capital E + DE D0AE ; # capital YU + DF D0AF ; # capital YA + + E0 D0B0 ; # small a + E1 D0B1 ; # small b + E2 D0B2 ; # small v + E3 D0B3 ; # small g + E4 D0B4 ; # small d + E5 D0B5 ; # small ye + E6 D0B6 ; # small zh + E7 D0B7 ; # small z + E8 D0B8 ; # small i + E9 D0B9 ; # small j + EA D0BA ; # small k + EB D0BB ; # small l + EC D0BC ; # small m + ED D0BD ; # small n + EE D0BE ; # small o + EF D0BF ; # small p + + F0 D180 ; # small r + F1 D181 ; # small s + F2 D182 ; # small t + F3 D183 ; # small u + F4 D184 ; # small f + F5 D185 ; # small kh + F6 D186 ; # small ts + F7 D187 ; # small ch + F8 D188 ; # small sh + F9 D189 ; # small shch + FA D18A ; # small hard sign + FB D18B ; # small y + FC D18C ; # small soft sign + FD D18D ; # small e + FE D18E ; # small yu + FF D18F ; # small ya +} diff --git a/nginx/lua/mail.lua b/nginx/lua/mail.lua new file mode 100644 index 0000000..fbf901b --- /dev/null +++ b/nginx/lua/mail.lua @@ -0,0 +1,38 @@ +local request = require 'request' +local response = require 'response' +local helper = require 'helper' + +local request_uri +local response_type +local domain +local result +local err +local red = helper.redis.connect() + +request_uri = helper.parseURL() +if type(request_uri) ~= "table" or table.getn(request_uri) < 2 then + response.quit(ngx.HTTP_BAD_REQUEST) + return +end + +local response_type = request_uri[2] +if not response.isValidResponseType(response_type) then + response.quit(ngx.HTTP_BAD_REQUEST) + return +end + +local data = request.getPostData() +if data == nil or data['email'] == nil then + response.quit(ngx.HTTP_BAD_REQUEST) + return +end + +domain = helper.getDomain(data['email']) +if not domain then + response.quit(ngx.HTTP_BAD_REQUEST) + return +end + +result = helper.redis.checkDomain(red, 'disposable', domain) + +response.createResponse(response_type, data['email'], domain, result) diff --git a/nginx/lua/modules/dns.lua b/nginx/lua/modules/dns.lua new file mode 100644 index 0000000..6650aea --- /dev/null +++ b/nginx/lua/modules/dns.lua @@ -0,0 +1,981 @@ +-- Copyright (C) Yichun Zhang (agentzh) + + +-- local socket = require "socket" +local bit = require "bit" +local udp = ngx.socket.udp +local rand = math.random +local char = string.char +local byte = string.byte +local find = string.find +local gsub = string.gsub +local sub = string.sub +local rep = string.rep +local format = string.format +local band = bit.band +local rshift = bit.rshift +local lshift = bit.lshift +local insert = table.insert +local concat = table.concat +local re_sub = ngx.re.sub +local tcp = ngx.socket.tcp +local log = ngx.log +local DEBUG = ngx.DEBUG +local unpack = unpack +local setmetatable = setmetatable +local type = type +local ipairs = ipairs + + +local ok, new_tab = pcall(require, "table.new") +if not ok then + new_tab = function (narr, nrec) return {} end +end + + +local DOT_CHAR = byte(".") +local ZERO_CHAR = byte("0") +local COLON_CHAR = byte(":") + +local IP6_ARPA = "ip6.arpa" + +local TYPE_A = 1 +local TYPE_NS = 2 +local TYPE_CNAME = 5 +local TYPE_SOA = 6 +local TYPE_PTR = 12 +local TYPE_MX = 15 +local TYPE_TXT = 16 +local TYPE_AAAA = 28 +local TYPE_SRV = 33 +local TYPE_SPF = 99 + +local CLASS_IN = 1 + +local SECTION_AN = 1 +local SECTION_NS = 2 +local SECTION_AR = 3 + + +local _M = { + _VERSION = '0.21', + TYPE_A = TYPE_A, + TYPE_NS = TYPE_NS, + TYPE_CNAME = TYPE_CNAME, + TYPE_SOA = TYPE_SOA, + TYPE_PTR = TYPE_PTR, + TYPE_MX = TYPE_MX, + TYPE_TXT = TYPE_TXT, + TYPE_AAAA = TYPE_AAAA, + TYPE_SRV = TYPE_SRV, + TYPE_SPF = TYPE_SPF, + CLASS_IN = CLASS_IN, + SECTION_AN = SECTION_AN, + SECTION_NS = SECTION_NS, + SECTION_AR = SECTION_AR +} + + +local resolver_errstrs = { + "format error", -- 1 + "server failure", -- 2 + "name error", -- 3 + "not implemented", -- 4 + "refused", -- 5 +} + +local soa_int32_fields = { "serial", "refresh", "retry", "expire", "minimum" } + +local mt = { __index = _M } + + +local arpa_tmpl = new_tab(72, 0) + +for i = 1, #IP6_ARPA do + arpa_tmpl[64 + i] = byte(IP6_ARPA, i) +end + +for i = 2, 64, 2 do + arpa_tmpl[i] = DOT_CHAR +end + + +function _M.new(class, opts) + if not opts then + return nil, "no options table specified" + end + + local servers = opts.nameservers + if not servers or #servers == 0 then + return nil, "no nameservers specified" + end + + local timeout = opts.timeout or 2000 -- default 2 sec + + local n = #servers + + local socks = {} + + for i = 1, n do + local server = servers[i] + local sock, err = udp() + if not sock then + return nil, "failed to create udp socket: " .. err + end + + local host, port + if type(server) == 'table' then + host = server[1] + port = server[2] or 53 + + else + host = server + port = 53 + servers[i] = {host, port} + end + + local ok, err = sock:setpeername(host, port) + if not ok then + return nil, "failed to set peer name: " .. err + end + + sock:settimeout(timeout) + + insert(socks, sock) + end + + local tcp_sock, err = tcp() + if not tcp_sock then + return nil, "failed to create tcp socket: " .. err + end + + tcp_sock:settimeout(timeout) + + return setmetatable( + { cur = rand(1, n), socks = socks, + tcp_sock = tcp_sock, + servers = servers, + retrans = opts.retrans or 5, + no_recurse = opts.no_recurse, + }, mt) +end + + +local function pick_sock(self, socks) + local cur = self.cur + + if cur == #socks then + self.cur = 1 + else + self.cur = cur + 1 + end + + return socks[cur] +end + + +local function _get_cur_server(self) + local cur = self.cur + + local servers = self.servers + + if cur == 1 then + return servers[#servers] + end + + return servers[cur - 1] +end + + +function _M.set_timeout(self, timeout) + local socks = self.socks + if not socks then + return nil, "not initialized" + end + + for i = 1, #socks do + local sock = socks[i] + sock:settimeout(timeout) + end + + local tcp_sock = self.tcp_sock + if not tcp_sock then + return nil, "not initialized" + end + + tcp_sock:settimeout(timeout) +end + + +local function _encode_name(s) + return char(#s) .. s +end + + +local function _decode_name(buf, pos) + local labels = {} + local nptrs = 0 + local p = pos + while nptrs < 128 do + local fst = byte(buf, p) + + if not fst then + return nil, 'truncated'; + end + + -- print("fst at ", p, ": ", fst) + + if fst == 0 then + if nptrs == 0 then + pos = pos + 1 + end + break + end + + if band(fst, 0xc0) ~= 0 then + -- being a pointer + if nptrs == 0 then + pos = pos + 2 + end + + nptrs = nptrs + 1 + + local snd = byte(buf, p + 1) + if not snd then + return nil, 'truncated' + end + + p = lshift(band(fst, 0x3f), 8) + snd + 1 + + -- print("resolving ptr ", p, ": ", byte(buf, p)) + + else + -- being a label + local label = sub(buf, p + 1, p + fst) + insert(labels, label) + + -- print("resolved label ", label) + + p = p + fst + 1 + + if nptrs == 0 then + pos = p + end + end + end + + return concat(labels, "."), pos +end + + +local function _build_request(qname, id, no_recurse, opts) + local qtype + + if opts then + qtype = opts.qtype + end + + if not qtype then + qtype = 1 -- A record + end + + local ident_hi = char(rshift(id, 8)) + local ident_lo = char(band(id, 0xff)) + + local flags + if no_recurse then + -- print("found no recurse") + flags = "\0\0" + else + flags = "\1\0" + end + + local nqs = "\0\1" + local nan = "\0\0" + local nns = "\0\0" + local nar = "\0\0" + local typ = char(rshift(qtype, 8), band(qtype, 0xff)) + local class = "\0\1" -- the Internet class + + if byte(qname, 1) == DOT_CHAR then + return nil, "bad name" + end + + local name = gsub(qname, "([^.]+)%.?", _encode_name) .. '\0' + + return { + ident_hi, ident_lo, flags, nqs, nan, nns, nar, + name, typ, class + } +end + + +local function parse_section(answers, section, buf, start_pos, size, + should_skip) + local pos = start_pos + + for _ = 1, size do + -- print(format("ans %d: qtype:%d qclass:%d", i, qtype, qclass)) + local ans = {} + + if not should_skip then + insert(answers, ans) + end + + ans.section = section + + local name + name, pos = _decode_name(buf, pos) + if not name then + return nil, pos + end + + ans.name = name + + -- print("name: ", name) + + local type_hi = byte(buf, pos) + local type_lo = byte(buf, pos + 1) + local typ = lshift(type_hi, 8) + type_lo + + ans.type = typ + + -- print("type: ", typ) + + local class_hi = byte(buf, pos + 2) + local class_lo = byte(buf, pos + 3) + local class = lshift(class_hi, 8) + class_lo + + ans.class = class + + -- print("class: ", class) + + local byte_1, byte_2, byte_3, byte_4 = byte(buf, pos + 4, pos + 7) + + local ttl = lshift(byte_1, 24) + lshift(byte_2, 16) + + lshift(byte_3, 8) + byte_4 + + -- print("ttl: ", ttl) + + ans.ttl = ttl + + local len_hi = byte(buf, pos + 8) + local len_lo = byte(buf, pos + 9) + local len = lshift(len_hi, 8) + len_lo + + -- print("record len: ", len) + + pos = pos + 10 + + if typ == TYPE_A then + + if len ~= 4 then + return nil, "bad A record value length: " .. len + end + + local addr_bytes = { byte(buf, pos, pos + 3) } + local addr = concat(addr_bytes, ".") + -- print("ipv4 address: ", addr) + + ans.address = addr + + pos = pos + 4 + + elseif typ == TYPE_CNAME then + + local cname, p = _decode_name(buf, pos) + if not cname then + return nil, pos + end + + if p - pos ~= len then + return nil, format("bad cname record length: %d ~= %d", + p - pos, len) + end + + pos = p + + -- print("cname: ", cname) + + ans.cname = cname + + elseif typ == TYPE_AAAA then + + if len ~= 16 then + return nil, "bad AAAA record value length: " .. len + end + + local addr_bytes = { byte(buf, pos, pos + 15) } + local flds = {} + for i = 1, 16, 2 do + local a = addr_bytes[i] + local b = addr_bytes[i + 1] + if a == 0 then + insert(flds, format("%x", b)) + + else + insert(flds, format("%x%02x", a, b)) + end + end + + -- we do not compress the IPv6 addresses by default + -- due to performance considerations + + ans.address = concat(flds, ":") + + pos = pos + 16 + + elseif typ == TYPE_MX then + + -- print("len = ", len) + + if len < 3 then + return nil, "bad MX record value length: " .. len + end + + local pref_hi = byte(buf, pos) + local pref_lo = byte(buf, pos + 1) + + ans.preference = lshift(pref_hi, 8) + pref_lo + + local host, p = _decode_name(buf, pos + 2) + if not host then + return nil, pos + end + + if p - pos ~= len then + return nil, format("bad cname record length: %d ~= %d", + p - pos, len) + end + + ans.exchange = host + + pos = p + + elseif typ == TYPE_SRV then + if len < 7 then + return nil, "bad SRV record value length: " .. len + end + + local prio_hi = byte(buf, pos) + local prio_lo = byte(buf, pos + 1) + ans.priority = lshift(prio_hi, 8) + prio_lo + + local weight_hi = byte(buf, pos + 2) + local weight_lo = byte(buf, pos + 3) + ans.weight = lshift(weight_hi, 8) + weight_lo + + local port_hi = byte(buf, pos + 4) + local port_lo = byte(buf, pos + 5) + ans.port = lshift(port_hi, 8) + port_lo + + local name, p = _decode_name(buf, pos + 6) + if not name then + return nil, pos + end + + if p - pos ~= len then + return nil, format("bad srv record length: %d ~= %d", + p - pos, len) + end + + ans.target = name + + pos = p + + elseif typ == TYPE_NS then + + local name, p = _decode_name(buf, pos) + if not name then + return nil, pos + end + + if p - pos ~= len then + return nil, format("bad cname record length: %d ~= %d", + p - pos, len) + end + + pos = p + + -- print("name: ", name) + + ans.nsdname = name + + elseif typ == TYPE_TXT or typ == TYPE_SPF then + + local key = (typ == TYPE_TXT) and "txt" or "spf" + + local slen = byte(buf, pos) + if slen + 1 > len then + -- truncate the over-run TXT record data + slen = len + end + + -- print("slen: ", len) + + local val = sub(buf, pos + 1, pos + slen) + local last = pos + len + pos = pos + slen + 1 + + if pos < last then + -- more strings to be processed + -- this code path is usually cold, so we do not + -- merge the following loop on this code path + -- with the processing logic above. + + val = {val} + local idx = 2 + repeat + local slen = byte(buf, pos) + if pos + slen + 1 > last then + -- truncate the over-run TXT record data + slen = last - pos - 1 + end + + val[idx] = sub(buf, pos + 1, pos + slen) + idx = idx + 1 + pos = pos + slen + 1 + + until pos >= last + end + + ans[key] = val + + elseif typ == TYPE_PTR then + + local name, p = _decode_name(buf, pos) + if not name then + return nil, pos + end + + if p - pos ~= len then + return nil, format("bad cname record length: %d ~= %d", + p - pos, len) + end + + pos = p + + -- print("name: ", name) + + ans.ptrdname = name + + elseif typ == TYPE_SOA then + local name, p = _decode_name(buf, pos) + if not name then + return nil, pos + end + ans.mname = name + + pos = p + name, p = _decode_name(buf, pos) + if not name then + return nil, pos + end + ans.rname = name + + for _, field in ipairs(soa_int32_fields) do + local byte_1, byte_2, byte_3, byte_4 = byte(buf, p, p + 3) + ans[field] = lshift(byte_1, 24) + lshift(byte_2, 16) + + lshift(byte_3, 8) + byte_4 + p = p + 4 + end + + pos = p + + else + -- for unknown types, just forward the raw value + + ans.rdata = sub(buf, pos, pos + len - 1) + pos = pos + len + end + end + + return pos +end + + +local function parse_response(buf, id, opts) + local n = #buf + if n < 12 then + return nil, 'truncated'; + end + + -- header layout: ident flags nqs nan nns nar + + local ident_hi = byte(buf, 1) + local ident_lo = byte(buf, 2) + local ans_id = lshift(ident_hi, 8) + ident_lo + + -- print("id: ", id, ", ans id: ", ans_id) + + if ans_id ~= id then + -- identifier mismatch and throw it away + log(DEBUG, "id mismatch in the DNS reply: ", ans_id, " ~= ", id) + return nil, "id mismatch" + end + + local flags_hi = byte(buf, 3) + local flags_lo = byte(buf, 4) + local flags = lshift(flags_hi, 8) + flags_lo + + -- print(format("flags: 0x%x", flags)) + + if band(flags, 0x8000) == 0 then + return nil, format("bad QR flag in the DNS response") + end + + if band(flags, 0x200) ~= 0 then + return nil, "truncated" + end + + local code = band(flags, 0xf) + + -- print(format("code: %d", code)) + + local nqs_hi = byte(buf, 5) + local nqs_lo = byte(buf, 6) + local nqs = lshift(nqs_hi, 8) + nqs_lo + + -- print("nqs: ", nqs) + + if nqs ~= 1 then + return nil, format("bad number of questions in DNS response: %d", nqs) + end + + local nan_hi = byte(buf, 7) + local nan_lo = byte(buf, 8) + local nan = lshift(nan_hi, 8) + nan_lo + + -- print("nan: ", nan) + + local nns_hi = byte(buf, 9) + local nns_lo = byte(buf, 10) + local nns = lshift(nns_hi, 8) + nns_lo + + local nar_hi = byte(buf, 11) + local nar_lo = byte(buf, 12) + local nar = lshift(nar_hi, 8) + nar_lo + + -- skip the question part + + local ans_qname, pos = _decode_name(buf, 13) + if not ans_qname then + return nil, pos + end + + -- print("qname in reply: ", ans_qname) + + -- print("question: ", sub(buf, 13, pos)) + + if pos + 3 + nan * 12 > n then + -- print(format("%d > %d", pos + 3 + nan * 12, n)) + return nil, 'truncated'; + end + + -- question section layout: qname qtype(2) qclass(2) + + --[[ + local type_hi = byte(buf, pos) + local type_lo = byte(buf, pos + 1) + local ans_type = lshift(type_hi, 8) + type_lo + ]] + + -- print("ans qtype: ", ans_type) + + local class_hi = byte(buf, pos + 2) + local class_lo = byte(buf, pos + 3) + local qclass = lshift(class_hi, 8) + class_lo + + -- print("ans qclass: ", qclass) + + if qclass ~= 1 then + return nil, format("unknown query class %d in DNS response", qclass) + end + + pos = pos + 4 + + local answers = {} + + if code ~= 0 then + answers.errcode = code + answers.errstr = resolver_errstrs[code] or "unknown" + end + + local authority_section, additional_section + + if opts then + authority_section = opts.authority_section + additional_section = opts.additional_section + if opts.qtype == TYPE_SOA then + authority_section = true + end + end + + local err + + pos, err = parse_section(answers, SECTION_AN, buf, pos, nan) + + if not pos then + return nil, err + end + + if not authority_section and not additional_section then + return answers + end + + pos, err = parse_section(answers, SECTION_NS, buf, pos, nns, + not authority_section) + + if not pos then + return nil, err + end + + if not additional_section then + return answers + end + + pos, err = parse_section(answers, SECTION_AR, buf, pos, nar) + + if not pos then + return nil, err + end + + return answers +end + + +local function _gen_id(self) + local id = self._id -- for regression testing + if id then + return id + end + return rand(0, 65535) -- two bytes +end + + +local function _tcp_query(self, query, id, opts) + local sock = self.tcp_sock + if not sock then + return nil, "not initialized" + end + + log(DEBUG, "query the TCP server due to reply truncation") + + local server = _get_cur_server(self) + + local ok, err = sock:connect(server[1], server[2]) + if not ok then + return nil, "failed to connect to TCP server " + .. concat(server, ":") .. ": " .. err + end + + query = concat(query, "") + local len = #query + + local len_hi = char(rshift(len, 8)) + local len_lo = char(band(len, 0xff)) + + local bytes, err = sock:send({len_hi, len_lo, query}) + if not bytes then + return nil, "failed to send query to TCP server " + .. concat(server, ":") .. ": " .. err + end + + local buf, err = sock:receive(2) + if not buf then + return nil, "failed to receive the reply length field from TCP server " + .. concat(server, ":") .. ": " .. err + end + + len_hi = byte(buf, 1) + len_lo = byte(buf, 2) + len = lshift(len_hi, 8) + len_lo + + -- print("tcp message len: ", len) + + buf, err = sock:receive(len) + if not buf then + return nil, "failed to receive the reply message body from TCP server " + .. concat(server, ":") .. ": " .. err + end + + local answers, err = parse_response(buf, id, opts) + if not answers then + return nil, "failed to parse the reply from the TCP server " + .. concat(server, ":") .. ": " .. err + end + + sock:close() + + return answers +end + + +function _M.tcp_query(self, qname, opts) + local socks = self.socks + if not socks then + return nil, "not initialized" + end + + pick_sock(self, socks) + + local id = _gen_id(self) + + local query, err = _build_request(qname, id, self.no_recurse, opts) + if not query then + return nil, err + end + + return _tcp_query(self, query, id, opts) +end + + +function _M.query(self, qname, opts, tries) + local socks = self.socks + if not socks then + return nil, "not initialized" + end + + local id = _gen_id(self) + + local query, err = _build_request(qname, id, self.no_recurse, opts) + if not query then + return nil, err + end + + -- local cjson = require "cjson" + -- print("query: ", cjson.encode(concat(query, ""))) + + local retrans = self.retrans + if tries then + tries[1] = nil + end + + -- print("retrans: ", retrans) + + for i = 1, retrans do + local sock = pick_sock(self, socks) + + local ok + ok, err = sock:send(query) + if not ok then + local server = _get_cur_server(self) + err = "failed to send request to UDP server " + .. concat(server, ":") .. ": " .. err + + else + local buf + + for _ = 1, 128 do + buf, err = sock:receive(4096) + if err then + local server = _get_cur_server(self) + err = "failed to receive reply from UDP server " + .. concat(server, ":") .. ": " .. err + break + end + + if buf then + local answers + answers, err = parse_response(buf, id, opts) + if err == "truncated" then + answers, err = _tcp_query(self, query, id, opts) + end + + if err and err ~= "id mismatch" then + break + end + + if answers then + return answers, nil, tries + end + end + -- only here in case of an "id mismatch" + end + end + + if tries then + tries[i] = err + tries[i + 1] = nil -- ensure termination for user supplied table + end + end + + return nil, err, tries +end + + +function _M.compress_ipv6_addr(addr) + local addr = re_sub(addr, "^(0:)+|(:0)+$|:(0:)+", "::", "jo") + if addr == "::0" then + addr = "::" + end + + return addr +end + + +local function _expand_ipv6_addr(addr) + if find(addr, "::", 1, true) then + local ncol, addrlen = 8, #addr + + for i = 1, addrlen do + if byte(addr, i) == COLON_CHAR then + ncol = ncol - 1 + end + end + + if byte(addr, 1) == COLON_CHAR then + addr = "0" .. addr + end + + if byte(addr, -1) == COLON_CHAR then + addr = addr .. "0" + end + + addr = re_sub(addr, "::", ":" .. rep("0:", ncol), "jo") + end + + return addr +end + + +_M.expand_ipv6_addr = _expand_ipv6_addr + + +function _M.arpa_str(addr) + if find(addr, ":", 1, true) then + addr = _expand_ipv6_addr(addr) + local idx, hidx, addrlen = 1, 1, #addr + + for i = addrlen, 0, -1 do + local s = byte(addr, i) + if s == COLON_CHAR or not s then + for _ = hidx, 4 do + arpa_tmpl[idx] = ZERO_CHAR + idx = idx + 2 + end + hidx = 1 + else + arpa_tmpl[idx] = s + idx = idx + 2 + hidx = hidx + 1 + end + end + + addr = char(unpack(arpa_tmpl)) + else + addr = re_sub(addr, [[(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})]], + "$4.$3.$2.$1.in-addr.arpa", "ajo") + end + + return addr +end + + +function _M.reverse_query(self, addr) + return self.query(self, self.arpa_str(addr), + {qtype = self.TYPE_PTR}) +end + + +return _M diff --git a/nginx/lua/modules/helper.lua b/nginx/lua/modules/helper.lua new file mode 100644 index 0000000..eb680a5 --- /dev/null +++ b/nginx/lua/modules/helper.lua @@ -0,0 +1,60 @@ +local json = require 'json' +local redis = require 'redis' +local response = require 'response' + +local module = { + ['redis'] = {} +} + +module.redis.connect = function() + local red = redis:new(); + red:set_timeout(1000); + local ok, err = red:connect('redis', 6379) + if not ok then + ngx.log(ngx.ERR, err) + response.quit(ngx.HTTP_SERVICE_UNAVAILABLE) + return + end + + return red +end + +module.redis.checkDomain = function(red, category, domain) + local result, err = red:hmget(category, domain) + if err then + ngx.log(ngx.ERR, err) + response.quit(ngx.HTTP_SERVICE_UNAVAILABLE) + return + end + return type(result) == 'string' and true or false +end + +local function split(str, pat) + local t = {} + local fpat = "(.-)" .. pat + local last_end = 1 + local s, e, cap = str:find(fpat, 1) + while s do + if s ~= 1 or cap ~= "" then + table.insert(t,cap) + end + last_end = e+1 + s, e, cap = str:find(fpat, last_end) + end + if last_end <= #str then + cap = str:sub(last_end) + table.insert(t, cap) + end + return t +end + +module.parseURL = function(uri) + local uri = uri or ngx.var.request_uri + return split(uri, '[\\/]+') +end + +module.getDomain = function(str) + return string.match(str, '%w+%.%w+$') +end + +return module diff --git a/nginx/lua/modules/ip.lua b/nginx/lua/modules/ip.lua new file mode 100644 index 0000000..95ab425 --- /dev/null +++ b/nginx/lua/modules/ip.lua @@ -0,0 +1,504 @@ +-- +-- IP address manipulation library in Lua +-- +-- @author leite (xico@simbio.se) +-- @license MIT +-- @copyright Simbiose 2015 + +local math, string, table, bit_available, bit = +require [[math]], require [[string]], require [[table]], pcall(require, 'bit') + +-- yey! Lua 5.3 bitwise keywords available +if not bit_available and _VERSION == 'Lua 5.3' then + bit = assert(load([[return { + band = function (a, b) return a & b end, + bor = function (a, b) return a | b end, + rshift = function (a, b) return a >> b end, + lshift = function (a, b) return a << b end + }]]))() +end + +local ip, modf, len, match, find, format, concat, insert, band, bor, rshift, lshift, type, +assert, error, tonumber, pcall, setmetatable = +{}, math.modf, string.len, string.match, string.find, string.format, table.concat, table.insert, +bit.band, bit.bor, bit.rshift, bit.lshift, type, assert, error, tonumber, pcall, setmetatable + +local _octets, _octet, _part, _parts_with_octets, EMPTY, COLON, ZERO = +'^((0?[xX]?)[%da-fA-F]+)%.((0?[xX]?)[%da-fA-F]+)%.((0?[xX]?)[%da-fA-F]+)%.((0?[xX]?)[%da-fA-F]+)/?(%d*)$', +'^((0?[xX]?)[%da-fA-F]+)((/?)(%d*))$', '(:?)([^:/$]*)(:?)', +'^([:%dxXa-fA-F]-::?)(([%dxXa-fA-F]*)[%.%dxXa-fA-F]*)/?(%d*)$', '', ':', '0' + +math, string, bit, table = nil, nil, nil, nil + +-- IP special ranges + +local special_ranges = { + ipv4 = { + {'unspecified', octets={0, 0, 0, 0}, _cidr=8}, + {'broadcast', octets={255, 255, 255, 255}, _cidr=32}, + {'multicast', octets={224, 0, 0, 0}, _cidr=4}, + {'linkLocal', octets={169, 254, 0, 0}, _cidr=16}, + {'loopback', octets={127, 0, 0, 0}, _cidr=8}, + { + 'private', {octets={10, 0, 0, 0}, _cidr=8}, {octets={172, 16, 0, 0}, _cidr=12}, + {octets={192, 168, 0, 0}, _cidr=16} + }, { + 'reserved', {octets={192, 0, 0, 0}, _cidr=24}, {octets={192, 0, 2, 0}, _cidr=24}, + {octets={192, 88, 99, 0}, _cidr=24}, {octets={198, 51, 100, 0}, _cidr=24}, + {octets={203, 0, 113, 0}, _cidr=24}, {octets={240, 0, 0, 0}, _cidr=4} + } + }, ipv6 = { + {'unspecified', parts={0, 0, 0, 0, 0, 0, 0, 0}, _cidr=128}, + {'linkLocal', parts={0xfe80, 0, 0, 0, 0, 0, 0, 0}, _cidr=10}, + {'multicast', parts={0xff00, 0, 0, 0, 0, 0, 0, 0}, _cidr=8}, + {'loopback', parts={0, 0, 0, 0, 0, 0, 0, 1}, _cidr=128}, + {'uniqueLocal', parts={0xfc00, 0, 0, 0, 0, 0, 0, 0}, _cidr=7}, + {'ipv4Mapped', parts={0, 0, 0, 0, 0, 0xffff, 0, 0}, _cidr=96}, + {'rfc6145', parts={0, 0, 0, 0, 0xffff, 0, 0, 0}, _cidr=96}, + {'rfc6052', parts={0x64, 0xff9b, 0, 0, 0, 0, 0, 0}, _cidr=96}, + {'6to4', parts={0x2002, 0, 0, 0, 0, 0, 0, 0}, _cidr=16}, + {'teredo', parts={0x2001, 0, 0, 0, 0, 0, 0, 0}, _cidr=32}, + {'reserved', parts={0x2001, 0xdb8, 0, 0, 0, 0, 0, 0}, _cidr=32} + } +} + +-- assert ipv4 octets +-- +-- @table octets +-- @return boolean, [string] + +local function assert_ipv4 (octets) + if not(octets and type(octets) == 'table') then + return false, 'octets should be a table' + end + if not(#octets == 4) then + return false, 'ipv4 octet count should be 4' + end + if not((-1 < octets[1] and 256 > octets[1]) and (-1 < octets[2] and 256 > octets[2]) and + (-1 < octets[3] and 256 > octets[3]) and (-1 < octets[4] and 256 > octets[4])) then + return false, 'ipv4 octet is a byte' + end + return true +end + +-- assert ipv6 parts +-- +-- @table parts +-- @return boolean, [string] + +local function assert_ipv6 (parts) + if not(parts and type(parts) == 'table') then + return false, 'parts should be a table' + end + if not(#parts == 8) then + return false, 'ipv6 part count should be 8' + end + if not((-1 < parts[1] and 0x10000 > parts[1]) and (-1 < parts[2] and 0x10000 > parts[2]) and + (-1 < parts[3] and 0x10000 > parts[3]) and (-1 < parts[4] and 0x10000 > parts[4]) and + (-1 < parts[5] and 0x10000 > parts[5]) and (-1 < parts[6] and 0x10000 > parts[6]) and + (-1 < parts[7] and 0x10000 > parts[7]) and (-1 < parts[8] and 0x10000 > parts[8])) then + return false, 'ipv6 part should fit to two octets' + end + return true +end + +-- generic CIDR matcher +-- +-- @table first +-- @table second +-- @number part_size +-- @number cidr_bits +-- @return boolean + +local function match_cidr(first, second, part_size, cidr_bits) + assert(#first == #second, 'cannot match CIDR for objects with different lengths') + local part, shift = 0, 0 + while cidr_bits > 0 do + part = part + 1 + shift = part_size - cidr_bits + shift = shift < 0 and 0 or shift + if rshift(first[part], shift) ~= rshift(second[part], shift) then + return false + end + cidr_bits = cidr_bits - part_size + end + return true +end + +-- funct address named range matching +-- +-- @table address +-- @table range_list +-- @string default_name +-- @return string + +local function subnet_match(address, range_list, default_name) + local subnet = {} + for i = 1, #range_list do + subnet = range_list[i] + if #subnet == 1 then + if address:match(subnet) then + return subnet[1] + end + else + for j = 2, #subnet do + if address:match(subnet[j]) then + return subnet[1] + end + end + end + end + + return default_name or 'unicast' +end + +-- parse IP version 4 +-- +-- @string string +-- @table octets +-- @number cidr +-- @return boolean, [string] + +local function parse_v4(string, octets, cidr) + local value, hex, _, __, _cidr = match(string, _octet) + + if value then + value = tonumber(value, hex == ZERO and 8 or nil) + if value > 0xffffffff or value < 0 then + return false, 'address outside defined range' + end + octets[1], octets[2], octets[3], octets[4] = + band(rshift(value, 24), 0xff), band(rshift(value, 16), 0xff), + band(rshift(value, 8), 0xff), band(value, 0xff) + return tonumber(_cidr == EMPTY and 32 or _cidr) + end + + local st, _st, nd, _nd, rd, _rd, th, _th, _cidr = match(string, _octets) + + if not(st) then + return false, 'invalid ip address' + end + + octets[1], octets[2], octets[3], octets[4] = + tonumber(st, _st == ZERO and 8 or nil), tonumber(nd, _nd == ZERO and 8 or nil), + tonumber(rd, _rd == ZERO and 8 or nil), tonumber(th, _th == ZERO and 8 or nil) + return tonumber(_cidr == EMPTY and (cidr and cidr or 32) or _cidr) +end + +-- parse IP version 6 +-- +-- @string string +-- @table parts +-- @table octets +-- @number cidr +-- @return boolean, [string] + +local function parse_v6(string, parts, octets, cidr) + local nd_sep, part, l_sep, count, double, length, index, last, string, octets_st, sep, _cidr = + '', '', false, 1, 0, 0, 0, 0, match(string, _parts_with_octets) + + if not string or EMPTY == string then + return false, 'invalid ipv6 format' + end + + if #octets_st == #sep then + string = string .. sep + else + local err, message = parse_v4(octets_st, octets) + if not err then + return err, message + end + end + + _cidr, length, index, last, sep, part, nd_sep = + tonumber(_cidr==EMPTY and (cidr and cidr or 128) or _cidr), len(string), find(string, _part) + + while index and index <= length do + if sep == COLON and nd_sep == COLON then + if part == EMPTY or l_sep then + if double > 0 then + return false, 'string is not formatted like ip address' + end + double = count + end + elseif sep == COLON or nd_sep == COLON then + if l_sep and sep == COLON then + if double > 0 then + return false, 'string is not formatted like ip address' + end + double = count + end + end + + insert(parts, tonumber(part == EMPTY and '0' or part, 16)) + + l_sep, count, index, last, sep, part, nd_sep = + nd_sep == COLON, count + 1, find(string, _part, last + 1) + end + + if #octets > 0 then + insert(parts, bor(lshift(octets[1], 8), octets[2])) + insert(parts, bor(lshift(octets[3], 8), octets[4])) + length = 7 + else + length = 9 + end + + for index = 1, (length - count) do + insert(parts, double, 0) + end + + return _cidr +end + +-- ip metatable + +local ip_metatable = { + + -- set CIDR + -- + -- @number cidr + -- @return metatable + + cidr = function(self, cidr) + self._cidr = cidr + return self + end, + + -- get address named range + -- + -- @return string + + range = function(self) + return subnet_match(self, special_ranges[self:kind()]) + end, + + -- get or match address kind + -- + -- @string [kind] + -- @return string|boolean + + kind = function(self, kind) + local _kind = #self.parts > 0 and 'ipv6' or (#self.octets > 0 and 'ipv4' or EMPTY) + if kind then + return kind == _kind + end + return _kind + end, + + -- match two addresses + -- + -- @table address + -- @number cidr + -- @return boolean + + match = function(self, address, cidr) + if cidr and address._cidr then + address._cidr = cidr + end + return self.__eq(self, address) + end, + + -- converts ipv4 to ipv4-mapped ipv6 address + -- + -- @return string|nil + + ipv4_mapped_address = function (self) + return self:kind('ipv4') and ip.parsev6('::ffff:' .. self:__tostring()) or nil + end, + + -- check if it's a ipv4 mapped address + -- + -- @return boolean + + is_ipv4_mapped = function (self) + return self:range() == 'ipv4Mapped' + end, + + -- converts ipv6 ipv4-mapped address to ipv4 address + -- + -- @return metatable + + ipv4_address = function (self) + assert(self:is_ipv4_mapped(), 'trying to convert a generic ipv6 address to ipv4') + local high, low = self.parts[7], self.parts[8] + return ip.v4({rshift(high, 8), band(high, 0xff), rshift(low, 8), band(low, 0xff)}) + end, + + -- IP table to string + -- + -- @return string + + __tostring = function(self) + if self:kind('ipv4') then + return concat(self.octets, '.') + end + + local part, state, size, output = '', 0, #self.parts, {} + + for i = 1, size do + part = format('%x', self.parts[i]) + if 0 == state then + insert(output, (ZERO == part and EMPTY or part)) + state = 1 + elseif 1 == state then + if ZERO == part then + state = 2 + else + insert(output, part) + end + elseif 2 == state then + if ZERO ~= part then + insert(output, EMPTY) + insert(output, part) + state = 3 + end + else + insert(output, part) + end + end + + if 2 == state then + insert(output, COLON) + end + + return concat(output, COLON) + end, + + -- compare two IP addresses + -- + -- @table value + -- @return boolean + + __eq = function(self, value) + if #self.parts > 0 then + assert(value.parts and #value.parts > 0, 'cannot match different address version') + return match_cidr(self.parts, value.parts, 16, value._cidr) + end + assert(value.octets and #value.octets > 0, 'cannot match different address version') + return match_cidr(self.octets, value.octets, 8, value._cidr) + end +} + +ip_metatable.__index = ip_metatable + +-- create new IP metatable +-- +-- @table parts +-- @table octets +-- @number cidr +-- @return metatable + +local function new (parts, octets, cidr) + return setmetatable({octets=octets, parts=parts, _cidr=cidr}, ip_metatable) +end + +-- assert IP version 4 octets and create it's metatable +-- +-- @table octets +-- @number cidr +-- @return metatable + +function ip.v4 (octets, cidr) + local err, message = assert_ipv4(octets) + assert(err, message) + return new({}, octets, cidr or 32) +end + +-- assert IP version 6 parts and create it's metatable +-- +-- @table parts +-- @number cidr +-- @table [octets] +-- @return metatable + +function ip.v6 (parts, cidr, octets) + local err, message = assert_ipv6(parts) + assert(err, message) + + if octets and #octets > 0 then + err, message = assert_ipv4(octets) + assert(err, message) + end + + return new(parts, octets or {}, cidr or 128) +end + +-- parse string to IP version 4 metatable +-- +-- @string string +-- @number [cidr] +-- @return metatable + +function ip.parsev4 (string, cidr) + local octets, message = {}, '' + cidr, message = parse_v4(string, octets, cidr) + assert(cidr ~= false, message) + return ip.v4(octets, cidr) +end + +-- parse string to IP version 6 metatable +-- +-- @string string +-- @number [cidr] +-- @return metatable + +function ip.parsev6 (string, cidr) + local parts, octets, message = {}, {}, '' + cidr, message = parse_v6(string, parts, octets, cidr) + assert(cidr ~= false, message) + return ip.v6(parts, cidr, octets) +end + +-- check and parse string to IP metatable +-- +-- @string string +-- @return metatable + +function ip.parse (string) + if ip.isv6(string) then + return ip.parsev6(string) + elseif ip.isv4(string) then + return ip.parsev4(string) + end + error('the address has neither IPv6 nor IPv4 format') +end + +-- check if string is a IP version 4 address +-- +-- @string string +-- @boolean validate +-- @return boolean + +function ip.isv4 (string, validate) + if validate then + local octets = {} + return parse_v4(string, octets) ~= false and assert_ipv4(octets) + end + return find(string, _octet) ~= nil or find(string, _octets) ~= nil +end + +-- check if string is a IP version 6 address +-- +-- @string string +-- @boolean validate +-- @return boolean + +function ip.isv6 (string, validate) + if validate then + local octets, parts = {}, {} + return parse_v6(string, parts, octets) ~= false and assert_ipv6(parts) + end + return find(string, _parts_with_octets) ~= nil +end + +-- check if IP address is valid +-- +-- @string string +-- @return boolean + +function ip.valid (string) + return pcall(ip.parse, string) +end + +return ip \ No newline at end of file diff --git a/nginx/lua/modules/json.lua b/nginx/lua/modules/json.lua new file mode 100644 index 0000000..3a495f2 --- /dev/null +++ b/nginx/lua/modules/json.lua @@ -0,0 +1,385 @@ +-- +-- json.lua +-- +-- Copyright (c) 2015 rxi +-- +-- This library is free software; you can redistribute it and/or modify it +-- under the terms of the MIT license. See LICENSE for details. +-- + +local json = { _version = "0.1.0" } + +------------------------------------------------------------------------------- +-- Encode +------------------------------------------------------------------------------- + +local encode + +local escape_char_map = { + [ "\\" ] = "\\\\", + [ "\"" ] = "\\\"", + [ "\b" ] = "\\b", + [ "\f" ] = "\\f", + [ "\n" ] = "\\n", + [ "\r" ] = "\\r", + [ "\t" ] = "\\t", +} + +local escape_char_map_inv = { [ "\\/" ] = "/" } +for k, v in pairs(escape_char_map) do + escape_char_map_inv[v] = k +end + + +local function escape_char(c) + return escape_char_map[c] or string.format("\\u%04x", c:byte()) +end + + +local function encode_nil(val) + return "null" +end + + +local function encode_table(val, stack) + local res = {} + stack = stack or {} + + -- Circular reference? + if stack[val] then error("circular reference") end + + stack[val] = true + + if val[1] ~= nil or next(val) == nil then + -- Treat as array -- check keys are valid and it is not sparse + local n = 0 + for k in pairs(val) do + if type(k) ~= "number" then + error("invalid table: mixed or invalid key types") + end + n = n + 1 + end + if n ~= #val then + error("invalid table: sparse array") + end + -- Encode + for i, v in ipairs(val) do + table.insert(res, encode(v, stack)) + end + stack[val] = nil + return "[" .. table.concat(res, ",") .. "]" + + else + -- Treat as an object + for k, v in pairs(val) do + if type(k) ~= "string" then + error("invalid table: mixed or invalid key types") + end + table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) + end + stack[val] = nil + return "{" .. table.concat(res, ",") .. "}" + end +end + + +local function encode_string(val) + return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' +end + + +local function encode_number(val) + -- Check for NaN, -inf and inf + if val ~= val or val <= -math.huge or val >= math.huge then + error("unexpected number value '" .. tostring(val) .. "'") + end + return string.format("%.14g", val) +end + + +local type_func_map = { + [ "nil" ] = encode_nil, + [ "table" ] = encode_table, + [ "string" ] = encode_string, + [ "number" ] = encode_number, + [ "boolean" ] = tostring, +} + + +encode = function(val, stack) + local t = type(val) + local f = type_func_map[t] + if f then + return f(val, stack) + end + error("unexpected type '" .. t .. "'") +end + + +function json.encode(val) + return ( encode(val) ) +end + + +------------------------------------------------------------------------------- +-- Decode +------------------------------------------------------------------------------- + +local parse + +local function create_set(...) + local res = {} + for i = 1, select("#", ...) do + res[ select(i, ...) ] = true + end + return res +end + +local space_chars = create_set(" ", "\t", "\r", "\n") +local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") +local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") +local literals = create_set("true", "false", "null") + +local literal_map = { + [ "true" ] = true, + [ "false" ] = false, + [ "null" ] = nil, +} + + +local function next_char(str, idx, set, negate) + for i = idx, #str do + if set[str:sub(i, i)] ~= negate then + return i + end + end + return #str + 1 +end + + +local function decode_error(str, idx, msg) + local line_count = 1 + local col_count = 1 + for i = 1, idx - 1 do + col_count = col_count + 1 + if str:sub(i, i) == "\n" then + line_count = line_count + 1 + col_count = 1 + end + end + error( string.format("%s at line %d col %d", msg, line_count, col_count) ) +end + + +local function codepoint_to_utf8(n) + -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa + local f = math.floor + if n <= 0x7f then + return string.char(n) + elseif n <= 0x7ff then + return string.char(f(n / 64) + 192, n % 64 + 128) + elseif n <= 0xffff then + return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) + elseif n <= 0x10ffff then + return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, + f(n % 4096 / 64) + 128, n % 64 + 128) + end + error( string.format("invalid unicode codepoint '%x'", n) ) +end + + +local function parse_unicode_escape(s) + local n1 = tonumber( s:sub(3, 6), 16 ) + local n2 = tonumber( s:sub(9, 12), 16 ) + -- Surrogate pair? + if n2 then + return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) + else + return codepoint_to_utf8(n1) + end +end + + +local function parse_string(str, i) + local has_unicode_escape = false + local has_surrogate_escape = false + local has_escape = false + local last + for j = i + 1, #str do + local x = str:byte(j) + + if x < 32 then + decode_error(str, j, "control character in string") + end + + if last == 92 then -- "\\" (escape char) + if x == 117 then -- "u" (unicode escape sequence) + local hex = str:sub(j + 1, j + 5) + if not hex:find("%x%x%x%x") then + decode_error(str, j, "invalid unicode escape in string") + end + if hex:find("^[dD][89aAbB]") then + has_surrogate_escape = true + else + has_unicode_escape = true + end + else + local c = string.char(x) + if not escape_chars[c] then + decode_error(str, j, "invalid escape char '" .. c .. "' in string") + end + has_escape = true + end + last = nil + + elseif x == 34 then -- '"' (end of string) + local s = str:sub(i + 1, j - 1) + if has_surrogate_escape then + s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape) + end + if has_unicode_escape then + s = s:gsub("\\u....", parse_unicode_escape) + end + if has_escape then + s = s:gsub("\\.", escape_char_map_inv) + end + return s, j + 1 + + else + last = x + end + end + decode_error(str, i, "expected closing quote for string") +end + + +local function parse_number(str, i) + local x = next_char(str, i, delim_chars) + local s = str:sub(i, x - 1) + local n = tonumber(s) + if not n then + decode_error(str, i, "invalid number '" .. s .. "'") + end + return n, x +end + + +local function parse_literal(str, i) + local x = next_char(str, i, delim_chars) + local word = str:sub(i, x - 1) + if not literals[word] then + decode_error(str, i, "invalid literal '" .. word .. "'") + end + return literal_map[word], x +end + + +local function parse_array(str, i) + local res = {} + local n = 1 + i = i + 1 + while 1 do + local x + i = next_char(str, i, space_chars, true) + -- Empty / end of array? + if str:sub(i, i) == "]" then + i = i + 1 + break + end + -- Read token + x, i = parse(str, i) + res[n] = x + n = n + 1 + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "]" then break end + if chr ~= "," then decode_error(str, i, "expected ']' or ','") end + end + return res, i +end + + +local function parse_object(str, i) + local res = {} + i = i + 1 + while 1 do + local key, val + i = next_char(str, i, space_chars, true) + -- Empty / end of object? + if str:sub(i, i) == "}" then + i = i + 1 + break + end + -- Read key + if str:sub(i, i) ~= '"' then + decode_error(str, i, "expected string for key") + end + key, i = parse(str, i) + -- Read ':' delimiter + i = next_char(str, i, space_chars, true) + if str:sub(i, i) ~= ":" then + decode_error(str, i, "expected ':' after key") + end + i = next_char(str, i + 1, space_chars, true) + -- Read value + val, i = parse(str, i) + -- Set + res[key] = val + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "}" then break end + if chr ~= "," then decode_error(str, i, "expected '}' or ','") end + end + return res, i +end + + +local char_func_map = { + [ '"' ] = parse_string, + [ "0" ] = parse_number, + [ "1" ] = parse_number, + [ "2" ] = parse_number, + [ "3" ] = parse_number, + [ "4" ] = parse_number, + [ "5" ] = parse_number, + [ "6" ] = parse_number, + [ "7" ] = parse_number, + [ "8" ] = parse_number, + [ "9" ] = parse_number, + [ "-" ] = parse_number, + [ "t" ] = parse_literal, + [ "f" ] = parse_literal, + [ "n" ] = parse_literal, + [ "[" ] = parse_array, + [ "{" ] = parse_object, +} + + +parse = function(str, idx) + local chr = str:sub(idx, idx) + local f = char_func_map[chr] + if f then + return f(str, idx) + end + decode_error(str, idx, "unexpected character '" .. chr .. "'") +end + + +function json.decode(str) + if type(str) ~= "string" then + error("expected argument of type string, got " .. type(str)) + end + local status, result = pcall( + function() + return parse(str, next_char(str, 1, space_chars, true)) + end + ) + return result, not status +end + + +return json diff --git a/nginx/lua/modules/redis.lua b/nginx/lua/modules/redis.lua new file mode 100644 index 0000000..505341d --- /dev/null +++ b/nginx/lua/modules/redis.lua @@ -0,0 +1,439 @@ +-- Copyright (C) Yichun Zhang (agentzh) + + +local sub = string.sub +local byte = string.byte +local tcp = ngx.socket.tcp +local null = ngx.null +local type = type +local pairs = pairs +local unpack = unpack +local setmetatable = setmetatable +local tonumber = tonumber +local tostring = tostring +local rawget = rawget +--local error = error + + +local ok, new_tab = pcall(require, "table.new") +if not ok or type(new_tab) ~= "function" then + new_tab = function (narr, nrec) return {} end +end + + +local _M = new_tab(0, 54) + +_M._VERSION = '0.26' + + +local common_cmds = { + "get", "set", "mget", "mset", + "del", "incr", "decr", -- Strings + "llen", "lindex", "lpop", "lpush", + "lrange", "linsert", -- Lists + "hexists", "hget", "hset", "hmget", + --[[ "hmset", ]] "hdel", -- Hashes + "smembers", "sismember", "sadd", "srem", + "sdiff", "sinter", "sunion", -- Sets + "zrange", "zrangebyscore", "zrank", "zadd", + "zrem", "zincrby", -- Sorted Sets + "auth", "eval", "expire", "script", + "sort" -- Others +} + + +local sub_commands = { + "subscribe", "psubscribe" +} + + +local unsub_commands = { + "unsubscribe", "punsubscribe" +} + + +local mt = { __index = _M } + + +function _M.new(self) + local sock, err = tcp() + if not sock then + return nil, err + end + return setmetatable({ _sock = sock, _subscribed = false }, mt) +end + + +function _M.set_timeout(self, timeout) + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + return sock:settimeout(timeout) +end + + +function _M.connect(self, ...) + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + self._subscribed = false + + return sock:connect(...) +end + + +function _M.set_keepalive(self, ...) + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + if rawget(self, "_subscribed") then + return nil, "subscribed state" + end + + return sock:setkeepalive(...) +end + + +function _M.get_reused_times(self) + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + return sock:getreusedtimes() +end + + +local function close(self) + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + return sock:close() +end +_M.close = close + + +local function _read_reply(self, sock) + local line, err = sock:receive() + if not line then + if err == "timeout" and not rawget(self, "_subscribed") then + sock:close() + end + return nil, err + end + + local prefix = byte(line) + + if prefix == 36 then -- char '$' + -- print("bulk reply") + + local size = tonumber(sub(line, 2)) + if size < 0 then + return null + end + + local data, err = sock:receive(size) + if not data then + if err == "timeout" then + sock:close() + end + return nil, err + end + + local dummy, err = sock:receive(2) -- ignore CRLF + if not dummy then + return nil, err + end + + return data + + elseif prefix == 43 then -- char '+' + -- print("status reply") + + return sub(line, 2) + + elseif prefix == 42 then -- char '*' + local n = tonumber(sub(line, 2)) + + -- print("multi-bulk reply: ", n) + if n < 0 then + return null + end + + local vals = new_tab(n, 0) + local nvals = 0 + for i = 1, n do + local res, err = _read_reply(self, sock) + if res then + nvals = nvals + 1 + vals[nvals] = res + + elseif res == nil then + return nil, err + + else + -- be a valid redis error value + nvals = nvals + 1 + vals[nvals] = {false, err} + end + end + + return vals + + elseif prefix == 58 then -- char ':' + -- print("integer reply") + return tonumber(sub(line, 2)) + + elseif prefix == 45 then -- char '-' + -- print("error reply: ", n) + + return false, sub(line, 2) + + else + -- when `line` is an empty string, `prefix` will be equal to nil. + return nil, "unknown prefix: \"" .. tostring(prefix) .. "\"" + end +end + + +local function _gen_req(args) + local nargs = #args + + local req = new_tab(nargs * 5 + 1, 0) + req[1] = "*" .. nargs .. "\r\n" + local nbits = 2 + + for i = 1, nargs do + local arg = args[i] + if type(arg) ~= "string" then + arg = tostring(arg) + end + + req[nbits] = "$" + req[nbits + 1] = #arg + req[nbits + 2] = "\r\n" + req[nbits + 3] = arg + req[nbits + 4] = "\r\n" + + nbits = nbits + 5 + end + + -- it is much faster to do string concatenation on the C land + -- in real world (large number of strings in the Lua VM) + return req +end + + +local function _do_cmd(self, ...) + local args = {...} + + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + local req = _gen_req(args) + + local reqs = rawget(self, "_reqs") + if reqs then + reqs[#reqs + 1] = req + return + end + + -- print("request: ", table.concat(req)) + + local bytes, err = sock:send(req) + if not bytes then + return nil, err + end + + return _read_reply(self, sock) +end + + +local function _check_subscribed(self, res) + if type(res) == "table" + and (res[1] == "unsubscribe" or res[1] == "punsubscribe") + and res[3] == 0 + then + self._subscribed = false + end +end + + +function _M.read_reply(self) + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + if not rawget(self, "_subscribed") then + return nil, "not subscribed" + end + + local res, err = _read_reply(self, sock) + _check_subscribed(self, res) + + return res, err +end + + +for i = 1, #common_cmds do + local cmd = common_cmds[i] + + _M[cmd] = + function (self, ...) + return _do_cmd(self, cmd, ...) + end +end + + +for i = 1, #sub_commands do + local cmd = sub_commands[i] + + _M[cmd] = + function (self, ...) + self._subscribed = true + return _do_cmd(self, cmd, ...) + end +end + + +for i = 1, #unsub_commands do + local cmd = unsub_commands[i] + + _M[cmd] = + function (self, ...) + local res, err = _do_cmd(self, cmd, ...) + _check_subscribed(self, res) + return res, err + end +end + + +function _M.hmset(self, hashname, ...) + if select('#', ...) == 1 then + local t = select(1, ...) + + local n = 0 + for k, v in pairs(t) do + n = n + 2 + end + + local array = new_tab(n, 0) + + local i = 0 + for k, v in pairs(t) do + array[i + 1] = k + array[i + 2] = v + i = i + 2 + end + -- print("key", hashname) + return _do_cmd(self, "hmset", hashname, unpack(array)) + end + + -- backwards compatibility + return _do_cmd(self, "hmset", hashname, ...) +end + + +function _M.init_pipeline(self, n) + self._reqs = new_tab(n or 4, 0) +end + + +function _M.cancel_pipeline(self) + self._reqs = nil +end + + +function _M.commit_pipeline(self) + local reqs = rawget(self, "_reqs") + if not reqs then + return nil, "no pipeline" + end + + self._reqs = nil + + local sock = rawget(self, "_sock") + if not sock then + return nil, "not initialized" + end + + local bytes, err = sock:send(reqs) + if not bytes then + return nil, err + end + + local nvals = 0 + local nreqs = #reqs + local vals = new_tab(nreqs, 0) + for i = 1, nreqs do + local res, err = _read_reply(self, sock) + if res then + nvals = nvals + 1 + vals[nvals] = res + + elseif res == nil then + if err == "timeout" then + close(self) + end + return nil, err + + else + -- be a valid redis error value + nvals = nvals + 1 + vals[nvals] = {false, err} + end + end + + return vals +end + + +function _M.array_to_hash(self, t) + local n = #t + -- print("n = ", n) + local h = new_tab(0, n / 2) + for i = 1, n, 2 do + h[t[i]] = t[i + 1] + end + return h +end + + +-- this method is deperate since we already do lazy method generation. +function _M.add_commands(...) + local cmds = {...} + for i = 1, #cmds do + local cmd = cmds[i] + _M[cmd] = + function (self, ...) + return _do_cmd(self, cmd, ...) + end + end +end + + +setmetatable(_M, {__index = function(self, cmd) + local method = + function (self, ...) + return _do_cmd(self, cmd, ...) + end + + -- cache the lazily generated method in our + -- module table + _M[cmd] = method + return method +end}) + + +return _M diff --git a/nginx/lua/modules/request.lua b/nginx/lua/modules/request.lua new file mode 100644 index 0000000..1b53a85 --- /dev/null +++ b/nginx/lua/modules/request.lua @@ -0,0 +1,22 @@ +local json = require 'json' + +local module = {} + +module.getPostData = function() + local data, err + + ngx.req.read_body() + data = ngx.req.get_body_data() + if not data then + response.quit(ngx.HTTP_BAD_REQUEST) + return + end + + data, err = json.decode(data) + if err or type(data) ~= "table" then + return nil + end + return data +end + +return module \ No newline at end of file diff --git a/nginx/lua/modules/response.lua b/nginx/lua/modules/response.lua new file mode 100644 index 0000000..b03dd0b --- /dev/null +++ b/nginx/lua/modules/response.lua @@ -0,0 +1,63 @@ +local json = require 'json' + +local module = {} + +module.raw = function(status, data) + ngx.status = status + ngx.say(data) +end + +module.json = function(status, data) + ngx.status = status + ngx.header['Content-type'] = 'application/json' + ngx.say(json.encode(data)) +end + +module.status = function(status) + ngx.status = status +end + +module.exit = function(status) + return ngx.exit(status) +end + +module.isValidResponseType = function(request_type) + return (request_type == 'raw' or request_type == 'json' or request_type == 'xml' or request_type == 'status') and true or false +end + +module.quit = module.exit + +module.xml = function(status, data) + local response = '' + for i, v in pairs(data) do + response = response .. '<'..i..'>'..tostring(v)..'' + end + + ngx.status = status + response = response .. '' + ngx.header['Content-type'] = 'text/xml; charset=utf-8' + ngx.say(response) +end + +module.createResponse = function(response_type, email, domain, isBad) + if response_type == 'status' then + module.status(isBad and ngx.HTTP_FORBIDDEN or ngx.HTTP_OK) + elseif response_type == 'raw' then + module.raw(ngx.HTTP_OK, tostring(isBad)) + elseif response_type == 'json' then + module.json(ngx.HTTP_OK, { + ['email'] = email, + ['domain'] = domain, + ['bad'] = isBad + }) + elseif response_type == 'xml' then + module.xml(ngx.HTTP_OK, { + ['email'] = email, + ['domain'] = domain, + ['bad'] = isBad + }) + end +end + + +return module diff --git a/nginx/lua/validator.lua b/nginx/lua/validator.lua new file mode 100644 index 0000000..d2f8618 --- /dev/null +++ b/nginx/lua/validator.lua @@ -0,0 +1,80 @@ +local request = require 'request' +local response = require 'response' +local helper = require 'helper' +local resolver = require 'dns' +local json = require 'json' +local ip = require 'ip' + +local red = helper.redis.connect() + +request_uri = helper.parseURL() +if type(request_uri) ~= "table" or table.getn(request_uri) < 2 then + response.quit(ngx.HTTP_BAD_REQUEST) + return +end + +local response_type = request_uri[2] +if not response.isValidResponseType(response_type) then + response.quit(ngx.HTTP_BAD_REQUEST) + return +end + +local data = request.getPostData() +if data == nil or data['email'] == nil then + response.quit(ngx.HTTP_BAD_REQUEST) + return +end + +domain = helper.getDomain(data['email']) +if not domain then + response.quit(ngx.HTTP_BAD_REQUEST) + return +end + +local isBad = helper.redis.checkDomain(red, "disposable", domain) +if not isBad or postData['no_dns_check'] == true then + local r, err = resolver:new{ + nameservers = {'8.8.8.8', '8.8.4.4'}, + retrans = 5, + timeout = 2000, + } + + if not r or err then + ngx.log(ngx.ERR, err) + response.quit(ngx.HTTP_SERVICE_UNAVAILABLE) + end + + local ans, err = r:tcp_query(domain, { qtype = r.TYPE_MX }) + if not ans or err then + ngx.log(ngx.ERR, err) + if type(ans) == "table" then + for i, v in pairs(ans) do + ngx.log(ngx.ERR, json.encode(err)) + end + end + response.quit(ngx.HTTP_SERVICE_UNAVAILABLE) + return + end + + if not (type(ans) ~= "table" or #ans == 0) then + for _i, _v in pairs(ans) do + local ip_addrs, err = r:tcp_query(_v.exchange) + if not ip_addrs or err or type(ip_addrs) ~= 'table' or #ip_addrs == 0 then + isBad = true + break + end + local p + for i, v in pairs(ip_addrs) do + p = ip.parse(v.address) + if p.range(p) ~= "unicast" then + isBad = true + break + end + end + end + else + isBad = true + end +end + +response.createResponse(response_type, data['email'], domain, isBad) diff --git a/node/index.js b/node/index.js new file mode 100644 index 0000000..8a23bea --- /dev/null +++ b/node/index.js @@ -0,0 +1,56 @@ +'use strict'; + +const git = require('simple-git'), + commander = require('commander'), + request = require('request-promise'), + fs = require('fs'), + redis = require("redis"); + +let pkg = require('./package.json'), + repos = require('./repositories.json'), + client = redis.createClient({ + 'host': 'redis', + 'port': 6379 + }), + saveList = [], + domainList = []; + +client.on('error', (err) => { + console.error(`[x] ${err.toString()}`); +}); + +console.log(`[*] Gathering from ${repos.length} sources...`); +Promise.all(repos.map(d => request(d.url))).then((data) => { + repos.forEach((source) => { + console.log(` - ${source.url}`); + }); + + let tempList = data.join('\n').split('\n'); + let domainList = []; + console.log(`[*] Creating domain list...`); + tempList.forEach((domain) => { + if (domain === '') { + return; + } + if (domainList[domain] === undefined) { + domainList[domain] = true; + } + }); + domainList = Object.keys(domainList); + console.log(`[*] Found ${domainList.length} domains`); + console.log(`[*] Sorting by alphabet...`); + domainList = domainList.sort((a, b) => a !== b ? a < b ? -1 : 1 : 0); + + console.log(`[*] Adding domains to cache...`) + domainList.forEach((domain) => { + client.hset('disposable', domain, true); + }); + + console.log(`[*] Done!`); + client.quit(); +}).catch((err) => { + console.error(err.toString()); + console.error(`URL: ${err.options.uri}`); + client.quit(); + process.exit(1); +}); diff --git a/node/package-lock.json b/node/package-lock.json new file mode 100644 index 0000000..9dadd41 --- /dev/null +++ b/node/package-lock.json @@ -0,0 +1,468 @@ +{ + "name": "trash-bot", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "ajv": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.1.tgz", + "integrity": "sha1-s4u4h22ehr7plJVqBOch6IskjrI=", + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, + "boom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "requires": { + "hoek": "4.2.0" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "requires": { + "delayed-stream": "1.0.0" + } + }, + "commander": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", + "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "requires": { + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "requires": { + "hoek": "4.2.0" + } + } + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "1.0.0" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "double-ended-queue": { + "version": "2.1.0-0", + "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", + "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=" + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", + "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "5.5.1", + "har-schema": "2.0.0" + } + }, + "hawk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "requires": { + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.0", + "sntp": "2.1.0" + } + }, + "hoek": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", + "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "requires": { + "mime-db": "1.30.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + }, + "redis": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", + "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", + "requires": { + "double-ended-queue": "2.1.0-0", + "redis-commands": "1.3.1", + "redis-parser": "2.6.0" + } + }, + "redis-commands": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz", + "integrity": "sha1-gdgm9F+pyLIBH0zXoP5ZfSQdRCs=" + }, + "redis-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", + "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=" + }, + "request": { + "version": "2.83.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.1", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + } + }, + "request-promise": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz", + "integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=", + "requires": { + "bluebird": "3.5.1", + "request-promise-core": "1.1.1", + "stealthy-require": "1.1.1", + "tough-cookie": "2.3.3" + } + }, + "request-promise-core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", + "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", + "requires": { + "lodash": "4.17.4" + }, + "dependencies": { + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + } + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "simple-git": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.83.0.tgz", + "integrity": "sha1-OiaE32L3W6TnleE2QtArnYV/wJ0=", + "requires": { + "debug": "3.1.0" + } + }, + "sntp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", + "requires": { + "hoek": "4.2.0" + } + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + } + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + } + } + } +} diff --git a/node/package.json b/node/package.json new file mode 100644 index 0000000..72cf907 --- /dev/null +++ b/node/package.json @@ -0,0 +1,19 @@ +{ + "name": "trash-bot", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "./index.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "commander": "^2.12.2", + "redis": "^2.8.0", + "request": "^2.83.0", + "request-promise": "^4.2.2", + "simple-git": "^1.83.0" + } +} diff --git a/node/repositories.json b/node/repositories.json new file mode 100644 index 0000000..53a2853 --- /dev/null +++ b/node/repositories.json @@ -0,0 +1,6 @@ +[ + { + "name": "adamloving temporary email address domains", + "url": "https://gist.githubusercontent.com/adamloving/4401361/raw/db901ef28d20af8aa91bf5082f5197d27926dea4/temporary-email-address-domains" + } +]