You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lua_http.c 25KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "lua_common.h"
  17. #include "lua_thread_pool.h"
  18. #include "http_private.h"
  19. #include "ref.h"
  20. #include "unix-std.h"
  21. #include "zlib.h"
  22. /***
  23. * @module rspamd_http
  24. * Rspamd HTTP module represents HTTP asynchronous client available from LUA code.
  25. * This module hides all complexity: DNS resolving, sessions management, zero-copy
  26. * text transfers and so on under the hood.
  27. * @example
  28. local rspamd_http = require "rspamd_http"
  29. local function symbol_callback(task)
  30. local function http_callback(err_message, code, body, headers)
  31. task:insert_result('SYMBOL', 1) -- task is available via closure
  32. end
  33. rspamd_http.request({
  34. task=task,
  35. url='http://example.com/data',
  36. body=task:get_content(),
  37. callback=http_callback,
  38. headers={Header='Value', OtherHeader='Value'},
  39. mime_type='text/plain',
  40. })
  41. end
  42. */
  43. #define MAX_HEADERS_SIZE 8192
  44. static const gchar *M = "rspamd lua http";
  45. LUA_FUNCTION_DEF (http, request);
  46. static const struct luaL_reg httplib_m[] = {
  47. LUA_INTERFACE_DEF (http, request),
  48. {"__tostring", rspamd_lua_class_tostring},
  49. {NULL, NULL}
  50. };
  51. #define RSPAMD_LUA_HTTP_FLAG_TEXT (1 << 0)
  52. #define RSPAMD_LUA_HTTP_FLAG_NOVERIFY (1 << 1)
  53. #define RSPAMD_LUA_HTTP_FLAG_RESOLVED (1 << 2)
  54. #define RSPAMD_LUA_HTTP_FLAG_KEEP_ALIVE (1 << 3)
  55. #define RSPAMD_LUA_HTTP_FLAG_YIELDED (1 << 4)
  56. struct lua_http_cbdata {
  57. struct rspamd_http_connection *conn;
  58. struct rspamd_async_session *session;
  59. struct rspamd_symcache_item *item;
  60. struct rspamd_http_message *msg;
  61. struct event_base *ev_base;
  62. struct rspamd_config *cfg;
  63. struct rspamd_task *task;
  64. struct timeval tv;
  65. struct rspamd_cryptobox_keypair *local_kp;
  66. struct rspamd_cryptobox_pubkey *peer_pk;
  67. rspamd_inet_addr_t *addr;
  68. gchar *mime_type;
  69. gchar *host;
  70. gchar *auth;
  71. const gchar *url;
  72. gsize max_size;
  73. gint flags;
  74. gint fd;
  75. gint cbref;
  76. struct thread_entry *thread;
  77. ref_entry_t ref;
  78. };
  79. static const int default_http_timeout = 5000;
  80. static struct rspamd_dns_resolver *
  81. lua_http_global_resolver (struct event_base *ev_base)
  82. {
  83. static struct rspamd_dns_resolver *global_resolver;
  84. if (global_resolver == NULL) {
  85. global_resolver = rspamd_dns_resolver_init (NULL, ev_base, NULL);
  86. }
  87. return global_resolver;
  88. }
  89. static void
  90. lua_http_fin (gpointer arg)
  91. {
  92. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *)arg;
  93. if (cbd->cbref != -1) {
  94. luaL_unref (cbd->cfg->lua_state, LUA_REGISTRYINDEX, cbd->cbref);
  95. }
  96. if (cbd->conn) {
  97. /* Here we already have a connection, so we need to unref it */
  98. rspamd_http_connection_unref (cbd->conn);
  99. }
  100. else if (cbd->msg != NULL) {
  101. /* We need to free message */
  102. rspamd_http_message_unref (cbd->msg);
  103. }
  104. if (cbd->fd != -1) {
  105. close (cbd->fd);
  106. }
  107. if (cbd->addr) {
  108. rspamd_inet_address_free (cbd->addr);
  109. }
  110. if (cbd->mime_type) {
  111. g_free (cbd->mime_type);
  112. }
  113. if (cbd->auth) {
  114. g_free (cbd->auth);
  115. }
  116. if (cbd->local_kp) {
  117. rspamd_keypair_unref (cbd->local_kp);
  118. }
  119. if (cbd->peer_pk) {
  120. rspamd_pubkey_unref (cbd->peer_pk);
  121. }
  122. g_free (cbd);
  123. }
  124. static void
  125. lua_http_cbd_dtor (struct lua_http_cbdata *cbd)
  126. {
  127. if (cbd->session) {
  128. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_RESOLVED) {
  129. /* Event is added merely for resolved events */
  130. if (cbd->item) {
  131. rspamd_symcache_item_async_dec_check (cbd->task, cbd->item, M);
  132. }
  133. rspamd_session_remove_event (cbd->session, lua_http_fin, cbd);
  134. }
  135. }
  136. else {
  137. lua_http_fin (cbd);
  138. }
  139. }
  140. static void
  141. lua_http_push_error (struct lua_http_cbdata *cbd, const char *err)
  142. {
  143. struct lua_callback_state lcbd;
  144. lua_State *L;
  145. lua_thread_pool_prepare_callback (cbd->cfg->lua_thread_pool, &lcbd);
  146. L = lcbd.L;
  147. lua_rawgeti (L, LUA_REGISTRYINDEX, cbd->cbref);
  148. lua_pushstring (L, err);
  149. if (cbd->item) {
  150. rspamd_symcache_set_cur_item (cbd->task, cbd->item);
  151. }
  152. if (lua_pcall (L, 1, 0, 0) != 0) {
  153. msg_info ("callback call failed: %s", lua_tostring (L, -1));
  154. lua_pop (L, 1);
  155. }
  156. lua_thread_pool_restore_callback (&lcbd);
  157. }
  158. static void lua_http_resume_handler (struct rspamd_http_connection *conn,
  159. struct rspamd_http_message *msg, const char *err);
  160. static void
  161. lua_http_error_handler (struct rspamd_http_connection *conn, GError *err)
  162. {
  163. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *)conn->ud;
  164. if (cbd->cbref == -1) {
  165. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_YIELDED) {
  166. cbd->flags &= ~RSPAMD_LUA_HTTP_FLAG_YIELDED;
  167. lua_http_resume_handler (conn, NULL, err->message);
  168. }
  169. else {
  170. /* TODO: kill me please */
  171. msg_info ("lost HTTP error from %s in coroutines mess: %s",
  172. rspamd_inet_address_to_string_pretty (cbd->addr),
  173. err->message);
  174. }
  175. }
  176. else {
  177. lua_http_push_error (cbd, err->message);
  178. }
  179. REF_RELEASE (cbd);
  180. }
  181. static int
  182. lua_http_finish_handler (struct rspamd_http_connection *conn,
  183. struct rspamd_http_message *msg)
  184. {
  185. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *)conn->ud;
  186. struct rspamd_http_header *h, *htmp;
  187. const gchar *body;
  188. gsize body_len;
  189. struct lua_callback_state lcbd;
  190. lua_State *L;
  191. if (cbd->cbref == -1) {
  192. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_YIELDED) {
  193. cbd->flags &= ~RSPAMD_LUA_HTTP_FLAG_YIELDED;
  194. lua_http_resume_handler (conn, msg, NULL);
  195. }
  196. else {
  197. /* TODO: kill me please */
  198. msg_err ("lost HTTP data from %s in coroutines mess",
  199. rspamd_inet_address_to_string_pretty (cbd->addr));
  200. }
  201. REF_RELEASE (cbd);
  202. return 0;
  203. }
  204. lua_thread_pool_prepare_callback (cbd->cfg->lua_thread_pool, &lcbd);
  205. L = lcbd.L;
  206. lua_rawgeti (L, LUA_REGISTRYINDEX, cbd->cbref);
  207. /* Error */
  208. lua_pushnil (L);
  209. /* Reply code */
  210. lua_pushinteger (L, msg->code);
  211. /* Body */
  212. body = rspamd_http_message_get_body (msg, &body_len);
  213. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_TEXT) {
  214. struct rspamd_lua_text *t;
  215. t = lua_newuserdata (L, sizeof (*t));
  216. rspamd_lua_setclass (L, "rspamd{text}", -1);
  217. t->start = body;
  218. t->len = body_len;
  219. t->flags = 0;
  220. }
  221. else {
  222. if (body_len > 0) {
  223. lua_pushlstring (L, body, body_len);
  224. }
  225. else {
  226. lua_pushnil (L);
  227. }
  228. }
  229. /* Headers */
  230. lua_newtable (L);
  231. HASH_ITER (hh, msg->headers, h, htmp) {
  232. /*
  233. * Lowercase header name, as Lua cannot search in caseless matter
  234. */
  235. rspamd_str_lc (h->combined->str, h->name.len);
  236. lua_pushlstring (L, h->name.begin, h->name.len);
  237. lua_pushlstring (L, h->value.begin, h->value.len);
  238. lua_settable (L, -3);
  239. }
  240. if (cbd->item) {
  241. /* Replace watcher to deal with nested calls */
  242. rspamd_symcache_set_cur_item (cbd->task, cbd->item);
  243. }
  244. if (lua_pcall (L, 4, 0, 0) != 0) {
  245. msg_info ("callback call failed: %s", lua_tostring (L, -1));
  246. lua_pop (L, 1);
  247. }
  248. REF_RELEASE (cbd);
  249. lua_thread_pool_restore_callback (&lcbd);
  250. return 0;
  251. }
  252. /*
  253. * resumes yielded thread
  254. */
  255. static void
  256. lua_http_resume_handler (struct rspamd_http_connection *conn,
  257. struct rspamd_http_message *msg, const char *err)
  258. {
  259. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *)conn->ud;
  260. lua_State *L = cbd->thread->lua_state;
  261. const gchar *body;
  262. gsize body_len;
  263. struct rspamd_http_header *h, *htmp;
  264. if (err) {
  265. lua_pushstring (L, err);
  266. lua_pushnil (L);
  267. }
  268. else {
  269. /*
  270. * 1 - nil (error)
  271. * 2 - table:
  272. * code (int)
  273. * content (string)
  274. * headers (table: header -> value)
  275. */
  276. lua_pushnil (L); // error code
  277. lua_createtable (L, 0, 3);
  278. /* code */
  279. lua_pushliteral (L, "code");
  280. lua_pushinteger (L, msg->code);
  281. lua_settable (L, -3);
  282. /* content */
  283. lua_pushliteral (L, "content");
  284. body = rspamd_http_message_get_body (msg, &body_len);
  285. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_TEXT) {
  286. struct rspamd_lua_text *t;
  287. t = lua_newuserdata (L, sizeof (*t));
  288. rspamd_lua_setclass (L, "rspamd{text}", -1);
  289. t->start = body;
  290. t->len = body_len;
  291. t->flags = 0;
  292. }
  293. else {
  294. if (body_len > 0) {
  295. lua_pushlstring (L, body, body_len);
  296. }
  297. else {
  298. lua_pushnil (L);
  299. }
  300. }
  301. lua_settable (L, -3);
  302. /* headers */
  303. lua_pushliteral (L, "headers");
  304. lua_newtable (L);
  305. HASH_ITER (hh, msg->headers, h, htmp) {
  306. /*
  307. * Lowercase header name, as Lua cannot search in caseless matter
  308. */
  309. rspamd_str_lc (h->combined->str, h->name.len);
  310. lua_pushlstring (L, h->name.begin, h->name.len);
  311. lua_pushlstring (L, h->value.begin, h->value.len);
  312. lua_settable (L, -3);
  313. }
  314. lua_settable (L, -3);
  315. }
  316. if (cbd->item) {
  317. /* Replace watcher to deal with nested calls */
  318. rspamd_symcache_set_cur_item (cbd->task, cbd->item);
  319. }
  320. lua_thread_resume (cbd->thread, 2);
  321. }
  322. static gboolean
  323. lua_http_make_connection (struct lua_http_cbdata *cbd)
  324. {
  325. rspamd_inet_address_set_port (cbd->addr, cbd->msg->port);
  326. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_KEEP_ALIVE) {
  327. cbd->fd = -1; /* FD is owned by keepalive connection */
  328. cbd->conn = rspamd_http_connection_new_keepalive (
  329. NULL, /* Default context */
  330. NULL,
  331. lua_http_error_handler,
  332. lua_http_finish_handler,
  333. cbd->addr,
  334. cbd->host);
  335. }
  336. else {
  337. cbd->fd = -1;
  338. cbd->conn = rspamd_http_connection_new_client (
  339. NULL, /* Default context */
  340. NULL,
  341. lua_http_error_handler,
  342. lua_http_finish_handler,
  343. RSPAMD_HTTP_CLIENT_SIMPLE,
  344. cbd->addr);
  345. }
  346. if (cbd->conn) {
  347. if (cbd->local_kp) {
  348. rspamd_http_connection_set_key (cbd->conn, cbd->local_kp);
  349. }
  350. if (cbd->peer_pk) {
  351. rspamd_http_message_set_peer_key (cbd->msg, cbd->peer_pk);
  352. }
  353. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_NOVERIFY) {
  354. cbd->msg->flags |= RSPAMD_HTTP_FLAG_SSL_NOVERIFY;
  355. }
  356. if (cbd->max_size) {
  357. rspamd_http_connection_set_max_size (cbd->conn, cbd->max_size);
  358. }
  359. if (cbd->auth) {
  360. rspamd_http_message_add_header (cbd->msg, "Authorization",
  361. cbd->auth);
  362. }
  363. if (cbd->session) {
  364. rspamd_session_add_event (cbd->session,
  365. (event_finalizer_t) lua_http_fin, cbd,
  366. M);
  367. cbd->flags |= RSPAMD_LUA_HTTP_FLAG_RESOLVED;
  368. }
  369. if (cbd->item) {
  370. rspamd_symcache_item_async_inc (cbd->task, cbd->item, M);
  371. }
  372. struct rspamd_http_message *msg = cbd->msg;
  373. /* Message is now owned by a connection object */
  374. cbd->msg = NULL;
  375. rspamd_http_connection_write_message (cbd->conn, msg,
  376. cbd->host, cbd->mime_type, cbd,
  377. &cbd->tv);
  378. return TRUE;
  379. }
  380. return FALSE;
  381. }
  382. static void
  383. lua_http_dns_handler (struct rdns_reply *reply, gpointer ud)
  384. {
  385. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *)ud;
  386. if (reply->code != RDNS_RC_NOERROR) {
  387. lua_http_push_error (cbd, "unable to resolve host");
  388. REF_RELEASE (cbd);
  389. }
  390. else {
  391. if (reply->entries->type == RDNS_REQUEST_A) {
  392. cbd->addr = rspamd_inet_address_new (AF_INET,
  393. &reply->entries->content.a.addr);
  394. }
  395. else if (reply->entries->type == RDNS_REQUEST_AAAA) {
  396. cbd->addr = rspamd_inet_address_new (AF_INET6,
  397. &reply->entries->content.aaa.addr);
  398. }
  399. REF_RETAIN (cbd);
  400. if (!lua_http_make_connection (cbd)) {
  401. lua_http_push_error (cbd, "unable to make connection to the host");
  402. if (cbd->ref.refcount > 1) {
  403. REF_RELEASE (cbd);
  404. }
  405. REF_RELEASE (cbd);
  406. return;
  407. }
  408. REF_RELEASE (cbd);
  409. }
  410. if (cbd->item) {
  411. rspamd_symcache_item_async_dec_check (cbd->task, cbd->item, M);
  412. }
  413. }
  414. static void
  415. lua_http_push_headers (lua_State *L, struct rspamd_http_message *msg)
  416. {
  417. const char *name, *value;
  418. gint i, sz;
  419. lua_pushnil (L);
  420. while (lua_next (L, -2) != 0) {
  421. lua_pushvalue (L, -2);
  422. name = lua_tostring (L, -1);
  423. sz = rspamd_lua_table_size (L, -2);
  424. if (sz != 0 && name != NULL) {
  425. for (i = 1; i <= sz ; i++) {
  426. lua_rawgeti (L, -2, i);
  427. value = lua_tostring (L, -1);
  428. if (value != NULL) {
  429. rspamd_http_message_add_header (msg, name, value);
  430. }
  431. lua_pop (L, 1);
  432. }
  433. } else {
  434. value = lua_tostring (L, -2);
  435. if (name != NULL && value != NULL) {
  436. rspamd_http_message_add_header (msg, name, value);
  437. }
  438. }
  439. lua_pop (L, 2);
  440. }
  441. }
  442. /***
  443. * @function rspamd_http.request({params...})
  444. * This function creates HTTP request and accepts several parameters as a table using key=value syntax.
  445. * Required params are:
  446. *
  447. * - `url`
  448. * - `task`
  449. *
  450. * In taskless mode, instead of `task` required are:
  451. *
  452. * - `ev_base`
  453. * - `config`
  454. *
  455. * @param {string} url specifies URL for a request in the standard URI form (e.g. 'http://example.com/path')
  456. * @param {function} callback specifies callback function in format `function (err_message, code, body, headers)` that is called on HTTP request completion. if this parameter is missing, the function performs "pseudo-synchronous" call (see [Synchronous and Asynchronous API overview](/doc/lua/sync_async.html#API-example-http-module)
  457. * @param {task} task if called from symbol handler it is generally a good idea to use the common task objects: event base, DNS resolver and events session
  458. * @param {table} headers optional headers in form `[name='value', name='value']`
  459. * @param {string} mime_type MIME type of the HTTP content (for example, `text/html`)
  460. * @param {string/text} body full body content, can be opaque `rspamd{text}` to avoid data copying
  461. * @param {number} timeout floating point request timeout value in seconds (default is 5.0 seconds)
  462. * @param {resolver} resolver to perform DNS-requests. Usually got from either `task` or `config`
  463. * @param {boolean} gzip if true, body of the requests will be compressed
  464. * @param {boolean} no_ssl_verify disable SSL peer checks
  465. * @param {boolean} keepalive enable keep-alive pool
  466. * @param {string} user for HTTP authentication
  467. * @param {string} password for HTTP authentication, only if "user" present
  468. * @return {boolean} `true`, in **async** mode, if a request has been successfully scheduled. If this value is `false` then some error occurred, the callback thus will not be called.
  469. * @return In **sync** mode `string|nil, nil|table` In sync mode error message if any and response as table: `int` _code_, `string` _content_ and `table` _headers_ (header -> value)
  470. */
  471. static gint
  472. lua_http_request (lua_State *L)
  473. {
  474. LUA_TRACE_POINT;
  475. struct event_base *ev_base;
  476. struct rspamd_http_message *msg;
  477. struct lua_http_cbdata *cbd;
  478. struct rspamd_dns_resolver *resolver;
  479. struct rspamd_async_session *session = NULL;
  480. struct rspamd_lua_text *t;
  481. struct rspamd_task *task = NULL;
  482. struct rspamd_config *cfg = NULL;
  483. struct rspamd_cryptobox_pubkey *peer_key = NULL;
  484. struct rspamd_cryptobox_keypair *local_kp = NULL;
  485. const gchar *url, *lua_body;
  486. rspamd_fstring_t *body = NULL;
  487. gint cbref = -1;
  488. gsize bodylen;
  489. gdouble timeout = default_http_timeout;
  490. gint flags = 0;
  491. gchar *mime_type = NULL;
  492. gchar *auth = NULL;
  493. gsize max_size = 0;
  494. gboolean gzip = FALSE;
  495. if (lua_gettop (L) >= 2) {
  496. /* url, callback and event_base format */
  497. url = luaL_checkstring (L, 1);
  498. if (url == NULL || lua_type (L, 2) != LUA_TFUNCTION) {
  499. msg_err ("http request has bad params");
  500. lua_pushboolean (L, FALSE);
  501. return 1;
  502. }
  503. lua_pushvalue (L, 2);
  504. cbref = luaL_ref (L, LUA_REGISTRYINDEX);
  505. if (lua_gettop (L) >= 3 && rspamd_lua_check_udata_maybe (L, 3, "rspamd{ev_base}")) {
  506. ev_base = *(struct event_base **)lua_touserdata (L, 3);
  507. }
  508. else {
  509. ev_base = NULL;
  510. }
  511. if (lua_gettop (L) >= 4 && rspamd_lua_check_udata_maybe (L, 4, "rspamd{resolver}")) {
  512. resolver = *(struct rspamd_dns_resolver **)lua_touserdata (L, 4);
  513. }
  514. else {
  515. resolver = lua_http_global_resolver (ev_base);
  516. }
  517. if (lua_gettop (L) >= 5 && rspamd_lua_check_udata_maybe (L, 5, "rspamd{session}")) {
  518. session = *(struct rspamd_async_session **)lua_touserdata (L, 5);
  519. }
  520. else {
  521. session = NULL;
  522. }
  523. msg = rspamd_http_message_from_url (url);
  524. if (msg == NULL) {
  525. lua_pushboolean (L, FALSE);
  526. return 1;
  527. }
  528. }
  529. else if (lua_type (L, 1) == LUA_TTABLE) {
  530. lua_pushstring (L, "url");
  531. lua_gettable (L, 1);
  532. url = luaL_checkstring (L, -1);
  533. lua_pop (L, 1);
  534. lua_pushstring (L, "callback");
  535. lua_gettable (L, 1);
  536. if (url == NULL || lua_type (L, -1) != LUA_TFUNCTION) {
  537. lua_pop (L, 1);
  538. } else {
  539. cbref = luaL_ref (L, LUA_REGISTRYINDEX);
  540. }
  541. lua_pushstring (L, "task");
  542. lua_gettable (L, 1);
  543. if (lua_type (L, -1) == LUA_TUSERDATA) {
  544. task = lua_check_task (L, -1);
  545. ev_base = task->ev_base;
  546. resolver = task->resolver;
  547. session = task->s;
  548. cfg = task->cfg;
  549. }
  550. lua_pop (L, 1);
  551. if (task == NULL) {
  552. lua_pushstring (L, "ev_base");
  553. lua_gettable (L, 1);
  554. if (rspamd_lua_check_udata_maybe (L, -1, "rspamd{ev_base}")) {
  555. ev_base = *(struct event_base **)lua_touserdata (L, -1);
  556. }
  557. else {
  558. ev_base = NULL;
  559. }
  560. lua_pop (L, 1);
  561. lua_pushstring (L, "session");
  562. lua_gettable (L, 1);
  563. if (rspamd_lua_check_udata_maybe (L, -1, "rspamd{session}")) {
  564. session = *(struct rspamd_async_session **)lua_touserdata (L, -1);
  565. }
  566. else {
  567. session = NULL;
  568. }
  569. lua_pop (L, 1);
  570. lua_pushstring (L, "config");
  571. lua_gettable (L, 1);
  572. if (rspamd_lua_check_udata_maybe (L, -1, "rspamd{config}")) {
  573. cfg = *(struct rspamd_config **)lua_touserdata (L, -1);
  574. }
  575. else {
  576. cfg = NULL;
  577. }
  578. lua_pop (L, 1);
  579. lua_pushstring (L, "resolver");
  580. lua_gettable (L, 1);
  581. if (rspamd_lua_check_udata_maybe (L, -1, "rspamd{resolver}")) {
  582. resolver = *(struct rspamd_dns_resolver **)lua_touserdata (L, -1);
  583. }
  584. else {
  585. if (cfg && cfg->dns_resolver) {
  586. resolver = cfg->dns_resolver;
  587. }
  588. else {
  589. resolver = lua_http_global_resolver (ev_base);
  590. }
  591. }
  592. lua_pop (L, 1);
  593. }
  594. msg = rspamd_http_message_from_url (url);
  595. if (msg == NULL) {
  596. msg_err ("cannot create HTTP message from url %s", url);
  597. lua_pushboolean (L, FALSE);
  598. return 1;
  599. }
  600. lua_pushstring (L, "headers");
  601. lua_gettable (L, 1);
  602. if (lua_type (L, -1) == LUA_TTABLE) {
  603. lua_http_push_headers (L, msg);
  604. }
  605. lua_pop (L, 1);
  606. lua_pushstring (L, "timeout");
  607. lua_gettable (L, 1);
  608. if (lua_type (L, -1) == LUA_TNUMBER) {
  609. timeout = lua_tonumber (L, -1) * 1000.;
  610. }
  611. lua_pop (L, 1);
  612. lua_pushstring (L, "mime_type");
  613. lua_gettable (L, 1);
  614. if (lua_type (L, -1) == LUA_TSTRING) {
  615. mime_type = g_strdup (lua_tostring (L, -1));
  616. }
  617. lua_pop (L, 1);
  618. lua_pushstring (L, "body");
  619. lua_gettable (L, 1);
  620. if (lua_type (L, -1) == LUA_TSTRING) {
  621. lua_body = lua_tolstring (L, -1, &bodylen);
  622. body = rspamd_fstring_new_init (lua_body, bodylen);
  623. }
  624. else if (lua_type (L, -1) == LUA_TUSERDATA) {
  625. t = lua_check_text (L, -1);
  626. /* TODO: think about zero-copy possibilities */
  627. if (t) {
  628. body = rspamd_fstring_new_init (t->start, t->len);
  629. }
  630. else {
  631. rspamd_http_message_unref (msg);
  632. g_free (mime_type);
  633. return luaL_error (L, "invalid body argument type: %s",
  634. lua_typename (L, lua_type (L, -1)));
  635. }
  636. }
  637. else if (lua_type (L, -1) == LUA_TTABLE) {
  638. body = rspamd_fstring_new ();
  639. for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 1)) {
  640. if (lua_type (L, -1) == LUA_TSTRING) {
  641. lua_body = lua_tolstring (L, -1, &bodylen);
  642. body = rspamd_fstring_append (body, lua_body, bodylen);
  643. }
  644. else if (lua_type (L, -1) == LUA_TUSERDATA) {
  645. t = lua_check_text (L, -1);
  646. if (t) {
  647. body = rspamd_fstring_append (body, t->start, t->len);
  648. }
  649. else {
  650. rspamd_http_message_unref (msg);
  651. if (body) {
  652. rspamd_fstring_free (body);
  653. }
  654. if (mime_type) {
  655. g_free (mime_type);
  656. }
  657. return luaL_error (L, "invalid body argument: %s",
  658. lua_typename (L, lua_type (L, -1)));
  659. }
  660. }
  661. else {
  662. rspamd_http_message_unref (msg);
  663. if (body) {
  664. rspamd_fstring_free (body);
  665. }
  666. return luaL_error (L, "invalid body argument type: %s",
  667. lua_typename (L, lua_type (L, -1)));
  668. }
  669. }
  670. }
  671. else if (lua_type (L, -1) != LUA_TNONE && lua_type (L, -1) != LUA_TNIL) {
  672. rspamd_http_message_unref (msg);
  673. return luaL_error (L, "invalid body argument type: %s",
  674. lua_typename (L, lua_type (L, -1)));
  675. }
  676. lua_pop (L, 1);
  677. lua_pushstring (L, "peer_key");
  678. lua_gettable (L, 1);
  679. if (lua_type (L, -1) == LUA_TSTRING) {
  680. const gchar *in;
  681. gsize inlen;
  682. in = lua_tolstring (L, -1, &inlen);
  683. peer_key = rspamd_pubkey_from_base32 (in, inlen,
  684. RSPAMD_KEYPAIR_KEX, RSPAMD_CRYPTOBOX_MODE_25519);
  685. }
  686. lua_pop (L, 1);
  687. lua_pushstring (L, "keypair");
  688. lua_gettable (L, 1);
  689. if (lua_type (L, -1) == LUA_TTABLE) {
  690. ucl_object_t *kp_ucl = ucl_object_lua_import (L, -1);
  691. local_kp = rspamd_keypair_from_ucl (kp_ucl);
  692. ucl_object_unref (kp_ucl);
  693. }
  694. lua_pop (L, 1);
  695. lua_pushstring (L, "opaque_body");
  696. lua_gettable (L, 1);
  697. if (!!lua_toboolean (L, -1)) {
  698. flags |= RSPAMD_LUA_HTTP_FLAG_TEXT;
  699. }
  700. lua_pop (L, 1);
  701. lua_pushstring (L, "gzip");
  702. lua_gettable (L, 1);
  703. if (!!lua_toboolean (L, -1)) {
  704. gzip = TRUE;
  705. }
  706. lua_pop (L, 1);
  707. lua_pushstring (L, "no_ssl_verify");
  708. lua_gettable (L, 1);
  709. if (!!lua_toboolean (L, -1)) {
  710. flags |= RSPAMD_LUA_HTTP_FLAG_NOVERIFY;
  711. }
  712. lua_pop (L, 1);
  713. lua_pushstring (L, "keepalive");
  714. lua_gettable (L, 1);
  715. if (!!lua_toboolean (L, -1)) {
  716. flags |= RSPAMD_LUA_HTTP_FLAG_KEEP_ALIVE;
  717. }
  718. lua_pop (L, 1);
  719. lua_pushstring (L, "max_size");
  720. lua_gettable (L, 1);
  721. if (lua_type (L, -1) == LUA_TNUMBER) {
  722. max_size = lua_tonumber (L, -1);
  723. }
  724. lua_pop (L, 1);
  725. lua_pushstring (L, "method");
  726. lua_gettable (L, 1);
  727. if (lua_type (L, -1) == LUA_TSTRING) {
  728. rspamd_http_message_set_method (msg, lua_tostring (L, -1));
  729. }
  730. lua_pop (L, 1);
  731. lua_pushstring (L, "user");
  732. lua_gettable (L, 1);
  733. if (lua_type (L, -1) == LUA_TSTRING) {
  734. const gchar *user = lua_tostring (L, -1);
  735. lua_pushstring (L, "password");
  736. lua_gettable (L, 1);
  737. if (lua_type (L, -1) == LUA_TSTRING) {
  738. const gchar *password = lua_tostring (L, -1);
  739. gchar *tmpbuf;
  740. gsize tlen;
  741. tlen = strlen (user) + strlen (password) + 1;
  742. tmpbuf = g_malloc (tlen + 1);
  743. rspamd_snprintf (tmpbuf, tlen + 1, "%s:%s", user, password);
  744. tlen *= 2;
  745. tlen += sizeof ("Basic ") - 1;
  746. auth = g_malloc (tlen + 1);
  747. rspamd_snprintf (auth, tlen + 1, "Basic %Bs", tmpbuf);
  748. g_free (tmpbuf);
  749. }
  750. else {
  751. msg_warn ("HTTP user must have password, disabling auth");
  752. }
  753. lua_pop (L, 1); /* password */
  754. }
  755. lua_pop (L, 1); /* username */
  756. }
  757. else {
  758. msg_err ("http request has bad params");
  759. lua_pushboolean (L, FALSE);
  760. return 1;
  761. }
  762. if (session && rspamd_session_blocked (session)) {
  763. lua_pushboolean (L, FALSE);
  764. g_free (auth);
  765. rspamd_http_message_unref (msg);
  766. if (body) {
  767. rspamd_fstring_free (body);
  768. }
  769. return 1;
  770. }
  771. if (task == NULL && cfg == NULL) {
  772. g_free (auth);
  773. rspamd_http_message_unref (msg);
  774. if (body) {
  775. rspamd_fstring_free (body);
  776. }
  777. return luaL_error (L,
  778. "Bad params to rspamd_http:request(): either task or config should be set");
  779. }
  780. if (ev_base == NULL) {
  781. return luaL_error (L,
  782. "Bad params to rspamd_http:request(): ev_base isn't passed");
  783. }
  784. cbd = g_malloc0 (sizeof (*cbd));
  785. cbd->cbref = cbref;
  786. cbd->msg = msg;
  787. cbd->ev_base = ev_base;
  788. cbd->mime_type = mime_type;
  789. msec_to_tv (timeout, &cbd->tv);
  790. cbd->fd = -1;
  791. cbd->cfg = cfg;
  792. cbd->peer_pk = peer_key;
  793. cbd->local_kp = local_kp;
  794. cbd->flags = flags;
  795. cbd->max_size = max_size;
  796. cbd->url = url;
  797. cbd->auth = auth;
  798. cbd->task = task;
  799. if (cbd->cbref == -1) {
  800. cbd->thread = lua_thread_pool_get_running_entry (cfg->lua_thread_pool);
  801. }
  802. REF_INIT_RETAIN (cbd, lua_http_cbd_dtor);
  803. if (task) {
  804. cbd->item = rspamd_symcache_get_cur_item (task);
  805. }
  806. if (msg->host) {
  807. cbd->host = msg->host->str;
  808. }
  809. if (body) {
  810. if (gzip) {
  811. if (rspamd_fstring_gzip (&body)) {
  812. rspamd_http_message_add_header (msg, "Content-Encoding", "gzip");
  813. }
  814. }
  815. rspamd_http_message_set_body_from_fstring_steal (msg, body);
  816. }
  817. if (session) {
  818. cbd->session = session;
  819. }
  820. if (rspamd_parse_inet_address (&cbd->addr, msg->host->str, msg->host->len)) {
  821. /* Host is numeric IP, no need to resolve */
  822. gboolean ret;
  823. REF_RETAIN (cbd);
  824. ret = lua_http_make_connection (cbd);
  825. if (!ret) {
  826. if (cbd->ref.refcount > 1) {
  827. /* Not released by make_connection */
  828. REF_RELEASE (cbd);
  829. }
  830. REF_RELEASE (cbd);
  831. lua_pushboolean (L, FALSE);
  832. return 1;
  833. }
  834. REF_RELEASE (cbd);
  835. }
  836. else {
  837. if (!cbd->host) {
  838. REF_RELEASE (cbd);
  839. return luaL_error (L, "no host has been specified");
  840. }
  841. if (task == NULL) {
  842. REF_RETAIN (cbd);
  843. if (!rspamd_dns_resolver_request (resolver, session, NULL, lua_http_dns_handler, cbd,
  844. RDNS_REQUEST_A,
  845. cbd->host)) {
  846. if (cbd->ref.refcount > 1) {
  847. /* Not released by make_connection */
  848. REF_RELEASE (cbd);
  849. }
  850. REF_RELEASE (cbd);
  851. lua_pushboolean (L, FALSE);
  852. return 1;
  853. }
  854. REF_RELEASE (cbd);
  855. }
  856. else {
  857. REF_RETAIN (cbd);
  858. if (!rspamd_dns_resolver_request_task_forced (task, lua_http_dns_handler, cbd,
  859. RDNS_REQUEST_A, cbd->host)) {
  860. if (cbd->ref.refcount > 1) {
  861. /* Not released by make_connection */
  862. REF_RELEASE (cbd);
  863. }
  864. REF_RELEASE (cbd);
  865. lua_pushboolean (L, FALSE);
  866. return 1;
  867. }
  868. else if (cbd->item) {
  869. rspamd_symcache_item_async_inc (cbd->task, cbd->item, M);
  870. }
  871. REF_RELEASE (cbd);
  872. }
  873. }
  874. if (cbd->cbref == -1) {
  875. cbd->thread = lua_thread_pool_get_running_entry (cfg->lua_thread_pool);
  876. cbd->flags |= RSPAMD_LUA_HTTP_FLAG_YIELDED;
  877. return lua_thread_yield (cbd->thread, 0);
  878. }
  879. else {
  880. lua_pushboolean (L, TRUE);
  881. }
  882. return 1;
  883. }
  884. static gint
  885. lua_load_http (lua_State * L)
  886. {
  887. lua_newtable (L);
  888. luaL_register (L, NULL, httplib_m);
  889. return 1;
  890. }
  891. void
  892. luaopen_http (lua_State * L)
  893. {
  894. rspamd_lua_add_preload (L, "rspamd_http", lua_load_http);
  895. }