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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270
  1. /*
  2. * Copyright 2023 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 "libutil/upstream.h"
  20. #include "ref.h"
  21. #include "unix-std.h"
  22. #include "zlib.h"
  23. #include "utlist.h"
  24. /***
  25. * @module rspamd_http
  26. * Rspamd HTTP module represents HTTP asynchronous client available from LUA code.
  27. * This module hides all complexity: DNS resolving, sessions management, zero-copy
  28. * text transfers and so on under the hood.
  29. * @example
  30. local rspamd_http = require "rspamd_http"
  31. local function symbol_callback(task)
  32. local function http_callback(err_message, code, body, headers)
  33. task:insert_result('SYMBOL', 1) -- task is available via closure
  34. end
  35. rspamd_http.request({
  36. task=task,
  37. url='http://example.com/data',
  38. body=task:get_content(),
  39. callback=http_callback,
  40. headers={Header='Value', OtherHeader='Value'},
  41. mime_type='text/plain',
  42. })
  43. end
  44. */
  45. #define MAX_HEADERS_SIZE 8192
  46. static const char *M = "rspamd lua http";
  47. LUA_FUNCTION_DEF(http, request);
  48. static const struct luaL_reg httplib_m[] = {
  49. LUA_INTERFACE_DEF(http, request),
  50. {"__tostring", rspamd_lua_class_tostring},
  51. {NULL, NULL}};
  52. #define RSPAMD_LUA_HTTP_FLAG_TEXT (1 << 0)
  53. #define RSPAMD_LUA_HTTP_FLAG_NOVERIFY (1 << 1)
  54. #define RSPAMD_LUA_HTTP_FLAG_RESOLVED (1 << 2)
  55. #define RSPAMD_LUA_HTTP_FLAG_KEEP_ALIVE (1 << 3)
  56. #define RSPAMD_LUA_HTTP_FLAG_YIELDED (1 << 4)
  57. struct lua_http_cbdata {
  58. struct rspamd_http_connection *conn;
  59. struct rspamd_async_session *session;
  60. struct rspamd_symcache_dynamic_item *item;
  61. struct rspamd_http_message *msg;
  62. struct ev_loop *event_loop;
  63. struct rspamd_config *cfg;
  64. struct rspamd_task *task;
  65. ev_tstamp timeout;
  66. struct rspamd_cryptobox_keypair *local_kp;
  67. struct rspamd_cryptobox_pubkey *peer_pk;
  68. rspamd_inet_addr_t *addr;
  69. char *mime_type;
  70. char *host;
  71. char *auth;
  72. struct upstream *up;
  73. const char *url;
  74. gsize max_size;
  75. int flags;
  76. int fd;
  77. int cbref;
  78. struct thread_entry *thread;
  79. ref_entry_t ref;
  80. };
  81. static const double default_http_timeout = 5.0;
  82. static struct rspamd_dns_resolver *
  83. lua_http_global_resolver(struct ev_loop *ev_base)
  84. {
  85. static struct rspamd_dns_resolver *global_resolver;
  86. if (global_resolver == NULL) {
  87. global_resolver = rspamd_dns_resolver_init(NULL, ev_base, NULL);
  88. }
  89. return global_resolver;
  90. }
  91. static void
  92. lua_http_fin(gpointer arg)
  93. {
  94. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *) arg;
  95. if (cbd->cbref != -1) {
  96. luaL_unref(cbd->cfg->lua_state, LUA_REGISTRYINDEX, cbd->cbref);
  97. }
  98. if (cbd->conn) {
  99. /* Here we already have a connection, so we need to unref it */
  100. rspamd_http_connection_unref(cbd->conn);
  101. }
  102. else if (cbd->msg != NULL) {
  103. /* We need to free message */
  104. rspamd_http_message_unref(cbd->msg);
  105. }
  106. if (cbd->fd != -1) {
  107. close(cbd->fd);
  108. }
  109. if (cbd->addr) {
  110. rspamd_inet_address_free(cbd->addr);
  111. }
  112. if (cbd->up) {
  113. rspamd_upstream_unref(cbd->up);
  114. }
  115. if (cbd->mime_type) {
  116. g_free(cbd->mime_type);
  117. }
  118. if (cbd->auth) {
  119. g_free(cbd->auth);
  120. }
  121. if (cbd->host) {
  122. g_free(cbd->host);
  123. }
  124. if (cbd->local_kp) {
  125. rspamd_keypair_unref(cbd->local_kp);
  126. }
  127. if (cbd->peer_pk) {
  128. rspamd_pubkey_unref(cbd->peer_pk);
  129. }
  130. g_free(cbd);
  131. }
  132. static void
  133. lua_http_cbd_dtor(struct lua_http_cbdata *cbd)
  134. {
  135. if (cbd->session) {
  136. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_RESOLVED) {
  137. /* Event is added merely for resolved events */
  138. if (cbd->item) {
  139. rspamd_symcache_item_async_dec_check(cbd->task, cbd->item, M);
  140. }
  141. rspamd_session_remove_event(cbd->session, lua_http_fin, cbd);
  142. }
  143. }
  144. else {
  145. lua_http_fin(cbd);
  146. }
  147. }
  148. static void
  149. lua_http_push_error(struct lua_http_cbdata *cbd, const char *err)
  150. {
  151. struct lua_callback_state lcbd;
  152. lua_State *L;
  153. lua_thread_pool_prepare_callback(cbd->cfg->lua_thread_pool, &lcbd);
  154. L = lcbd.L;
  155. lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->cbref);
  156. lua_pushstring(L, err);
  157. if (cbd->item) {
  158. rspamd_symcache_set_cur_item(cbd->task, cbd->item);
  159. }
  160. if (lua_pcall(L, 1, 0, 0) != 0) {
  161. msg_info("callback call failed: %s", lua_tostring(L, -1));
  162. lua_pop(L, 1);
  163. }
  164. lua_thread_pool_restore_callback(&lcbd);
  165. }
  166. static void lua_http_resume_handler(struct rspamd_http_connection *conn,
  167. struct rspamd_http_message *msg, const char *err);
  168. static void
  169. lua_http_error_handler(struct rspamd_http_connection *conn, GError *err)
  170. {
  171. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *) conn->ud;
  172. if (cbd->up) {
  173. rspamd_upstream_fail(cbd->up, false, err ? err->message : "unknown error");
  174. }
  175. if (cbd->cbref == -1) {
  176. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_YIELDED) {
  177. cbd->flags &= ~RSPAMD_LUA_HTTP_FLAG_YIELDED;
  178. lua_http_resume_handler(conn, NULL, err->message);
  179. }
  180. else {
  181. /* TODO: kill me please */
  182. msg_info("lost HTTP error from %s in coroutines mess: %s",
  183. rspamd_inet_address_to_string_pretty(cbd->addr),
  184. err->message);
  185. }
  186. }
  187. else {
  188. lua_http_push_error(cbd, err->message);
  189. }
  190. REF_RELEASE(cbd);
  191. }
  192. static int
  193. lua_http_finish_handler(struct rspamd_http_connection *conn,
  194. struct rspamd_http_message *msg)
  195. {
  196. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *) conn->ud;
  197. struct rspamd_http_header *h;
  198. const char *body;
  199. gsize body_len;
  200. struct lua_callback_state lcbd;
  201. lua_State *L;
  202. if (cbd->cbref == -1) {
  203. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_YIELDED) {
  204. cbd->flags &= ~RSPAMD_LUA_HTTP_FLAG_YIELDED;
  205. lua_http_resume_handler(conn, msg, NULL);
  206. }
  207. else {
  208. /* TODO: kill me please */
  209. msg_err("lost HTTP data from %s in coroutines mess",
  210. rspamd_inet_address_to_string_pretty(cbd->addr));
  211. }
  212. REF_RELEASE(cbd);
  213. return 0;
  214. }
  215. lua_thread_pool_prepare_callback(cbd->cfg->lua_thread_pool, &lcbd);
  216. if (cbd->up) {
  217. rspamd_upstream_ok(cbd->up);
  218. }
  219. L = lcbd.L;
  220. lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->cbref);
  221. /* Error */
  222. lua_pushnil(L);
  223. /* Reply code */
  224. lua_pushinteger(L, msg->code);
  225. /* Body */
  226. body = rspamd_http_message_get_body(msg, &body_len);
  227. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_TEXT) {
  228. struct rspamd_lua_text *t;
  229. t = lua_newuserdata(L, sizeof(*t));
  230. rspamd_lua_setclass(L, rspamd_text_classname, -1);
  231. t->start = body;
  232. t->len = body_len;
  233. t->flags = 0;
  234. }
  235. else {
  236. if (body_len > 0) {
  237. lua_pushlstring(L, body, body_len);
  238. }
  239. else {
  240. lua_pushnil(L);
  241. }
  242. }
  243. /* Headers */
  244. lua_newtable(L);
  245. kh_foreach_value(msg->headers, h, {
  246. /*
  247. * Lowercase header name, as Lua cannot search in caseless matter
  248. */
  249. rspamd_str_lc(h->combined->str, h->name.len);
  250. lua_pushlstring(L, h->name.begin, h->name.len);
  251. lua_pushlstring(L, h->value.begin, h->value.len);
  252. lua_settable(L, -3);
  253. });
  254. if (cbd->item) {
  255. /* Replace watcher to deal with nested calls */
  256. rspamd_symcache_set_cur_item(cbd->task, cbd->item);
  257. }
  258. if (lua_pcall(L, 4, 0, 0) != 0) {
  259. msg_info("callback call failed: %s", lua_tostring(L, -1));
  260. lua_pop(L, 1);
  261. }
  262. REF_RELEASE(cbd);
  263. lua_thread_pool_restore_callback(&lcbd);
  264. return 0;
  265. }
  266. /*
  267. * resumes yielded thread
  268. */
  269. static void
  270. lua_http_resume_handler(struct rspamd_http_connection *conn,
  271. struct rspamd_http_message *msg, const char *err)
  272. {
  273. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *) conn->ud;
  274. lua_State *L = cbd->thread->lua_state;
  275. const char *body;
  276. gsize body_len;
  277. struct rspamd_http_header *h;
  278. if (err) {
  279. lua_pushstring(L, err);
  280. lua_pushnil(L);
  281. }
  282. else {
  283. /*
  284. * 1 - nil (error)
  285. * 2 - table:
  286. * code (int)
  287. * content (string)
  288. * headers (table: header -> value)
  289. */
  290. lua_pushnil(L);// error code
  291. lua_createtable(L, 0, 3);
  292. /* code */
  293. lua_pushliteral(L, "code");
  294. lua_pushinteger(L, msg->code);
  295. lua_settable(L, -3);
  296. /* content */
  297. lua_pushliteral(L, "content");
  298. body = rspamd_http_message_get_body(msg, &body_len);
  299. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_TEXT) {
  300. struct rspamd_lua_text *t;
  301. t = lua_newuserdata(L, sizeof(*t));
  302. rspamd_lua_setclass(L, rspamd_text_classname, -1);
  303. t->start = body;
  304. t->len = body_len;
  305. t->flags = 0;
  306. }
  307. else {
  308. if (body_len > 0) {
  309. lua_pushlstring(L, body, body_len);
  310. }
  311. else {
  312. lua_pushnil(L);
  313. }
  314. }
  315. lua_settable(L, -3);
  316. /* headers */
  317. lua_pushliteral(L, "headers");
  318. lua_newtable(L);
  319. kh_foreach_value(msg->headers, h, {
  320. /*
  321. * Lowercase header name, as Lua cannot search in caseless matter
  322. */
  323. rspamd_str_lc(h->combined->str, h->name.len);
  324. lua_pushlstring(L, h->name.begin, h->name.len);
  325. lua_pushlstring(L, h->value.begin, h->value.len);
  326. lua_settable(L, -3);
  327. });
  328. lua_settable(L, -3);
  329. }
  330. if (cbd->item) {
  331. /* Replace watcher to deal with nested calls */
  332. rspamd_symcache_set_cur_item(cbd->task, cbd->item);
  333. }
  334. lua_thread_resume(cbd->thread, 2);
  335. }
  336. static gboolean
  337. lua_http_make_connection(struct lua_http_cbdata *cbd)
  338. {
  339. rspamd_inet_address_set_port(cbd->addr, cbd->msg->port);
  340. unsigned http_opts = RSPAMD_HTTP_CLIENT_SIMPLE;
  341. if (cbd->msg->flags & RSPAMD_HTTP_FLAG_WANT_SSL) {
  342. http_opts |= RSPAMD_HTTP_CLIENT_SSL;
  343. }
  344. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_KEEP_ALIVE) {
  345. cbd->fd = -1; /* FD is owned by keepalive connection */
  346. cbd->conn = rspamd_http_connection_new_client_keepalive(
  347. NULL, /* Default context */
  348. NULL,
  349. lua_http_error_handler,
  350. lua_http_finish_handler,
  351. http_opts,
  352. cbd->addr,
  353. cbd->host);
  354. }
  355. else {
  356. cbd->fd = -1;
  357. cbd->conn = rspamd_http_connection_new_client(
  358. NULL, /* Default context */
  359. NULL,
  360. lua_http_error_handler,
  361. lua_http_finish_handler,
  362. http_opts,
  363. cbd->addr);
  364. }
  365. if (cbd->conn) {
  366. if (cbd->local_kp) {
  367. rspamd_http_connection_set_key(cbd->conn, cbd->local_kp);
  368. }
  369. if (cbd->peer_pk) {
  370. rspamd_http_message_set_peer_key(cbd->msg, cbd->peer_pk);
  371. }
  372. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_NOVERIFY) {
  373. cbd->msg->flags |= RSPAMD_HTTP_FLAG_SSL_NOVERIFY;
  374. }
  375. if (cbd->max_size) {
  376. rspamd_http_connection_set_max_size(cbd->conn, cbd->max_size);
  377. }
  378. if (cbd->auth) {
  379. rspamd_http_message_add_header(cbd->msg, "Authorization",
  380. cbd->auth);
  381. }
  382. if (cbd->session) {
  383. if (cbd->item) {
  384. rspamd_session_add_event_full(cbd->session,
  385. (event_finalizer_t) lua_http_fin, cbd,
  386. M,
  387. rspamd_symcache_dyn_item_name(cbd->task, cbd->item));
  388. }
  389. else {
  390. rspamd_session_add_event(cbd->session,
  391. (event_finalizer_t) lua_http_fin, cbd,
  392. M);
  393. }
  394. cbd->flags |= RSPAMD_LUA_HTTP_FLAG_RESOLVED;
  395. }
  396. if (cbd->task) {
  397. cbd->conn->log_tag = cbd->task->task_pool->tag.uid;
  398. if (cbd->item) {
  399. rspamd_symcache_item_async_inc(cbd->task, cbd->item, M);
  400. }
  401. }
  402. else if (cbd->cfg) {
  403. cbd->conn->log_tag = cbd->cfg->cfg_pool->tag.uid;
  404. }
  405. struct rspamd_http_message *msg = cbd->msg;
  406. /* Message is now owned by a connection object */
  407. cbd->msg = NULL;
  408. return rspamd_http_connection_write_message(cbd->conn, msg,
  409. cbd->host, cbd->mime_type, cbd,
  410. cbd->timeout);
  411. }
  412. return FALSE;
  413. }
  414. static void
  415. lua_http_dns_handler(struct rdns_reply *reply, gpointer ud)
  416. {
  417. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *) ud;
  418. struct rspamd_symcache_dynamic_item *item;
  419. struct rspamd_task *task;
  420. task = cbd->task;
  421. item = cbd->item;
  422. if (reply->code != RDNS_RC_NOERROR) {
  423. lua_http_push_error(cbd, "unable to resolve host");
  424. REF_RELEASE(cbd);
  425. }
  426. else {
  427. struct rdns_reply_entry *entry;
  428. DL_FOREACH(reply->entries, entry)
  429. {
  430. if (entry->type == RDNS_REQUEST_A) {
  431. cbd->addr = rspamd_inet_address_new(AF_INET,
  432. &entry->content.a.addr);
  433. break;
  434. }
  435. else if (entry->type == RDNS_REQUEST_AAAA) {
  436. cbd->addr = rspamd_inet_address_new(AF_INET6,
  437. &entry->content.aaa.addr);
  438. break;
  439. }
  440. }
  441. if (cbd->addr == NULL) {
  442. lua_http_push_error(cbd, "unable to resolve host: no records with such name");
  443. REF_RELEASE(cbd);
  444. }
  445. else {
  446. REF_RETAIN(cbd);
  447. if (!lua_http_make_connection(cbd)) {
  448. lua_http_push_error(cbd, "unable to make connection to the host");
  449. if (cbd->ref.refcount > 1) {
  450. REF_RELEASE(cbd);
  451. }
  452. REF_RELEASE(cbd);
  453. return;
  454. }
  455. REF_RELEASE(cbd);
  456. }
  457. }
  458. if (item) {
  459. rspamd_symcache_item_async_dec_check(task, item, M);
  460. }
  461. }
  462. static void
  463. lua_http_push_headers(lua_State *L, struct rspamd_http_message *msg)
  464. {
  465. const char *name, *value;
  466. int i, sz;
  467. lua_pushnil(L);
  468. while (lua_next(L, -2) != 0) {
  469. lua_pushvalue(L, -2);
  470. name = lua_tostring(L, -1);
  471. sz = rspamd_lua_table_size(L, -2);
  472. if (sz != 0 && name != NULL) {
  473. for (i = 1; i <= sz; i++) {
  474. lua_rawgeti(L, -2, i);
  475. value = lua_tostring(L, -1);
  476. if (value != NULL) {
  477. rspamd_http_message_add_header(msg, name, value);
  478. }
  479. lua_pop(L, 1);
  480. }
  481. }
  482. else {
  483. value = lua_tostring(L, -2);
  484. if (name != NULL && value != NULL) {
  485. rspamd_http_message_add_header(msg, name, value);
  486. }
  487. }
  488. lua_pop(L, 2);
  489. }
  490. }
  491. /***
  492. * @function rspamd_http.request({params...})
  493. * This function creates HTTP request and accepts several parameters as a table using key=value syntax.
  494. * Required params are:
  495. *
  496. * - `url`
  497. * - `task`
  498. *
  499. * In taskless mode, instead of `task` required are:
  500. *
  501. * - `ev_base`
  502. * - `config`
  503. *
  504. * @param {string} url specifies URL for a request in the standard URI form (e.g. 'http://example.com/path')
  505. * @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)
  506. * @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
  507. * @param {table} headers optional headers in form `[name='value', name='value']`
  508. * @param {string} mime_type MIME type of the HTTP content (for example, `text/html`)
  509. * @param {string/text} body full body content, can be opaque `rspamd{text}` to avoid data copying
  510. * @param {number} timeout floating point request timeout value in seconds (default is 5.0 seconds)
  511. * @param {resolver} resolver to perform DNS-requests. Usually got from either `task` or `config`
  512. * @param {boolean} gzip if true, body of the requests will be compressed
  513. * @param {boolean} no_ssl_verify disable SSL peer checks
  514. * @param {boolean} keepalive enable keep-alive pool
  515. * @param {string} user for HTTP authentication
  516. * @param {string} password for HTTP authentication, only if "user" present
  517. * @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.
  518. * @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)
  519. */
  520. static int
  521. lua_http_request(lua_State *L)
  522. {
  523. LUA_TRACE_POINT;
  524. struct ev_loop *ev_base;
  525. struct rspamd_http_message *msg;
  526. struct lua_http_cbdata *cbd;
  527. struct rspamd_dns_resolver *resolver;
  528. struct rspamd_async_session *session = NULL;
  529. struct rspamd_lua_text *t;
  530. struct rspamd_task *task = NULL;
  531. struct rspamd_config *cfg = NULL;
  532. struct rspamd_cryptobox_pubkey *peer_key = NULL;
  533. struct rspamd_cryptobox_keypair *local_kp = NULL;
  534. struct upstream *up = NULL;
  535. const char *url, *lua_body;
  536. rspamd_fstring_t *body = NULL;
  537. int cbref = -1;
  538. gsize bodylen;
  539. double timeout = default_http_timeout;
  540. int flags = 0;
  541. char *mime_type = NULL;
  542. char *auth = NULL;
  543. gsize max_size = 0;
  544. gboolean gzip = FALSE;
  545. if (lua_gettop(L) >= 2) {
  546. /* url, callback and event_base format */
  547. url = luaL_checkstring(L, 1);
  548. if (url == NULL || lua_type(L, 2) != LUA_TFUNCTION) {
  549. msg_err("http request has bad params");
  550. lua_pushboolean(L, FALSE);
  551. return 1;
  552. }
  553. lua_pushvalue(L, 2);
  554. cbref = luaL_ref(L, LUA_REGISTRYINDEX);
  555. if (lua_gettop(L) >= 3 && rspamd_lua_check_udata_maybe(L, 3, rspamd_ev_base_classname)) {
  556. ev_base = *(struct ev_loop **) lua_touserdata(L, 3);
  557. }
  558. else {
  559. ev_base = NULL;
  560. }
  561. if (lua_gettop(L) >= 4 && rspamd_lua_check_udata_maybe(L, 4, rspamd_resolver_classname)) {
  562. resolver = *(struct rspamd_dns_resolver **) lua_touserdata(L, 4);
  563. }
  564. else {
  565. resolver = lua_http_global_resolver(ev_base);
  566. }
  567. if (lua_gettop(L) >= 5 && rspamd_lua_check_udata_maybe(L, 5, rspamd_session_classname)) {
  568. session = *(struct rspamd_async_session **) lua_touserdata(L, 5);
  569. }
  570. else {
  571. session = NULL;
  572. }
  573. msg = rspamd_http_message_from_url(url);
  574. if (msg == NULL) {
  575. luaL_unref(L, LUA_REGISTRYINDEX, cbref);
  576. msg_err("cannot create HTTP message from url %s", url);
  577. lua_pushboolean(L, FALSE);
  578. return 1;
  579. }
  580. }
  581. else if (lua_type(L, 1) == LUA_TTABLE) {
  582. lua_pushstring(L, "url");
  583. lua_gettable(L, 1);
  584. url = luaL_checkstring(L, -1);
  585. lua_pop(L, 1);
  586. if (url == NULL) {
  587. msg_err("cannot create HTTP message without url");
  588. lua_pushboolean(L, FALSE);
  589. return 1;
  590. }
  591. lua_pushstring(L, "callback");
  592. lua_gettable(L, 1);
  593. if (url == NULL || lua_type(L, -1) != LUA_TFUNCTION) {
  594. lua_pop(L, 1);
  595. }
  596. else {
  597. cbref = luaL_ref(L, LUA_REGISTRYINDEX);
  598. }
  599. lua_pushstring(L, "task");
  600. lua_gettable(L, 1);
  601. if (lua_type(L, -1) == LUA_TUSERDATA) {
  602. task = lua_check_task(L, -1);
  603. if (task) {
  604. ev_base = task->event_loop;
  605. resolver = task->resolver;
  606. session = task->s;
  607. cfg = task->cfg;
  608. }
  609. }
  610. lua_pop(L, 1);
  611. if (task == NULL) {
  612. lua_pushstring(L, "ev_base");
  613. lua_gettable(L, 1);
  614. if (rspamd_lua_check_udata_maybe(L, -1, rspamd_ev_base_classname)) {
  615. ev_base = *(struct ev_loop **) lua_touserdata(L, -1);
  616. }
  617. else {
  618. ev_base = NULL;
  619. }
  620. lua_pop(L, 1);
  621. lua_pushstring(L, "session");
  622. lua_gettable(L, 1);
  623. if (rspamd_lua_check_udata_maybe(L, -1, rspamd_session_classname)) {
  624. session = *(struct rspamd_async_session **) lua_touserdata(L, -1);
  625. }
  626. else {
  627. session = NULL;
  628. }
  629. lua_pop(L, 1);
  630. lua_pushstring(L, "config");
  631. lua_gettable(L, 1);
  632. if (rspamd_lua_check_udata_maybe(L, -1, rspamd_config_classname)) {
  633. cfg = *(struct rspamd_config **) lua_touserdata(L, -1);
  634. }
  635. else {
  636. cfg = NULL;
  637. }
  638. lua_pop(L, 1);
  639. lua_pushstring(L, "resolver");
  640. lua_gettable(L, 1);
  641. if (rspamd_lua_check_udata_maybe(L, -1, rspamd_resolver_classname)) {
  642. resolver = *(struct rspamd_dns_resolver **) lua_touserdata(L, -1);
  643. }
  644. else {
  645. if (cfg && cfg->dns_resolver) {
  646. resolver = cfg->dns_resolver;
  647. }
  648. else {
  649. resolver = lua_http_global_resolver(ev_base);
  650. }
  651. }
  652. lua_pop(L, 1);
  653. }
  654. msg = rspamd_http_message_from_url(url);
  655. if (msg == NULL) {
  656. msg_err_task_check("cannot create HTTP message from url %s", url);
  657. lua_pushboolean(L, FALSE);
  658. return 1;
  659. }
  660. lua_pushstring(L, "headers");
  661. lua_gettable(L, 1);
  662. if (lua_type(L, -1) == LUA_TTABLE) {
  663. lua_http_push_headers(L, msg);
  664. }
  665. lua_pop(L, 1);
  666. lua_pushstring(L, "timeout");
  667. lua_gettable(L, 1);
  668. if (lua_type(L, -1) == LUA_TNUMBER) {
  669. timeout = lua_tonumber(L, -1);
  670. }
  671. lua_pop(L, 1);
  672. lua_pushstring(L, "mime_type");
  673. lua_gettable(L, 1);
  674. if (lua_type(L, -1) == LUA_TSTRING) {
  675. mime_type = g_strdup(lua_tostring(L, -1));
  676. }
  677. lua_pop(L, 1);
  678. lua_pushstring(L, "body");
  679. lua_gettable(L, 1);
  680. if (lua_type(L, -1) == LUA_TSTRING) {
  681. lua_body = lua_tolstring(L, -1, &bodylen);
  682. body = rspamd_fstring_new_init(lua_body, bodylen);
  683. }
  684. else if (lua_type(L, -1) == LUA_TUSERDATA) {
  685. t = lua_check_text(L, -1);
  686. /* TODO: think about zero-copy possibilities */
  687. if (t) {
  688. body = rspamd_fstring_new_init(t->start, t->len);
  689. }
  690. else {
  691. rspamd_http_message_unref(msg);
  692. g_free(mime_type);
  693. return luaL_error(L, "invalid body argument type: %s",
  694. lua_typename(L, lua_type(L, -1)));
  695. }
  696. }
  697. else if (lua_type(L, -1) == LUA_TTABLE) {
  698. gsize total_len = 0, nelts = rspamd_lua_table_size(L, -1);
  699. /* Calculate length and check types */
  700. for (gsize i = 0; i < nelts; i++) {
  701. lua_rawgeti(L, -1, i + 1);
  702. if (lua_type(L, -1) == LUA_TSTRING) {
  703. #if LUA_VERSION_NUM >= 502
  704. total_len += lua_rawlen(L, -1);
  705. #else
  706. total_len += lua_objlen(L, -1);
  707. #endif
  708. }
  709. else if (lua_type(L, -1) == LUA_TUSERDATA) {
  710. t = lua_check_text(L, -1);
  711. if (t) {
  712. total_len += t->len;
  713. }
  714. else {
  715. rspamd_http_message_unref(msg);
  716. if (mime_type) {
  717. g_free(mime_type);
  718. }
  719. return luaL_error(L, "invalid body argument: %s",
  720. lua_typename(L, lua_type(L, -1)));
  721. }
  722. }
  723. else {
  724. rspamd_http_message_unref(msg);
  725. if (mime_type) {
  726. g_free(mime_type);
  727. }
  728. return luaL_error(L, "invalid body argument type: %s",
  729. lua_typename(L, lua_type(L, -1)));
  730. }
  731. lua_pop(L, 1);
  732. }
  733. /* Preallocate body */
  734. if (total_len > 0) {
  735. body = rspamd_fstring_sized_new(total_len);
  736. }
  737. else {
  738. rspamd_http_message_unref(msg);
  739. if (mime_type) {
  740. g_free(mime_type);
  741. }
  742. return luaL_error(L, "empty body specified");
  743. }
  744. /* Fill elements */
  745. for (gsize i = 0; i < nelts; i++) {
  746. lua_rawgeti(L, -1, i + 1);
  747. if (lua_type(L, -1) == LUA_TSTRING) {
  748. lua_body = lua_tolstring(L, -1, &bodylen);
  749. body = rspamd_fstring_append(body, lua_body, bodylen);
  750. }
  751. else {
  752. t = lua_check_text(L, -1);
  753. if (t) {
  754. body = rspamd_fstring_append(body, t->start, t->len);
  755. }
  756. }
  757. lua_pop(L, 1);
  758. }
  759. }
  760. else if (lua_type(L, -1) != LUA_TNONE && lua_type(L, -1) != LUA_TNIL) {
  761. rspamd_http_message_unref(msg);
  762. return luaL_error(L, "invalid body argument type: %s",
  763. lua_typename(L, lua_type(L, -1)));
  764. }
  765. lua_pop(L, 1);
  766. lua_pushstring(L, "peer_key");
  767. lua_gettable(L, 1);
  768. if (lua_type(L, -1) == LUA_TSTRING) {
  769. const char *in;
  770. gsize inlen;
  771. in = lua_tolstring(L, -1, &inlen);
  772. peer_key = rspamd_pubkey_from_base32(in, inlen,
  773. RSPAMD_KEYPAIR_KEX, RSPAMD_CRYPTOBOX_MODE_25519);
  774. }
  775. lua_pop(L, 1);
  776. lua_pushstring(L, "keypair");
  777. lua_gettable(L, 1);
  778. if (lua_type(L, -1) == LUA_TTABLE) {
  779. ucl_object_t *kp_ucl = ucl_object_lua_import(L, -1);
  780. local_kp = rspamd_keypair_from_ucl(kp_ucl);
  781. ucl_object_unref(kp_ucl);
  782. }
  783. lua_pop(L, 1);
  784. lua_pushstring(L, "opaque_body");
  785. lua_gettable(L, 1);
  786. if (!!lua_toboolean(L, -1)) {
  787. flags |= RSPAMD_LUA_HTTP_FLAG_TEXT;
  788. }
  789. lua_pop(L, 1);
  790. lua_pushstring(L, "gzip");
  791. lua_gettable(L, 1);
  792. if (!!lua_toboolean(L, -1)) {
  793. gzip = TRUE;
  794. }
  795. lua_pop(L, 1);
  796. lua_pushstring(L, "no_ssl_verify");
  797. lua_gettable(L, 1);
  798. if (!!lua_toboolean(L, -1)) {
  799. flags |= RSPAMD_LUA_HTTP_FLAG_NOVERIFY;
  800. }
  801. lua_pop(L, 1);
  802. lua_pushstring(L, "keepalive");
  803. lua_gettable(L, 1);
  804. if (!!lua_toboolean(L, -1)) {
  805. flags |= RSPAMD_LUA_HTTP_FLAG_KEEP_ALIVE;
  806. }
  807. lua_pop(L, 1);
  808. lua_pushstring(L, "max_size");
  809. lua_gettable(L, 1);
  810. if (lua_type(L, -1) == LUA_TNUMBER) {
  811. max_size = lua_tointeger(L, -1);
  812. }
  813. lua_pop(L, 1);
  814. lua_pushstring(L, "method");
  815. lua_gettable(L, 1);
  816. if (lua_type(L, -1) == LUA_TSTRING) {
  817. rspamd_http_message_set_method(msg, lua_tostring(L, -1));
  818. }
  819. lua_pop(L, 1);
  820. lua_pushstring(L, "upstream");
  821. lua_gettable(L, 1);
  822. if (lua_type(L, -1) == LUA_TUSERDATA) {
  823. struct rspamd_lua_upstream *lup = lua_check_upstream(L, -1);
  824. if (lup) {
  825. /* Preserve pointer in case if lup is destructed */
  826. up = lup->up;
  827. }
  828. }
  829. lua_pop(L, 1);
  830. lua_pushstring(L, "user");
  831. lua_gettable(L, 1);
  832. if (lua_type(L, -1) == LUA_TSTRING) {
  833. const char *user = lua_tostring(L, -1);
  834. lua_pushstring(L, "password");
  835. lua_gettable(L, 1);
  836. if (lua_type(L, -1) == LUA_TSTRING) {
  837. const char *password = lua_tostring(L, -1);
  838. char *tmpbuf;
  839. gsize tlen;
  840. tlen = strlen(user) + strlen(password) + 1;
  841. tmpbuf = g_malloc(tlen + 1);
  842. rspamd_snprintf(tmpbuf, tlen + 1, "%s:%s", user, password);
  843. tlen *= 2;
  844. tlen += sizeof("Basic ") - 1;
  845. auth = g_malloc(tlen + 1);
  846. rspamd_snprintf(auth, tlen + 1, "Basic %Bs", tmpbuf);
  847. g_free(tmpbuf);
  848. }
  849. else {
  850. msg_warn("HTTP user must have password, disabling auth");
  851. }
  852. lua_pop(L, 1); /* password */
  853. }
  854. lua_pop(L, 1); /* username */
  855. }
  856. else {
  857. msg_err("http request has bad params");
  858. lua_pushboolean(L, FALSE);
  859. return 1;
  860. }
  861. if (session && rspamd_session_blocked(session)) {
  862. lua_pushboolean(L, FALSE);
  863. g_free(auth);
  864. rspamd_http_message_unref(msg);
  865. if (body) {
  866. rspamd_fstring_free(body);
  867. }
  868. if (local_kp) {
  869. rspamd_keypair_unref(local_kp);
  870. }
  871. return 1;
  872. }
  873. if (task == NULL && cfg == NULL) {
  874. g_free(auth);
  875. rspamd_http_message_unref(msg);
  876. if (body) {
  877. rspamd_fstring_free(body);
  878. }
  879. if (local_kp) {
  880. rspamd_keypair_unref(local_kp);
  881. }
  882. return luaL_error(L,
  883. "Bad params to rspamd_http:request(): either task or config should be set");
  884. }
  885. if (ev_base == NULL) {
  886. g_free(auth);
  887. rspamd_http_message_unref(msg);
  888. if (body) {
  889. rspamd_fstring_free(body);
  890. }
  891. if (local_kp) {
  892. rspamd_keypair_unref(local_kp);
  893. }
  894. return luaL_error(L,
  895. "Bad params to rspamd_http:request(): ev_base isn't passed");
  896. }
  897. cbd = g_malloc0(sizeof(*cbd));
  898. cbd->cbref = cbref;
  899. cbd->msg = msg;
  900. cbd->event_loop = ev_base;
  901. cbd->mime_type = mime_type;
  902. cbd->timeout = timeout;
  903. cbd->fd = -1;
  904. cbd->cfg = cfg;
  905. cbd->peer_pk = peer_key;
  906. cbd->local_kp = local_kp;
  907. cbd->flags = flags;
  908. cbd->max_size = max_size;
  909. cbd->url = url;
  910. cbd->auth = auth;
  911. cbd->task = task;
  912. if (up) {
  913. cbd->up = rspamd_upstream_ref(up);
  914. }
  915. if (cbd->cbref == -1) {
  916. cbd->thread = lua_thread_pool_get_running_entry(cfg->lua_thread_pool);
  917. }
  918. REF_INIT_RETAIN(cbd, lua_http_cbd_dtor);
  919. if (task) {
  920. cbd->item = rspamd_symcache_get_cur_item(task);
  921. }
  922. if (body) {
  923. if (gzip) {
  924. if (rspamd_fstring_gzip(&body)) {
  925. rspamd_http_message_add_header(msg, "Content-Encoding", "gzip");
  926. }
  927. }
  928. rspamd_http_message_set_body_from_fstring_steal(msg, body);
  929. }
  930. if (session) {
  931. cbd->session = session;
  932. }
  933. bool numeric_ip = false;
  934. /* Check if we can skip resolving */
  935. gsize hostlen = 0;
  936. const char *host = rspamd_http_message_get_http_host(msg, &hostlen);
  937. if (host) {
  938. cbd->host = g_malloc(hostlen + 1);
  939. rspamd_strlcpy(cbd->host, host, hostlen + 1);
  940. /* Keep-alive entry is available */
  941. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_KEEP_ALIVE) {
  942. const rspamd_inet_addr_t *ka_addr = rspamd_http_context_has_keepalive(NULL,
  943. cbd->host,
  944. msg->port,
  945. msg->flags & RSPAMD_HTTP_FLAG_WANT_SSL);
  946. if (ka_addr) {
  947. cbd->addr = rspamd_inet_address_copy(ka_addr, NULL);
  948. numeric_ip = true;
  949. }
  950. }
  951. /*
  952. * No keep-alive stuff, check if we have upstream or if we can parse host as
  953. * a numeric address
  954. */
  955. if (!cbd->addr) {
  956. if (cbd->up) {
  957. numeric_ip = true;
  958. cbd->addr = rspamd_inet_address_copy(rspamd_upstream_addr_next(cbd->up), NULL);
  959. }
  960. else {
  961. /* We use msg->host here, not cbd->host ! */
  962. if (rspamd_parse_inet_address(&cbd->addr,
  963. msg->host->str, msg->host->len,
  964. RSPAMD_INET_ADDRESS_PARSE_DEFAULT)) {
  965. numeric_ip = true;
  966. }
  967. }
  968. }
  969. }
  970. else {
  971. if (cbd->up) {
  972. numeric_ip = true;
  973. cbd->addr = rspamd_inet_address_copy(rspamd_upstream_addr_next(cbd->up), NULL);
  974. }
  975. cbd->host = NULL;
  976. }
  977. if (numeric_ip) {
  978. /* Host is numeric IP, no need to resolve */
  979. gboolean ret;
  980. REF_RETAIN(cbd);
  981. ret = lua_http_make_connection(cbd);
  982. if (!ret) {
  983. if (cbd->up) {
  984. rspamd_upstream_fail(cbd->up, true, "HTTP connection failed");
  985. }
  986. if (cbd->ref.refcount > 1) {
  987. /* Not released by make_connection */
  988. REF_RELEASE(cbd);
  989. }
  990. REF_RELEASE(cbd);
  991. lua_pushboolean(L, FALSE);
  992. return 1;
  993. }
  994. REF_RELEASE(cbd);
  995. }
  996. else {
  997. if (!cbd->host) {
  998. REF_RELEASE(cbd);
  999. return luaL_error(L, "no host has been specified");
  1000. }
  1001. if (task == NULL) {
  1002. REF_RETAIN(cbd);
  1003. if (!rspamd_dns_resolver_request(resolver, session, NULL, lua_http_dns_handler, cbd,
  1004. RDNS_REQUEST_A,
  1005. cbd->host)) {
  1006. if (cbd->ref.refcount > 1) {
  1007. /* Not released by make_connection */
  1008. REF_RELEASE(cbd);
  1009. }
  1010. REF_RELEASE(cbd);
  1011. lua_pushboolean(L, FALSE);
  1012. return 1;
  1013. }
  1014. REF_RELEASE(cbd);
  1015. }
  1016. else {
  1017. REF_RETAIN(cbd);
  1018. if (!rspamd_dns_resolver_request_task_forced(task, lua_http_dns_handler, cbd,
  1019. RDNS_REQUEST_A, cbd->host)) {
  1020. if (cbd->ref.refcount > 1) {
  1021. /* Not released by make_connection */
  1022. REF_RELEASE(cbd);
  1023. }
  1024. REF_RELEASE(cbd);
  1025. lua_pushboolean(L, FALSE);
  1026. return 1;
  1027. }
  1028. else if (cbd->item) {
  1029. rspamd_symcache_item_async_inc(cbd->task, cbd->item, M);
  1030. }
  1031. REF_RELEASE(cbd);
  1032. }
  1033. }
  1034. if (cbd->cbref == -1) {
  1035. cbd->thread = lua_thread_pool_get_running_entry(cfg->lua_thread_pool);
  1036. cbd->flags |= RSPAMD_LUA_HTTP_FLAG_YIELDED;
  1037. return lua_thread_yield(cbd->thread, 0);
  1038. }
  1039. else {
  1040. lua_pushboolean(L, TRUE);
  1041. }
  1042. return 1;
  1043. }
  1044. static int
  1045. lua_load_http(lua_State *L)
  1046. {
  1047. lua_newtable(L);
  1048. luaL_register(L, NULL, httplib_m);
  1049. return 1;
  1050. }
  1051. void luaopen_http(lua_State *L)
  1052. {
  1053. rspamd_lua_add_preload(L, "rspamd_http", lua_load_http);
  1054. }