Init commit

This commit is contained in:
Onion Limited 2020-05-26 07:36:40 +00:00
commit 770d6da100
No known key found for this signature in database
GPG Key ID: E4B6CAC49B242A44
40 changed files with 6875 additions and 0 deletions

View File

@ -0,0 +1,306 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
sGsEejVHxvX7/iOE3rM=
=47bK
-----END PGP PUBLIC KEY BLOCK-----

110
README.MD Normal file
View File

@ -0,0 +1,110 @@
# EndGame - Onion Service DDOS Prevention Front System
Provided by [Dread](http://dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion/) and [White House Market](http://dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion/d/WhiteHouseMarket). With help from [Big Blue Market](http://dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion/d/BigBlueMarket) and [Empire Market](dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion/d/EmpireMarket).
EndGame is
- a front system designed to protect the core application servers on an onion service in a safe and private way.
- locally complied and locally run (no trusted or middle party).
- a combination of multiple different technologies working together in harmony (listed below).
- FREE FOR ALL TO USE!
- *arguably* magic ㄟ( ▔, ▔ )ㄏ
# Main Features
- Fully scripted and easily deploy-able (for mass scaling!) on blank Debian 10 systems.
- Full featured NGINX LUA script to filter packets and provide a captcha directly using the NGINX layer.
- Rate limiting via Tor's V3 onion service circuit ID system with secondary rate limiting based on a testcookie like system.
- Easy Configuration for both local and remote (over Tor) front systems.
- Easily configurable and change-able to meet an onion service's needs.
It can also:
- Cause you to grow a bigger dick than the asshole DDOSER (true *figurally*, lies *probably*)
- Save you millions of dollars do to DDOSER's downing your site for ransom or for their extorting fees.
- Make it look like you know what the fuck you are doing.
### Tech Overview
Endgame uses a number of open source projects (and libraries) to work properly.
Projects:
* [NGINX](https://NGINX.org/) - NGINX! A web server *obviously* to provide the packet handling, threading, and proxying.
* [Tor](https://www.torproject.org/) - Tor is free and open-source software for enabling anonymous communication. It's awesome and makes all this possible.
* [Vanguards](https://github.com/mikeperry-tor/vanguards) - A safer onion service circuit building system (to prevent some traffic analysis attacks)
* [STEM](https://stem.torproject.org/) - A python controller for Tor.
* [NYX](https://nyx.torproject.org/) - A command-line monitor for Tor (to easily check the endgame front's Tor process.
* [V3 OnionBalance](https://github.com/asn-d6/onionbalance) - A distributed DNS round-robin like system on Tor to allow load-balancing and elimiate single points of failure.
* [OpenSSL](https://www.openssl.org/) - A dependency for a lot of this projects and libraries.
* [Python3](https://www.python.org/) - A easy to work with programming language we use for background image generation.
NGINX Modules:
* [Socks NGINX](https://github.com/yorkane/socks-NGINX-module) - A NGINX module to allow proxying to Tor onion services directly on the NGINX layer.
* [NAXSI](https://github.com/nbs-system/naxsi) - A high performance web application firewall for NGINX.
* [Headers More](https://github.com/openresty/headers-more-NGINX-module) - A module for better control of headers in NGINX.
* [Echo NGINX](https://github.com/openresty/echo-nginx-module) - A NGINX module which allows shell style commands in the NGINX configuration file.
* [LUA NGINX](https://github.com/openresty/lua-nginx-module) - The power of LUA into NGINX via a module. This allows all the scripting, packet filtering, and captcha functionality EndGame does.
* [NGINX Development Kit](https://github.com/vision5/ngx_devel_kit) - Development Kit for NGINX (dependency)
Libraries:
* [LUAJIT2 NGINX](https://github.com/openresty/luajit2) - Just in time compiler for LUA.
* [LUA Resty String](https://github.com/openresty/lua-resty-string) - String functions for ngx_lua and LUAJIT2
* [LUA Resty Cookie](https://github.com/cloudflare/lua-resty-cookie) - Provides cookie manipulation
* [LUA Resty Session](https://github.com/bungle/lua-resty-session) - Provides session manipulation
* [LUA Resty AES](https://github.com/c64bob/lua-resty-aes/raw/master/lib/resty/aes_functions.lua) - AES Functions file for LUA. Used for shared session cookies.
* [LUA GD](https://github.com/ittner/lua-gd/) - GD image generation bindings For LUA
### Configuration
EndGame requires configuration to work properly.
The main configuration can be found at the top of the `setup.sh` file. It customizes most of the script
There are options. Such as:
* MASTERONION - Your V3 Master OnionBalance Address **WITHOUT http://** (example: dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion)
* TORAUTHPASSWORD - Password which is used for your Tor Control Port Authentication with NGINX. Alphanumeric without spaces (example: passwordIcanremembertyping)
* KEY - Alphanumeric Key for the shared front session key. Random between 64-128 would do fine. (example: isthis64charactorsalreadyicantbelieveitwowsocoolwaitnotyetohdarn)
* SALT - 8 character salt used with the key. 8 random alphanumeric characters (example: saltsalt)
* HEXCOLOR - HEX color put into the css file to be not purple but your main site's color. Any CSS hex will work. (example: #9b59b6)
There is also some editing you need to do in the `caphtml_d.lua`, `naxsi_whitelist.rules`, `site.conf`, and `torrc` files.
- `resty/caphtml_d.lua` - Two Base64 Images. The favicon (line 143) and main logo (line 162). You can use [this](https://base64.guru/converter/encode/image/ico) for the favicon and [this](https://base64.guru/converter/encode/image) for the main logo.
- `naxsi_whitelist.rules` - NAXSI's Whitelist Rules with some internal rules [see this](https://github.com/nbs-system/naxsi/wiki/internal-rules). To be configured for your specific application's use case.
- `site.conf` - Line 110 and 111 has the two rate limiting EndGame does. One by the circuit ID and one by the cookie. Depending on how your site calls files you may need to change these values.
- Defaulty set to 3 consistent on line 110 and 111. 110 for circuit. 111 for cookie.
- Line 245 has a nodelay burst of 6 for the circuit. Line 251 has a nodelay burst of 6 for the cookie.
- Line 268-272 socks proxy_pass system. If you want EndGame to pass the filtered request over Tor you uncomment the socks_* lines and change the exampleprivatev3onion.onion to your core webserver's private v3 address.
- Line 273 regular proxy_pass. If you have a secure local connection you want to use the regular proxy_pass for reliability and latency improvements. Just change it to your core webserver's private IP.
- `torrc` - Depending on what you set your burst as change the HiddenServiceMaxStreams value to that plus 2.
### Setup Process
EndGame is **HIGHLY** scripted. Which means it is important you run it on the system that it is intended for or there could be issues. Endgame is designed for `DEBIAN 10`.
##### STEP 1:
You need a v3 onionbalance master onion! There is a script included in the onionbalance folder. Onionbalance signed specific descriptors and publishes them to the Tor network. There is no site traffic that goes through onionbalance. As such you can put it on a low powered server or even on your core server. Recommendation is 2 CPU Cores with 2GB of RAM.
##### STEP 2:
After you get your onionbalance master onion you should configure the endgame script for your site with the correct variables. While EndGame is designed to work for most onion services it isn't perfect for everyone. You will need to customize it for your own needs.
##### STEP 3:
Transfer the files over to a blank debian 10 system with ideally 4 CPU cores and 4GB of RAM. High clocked cores are important (at least 3GHZ). Tor is single threaded with minimal hardware acceleration; getting higher performance cores will provide more resistance to attacks.
After the files are transferred make the setup.sh file executable and run it with bash. It will do the full setup process and export an onion URL. Visit that onion and hopefully everything will work. If not look at the error logs (located in /var/logs/nginx/) and see where you messed up.
##### STEP 4:
Scale out. Without scaling out you are bring a knife to a gun fight. At minimum you need 3 fronts. Onionbalance v3 doesn't have distinct descriptors which means if you go past about 6-9 fronts there might be some descriptor sizing issues on the default onionbalance setup. Endgame does make it much harder to take you down but you need to scale to keep up. Otherwise your front's Tor will get overloaded and you will go down. It's a dick measuring contest between you and the attacker. By scaling out you are effectively adding more length to your dick.
##### STEP 5:
After scaling out with multiple fronts add all their onion addresses to onionbalance's configuration and run it. Now you can publish that onionbalance master address as an EndGame protected address. That is it. Repeat these steps as many times as needed to make as many Endgame protected addresses to outscale any DDOSER.
### End Notes
EndGame isn't perfect. It can't protect against introduction cell type attacks (the Tor project will need to add POW at the introduction points to fix that). But it does provide good protection and scaling which makes it much harder to take you down overall for whatever people throw at you.
This all is a major step forward for the darknet community. Never give in to the extorting DDOSERS. You are only paying to be attacked with more power in the future. Instead stand together and say "NO". As a united front we will reach heights never seen before.

3
cap_d.css Normal file
View File

@ -0,0 +1,3 @@
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}strong{font-weight:bold}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}table{border-collapse:collapse;border-spacing:0}*:focus{outline:0}input,select,textarea{border:0;box-shadow:0}html{height:100%}body{height:100%;line-height:1;background:#1A1E23;font-family:roboto, helvetica, sans-serif, arial, verdana, tahoma;font-size:16px;color:#fff}.container{width:100%;margin:0 auto;min-height:100%;position:relative;max-height:100vh;overflow:hidden}.container>.inner{position:absolute;top:50%;left:0;right:0;margin:0 auto;text-align:center;-webkit-transform:translateY(-50%);-moz-transform:translateY(-50%);transform:translateY(-50%);}.container>.inner>.logo{display:inline-block;vertical-align:middle;text-decoration:none;margin-bottom:10px}.container>.inner>.logo>.square{display:inline-block;vertical-align:middle;width:40px;height:40px;background-color:HEXCOLOR;background-size:24px 24px;background-position:center center;background-repeat:no-repeat;margin-right:10px}.container>.inner>.logo>.text{display:inline-block;vertical-align:middle;font-size:30px;color:#fff;font-weight:700}.container>.inner>.date{display:block;text-align:center;font-size:42px}.container>.inner>.date>.day{color:HEXCOLOR;font-weight:bold}.signed{display:block;width:400px;height:150px;margin-top:20px;margin:20px auto 0 auto;}.signed>textarea{margin:0 auto;width:400px;height:150px;min-width:400px;max-width:400px;display:block;padding:15px;background:#fff;border:1px solid HEXCOLOR;min-height:150px;max-height:150px;}p{margin:0 auto 20px auto;max-width:300px;}form.ddos_form .captcha-input{margin:0 auto 20px auto;display: block;width:300px;font-size:0;}form.ddos_form .captcha-input input{display:inline-block;vertical-align:top;height:50px;width:calc(100% - 150px);outline:0;border:none;font-size:16px;color:#000;padding:0 15px;line-height:50px;}form.ddos_form .captcha-input img{display:inline-block;vertical-align:top;}form.ddos_form button{border-radius:3px;display:block;width:300px;margin:0 auto;background:HEXCOLOR;cursor:pointer;color:#fff;font-size:16px;text-transform:uppercase;text-align:center;height:40px;line-height:40px;outline:0;border:none;}
.captchav2 {text-align: center;width: 100%;}.captchav2 > .imgWrap {display: inline-block;vertical-align: middle;width: 150px;height: 150px;background-size: cover;background-position: center center;}.captchav2 > .inputWrap {display: inline-block;vertical-align: middle;width: 120px;height: 120px;position: relative;margin-left: 15px;}.captchav2 > .inputWrap > div {position: absolute;width: 30px;height: 30px;cursor: pointer;border-radius: 100%;border: 2px solid #fff;} .captchav2 > .inputWrap > div.c1 { top: 0; left: 0; }.captchav2 > .inputWrap > div.c2 { top: 0; left: 0; right: 0; margin: 0 auto; }.captchav2 > .inputWrap > div.c3 { top: 0; right: 0; } .captchav2 > .inputWrap > div.c4 { top: 50%; left: 0; margin-top: -15px; }.captchav2 > .inputWrap > div.c5 { top: 50%; left: 0; right: 0; margin: -15px auto 0 auto; }.captchav2 > .inputWrap > div.c6 { top: 50%; right: 0; margin-top: -15px; } .captchav2 > .inputWrap > div.c7 { bottom: 0; left: 0; }.captchav2 > .inputWrap > div.c8 { bottom: 0; left: 0; right: 0; margin: 0 auto; }.captchav2 > .inputWrap > div.c9 { bottom: 0; right: 0; } .captchav2 > .inputWrap > input[type="checkbox"] {opacity: 0;margin: 0;padding: 0;cursor: pointer;position: absolute;width: 30px;height: 30px;z-index: 5;}.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(1) { top: 0; left: 0; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(1):checked ~ .c1 { background: #fff; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(2) { top: 0; left: 0; right: 0; margin: 0 auto; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(2):checked ~ .c2 { background: #fff; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(3) { top: 0; right: 0; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(3):checked ~ .c3 { background: #fff; } .captchav2 > .inputWrap > input[type="checkbox"]:nth-child(4) { top: 50%; left: 0; margin-top: -15px; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(4):checked ~ .c4 { background: #fff; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(5) { top: 50%; left: 0; right: 0; margin: -15px auto 0 auto; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(5):checked ~ .c5 { background: #fff; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(6) { top: 50%; right: 0; margin-top: -15px; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(6):checked ~ .c6 { background: #fff; } .captchav2 > .inputWrap > input[type="checkbox"]:nth-child(7) { bottom: 0; left: 0; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(7):checked ~ .c7 { background: #fff; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(8) { bottom: 0; left: 0; right: 0; margin: 0 auto; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(8):checked ~ .c8 { background: #fff; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(9) { bottom: 0; right: 0; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(9):checked ~ .c9 { background: #fff; }

BIN
font.ttf Normal file

Binary file not shown.

60
gen_background.py Normal file
View File

@ -0,0 +1,60 @@
#!/usr/bin/python3 -u
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
import random
import os
def generate_background():
random.seed()
unicode_chars = (
"\u2605",
"\u2606",
"\u2663",
"\u2667",
"\u2660",
"\u2664",
"\u2662",
"\u2666",
"\u263a",
"\u263b",
"\u26aa",
"\u26ab",
"\u2b53",
"\u2b54",
"\u2b00",
"\u2b08",
"\u2780",
"\u278a",
"\u267c",
"\u267d",
"\u25b2",
"\u25b3",
)
unicode_max = len(unicode_chars)
try:
for i in range(0, 25):
im_cropped = Image.new('RGB', (150, 150),
(random.randrange(120, 255), random.randrange(120, 255), random.randrange(120, 255)))
origwidth, origheight = im_cropped.size
watermark = Image.new("RGBA", im_cropped.size)
waterdraw = ImageDraw.ImageDraw(watermark, "RGBA")
number_of_shapes = random.randrange(10, 15)
for step in range(0, number_of_shapes):
fillcolor = (
random.randrange(0, 255), random.randrange(0, 255), random.randrange(0, 255),
random.randrange(240, 255))
u_char = unicode_chars[random.randrange(0, unicode_max)]
font = ImageFont.truetype("/etc/nginx/font.ttf", random.randrange(25, 30))
waterdraw.text((random.randrange(-10, 130), random.randrange(-10, 130)), u_char, fill=fillcolor, font=font)
im_cropped.paste(watermark, None, watermark)
im_cropped.save("/tmp/background-" + str(i) + '.jpg', format="JPEG")
except Exception as e:
print(str(e))
generate_background()

282
lua/cap.lua Normal file
View File

@ -0,0 +1,282 @@
-- encryption key and salt must be shared across fronts. salt must be 8 chars
local key = "encryption_key"
local salt = "salt1234"
-- for how long the captcha is valid. 120 sec is for testing, 3600 1 hour should be production.
local session_timeout = 3600
aes = require "resty.aes"
str = require "resty.string"
cook = require "resty.cookie"
aes_128_cbc_sha512x1 = aes:new(key, salt, aes.cipher(128,"cbc"), aes.hash.sha512, 1)
local cookie, err = cook:new()
if not cookie then
ngx.log(ngx.ERR, err)
return
end
function fromhex(str)
return (str:gsub('..', function (cc)
return string.char(tonumber(cc, 16))
end))
end
function tohex(str)
return (str:gsub('.', function (c)
return string.format('%02X', string.byte(c))
end))
end
caperror = nil
-- check proxy_protocol_addr if present kill circuit if needed
local pa = "no_proxy"
if ngx.var.proxy_protocol_addr ~= nil then
pa = ngx.var.proxy_protocol_addr
end
-- if "Host" header is invalid / missing kill circuit and return nothing
if in_array(allowed_hosts, ngx.var.http_host) == nil then
ngx.log(ngx.ERR, "Wrong host (" .. ngx.var.http_host .. ") " .. ngx.var.remote_addr .. "|" .. pa)
if pa ~= "no_proxy" then
local ok, err = ngx.timer.at(0, kill_circuit, ngx.var.remote_addr, ngx.var.proxy_protocol_addr)
if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
end
ngx.exit(444)
end
-- only GET and POST requests are allowed the others are not used. HEAD for recon checker
if ngx.var.request_method ~= "POST" and ngx.var.request_method ~= "GET" and ngx.var.request_method ~= "HEAD" then
ngx.log(ngx.ERR, "Wrong request (" .. ngx.var.request_method .. ") " .. ngx.var.remote_addr .. "|" .. pa)
if pa ~= "no_proxy" then
local ok, err = ngx.timer.at(0, kill_circuit, ngx.var.remote_addr, ngx.var.proxy_protocol_addr)
if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
end
ngx.exit(444)
end
-- requests without user-agent are usually invalid
if ngx.var.http_user_agent == nil then
ngx.log(ngx.ERR, "Missing user agent " .. ngx.var.remote_addr .. "|" .. pa)
if pa ~= "no_proxy" then
local ok, err = ngx.timer.at(0, kill_circuit, ngx.var.remote_addr, ngx.var.proxy_protocol_addr)
if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
end
ngx.exit(444)
end
-- POST without referer is invalid. some poorly configured clients may complain about this
if ngx.var.request_method == "POST" and ngx.var.http_referer == nil then
ngx.log(ngx.ERR, "Post without referer " .. ngx.var.remote_addr .. "|" .. pa)
if pa ~= "no_proxy" then
local ok, err = ngx.timer.at(0, kill_circuit, ngx.var.remote_addr, ngx.var.proxy_protocol_addr)
if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
end
ngx.exit(444)
end
-- check cookie support similar to testcookie
if ngx.var.request_method == "GET" then
local args = ngx.req.get_uri_args()
if args['tca'] == "1" then
local field, err = cookie:get("dcap")
if err or not field then
ngx.exit(403)
end
-- if cookie cannot be decrypted most likely it has been messed with
local cookdata = aes_128_cbc_sha512x1:decrypt(fromhex(field))
if not cookdata then
ngx.header.content_type = 'text/plain'
ngx.say("403 DDOS fliter killed your path. (You probably sent too many requests at once). Not calling you a bot, bot, but grab a new identity and try again.")
ngx.flush()
ngx.exit(200)
end
cooktest = split(cookdata, "|")[1]
if cooktest ~= "cap_not_solved" and cooktest ~= "captcha_solved" then
ngx.exit(403)
end
end
-- try to set cookie. max-age is irrelevant as it can be faked and check is done against cookie content anyway. should be set to a large value otherwise it will annoy users
local field, err = cookie:get("dcap")
if err then
local tstamp = ngx.now()
local plaintext = "cap_not_solved|" .. tstamp .. "|1"
local ciphertext = tohex(aes_128_cbc_sha512x1:encrypt(plaintext))
local ok, err = cookie:set({
key = "dcap", value = ciphertext, path = "/",
domain = ngx.var.host, httponly = true,
max_age = 21600,
samesite = "Strict"
})
if not ok then
ngx.log(ngx.ERR, err)
return
end
ngx.header.content_type = 'text/html'
ngx.say("<head> \
<meta http-equiv=\"refresh\" content=\"1\"> \
</head><a href=\"/\">One moment...</p>")
ngx.flush()
ngx.exit(200)
end
end
-- captcha generator functions
require "caphtml_d"
local field, err = cookie:get("dcap")
if not field or field == nil then
displaycap()
ngx.flush()
ngx.exit(200)
end
-- check if cookie is blacklisted by rate limiter. if it is show the client a message and exit. can get creative with this.
local blocked_cookies = ngx.shared.blocked_cookies
local bct, btcflags = blocked_cookies:get(field)
if bct then
ngx.log(ngx.ERR, "Cookie " .. field .. " blacklisted.")
ngx.header.content_type = 'text/plain'
ngx.say("403 DDOS fliter killed your path. (You probably sent too many requests at once). Not calling you a bot, bot, but grab a new identity and try again.")
ngx.flush()
ngx.exit(200)
end
if ngx.var.request_method == "POST" then
local field, err = cookie:get("dcap")
if err then
ngx.exit(403)
end
if field then
plaintext = aes_128_cbc_sha512x1:decrypt(fromhex(field))
if not plaintext then
ngx.header.content_type = 'text/plain'
ngx.say("403 DDOS fliter killed your path. (You probably sent too many requests at once). Not calling you a bot, bot, but grab a new identity and try again.")
ngx.flush()
ngx.exit(200)
end
cookdata = split(plaintext,"|")
local expired = nil
if (tonumber(cookdata[2]) + session_timeout) < ngx.now() then
expired = true
caperror = "Session expired"
displaycap()
ngx.flush()
ngx.exit(200)
end
if cookdata[1] == "captcha_solved" and not expired then
return
end
end
-- resty has a library for parsing POST data but it's not really needed
ngx.req.read_body()
local dataraw = ngx.req.get_body_data()
if dataraw == nil then
caperror = "You didn't submit anything. Try again."
displaycap()
ngx.flush()
ngx.exit(200)
end
local data = ngx.req.get_body_data()
data = split(data, "&")
local sentcap = ""
for index, value in ipairs(data) do
sentcap = sentcap .. split(value,"=")[2]
end
if field then
plaintext = aes_128_cbc_sha512x1:decrypt(fromhex(field))
if not plaintext then
ngx.header.content_type = 'text/plain'
ngx.say("403 DDOS fliter killed your path. (You probably sent too many requests at once). Not calling you a bot, bot, but grab a new identity and try again.")
ngx.flush()
ngx.exit(200)
end
cookdata = split(plaintext,"|")
if (tonumber(cookdata[2]) + 60) < ngx.now() then
caperror = "Captcha expired"
displaycap()
ngx.flush()
ngx.exit(200)
end
if sentcap == cookdata[3] then
local newcookdata = ""
cookdata[1] = "captcha_solved"
for k,v in pairs(cookdata) do
newcookdata = newcookdata .. "|" .. v
end
local tstamp = ngx.now()
local ciphertext = tohex(aes_128_cbc_sha512x1:encrypt(newcookdata))
local ok, err = cookie:set({
key = "dcap", value = ciphertext, path = "/",
domain = ngx.var.host, httponly = true,
max_age = 21600,
samesite = "Strict"
})
if not ok then
ngx.say("cookie error")
return
end
local redirect_to = ngx.var.uri
if ngx.var.query_string ~= nil then
redirect_to = redirect_to .. "?" .. ngx.var.query_string
end
return ngx.redirect(redirect_to)
else
caperror = "You Got That Wrong. Try again"
end
else
caperror = "Session invalid or expired"
displaycap()
ngx.flush()
ngx.exit(200)
end
end
plaintext = aes_128_cbc_sha512x1:decrypt(fromhex(field))
if not plaintext then
ngx.header.content_type = 'text/plain'
ngx.say("403 DDOS fliter killed your path. (You probably sent too many requests at once). Not calling you a bot, bot, but grab a new identity and try again.")
ngx.flush()
ngx.exit(200)
end
cookdata = split(plaintext,"|")
if not cookdata then
displaycap()
ngx.flush()
ngx.exit(200)
end
local expired = nil
if (tonumber(cookdata[2]) + session_timeout) < ngx.now() then
expired = true
caperror = "Session expired"
end
if cookdata[1] ~= "captcha_solved" or expired then
displaycap()
ngx.flush()
ngx.exit(200)
end

88
naxsi_core.rules Normal file
View File

@ -0,0 +1,88 @@
##################################
## INTERNAL RULES IDS:1-999 ##
##################################
#@MainRule "msg:weird request, unable to parse" id:1;
#@MainRule "msg:request too big, stored on disk and not parsed" id:2;
#@MainRule "msg:invalid hex encoding, null bytes" id:10;
#@MainRule "msg:unknown content-type" id:11;
#@MainRule "msg:invalid formatted url" id:12;
#@MainRule "msg:invalid POST format" id:13;
#@MainRule "msg:invalid POST boundary" id:14;
#@MainRule "msg:invalid JSON" id:15;
#@MainRule "msg:empty POST" id:16;
#@MainRule "msg:libinjection_sql" id:17;
#@MainRule "msg:libinjection_xss" id:18;
##################################
## SQL Injections IDs:1000-1099 ##
##################################
MainRule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000;
MainRule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001;
MainRule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002;
## Hardcore rules
MainRule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003;
MainRule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004;
MainRule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005;
MainRule "str:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006;
## end of hardcore rules
MainRule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007;
MainRule "str:;" "msg:semicolon" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008;
MainRule "str:=" "msg:equal sign in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009;
MainRule "str:(" "msg:open parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010;
MainRule "str:)" "msg:close parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011;
MainRule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013;
MainRule "str:," "msg:comma" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015;
MainRule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016;
MainRule "str:@@" "msg:double arobase (@@)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1017;
###############################
## OBVIOUS RFI IDs:1100-1199 ##
###############################
MainRule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100;
MainRule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101;
MainRule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102;
MainRule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103;
MainRule "str:sftp://" "msg:sftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1104;
MainRule "str:zlib://" "msg:zlib:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1105;
MainRule "str:data://" "msg:data:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1106;
MainRule "str:glob://" "msg:glob:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1107;
MainRule "str:phar://" "msg:phar:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1108;
MainRule "str:file://" "msg:file:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1109;
MainRule "str:gopher://" "msg:gopher:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1110;
#######################################
## Directory traversal IDs:1200-1299 ##
#######################################
MainRule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200;
MainRule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202;
MainRule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203;
MainRule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204;
MainRule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205;
#MainRule "str:/" "msg:slash in args" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:2" id:1206;
########################################
## Cross Site Scripting IDs:1300-1399 ##
########################################
MainRule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302;
MainRule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303;
MainRule "str:[" "msg:open square backet ([), possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310;
MainRule "str:]" "msg:close square bracket (]), possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311;
MainRule "str:~" "msg:tilde (~) character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312;
MainRule "str:`" "msg:grave accent (`)" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314;
MainRule "rx:%[2|3]." "msg:double encoding" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315;
####################################
## Evading tricks IDs: 1400-1500 ##
####################################
MainRule "str:&#" "msg:utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400;
MainRule "str:%U" "msg:M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401;
#############################
## File uploads: 1500-1600 ##
#############################
MainRule "rx:\.ph|\.asp|\.ht" "msg:asp/php file upload" "mz:FILE_EXT" "s:$UPLOAD:8" id:1500;
MainRule "str:/public/uploads/" "msg:Access to uploads" "mz:URL" "s:$UWA:8" id:42000400;
MainRule "str:/public/image/" "msg:Access to image folder" "mz:URL" "s:$UWA:8" id:42000401;
MainRule "str:/public/" "msg:Access to public folder" "mz:URL" "s:$UWA:8" id:42000402;
MainRule "str:/system/" "msg:Access to system folder" "mz:URL" "s:$UWA:8" id:42000403;

4
naxsi_whitelist.rules Normal file
View File

@ -0,0 +1,4 @@
BasicRule wl:10;
BasicRule wl:20;
BasicRule wl:16;
BasicRule wl:12;

89
nginx-update.sh Normal file
View File

@ -0,0 +1,89 @@
#!/bin/bash
apt-get update
apt-get -y upgrade
command="nginx -v"
nginxv=$( ${command} 2>&1 )
NGINXVERSION=$(echo $nginxv | grep -o '[0-9.]*$')
NGINXOPENSSL="1.1.1d"
wget https://nginx.org/download/nginx-$NGINXVERSION.tar.gz
tar -xzvf nginx-$NGINXVERSION.tar.gz
cd nginx-$NGINXVERSION
apt-get install -y build-essential zlib1g-dev libpcre3 libpcre3-dev unzip uuid-dev gcc git wget curl libgd3 libgd-dev
wget https://github.com/apache/incubator-pagespeed-ngx/archive/latest-beta.tar.gz
tar -xzvf latest-beta.tar.gz
cd incubator-pagespeed-ngx-latest-beta
wget https://dl.google.com/dl/page-speed/psol/1.13.35.2-x64.tar.gz
tar -xzvf 1.13.35.2-x64.tar.gz
cd ..
git clone https://github.com/yorkane/socks-nginx-module
git clone https://github.com/nbs-system/naxsi.git
git clone https://github.com/kyprizel/testcookie-nginx-module.git
wget https://www.openssl.org/source/openssl-$NGINXOPENSSL.tar.gz
tar -xzvf openssl-$NGINXOPENSSL.tar.gz
git clone https://github.com/openresty/headers-more-nginx-module.git
git clone https://github.com/openresty/echo-nginx-module.git
#some required stuff for lua/luajit. obviously versions should be ckecked with every install/update
git clone https://github.com/openresty/lua-nginx-module
cd lua-nginx-module
git checkout v0.10.16
cd ..
git clone https://github.com/openresty/luajit2
cd luajit2
git checkout v2.1-20200102
cd ..
git clone https://github.com/vision5/ngx_devel_kit
cd luajit2
make -j8 && make install
cd ..
export LUAJIT_LIB=/usr/local/lib
export LUAJIT_INC=/usr/local/include/luajit-2.1
./configure --with-cc-opt='-Wno-stringop-overflow -Wno-stringop-truncation -Wno-cast-function-type' \
--with-ld-opt="-Wl,-rpath,/usr/local/lib" \
--with-compat --with-openssl=openssl-$NGINXOPENSSL \
--with-http_ssl_module \
--add-dynamic-module=incubator-pagespeed-ngx-latest-beta \
--add-dynamic-module=naxsi/naxsi_src --add-dynamic-module=testcookie-nginx-module \
--add-dynamic-module=headers-more-nginx-module \
--add-dynamic-module=socks-nginx-module \
--add-dynamic-module=echo-nginx-module \
--add-dynamic-module=ngx_devel_kit \
--add-dynamic-module=lua-nginx-module
git clone https://github.com/openresty/lua-resty-string
cd lua-resty-string
make install
cd ..
git clone https://github.com/cloudflare/lua-resty-cookie
cd lua-resty-cookie
make install
cd ..
git clone https://github.com/bungle/lua-resty-session
cp -a lua-resty-session/lib/resty/session* /usr/local/lib/lua/resty/
git clone https://github.com/ittner/lua-gd/
cd lua-gd
gcc -o gd.so -DGD_XPM -DGD_JPEG -DGD_FONTCONFIG -DGD_FREETYPE -DGD_PNG -DGD_GIF -O2 -Wall -fPIC -fomit-frame-pointer -I/usr/local/include/luajit-2.1 -DVERSION=\"2.0.33r3\" -shared -lgd luagd.c
mv gd.so /usr/local/lib/lua/5.1/gd.so
cd ..
wget -O /usr/local/lib/lua/resty/aes_functions.lua https://github.com/c64bob/lua-resty-aes/raw/master/lib/resty/aes_functions.lua
#include seems to be a bit mssed up with luajit
mkdir /etc/nginx/resty
ln -s /usr/local/lib/lua/resty/ /etc/nginx/resty/
make -j16 modules
cp -r objs modules
rm -R /etc/nginx/modules/modules
mv modules /etc/nginx/modules

85
nginx.conf Normal file
View File

@ -0,0 +1,85 @@
user www-data;
worker_processes auto;
worker_priority -5;
worker_rlimit_nofile 1024000;
pid /run/nginx.pid;
load_module modules/modules/ngx_http_headers_more_filter_module.so;
load_module modules/modules/ngx_http_naxsi_module.so;
load_module modules/modules/ngx_http_echo_module.so;
load_module modules/modules/ngx_http_socks_module.so;
load_module modules/modules/ndk_http_module.so;
load_module modules/modules/ngx_http_lua_module.so;
events {
worker_connections 4096;
}
http {
##
# Basic Settings
##
server_tokens off;
# Keep Alive
sendfile on;
tcp_nopush on;
tcp_nodelay on;
reset_timedout_connection on;
lua_shared_dict blocked_cookies 100M;
# Timeouts
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 240s;
send_timeout 120s;
client_max_body_size 10m;
client_body_buffer_size 10m;
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
log_format detailed escape=json
'{'
'"timestamp": "$time_iso8601",'
'"remote_addr": "$remote_addr",'
'"upstream_addr": "$upstream_addr",'
'"connection": "$connection",'
'"connection_requests": "$connection_requests",'
'"request_time": "$request_time",'
'"upstream_response_time": "$upstream_response_time",'
'"status": "$status",'
'"upstream_status": "$upstream_status",'
'"body_bytes_sent": "$body_bytes_sent ",'
'"request": "$request",'
'"http_user_agent": "$http_user_agent"'
'}';
#access_log /var/log/site.access.log detailed;
proxy_redirect off;
# Gzipping Content
gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
gzip_types application/x-javascript text/css application/javascript text/javascript text/plain text/xml application/json application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype application/x-font-ttf application/xml font/eot font/opentype font/otf image/svg+xml image/vnd.microsoft.icon;
gzip_disable "MSIE [1-6]\.";
include /etc/nginx/mime.types;
default_type application/octet-stream;
include /etc/nginx/naxsi_core.rules;
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "DENY";
add_header X-Xss-Protection "1; mode=block";
##
# Virtual Host Configs
##
include /etc/nginx/sites-enabled/*;
}

28
nginx_signing.key Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2.0.22 (GNU/Linux)
mQENBE5OMmIBCAD+FPYKGriGGf7NqwKfWC83cBV01gabgVWQmZbMcFzeW+hMsgxH
W6iimD0RsfZ9oEbfJCPG0CRSZ7ppq5pKamYs2+EJ8Q2ysOFHHwpGrA2C8zyNAs4I
QxnZZIbETgcSwFtDun0XiqPwPZgyuXVm9PAbLZRbfBzm8wR/3SWygqZBBLdQk5TE
fDR+Eny/M1RVR4xClECONF9UBB2ejFdI1LD45APbP2hsN/piFByU1t7yK2gpFyRt
97WzGHn9MV5/TL7AmRPM4pcr3JacmtCnxXeCZ8nLqedoSuHFuhwyDnlAbu8I16O5
XRrfzhrHRJFM1JnIiGmzZi6zBvH0ItfyX6ttABEBAAG0KW5naW54IHNpZ25pbmcg
a2V5IDxzaWduaW5nLWtleUBuZ2lueC5jb20+iQE+BBMBAgAoAhsDBgsJCAcDAgYV
CAIJCgsEFgIDAQIeAQIXgAUCV2K1+AUJGB4fQQAKCRCr9b2Ce9m/YloaB/9XGrol
kocm7l/tsVjaBQCteXKuwsm4XhCuAQ6YAwA1L1UheGOG/aa2xJvrXE8X32tgcTjr
KoYoXWcdxaFjlXGTt6jV85qRguUzvMOxxSEM2Dn115etN9piPl0Zz+4rkx8+2vJG
F+eMlruPXg/zd88NvyLq5gGHEsFRBMVufYmHtNfcp4okC1klWiRIRSdp4QY1wdrN
1O+/oCTl8Bzy6hcHjLIq3aoumcLxMjtBoclc/5OTioLDwSDfVx7rWyfRhcBzVbwD
oe/PD08AoAA6fxXvWjSxy+dGhEaXoTHjkCbz/l6NxrK3JFyauDgU4K4MytsZ1HDi
MgMW8hZXxszoICTTiQEcBBABAgAGBQJOTkelAAoJEKZP1bF62zmo79oH/1XDb29S
YtWp+MTJTPFEwlWRiyRuDXy3wBd/BpwBRIWfWzMs1gnCjNjk0EVBVGa2grvy9Jtx
JKMd6l/PWXVucSt+U/+GO8rBkw14SdhqxaS2l14v6gyMeUrSbY3XfToGfwHC4sa/
Thn8X4jFaQ2XN5dAIzJGU1s5JA0tjEzUwCnmrKmyMlXZaoQVrmORGjCuH0I0aAFk
RS0UtnB9HPpxhGVbs24xXZQnZDNbUQeulFxS4uP3OLDBAeCHl+v4t/uotIad8v6J
SO93vc1evIje6lguE81HHmJn9noxPItvOvSMb2yPsE8mH4cJHRTFNSEhPW6ghmlf
Wa9ZwiVX5igxcvaIRgQQEQIABgUCTk5b0gAKCRDs8OkLLBcgg1G+AKCnacLb/+W6
cflirUIExgZdUJqoogCeNPVwXiHEIVqithAM1pdY/gcaQZmIRgQQEQIABgUCTk5f
YQAKCRCpN2E5pSTFPnNWAJ9gUozyiS+9jf2rJvqmJSeWuCgVRwCcCUFhXRCpQO2Y
Va3l3WuB+rgKjsQ=
=EWWI
-----END PGP PUBLIC KEY BLOCK-----

View File

@ -0,0 +1,34 @@
#/bin/bash
clear
echo "Welcome To The End Game DDOS OnionBalance Setup."
sleep 0.5
echo "Starting now!"
apt-get update
apt-get install -y apt-transport-https lsb-release ca-certificates dirmngr git python3-setuptools python3-dev gcc libyaml-0-2
echo "deb https://deb.torproject.org/torproject.org buster main" >> /etc/apt/sources.list.d/tor.list
echo "deb-src https://deb.torproject.org/torproject.org buster main" >> /etc/apt/sources.list.d/tor.list
echo "deb https://deb.torproject.org/torproject.org tor-nightly-master-buster main" >> /etc/apt/sources.list.d/tor.list
echo "deb-src https://deb.torproject.org/torproject.org tor-nightly-master-buster main" >> /etc/apt/sources.list.d/tor.list
wget -qO- https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gpg --import
gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | apt-key add -
apt-get update
apt-get install -y tor nyx
apt-get install -y vanguards
service tor stop
rm /etc/tor/torrc
mv torrc /etc/tor/torrc
git clone https://gitlab.torproject.org/asn/onionbalance.git
cd onionbalance
python3 setup.py install
clear
onionbalance-config --hs-version v3 -n 3
echo "Setup Done.You need to do configuration"

5
onionbalance/torrc Normal file
View File

@ -0,0 +1,5 @@
SocksPort 0
ControlPort 9051
CookieAuthentication 1
HardwareAccel 1
RunAsDaemon 1

6
rc.local Normal file
View File

@ -0,0 +1,6 @@
#!/bin/sh -e
# This script is executed at the end of each multiuser runlevel
/startup.sh
exit 0

203
resty/caphtml_d.lua Normal file
View File

@ -0,0 +1,203 @@
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
function base64_encode(data)
return ((data:gsub('.', function(x)
local r,b='',x:byte()
for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
return r;
end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
if (#x < 6) then return '' end
local c=0
for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
return b:sub(c+1,c+1)
end)..({ '', '==', '=' })[#data%3+1])
end
function base64_decode(data)
data = string.gsub(data, '[^'..b..'=]', '')
return (data:gsub('.', function(x)
if (x == '=') then return '' end
local r,f='',(b:find(x)-1)
for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
return r;
end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
if (#x ~= 8) then return '' end
local c=0
for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
return string.char(c)
end))
end
function in_array(tab, val)
for index, value in ipairs(tab) do
if value == val then
return true
end
end
end
local gd = require("gd")
function displaycap()
math.randomseed(ngx.now())
local img_width = 150;
local img_height = 150;
local capgrid = {}
local checkmin = 1
local checkmax = 6
local checktotal = 0
local sessiondice = "";
while checktotal < checkmin do
for i=1,9,1 do
check = math.random(0,1)
if checktotal < checkmax and check == 1 then capgrid[i] = check else capgrid[i] = 0 end
if check == 1 then
checktotal = checktotal + 1
sessiondice = sessiondice .. tostring(i)
end
end
end
local cookie, err = cook:new()
if not cookie then
ngx.log(ngx.ERR, err)
ngx.say("cookie error")
ngx.exit(200)
end
local tstamp = ngx.now()
local newcookdata = "cap_not_solved|" .. tstamp .. "|"
newcookdata = newcookdata .. sessiondice
local ciphertext = tohex(aes_128_cbc_sha512x1:encrypt(newcookdata))
local ok, err = cookie:set({
key = "dcap", value = ciphertext, path = "/",
domain = ngx.var.host, httponly = true,
max_age = 21600,
samesite = "Strict"
})
if not ok then
ngx.say("cookie error")
ngx.exit(200)
end
local symbols_zero = {'','','','','','','','','','','','','',''};
local symbols_one = {'','','','','','','','','','','','','',''};
local img = gd.createFromJpeg("/tmp/background-" .. math.random(0,25) .. ".jpg")
if img == nil then
img = gd.createTrueColor(150, 150)
local white = img:colorAllocate(255, 255, 255)
img:filledRectangle(0, 0, img_width, img_height, white)
end
img:setThickness(1)
-- if 0 each row will be horizontal
local draw_angle = 0
local current_row = 1
local capstring = ""
for i=1,9,1 do
local symbol_id = math.random(1,14)
local fillcolor = img:colorAllocate(math.random(5,255), math.random(5,255), math.random(5,255))
if capgrid[i] == 1 then
capstring = capstring .. symbols_one[symbol_id]
else
capstring = capstring .. symbols_zero[symbol_id]
end
capstring = capstring .. " "
if i % 3 == 0 then
if draw_angle == 1 then
angle = math.rad(math.random(0,10))
else
angle = 0
end
if current_row == 1 then
img:stringFT(fillcolor, "/etc/nginx/font.ttf", math.random(18,22), angle, math.random(10,50), math.random(30,60), capstring)
elseif current_row == 2 then
img:stringFT(fillcolor, "/etc/nginx/font.ttf", math.random(18,22), angle, math.random(10,50), math.random(60,90), capstring)
else
img:stringFT(fillcolor, "/etc/nginx/font.ttf", math.random(18,22), angle, math.random(10,50), math.random(100,130), capstring)
end
current_row = current_row + 1
capstring = ""
end
end
imgbase64 = base64_encode(img:pngStrEx(6))
ngx.header.content_type = 'text/html';
ngx.say("<html lang=en> \
<head> \
<title>DDOS Protection</title> \
<meta charset=\"UTF-8\"> \
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"> \
<link id=\"favicon\" rel=\"shortcut icon\" href=\"data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABMLAAATCwAAAAAAAAAAAACtRI7/rUSO/61Ejv+tRI7/rUSO/61Fjv+qPor/pzaG/6k7if+sQo3/qDiH/6g4h/+sQ43/rUSO/61Ejv+tRI7/rUSO/61Ejv+tRI7/rUSO/61Fjv+sQo3/uV6e/8iBs/+9aaT/sEyT/8V7r//Feq//sEqS/6xDjf+tRI7/rUSO/61Ejv+tRI7/rUSO/65Fj/+vR5D/rEGM/+fI3v///////fv8/+/a6f/+/f7/+vT4/7Zam/+rP4v/rkWP/61Ejv+tRI7/rUSO/61Fjv+sQYz/qTqI/6g4h//hudX/5sXc/+7Z6P////////7///ft9P+2WZr/q0CL/61Fj/+tRI7/rUSO/61Fj/+rQIv/uFyd/82Ou//Njrv/uWGf/6g6iP+uR5D/5sbc///////47vX/tlma/6s/i/+tRY//rUSO/61Ejv+uRo//qDqI/9aix///////69Hj/61Ejv+vSJD/qTqI/8BvqP//////+O/1/7ZZmv+rP4v/rUWP/61Ejv+tRI7/rkaP/6k8if/fttP//////9ekyP+oOIf/sEuS/6tAi/+7ZKH//vv9//nw9v+2WJr/qz+L/61Fj/+tRI7/rUSO/65Gj/+oOoj/1qHG///////pzeH/qj6K/6o8if+lMoP/0pjB///////47vX/tlma/6s/i/+tRY//rUSO/61Ejv+uRo//qj2K/7xmo//8+Pv//////+G61f+8ZqP/zpC8//v2+v//////+O/1/7ZZmv+rP4v/rUWP/61Ejv+tRI7/rUSO/65Gj/+pPIn/zo+7//79/v///////////////////v////////jw9v+2WZr/qz+L/61Fj/+tRI7/rUSO/61Ejv+tRI7/rUWP/6o9iv/Ab6j/37bT/+vR4//kwdr/16XI//36/P/58ff/tlma/6s/i/+tRY//rUSO/61Ejv+tRI7/rUSO/61Ejv+uRo//qj2K/6o9if+tRY7/qDmH/7VYmv/9+fv/+fH3/7ZYmv+rP4v/rUWP/61Ejv+tRI7/rUSO/61Ejv+tRI7/rUSO/65Gj/+uRo//rkaP/6s/i/+6Y6H//Pf6//ju9f+1WJr/q0CL/61Fj/+tRI7/rUSO/61Ejv+tRI7/rUSO/61Ejv+tRI7/rUSO/65Gj/+qPor/umOh//79/v/69Pj/tlqb/6s/i/+uRY//rUSO/61Ejv+tRI7/rUSO/61Ejv+tRI7/rUSO/61Ejv+tRI7/rEKN/7FNk//GfLD/xHmu/7BKkv+sQ43/rUSO/61Ejv+tRI7/rUSO/61Ejv+tRI7/rUSO/61Ejv+tRI7/rUSO/61Ejv+sQo3/qDiH/6g4h/+sQ43/rUSO/61Ejv+tRI7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==\"> \
</head><body><style>")
local file = io.open("/etc/nginx/cap_d.css")
if not file then
ngx.exit(500)
end
local css, err = file:read("*a")
file:close()
ngx.say(css)
ngx.say("</style> \
<div class=\"container\"> \
<div class=\"inner\"> \
<div class=\"logo\"> \
<div class=\"square\" style=\"background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAMAAAAM7l6QAAAA4VBMVEX///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9pPP/NAAAASnRSTlMABAcJCwwQFBYdHyIjKTQ2Oj5ESkxOUVZXWGBkZWlqb3Bzd3+Ag5GZnJ6io6Sxt7q+wMbHyMnNztXW2dvh5ejp6uvw8/T2+fr7/f+3i2wAAADLSURBVCjPzdPXDoJAEAVQ7NgL9q7YG/Zesc//f5DuJG4YBBLfvG93T2CyuyAIXwkAy0AwiR95Zs3Tf2SfIYs5uV57p9Iw4OAQaAiXASxYBitOghU79rjwaEvRRPf5xVXsaw8Wz0rH9gmrR/dngyrlGNYif1mWcgEHi5y9lPGUNppt7gi3WFtqeEsYz+Ro4+o6E07jrAjnMJ0dPLGqcO7ojiWUkqR45vN4CQzvuz92ssFNMOYe3OfK4gambP45/Mz6nyh/UK8XHhjh4gvTGmQQRyXgEgAAAABJRU5ErkJggg==)\"></div> \
<div class=\"text\">dread</div> \
</div>")
if caperror ~= nil
then
ngx.say("<p class=\"alert alert-danger text-center\"><strong>Error: </strong>" .. caperror .. "</p>")
else
ngx.say("<p>Due to on-going DDOS attacks against our servers, you must complete a captcha challenge to prove you are human.</p>")
end
ngx.say("<form class=\"ddos_form\" method=\"post\"> \
<div class=\"captchav2\" style=\"margin-bottom:15px;\"> \
<div class=\"imgWrap\" style=\"border:1px solid #000;background-image:url(data:image/png;base64," .. imgbase64 .. "\"></div>")
ngx.say("<div class=\"inputWrap\"> \
<input type=\"checkbox\" name=\"cap\" value=\"1\"> \
<input type=\"checkbox\" name=\"cap\" value=\"2\"> \
<input type=\"checkbox\" name=\"cap\" value=\"3\"> \
<input type=\"checkbox\" name=\"cap\" value=\"4\"> \
<input type=\"checkbox\" name=\"cap\" value=\"5\"> \
<input type=\"checkbox\" name=\"cap\" value=\"6\"> \
<input type=\"checkbox\" name=\"cap\" value=\"7\"> \
<input type=\"checkbox\" name=\"cap\" value=\"8\"> \
<input type=\"checkbox\" name=\"cap\" value=\"9\">")
ngx.say("<div class=\"c1\"></div> \
<div class=\"c2\"></div> \
<div class=\"c3\"></div> \
<div class=\"c4\"></div> \
<div class=\"c5\"></div> \
<div class=\"c6\"></div> \
<div class=\"c7\"></div> \
<div class=\"c8\"></div> \
<div class=\"c9\"></div>")
ngx.say("</div> \
</div> \
<button type=\"submit\">Verify</button> \
</form> \
</div> \
</div> \
</body> \
</html>")
end

34
resty/core.lua Normal file
View File

@ -0,0 +1,34 @@
-- Copyright (C) Yichun Zhang (agentzh)
local subsystem = ngx.config.subsystem
require "resty.core.var"
require "resty.core.worker"
require "resty.core.regex"
require "resty.core.shdict"
require "resty.core.time"
require "resty.core.hash"
require "resty.core.uri"
require "resty.core.exit"
require "resty.core.base64"
if subsystem == 'http' then
require "resty.core.request"
require "resty.core.response"
require "resty.core.phase"
require "resty.core.ndk"
end
require "resty.core.misc"
require "resty.core.ctx"
local base = require "resty.core.base"
return {
version = base.version
}

258
resty/core/base.lua Normal file
View File

@ -0,0 +1,258 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local ffi_new = ffi.new
local error = error
local select = select
local ceil = math.ceil
local subsystem = ngx.config.subsystem
local str_buf_size = 4096
local str_buf
local size_ptr
local FREE_LIST_REF = 0
if subsystem == 'http' then
if not ngx.config
or not ngx.config.ngx_lua_version
or ngx.config.ngx_lua_version ~= 10016
then
error("ngx_http_lua_module 0.10.16 required")
end
elseif subsystem == 'stream' then
if not ngx.config
or not ngx.config.ngx_lua_version
or ngx.config.ngx_lua_version ~= 8
then
error("ngx_stream_lua_module 0.0.8 required")
end
else
error("ngx_http_lua_module 0.10.16 or "
.. "ngx_stream_lua_module 0.0.8 required")
end
if string.find(jit.version, " 2.0", 1, true) then
ngx.log(ngx.ALERT, "use of lua-resty-core with LuaJIT 2.0 is ",
"not recommended; use LuaJIT 2.1+ instead")
end
local ok, new_tab = pcall(require, "table.new")
if not ok then
new_tab = function (narr, nrec) return {} end
end
local clear_tab
ok, clear_tab = pcall(require, "table.clear")
if not ok then
local pairs = pairs
clear_tab = function (tab)
for k, _ in pairs(tab) do
tab[k] = nil
end
end
end
-- XXX for now LuaJIT 2.1 cannot compile require()
-- so we make the fast code path Lua only in our own
-- wrapper so that most of the require() calls in hot
-- Lua code paths can be JIT compiled.
do
local orig_require = require
local pkg_loaded = package.loaded
local function my_require(name)
local mod = pkg_loaded[name]
if mod then
return mod
end
return orig_require(name)
end
getfenv(0).require = my_require
end
if not pcall(ffi.typeof, "ngx_str_t") then
ffi.cdef[[
typedef struct {
size_t len;
const unsigned char *data;
} ngx_str_t;
]]
end
if subsystem == 'http' then
if not pcall(ffi.typeof, "ngx_http_request_t") then
ffi.cdef[[
typedef struct ngx_http_request_s ngx_http_request_t;
]]
end
if not pcall(ffi.typeof, "ngx_http_lua_ffi_str_t") then
ffi.cdef[[
typedef struct {
int len;
const unsigned char *data;
} ngx_http_lua_ffi_str_t;
]]
end
elseif subsystem == 'stream' then
if not pcall(ffi.typeof, "ngx_stream_lua_request_t") then
ffi.cdef[[
typedef struct ngx_stream_lua_request_s ngx_stream_lua_request_t;
]]
end
if not pcall(ffi.typeof, "ngx_stream_lua_ffi_str_t") then
ffi.cdef[[
typedef struct {
int len;
const unsigned char *data;
} ngx_stream_lua_ffi_str_t;
]]
end
else
error("unknown subsystem: " .. subsystem)
end
local c_buf_type = ffi.typeof("char[?]")
local _M = new_tab(0, 18)
_M.version = "0.1.17"
_M.new_tab = new_tab
_M.clear_tab = clear_tab
local errmsg
function _M.get_errmsg_ptr()
if not errmsg then
errmsg = ffi_new("char *[1]")
end
return errmsg
end
if not ngx then
error("no existing ngx. table found")
end
function _M.set_string_buf_size(size)
if size <= 0 then
return
end
if str_buf then
str_buf = nil
end
str_buf_size = ceil(size)
end
function _M.get_string_buf_size()
return str_buf_size
end
function _M.get_size_ptr()
if not size_ptr then
size_ptr = ffi_new("size_t[1]")
end
return size_ptr
end
function _M.get_string_buf(size, must_alloc)
-- ngx.log(ngx.ERR, "str buf size: ", str_buf_size)
if size > str_buf_size or must_alloc then
return ffi_new(c_buf_type, size)
end
if not str_buf then
str_buf = ffi_new(c_buf_type, str_buf_size)
end
return str_buf
end
function _M.ref_in_table(tb, key)
if key == nil then
return -1
end
local ref = tb[FREE_LIST_REF]
if ref and ref ~= 0 then
tb[FREE_LIST_REF] = tb[ref]
else
ref = #tb + 1
end
tb[ref] = key
-- print("ref key_id returned ", ref)
return ref
end
function _M.allows_subsystem(...)
local total = select("#", ...)
for i = 1, total do
if select(i, ...) == subsystem then
return
end
end
error("unsupported subsystem: " .. subsystem, 2)
end
_M.FFI_OK = 0
_M.FFI_NO_REQ_CTX = -100
_M.FFI_BAD_CONTEXT = -101
_M.FFI_ERROR = -1
_M.FFI_AGAIN = -2
_M.FFI_BUSY = -3
_M.FFI_DONE = -4
_M.FFI_DECLINED = -5
do
local exdata
ok, exdata = pcall(require, "thread.exdata")
if ok and exdata then
function _M.get_request()
local r = exdata()
if r ~= nil then
return r
end
end
else
local getfenv = getfenv
function _M.get_request()
return getfenv(0).__ngx_req
end
end
end
return _M

115
resty/core/base64.lua Normal file
View File

@ -0,0 +1,115 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_string = ffi.string
local ngx = ngx
local type = type
local error = error
local floor = math.floor
local tostring = tostring
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_encode_base64
local ngx_lua_ffi_decode_base64
if subsystem == "http" then
ffi.cdef[[
size_t ngx_http_lua_ffi_encode_base64(const unsigned char *src,
size_t len, unsigned char *dst,
int no_padding);
int ngx_http_lua_ffi_decode_base64(const unsigned char *src,
size_t len, unsigned char *dst,
size_t *dlen);
]]
ngx_lua_ffi_encode_base64 = C.ngx_http_lua_ffi_encode_base64
ngx_lua_ffi_decode_base64 = C.ngx_http_lua_ffi_decode_base64
elseif subsystem == "stream" then
ffi.cdef[[
size_t ngx_stream_lua_ffi_encode_base64(const unsigned char *src,
size_t len, unsigned char *dst,
int no_padding);
int ngx_stream_lua_ffi_decode_base64(const unsigned char *src,
size_t len, unsigned char *dst,
size_t *dlen);
]]
ngx_lua_ffi_encode_base64 = C.ngx_stream_lua_ffi_encode_base64
ngx_lua_ffi_decode_base64 = C.ngx_stream_lua_ffi_decode_base64
end
local function base64_encoded_length(len, no_padding)
return no_padding and floor((len * 8 + 5) / 6) or
floor((len + 2) / 3) * 4
end
ngx.encode_base64 = function (s, no_padding)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
local slen = #s
local no_padding_bool = false;
local no_padding_int = 0;
if no_padding then
if no_padding ~= true then
local typ = type(no_padding)
error("bad no_padding: boolean expected, got " .. typ, 2)
end
no_padding_bool = true
no_padding_int = 1;
end
local dlen = base64_encoded_length(slen, no_padding_bool)
local dst = get_string_buf(dlen)
local r_dlen = ngx_lua_ffi_encode_base64(s, slen, dst, no_padding_int)
-- if dlen ~= r_dlen then error("discrepancy in len") end
return ffi_string(dst, r_dlen)
end
local function base64_decoded_length(len)
return floor((len + 3) / 4) * 3
end
ngx.decode_base64 = function (s)
if type(s) ~= 'string' then
error("string argument only", 2)
end
local slen = #s
local dlen = base64_decoded_length(slen)
-- print("dlen: ", tonumber(dlen))
local dst = get_string_buf(dlen)
local pdlen = get_size_ptr()
local ok = ngx_lua_ffi_decode_base64(s, slen, dst, pdlen)
if ok == 0 then
return nil
end
return ffi_string(dst, pdlen[0])
end
return {
version = base.version
}

101
resty/core/ctx.lua Normal file
View File

@ -0,0 +1,101 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local debug = require "debug"
local base = require "resty.core.base"
local misc = require "resty.core.misc"
local C = ffi.C
local register_getter = misc.register_ngx_magic_key_getter
local register_setter = misc.register_ngx_magic_key_setter
local registry = debug.getregistry()
local new_tab = base.new_tab
local ref_in_table = base.ref_in_table
local get_request = base.get_request
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
local FFI_OK = base.FFI_OK
local error = error
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_get_ctx_ref
local ngx_lua_ffi_set_ctx_ref
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_get_ctx_ref(ngx_http_request_t *r);
int ngx_http_lua_ffi_set_ctx_ref(ngx_http_request_t *r, int ref);
]]
ngx_lua_ffi_get_ctx_ref = C.ngx_http_lua_ffi_get_ctx_ref
ngx_lua_ffi_set_ctx_ref = C.ngx_http_lua_ffi_set_ctx_ref
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_get_ctx_ref(ngx_stream_lua_request_t *r);
int ngx_stream_lua_ffi_set_ctx_ref(ngx_stream_lua_request_t *r, int ref);
]]
ngx_lua_ffi_get_ctx_ref = C.ngx_stream_lua_ffi_get_ctx_ref
ngx_lua_ffi_set_ctx_ref = C.ngx_stream_lua_ffi_set_ctx_ref
end
local _M = {
_VERSION = base.version
}
local function get_ctx_table()
local r = get_request()
if not r then
error("no request found")
end
local ctx_ref = ngx_lua_ffi_get_ctx_ref(r)
if ctx_ref == FFI_NO_REQ_CTX then
error("no request ctx found")
end
local ctxs = registry.ngx_lua_ctx_tables
if ctx_ref < 0 then
local ctx = new_tab(0, 4)
ctx_ref = ref_in_table(ctxs, ctx)
if ngx_lua_ffi_set_ctx_ref(r, ctx_ref) ~= FFI_OK then
return nil
end
return ctx
end
return ctxs[ctx_ref]
end
register_getter("ctx", get_ctx_table)
local function set_ctx_table(ctx)
local r = get_request()
if not r then
error("no request found")
end
local ctx_ref = ngx_lua_ffi_get_ctx_ref(r)
if ctx_ref == FFI_NO_REQ_CTX then
error("no request ctx found")
end
local ctxs = registry.ngx_lua_ctx_tables
if ctx_ref < 0 then
ctx_ref = ref_in_table(ctxs, ctx)
ngx_lua_ffi_set_ctx_ref(r, ctx_ref)
return
end
ctxs[ctx_ref] = ctx
end
register_setter("ctx", set_ctx_table)
return _M

66
resty/core/exit.lua Normal file
View File

@ -0,0 +1,66 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_string = ffi.string
local ngx = ngx
local error = error
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local get_request = base.get_request
local co_yield = coroutine._yield
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_exit
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status,
unsigned char *err, size_t *errlen);
]]
ngx_lua_ffi_exit = C.ngx_http_lua_ffi_exit
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_exit(ngx_stream_lua_request_t *r, int status,
unsigned char *err, size_t *errlen);
]]
ngx_lua_ffi_exit = C.ngx_stream_lua_ffi_exit
end
local ERR_BUF_SIZE = 128
local FFI_DONE = base.FFI_DONE
ngx.exit = function (rc)
local err = get_string_buf(ERR_BUF_SIZE)
local errlen = get_size_ptr()
local r = get_request()
if r == nil then
error("no request found")
end
errlen[0] = ERR_BUF_SIZE
rc = ngx_lua_ffi_exit(r, rc, err, errlen)
if rc == 0 then
-- print("yielding...")
return co_yield()
end
if rc == FFI_DONE then
return
end
error(ffi_string(err, errlen[0]), 2)
end
return {
version = base.version
}

110
resty/core/hash.lua Normal file
View File

@ -0,0 +1,110 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_new = ffi.new
local ffi_string = ffi.string
local ngx = ngx
local type = type
local error = error
local tostring = tostring
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_md5
local ngx_lua_ffi_md5_bin
local ngx_lua_ffi_sha1_bin
if subsystem == "http" then
ffi.cdef[[
void ngx_http_lua_ffi_md5_bin(const unsigned char *src, size_t len,
unsigned char *dst);
void ngx_http_lua_ffi_md5(const unsigned char *src, size_t len,
unsigned char *dst);
int ngx_http_lua_ffi_sha1_bin(const unsigned char *src, size_t len,
unsigned char *dst);
]]
ngx_lua_ffi_md5 = C.ngx_http_lua_ffi_md5
ngx_lua_ffi_md5_bin = C.ngx_http_lua_ffi_md5_bin
ngx_lua_ffi_sha1_bin = C.ngx_http_lua_ffi_sha1_bin
elseif subsystem == "stream" then
ffi.cdef[[
void ngx_stream_lua_ffi_md5_bin(const unsigned char *src, size_t len,
unsigned char *dst);
void ngx_stream_lua_ffi_md5(const unsigned char *src, size_t len,
unsigned char *dst);
int ngx_stream_lua_ffi_sha1_bin(const unsigned char *src, size_t len,
unsigned char *dst);
]]
ngx_lua_ffi_md5 = C.ngx_stream_lua_ffi_md5
ngx_lua_ffi_md5_bin = C.ngx_stream_lua_ffi_md5_bin
ngx_lua_ffi_sha1_bin = C.ngx_stream_lua_ffi_sha1_bin
end
local MD5_DIGEST_LEN = 16
local md5_buf = ffi_new("unsigned char[?]", MD5_DIGEST_LEN)
ngx.md5_bin = function (s)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
ngx_lua_ffi_md5_bin(s, #s, md5_buf)
return ffi_string(md5_buf, MD5_DIGEST_LEN)
end
local MD5_HEX_DIGEST_LEN = MD5_DIGEST_LEN * 2
local md5_hex_buf = ffi_new("unsigned char[?]", MD5_HEX_DIGEST_LEN)
ngx.md5 = function (s)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
ngx_lua_ffi_md5(s, #s, md5_hex_buf)
return ffi_string(md5_hex_buf, MD5_HEX_DIGEST_LEN)
end
local SHA_DIGEST_LEN = 20
local sha_buf = ffi_new("unsigned char[?]", SHA_DIGEST_LEN)
ngx.sha1_bin = function (s)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
local ok = ngx_lua_ffi_sha1_bin(s, #s, sha_buf)
if ok == 0 then
error("SHA-1 support missing in Nginx")
end
return ffi_string(sha_buf, SHA_DIGEST_LEN)
end
return {
version = base.version
}

240
resty/core/misc.lua Normal file
View File

@ -0,0 +1,240 @@
-- Copyright (C) Yichun Zhang (agentzh)
local base = require "resty.core.base"
local ffi = require "ffi"
local os = require "os"
local C = ffi.C
local ffi_new = ffi.new
local ffi_str = ffi.string
local ngx = ngx
local type = type
local error = error
local rawget = rawget
local rawset = rawset
local tonumber = tonumber
local setmetatable = setmetatable
local FFI_OK = base.FFI_OK
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
local FFI_BAD_CONTEXT = base.FFI_BAD_CONTEXT
local new_tab = base.new_tab
local get_request = base.get_request
local get_size_ptr = base.get_size_ptr
local get_string_buf = base.get_string_buf
local get_string_buf_size = base.get_string_buf_size
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_get_resp_status
local ngx_lua_ffi_get_conf_env
local ngx_magic_key_getters
local ngx_magic_key_setters
local _M = new_tab(0, 3)
local ngx_mt = new_tab(0, 2)
if subsystem == "http" then
ngx_magic_key_getters = new_tab(0, 4)
ngx_magic_key_setters = new_tab(0, 2)
elseif subsystem == "stream" then
ngx_magic_key_getters = new_tab(0, 2)
ngx_magic_key_setters = new_tab(0, 1)
end
local function register_getter(key, func)
ngx_magic_key_getters[key] = func
end
_M.register_ngx_magic_key_getter = register_getter
local function register_setter(key, func)
ngx_magic_key_setters[key] = func
end
_M.register_ngx_magic_key_setter = register_setter
ngx_mt.__index = function (tb, key)
local f = ngx_magic_key_getters[key]
if f then
return f()
end
return rawget(tb, key)
end
ngx_mt.__newindex = function (tb, key, ctx)
local f = ngx_magic_key_setters[key]
if f then
return f(ctx)
end
return rawset(tb, key, ctx)
end
setmetatable(ngx, ngx_mt)
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_get_resp_status(ngx_http_request_t *r);
int ngx_http_lua_ffi_set_resp_status(ngx_http_request_t *r, int r);
int ngx_http_lua_ffi_is_subrequest(ngx_http_request_t *r);
int ngx_http_lua_ffi_headers_sent(ngx_http_request_t *r);
int ngx_http_lua_ffi_get_conf_env(const unsigned char *name,
unsigned char **env_buf,
size_t *name_len);
]]
ngx_lua_ffi_get_resp_status = C.ngx_http_lua_ffi_get_resp_status
ngx_lua_ffi_get_conf_env = C.ngx_http_lua_ffi_get_conf_env
-- ngx.status
local function set_status(status)
local r = get_request()
if not r then
error("no request found")
end
if type(status) ~= 'number' then
status = tonumber(status)
end
local rc = C.ngx_http_lua_ffi_set_resp_status(r, status)
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
return
end
register_setter("status", set_status)
-- ngx.is_subrequest
local function is_subreq()
local r = get_request()
if not r then
error("no request found")
end
local rc = C.ngx_http_lua_ffi_is_subrequest(r)
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
return rc == 1
end
register_getter("is_subrequest", is_subreq)
-- ngx.headers_sent
local function headers_sent()
local r = get_request()
if not r then
error("no request found")
end
local rc = C.ngx_http_lua_ffi_headers_sent(r)
if rc == FFI_NO_REQ_CTX then
error("no request ctx found")
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
return rc == 1
end
register_getter("headers_sent", headers_sent)
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_get_resp_status(ngx_stream_lua_request_t *r);
int ngx_stream_lua_ffi_get_conf_env(const unsigned char *name,
unsigned char **env_buf,
size_t *name_len);
]]
ngx_lua_ffi_get_resp_status = C.ngx_stream_lua_ffi_get_resp_status
ngx_lua_ffi_get_conf_env = C.ngx_stream_lua_ffi_get_conf_env
end
-- ngx.status
local function get_status()
local r = get_request()
if not r then
error("no request found")
end
local rc = ngx_lua_ffi_get_resp_status(r)
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
return rc
end
register_getter("status", get_status)
do
local _getenv = os.getenv
local env_ptr = ffi_new("unsigned char *[1]")
os.getenv = function (name)
local r = get_request()
if r then
-- past init_by_lua* phase now
os.getenv = _getenv
env_ptr = nil
return os.getenv(name)
end
local size = get_string_buf_size()
env_ptr[0] = get_string_buf(size)
local name_len_ptr = get_size_ptr()
local rc = ngx_lua_ffi_get_conf_env(name, env_ptr, name_len_ptr)
if rc == FFI_OK then
return ffi_str(env_ptr[0] + name_len_ptr[0] + 1)
end
-- FFI_DECLINED
local value = _getenv(name)
if value ~= nil then
return value
end
return nil
end
end
_M._VERSION = base.version
return _M

92
resty/core/ndk.lua Normal file
View File

@ -0,0 +1,92 @@
-- Copyright (C) by OpenResty Inc.
local ffi = require 'ffi'
local base = require "resty.core.base"
base.allows_subsystem('http')
local C = ffi.C
local ffi_cast = ffi.cast
local ffi_new = ffi.new
local ffi_str = ffi.string
local FFI_OK = base.FFI_OK
local new_tab = base.new_tab
local get_string_buf = base.get_string_buf
local get_request = base.get_request
local setmetatable = setmetatable
local type = type
local tostring = tostring
local error = error
local _M = {
version = base.version
}
ffi.cdef[[
typedef void * ndk_set_var_value_pt;
int ngx_http_lua_ffi_ndk_lookup_directive(const unsigned char *var_data,
size_t var_len, ndk_set_var_value_pt *func);
int ngx_http_lua_ffi_ndk_set_var_get(ngx_http_request_t *r,
ndk_set_var_value_pt func, const unsigned char *arg_data, size_t arg_len,
ngx_http_lua_ffi_str_t *value);
]]
local func_p = ffi_new("void*[1]")
local ffi_str_size = ffi.sizeof("ngx_http_lua_ffi_str_t")
local ffi_str_type = ffi.typeof("ngx_http_lua_ffi_str_t*")
local function ndk_set_var_get(self, var)
if type(var) ~= "string" then
var = tostring(var)
end
if C.ngx_http_lua_ffi_ndk_lookup_directive(var, #var, func_p) ~= FFI_OK then
error('ndk.set_var: directive "' .. var
.. '" not found or does not use ndk_set_var_value', 2)
end
local func = func_p[0]
return function (arg)
local r = get_request()
if not r then
error("no request found")
end
if type(arg) ~= "string" then
arg = tostring(arg)
end
local buf = get_string_buf(ffi_str_size)
local value = ffi_cast(ffi_str_type, buf)
local rc = C.ngx_http_lua_ffi_ndk_set_var_get(r, func, arg, #arg, value)
if rc ~= FFI_OK then
error("calling directive " .. var .. " failed with code " .. rc, 2)
end
return ffi_str(value.data, value.len)
end
end
local function ndk_set_var_set()
error("not allowed", 2)
end
if ndk then
local mt = new_tab(0, 2)
mt.__newindex = ndk_set_var_set
mt.__index = ndk_set_var_get
ndk.set_var = setmetatable(new_tab(0, 0), mt)
end
return _M

58
resty/core/phase.lua Normal file
View File

@ -0,0 +1,58 @@
local ffi = require 'ffi'
local base = require "resty.core.base"
local C = ffi.C
local FFI_ERROR = base.FFI_ERROR
local get_request = base.get_request
local error = error
local tostring = tostring
ffi.cdef[[
int ngx_http_lua_ffi_get_phase(ngx_http_request_t *r, char **err)
]]
local errmsg = base.get_errmsg_ptr()
local context_names = {
[0x0001] = "set",
[0x0002] = "rewrite",
[0x0004] = "access",
[0x0008] = "content",
[0x0010] = "log",
[0x0020] = "header_filter",
[0x0040] = "body_filter",
[0x0080] = "timer",
[0x0100] = "init_worker",
[0x0200] = "balancer",
[0x0400] = "ssl_cert",
[0x0800] = "ssl_session_store",
[0x1000] = "ssl_session_fetch",
}
function ngx.get_phase()
local r = get_request()
-- if we have no request object, assume we are called from the "init" phase
if not r then
return "init"
end
local context = C.ngx_http_lua_ffi_get_phase(r, errmsg)
if context == FFI_ERROR then -- NGX_ERROR
error(errmsg, 2)
end
local phase = context_names[context]
if not phase then
error("unknown phase: " .. tostring(context))
end
return phase
end
return {
version = base.version
}

1195
resty/core/regex.lua Normal file

File diff suppressed because it is too large Load Diff

367
resty/core/request.lua Normal file
View File

@ -0,0 +1,367 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local base = require "resty.core.base"
base.allows_subsystem("http")
local utils = require "resty.core.utils"
local FFI_BAD_CONTEXT = base.FFI_BAD_CONTEXT
local FFI_DECLINED = base.FFI_DECLINED
local FFI_OK = base.FFI_OK
local new_tab = base.new_tab
local C = ffi.C
local ffi_cast = ffi.cast
local ffi_new = ffi.new
local ffi_str = ffi.string
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local setmetatable = setmetatable
local lower = string.lower
local rawget = rawget
local ngx = ngx
local get_request = base.get_request
local type = type
local error = error
local tostring = tostring
local tonumber = tonumber
local str_replace_char = utils.str_replace_char
ffi.cdef[[
typedef struct {
ngx_http_lua_ffi_str_t key;
ngx_http_lua_ffi_str_t value;
} ngx_http_lua_ffi_table_elt_t;
int ngx_http_lua_ffi_req_get_headers_count(ngx_http_request_t *r,
int max, int *truncated);
int ngx_http_lua_ffi_req_get_headers(ngx_http_request_t *r,
ngx_http_lua_ffi_table_elt_t *out, int count, int raw);
int ngx_http_lua_ffi_req_get_uri_args_count(ngx_http_request_t *r,
int max, int *truncated);
size_t ngx_http_lua_ffi_req_get_querystring_len(ngx_http_request_t *r);
int ngx_http_lua_ffi_req_get_uri_args(ngx_http_request_t *r,
unsigned char *buf, ngx_http_lua_ffi_table_elt_t *out, int count);
double ngx_http_lua_ffi_req_start_time(ngx_http_request_t *r);
int ngx_http_lua_ffi_req_get_method(ngx_http_request_t *r);
int ngx_http_lua_ffi_req_get_method_name(ngx_http_request_t *r,
unsigned char **name, size_t *len);
int ngx_http_lua_ffi_req_set_method(ngx_http_request_t *r, int method);
int ngx_http_lua_ffi_req_header_set_single_value(ngx_http_request_t *r,
const unsigned char *key, size_t key_len, const unsigned char *value,
size_t value_len);
]]
local table_elt_type = ffi.typeof("ngx_http_lua_ffi_table_elt_t*")
local table_elt_size = ffi.sizeof("ngx_http_lua_ffi_table_elt_t")
local truncated = ffi.new("int[1]")
local req_headers_mt = {
__index = function (tb, key)
return rawget(tb, (str_replace_char(lower(key), '_', '-')))
end
}
function ngx.req.get_headers(max_headers, raw)
local r = get_request()
if not r then
error("no request found")
end
if not max_headers then
max_headers = -1
end
if not raw then
raw = 0
else
raw = 1
end
local n = C.ngx_http_lua_ffi_req_get_headers_count(r, max_headers,
truncated)
if n == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
if n == 0 then
return {}
end
local raw_buf = get_string_buf(n * table_elt_size)
local buf = ffi_cast(table_elt_type, raw_buf)
local rc = C.ngx_http_lua_ffi_req_get_headers(r, buf, n, raw)
if rc == 0 then
local headers = new_tab(0, n)
for i = 0, n - 1 do
local h = buf[i]
local key = h.key
key = ffi_str(key.data, key.len)
local value = h.value
value = ffi_str(value.data, value.len)
local existing = headers[key]
if existing then
if type(existing) == "table" then
existing[#existing + 1] = value
else
headers[key] = {existing, value}
end
else
headers[key] = value
end
end
if raw == 0 then
headers = setmetatable(headers, req_headers_mt)
end
if truncated[0] ~= 0 then
return headers, "truncated"
end
return headers
end
return nil
end
function ngx.req.get_uri_args(max_args)
local r = get_request()
if not r then
error("no request found")
end
if not max_args then
max_args = -1
end
local n = C.ngx_http_lua_ffi_req_get_uri_args_count(r, max_args, truncated)
if n == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
if n == 0 then
return {}
end
local args_len = C.ngx_http_lua_ffi_req_get_querystring_len(r)
local strbuf = get_string_buf(args_len + n * table_elt_size)
local kvbuf = ffi_cast(table_elt_type, strbuf + args_len)
local nargs = C.ngx_http_lua_ffi_req_get_uri_args(r, strbuf, kvbuf, n)
local args = new_tab(0, nargs)
for i = 0, nargs - 1 do
local arg = kvbuf[i]
local key = arg.key
key = ffi_str(key.data, key.len)
local value = arg.value
local len = value.len
if len == -1 then
value = true
else
value = ffi_str(value.data, len)
end
local existing = args[key]
if existing then
if type(existing) == "table" then
existing[#existing + 1] = value
else
args[key] = {existing, value}
end
else
args[key] = value
end
end
if truncated[0] ~= 0 then
return args, "truncated"
end
return args
end
function ngx.req.start_time()
local r = get_request()
if not r then
error("no request found")
end
return tonumber(C.ngx_http_lua_ffi_req_start_time(r))
end
do
local methods = {
[0x0002] = "GET",
[0x0004] = "HEAD",
[0x0008] = "POST",
[0x0010] = "PUT",
[0x0020] = "DELETE",
[0x0040] = "MKCOL",
[0x0080] = "COPY",
[0x0100] = "MOVE",
[0x0200] = "OPTIONS",
[0x0400] = "PROPFIND",
[0x0800] = "PROPPATCH",
[0x1000] = "LOCK",
[0x2000] = "UNLOCK",
[0x4000] = "PATCH",
[0x8000] = "TRACE",
}
local namep = ffi_new("unsigned char *[1]")
function ngx.req.get_method()
local r = get_request()
if not r then
error("no request found")
end
do
local id = C.ngx_http_lua_ffi_req_get_method(r)
if id == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
local method = methods[id]
if method then
return method
end
end
local sizep = get_size_ptr()
local rc = C.ngx_http_lua_ffi_req_get_method_name(r, namep, sizep)
if rc ~= 0 then
return nil
end
return ffi_str(namep[0], sizep[0])
end
end -- do
function ngx.req.set_method(method)
local r = get_request()
if not r then
error("no request found")
end
if type(method) ~= "number" then
error("bad method number", 2)
end
local rc = C.ngx_http_lua_ffi_req_set_method(r, method)
if rc == FFI_OK then
return
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
if rc == FFI_DECLINED then
error("unsupported HTTP method: " .. method, 2)
end
error("unknown error: " .. rc)
end
do
local orig_func = ngx.req.set_header
function ngx.req.set_header(name, value)
if type(value) == "table" then
return orig_func(name, value)
end
local r = get_request()
if not r then
error("no request found")
end
if type(name) ~= "string" then
name = tostring(name)
end
local rc
if not value then
rc = C.ngx_http_lua_ffi_req_header_set_single_value(r, name,
#name, nil, 0)
else
if type(value) ~= "string" then
value = tostring(value)
end
rc = C.ngx_http_lua_ffi_req_header_set_single_value(r, name,
#name, value, #value)
end
if rc == FFI_OK or rc == FFI_DECLINED then
return
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
error("error")
end
end -- do
function ngx.req.clear_header(name)
local r = get_request()
if not r then
error("no request found")
end
if type(name) ~= "string" then
name = tostring(name)
end
local rc = C.ngx_http_lua_ffi_req_header_set_single_value(r, name, #name,
nil, 0)
if rc == FFI_OK or rc == FFI_DECLINED then
return
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
error("error")
end
return {
version = base.version
}

183
resty/core/response.lua Normal file
View File

@ -0,0 +1,183 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local base = require "resty.core.base"
local C = ffi.C
local ffi_cast = ffi.cast
local ffi_str = ffi.string
local new_tab = base.new_tab
local FFI_BAD_CONTEXT = base.FFI_BAD_CONTEXT
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
local FFI_DECLINED = base.FFI_DECLINED
local get_string_buf = base.get_string_buf
local setmetatable = setmetatable
local type = type
local tostring = tostring
local get_request = base.get_request
local error = error
local ngx = ngx
local _M = {
version = base.version
}
local MAX_HEADER_VALUES = 100
local errmsg = base.get_errmsg_ptr()
local ffi_str_type = ffi.typeof("ngx_http_lua_ffi_str_t*")
local ffi_str_size = ffi.sizeof("ngx_http_lua_ffi_str_t")
ffi.cdef[[
int ngx_http_lua_ffi_set_resp_header(ngx_http_request_t *r,
const char *key_data, size_t key_len, int is_nil,
const char *sval, size_t sval_len, ngx_http_lua_ffi_str_t *mvals,
size_t mvals_len, int override, char **errmsg);
int ngx_http_lua_ffi_get_resp_header(ngx_http_request_t *r,
const unsigned char *key, size_t key_len,
unsigned char *key_buf, ngx_http_lua_ffi_str_t *values,
int max_nvalues, char **errmsg);
]]
local function set_resp_header(tb, key, value, no_override)
local r = get_request()
if not r then
error("no request found")
end
if type(key) ~= "string" then
key = tostring(key)
end
local rc
if value == nil then
if no_override then
error("invalid header value", 3)
end
rc = C.ngx_http_lua_ffi_set_resp_header(r, key, #key, true, nil, 0, nil,
0, 1, errmsg)
else
local sval, sval_len, mvals, mvals_len, buf
if type(value) == "table" then
mvals_len = #value
if mvals_len == 0 and no_override then
return
end
buf = get_string_buf(ffi_str_size * mvals_len)
mvals = ffi_cast(ffi_str_type, buf)
for i = 1, mvals_len do
local s = value[i]
if type(s) ~= "string" then
s = tostring(s)
value[i] = s
end
local str = mvals[i - 1]
str.data = s
str.len = #s
end
sval_len = 0
else
if type(value) ~= "string" then
sval = tostring(value)
else
sval = value
end
sval_len = #sval
mvals_len = 0
end
local override_int = no_override and 0 or 1
rc = C.ngx_http_lua_ffi_set_resp_header(r, key, #key, false, sval,
sval_len, mvals, mvals_len,
override_int, errmsg)
end
if rc == 0 or rc == FFI_DECLINED then
return
end
if rc == FFI_NO_REQ_CTX then
error("no request ctx found")
end
if rc == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
-- rc == FFI_ERROR
error(ffi_str(errmsg[0]), 2)
end
_M.set_resp_header = set_resp_header
local function get_resp_header(tb, key)
local r = get_request()
if not r then
error("no request found")
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
local key_buf = get_string_buf(key_len + ffi_str_size * MAX_HEADER_VALUES)
local values = ffi_cast(ffi_str_type, key_buf + key_len)
local n = C.ngx_http_lua_ffi_get_resp_header(r, key, key_len, key_buf,
values, MAX_HEADER_VALUES,
errmsg)
-- print("retval: ", n)
if n == FFI_BAD_CONTEXT then
error("API disabled in the current context", 2)
end
if n == 0 then
return nil
end
if n == 1 then
local v = values[0]
return ffi_str(v.data, v.len)
end
if n > 0 then
local ret = new_tab(n, 0)
for i = 1, n do
local v = values[i - 1]
ret[i] = ffi_str(v.data, v.len)
end
return ret
end
-- n == FFI_ERROR
error(ffi_str(errmsg[0]), 2)
end
do
local mt = new_tab(0, 2)
mt.__newindex = set_resp_header
mt.__index = get_resp_header
ngx.header = setmetatable(new_tab(0, 0), mt)
end
return _M

638
resty/core/shdict.lua Normal file
View File

@ -0,0 +1,638 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local base = require "resty.core.base"
local _M = {
version = base.version
}
local ngx_shared = ngx.shared
if not ngx_shared then
return _M
end
local ffi_new = ffi.new
local ffi_str = ffi.string
local C = ffi.C
local get_string_buf = base.get_string_buf
local get_string_buf_size = base.get_string_buf_size
local get_size_ptr = base.get_size_ptr
local tonumber = tonumber
local tostring = tostring
local next = next
local type = type
local error = error
local getmetatable = getmetatable
local FFI_DECLINED = base.FFI_DECLINED
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_shdict_get
local ngx_lua_ffi_shdict_incr
local ngx_lua_ffi_shdict_store
local ngx_lua_ffi_shdict_flush_all
local ngx_lua_ffi_shdict_get_ttl
local ngx_lua_ffi_shdict_set_expire
local ngx_lua_ffi_shdict_capacity
local ngx_lua_ffi_shdict_free_space
local ngx_lua_ffi_shdict_udata_to_zone
if subsystem == 'http' then
ffi.cdef[[
int ngx_http_lua_ffi_shdict_get(void *zone, const unsigned char *key,
size_t key_len, int *value_type, unsigned char **str_value_buf,
size_t *str_value_len, double *num_value, int *user_flags,
int get_stale, int *is_stale, char **errmsg);
int ngx_http_lua_ffi_shdict_incr(void *zone, const unsigned char *key,
size_t key_len, double *value, char **err, int has_init,
double init, long init_ttl, int *forcible);
int ngx_http_lua_ffi_shdict_store(void *zone, int op,
const unsigned char *key, size_t key_len, int value_type,
const unsigned char *str_value_buf, size_t str_value_len,
double num_value, long exptime, int user_flags, char **errmsg,
int *forcible);
int ngx_http_lua_ffi_shdict_flush_all(void *zone);
long ngx_http_lua_ffi_shdict_get_ttl(void *zone,
const unsigned char *key, size_t key_len);
int ngx_http_lua_ffi_shdict_set_expire(void *zone,
const unsigned char *key, size_t key_len, long exptime);
size_t ngx_http_lua_ffi_shdict_capacity(void *zone);
void *ngx_http_lua_ffi_shdict_udata_to_zone(void *zone_udata);
]]
ngx_lua_ffi_shdict_get = C.ngx_http_lua_ffi_shdict_get
ngx_lua_ffi_shdict_incr = C.ngx_http_lua_ffi_shdict_incr
ngx_lua_ffi_shdict_store = C.ngx_http_lua_ffi_shdict_store
ngx_lua_ffi_shdict_flush_all = C.ngx_http_lua_ffi_shdict_flush_all
ngx_lua_ffi_shdict_get_ttl = C.ngx_http_lua_ffi_shdict_get_ttl
ngx_lua_ffi_shdict_set_expire = C.ngx_http_lua_ffi_shdict_set_expire
ngx_lua_ffi_shdict_capacity = C.ngx_http_lua_ffi_shdict_capacity
ngx_lua_ffi_shdict_udata_to_zone =
C.ngx_http_lua_ffi_shdict_udata_to_zone
if not pcall(function ()
return C.ngx_http_lua_ffi_shdict_free_space
end)
then
ffi.cdef[[
size_t ngx_http_lua_ffi_shdict_free_space(void *zone);
]]
end
pcall(function ()
ngx_lua_ffi_shdict_free_space = C.ngx_http_lua_ffi_shdict_free_space
end)
elseif subsystem == 'stream' then
ffi.cdef[[
int ngx_stream_lua_ffi_shdict_get(void *zone, const unsigned char *key,
size_t key_len, int *value_type, unsigned char **str_value_buf,
size_t *str_value_len, double *num_value, int *user_flags,
int get_stale, int *is_stale, char **errmsg);
int ngx_stream_lua_ffi_shdict_incr(void *zone, const unsigned char *key,
size_t key_len, double *value, char **err, int has_init,
double init, long init_ttl, int *forcible);
int ngx_stream_lua_ffi_shdict_store(void *zone, int op,
const unsigned char *key, size_t key_len, int value_type,
const unsigned char *str_value_buf, size_t str_value_len,
double num_value, long exptime, int user_flags, char **errmsg,
int *forcible);
int ngx_stream_lua_ffi_shdict_flush_all(void *zone);
long ngx_stream_lua_ffi_shdict_get_ttl(void *zone,
const unsigned char *key, size_t key_len);
int ngx_stream_lua_ffi_shdict_set_expire(void *zone,
const unsigned char *key, size_t key_len, long exptime);
size_t ngx_stream_lua_ffi_shdict_capacity(void *zone);
void *ngx_stream_lua_ffi_shdict_udata_to_zone(void *zone_udata);
]]
ngx_lua_ffi_shdict_get = C.ngx_stream_lua_ffi_shdict_get
ngx_lua_ffi_shdict_incr = C.ngx_stream_lua_ffi_shdict_incr
ngx_lua_ffi_shdict_store = C.ngx_stream_lua_ffi_shdict_store
ngx_lua_ffi_shdict_flush_all = C.ngx_stream_lua_ffi_shdict_flush_all
ngx_lua_ffi_shdict_get_ttl = C.ngx_stream_lua_ffi_shdict_get_ttl
ngx_lua_ffi_shdict_set_expire = C.ngx_stream_lua_ffi_shdict_set_expire
ngx_lua_ffi_shdict_capacity = C.ngx_stream_lua_ffi_shdict_capacity
ngx_lua_ffi_shdict_udata_to_zone =
C.ngx_stream_lua_ffi_shdict_udata_to_zone
if not pcall(function ()
return C.ngx_stream_lua_ffi_shdict_free_space
end)
then
ffi.cdef[[
size_t ngx_stream_lua_ffi_shdict_free_space(void *zone);
]]
end
-- ngx_stream_lua is only compatible with NGINX >= 1.13.6, meaning it
-- cannot lack support for ngx_stream_lua_ffi_shdict_free_space.
ngx_lua_ffi_shdict_free_space = C.ngx_stream_lua_ffi_shdict_free_space
else
error("unknown subsystem: " .. subsystem)
end
if not pcall(function () return C.free end) then
ffi.cdef[[
void free(void *ptr);
]]
end
local value_type = ffi_new("int[1]")
local user_flags = ffi_new("int[1]")
local num_value = ffi_new("double[1]")
local is_stale = ffi_new("int[1]")
local forcible = ffi_new("int[1]")
local str_value_buf = ffi_new("unsigned char *[1]")
local errmsg = base.get_errmsg_ptr()
local function check_zone(zone)
if not zone or type(zone) ~= "table" then
error("bad \"zone\" argument", 3)
end
zone = zone[1]
if type(zone) ~= "userdata" then
error("bad \"zone\" argument", 3)
end
zone = ngx_lua_ffi_shdict_udata_to_zone(zone)
if zone == nil then
error("bad \"zone\" argument", 3)
end
return zone
end
local function shdict_store(zone, op, key, value, exptime, flags)
zone = check_zone(zone)
if not exptime then
exptime = 0
elseif exptime < 0 then
error('bad "exptime" argument', 2)
end
if not flags then
flags = 0
end
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local str_val_buf
local str_val_len = 0
local num_val = 0
local valtyp = type(value)
-- print("value type: ", valtyp)
-- print("exptime: ", exptime)
if valtyp == "string" then
valtyp = 4 -- LUA_TSTRING
str_val_buf = value
str_val_len = #value
elseif valtyp == "number" then
valtyp = 3 -- LUA_TNUMBER
num_val = value
elseif value == nil then
valtyp = 0 -- LUA_TNIL
elseif valtyp == "boolean" then
valtyp = 1 -- LUA_TBOOLEAN
num_val = value and 1 or 0
else
return nil, "bad value type"
end
local rc = ngx_lua_ffi_shdict_store(zone, op, key, key_len,
valtyp, str_val_buf,
str_val_len, num_val,
exptime * 1000, flags, errmsg,
forcible)
-- print("rc == ", rc)
if rc == 0 then -- NGX_OK
return true, nil, forcible[0] == 1
end
-- NGX_DECLINED or NGX_ERROR
return false, ffi_str(errmsg[0]), forcible[0] == 1
end
local function shdict_set(zone, key, value, exptime, flags)
return shdict_store(zone, 0, key, value, exptime, flags)
end
local function shdict_safe_set(zone, key, value, exptime, flags)
return shdict_store(zone, 0x0004, key, value, exptime, flags)
end
local function shdict_add(zone, key, value, exptime, flags)
return shdict_store(zone, 0x0001, key, value, exptime, flags)
end
local function shdict_safe_add(zone, key, value, exptime, flags)
return shdict_store(zone, 0x0005, key, value, exptime, flags)
end
local function shdict_replace(zone, key, value, exptime, flags)
return shdict_store(zone, 0x0002, key, value, exptime, flags)
end
local function shdict_delete(zone, key)
return shdict_set(zone, key, nil)
end
local function shdict_get(zone, key)
zone = check_zone(zone)
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local size = get_string_buf_size()
local buf = get_string_buf(size)
str_value_buf[0] = buf
local value_len = get_size_ptr()
value_len[0] = size
local rc = ngx_lua_ffi_shdict_get(zone, key, key_len, value_type,
str_value_buf, value_len,
num_value, user_flags, 0,
is_stale, errmsg)
if rc ~= 0 then
if errmsg[0] then
return nil, ffi_str(errmsg[0])
end
error("failed to get the key")
end
local typ = value_type[0]
if typ == 0 then -- LUA_TNIL
return nil
end
local flags = tonumber(user_flags[0])
local val
if typ == 4 then -- LUA_TSTRING
if str_value_buf[0] ~= buf then
-- ngx.say("len: ", tonumber(value_len[0]))
buf = str_value_buf[0]
val = ffi_str(buf, value_len[0])
C.free(buf)
else
val = ffi_str(buf, value_len[0])
end
elseif typ == 3 then -- LUA_TNUMBER
val = tonumber(num_value[0])
elseif typ == 1 then -- LUA_TBOOLEAN
val = (tonumber(buf[0]) ~= 0)
else
error("unknown value type: " .. typ)
end
if flags ~= 0 then
return val, flags
end
return val
end
local function shdict_get_stale(zone, key)
zone = check_zone(zone)
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local size = get_string_buf_size()
local buf = get_string_buf(size)
str_value_buf[0] = buf
local value_len = get_size_ptr()
value_len[0] = size
local rc = ngx_lua_ffi_shdict_get(zone, key, key_len, value_type,
str_value_buf, value_len,
num_value, user_flags, 1,
is_stale, errmsg)
if rc ~= 0 then
if errmsg[0] then
return nil, ffi_str(errmsg[0])
end
error("failed to get the key")
end
local typ = value_type[0]
if typ == 0 then -- LUA_TNIL
return nil
end
local flags = tonumber(user_flags[0])
local val
if typ == 4 then -- LUA_TSTRING
if str_value_buf[0] ~= buf then
-- ngx.say("len: ", tonumber(value_len[0]))
buf = str_value_buf[0]
val = ffi_str(buf, value_len[0])
C.free(buf)
else
val = ffi_str(buf, value_len[0])
end
elseif typ == 3 then -- LUA_TNUMBER
val = tonumber(num_value[0])
elseif typ == 1 then -- LUA_TBOOLEAN
val = (tonumber(buf[0]) ~= 0)
else
error("unknown value type: " .. typ)
end
if flags ~= 0 then
return val, flags, is_stale[0] == 1
end
return val, nil, is_stale[0] == 1
end
local function shdict_incr(zone, key, value, init, init_ttl)
zone = check_zone(zone)
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
if type(value) ~= "number" then
value = tonumber(value)
end
num_value[0] = value
if init then
local typ = type(init)
if typ ~= "number" then
init = tonumber(init)
if not init then
error("bad init arg: number expected, got " .. typ, 2)
end
end
end
if init_ttl ~= nil then
local typ = type(init_ttl)
if typ ~= "number" then
init_ttl = tonumber(init_ttl)
if not init_ttl then
error("bad init_ttl arg: number expected, got " .. typ, 2)
end
end
if init_ttl < 0 then
error('bad "init_ttl" argument', 2)
end
if not init then
error('must provide "init" when providing "init_ttl"', 2)
end
else
init_ttl = 0
end
local rc = ngx_lua_ffi_shdict_incr(zone, key, key_len, num_value,
errmsg, init and 1 or 0,
init or 0, init_ttl * 1000,
forcible)
if rc ~= 0 then -- ~= NGX_OK
return nil, ffi_str(errmsg[0])
end
if not init then
return tonumber(num_value[0])
end
return tonumber(num_value[0]), nil, forcible[0] == 1
end
local function shdict_flush_all(zone)
zone = check_zone(zone)
ngx_lua_ffi_shdict_flush_all(zone)
end
local function shdict_ttl(zone, key)
zone = check_zone(zone)
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local rc = ngx_lua_ffi_shdict_get_ttl(zone, key, key_len)
if rc == FFI_DECLINED then
return nil, "not found"
end
return tonumber(rc) / 1000
end
local function shdict_expire(zone, key, exptime)
zone = check_zone(zone)
if not exptime then
error('bad "exptime" argument', 2)
end
if key == nil then
return nil, "nil key"
end
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
if key_len > 65535 then
return nil, "key too long"
end
local rc = ngx_lua_ffi_shdict_set_expire(zone, key, key_len,
exptime * 1000)
if rc == FFI_DECLINED then
return nil, "not found"
end
-- NGINX_OK/FFI_OK
return true
end
local function shdict_capacity(zone)
zone = check_zone(zone)
return tonumber(ngx_lua_ffi_shdict_capacity(zone))
end
local shdict_free_space
if ngx_lua_ffi_shdict_free_space then
shdict_free_space = function (zone)
zone = check_zone(zone)
return tonumber(ngx_lua_ffi_shdict_free_space(zone))
end
else
shdict_free_space = function ()
error("'shm:free_space()' not supported in NGINX < 1.11.7", 2)
end
end
local _, dict = next(ngx_shared, nil)
if dict then
local mt = getmetatable(dict)
if mt then
mt = mt.__index
if mt then
mt.get = shdict_get
mt.get_stale = shdict_get_stale
mt.incr = shdict_incr
mt.set = shdict_set
mt.safe_set = shdict_safe_set
mt.add = shdict_add
mt.safe_add = shdict_safe_add
mt.replace = shdict_replace
mt.delete = shdict_delete
mt.flush_all = shdict_flush_all
mt.ttl = shdict_ttl
mt.expire = shdict_expire
mt.capacity = shdict_capacity
mt.free_space = shdict_free_space
end
end
end
return _M

159
resty/core/time.lua Normal file
View File

@ -0,0 +1,159 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require 'ffi'
local base = require "resty.core.base"
local error = error
local tonumber = tonumber
local type = type
local C = ffi.C
local ffi_new = ffi.new
local ffi_str = ffi.string
local time_val = ffi_new("long[1]")
local get_string_buf = base.get_string_buf
local ngx = ngx
local FFI_ERROR = base.FFI_ERROR
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_now
local ngx_lua_ffi_time
local ngx_lua_ffi_today
local ngx_lua_ffi_localtime
local ngx_lua_ffi_utctime
local ngx_lua_ffi_update_time
if subsystem == 'http' then
ffi.cdef[[
double ngx_http_lua_ffi_now(void);
long ngx_http_lua_ffi_time(void);
void ngx_http_lua_ffi_today(unsigned char *buf);
void ngx_http_lua_ffi_localtime(unsigned char *buf);
void ngx_http_lua_ffi_utctime(unsigned char *buf);
void ngx_http_lua_ffi_update_time(void);
int ngx_http_lua_ffi_cookie_time(unsigned char *buf, long t);
void ngx_http_lua_ffi_http_time(unsigned char *buf, long t);
void ngx_http_lua_ffi_parse_http_time(const unsigned char *str, size_t len,
long *time);
]]
ngx_lua_ffi_now = C.ngx_http_lua_ffi_now
ngx_lua_ffi_time = C.ngx_http_lua_ffi_time
ngx_lua_ffi_today = C.ngx_http_lua_ffi_today
ngx_lua_ffi_localtime = C.ngx_http_lua_ffi_localtime
ngx_lua_ffi_utctime = C.ngx_http_lua_ffi_utctime
ngx_lua_ffi_update_time = C.ngx_http_lua_ffi_update_time
elseif subsystem == 'stream' then
ffi.cdef[[
double ngx_stream_lua_ffi_now(void);
long ngx_stream_lua_ffi_time(void);
void ngx_stream_lua_ffi_today(unsigned char *buf);
void ngx_stream_lua_ffi_localtime(unsigned char *buf);
void ngx_stream_lua_ffi_utctime(unsigned char *buf);
void ngx_stream_lua_ffi_update_time(void);
]]
ngx_lua_ffi_now = C.ngx_stream_lua_ffi_now
ngx_lua_ffi_time = C.ngx_stream_lua_ffi_time
ngx_lua_ffi_today = C.ngx_stream_lua_ffi_today
ngx_lua_ffi_localtime = C.ngx_stream_lua_ffi_localtime
ngx_lua_ffi_utctime = C.ngx_stream_lua_ffi_utctime
ngx_lua_ffi_update_time = C.ngx_stream_lua_ffi_update_time
end
function ngx.now()
return tonumber(ngx_lua_ffi_now())
end
function ngx.time()
return tonumber(ngx_lua_ffi_time())
end
function ngx.update_time()
ngx_lua_ffi_update_time()
end
function ngx.today()
-- the format of today is 2010-11-19
local today_buf_size = 10
local buf = get_string_buf(today_buf_size)
ngx_lua_ffi_today(buf)
return ffi_str(buf, today_buf_size)
end
function ngx.localtime()
-- the format of localtime is 2010-11-19 20:56:31
local localtime_buf_size = 19
local buf = get_string_buf(localtime_buf_size)
ngx_lua_ffi_localtime(buf)
return ffi_str(buf, localtime_buf_size)
end
function ngx.utctime()
-- the format of utctime is 2010-11-19 20:56:31
local utctime_buf_size = 19
local buf = get_string_buf(utctime_buf_size)
ngx_lua_ffi_utctime(buf)
return ffi_str(buf, utctime_buf_size)
end
if subsystem == 'http' then
function ngx.cookie_time(sec)
if type(sec) ~= "number" then
error("number argument only", 2)
end
-- the format of cookie time is Mon, 28-Sep-2038 06:00:00 GMT
-- or Mon, 28-Sep-18 06:00:00 GMT
local cookie_time_buf_size = 29
local buf = get_string_buf(cookie_time_buf_size)
local used_size = C.ngx_http_lua_ffi_cookie_time(buf, sec)
return ffi_str(buf, used_size)
end
function ngx.http_time(sec)
if type(sec) ~= "number" then
error("number argument only", 2)
end
-- the format of http time is Mon, 28 Sep 1970 06:00:00 GMT
local http_time_buf_size = 29
local buf = get_string_buf(http_time_buf_size)
C.ngx_http_lua_ffi_http_time(buf, sec)
return ffi_str(buf, http_time_buf_size)
end
function ngx.parse_http_time(time_str)
if type(time_str) ~= "string" then
error("string argument only", 2)
end
C.ngx_http_lua_ffi_parse_http_time(time_str, #time_str, time_val)
local res = time_val[0]
if res == FFI_ERROR then
return nil
end
return tonumber(res)
end
end
return {
version = base.version
}

94
resty/core/uri.lua Normal file
View File

@ -0,0 +1,94 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_string = ffi.string
local ngx = ngx
local type = type
local tostring = tostring
local get_string_buf = base.get_string_buf
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_escape_uri
local ngx_lua_ffi_unescape_uri
local ngx_lua_ffi_uri_escaped_length
if subsystem == "http" then
ffi.cdef[[
size_t ngx_http_lua_ffi_uri_escaped_length(const unsigned char *src,
size_t len);
void ngx_http_lua_ffi_escape_uri(const unsigned char *src, size_t len,
unsigned char *dst);
size_t ngx_http_lua_ffi_unescape_uri(const unsigned char *src,
size_t len, unsigned char *dst);
]]
ngx_lua_ffi_escape_uri = C.ngx_http_lua_ffi_escape_uri
ngx_lua_ffi_unescape_uri = C.ngx_http_lua_ffi_unescape_uri
ngx_lua_ffi_uri_escaped_length = C.ngx_http_lua_ffi_uri_escaped_length
elseif subsystem == "stream" then
ffi.cdef[[
size_t ngx_stream_lua_ffi_uri_escaped_length(const unsigned char *src,
size_t len);
void ngx_stream_lua_ffi_escape_uri(const unsigned char *src, size_t len,
unsigned char *dst);
size_t ngx_stream_lua_ffi_unescape_uri(const unsigned char *src,
size_t len, unsigned char *dst);
]]
ngx_lua_ffi_escape_uri = C.ngx_stream_lua_ffi_escape_uri
ngx_lua_ffi_unescape_uri = C.ngx_stream_lua_ffi_unescape_uri
ngx_lua_ffi_uri_escaped_length = C.ngx_stream_lua_ffi_uri_escaped_length
end
ngx.escape_uri = function (s)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
local slen = #s
local dlen = ngx_lua_ffi_uri_escaped_length(s, slen)
-- print("dlen: ", tonumber(dlen))
if dlen == slen then
return s
end
local dst = get_string_buf(dlen)
ngx_lua_ffi_escape_uri(s, slen, dst)
return ffi_string(dst, dlen)
end
ngx.unescape_uri = function (s)
if type(s) ~= 'string' then
if not s then
s = ''
else
s = tostring(s)
end
end
local slen = #s
local dlen = slen
local dst = get_string_buf(dlen)
dlen = ngx_lua_ffi_unescape_uri(s, slen, dst)
return ffi_string(dst, dlen)
end
return {
version = base.version,
}

44
resty/core/utils.lua Normal file
View File

@ -0,0 +1,44 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
base.allows_subsystem("http")
local C = ffi.C
local ffi_str = ffi.string
local ffi_copy = ffi.copy
local byte = string.byte
local str_find = string.find
local get_string_buf = base.get_string_buf
ffi.cdef[[
void ngx_http_lua_ffi_str_replace_char(unsigned char *buf, size_t len,
const unsigned char find, const unsigned char replace);
]]
local _M = {
version = base.version
}
function _M.str_replace_char(str, find, replace)
if not str_find(str, find, nil, true) then
return str
end
local len = #str
local buf = get_string_buf(len)
ffi_copy(buf, str)
C.ngx_http_lua_ffi_str_replace_char(buf, len, byte(find),
byte(replace))
return ffi_str(buf, len)
end
return _M

160
resty/core/var.lua Normal file
View File

@ -0,0 +1,160 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local ffi_new = ffi.new
local ffi_str = ffi.string
local type = type
local error = error
local tostring = tostring
local setmetatable = setmetatable
local get_request = base.get_request
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local new_tab = base.new_tab
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_var_get
local ngx_lua_ffi_var_set
local ERR_BUF_SIZE = 256
ngx.var = new_tab(0, 0)
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_var_get(ngx_http_request_t *r,
const char *name_data, size_t name_len, char *lowcase_buf,
int capture_id, char **value, size_t *value_len, char **err);
int ngx_http_lua_ffi_var_set(ngx_http_request_t *r,
const unsigned char *name_data, size_t name_len,
unsigned char *lowcase_buf, const unsigned char *value,
size_t value_len, unsigned char *errbuf, size_t *errlen);
]]
ngx_lua_ffi_var_get = C.ngx_http_lua_ffi_var_get
ngx_lua_ffi_var_set = C.ngx_http_lua_ffi_var_set
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_var_get(ngx_stream_lua_request_t *r,
const char *name_data, size_t name_len, char *lowcase_buf,
int capture_id, char **value, size_t *value_len, char **err);
int ngx_stream_lua_ffi_var_set(ngx_stream_lua_request_t *r,
const unsigned char *name_data, size_t name_len,
unsigned char *lowcase_buf, const unsigned char *value,
size_t value_len, unsigned char *errbuf, size_t *errlen);
]]
ngx_lua_ffi_var_get = C.ngx_stream_lua_ffi_var_get
ngx_lua_ffi_var_set = C.ngx_stream_lua_ffi_var_set
end
local value_ptr = ffi_new("unsigned char *[1]")
local errmsg = base.get_errmsg_ptr()
local function var_get(self, name)
local r = get_request()
if not r then
error("no request found")
end
local value_len = get_size_ptr()
local rc
if type(name) == "number" then
rc = ngx_lua_ffi_var_get(r, nil, 0, nil, name, value_ptr, value_len,
errmsg)
else
if type(name) ~= "string" then
error("bad variable name", 2)
end
local name_len = #name
local lowcase_buf = get_string_buf(name_len)
rc = ngx_lua_ffi_var_get(r, name, name_len, lowcase_buf, 0, value_ptr,
value_len, errmsg)
end
-- ngx.log(ngx.WARN, "rc = ", rc)
if rc == 0 then -- NGX_OK
return ffi_str(value_ptr[0], value_len[0])
end
if rc == -5 then -- NGX_DECLINED
return nil
end
if rc == -1 then -- NGX_ERROR
error(ffi_str(errmsg[0]), 2)
end
end
local function var_set(self, name, value)
local r = get_request()
if not r then
error("no request found")
end
if type(name) ~= "string" then
error("bad variable name", 2)
end
local name_len = #name
local errlen = get_size_ptr()
errlen[0] = ERR_BUF_SIZE
local lowcase_buf = get_string_buf(name_len + ERR_BUF_SIZE)
local value_len
if value == nil then
value_len = 0
else
if type(value) ~= 'string' then
value = tostring(value)
end
value_len = #value
end
local errbuf = lowcase_buf + name_len
local rc = ngx_lua_ffi_var_set(r, name, name_len, lowcase_buf, value,
value_len, errbuf, errlen)
-- ngx.log(ngx.WARN, "rc = ", rc)
if rc == 0 then -- NGX_OK
return
end
if rc == -1 then -- NGX_ERROR
error(ffi_str(errbuf, errlen[0]), 2)
end
end
do
local mt = new_tab(0, 2)
mt.__index = var_get
mt.__newindex = var_set
setmetatable(ngx.var, mt)
end
return {
version = base.version
}

77
resty/core/worker.lua Normal file
View File

@ -0,0 +1,77 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local new_tab = base.new_tab
local subsystem = ngx.config.subsystem
local ngx_lua_ffi_worker_id
local ngx_lua_ffi_worker_pid
local ngx_lua_ffi_worker_count
local ngx_lua_ffi_worker_exiting
ngx.worker = new_tab(0, 4)
if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_worker_id(void);
int ngx_http_lua_ffi_worker_pid(void);
int ngx_http_lua_ffi_worker_count(void);
int ngx_http_lua_ffi_worker_exiting(void);
]]
ngx_lua_ffi_worker_id = C.ngx_http_lua_ffi_worker_id
ngx_lua_ffi_worker_pid = C.ngx_http_lua_ffi_worker_pid
ngx_lua_ffi_worker_count = C.ngx_http_lua_ffi_worker_count
ngx_lua_ffi_worker_exiting = C.ngx_http_lua_ffi_worker_exiting
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_worker_id(void);
int ngx_stream_lua_ffi_worker_pid(void);
int ngx_stream_lua_ffi_worker_count(void);
int ngx_stream_lua_ffi_worker_exiting(void);
]]
ngx_lua_ffi_worker_id = C.ngx_stream_lua_ffi_worker_id
ngx_lua_ffi_worker_pid = C.ngx_stream_lua_ffi_worker_pid
ngx_lua_ffi_worker_count = C.ngx_stream_lua_ffi_worker_count
ngx_lua_ffi_worker_exiting = C.ngx_stream_lua_ffi_worker_exiting
end
function ngx.worker.exiting()
return ngx_lua_ffi_worker_exiting() ~= 0
end
function ngx.worker.pid()
return ngx_lua_ffi_worker_pid()
end
function ngx.worker.id()
local id = ngx_lua_ffi_worker_id()
if id < 0 then
return nil
end
return id
end
function ngx.worker.count()
return ngx_lua_ffi_worker_count()
end
return {
_VERSION = base.version
}

338
resty/lrucache.lua Normal file
View File

@ -0,0 +1,338 @@
-- Copyright (C) Yichun Zhang (agentzh)
local ffi = require "ffi"
local ffi_new = ffi.new
local ffi_sizeof = ffi.sizeof
local ffi_cast = ffi.cast
local ffi_fill = ffi.fill
local ngx_now = ngx.now
local uintptr_t = ffi.typeof("uintptr_t")
local setmetatable = setmetatable
local tonumber = tonumber
local type = type
local new_tab
do
local ok
ok, new_tab = pcall(require, "table.new")
if not ok then
new_tab = function(narr, nrec) return {} end
end
end
if string.find(jit.version, " 2.0", 1, true) then
ngx.log(ngx.ALERT, "use of lua-resty-lrucache with LuaJIT 2.0 is ",
"not recommended; use LuaJIT 2.1+ instead")
end
local ok, tb_clear = pcall(require, "table.clear")
if not ok then
local pairs = pairs
tb_clear = function (tab)
for k, _ in pairs(tab) do
tab[k] = nil
end
end
end
-- queue data types
--
-- this queue is a double-ended queue and the first node
-- is reserved for the queue itself.
-- the implementation is mostly borrowed from nginx's ngx_queue_t data
-- structure.
ffi.cdef[[
typedef struct lrucache_queue_s lrucache_queue_t;
struct lrucache_queue_s {
double expire; /* in seconds */
lrucache_queue_t *prev;
lrucache_queue_t *next;
uint32_t user_flags;
};
]]
local queue_arr_type = ffi.typeof("lrucache_queue_t[?]")
local queue_type = ffi.typeof("lrucache_queue_t")
local NULL = ffi.null
-- queue utility functions
local function queue_insert_tail(h, x)
local last = h[0].prev
x.prev = last
last.next = x
x.next = h
h[0].prev = x
end
local function queue_init(size)
if not size then
size = 0
end
local q = ffi_new(queue_arr_type, size + 1)
ffi_fill(q, ffi_sizeof(queue_type, size + 1), 0)
if size == 0 then
q[0].prev = q
q[0].next = q
else
local prev = q[0]
for i = 1, size do
local e = q + i
e.user_flags = 0
prev.next = e
e.prev = prev
prev = e
end
local last = q[size]
last.next = q
q[0].prev = last
end
return q
end
local function queue_is_empty(q)
-- print("q: ", tostring(q), "q.prev: ", tostring(q), ": ", q == q.prev)
return q == q[0].prev
end
local function queue_remove(x)
local prev = x.prev
local next = x.next
next.prev = prev
prev.next = next
-- for debugging purpose only:
x.prev = NULL
x.next = NULL
end
local function queue_insert_head(h, x)
x.next = h[0].next
x.next.prev = x
x.prev = h
h[0].next = x
end
local function queue_last(h)
return h[0].prev
end
local function queue_head(h)
return h[0].next
end
-- true module stuffs
local _M = {
_VERSION = '0.10'
}
local mt = { __index = _M }
local function ptr2num(ptr)
return tonumber(ffi_cast(uintptr_t, ptr))
end
function _M.new(size)
if size < 1 then
return nil, "size too small"
end
local self = {
hasht = {},
free_queue = queue_init(size),
cache_queue = queue_init(),
key2node = {},
node2key = {},
num_items = 0,
max_items = size,
}
return setmetatable(self, mt)
end
function _M.count(self)
return self.num_items
end
function _M.capacity(self)
return self.max_items
end
function _M.get(self, key)
local hasht = self.hasht
local val = hasht[key]
if val == nil then
return nil
end
local node = self.key2node[key]
-- print(key, ": moving node ", tostring(node), " to cache queue head")
local cache_queue = self.cache_queue
queue_remove(node)
queue_insert_head(cache_queue, node)
if node.expire >= 0 and node.expire < ngx_now() then
-- print("expired: ", node.expire, " > ", ngx_now())
return nil, val, node.user_flags
end
return val, nil, node.user_flags
end
function _M.delete(self, key)
self.hasht[key] = nil
local key2node = self.key2node
local node = key2node[key]
if not node then
return false
end
key2node[key] = nil
self.node2key[ptr2num(node)] = nil
queue_remove(node)
queue_insert_tail(self.free_queue, node)
self.num_items = self.num_items - 1
return true
end
function _M.set(self, key, value, ttl, flags)
local hasht = self.hasht
hasht[key] = value
local key2node = self.key2node
local node = key2node[key]
if not node then
local free_queue = self.free_queue
local node2key = self.node2key
if queue_is_empty(free_queue) then
-- evict the least recently used key
-- assert(not queue_is_empty(self.cache_queue))
node = queue_last(self.cache_queue)
local oldkey = node2key[ptr2num(node)]
-- print(key, ": evicting oldkey: ", oldkey, ", oldnode: ",
-- tostring(node))
if oldkey then
hasht[oldkey] = nil
key2node[oldkey] = nil
end
else
-- take a free queue node
node = queue_head(free_queue)
-- only add count if we are not evicting
self.num_items = self.num_items + 1
-- print(key, ": get a new free node: ", tostring(node))
end
node2key[ptr2num(node)] = key
key2node[key] = node
end
queue_remove(node)
queue_insert_head(self.cache_queue, node)
if ttl then
node.expire = ngx_now() + ttl
else
node.expire = -1
end
if type(flags) == "number" and flags >= 0 then
node.user_flags = flags
else
node.user_flags = 0
end
end
function _M.get_keys(self, max_count, res)
if not max_count or max_count == 0 then
max_count = self.num_items
end
if not res then
res = new_tab(max_count + 1, 0) -- + 1 for trailing hole
end
local cache_queue = self.cache_queue
local node2key = self.node2key
local i = 0
local node = queue_head(cache_queue)
while node ~= cache_queue do
if i >= max_count then
break
end
i = i + 1
res[i] = node2key[ptr2num(node)]
node = node.next
end
res[i + 1] = nil
return res
end
function _M.flush_all(self)
tb_clear(self.hasht)
tb_clear(self.node2key)
tb_clear(self.key2node)
local cache_queue = self.cache_queue
local free_queue = self.free_queue
-- splice the cache_queue into free_queue
if not queue_is_empty(cache_queue) then
local free_head = free_queue[0]
local free_last = free_head.prev
local cache_head = cache_queue[0]
local cache_first = cache_head.next
local cache_last = cache_head.prev
free_last.next = cache_first
cache_first.prev = free_last
cache_last.next = free_head
free_head.prev = cache_last
cache_head.next = cache_queue
cache_head.prev = cache_queue
end
end
return _M

606
resty/lrucache/pureffi.lua Normal file
View File

@ -0,0 +1,606 @@
-- Copyright (C) Yichun Zhang (agentzh)
-- Copyright (C) Shuxin Yang
--[[
This module implements a key/value cache store. We adopt LRU as our
replace/evict policy. Each key/value pair is tagged with a Time-to-Live (TTL);
from user's perspective, stale pairs are automatically removed from the cache.
Why FFI
-------
In Lua, expression "table[key] = nil" does not *PHYSICALLY* remove the value
associated with the key; it just set the value to be nil! So the table will
keep growing with large number of the key/nil pairs which will be purged until
resize() operator is called.
This "feature" is terribly ill-suited to what we need. Therefore we have to
rely on FFI to build a hash-table where any entry can be physically deleted
immediately.
Under the hood:
--------------
In concept, we introduce three data structures to implement the cache store:
1. key/value vector for storing keys and values.
2. a queue to mimic the LRU.
3. hash-table for looking up the value for a given key.
Unfortunately, efficiency and clarity usually come at each other cost. The
data strucutres we are using are slightly more complicated than what we
described above.
o. Lua does not have efficient way to store a vector of pair. So, we use
two vectors for key/value pair: one for keys and the other for values
(_M.key_v and _M.val_v, respectively), and i-th key corresponds to
i-th value.
A key/value pair is identified by the "id" field in a "node" (we shall
discuss node later)
o. The queue is nothing more than a doubly-linked list of "node" linked via
lrucache_pureffi_queue_s::{next|prev} fields.
o. The hash-table has two parts:
- the _M.bucket_v[] a vector of bucket, indiced by hash-value, and
- a bucket is a singly-linked list of "node" via the
lrucache_pureffi_queue_s::conflict field.
A key must be a string, and the hash value of a key is evaluated by:
crc32(key-cast-to-pointer) % size(_M.bucket_v).
We mandate size(_M.bucket_v) being a power-of-two in order to avoid
expensive modulo operation.
At the heart of the module is an array of "node" (of type
lrucache_pureffi_queue_s). A node:
- keeps the meta-data of its corresponding key/value pair
(embodied by the "id", and "expire" field);
- is a part of LRU queue (embodied by "prev" and "next" fields);
- is a part of hash-table (embodied by the "conflict" field).
]]
local ffi = require "ffi"
local bit = require "bit"
local ffi_new = ffi.new
local ffi_sizeof = ffi.sizeof
local ffi_cast = ffi.cast
local ffi_fill = ffi.fill
local ngx_now = ngx.now
local uintptr_t = ffi.typeof("uintptr_t")
local c_str_t = ffi.typeof("const char*")
local int_t = ffi.typeof("int")
local int_array_t = ffi.typeof("int[?]")
local crc_tab = ffi.new("const unsigned int[256]", {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D });
local setmetatable = setmetatable
local tonumber = tonumber
local tostring = tostring
local type = type
local brshift = bit.rshift
local bxor = bit.bxor
local band = bit.band
local new_tab
do
local ok
ok, new_tab = pcall(require, "table.new")
if not ok then
new_tab = function(narr, nrec) return {} end
end
end
-- queue data types
--
-- this queue is a double-ended queue and the first node
-- is reserved for the queue itself.
-- the implementation is mostly borrowed from nginx's ngx_queue_t data
-- structure.
ffi.cdef[[
/* A lrucache_pureffi_queue_s node hook together three data structures:
* o. the key/value store as embodied by the "id" (which is in essence the
* indentifier of key/pair pair) and the "expire" (which is a metadata
* of the corresponding key/pair pair).
* o. The LRU queue via the prev/next fields.
* o. The hash-tabble as embodied by the "conflict" field.
*/
typedef struct lrucache_pureffi_queue_s lrucache_pureffi_queue_t;
struct lrucache_pureffi_queue_s {
/* Each node is assigned a unique ID at construction time, and the
* ID remain immutatble, regardless the node is in active-list or
* free-list. The queue header is assigned ID 0. Since queue-header
* is a sentinel node, 0 denodes "invalid ID".
*
* Intuitively, we can view the "id" as the identifier of key/value
* pair.
*/
int id;
/* The bucket of the hash-table is implemented as a singly-linked list.
* The "conflict" refers to the ID of the next node in the bucket.
*/
int conflict;
uint32_t user_flags;
double expire; /* in seconds */
lrucache_pureffi_queue_t *prev;
lrucache_pureffi_queue_t *next;
};
]]
local queue_arr_type = ffi.typeof("lrucache_pureffi_queue_t[?]")
--local queue_ptr_type = ffi.typeof("lrucache_pureffi_queue_t*")
local queue_type = ffi.typeof("lrucache_pureffi_queue_t")
local NULL = ffi.null
--========================================================================
--
-- Queue utility functions
--
--========================================================================
-- Append the element "x" to the given queue "h".
local function queue_insert_tail(h, x)
local last = h[0].prev
x.prev = last
last.next = x
x.next = h
h[0].prev = x
end
--[[
Allocate a queue with size + 1 elements. Elements are linked together in a
circular way, i.e. the last element's "next" points to the first element,
while the first element's "prev" element points to the last element.
]]
local function queue_init(size)
if not size then
size = 0
end
local q = ffi_new(queue_arr_type, size + 1)
ffi_fill(q, ffi_sizeof(queue_type, size + 1), 0)
if size == 0 then
q[0].prev = q
q[0].next = q
else
local prev = q[0]
for i = 1, size do
local e = q[i]
e.id = i
e.user_flags = 0
prev.next = e
e.prev = prev
prev = e
end
local last = q[size]
last.next = q
q[0].prev = last
end
return q
end
local function queue_is_empty(q)
-- print("q: ", tostring(q), "q.prev: ", tostring(q), ": ", q == q.prev)
return q == q[0].prev
end
local function queue_remove(x)
local prev = x.prev
local next = x.next
next.prev = prev
prev.next = next
-- for debugging purpose only:
x.prev = NULL
x.next = NULL
end
-- Insert the element "x" the to the given queue "h"
local function queue_insert_head(h, x)
x.next = h[0].next
x.next.prev = x
x.prev = h
h[0].next = x
end
local function queue_last(h)
return h[0].prev
end
local function queue_head(h)
return h[0].next
end
--========================================================================
--
-- Miscellaneous Utility Functions
--
--========================================================================
local function ptr2num(ptr)
return tonumber(ffi_cast(uintptr_t, ptr))
end
local function crc32_ptr(ptr)
local p = brshift(ptr2num(ptr), 3)
local b = band(p, 255)
local crc32 = crc_tab[b]
b = band(brshift(p, 8), 255)
crc32 = bxor(brshift(crc32, 8), crc_tab[band(bxor(crc32, b), 255)])
b = band(brshift(p, 16), 255)
crc32 = bxor(brshift(crc32, 8), crc_tab[band(bxor(crc32, b), 255)])
--b = band(brshift(p, 24), 255)
--crc32 = bxor(brshift(crc32, 8), crc_tab[band(bxor(crc32, b), 255)])
return crc32
end
--========================================================================
--
-- Implementation of "export" functions
--
--========================================================================
local _M = {
_VERSION = '0.10'
}
local mt = { __index = _M }
-- "size" specifies the maximum number of entries in the LRU queue, and the
-- "load_factor" designates the 'load factor' of the hash-table we are using
-- internally. The default value of load-factor is 0.5 (i.e. 50%); if the
-- load-factor is specified, it will be clamped to the range of [0.1, 1](i.e.
-- if load-factor is greater than 1, it will be saturated to 1, likewise,
-- if load-factor is smaller than 0.1, it will be clamped to 0.1).
function _M.new(size, load_factor)
if size < 1 then
return nil, "size too small"
end
-- Determine bucket size, which must be power of two.
local load_f = load_factor
if not load_factor then
load_f = 0.5
elseif load_factor > 1 then
load_f = 1
elseif load_factor < 0.1 then
load_f = 0.1
end
local bs_min = size / load_f
-- The bucket_sz *MUST* be a power-of-two. See the hash_string().
local bucket_sz = 1
repeat
bucket_sz = bucket_sz * 2
until bucket_sz >= bs_min
local self = {
size = size,
bucket_sz = bucket_sz,
free_queue = queue_init(size),
cache_queue = queue_init(0),
node_v = nil,
key_v = new_tab(size, 0),
val_v = new_tab(size, 0),
bucket_v = ffi_new(int_array_t, bucket_sz),
num_items = 0,
}
-- "note_v" is an array of all the nodes used in the LRU queue. Exprpession
-- node_v[i] evaluates to the element of ID "i".
self.node_v = self.free_queue
-- Allocate the array-part of the key_v, val_v, bucket_v.
--local key_v = self.key_v
--local val_v = self.val_v
--local bucket_v = self.bucket_v
ffi_fill(self.bucket_v, ffi_sizeof(int_t, bucket_sz), 0)
return setmetatable(self, mt)
end
function _M.count(self)
return self.num_items
end
function _M.capacity(self)
return self.size
end
local function hash_string(self, str)
local c_str = ffi_cast(c_str_t, str)
local hv = crc32_ptr(c_str)
hv = band(hv, self.bucket_sz - 1)
-- Hint: bucket is 0-based
return hv
end
-- Search the node associated with the key in the bucket, if found returns
-- the the id of the node, and the id of its previous node in the conflict list.
-- The "bucket_hdr_id" is the ID of the first node in the bucket
local function _find_node_in_bucket(key, key_v, node_v, bucket_hdr_id)
if bucket_hdr_id ~= 0 then
local prev = 0
local cur = bucket_hdr_id
while cur ~= 0 and key_v[cur] ~= key do
prev = cur
cur = node_v[cur].conflict
end
if cur ~= 0 then
return cur, prev
end
end
end
-- Return the node corresponding to the key/val.
local function find_key(self, key)
local key_hash = hash_string(self, key)
return _find_node_in_bucket(key, self.key_v, self.node_v,
self.bucket_v[key_hash])
end
--[[ This function tries to
1. Remove the given key and the associated value from the key/value store,
2. Remove the entry associated with the key from the hash-table.
NOTE: all queues remain intact.
If there was a node bound to the key/val, return that node; otherwise,
nil is returned.
]]
local function remove_key(self, key)
local key_v = self.key_v
local val_v = self.val_v
local node_v = self.node_v
local bucket_v = self.bucket_v
local key_hash = hash_string(self, key)
local cur, prev =
_find_node_in_bucket(key, key_v, node_v, bucket_v[key_hash])
if cur then
-- In an attempt to make key and val dead.
key_v[cur] = nil
val_v[cur] = nil
self.num_items = self.num_items - 1
-- Remove the node from the hash table
local next_node = node_v[cur].conflict
if prev ~= 0 then
node_v[prev].conflict = next_node
else
bucket_v[key_hash] = next_node
end
node_v[cur].conflict = 0
return cur
end
end
--[[ Bind the key/val with the given node, and insert the node into the Hashtab.
NOTE: this function does not touch any queue
]]
local function insert_key(self, key, val, node)
-- Bind the key/val with the node
local node_id = node.id
self.key_v[node_id] = key
self.val_v[node_id] = val
-- Insert the node into the hash-table
local key_hash = hash_string(self, key)
local bucket_v = self.bucket_v
node.conflict = bucket_v[key_hash]
bucket_v[key_hash] = node_id
self.num_items = self.num_items + 1
end
function _M.get(self, key)
if type(key) ~= "string" then
key = tostring(key)
end
local node_id = find_key(self, key)
if not node_id then
return nil
end
-- print(key, ": moving node ", tostring(node), " to cache queue head")
local cache_queue = self.cache_queue
local node = self.node_v + node_id
queue_remove(node)
queue_insert_head(cache_queue, node)
local expire = node.expire
if expire >= 0 and expire < ngx_now() then
-- print("expired: ", node.expire, " > ", ngx_now())
return nil, self.val_v[node_id], node.user_flags
end
return self.val_v[node_id], nil, node.user_flags
end
function _M.delete(self, key)
if type(key) ~= "string" then
key = tostring(key)
end
local node_id = remove_key(self, key);
if not node_id then
return false
end
local node = self.node_v + node_id
queue_remove(node)
queue_insert_tail(self.free_queue, node)
return true
end
function _M.set(self, key, value, ttl, flags)
if type(key) ~= "string" then
key = tostring(key)
end
local node_id = find_key(self, key)
local node
if not node_id then
local free_queue = self.free_queue
if queue_is_empty(free_queue) then
-- evict the least recently used key
-- assert(not queue_is_empty(self.cache_queue))
node = queue_last(self.cache_queue)
remove_key(self, self.key_v[node.id])
else
-- take a free queue node
node = queue_head(free_queue)
-- print(key, ": get a new free node: ", tostring(node))
end
-- insert the key
insert_key(self, key, value, node)
else
node = self.node_v + node_id
self.val_v[node_id] = value
end
queue_remove(node)
queue_insert_head(self.cache_queue, node)
if ttl then
node.expire = ngx_now() + ttl
else
node.expire = -1
end
if type(flags) == "number" and flags >= 0 then
node.user_flags = flags
else
node.user_flags = 0
end
end
function _M.get_keys(self, max_count, res)
if not max_count or max_count == 0 then
max_count = self.num_items
end
if not res then
res = new_tab(max_count + 1, 0) -- + 1 for trailing hole
end
local cache_queue = self.cache_queue
local key_v = self.key_v
local i = 0
local node = queue_head(cache_queue)
while node ~= cache_queue do
if i >= max_count then
break
end
i = i + 1
res[i] = key_v[node.id]
node = node.next
end
res[i + 1] = nil
return res
end
function _M.flush_all(self)
local cache_queue = self.cache_queue
local key_v = self.key_v
local node = queue_head(cache_queue)
while node ~= cache_queue do
local key = key_v[node.id]
node = node.next
_M.delete(self, key)
end
end
return _M

233
setup.sh Normal file
View File

@ -0,0 +1,233 @@
#!/bin/bash
#OPTIONS!
MASTERONION="dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion"
TORAUTHPASSWORD="password"
#Shared Front Captcha Key. Key alphanumeric between 64-128. Salt needs to be exactly 8 chars.
KEY="encryption_key"
SALT="salt1234"
#CSS Branding
HEXCOLOR="#9b59b6"
#There is more branding you need to do in the resty/caphtml_d.lua file near the end.
clear
echo "Welcome To The End Game DDOS Prevention Setup..."
sleep 1
BLUE='\033[1;34m'
RED='\033[0;31m'
NC='\033[0m' # No Color
printf "\r\nProvided by your lovely ${BLUE}/u/Paris${NC} from dread. \r\n"
printf "with help from ${BLUE}/u/mr_white${NC} from whitehousemarket.\n"
echo "For the full effects of the DDOS prevention you will need to make sure to setup v3 onionbalance."
echo "Max 6-9 backend instances for each onion. This script will help make the backend instances."
if [ ${#MASTERONION} -lt 62 ]; then
echo "MASTEWRONION doesn't have the correct length. The url needs to include the .onion at the end."
exit 0
fi
if [ -z "$TORAUTHPASSWORD" ]
then
echo "you didn't enter your tor authpassword"
exit 0
fi
sleep 5
echo "Proceeding to do the configuration and setup. This will take awhile."
### Configuration
string="s/masterbalanceonion/"
string+="$MASTERONION"
string+="/g"
sed -i $string site.conf
string="s/torauthpassword/"
string+="$torinput"
string+="/g"
sed -i $string site.conf
string="s/encryption_key/"
string+="$KEY"
string+="/g"
sed -i $string lua/cap.lua
string="s/salt1234/"
string+="$SALT"
string+="/g"
sed -i $string lua/cap.lua
string="s/HEXCOLOR/"
string+="$HEXCOLOR"
string+="/g"
sed -i $string cap_d.css
string="s/SITENAME/"
string+="$SITENAME"
string+="/g"
sed -i $string resty/caphtml_d.lua
apt-get update
apt-get install -y apt-transport-https lsb-release ca-certificates dirmngr
echo "deb https://deb.torproject.org/torproject.org buster main" > /etc/apt/sources.list.d/tor.list
echo "deb-src https://deb.torproject.org/torproject.org buster main" >> /etc/apt/sources.list.d/tor.list
echo "deb https://deb.torproject.org/torproject.org tor-nightly-master-buster main" >> /etc/apt/sources.list.d/tor.list
echo "deb-src https://deb.torproject.org/torproject.org tor-nightly-master-buster main" >> /etc/apt/sources.list.d/tor.list
echo "deb https://nginx.org/packages/debian/ buster nginx" > /etc/apt/sources.list.d/nginx.list
gpg --import A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc
gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | apt-key add -
apt-key add nginx_signing.key
apt-get update
apt-get install -y tor nyx nginx
apt-get install -y vanguards
command="nginx -v"
nginxv=$( ${command} 2>&1 )
NGINXVERSION=$(echo $nginxv | grep -o '[0-9.]*$')
NGINXOPENSSL="1.1.1d"
wget https://nginx.org/download/nginx-$NGINXVERSION.tar.gz
tar -xzvf nginx-$NGINXVERSION.tar.gz
cd nginx-$NGINXVERSION
apt-get install -y build-essential zlib1g-dev libpcre3 libpcre3-dev uuid-dev gcc git wget curl libgd3 libgd-dev
git clone https://github.com/yorkane/socks-nginx-module.git
git clone https://github.com/nbs-system/naxsi.git
wget https://www.openssl.org/source/openssl-$NGINXOPENSSL.tar.gz
tar -xzvf openssl-$NGINXOPENSSL.tar.gz
git clone https://github.com/openresty/headers-more-nginx-module.git
git clone https://github.com/openresty/echo-nginx-module.git
#some required stuff for lua/luajit. obviously versions should be ckecked with every install/update
git clone https://github.com/openresty/lua-nginx-module
cd lua-nginx-module
git checkout v0.10.16
cd ..
git clone https://github.com/openresty/luajit2
cd luajit2
git checkout v2.1-20200102
cd ..
git clone https://github.com/vision5/ngx_devel_kit
cd luajit2
make -j8 && make install
cd ..
export LUAJIT_LIB=/usr/local/lib
export LUAJIT_INC=/usr/local/include/luajit-2.1
./configure --with-cc-opt='-Wno-stringop-overflow -Wno-stringop-truncation -Wno-cast-function-type' \
--with-ld-opt="-Wl,-rpath,/usr/local/lib" \
--with-compat --with-openssl=openssl-$NGINXOPENSSL \
--with-http_ssl_module \
--add-dynamic-module=naxsi/naxsi_src \
--add-dynamic-module=headers-more-nginx-module \
--add-dynamic-module=socks-nginx-module \
--add-dynamic-module=echo-nginx-module \
--add-dynamic-module=ngx_devel_kit \
--add-dynamic-module=lua-nginx-module
git clone https://github.com/openresty/lua-resty-string
cd lua-resty-string
make install
cd ..
git clone https://github.com/cloudflare/lua-resty-cookie
cd lua-resty-cookie
make install
cd ..
git clone https://github.com/bungle/lua-resty-session
cp -a lua-resty-session/lib/resty/session* /usr/local/lib/lua/resty/
git clone https://github.com/ittner/lua-gd/
cd lua-gd
gcc -o gd.so -DGD_XPM -DGD_JPEG -DGD_FONTCONFIG -DGD_FREETYPE -DGD_PNG -DGD_GIF -O2 -Wall -fPIC -fomit-frame-pointer -I/usr/local/include/luajit-2.1 -DVERSION=\"2.0.33r3\" -shared -lgd luagd.c
mv gd.so /usr/local/lib/lua/5.1/gd.so
cd ..
wget -O /usr/local/lib/lua/resty/aes_functions.lua https://github.com/c64bob/lua-resty-aes/raw/master/lib/resty/aes_functions.lua
#include seems to be a bit mssed up with luajit
mkdir /etc/nginx/resty
ln -s /usr/local/lib/lua/resty/ /etc/nginx/resty/
make -j16 modules
cp -r objs modules
mv modules /etc/nginx/modules
cd ..
mv nginx.conf /etc/nginx/nginx.conf
mv naxsi_core.rules /etc/nginx/naxsi_core.rules
mv naxsi_whitelist.rules /etc/nginx/naxsi_whitelist.rules
mv lua /etc/nginx/
mv resty/* /etc/nginx/resty/resty/
mv /etc/nginx/resty/resty/caphtml_d.lua /etc/nginx/resty/caphtml_d.lua
mkdir /etc/nginx/sites-enabled/
mv site.conf /etc/nginx/sites-enabled/site.conf
#background generation
apt-get install -y python3-pil
mv gen_background.py /etc/nginx/gen_background.py
echo "* * * * * root python3 /etc/nginx/gen_background.py" > /etc/cron.d/background-generate
mv font.ttf /etc/nginx/font.ttf
mv cap_d.css /etc/nginx/cap_d.css
chown -R www-data:www-data /etc/nginx/
chown -R www-data:www-data /usr/local/lib/lua
chmod 755 startup.sh
mv startup.sh /startup.sh
chmod 755 rc.local
mv rc.local /etc/rc.local
mv sysctl.conf /etc/sysctl.conf
pkill tor
mv torrc /etc/tor/torrc
torhash=$(tor --hash-password $TORAUTHPASSWORD| tail -c 62)
string="s/hashedpassword/"
string+="$torhash"
string+="/g"
sed -i $string /etc/tor/torrc
sleep 10
tor
sleep 20
HOSTNAME="$(cat /etc/tor/hidden_service/hostname)"
string="s/mainonion/"
string+="$HOSTNAME"
string+="/g"
sed -i $string /etc/nginx/sites-enabled/site.conf
echo "MasterOnionAddress $MASTERONION" >> /etc/tor/hidden_service/ob_config
pkill tor
sleep 10
sed -i "s/#HiddenServiceOnionBalanceInstance/HiddenServiceOnionBalanceInstance/g" /etc/tor/torrc
tor
nginx
service vanguards start
clear
echo "ALL SETUP! Your new front address is"
echo $HOSTNAME
echo "Add it to your onionbalance configuration!"

309
site.conf Normal file
View File

@ -0,0 +1,309 @@
#right now there is a lot of logging to error_log so during an attack those logs will fill the disk eventually.
#a good idea would be to use a syslog server and log to a socket instead of a file for IO optimization
#logging could also be disabled in production
#depending on cluster setup some things can be changed here.
#keepalive 128; or proxy_bind on multiple local ips can be used to mitigate local port exhaustion
#most likely with this setup it's not the case
#if this runs on the same machine as the application server UNIX sockets should be used instead of TCP
access_by_lua_no_postpone on;
lua_package_path "/etc/nginx/resty/?.lua;;";
init_by_lua_block {
allowed_hosts = { "mainonion",
"masterbalanceonion"
}
function in_array(tab, val)
for index, value in ipairs(tab) do
if value == val then
return true
end
end
return nil
end
function split(str, sep)
local result = {}
local regex = ("([^%s]+)"):format(sep)
for each in str:gmatch(regex) do
table.insert(result, each)
end
return result
end
local function calc_circuit(proxyheaderip)
local cg = split(proxyheaderip, ":")
local g1 = cg[5]
local g2 = cg[6]
local glen = string.len(g1)
if (glen < 4) then
for i = (4 - glen),1,-1 do
g1 = "0" .. g1
::loop_label_1::
end
end
glen = string.len(g2)
if (glen < 4) then
for i = (4 - glen),1,-1 do
g2 = "0" .. g2
::loop_label_2::
end
end
local d1 = (string.sub(g1,1,1) .. string.sub(g1,2,2))
local d2 = (string.sub(g1,3,3) .. string.sub(g1,4,4))
local d3 = (string.sub(g2,1,1) .. string.sub(g2,2,2))
local d4 = (string.sub(g2,3,3) .. string.sub(g2,4,4))
local circuit_id = ((((bit.lshift(tonumber(d1, 16), 24)) + (bit.lshift(tonumber(d2, 16), 16))) + (bit.lshift(tonumber(d3, 16), 8))) + tonumber(d4, 16))
return circuit_id
end
function kill_circuit(premature, clientip, headerip)
local circuitid = calc_circuit(headerip)
local response = "Closing circuit " .. circuitid .. " "
local sock = ngx.socket.tcp()
sock:settimeout(1000)
local ok, err = sock:connect(clientip, 9051)
if not ok then
ngx.log(ngx.ERR, "failed to connect to tor: " .. err)
return
end
ngx.log(ngx.ERR, "connected to tor")
local bytes, err = sock:send("authenticate \"torauthpassword\"\n")
if not bytes then
ngx.log(ngx.ERR, "failed authenticate to tor: " .. err)
return
end
local data, err, partial = sock:receive()
if not data then
ngx.log(ngx.ERR, "failed receive data from tor: " .. err)
return
end
local response = response .. " " .. data
local bytes, err = sock:send("closecircuit " .. circuitid .. "\n")
if not bytes then
ngx.log(ngx.ERR, "failed send data to tor: " .. err)
return
end
local data, err, partial = sock:receive()
if not data then
ngx.log(ngx.ERR, "failed receive data from tor: " .. err)
return
end
local response = response .. " " .. data
ngx.log(ngx.ERR, response)
sock:close()
return
end
}
#rate limits should be set to the maximum number of resources (css/images/iframes) a page will load. those should be kept to a minimum for optimization reasons
#limiting by proxy_protocol_addr won't work with V2 onions and maybe should be disabled.
#limiting by cookie_<name> works regarless and must be used, otherwise an attacker can solve a captcha by hand and add it to a script/bot
limit_req_zone $proxy_protocol_addr zone=circuits:50m rate=3r/s;
limit_req_zone $cookie_dcap zone=capcookie:50m rate=3r/s;
#proxy_protocol only makes sense with V3 onions (exportcircuitid) otherwise it will break things.
#kill_circuit won't be used without it
server {
listen 88 proxy_protocol backlog=16384;
allow 127.0.0.1;
deny all;
#access_log /var/log/nginx/front_access.log;
if ($http_x_tor2web) {
return 401;
}
error_page 401 @tor2web;
location @tor2web {
echo_status 401;
default_type text/html;
echo <h1>It seems you are connecting over a Tor2Web Proxy.</h1><p>This is unsafe being that you are giving the proxy a privileged position where it can modify and/or inject content into the webpages you visit as well as track what you do.</p>;
echo <p>When visiting please use the Tor Browser and go to the offical onion address. This keeps you private and safe.</body></html>;
}
more_clear_headers 'Server:*';
more_clear_headers 'X-Page-Speed:*';
more_clear_headers 'Vary*';
more_clear_headers 'captcha-fails*';
#what do do when rate limit is triggered, blacklist the cookie (if exists) and kill circuit
location @ratelimit {
error_log /var/log/nginx/front_error.log;
access_by_lua_block {
local pa = "no_proxy"
if ngx.var.proxy_protocol_addr ~= nil then
pa = ngx.var.proxy_protocol_addr
end
local cook = require "resty.cookie"
local cookie, err = cook:new()
if not cookie then
ngx.log(ngx.ERR, err)
return
end
local field, err = cookie:get("dcap")
if field then
local blocked_cookies = ngx.shared.blocked_cookies
blocked_cookies:set(field, 1, 3600)
end
ngx.log(ngx.ERR, "Rate limited " .. ngx.var.remote_addr .. "|" .. pa)
if pa ~= "no_proxy" then
local ok, err = ngx.timer.at(0, kill_circuit, ngx.var.remote_addr, ngx.var.proxy_protocol_addr)
if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
end
ngx.exit(444)
}
}
#what do do when waf is triggered, just show the error page and kill circuit for now.
#naxsi seems to kick in before everything else except rate limiter but if it does trash traffic won't make it to the application servers anyway
#doesn't make sense to blacklist cookie as it will annoy users
location = @waf {
error_log /var/log/nginx/front_error.log;
default_type text/html;
content_by_lua_block {
ngx.say("<head><title>Error</title></head>")
ngx.say("<body bgcolor=\"white\">")
ngx.say("<center><h1>Error</h1></center>")
ngx.say("<hr><center><p>Your browser sent a request that this server could not understand.</p></center>")
ngx.say("<center><p>Most likely your input contains invalid characters (\" , `, etc.) that except for passwords should not be used.</p></center>")
ngx.say("<center><p>This may also happen if you are trying to send contact information or external links.</p></center>")
ngx.say("<center><p>Please go back, check your input and try again.</p></center></body>")
proxyip = "no_proxy"
torip = ngx.var.remote_addr
if ngx.var.proxy_protocol_addr ~= nil then
proxyip = ngx.var.proxy_protocol_addr
end
ngx.log(ngx.ERR, "WAF triggered " .. torip .. "|" .. proxyip)
if proxyip ~= "no_proxy" then
local ok, err = ngx.timer.at(0, kill_circuit, torip, proxyip)
if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
end
}
}
location /kill {
access_by_lua_block {
proxyip = "no_proxy"
torip = ngx.var.remote_addr
if ngx.var.proxy_protocol_addr ~= nil then
proxyip = ngx.var.proxy_protocol_addr
end
ngx.log(ngx.ERR, "Kill area visited" .. torip .. "|" .. proxyip)
local cook = require "resty.cookie"
local cookie, err = cook:new()
if not cookie then
ngx.log(ngx.ERR, err)
return
end
local field, err = cookie:get("dcap")
if field then
local blocked_cookies = ngx.shared.blocked_cookies
blocked_cookies:set(field, 1, 3600)
end
if proxyip ~= "no_proxy" then
local ok, err = ngx.timer.at(0, kill_circuit, torip, proxyip)
if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
end
ngx.exit(444)
}
}
location / {
#access_log /var/log/nginx/front_access.log;
error_log /var/log/nginx/front_error.log;
#rate limits per circuit ID (won't work with V2 and maybe should be disabled)
limit_req zone=circuits burst=6 nodelay;
error_page 503 =503 @ratelimit;
#rate limits based on captcha cookie. if an attacker or bot solves the capcha by hand and inputs the cookie in a script
#the cookie will be blacklisted by all fronts (eventually) and subsequent requests dropped.
limit_req zone=capcookie burst=6 nodelay;
error_page 503 =503 @ratelimit;
#check if access captca is solved and other things
access_by_lua_file lua/cap.lua;
SecRulesEnabled;
#LearningMode;
DeniedUrl "@waf";
CheckRule "$SQL >= 8" BLOCK;
CheckRule "$RFI >= 8" BLOCK;
CheckRule "$TRAVERSAL >= 4" BLOCK;
CheckRule "$EVADE >= 4" BLOCK;
CheckRule "$XSS >= 8" BLOCK;
include "/etc/nginx/naxsi_whitelist.rules";
error_log /etc/nginx/naxsi.log;
proxy_set_header Host $host;
#socks_pass socks5://127.0.0.1:9050;
#socks_set_host exampleprivatev3onion.onion;
#socks_set_header Host $host;
#socks_redirect off;
#socks_http_version 1.1;
proxy_pass http://10.10.10.10;
header_filter_by_lua_block {
local cookie, err = cook:new()
if not cookie then
ngx.log(ngx.ERR, err)
return
end
local block_cookie = 0
if ngx.resp.get_headers()['captcha-fails'] ~= nil then
local field, err = cookie:get("dcap")
if field then
local failed = ngx.shared.failed
local fl = failed:get(field)
if fl ~= nil then
fl = fl + 1
else
fl = 1
end
failed:set(field, fl, 3600)
if fl > 3 then
block_cookie = 1
failed:delete(field)
end
end
end
if block_cookie > 0 then
local field, err = cookie:get("dcap")
if field then
local blocked_cookies = ngx.shared.blocked_cookies
blocked_cookies:set(field, 1, 3600)
end
end
}
}
}

5
startup.sh Normal file
View File

@ -0,0 +1,5 @@
#!/bin/bash
tor
service vanguards start
exit 0

65
sysctl.conf Normal file
View File

@ -0,0 +1,65 @@
#
# /etc/sysctl.conf - Configuration file for setting system variables
# See /etc/sysctl.d/ for additional system variables.
# See sysctl.conf (5) for information.
#
#kernel.domainname = example.com
# Uncomment the following to stop low-level messages on console
#kernel.printk = 3 4 1 3
##############################################################3
# Functions previously found in netbase
#
# Uncomment the next two lines to enable Spoof protection (reverse-path filter)
# Turn on Source Address Verification in all interfaces to
# prevent some spoofing attacks
#net.ipv4.conf.default.rp_filter=1
#net.ipv4.conf.all.rp_filter=1
# Uncomment the next line to enable TCP/IP SYN cookies
# See http://lwn.net/Articles/277146/
# Note: This may impact IPv6 TCP sessions too
#net.ipv4.tcp_syncookies=1
# Uncomment the next line to enable packet forwarding for IPv4
#net.ipv4.ip_forward=1
# Uncomment the next line to enable packet forwarding for IPv6
# Enabling this option disables Stateless Address Autoconfiguration
# based on Router Advertisements for this host
#net.ipv6.conf.all.forwarding=1
###################################################################
# Additional settings - these settings can improve the network
# security of the host and prevent against some network attacks
# including spoofing attacks and man in the middle attacks through
# redirection. Some network environments, however, require that these
# settings are disabled so review and enable them as needed.
#
# Do not accept ICMP redirects (prevent MITM attacks)
#net.ipv4.conf.all.accept_redirects = 0
#net.ipv6.conf.all.accept_redirects = 0
# _or_
# Accept ICMP redirects only for gateways listed in our default
# gateway list (enabled by default)
# net.ipv4.conf.all.secure_redirects = 1
#
# Do not send ICMP redirects (we are not a router)
#net.ipv4.conf.all.send_redirects = 0
#
# Do not accept IP source route packets (we are not a router)
#net.ipv4.conf.all.accept_source_route = 0
#net.ipv6.conf.all.accept_source_route = 0
#
# Log Martian Packets
#net.ipv4.conf.all.log_martians = 1
#
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
net.core.somaxconn=16384
net.core.netdev_max_backlog=100000

25
torrc Normal file
View File

@ -0,0 +1,25 @@
# Tor config for the onion service instance servers
# ---
# The instance servers run standard onion services. In Basic mode the
# control port does not need to be enabled.
#User debian-tor
#DataDirectory /etc/tor/tor-data
# ControlPort 9051
# CookieAuthentication 1
#SocksPort 0
RunAsDaemon 1
HiddenServiceDir /etc/tor/hidden_service
HiddenServicePort 80 127.0.0.1:88
HiddenServiceMaxStreams 8
HiddenServiceMaxStreamsCloseCircuit 1
HiddenServiceNumIntroductionPoints 3
HiddenServiceExportCircuitID haproxy
#HiddenServiceOnionBalanceInstance 1
CookieAuthentication 1
ControlPort 9051
HashedControlPassword hashedpassword
HardwareAccel 1