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.

http_router.c 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. /*-
  2. * Copyright 2019 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 "libutil/http_router.h"
  17. #include "libutil/http_connection.h"
  18. #include "libutil/http_private.h"
  19. #include "libutil/regexp.h"
  20. #include "libutil/printf.h"
  21. #include "libutil/logger.h"
  22. #include "utlist.h"
  23. #include "unix-std.h"
  24. enum http_magic_type {
  25. HTTP_MAGIC_PLAIN = 0,
  26. HTTP_MAGIC_HTML,
  27. HTTP_MAGIC_CSS,
  28. HTTP_MAGIC_JS,
  29. HTTP_MAGIC_PNG,
  30. HTTP_MAGIC_JPG
  31. };
  32. static const struct _rspamd_http_magic {
  33. const gchar *ext;
  34. const gchar *ct;
  35. } http_file_types[] = {
  36. [HTTP_MAGIC_PLAIN] = { "txt", "text/plain" },
  37. [HTTP_MAGIC_HTML] = { "html", "text/html" },
  38. [HTTP_MAGIC_CSS] = { "css", "text/css" },
  39. [HTTP_MAGIC_JS] = { "js", "application/javascript" },
  40. [HTTP_MAGIC_PNG] = { "png", "image/png" },
  41. [HTTP_MAGIC_JPG] = { "jpg", "image/jpeg" },
  42. };
  43. /*
  44. * HTTP router functions
  45. */
  46. static void
  47. rspamd_http_entry_free (struct rspamd_http_connection_entry *entry)
  48. {
  49. if (entry != NULL) {
  50. close (entry->conn->fd);
  51. rspamd_http_connection_unref (entry->conn);
  52. if (entry->rt->finish_handler) {
  53. entry->rt->finish_handler (entry);
  54. }
  55. DL_DELETE (entry->rt->conns, entry);
  56. g_free (entry);
  57. }
  58. }
  59. static void
  60. rspamd_http_router_error_handler (struct rspamd_http_connection *conn,
  61. GError *err)
  62. {
  63. struct rspamd_http_connection_entry *entry = conn->ud;
  64. struct rspamd_http_message *msg;
  65. if (entry->is_reply) {
  66. /* At this point we need to finish this session and close owned socket */
  67. if (entry->rt->error_handler != NULL) {
  68. entry->rt->error_handler (entry, err);
  69. }
  70. rspamd_http_entry_free (entry);
  71. }
  72. else {
  73. /* Here we can write a reply to a client */
  74. if (entry->rt->error_handler != NULL) {
  75. entry->rt->error_handler (entry, err);
  76. }
  77. msg = rspamd_http_new_message (HTTP_RESPONSE);
  78. msg->date = time (NULL);
  79. msg->code = err->code;
  80. rspamd_http_message_set_body (msg, err->message, strlen (err->message));
  81. rspamd_http_connection_reset (entry->conn);
  82. rspamd_http_connection_write_message (entry->conn,
  83. msg,
  84. NULL,
  85. "text/plain",
  86. entry,
  87. entry->conn->fd,
  88. entry->rt->ptv,
  89. entry->rt->ev_base);
  90. entry->is_reply = TRUE;
  91. }
  92. }
  93. static const gchar *
  94. rspamd_http_router_detect_ct (const gchar *path)
  95. {
  96. const gchar *dot;
  97. guint i;
  98. dot = strrchr (path, '.');
  99. if (dot == NULL) {
  100. return http_file_types[HTTP_MAGIC_PLAIN].ct;
  101. }
  102. dot++;
  103. for (i = 0; i < G_N_ELEMENTS (http_file_types); i++) {
  104. if (strcmp (http_file_types[i].ext, dot) == 0) {
  105. return http_file_types[i].ct;
  106. }
  107. }
  108. return http_file_types[HTTP_MAGIC_PLAIN].ct;
  109. }
  110. static gboolean
  111. rspamd_http_router_is_subdir (const gchar *parent, const gchar *sub)
  112. {
  113. if (parent == NULL || sub == NULL || *parent == '\0') {
  114. return FALSE;
  115. }
  116. while (*parent != '\0') {
  117. if (*sub != *parent) {
  118. return FALSE;
  119. }
  120. parent++;
  121. sub++;
  122. }
  123. parent--;
  124. if (*parent == G_DIR_SEPARATOR) {
  125. return TRUE;
  126. }
  127. return (*sub == G_DIR_SEPARATOR || *sub == '\0');
  128. }
  129. static gboolean
  130. rspamd_http_router_try_file (struct rspamd_http_connection_entry *entry,
  131. rspamd_ftok_t *lookup, gboolean expand_path)
  132. {
  133. struct stat st;
  134. gint fd;
  135. gchar filebuf[PATH_MAX], realbuf[PATH_MAX], *dir;
  136. struct rspamd_http_message *reply_msg;
  137. rspamd_snprintf (filebuf, sizeof (filebuf), "%s%c%T",
  138. entry->rt->default_fs_path, G_DIR_SEPARATOR, lookup);
  139. if (realpath (filebuf, realbuf) == NULL ||
  140. lstat (realbuf, &st) == -1) {
  141. return FALSE;
  142. }
  143. if (S_ISDIR (st.st_mode) && expand_path) {
  144. /* Try to append 'index.html' to the url */
  145. rspamd_fstring_t *nlookup;
  146. rspamd_ftok_t tok;
  147. gboolean ret;
  148. nlookup = rspamd_fstring_sized_new (lookup->len + sizeof ("index.html"));
  149. rspamd_printf_fstring (&nlookup, "%T%c%s", lookup, G_DIR_SEPARATOR,
  150. "index.html");
  151. tok.begin = nlookup->str;
  152. tok.len = nlookup->len;
  153. ret = rspamd_http_router_try_file (entry, &tok, FALSE);
  154. rspamd_fstring_free (nlookup);
  155. return ret;
  156. }
  157. else if (!S_ISREG (st.st_mode)) {
  158. return FALSE;
  159. }
  160. /* We also need to ensure that file is inside the defined dir */
  161. rspamd_strlcpy (filebuf, realbuf, sizeof (filebuf));
  162. dir = dirname (filebuf);
  163. if (dir == NULL ||
  164. !rspamd_http_router_is_subdir (entry->rt->default_fs_path,
  165. dir)) {
  166. return FALSE;
  167. }
  168. fd = open (realbuf, O_RDONLY);
  169. if (fd == -1) {
  170. return FALSE;
  171. }
  172. reply_msg = rspamd_http_new_message (HTTP_RESPONSE);
  173. reply_msg->date = time (NULL);
  174. reply_msg->code = 200;
  175. rspamd_http_router_insert_headers (entry->rt, reply_msg);
  176. if (!rspamd_http_message_set_body_from_fd (reply_msg, fd)) {
  177. close (fd);
  178. return FALSE;
  179. }
  180. close (fd);
  181. rspamd_http_connection_reset (entry->conn);
  182. msg_debug ("requested file %s", realbuf);
  183. rspamd_http_connection_write_message (entry->conn, reply_msg, NULL,
  184. rspamd_http_router_detect_ct (realbuf), entry, entry->conn->fd,
  185. entry->rt->ptv, entry->rt->ev_base);
  186. return TRUE;
  187. }
  188. static void
  189. rspamd_http_router_send_error (GError *err,
  190. struct rspamd_http_connection_entry *entry)
  191. {
  192. struct rspamd_http_message *err_msg;
  193. err_msg = rspamd_http_new_message (HTTP_RESPONSE);
  194. err_msg->date = time (NULL);
  195. err_msg->code = err->code;
  196. rspamd_http_message_set_body (err_msg, err->message,
  197. strlen (err->message));
  198. entry->is_reply = TRUE;
  199. err_msg->status = rspamd_fstring_new_init (err->message, strlen (err->message));
  200. rspamd_http_router_insert_headers (entry->rt, err_msg);
  201. rspamd_http_connection_reset (entry->conn);
  202. rspamd_http_connection_write_message (entry->conn,
  203. err_msg,
  204. NULL,
  205. "text/plain",
  206. entry,
  207. entry->conn->fd,
  208. entry->rt->ptv,
  209. entry->rt->ev_base);
  210. }
  211. static int
  212. rspamd_http_router_finish_handler (struct rspamd_http_connection *conn,
  213. struct rspamd_http_message *msg)
  214. {
  215. struct rspamd_http_connection_entry *entry = conn->ud;
  216. rspamd_http_router_handler_t handler = NULL;
  217. gpointer found;
  218. GError *err;
  219. rspamd_ftok_t lookup;
  220. const rspamd_ftok_t *encoding;
  221. struct http_parser_url u;
  222. guint i;
  223. rspamd_regexp_t *re;
  224. struct rspamd_http_connection_router *router;
  225. G_STATIC_ASSERT (sizeof (rspamd_http_router_handler_t) ==
  226. sizeof (gpointer));
  227. memset (&lookup, 0, sizeof (lookup));
  228. router = entry->rt;
  229. if (entry->is_reply) {
  230. /* Request is finished, it is safe to free a connection */
  231. rspamd_http_entry_free (entry);
  232. }
  233. else {
  234. if (G_UNLIKELY (msg->method != HTTP_GET && msg->method != HTTP_POST)) {
  235. if (router->unknown_method_handler) {
  236. return router->unknown_method_handler (entry, msg);
  237. }
  238. else {
  239. err = g_error_new (HTTP_ERROR, 500,
  240. "Invalid method");
  241. if (entry->rt->error_handler != NULL) {
  242. entry->rt->error_handler (entry, err);
  243. }
  244. rspamd_http_router_send_error (err, entry);
  245. g_error_free (err);
  246. return 0;
  247. }
  248. }
  249. /* Search for path */
  250. if (msg->url != NULL && msg->url->len != 0) {
  251. http_parser_parse_url (msg->url->str, msg->url->len, TRUE, &u);
  252. if (u.field_set & (1 << UF_PATH)) {
  253. guint unnorm_len;
  254. lookup.begin = msg->url->str + u.field_data[UF_PATH].off;
  255. lookup.len = u.field_data[UF_PATH].len;
  256. rspamd_http_normalize_path_inplace ((gchar *)lookup.begin,
  257. lookup.len,
  258. &unnorm_len);
  259. lookup.len = unnorm_len;
  260. }
  261. else {
  262. lookup.begin = msg->url->str;
  263. lookup.len = msg->url->len;
  264. }
  265. found = g_hash_table_lookup (entry->rt->paths, &lookup);
  266. memcpy (&handler, &found, sizeof (found));
  267. msg_debug ("requested known path: %T", &lookup);
  268. }
  269. else {
  270. err = g_error_new (HTTP_ERROR, 404,
  271. "Empty path requested");
  272. if (entry->rt->error_handler != NULL) {
  273. entry->rt->error_handler (entry, err);
  274. }
  275. rspamd_http_router_send_error (err, entry);
  276. g_error_free (err);
  277. return 0;
  278. }
  279. entry->is_reply = TRUE;
  280. encoding = rspamd_http_message_find_header (msg, "Accept-Encoding");
  281. if (encoding && rspamd_substring_search (encoding->begin, encoding->len,
  282. "gzip", 4) != -1) {
  283. entry->support_gzip = TRUE;
  284. }
  285. if (handler != NULL) {
  286. return handler (entry, msg);
  287. }
  288. else {
  289. /* Try regexps */
  290. for (i = 0; i < router->regexps->len; i ++) {
  291. re = g_ptr_array_index (router->regexps, i);
  292. if (rspamd_regexp_match (re, lookup.begin, lookup.len,
  293. TRUE)) {
  294. found = rspamd_regexp_get_ud (re);
  295. memcpy (&handler, &found, sizeof (found));
  296. return handler (entry, msg);
  297. }
  298. }
  299. /* Now try plain file */
  300. if (entry->rt->default_fs_path == NULL || lookup.len == 0 ||
  301. !rspamd_http_router_try_file (entry, &lookup, TRUE)) {
  302. err = g_error_new (HTTP_ERROR, 404,
  303. "Not found");
  304. if (entry->rt->error_handler != NULL) {
  305. entry->rt->error_handler (entry, err);
  306. }
  307. msg_info ("path: %T not found", &lookup);
  308. rspamd_http_router_send_error (err, entry);
  309. g_error_free (err);
  310. }
  311. }
  312. }
  313. return 0;
  314. }
  315. struct rspamd_http_connection_router *
  316. rspamd_http_router_new (rspamd_http_router_error_handler_t eh,
  317. rspamd_http_router_finish_handler_t fh,
  318. struct timeval *timeout, struct event_base *base,
  319. const char *default_fs_path,
  320. struct rspamd_keypair_cache *cache)
  321. {
  322. struct rspamd_http_connection_router * new;
  323. struct stat st;
  324. new = g_malloc0 (sizeof (struct rspamd_http_connection_router));
  325. new->paths = g_hash_table_new_full (rspamd_ftok_icase_hash,
  326. rspamd_ftok_icase_equal, rspamd_fstring_mapped_ftok_free, NULL);
  327. new->regexps = g_ptr_array_new ();
  328. new->conns = NULL;
  329. new->error_handler = eh;
  330. new->finish_handler = fh;
  331. new->ev_base = base;
  332. new->response_headers = g_hash_table_new_full (rspamd_strcase_hash,
  333. rspamd_strcase_equal, g_free, g_free);
  334. if (timeout) {
  335. new->tv = *timeout;
  336. new->ptv = &new->tv;
  337. }
  338. else {
  339. new->ptv = NULL;
  340. }
  341. new->default_fs_path = NULL;
  342. if (default_fs_path != NULL) {
  343. if (stat (default_fs_path, &st) == -1) {
  344. msg_err ("cannot stat %s", default_fs_path);
  345. }
  346. else {
  347. if (!S_ISDIR (st.st_mode)) {
  348. msg_err ("path %s is not a directory", default_fs_path);
  349. }
  350. else {
  351. new->default_fs_path = realpath (default_fs_path, NULL);
  352. }
  353. }
  354. }
  355. new->cache = cache;
  356. return new;
  357. }
  358. void
  359. rspamd_http_router_set_key (struct rspamd_http_connection_router *router,
  360. struct rspamd_cryptobox_keypair *key)
  361. {
  362. g_assert (key != NULL);
  363. router->key = rspamd_keypair_ref (key);
  364. }
  365. void
  366. rspamd_http_router_add_path (struct rspamd_http_connection_router *router,
  367. const gchar *path, rspamd_http_router_handler_t handler)
  368. {
  369. gpointer ptr;
  370. rspamd_ftok_t *key;
  371. rspamd_fstring_t *storage;
  372. G_STATIC_ASSERT (sizeof (rspamd_http_router_handler_t) ==
  373. sizeof (gpointer));
  374. if (path != NULL && handler != NULL && router != NULL) {
  375. memcpy (&ptr, &handler, sizeof (ptr));
  376. storage = rspamd_fstring_new_init (path, strlen (path));
  377. key = g_malloc0 (sizeof (*key));
  378. key->begin = storage->str;
  379. key->len = storage->len;
  380. g_hash_table_insert (router->paths, key, ptr);
  381. }
  382. }
  383. void
  384. rspamd_http_router_set_unknown_handler (struct rspamd_http_connection_router *router,
  385. rspamd_http_router_handler_t handler)
  386. {
  387. if (router != NULL) {
  388. router->unknown_method_handler = handler;
  389. }
  390. }
  391. void
  392. rspamd_http_router_add_header (struct rspamd_http_connection_router *router,
  393. const gchar *name, const gchar *value)
  394. {
  395. if (name != NULL && value != NULL && router != NULL) {
  396. g_hash_table_replace (router->response_headers, g_strdup (name),
  397. g_strdup (value));
  398. }
  399. }
  400. void
  401. rspamd_http_router_insert_headers (struct rspamd_http_connection_router *router,
  402. struct rspamd_http_message *msg)
  403. {
  404. GHashTableIter it;
  405. gpointer k, v;
  406. if (router && msg) {
  407. g_hash_table_iter_init (&it, router->response_headers);
  408. while (g_hash_table_iter_next (&it, &k, &v)) {
  409. rspamd_http_message_add_header (msg, k, v);
  410. }
  411. }
  412. }
  413. void
  414. rspamd_http_router_add_regexp (struct rspamd_http_connection_router *router,
  415. struct rspamd_regexp_s *re, rspamd_http_router_handler_t handler)
  416. {
  417. gpointer ptr;
  418. G_STATIC_ASSERT (sizeof (rspamd_http_router_handler_t) ==
  419. sizeof (gpointer));
  420. if (re != NULL && handler != NULL && router != NULL) {
  421. memcpy (&ptr, &handler, sizeof (ptr));
  422. rspamd_regexp_set_ud (re, ptr);
  423. g_ptr_array_add (router->regexps, rspamd_regexp_ref (re));
  424. }
  425. }
  426. void
  427. rspamd_http_router_handle_socket (struct rspamd_http_connection_router *router,
  428. gint fd, gpointer ud)
  429. {
  430. struct rspamd_http_connection_entry *conn;
  431. conn = g_malloc0 (sizeof (struct rspamd_http_connection_entry));
  432. conn->rt = router;
  433. conn->ud = ud;
  434. conn->is_reply = FALSE;
  435. conn->conn = rspamd_http_connection_new (NULL,
  436. rspamd_http_router_error_handler,
  437. rspamd_http_router_finish_handler,
  438. 0,
  439. RSPAMD_HTTP_SERVER,
  440. router->cache,
  441. NULL);
  442. if (router->key) {
  443. rspamd_http_connection_set_key (conn->conn, router->key);
  444. }
  445. rspamd_http_connection_read_message (conn->conn, conn, fd, router->ptv,
  446. router->ev_base);
  447. DL_PREPEND (router->conns, conn);
  448. }
  449. void
  450. rspamd_http_router_free (struct rspamd_http_connection_router *router)
  451. {
  452. struct rspamd_http_connection_entry *conn, *tmp;
  453. rspamd_regexp_t *re;
  454. guint i;
  455. if (router) {
  456. DL_FOREACH_SAFE (router->conns, conn, tmp) {
  457. rspamd_http_entry_free (conn);
  458. }
  459. if (router->key) {
  460. rspamd_keypair_unref (router->key);
  461. }
  462. if (router->cache) {
  463. rspamd_keypair_cache_destroy (router->cache);
  464. }
  465. if (router->default_fs_path != NULL) {
  466. g_free (router->default_fs_path);
  467. }
  468. for (i = 0; i < router->regexps->len; i ++) {
  469. re = g_ptr_array_index (router->regexps, i);
  470. rspamd_regexp_unref (re);
  471. }
  472. g_ptr_array_free (router->regexps, TRUE);
  473. g_hash_table_unref (router->paths);
  474. g_hash_table_unref (router->response_headers);
  475. g_free (router);
  476. }
  477. }