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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125
  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 "libserver/http/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 ev_loop *event_loop;
  62. struct rspamd_config *cfg;
  63. struct rspamd_task *task;
  64. ev_tstamp timeout;
  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 gdouble default_http_timeout = 5.0;
  80. static struct rspamd_dns_resolver *
  81. lua_http_global_resolver (struct ev_loop *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;
  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. kh_foreach_value (msg->headers, h, {
  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;
  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. kh_foreach_value (msg->headers, h, {
  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->task) {
  370. cbd->conn->log_tag = cbd->task->task_pool->tag.uid;
  371. }
  372. else if (cbd->cfg) {
  373. cbd->conn->log_tag = cbd->cfg->cfg_pool->tag.uid;
  374. }
  375. if (cbd->item) {
  376. rspamd_symcache_item_async_inc (cbd->task, cbd->item, M);
  377. }
  378. struct rspamd_http_message *msg = cbd->msg;
  379. /* Message is now owned by a connection object */
  380. cbd->msg = NULL;
  381. return rspamd_http_connection_write_message (cbd->conn, msg,
  382. cbd->host, cbd->mime_type, cbd,
  383. cbd->timeout);
  384. }
  385. return FALSE;
  386. }
  387. static void
  388. lua_http_dns_handler (struct rdns_reply *reply, gpointer ud)
  389. {
  390. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *)ud;
  391. struct rspamd_symcache_item *item;
  392. struct rspamd_task *task;
  393. task = cbd->task;
  394. item = cbd->item;
  395. if (reply->code != RDNS_RC_NOERROR) {
  396. lua_http_push_error (cbd, "unable to resolve host");
  397. REF_RELEASE (cbd);
  398. }
  399. else {
  400. if (reply->entries->type == RDNS_REQUEST_A) {
  401. cbd->addr = rspamd_inet_address_new (AF_INET,
  402. &reply->entries->content.a.addr);
  403. }
  404. else if (reply->entries->type == RDNS_REQUEST_AAAA) {
  405. cbd->addr = rspamd_inet_address_new (AF_INET6,
  406. &reply->entries->content.aaa.addr);
  407. }
  408. REF_RETAIN (cbd);
  409. if (!lua_http_make_connection (cbd)) {
  410. lua_http_push_error (cbd, "unable to make connection to the host");
  411. if (cbd->ref.refcount > 1) {
  412. REF_RELEASE (cbd);
  413. }
  414. REF_RELEASE (cbd);
  415. return;
  416. }
  417. REF_RELEASE (cbd);
  418. }
  419. if (item) {
  420. rspamd_symcache_item_async_dec_check (task, item, M);
  421. }
  422. }
  423. static void
  424. lua_http_push_headers (lua_State *L, struct rspamd_http_message *msg)
  425. {
  426. const char *name, *value;
  427. gint i, sz;
  428. lua_pushnil (L);
  429. while (lua_next (L, -2) != 0) {
  430. lua_pushvalue (L, -2);
  431. name = lua_tostring (L, -1);
  432. sz = rspamd_lua_table_size (L, -2);
  433. if (sz != 0 && name != NULL) {
  434. for (i = 1; i <= sz ; i++) {
  435. lua_rawgeti (L, -2, i);
  436. value = lua_tostring (L, -1);
  437. if (value != NULL) {
  438. rspamd_http_message_add_header (msg, name, value);
  439. }
  440. lua_pop (L, 1);
  441. }
  442. } else {
  443. value = lua_tostring (L, -2);
  444. if (name != NULL && value != NULL) {
  445. rspamd_http_message_add_header (msg, name, value);
  446. }
  447. }
  448. lua_pop (L, 2);
  449. }
  450. }
  451. /***
  452. * @function rspamd_http.request({params...})
  453. * This function creates HTTP request and accepts several parameters as a table using key=value syntax.
  454. * Required params are:
  455. *
  456. * - `url`
  457. * - `task`
  458. *
  459. * In taskless mode, instead of `task` required are:
  460. *
  461. * - `ev_base`
  462. * - `config`
  463. *
  464. * @param {string} url specifies URL for a request in the standard URI form (e.g. 'http://example.com/path')
  465. * @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)
  466. * @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
  467. * @param {table} headers optional headers in form `[name='value', name='value']`
  468. * @param {string} mime_type MIME type of the HTTP content (for example, `text/html`)
  469. * @param {string/text} body full body content, can be opaque `rspamd{text}` to avoid data copying
  470. * @param {number} timeout floating point request timeout value in seconds (default is 5.0 seconds)
  471. * @param {resolver} resolver to perform DNS-requests. Usually got from either `task` or `config`
  472. * @param {boolean} gzip if true, body of the requests will be compressed
  473. * @param {boolean} no_ssl_verify disable SSL peer checks
  474. * @param {boolean} keepalive enable keep-alive pool
  475. * @param {string} user for HTTP authentication
  476. * @param {string} password for HTTP authentication, only if "user" present
  477. * @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.
  478. * @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)
  479. */
  480. static gint
  481. lua_http_request (lua_State *L)
  482. {
  483. LUA_TRACE_POINT;
  484. struct ev_loop *ev_base;
  485. struct rspamd_http_message *msg;
  486. struct lua_http_cbdata *cbd;
  487. struct rspamd_dns_resolver *resolver;
  488. struct rspamd_async_session *session = NULL;
  489. struct rspamd_lua_text *t;
  490. struct rspamd_task *task = NULL;
  491. struct rspamd_config *cfg = NULL;
  492. struct rspamd_cryptobox_pubkey *peer_key = NULL;
  493. struct rspamd_cryptobox_keypair *local_kp = NULL;
  494. const gchar *url, *lua_body;
  495. rspamd_fstring_t *body = NULL;
  496. gint cbref = -1;
  497. gsize bodylen;
  498. gdouble timeout = default_http_timeout;
  499. gint flags = 0;
  500. gchar *mime_type = NULL;
  501. gchar *auth = NULL;
  502. gsize max_size = 0;
  503. gboolean gzip = FALSE;
  504. if (lua_gettop (L) >= 2) {
  505. /* url, callback and event_base format */
  506. url = luaL_checkstring (L, 1);
  507. if (url == NULL || lua_type (L, 2) != LUA_TFUNCTION) {
  508. msg_err ("http request has bad params");
  509. lua_pushboolean (L, FALSE);
  510. return 1;
  511. }
  512. lua_pushvalue (L, 2);
  513. cbref = luaL_ref (L, LUA_REGISTRYINDEX);
  514. if (lua_gettop (L) >= 3 && rspamd_lua_check_udata_maybe (L, 3, "rspamd{ev_base}")) {
  515. ev_base = *(struct ev_loop **)lua_touserdata (L, 3);
  516. }
  517. else {
  518. ev_base = NULL;
  519. }
  520. if (lua_gettop (L) >= 4 && rspamd_lua_check_udata_maybe (L, 4, "rspamd{resolver}")) {
  521. resolver = *(struct rspamd_dns_resolver **)lua_touserdata (L, 4);
  522. }
  523. else {
  524. resolver = lua_http_global_resolver (ev_base);
  525. }
  526. if (lua_gettop (L) >= 5 && rspamd_lua_check_udata_maybe (L, 5, "rspamd{session}")) {
  527. session = *(struct rspamd_async_session **)lua_touserdata (L, 5);
  528. }
  529. else {
  530. session = NULL;
  531. }
  532. msg = rspamd_http_message_from_url (url);
  533. if (msg == NULL) {
  534. lua_pushboolean (L, FALSE);
  535. return 1;
  536. }
  537. }
  538. else if (lua_type (L, 1) == LUA_TTABLE) {
  539. lua_pushstring (L, "url");
  540. lua_gettable (L, 1);
  541. url = luaL_checkstring (L, -1);
  542. lua_pop (L, 1);
  543. lua_pushstring (L, "callback");
  544. lua_gettable (L, 1);
  545. if (url == NULL || lua_type (L, -1) != LUA_TFUNCTION) {
  546. lua_pop (L, 1);
  547. } else {
  548. cbref = luaL_ref (L, LUA_REGISTRYINDEX);
  549. }
  550. lua_pushstring (L, "task");
  551. lua_gettable (L, 1);
  552. if (lua_type (L, -1) == LUA_TUSERDATA) {
  553. task = lua_check_task (L, -1);
  554. ev_base = task->event_loop;
  555. resolver = task->resolver;
  556. session = task->s;
  557. cfg = task->cfg;
  558. }
  559. lua_pop (L, 1);
  560. if (task == NULL) {
  561. lua_pushstring (L, "ev_base");
  562. lua_gettable (L, 1);
  563. if (rspamd_lua_check_udata_maybe (L, -1, "rspamd{ev_base}")) {
  564. ev_base = *(struct ev_loop **)lua_touserdata (L, -1);
  565. }
  566. else {
  567. ev_base = NULL;
  568. }
  569. lua_pop (L, 1);
  570. lua_pushstring (L, "session");
  571. lua_gettable (L, 1);
  572. if (rspamd_lua_check_udata_maybe (L, -1, "rspamd{session}")) {
  573. session = *(struct rspamd_async_session **)lua_touserdata (L, -1);
  574. }
  575. else {
  576. session = NULL;
  577. }
  578. lua_pop (L, 1);
  579. lua_pushstring (L, "config");
  580. lua_gettable (L, 1);
  581. if (rspamd_lua_check_udata_maybe (L, -1, "rspamd{config}")) {
  582. cfg = *(struct rspamd_config **)lua_touserdata (L, -1);
  583. }
  584. else {
  585. cfg = NULL;
  586. }
  587. lua_pop (L, 1);
  588. lua_pushstring (L, "resolver");
  589. lua_gettable (L, 1);
  590. if (rspamd_lua_check_udata_maybe (L, -1, "rspamd{resolver}")) {
  591. resolver = *(struct rspamd_dns_resolver **)lua_touserdata (L, -1);
  592. }
  593. else {
  594. if (cfg && cfg->dns_resolver) {
  595. resolver = cfg->dns_resolver;
  596. }
  597. else {
  598. resolver = lua_http_global_resolver (ev_base);
  599. }
  600. }
  601. lua_pop (L, 1);
  602. }
  603. msg = rspamd_http_message_from_url (url);
  604. if (msg == NULL) {
  605. msg_err ("cannot create HTTP message from url %s", url);
  606. lua_pushboolean (L, FALSE);
  607. return 1;
  608. }
  609. lua_pushstring (L, "headers");
  610. lua_gettable (L, 1);
  611. if (lua_type (L, -1) == LUA_TTABLE) {
  612. lua_http_push_headers (L, msg);
  613. }
  614. lua_pop (L, 1);
  615. lua_pushstring (L, "timeout");
  616. lua_gettable (L, 1);
  617. if (lua_type (L, -1) == LUA_TNUMBER) {
  618. timeout = lua_tonumber (L, -1);
  619. }
  620. lua_pop (L, 1);
  621. lua_pushstring (L, "mime_type");
  622. lua_gettable (L, 1);
  623. if (lua_type (L, -1) == LUA_TSTRING) {
  624. mime_type = g_strdup (lua_tostring (L, -1));
  625. }
  626. lua_pop (L, 1);
  627. lua_pushstring (L, "body");
  628. lua_gettable (L, 1);
  629. if (lua_type (L, -1) == LUA_TSTRING) {
  630. lua_body = lua_tolstring (L, -1, &bodylen);
  631. body = rspamd_fstring_new_init (lua_body, bodylen);
  632. }
  633. else if (lua_type (L, -1) == LUA_TUSERDATA) {
  634. t = lua_check_text (L, -1);
  635. /* TODO: think about zero-copy possibilities */
  636. if (t) {
  637. body = rspamd_fstring_new_init (t->start, t->len);
  638. }
  639. else {
  640. rspamd_http_message_unref (msg);
  641. g_free (mime_type);
  642. return luaL_error (L, "invalid body argument type: %s",
  643. lua_typename (L, lua_type (L, -1)));
  644. }
  645. }
  646. else if (lua_type (L, -1) == LUA_TTABLE) {
  647. gsize total_len = 0, nelts = rspamd_lua_table_size (L, -1);
  648. /* Calculate length and check types */
  649. for (gsize i = 0; i < nelts; i ++) {
  650. lua_rawgeti (L, -1, i + 1);
  651. if (lua_type (L, -1) == LUA_TSTRING) {
  652. #if LUA_VERSION_NUM >= 502
  653. total_len += lua_rawlen (L, -1);
  654. #else
  655. total_len += lua_objlen (L, -1);
  656. #endif
  657. }
  658. else if (lua_type (L, -1) == LUA_TUSERDATA) {
  659. t = lua_check_text (L, -1);
  660. if (t) {
  661. total_len += t->len;
  662. }
  663. else {
  664. rspamd_http_message_unref (msg);
  665. if (mime_type) {
  666. g_free (mime_type);
  667. }
  668. return luaL_error (L, "invalid body argument: %s",
  669. lua_typename (L, lua_type (L, -1)));
  670. }
  671. }
  672. else {
  673. rspamd_http_message_unref (msg);
  674. if (mime_type) {
  675. g_free (mime_type);
  676. }
  677. return luaL_error (L, "invalid body argument type: %s",
  678. lua_typename (L, lua_type (L, -1)));
  679. }
  680. lua_pop (L, 1);
  681. }
  682. /* Preallocate body */
  683. if (total_len > 0) {
  684. body = rspamd_fstring_sized_new (total_len);
  685. }
  686. else {
  687. rspamd_http_message_unref (msg);
  688. if (mime_type) {
  689. g_free (mime_type);
  690. }
  691. return luaL_error (L, "empty body specified");
  692. }
  693. /* Fill elements */
  694. for (gsize i = 0; i < nelts; i ++) {
  695. lua_rawgeti (L, -1, i + 1);
  696. if (lua_type (L, -1) == LUA_TSTRING) {
  697. lua_body = lua_tolstring (L, -1, &bodylen);
  698. body = rspamd_fstring_append (body, lua_body, bodylen);
  699. }
  700. else {
  701. t = lua_check_text (L, -1);
  702. body = rspamd_fstring_append (body, t->start, t->len);
  703. }
  704. lua_pop (L, 1);
  705. }
  706. }
  707. else if (lua_type (L, -1) != LUA_TNONE && lua_type (L, -1) != LUA_TNIL) {
  708. rspamd_http_message_unref (msg);
  709. return luaL_error (L, "invalid body argument type: %s",
  710. lua_typename (L, lua_type (L, -1)));
  711. }
  712. lua_pop (L, 1);
  713. lua_pushstring (L, "peer_key");
  714. lua_gettable (L, 1);
  715. if (lua_type (L, -1) == LUA_TSTRING) {
  716. const gchar *in;
  717. gsize inlen;
  718. in = lua_tolstring (L, -1, &inlen);
  719. peer_key = rspamd_pubkey_from_base32 (in, inlen,
  720. RSPAMD_KEYPAIR_KEX, RSPAMD_CRYPTOBOX_MODE_25519);
  721. }
  722. lua_pop (L, 1);
  723. lua_pushstring (L, "keypair");
  724. lua_gettable (L, 1);
  725. if (lua_type (L, -1) == LUA_TTABLE) {
  726. ucl_object_t *kp_ucl = ucl_object_lua_import (L, -1);
  727. local_kp = rspamd_keypair_from_ucl (kp_ucl);
  728. ucl_object_unref (kp_ucl);
  729. }
  730. lua_pop (L, 1);
  731. lua_pushstring (L, "opaque_body");
  732. lua_gettable (L, 1);
  733. if (!!lua_toboolean (L, -1)) {
  734. flags |= RSPAMD_LUA_HTTP_FLAG_TEXT;
  735. }
  736. lua_pop (L, 1);
  737. lua_pushstring (L, "gzip");
  738. lua_gettable (L, 1);
  739. if (!!lua_toboolean (L, -1)) {
  740. gzip = TRUE;
  741. }
  742. lua_pop (L, 1);
  743. lua_pushstring (L, "no_ssl_verify");
  744. lua_gettable (L, 1);
  745. if (!!lua_toboolean (L, -1)) {
  746. flags |= RSPAMD_LUA_HTTP_FLAG_NOVERIFY;
  747. }
  748. lua_pop (L, 1);
  749. lua_pushstring (L, "keepalive");
  750. lua_gettable (L, 1);
  751. if (!!lua_toboolean (L, -1)) {
  752. flags |= RSPAMD_LUA_HTTP_FLAG_KEEP_ALIVE;
  753. }
  754. lua_pop (L, 1);
  755. lua_pushstring (L, "max_size");
  756. lua_gettable (L, 1);
  757. if (lua_type (L, -1) == LUA_TNUMBER) {
  758. max_size = lua_tointeger (L, -1);
  759. }
  760. lua_pop (L, 1);
  761. lua_pushstring (L, "method");
  762. lua_gettable (L, 1);
  763. if (lua_type (L, -1) == LUA_TSTRING) {
  764. rspamd_http_message_set_method (msg, lua_tostring (L, -1));
  765. }
  766. lua_pop (L, 1);
  767. lua_pushstring (L, "user");
  768. lua_gettable (L, 1);
  769. if (lua_type (L, -1) == LUA_TSTRING) {
  770. const gchar *user = lua_tostring (L, -1);
  771. lua_pushstring (L, "password");
  772. lua_gettable (L, 1);
  773. if (lua_type (L, -1) == LUA_TSTRING) {
  774. const gchar *password = lua_tostring (L, -1);
  775. gchar *tmpbuf;
  776. gsize tlen;
  777. tlen = strlen (user) + strlen (password) + 1;
  778. tmpbuf = g_malloc (tlen + 1);
  779. rspamd_snprintf (tmpbuf, tlen + 1, "%s:%s", user, password);
  780. tlen *= 2;
  781. tlen += sizeof ("Basic ") - 1;
  782. auth = g_malloc (tlen + 1);
  783. rspamd_snprintf (auth, tlen + 1, "Basic %Bs", tmpbuf);
  784. g_free (tmpbuf);
  785. }
  786. else {
  787. msg_warn ("HTTP user must have password, disabling auth");
  788. }
  789. lua_pop (L, 1); /* password */
  790. }
  791. lua_pop (L, 1); /* username */
  792. }
  793. else {
  794. msg_err ("http request has bad params");
  795. lua_pushboolean (L, FALSE);
  796. return 1;
  797. }
  798. if (session && rspamd_session_blocked (session)) {
  799. lua_pushboolean (L, FALSE);
  800. g_free (auth);
  801. rspamd_http_message_unref (msg);
  802. if (body) {
  803. rspamd_fstring_free (body);
  804. }
  805. return 1;
  806. }
  807. if (task == NULL && cfg == NULL) {
  808. g_free (auth);
  809. rspamd_http_message_unref (msg);
  810. if (body) {
  811. rspamd_fstring_free (body);
  812. }
  813. return luaL_error (L,
  814. "Bad params to rspamd_http:request(): either task or config should be set");
  815. }
  816. if (ev_base == NULL) {
  817. return luaL_error (L,
  818. "Bad params to rspamd_http:request(): ev_base isn't passed");
  819. }
  820. cbd = g_malloc0 (sizeof (*cbd));
  821. cbd->cbref = cbref;
  822. cbd->msg = msg;
  823. cbd->event_loop = ev_base;
  824. cbd->mime_type = mime_type;
  825. cbd->timeout = timeout;
  826. cbd->fd = -1;
  827. cbd->cfg = cfg;
  828. cbd->peer_pk = peer_key;
  829. cbd->local_kp = local_kp;
  830. cbd->flags = flags;
  831. cbd->max_size = max_size;
  832. cbd->url = url;
  833. cbd->auth = auth;
  834. cbd->task = task;
  835. if (cbd->cbref == -1) {
  836. cbd->thread = lua_thread_pool_get_running_entry (cfg->lua_thread_pool);
  837. }
  838. REF_INIT_RETAIN (cbd, lua_http_cbd_dtor);
  839. if (task) {
  840. cbd->item = rspamd_symcache_get_cur_item (task);
  841. }
  842. if (msg->host) {
  843. cbd->host = msg->host->str;
  844. }
  845. if (body) {
  846. if (gzip) {
  847. if (rspamd_fstring_gzip (&body)) {
  848. rspamd_http_message_add_header (msg, "Content-Encoding", "gzip");
  849. }
  850. }
  851. rspamd_http_message_set_body_from_fstring_steal (msg, body);
  852. }
  853. if (session) {
  854. cbd->session = session;
  855. }
  856. if (rspamd_parse_inet_address (&cbd->addr,
  857. msg->host->str, msg->host->len, RSPAMD_INET_ADDRESS_PARSE_DEFAULT)) {
  858. /* Host is numeric IP, no need to resolve */
  859. gboolean ret;
  860. REF_RETAIN (cbd);
  861. ret = lua_http_make_connection (cbd);
  862. if (!ret) {
  863. if (cbd->ref.refcount > 1) {
  864. /* Not released by make_connection */
  865. REF_RELEASE (cbd);
  866. }
  867. REF_RELEASE (cbd);
  868. lua_pushboolean (L, FALSE);
  869. return 1;
  870. }
  871. REF_RELEASE (cbd);
  872. }
  873. else {
  874. if (!cbd->host) {
  875. REF_RELEASE (cbd);
  876. return luaL_error (L, "no host has been specified");
  877. }
  878. if (task == NULL) {
  879. REF_RETAIN (cbd);
  880. if (!rspamd_dns_resolver_request (resolver, session, NULL, lua_http_dns_handler, cbd,
  881. RDNS_REQUEST_A,
  882. cbd->host)) {
  883. if (cbd->ref.refcount > 1) {
  884. /* Not released by make_connection */
  885. REF_RELEASE (cbd);
  886. }
  887. REF_RELEASE (cbd);
  888. lua_pushboolean (L, FALSE);
  889. return 1;
  890. }
  891. REF_RELEASE (cbd);
  892. }
  893. else {
  894. REF_RETAIN (cbd);
  895. if (!rspamd_dns_resolver_request_task_forced (task, lua_http_dns_handler, cbd,
  896. RDNS_REQUEST_A, cbd->host)) {
  897. if (cbd->ref.refcount > 1) {
  898. /* Not released by make_connection */
  899. REF_RELEASE (cbd);
  900. }
  901. REF_RELEASE (cbd);
  902. lua_pushboolean (L, FALSE);
  903. return 1;
  904. }
  905. else if (cbd->item) {
  906. rspamd_symcache_item_async_inc (cbd->task, cbd->item, M);
  907. }
  908. REF_RELEASE (cbd);
  909. }
  910. }
  911. if (cbd->cbref == -1) {
  912. cbd->thread = lua_thread_pool_get_running_entry (cfg->lua_thread_pool);
  913. cbd->flags |= RSPAMD_LUA_HTTP_FLAG_YIELDED;
  914. return lua_thread_yield (cbd->thread, 0);
  915. }
  916. else {
  917. lua_pushboolean (L, TRUE);
  918. }
  919. return 1;
  920. }
  921. static gint
  922. lua_load_http (lua_State * L)
  923. {
  924. lua_newtable (L);
  925. luaL_register (L, NULL, httplib_m);
  926. return 1;
  927. }
  928. void
  929. luaopen_http (lua_State * L)
  930. {
  931. rspamd_lua_add_preload (L, "rspamd_http", lua_load_http);
  932. }