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

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