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_message.c 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  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 "http_message.h"
  17. #include "libutil/http_connection.h"
  18. #include "libutil/http_private.h"
  19. #include "libutil/printf.h"
  20. #include "libutil/logger.h"
  21. #include "unix-std.h"
  22. struct rspamd_http_message *
  23. rspamd_http_new_message (enum rspamd_http_message_type type)
  24. {
  25. struct rspamd_http_message *new;
  26. new = g_malloc0 (sizeof (struct rspamd_http_message));
  27. if (type == HTTP_REQUEST) {
  28. new->url = rspamd_fstring_new ();
  29. }
  30. else {
  31. new->url = NULL;
  32. new->code = 200;
  33. }
  34. new->port = 80;
  35. new->type = type;
  36. new->method = HTTP_INVALID;
  37. REF_INIT_RETAIN (new, rspamd_http_message_free);
  38. return new;
  39. }
  40. struct rspamd_http_message*
  41. rspamd_http_message_from_url (const gchar *url)
  42. {
  43. struct http_parser_url pu;
  44. struct rspamd_http_message *msg;
  45. const gchar *host, *path;
  46. size_t pathlen, urllen;
  47. guint flags = 0;
  48. if (url == NULL) {
  49. return NULL;
  50. }
  51. urllen = strlen (url);
  52. memset (&pu, 0, sizeof (pu));
  53. if (http_parser_parse_url (url, urllen, FALSE, &pu) != 0) {
  54. msg_warn ("cannot parse URL: %s", url);
  55. return NULL;
  56. }
  57. if ((pu.field_set & (1 << UF_HOST)) == 0) {
  58. msg_warn ("no host argument in URL: %s", url);
  59. return NULL;
  60. }
  61. if ((pu.field_set & (1 << UF_SCHEMA))) {
  62. if (pu.field_data[UF_SCHEMA].len == sizeof ("https") - 1 &&
  63. memcmp (url + pu.field_data[UF_SCHEMA].off, "https", 5) == 0) {
  64. flags |= RSPAMD_HTTP_FLAG_SSL;
  65. }
  66. }
  67. if ((pu.field_set & (1 << UF_PATH)) == 0) {
  68. path = "/";
  69. pathlen = 1;
  70. }
  71. else {
  72. path = url + pu.field_data[UF_PATH].off;
  73. pathlen = urllen - pu.field_data[UF_PATH].off;
  74. }
  75. msg = rspamd_http_new_message (HTTP_REQUEST);
  76. host = url + pu.field_data[UF_HOST].off;
  77. msg->flags = flags;
  78. if ((pu.field_set & (1 << UF_PORT)) != 0) {
  79. msg->port = pu.port;
  80. }
  81. else {
  82. /* XXX: magic constant */
  83. if (flags & RSPAMD_HTTP_FLAG_SSL) {
  84. msg->port = 443;
  85. }
  86. else {
  87. msg->port = 80;
  88. }
  89. }
  90. msg->host = rspamd_fstring_new_init (host, pu.field_data[UF_HOST].len);
  91. msg->url = rspamd_fstring_append (msg->url, path, pathlen);
  92. REF_INIT_RETAIN (msg, rspamd_http_message_free);
  93. return msg;
  94. }
  95. const gchar *
  96. rspamd_http_message_get_body (struct rspamd_http_message *msg,
  97. gsize *blen)
  98. {
  99. const gchar *ret = NULL;
  100. if (msg->body_buf.len > 0) {
  101. ret = msg->body_buf.begin;
  102. }
  103. if (blen) {
  104. *blen = msg->body_buf.len;
  105. }
  106. return ret;
  107. }
  108. static void
  109. rspamd_http_shname_dtor (void *p)
  110. {
  111. struct rspamd_storage_shmem *n = p;
  112. #ifdef HAVE_SANE_SHMEM
  113. shm_unlink (n->shm_name);
  114. #else
  115. unlink (n->shm_name);
  116. #endif
  117. g_free (n->shm_name);
  118. g_free (n);
  119. }
  120. struct rspamd_storage_shmem *
  121. rspamd_http_message_shmem_ref (struct rspamd_http_message *msg)
  122. {
  123. if ((msg->flags & RSPAMD_HTTP_FLAG_SHMEM) && msg->body_buf.c.shared.name) {
  124. REF_RETAIN (msg->body_buf.c.shared.name);
  125. return msg->body_buf.c.shared.name;
  126. }
  127. return NULL;
  128. }
  129. guint
  130. rspamd_http_message_get_flags (struct rspamd_http_message *msg)
  131. {
  132. return msg->flags;
  133. }
  134. void
  135. rspamd_http_message_shmem_unref (struct rspamd_storage_shmem *p)
  136. {
  137. REF_RELEASE (p);
  138. }
  139. gboolean
  140. rspamd_http_message_set_body (struct rspamd_http_message *msg,
  141. const gchar *data, gsize len)
  142. {
  143. union _rspamd_storage_u *storage;
  144. storage = &msg->body_buf.c;
  145. rspamd_http_message_storage_cleanup (msg);
  146. if (msg->flags & RSPAMD_HTTP_FLAG_SHMEM) {
  147. storage->shared.name = g_malloc (sizeof (*storage->shared.name));
  148. REF_INIT_RETAIN (storage->shared.name, rspamd_http_shname_dtor);
  149. #ifdef HAVE_SANE_SHMEM
  150. #if defined(__DragonFly__)
  151. // DragonFly uses regular files for shm. User rspamd is not allowed to create
  152. // files in the root.
  153. storage->shared.name->shm_name = g_strdup ("/tmp/rhm.XXXXXXXXXXXXXXXXXXXX");
  154. #else
  155. storage->shared.name->shm_name = g_strdup ("/rhm.XXXXXXXXXXXXXXXXXXXX");
  156. #endif
  157. storage->shared.shm_fd = rspamd_shmem_mkstemp (storage->shared.name->shm_name);
  158. #else
  159. /* XXX: assume that tempdir is /tmp */
  160. storage->shared.name->shm_name = g_strdup ("/tmp/rhm.XXXXXXXXXXXXXXXXXXXX");
  161. storage->shared.shm_fd = mkstemp (storage->shared.name->shm_name);
  162. #endif
  163. if (storage->shared.shm_fd == -1) {
  164. return FALSE;
  165. }
  166. if (len != 0 && len != ULLONG_MAX) {
  167. if (ftruncate (storage->shared.shm_fd, len) == -1) {
  168. return FALSE;
  169. }
  170. msg->body_buf.str = mmap (NULL, len,
  171. PROT_WRITE|PROT_READ, MAP_SHARED,
  172. storage->shared.shm_fd, 0);
  173. if (msg->body_buf.str == MAP_FAILED) {
  174. return FALSE;
  175. }
  176. msg->body_buf.begin = msg->body_buf.str;
  177. msg->body_buf.allocated_len = len;
  178. if (data != NULL) {
  179. memcpy (msg->body_buf.str, data, len);
  180. msg->body_buf.len = len;
  181. }
  182. }
  183. else {
  184. msg->body_buf.len = 0;
  185. msg->body_buf.begin = NULL;
  186. msg->body_buf.str = NULL;
  187. msg->body_buf.allocated_len = 0;
  188. }
  189. }
  190. else {
  191. if (len != 0 && len != ULLONG_MAX) {
  192. if (data == NULL) {
  193. storage->normal = rspamd_fstring_sized_new (len);
  194. msg->body_buf.len = 0;
  195. }
  196. else {
  197. storage->normal = rspamd_fstring_new_init (data, len);
  198. msg->body_buf.len = len;
  199. }
  200. }
  201. else {
  202. storage->normal = rspamd_fstring_new ();
  203. }
  204. msg->body_buf.begin = storage->normal->str;
  205. msg->body_buf.str = storage->normal->str;
  206. msg->body_buf.allocated_len = storage->normal->allocated;
  207. }
  208. msg->flags |= RSPAMD_HTTP_FLAG_HAS_BODY;
  209. return TRUE;
  210. }
  211. void
  212. rspamd_http_message_set_method (struct rspamd_http_message *msg,
  213. const gchar *method)
  214. {
  215. gint i;
  216. /* Linear search: not very efficient method */
  217. for (i = 0; i < HTTP_METHOD_MAX; i ++) {
  218. if (g_ascii_strcasecmp (method, http_method_str (i)) == 0) {
  219. msg->method = i;
  220. }
  221. }
  222. }
  223. gboolean
  224. rspamd_http_message_set_body_from_fd (struct rspamd_http_message *msg,
  225. gint fd)
  226. {
  227. union _rspamd_storage_u *storage;
  228. struct stat st;
  229. rspamd_http_message_storage_cleanup (msg);
  230. storage = &msg->body_buf.c;
  231. msg->flags |= RSPAMD_HTTP_FLAG_SHMEM|RSPAMD_HTTP_FLAG_SHMEM_IMMUTABLE;
  232. storage->shared.shm_fd = dup (fd);
  233. msg->body_buf.str = MAP_FAILED;
  234. if (storage->shared.shm_fd == -1) {
  235. return FALSE;
  236. }
  237. if (fstat (storage->shared.shm_fd, &st) == -1) {
  238. return FALSE;
  239. }
  240. msg->body_buf.str = mmap (NULL, st.st_size,
  241. PROT_READ, MAP_SHARED,
  242. storage->shared.shm_fd, 0);
  243. if (msg->body_buf.str == MAP_FAILED) {
  244. return FALSE;
  245. }
  246. msg->body_buf.begin = msg->body_buf.str;
  247. msg->body_buf.len = st.st_size;
  248. msg->body_buf.allocated_len = st.st_size;
  249. return TRUE;
  250. }
  251. gboolean
  252. rspamd_http_message_set_body_from_fstring_steal (struct rspamd_http_message *msg,
  253. rspamd_fstring_t *fstr)
  254. {
  255. union _rspamd_storage_u *storage;
  256. rspamd_http_message_storage_cleanup (msg);
  257. storage = &msg->body_buf.c;
  258. msg->flags &= ~(RSPAMD_HTTP_FLAG_SHMEM|RSPAMD_HTTP_FLAG_SHMEM_IMMUTABLE);
  259. storage->normal = fstr;
  260. msg->body_buf.str = fstr->str;
  261. msg->body_buf.begin = msg->body_buf.str;
  262. msg->body_buf.len = fstr->len;
  263. msg->body_buf.allocated_len = fstr->allocated;
  264. return TRUE;
  265. }
  266. gboolean
  267. rspamd_http_message_set_body_from_fstring_copy (struct rspamd_http_message *msg,
  268. const rspamd_fstring_t *fstr)
  269. {
  270. union _rspamd_storage_u *storage;
  271. rspamd_http_message_storage_cleanup (msg);
  272. storage = &msg->body_buf.c;
  273. msg->flags &= ~(RSPAMD_HTTP_FLAG_SHMEM|RSPAMD_HTTP_FLAG_SHMEM_IMMUTABLE);
  274. storage->normal = rspamd_fstring_new_init (fstr->str, fstr->len);
  275. msg->body_buf.str = storage->normal->str;
  276. msg->body_buf.begin = msg->body_buf.str;
  277. msg->body_buf.len = storage->normal->len;
  278. msg->body_buf.allocated_len = storage->normal->allocated;
  279. return TRUE;
  280. }
  281. gboolean
  282. rspamd_http_message_grow_body (struct rspamd_http_message *msg, gsize len)
  283. {
  284. struct stat st;
  285. union _rspamd_storage_u *storage;
  286. gsize newlen;
  287. storage = &msg->body_buf.c;
  288. if (msg->flags & RSPAMD_HTTP_FLAG_SHMEM) {
  289. if (storage->shared.shm_fd == -1) {
  290. return FALSE;
  291. }
  292. if (fstat (storage->shared.shm_fd, &st) == -1) {
  293. return FALSE;
  294. }
  295. /* Check if we need to grow */
  296. if ((gsize)st.st_size < msg->body_buf.len + len) {
  297. /* Need to grow */
  298. newlen = rspamd_fstring_suggest_size (msg->body_buf.len, st.st_size,
  299. len);
  300. /* Unmap as we need another size of segment */
  301. if (msg->body_buf.str != MAP_FAILED) {
  302. munmap (msg->body_buf.str, st.st_size);
  303. }
  304. if (ftruncate (storage->shared.shm_fd, newlen) == -1) {
  305. return FALSE;
  306. }
  307. msg->body_buf.str = mmap (NULL, newlen,
  308. PROT_WRITE|PROT_READ, MAP_SHARED,
  309. storage->shared.shm_fd, 0);
  310. if (msg->body_buf.str == MAP_FAILED) {
  311. return FALSE;
  312. }
  313. msg->body_buf.begin = msg->body_buf.str;
  314. msg->body_buf.allocated_len = newlen;
  315. }
  316. }
  317. else {
  318. storage->normal = rspamd_fstring_grow (storage->normal, len);
  319. /* Append might cause realloc */
  320. msg->body_buf.begin = storage->normal->str;
  321. msg->body_buf.len = storage->normal->len;
  322. msg->body_buf.str = storage->normal->str;
  323. msg->body_buf.allocated_len = storage->normal->allocated;
  324. }
  325. return TRUE;
  326. }
  327. gboolean
  328. rspamd_http_message_append_body (struct rspamd_http_message *msg,
  329. const gchar *data, gsize len)
  330. {
  331. union _rspamd_storage_u *storage;
  332. storage = &msg->body_buf.c;
  333. if (msg->flags & RSPAMD_HTTP_FLAG_SHMEM) {
  334. if (!rspamd_http_message_grow_body (msg, len)) {
  335. return FALSE;
  336. }
  337. memcpy (msg->body_buf.str + msg->body_buf.len, data, len);
  338. msg->body_buf.len += len;
  339. }
  340. else {
  341. storage->normal = rspamd_fstring_append (storage->normal, data, len);
  342. /* Append might cause realloc */
  343. msg->body_buf.begin = storage->normal->str;
  344. msg->body_buf.len = storage->normal->len;
  345. msg->body_buf.str = storage->normal->str;
  346. msg->body_buf.allocated_len = storage->normal->allocated;
  347. }
  348. return TRUE;
  349. }
  350. void
  351. rspamd_http_message_storage_cleanup (struct rspamd_http_message *msg)
  352. {
  353. union _rspamd_storage_u *storage;
  354. struct stat st;
  355. if (msg->flags & RSPAMD_HTTP_FLAG_SHMEM) {
  356. storage = &msg->body_buf.c;
  357. if (storage->shared.shm_fd > 0) {
  358. g_assert (fstat (storage->shared.shm_fd, &st) != -1);
  359. if (msg->body_buf.str != MAP_FAILED) {
  360. munmap (msg->body_buf.str, st.st_size);
  361. }
  362. close (storage->shared.shm_fd);
  363. }
  364. if (storage->shared.name != NULL) {
  365. REF_RELEASE (storage->shared.name);
  366. }
  367. storage->shared.shm_fd = -1;
  368. msg->body_buf.str = MAP_FAILED;
  369. }
  370. else {
  371. if (msg->body_buf.c.normal) {
  372. rspamd_fstring_free (msg->body_buf.c.normal);
  373. }
  374. msg->body_buf.c.normal = NULL;
  375. }
  376. msg->body_buf.len = 0;
  377. }