1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
|
local base = require "resty.core.base"
-- base.allows_subsystem("http")
local debug = require "debug"
local ffi = require "ffi"
local subsystem = ngx.config.subsystem
local error = error
local assert = assert
local tonumber = tonumber
local tostring = tostring
local type = type
local select = select
local registry = debug.getregistry()
local C = ffi.C
local ffi_new = ffi.new
local ffi_str = ffi.string
local ffi_gc = ffi.gc
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 option_index = {
["keepalive"] = 1,
["reuseaddr"] = 2,
["tcp-nodelay"] = 3,
["sndbuf"] = 4,
["rcvbuf"] = 5,
["ip-transparent"] = 6,
}
local ngx_lua_ffi_socket_tcp_getoption
local ngx_lua_ffi_socket_tcp_setoption
if subsystem == 'http' then
ffi.cdef[[
typedef struct ngx_http_lua_socket_tcp_upstream_s
ngx_http_lua_socket_tcp_upstream_t;
int
ngx_http_lua_ffi_socket_tcp_getoption(ngx_http_lua_socket_tcp_upstream_t *u,
int opt, int *val, unsigned char *err, size_t *errlen);
int
ngx_http_lua_ffi_socket_tcp_setoption(ngx_http_lua_socket_tcp_upstream_t *u,
int opt, int val, unsigned char *err, size_t *errlen);
int
ngx_http_lua_ffi_socket_tcp_sslhandshake(ngx_http_request_t *r,
ngx_http_lua_socket_tcp_upstream_t *u, void *sess,
int enable_session_reuse, ngx_str_t *server_name, int verify,
int ocsp_status_req, void *chain, void *pkey, char **errmsg);
int
ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(ngx_http_request_t *r,
ngx_http_lua_socket_tcp_upstream_t *u, void **sess, char **errmsg,
int *openssl_error_code);
void
ngx_http_lua_ffi_ssl_free_session(void *sess);
int
ngx_http_lua_ffi_socket_tcp_getfd(ngx_http_request_t *r,
ngx_http_lua_socket_tcp_upstream_t *u, char **errmsg);
]]
ngx_lua_ffi_socket_tcp_getoption = C.ngx_http_lua_ffi_socket_tcp_getoption
ngx_lua_ffi_socket_tcp_setoption = C.ngx_http_lua_ffi_socket_tcp_setoption
elseif subsystem == 'stream' then
ffi.cdef[[
typedef struct ngx_stream_lua_socket_tcp_upstream_s
ngx_stream_lua_socket_tcp_upstream_t;
int
ngx_stream_lua_ffi_socket_tcp_getoption(ngx_stream_lua_socket_tcp_upstream_t *u,
int option, int *val, unsigned char *err, size_t *errlen);
int
ngx_stream_lua_ffi_socket_tcp_setoption(ngx_stream_lua_socket_tcp_upstream_t *u,
int opt, int val, unsigned char *err, size_t *errlen);
]]
ngx_lua_ffi_socket_tcp_getoption = C.ngx_stream_lua_ffi_socket_tcp_getoption
ngx_lua_ffi_socket_tcp_setoption = C.ngx_stream_lua_ffi_socket_tcp_setoption
end
local output_value_buf = ffi_new("int[1]")
local ERR_BUF_SIZE = 4096
local FFI_OK = base.FFI_OK
local FFI_ERROR = base.FFI_ERROR
local FFI_DONE = base.FFI_DONE
local FFI_AGAIN = base.FFI_AGAIN
local FFI_NO_REQ_CTX = base.FFI_NO_REQ_CTX
local SOCKET_CTX_INDEX = 1
local SOCKET_CLIENT_CERT_INDEX = 6
local SOCKET_CLIENT_PKEY_INDEX = 7
local SOCKET_IP_TRANSPARENT_INDEX = 9
local function get_tcp_socket(cosocket)
local tcp_socket = cosocket[SOCKET_CTX_INDEX]
if not tcp_socket then
error("socket is never created nor connected")
end
return tcp_socket
end
local function getoption(cosocket, option)
local tcp_socket = get_tcp_socket(cosocket)
if option == nil then
return nil, 'missing the "option" argument'
end
if option_index[option] == nil then
return nil, "unsupported option " .. tostring(option)
end
local err = get_string_buf(ERR_BUF_SIZE)
local errlen = get_size_ptr()
errlen[0] = ERR_BUF_SIZE
local rc = ngx_lua_ffi_socket_tcp_getoption(tcp_socket,
option_index[option],
output_value_buf,
err,
errlen)
if rc ~= FFI_OK then
return nil, ffi_str(err, errlen[0])
end
return tonumber(output_value_buf[0])
end
local function setoption(cosocket, option, value)
if option == nil then
return nil, 'missing the "option" argument'
end
if value == nil then
return nil, 'missing the "value" argument'
end
if option == "ip-transparent" then
cosocket[SOCKET_IP_TRANSPARENT_INDEX] = value
return true
end
local tcp_socket = get_tcp_socket(cosocket)
if option_index[option] == nil then
return nil, "unsupported option " .. tostring(option)
end
local err = get_string_buf(ERR_BUF_SIZE)
local errlen = get_size_ptr()
errlen[0] = ERR_BUF_SIZE
local rc = ngx_lua_ffi_socket_tcp_setoption(tcp_socket,
option_index[option],
value,
err,
errlen)
if rc ~= FFI_OK then
return nil, ffi_str(err, errlen[0])
end
return true
end
if subsystem == 'http' then
local errmsg = base.get_errmsg_ptr()
local session_ptr = ffi_new("void *[1]")
local server_name_str = ffi_new("ngx_str_t[1]")
local openssl_error_code = ffi_new("int[1]")
local function setclientcert(cosocket, cert, pkey)
if not cert and not pkey then
cosocket[SOCKET_CLIENT_CERT_INDEX] = nil
cosocket[SOCKET_CLIENT_PKEY_INDEX] = nil
return true
end
if not cert or not pkey then
return nil,
"client certificate must be supplied with corresponding " ..
"private key"
end
if type(cert) ~= "cdata" then
return nil, "bad cert arg: cdata expected, got " .. type(cert)
end
if type(pkey) ~= "cdata" then
return nil, "bad pkey arg: cdata expected, got " .. type(pkey)
end
cosocket[SOCKET_CLIENT_CERT_INDEX] = cert
cosocket[SOCKET_CLIENT_PKEY_INDEX] = pkey
return true
end
local function sslhandshake(cosocket, reused_session, server_name, ssl_verify,
send_status_req, ...)
local n = select("#", ...)
if not cosocket or n > 0 then
error("ngx.socket sslhandshake: expecting 1 ~ 5 arguments " ..
"(including the object), but seen " .. (cosocket and 5 + n or 0))
end
local r = get_request()
if not r then
error("no request found", 2)
end
session_ptr[0] = type(reused_session) == "cdata" and reused_session or nil
if server_name then
server_name_str[0].data = server_name
server_name_str[0].len = #server_name
else
server_name_str[0].data = nil
server_name_str[0].len = 0
end
local u = get_tcp_socket(cosocket)
local rc = C.ngx_http_lua_ffi_socket_tcp_sslhandshake(r, u,
session_ptr[0],
reused_session ~= false,
server_name_str,
ssl_verify and 1 or 0,
send_status_req and 1 or 0,
cosocket[SOCKET_CLIENT_CERT_INDEX],
cosocket[SOCKET_CLIENT_PKEY_INDEX],
errmsg)
if rc == FFI_NO_REQ_CTX then
error("no request ctx found", 2)
end
if rc == FFI_OK then
if reused_session == false then
return true
end
rc = C.ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(r, u,
session_ptr, errmsg, openssl_error_code)
end
while true do
if rc == FFI_ERROR then
if openssl_error_code[0] ~= 0 then
return nil, openssl_error_code[0] .. ": " .. ffi_str(errmsg[0])
end
return nil, ffi_str(errmsg[0])
end
if rc == FFI_DONE then
return reused_session
end
if rc == FFI_OK then
if reused_session == false then
return true
end
rc = C.ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(r, u,
session_ptr, errmsg, openssl_error_code)
assert(rc == FFI_OK)
if session_ptr[0] == nil then
return session_ptr[0]
end
return ffi_gc(session_ptr[0], C.ngx_http_lua_ffi_ssl_free_session)
end
assert(rc == FFI_AGAIN)
co_yield()
rc = C.ngx_http_lua_ffi_socket_tcp_get_sslhandshake_result(r, u,
session_ptr, errmsg, openssl_error_code)
end
end
local function getfd(cosocket)
if not cosocket then
error("ngx.socket getfd: expecting the cosocket object, but seen none")
end
local r = get_request()
if not r then
error("no request found")
end
local u = get_tcp_socket(cosocket)
local fd = C.ngx_http_lua_ffi_socket_tcp_getfd(r, u, errmsg)
if (fd < 0) then
return nil, ffi_str(errmsg[0])
end
return fd;
end
do
local method_table = registry.__tcp_cosocket_mt
method_table.getoption = getoption
method_table.setoption = setoption
method_table.setclientcert = setclientcert
method_table.sslhandshake = sslhandshake
method_table.getfd = getfd
method_table = registry.__tcp_req_cosocket_mt
method_table.getfd = getfd
method_table = registry.__tcp_raw_req_cosocket_mt
method_table.getfd = getfd
end
elseif subsystem == 'stream' then
do
local method_table = registry.__tcp_cosocket_mt
method_table.getoption = getoption
method_table.setoption = setoption
end
end
return { version = base.version }
|