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.

rspamdclient.c 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  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 "rspamdclient.h"
  17. #include "util.h"
  18. #include "http.h"
  19. #include "unix-std.h"
  20. #ifdef HAVE_FETCH_H
  21. #include <fetch.h>
  22. #elif defined(CURL_FOUND)
  23. #include <curl/curl.h>
  24. #endif
  25. struct rspamd_client_request;
  26. /*
  27. * Since rspamd uses untagged HTTP we can pass a single message per socket
  28. */
  29. struct rspamd_client_connection {
  30. gint fd;
  31. GString *server_name;
  32. gpointer key;
  33. gpointer keypair;
  34. struct event_base *ev_base;
  35. struct timeval timeout;
  36. struct rspamd_http_connection *http_conn;
  37. gboolean req_sent;
  38. struct rspamd_client_request *req;
  39. struct rspamd_keypair_cache *keys_cache;
  40. };
  41. struct rspamd_client_request {
  42. struct rspamd_client_connection *conn;
  43. struct rspamd_http_message *msg;
  44. GString *input;
  45. rspamd_client_callback cb;
  46. gpointer ud;
  47. };
  48. #define RCLIENT_ERROR rspamd_client_error_quark ()
  49. GQuark
  50. rspamd_client_error_quark (void)
  51. {
  52. return g_quark_from_static_string ("rspamd-client-error");
  53. }
  54. static void
  55. rspamd_client_request_free (struct rspamd_client_request *req)
  56. {
  57. if (req != NULL) {
  58. if (req->conn) {
  59. req->conn->req = NULL;
  60. }
  61. if (req->input) {
  62. g_string_free (req->input, TRUE);
  63. }
  64. g_slice_free1 (sizeof (*req), req);
  65. }
  66. }
  67. static gint
  68. rspamd_client_body_handler (struct rspamd_http_connection *conn,
  69. struct rspamd_http_message *msg,
  70. const gchar *chunk, gsize len)
  71. {
  72. /* Do nothing here */
  73. return 0;
  74. }
  75. static void
  76. rspamd_client_error_handler (struct rspamd_http_connection *conn, GError *err)
  77. {
  78. struct rspamd_client_request *req =
  79. (struct rspamd_client_request *)conn->ud;
  80. struct rspamd_client_connection *c;
  81. c = req->conn;
  82. req->cb (c, NULL, c->server_name->str, NULL, req->input, req->ud, err);
  83. }
  84. static gint
  85. rspamd_client_finish_handler (struct rspamd_http_connection *conn,
  86. struct rspamd_http_message *msg)
  87. {
  88. struct rspamd_client_request *req =
  89. (struct rspamd_client_request *)conn->ud;
  90. struct rspamd_client_connection *c;
  91. struct ucl_parser *parser;
  92. GError *err;
  93. c = req->conn;
  94. if (!c->req_sent) {
  95. c->req_sent = TRUE;
  96. rspamd_http_connection_reset (c->http_conn);
  97. rspamd_http_connection_read_message (c->http_conn,
  98. c->req,
  99. c->fd,
  100. &c->timeout,
  101. c->ev_base);
  102. return 0;
  103. }
  104. else {
  105. if (msg->body == NULL || msg->body_buf.len == 0 || msg->code != 200) {
  106. err = g_error_new (RCLIENT_ERROR, msg->code, "HTTP error: %d, %.*s",
  107. msg->code,
  108. (gint)msg->status->len, msg->status->str);
  109. req->cb (c, msg, c->server_name->str, NULL, req->input, req->ud, err);
  110. g_error_free (err);
  111. return 0;
  112. }
  113. parser = ucl_parser_new (0);
  114. if (!ucl_parser_add_chunk (parser, msg->body_buf.begin, msg->body_buf.len)) {
  115. err = g_error_new (RCLIENT_ERROR, msg->code, "Cannot parse UCL: %s",
  116. ucl_parser_get_error (parser));
  117. ucl_parser_free (parser);
  118. req->cb (c, msg, c->server_name->str, NULL, req->input, req->ud, err);
  119. g_error_free (err);
  120. return 0;
  121. }
  122. req->cb (c, msg, c->server_name->str, ucl_parser_get_object (
  123. parser), req->input, req->ud, NULL);
  124. ucl_parser_free (parser);
  125. }
  126. return 0;
  127. }
  128. struct rspamd_client_connection *
  129. rspamd_client_init (struct event_base *ev_base, const gchar *name,
  130. guint16 port, gdouble timeout, const gchar *key)
  131. {
  132. struct rspamd_client_connection *conn;
  133. gint fd;
  134. fd = rspamd_socket (name, port, SOCK_STREAM, TRUE, FALSE, TRUE);
  135. if (fd == -1) {
  136. return NULL;
  137. }
  138. conn = g_slice_alloc0 (sizeof (struct rspamd_client_connection));
  139. conn->ev_base = ev_base;
  140. conn->fd = fd;
  141. conn->req_sent = FALSE;
  142. conn->keys_cache = rspamd_keypair_cache_new (32);
  143. conn->http_conn = rspamd_http_connection_new (rspamd_client_body_handler,
  144. rspamd_client_error_handler,
  145. rspamd_client_finish_handler,
  146. 0,
  147. RSPAMD_HTTP_CLIENT,
  148. conn->keys_cache);
  149. conn->server_name = g_string_new (name);
  150. if (port != 0) {
  151. rspamd_printf_gstring (conn->server_name, ":%d", (int)port);
  152. }
  153. double_to_tv (timeout, &conn->timeout);
  154. if (key) {
  155. conn->key = rspamd_http_connection_make_peer_key (key);
  156. if (conn->key) {
  157. conn->keypair = rspamd_http_connection_gen_key ();
  158. rspamd_http_connection_set_key (conn->http_conn, conn->keypair);
  159. }
  160. else {
  161. rspamd_client_destroy (conn);
  162. return NULL;
  163. }
  164. }
  165. return conn;
  166. }
  167. gboolean
  168. rspamd_client_command (struct rspamd_client_connection *conn,
  169. const gchar *command, GQueue *attrs,
  170. FILE *in, rspamd_client_callback cb,
  171. gpointer ud, GError **err)
  172. {
  173. struct rspamd_client_request *req;
  174. struct rspamd_http_client_header *nh;
  175. gchar *p;
  176. gsize remain, old_len;
  177. GList *cur;
  178. GString *input = NULL;
  179. req = g_slice_alloc0 (sizeof (struct rspamd_client_request));
  180. req->conn = conn;
  181. req->cb = cb;
  182. req->ud = ud;
  183. req->msg = rspamd_http_new_message (HTTP_REQUEST);
  184. if (conn->key) {
  185. req->msg->peer_key = rspamd_http_connection_key_ref (conn->key);
  186. }
  187. if (in != NULL) {
  188. /* Read input stream */
  189. input = g_string_sized_new (BUFSIZ);
  190. while (!feof (in)) {
  191. p = input->str + input->len;
  192. remain = input->allocated_len - input->len - 1;
  193. if (remain == 0) {
  194. old_len = input->len;
  195. g_string_set_size (input, old_len * 2);
  196. input->len = old_len;
  197. continue;
  198. }
  199. remain = fread (p, 1, remain, in);
  200. if (remain > 0) {
  201. input->len += remain;
  202. input->str[input->len] = '\0';
  203. }
  204. }
  205. if (ferror (in) != 0) {
  206. g_set_error (err, RCLIENT_ERROR, ferror (
  207. in), "input IO error: %s", strerror (ferror (in)));
  208. g_slice_free1 (sizeof (struct rspamd_client_request), req);
  209. g_string_free (input, TRUE);
  210. return FALSE;
  211. }
  212. req->msg->body = rspamd_fstring_new_init (input->str, input->len);
  213. req->input = input;
  214. }
  215. else {
  216. req->msg->body = NULL;
  217. req->input = NULL;
  218. }
  219. /* Convert headers */
  220. cur = attrs->head;
  221. while (cur != NULL) {
  222. nh = cur->data;
  223. rspamd_http_message_add_header (req->msg, nh->name, nh->value);
  224. cur = g_list_next (cur);
  225. }
  226. req->msg->url = rspamd_fstring_append (req->msg->url, "/", 1);
  227. req->msg->url = rspamd_fstring_append (req->msg->url, command, strlen (command));
  228. conn->req = req;
  229. rspamd_http_connection_write_message (conn->http_conn, req->msg, NULL,
  230. "text/plain", req, conn->fd, &conn->timeout, conn->ev_base);
  231. return TRUE;
  232. }
  233. void
  234. rspamd_client_destroy (struct rspamd_client_connection *conn)
  235. {
  236. if (conn != NULL) {
  237. rspamd_http_connection_unref (conn->http_conn);
  238. if (conn->req != NULL) {
  239. rspamd_client_request_free (conn->req);
  240. }
  241. close (conn->fd);
  242. if (conn->key) {
  243. rspamd_http_connection_key_unref (conn->key);
  244. }
  245. if (conn->keypair) {
  246. rspamd_http_connection_key_unref (conn->keypair);
  247. }
  248. g_string_free (conn->server_name, TRUE);
  249. g_slice_free1 (sizeof (struct rspamd_client_connection), conn);
  250. }
  251. }