-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Expand file tree
/
Copy pathpatch.lua
More file actions
327 lines (265 loc) · 8.43 KB
/
Copy pathpatch.lua
File metadata and controls
327 lines (265 loc) · 8.43 KB
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
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local require = require
require("resty.dns.resolver") -- preload dns resolver to prevent recursive patch
local ipmatcher = require("resty.ipmatcher")
local socket = require("socket")
local unix_socket = require("socket.unix")
local ssl = require("ssl")
local get_phase = ngx.get_phase
local ngx_socket = ngx.socket
local original_tcp = ngx.socket.tcp
local original_udp = ngx.socket.udp
local concat_tab = table.concat
local new_tab = require("table.new")
local log = ngx.log
local WARN = ngx.WARN
local ipairs = ipairs
local select = select
local setmetatable = setmetatable
local type = type
local config_local
local _M = {}
local function get_local_conf()
if not config_local then
config_local = require("apisix.core.config_local")
end
return config_local.local_conf()
end
local patch_tcp_socket
do
local old_tcp_sock_connect
local function new_tcp_sock_connect(sock, host, port, opts)
local core_str = require("apisix.core.string")
local resolver = require("apisix.core.resolver")
if host then
if core_str.has_prefix(host, "unix:") then
if not opts then
-- workaround for https://github.com/openresty/lua-nginx-module/issues/860
return old_tcp_sock_connect(sock, host)
end
elseif not ipmatcher.parse_ipv4(host) and not ipmatcher.parse_ipv6(host) then
local err
host, err = resolver.parse_domain(host)
if not host then
return nil, "failed to parse domain: " .. err
end
end
end
return old_tcp_sock_connect(sock, host, port, opts)
end
function patch_tcp_socket(sock)
if not old_tcp_sock_connect then
old_tcp_sock_connect = sock.connect
end
sock.connect = new_tcp_sock_connect
return sock
end
end
local patch_udp_socket
do
local old_udp_sock_setpeername
local function new_udp_sock_setpeername(sock, host, port)
local core_str = require("apisix.core.string")
local resolver = require("apisix.core.resolver")
if host then
if core_str.has_prefix(host, "unix:") then
return old_udp_sock_setpeername(sock, host)
end
if not ipmatcher.parse_ipv4(host) and not ipmatcher.parse_ipv6(host) then
local err
host, err = resolver.parse_domain(host)
if not host then
return nil, "failed to parse domain: " .. err
end
end
end
return old_udp_sock_setpeername(sock, host, port)
end
function patch_udp_socket(sock)
if not old_udp_sock_setpeername then
old_udp_sock_setpeername = sock.setpeername
end
sock.setpeername = new_udp_sock_setpeername
return sock
end
end
local function flatten(args)
local buf = new_tab(#args, 0)
for i, v in ipairs(args) do
local ty = type(v)
if ty == "table" then
buf[i] = flatten(v)
elseif ty == "boolean" then
buf[i] = v and "true" or "false"
elseif ty == "nil" then
buf[i] = "nil"
else
buf[i] = v
end
end
return concat_tab(buf)
end
local luasocket_wrapper = {
connect = function (self, host, port)
if not port then
-- unix socket
self.sock = unix_socket()
if self.timeout then
self.sock:settimeout(self.timeout)
end
local path = host:sub(#("unix:") + 1)
return self.sock:connect(path)
end
return self.sock:connect(host, port)
end,
send = function(self, ...)
if select('#', ...) == 1 and type(select(1, ...)) == "string" then
-- fast path
return self.sock:send(...)
end
-- luasocket's send only accepts a single string
return self.sock:send(flatten({...}))
end,
getreusedtimes = function ()
return 0
end,
setkeepalive = function (self)
self.sock:close()
return 1
end,
settimeout = function (self, time)
if time then
time = time / 1000
end
self.timeout = time
return self.sock:settimeout(time)
end,
settimeouts = function (self, connect_time, read_time, write_time)
connect_time = connect_time or 0
read_time = read_time or 0
write_time = write_time or 0
-- set the max one as the timeout
local time = connect_time
if time < read_time then
time = read_time
end
if time < write_time then
time = write_time
end
if time > 0 then
time = time / 1000
else
time = nil
end
self.timeout = time
return self.sock:settimeout(time)
end,
tlshandshake = function (self, options)
local reused_session = options.reused_session
local server_name = options.server_name
local verify = options.verify
local send_status_req = options.ocsp_status_req
if reused_session then
log(WARN, "reused_session is not supported yet")
end
if send_status_req then
log(WARN, "send_status_req is not supported yet")
end
local params = {
mode = "client",
protocol = "any",
verify = verify and "peer" or "none",
certificate = options.client_cert_path,
key = options.client_priv_key_path,
options = {
"all",
"no_sslv2",
"no_sslv3",
"no_tlsv1"
}
}
local local_conf, err = get_local_conf()
if not local_conf then
return nil, err
end
local apisix_ssl = local_conf.apisix.ssl
if apisix_ssl and apisix_ssl.ssl_trusted_certificate then
params.cafile = apisix_ssl.ssl_trusted_certificate
end
local sec_sock, err = ssl.wrap(self.sock, params)
if not sec_sock then
return false, err
end
if server_name then
sec_sock:sni(server_name)
end
local success
success, err = sec_sock:dohandshake()
if not success then
return false, err
end
self.sock = sec_sock
return true
end,
sslhandshake = function (self, reused_session, server_name, verify, send_status_req)
return self:tlshandshake({
reused_session = reused_session,
server_name = server_name,
verify = verify,
ocsp_status_req = send_status_req,
})
end
}
local mt = {
__index = function(self, key)
local sock = self.sock
local fn = luasocket_wrapper[key]
if fn then
self[key] = fn
return fn
end
local origin = sock[key]
if type(origin) ~= "function" then
return origin
end
fn = function(_, ...)
return origin(sock, ...)
end
self[key] = fn
return fn
end
}
local function luasocket_tcp()
local sock = socket.tcp()
return setmetatable({sock = sock}, mt)
end
function _M.patch()
-- make linter happy
-- luacheck: ignore
ngx_socket.tcp = function ()
local phase = get_phase()
if phase ~= "init" and phase ~= "init_worker" then
return patch_tcp_socket(original_tcp())
end
return luasocket_tcp()
end
ngx_socket.udp = function ()
return patch_udp_socket(original_udp())
end
end
return _M