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 24KB

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