1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608 |
- /*-
- * Copyright 2016 Vsevolod Stakhov
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include "config.h"
- #include "http.h"
- #include "utlist.h"
- #include "util.h"
- #include "printf.h"
- #include "logger.h"
- #include "ref.h"
- #include "ottery.h"
- #include "keypair_private.h"
- #include "cryptobox.h"
- #include "unix-std.h"
-
- #define ENCRYPTED_VERSION " HTTP/1.0"
-
- struct _rspamd_http_privbuf {
- rspamd_fstring_t *data;
- ref_entry_t ref;
- };
-
- struct rspamd_http_connection_private {
- struct _rspamd_http_privbuf *buf;
- gboolean new_header;
- gboolean encrypted;
- gpointer peer_key;
- struct rspamd_http_keypair *local_key;
- struct rspamd_http_header *header;
- struct http_parser parser;
- struct http_parser_settings parser_cb;
- struct event ev;
- struct timeval tv;
- struct timeval *ptv;
- struct rspamd_http_message *msg;
- struct iovec *out;
- guint outlen;
- gsize wr_pos;
- gsize wr_total;
- };
-
- enum http_magic_type {
- HTTP_MAGIC_PLAIN = 0,
- HTTP_MAGIC_HTML,
- HTTP_MAGIC_CSS,
- HTTP_MAGIC_JS,
- HTTP_MAGIC_PNG,
- HTTP_MAGIC_JPG
- };
-
- static const struct _rspamd_http_magic {
- const gchar *ext;
- const gchar *ct;
- } http_file_types[] = {
- [HTTP_MAGIC_PLAIN] = { "txt", "text/plain" },
- [HTTP_MAGIC_HTML] = { "html", "text/html" },
- [HTTP_MAGIC_CSS] = { "css", "text/css" },
- [HTTP_MAGIC_JS] = { "js", "application/javascript" },
- [HTTP_MAGIC_PNG] = { "png", "image/png" },
- [HTTP_MAGIC_JPG] = { "jpg", "image/jpeg" },
- };
-
- static const gchar *http_week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
- static const gchar *http_month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
- static const rspamd_ftok_t key_header = {
- .begin = "Key",
- .len = 3
- };
- static const rspamd_ftok_t date_header = {
- .begin = "Date",
- .len = 4
- };
-
- #define RSPAMD_HTTP_KEY_ID_LEN 5
-
- #define HTTP_ERROR http_error_quark ()
- GQuark
- http_error_quark (void)
- {
- return g_quark_from_static_string ("http-error-quark");
- }
-
- static void
- rspamd_http_keypair_dtor (struct rspamd_http_keypair *kp)
- {
- rspamd_explicit_memzero (kp->sk, sizeof (kp->sk));
- rspamd_explicit_memzero (kp->nm, sizeof (kp->nm));
- g_slice_free1 (sizeof (*kp), kp);
- }
-
- static void
- rspamd_http_privbuf_dtor (gpointer ud)
- {
- struct _rspamd_http_privbuf *p = (struct _rspamd_http_privbuf *)ud;
-
- if (p->data) {
- rspamd_fstring_free (p->data);
- }
-
- g_slice_free1 (sizeof (struct _rspamd_http_privbuf), p);
- }
-
- static const gchar *
- rspamd_http_code_to_str (gint code)
- {
- if (code == 200) {
- return "OK";
- }
- else if (code == 404) {
- return "Not found";
- }
- else if (code == 403 || code == 401) {
- return "Not authorized";
- }
- else if (code >= 400 && code < 500) {
- return "Bad request";
- }
- else if (code >= 300 && code < 400) {
- return "See Other";
- }
- else if (code >= 500 && code < 600) {
- return "Internal server error";
- }
-
- return "Unknown error";
- }
-
- /*
- * Obtained from nginx
- * Copyright (C) Igor Sysoev
- */
- static guint mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-
- time_t
- rspamd_http_parse_date (const gchar *header, gsize len)
- {
- const gchar *p, *end;
- gint month;
- guint day, year, hour, min, sec;
- guint64 time;
- enum {
- no = 0, rfc822, /* Tue, 10 Nov 2002 23:50:13 */
- rfc850, /* Tuesday, 10-Dec-02 23:50:13 */
- isoc /* Tue Dec 10 23:50:13 2002 */
- } fmt;
-
- fmt = 0;
- if (len > 0) {
- end = header + len;
- }
- else {
- end = header + strlen (header);
- }
-
- day = 32;
- year = 2038;
-
- for (p = header; p < end; p++) {
- if (*p == ',') {
- break;
- }
-
- if (*p == ' ') {
- fmt = isoc;
- break;
- }
- }
-
- for (p++; p < end; p++)
- if (*p != ' ') {
- break;
- }
-
- if (end - p < 18) {
- return (time_t)-1;
- }
-
- if (fmt != isoc) {
- if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
- return (time_t)-1;
- }
-
- day = (*p - '0') * 10 + *(p + 1) - '0';
- p += 2;
-
- if (*p == ' ') {
- if (end - p < 18) {
- return (time_t)-1;
- }
- fmt = rfc822;
-
- }
- else if (*p == '-') {
- fmt = rfc850;
-
- }
- else {
- return (time_t)-1;
- }
-
- p++;
- }
-
- switch (*p) {
-
- case 'J':
- month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6;
- break;
-
- case 'F':
- month = 1;
- break;
-
- case 'M':
- month = *(p + 2) == 'r' ? 2 : 4;
- break;
-
- case 'A':
- month = *(p + 1) == 'p' ? 3 : 7;
- break;
-
- case 'S':
- month = 8;
- break;
-
- case 'O':
- month = 9;
- break;
-
- case 'N':
- month = 10;
- break;
-
- case 'D':
- month = 11;
- break;
-
- default:
- return (time_t)-1;
- }
-
- p += 3;
-
- if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) {
- return (time_t)-1;
- }
-
- p++;
-
- if (fmt == rfc822) {
- if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
- || *(p + 2) < '0' || *(p + 2) > '9' || *(p + 3) < '0'
- || *(p + 3) > '9') {
- return (time_t)-1;
- }
-
- year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
- + (*(p + 2) - '0') * 10 + *(p + 3) - '0';
- p += 4;
-
- }
- else if (fmt == rfc850) {
- if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
- return (time_t)-1;
- }
-
- year = (*p - '0') * 10 + *(p + 1) - '0';
- year += (year < 70) ? 2000 : 1900;
- p += 2;
- }
-
- if (fmt == isoc) {
- if (*p == ' ') {
- p++;
- }
-
- if (*p < '0' || *p > '9') {
- return (time_t)-1;
- }
-
- day = *p++ - '0';
-
- if (*p != ' ') {
- if (*p < '0' || *p > '9') {
- return (time_t)-1;
- }
-
- day = day * 10 + *p++ - '0';
- }
-
- if (end - p < 14) {
- return (time_t)-1;
- }
- }
-
- if (*p++ != ' ') {
- return (time_t)-1;
- }
-
- if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
- return (time_t)-1;
- }
-
- hour = (*p - '0') * 10 + *(p + 1) - '0';
- p += 2;
-
- if (*p++ != ':') {
- return (time_t)-1;
- }
-
- if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
- return (time_t)-1;
- }
-
- min = (*p - '0') * 10 + *(p + 1) - '0';
- p += 2;
-
- if (*p++ != ':') {
- return (time_t)-1;
- }
-
- if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
- return (time_t)-1;
- }
-
- sec = (*p - '0') * 10 + *(p + 1) - '0';
-
- if (fmt == isoc) {
- p += 2;
-
- if (*p++ != ' ') {
- return (time_t)-1;
- }
-
- if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
- || *(p + 2) < '0' || *(p + 2) > '9' || *(p + 3) < '0'
- || *(p + 3) > '9') {
- return (time_t)-1;
- }
-
- year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
- + (*(p + 2) - '0') * 10 + *(p + 3) - '0';
- }
-
- if (hour > 23 || min > 59 || sec > 59) {
- return (time_t)-1;
- }
-
- if (day == 29 && month == 1) {
- if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) {
- return (time_t)-1;
- }
-
- }
- else if (day > mday[month]) {
- return (time_t)-1;
- }
-
- /*
- * shift new year to March 1 and start months from 1 (not 0),
- * it is needed for Gauss' formula
- */
-
- if (--month <= 0) {
- month += 12;
- year -= 1;
- }
-
- /* Gauss' formula for Gregorian days since March 1, 1 BC */
-
- time = (guint64) (
- /* days in years including leap years since March 1, 1 BC */
-
- 365 * year + year / 4 - year / 100 + year / 400
-
- /* days before the month */
-
- + 367 * month / 12 - 30
-
- /* days before the day */
-
- + day - 1
-
- /*
- * 719527 days were between March 1, 1 BC and March 1, 1970,
- * 31 and 28 days were in January and February 1970
- */
-
- - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec;
-
- return (time_t) time;
- }
-
- static void
- rspamd_http_parse_key (rspamd_ftok_t *data, struct rspamd_http_connection *conn,
- struct rspamd_http_connection_private *priv)
- {
- guchar *decoded_id, *decoded_key;
- const gchar *eq_pos;
- gsize id_len, key_len;
- struct rspamd_http_keypair *kp;
-
- if (priv->local_key == NULL) {
- /* In this case we cannot do anything, e.g. we cannot decrypt payload */
- priv->encrypted = TRUE;
- }
- else {
- /* Check sanity of what we have */
- eq_pos = memchr (data->begin, '=', data->len);
- if (eq_pos != NULL) {
- decoded_id = rspamd_decode_base32 (data->begin, eq_pos - data->begin,
- &id_len);
- decoded_key = rspamd_decode_base32 (eq_pos + 1, data->begin + data->len -
- eq_pos - 1, &key_len);
- if (decoded_id != NULL && decoded_key != NULL) {
- if (id_len >= RSPAMD_HTTP_KEY_ID_LEN &&
- key_len >= rspamd_cryptobox_pk_bytes ()) {
- if (memcmp (priv->local_key->id, decoded_id,
- RSPAMD_HTTP_KEY_ID_LEN) == 0) {
- kp = g_slice_alloc0 (sizeof (*kp));
- REF_INIT_RETAIN (kp, rspamd_http_keypair_dtor);
- memcpy (kp->pk, decoded_key, rspamd_cryptobox_pk_bytes ());
- priv->msg->peer_key = kp;
-
- if (conn->cache && priv->msg->peer_key) {
- rspamd_keypair_cache_process (conn->cache,
- priv->local_key, priv->msg->peer_key);
- }
- }
- }
- }
- priv->encrypted = TRUE;
- g_free (decoded_key);
- g_free (decoded_id);
- }
- }
- }
-
- static inline void
- rspamd_http_check_special_header (struct rspamd_http_connection *conn,
- struct rspamd_http_connection_private *priv)
- {
- if (rspamd_ftok_casecmp (priv->header->name, &date_header) == 0) {
- priv->msg->date = rspamd_http_parse_date (priv->header->value->begin,
- priv->header->value->len);
- }
- else if (rspamd_ftok_casecmp (priv->header->name, &key_header) == 0) {
- rspamd_http_parse_key (priv->header->value, conn, priv);
- }
- }
-
- static gint
- rspamd_http_on_url (http_parser * parser, const gchar *at, size_t length)
- {
- struct rspamd_http_connection *conn =
- (struct rspamd_http_connection *)parser->data;
- struct rspamd_http_connection_private *priv;
-
- priv = conn->priv;
-
- priv->msg->url = rspamd_fstring_append (priv->msg->url, at, length);
-
- return 0;
- }
-
- static gint
- rspamd_http_on_status (http_parser * parser, const gchar *at, size_t length)
- {
- struct rspamd_http_connection *conn =
- (struct rspamd_http_connection *)parser->data;
- struct rspamd_http_connection_private *priv;
-
- priv = conn->priv;
-
- if (parser->status_code != 200) {
- if (priv->msg->status == NULL) {
- priv->msg->status = rspamd_fstring_new ();
- }
-
- priv->msg->status = rspamd_fstring_append (priv->msg->status, at, length);
- }
-
- return 0;
- }
-
- static void
- rspamd_http_finish_header (struct rspamd_http_connection *conn,
- struct rspamd_http_connection_private *priv)
- {
- priv->header->combined = rspamd_fstring_append (priv->header->combined,
- "\r\n", 2);
- priv->header->value->len = priv->header->combined->len -
- priv->header->name->len - 4;
- priv->header->value->begin = priv->header->combined->str +
- priv->header->name->len + 2;
- priv->header->name->begin = priv->header->combined->str;
- DL_APPEND (priv->msg->headers, priv->header);
-
- rspamd_http_check_special_header (conn, priv);
- }
-
- static void
- rspamd_http_init_header (struct rspamd_http_connection_private *priv)
- {
- priv->header = g_slice_alloc (sizeof (struct rspamd_http_header));
- priv->header->name = g_slice_alloc0 (sizeof (*priv->header->name));
- priv->header->value = g_slice_alloc0 (sizeof (*priv->header->value));
- priv->header->combined = rspamd_fstring_new ();
- }
-
- static gint
- rspamd_http_on_header_field (http_parser * parser,
- const gchar *at,
- size_t length)
- {
- struct rspamd_http_connection *conn =
- (struct rspamd_http_connection *)parser->data;
- struct rspamd_http_connection_private *priv;
-
- priv = conn->priv;
-
- if (priv->header == NULL) {
- rspamd_http_init_header (priv);
- }
- else if (priv->new_header) {
- rspamd_http_finish_header (conn, priv);
- rspamd_http_init_header (priv);
- }
-
- priv->new_header = FALSE;
- priv->header->combined = rspamd_fstring_append (priv->header->combined,
- at, length);
-
- return 0;
- }
-
- static gint
- rspamd_http_on_header_value (http_parser * parser,
- const gchar *at,
- size_t length)
- {
- struct rspamd_http_connection *conn =
- (struct rspamd_http_connection *)parser->data;
- struct rspamd_http_connection_private *priv;
-
- priv = conn->priv;
-
- if (priv->header == NULL) {
- /* Should not happen */
- return -1;
- }
-
- if (!priv->new_header) {
- priv->new_header = TRUE;
- priv->header->combined = rspamd_fstring_append (priv->header->combined,
- ": ", 2);
- priv->header->name->len = priv->header->combined->len - 2;
- }
-
- priv->header->combined = rspamd_fstring_append (priv->header->combined,
- at, length);
-
- return 0;
- }
-
- static int
- rspamd_http_on_headers_complete (http_parser * parser)
- {
- struct rspamd_http_connection *conn =
- (struct rspamd_http_connection *)parser->data;
- struct rspamd_http_connection_private *priv;
-
- priv = conn->priv;
-
- if (priv->header != NULL) {
- rspamd_http_finish_header (conn, priv);
-
- priv->header = NULL;
- priv->new_header = FALSE;
- }
-
- if (parser->content_length != 0 && parser->content_length != ULLONG_MAX) {
- priv->msg->body = rspamd_fstring_sized_new (parser->content_length);
- }
- else {
- priv->msg->body = rspamd_fstring_new ();
- }
-
- if (parser->flags & F_SPAMC) {
- priv->msg->flags |= RSPAMD_HTTP_FLAG_SPAMC;
- }
-
- priv->msg->body_buf.begin = priv->msg->body->str;
- priv->msg->method = parser->method;
- priv->msg->code = parser->status_code;
-
- return 0;
- }
-
- static int
- rspamd_http_on_body (http_parser * parser, const gchar *at, size_t length)
- {
- struct rspamd_http_connection *conn =
- (struct rspamd_http_connection *)parser->data;
- struct rspamd_http_connection_private *priv;
-
- priv = conn->priv;
-
- priv->msg->body = rspamd_fstring_append (priv->msg->body, at, length);
-
- /* Append might cause realloc */
- priv->msg->body_buf.begin = priv->msg->body->str;
- priv->msg->body_buf.len = priv->msg->body->len;
-
- if ((conn->opts & RSPAMD_HTTP_BODY_PARTIAL) && !priv->encrypted) {
- /* Incremental update is impossible for encrypted requests so far */
- return (conn->body_handler (conn, priv->msg, at, length));
- }
-
- return 0;
- }
-
- static int
- rspamd_http_on_body_decrypted (http_parser * parser, const gchar *at, size_t length)
- {
- struct rspamd_http_connection *conn =
- (struct rspamd_http_connection *)parser->data;
- struct rspamd_http_connection_private *priv;
-
- priv = conn->priv;
-
- if (priv->header != NULL) {
- rspamd_http_finish_header (conn, priv);
- priv->header = NULL;
- }
-
- if (priv->msg->body_buf.len == 0) {
-
- priv->msg->body_buf.begin = at;
- priv->msg->method = parser->method;
- priv->msg->code = parser->status_code;
- }
-
- priv->msg->body_buf.len += length;
-
- return 0;
- }
-
- static int
- rspamd_http_on_headers_complete_decrypted (http_parser *parser)
- {
- struct rspamd_http_connection *conn =
- (struct rspamd_http_connection *) parser->data;
- struct rspamd_http_connection_private *priv;
-
- priv = conn->priv;
-
- if (priv->header != NULL) {
- rspamd_http_finish_header (conn, priv);
-
- priv->header = NULL;
- priv->new_header = FALSE;
- }
-
- if (parser->flags & F_SPAMC) {
- priv->msg->flags |= RSPAMD_HTTP_FLAG_SPAMC;
- }
-
- priv->msg->method = parser->method;
- priv->msg->code = parser->status_code;
-
- return 0;
- }
-
- static int
- rspamd_http_decrypt_message (struct rspamd_http_connection *conn,
- struct rspamd_http_connection_private *priv,
- struct rspamd_http_keypair *peer_key)
- {
- guchar *nonce, *m;
- gsize dec_len;
- struct rspamd_http_message *msg = priv->msg;
- struct rspamd_http_header *hdr, *hdrtmp;
- struct http_parser decrypted_parser;
- struct http_parser_settings decrypted_cb;
-
- nonce = msg->body->str;
- m = msg->body->str + rspamd_cryptobox_nonce_bytes () +
- rspamd_cryptobox_mac_bytes ();
- dec_len = msg->body->len - rspamd_cryptobox_nonce_bytes () -
- rspamd_cryptobox_mac_bytes ();
-
- if (!peer_key->has_nm) {
- /* We still save NM for the following encryption */
- rspamd_cryptobox_nm (peer_key->nm, peer_key->pk, priv->local_key->sk);
- peer_key->has_nm = TRUE;
- }
-
- if (!rspamd_cryptobox_decrypt_nm_inplace (m, dec_len, nonce,
- peer_key->nm, m - rspamd_cryptobox_mac_bytes ())) {
- msg_err ("cannot verify encrypted message");
- return -1;
- }
-
- /* Cleanup message */
- DL_FOREACH_SAFE (msg->headers, hdr, hdrtmp) {
- rspamd_fstring_free (hdr->combined);
- g_slice_free1 (sizeof (*hdr->name), hdr->name);
- g_slice_free1 (sizeof (*hdr->value), hdr->value);
- g_slice_free1 (sizeof (struct rspamd_http_header), hdr);
- }
-
- msg->headers = NULL;
-
- if (msg->url != NULL) {
- msg->url = rspamd_fstring_assign (msg->url, "", 0);
- }
-
- msg->body_buf.len = 0;
-
- http_parser_init (&decrypted_parser,
- conn->type == RSPAMD_HTTP_SERVER ? HTTP_REQUEST : HTTP_RESPONSE);
-
- memset (&decrypted_cb, 0, sizeof (decrypted_cb));
- decrypted_cb.on_url = rspamd_http_on_url;
- decrypted_cb.on_status = rspamd_http_on_status;
- decrypted_cb.on_header_field = rspamd_http_on_header_field;
- decrypted_cb.on_header_value = rspamd_http_on_header_value;
- decrypted_cb.on_headers_complete = rspamd_http_on_headers_complete_decrypted;
- decrypted_cb.on_body = rspamd_http_on_body_decrypted;
- decrypted_parser.data = conn;
- decrypted_parser.content_length = dec_len;
-
- if (http_parser_execute (&decrypted_parser, &decrypted_cb, m,
- dec_len) != (size_t)dec_len) {
- msg_err ("HTTP parser error: %s when parsing encrypted request",
- http_errno_description (decrypted_parser.http_errno));
- return -1;
- }
-
- return 0;
- }
-
- static int
- rspamd_http_on_message_complete (http_parser * parser)
- {
- struct rspamd_http_connection *conn =
- (struct rspamd_http_connection *)parser->data;
- struct rspamd_http_connection_private *priv;
- int ret = 0;
- struct rspamd_http_keypair *peer_key = NULL;
-
- priv = conn->priv;
-
- if ((conn->opts & RSPAMD_HTTP_BODY_PARTIAL) == 0 && priv->encrypted) {
- if (priv->local_key == NULL || priv->msg->peer_key == NULL ||
- priv->msg->body->len < rspamd_cryptobox_nonce_bytes () +
- rspamd_cryptobox_mac_bytes ()) {
- msg_err ("cannot decrypt message");
- return -1;
- }
-
- /* We have keys, so we can decrypt message */
- peer_key = (struct rspamd_http_keypair *)priv->msg->peer_key;
- ret = rspamd_http_decrypt_message (conn, priv, peer_key);
-
- if (ret != 0) {
- return ret;
- }
-
- if (conn->body_handler != NULL) {
- rspamd_http_connection_ref (conn);
- ret = conn->body_handler (conn,
- priv->msg,
- priv->msg->body_buf.begin,
- priv->msg->body_buf.len);
- rspamd_http_connection_unref (conn);
- }
- }
- else if ((conn->opts & RSPAMD_HTTP_BODY_PARTIAL) == 0 && conn->body_handler) {
- g_assert (conn->body_handler != NULL);
- rspamd_http_connection_ref (conn);
- ret = conn->body_handler (conn,
- priv->msg,
- priv->msg->body_buf.begin,
- priv->msg->body_buf.len);
- rspamd_http_connection_unref (conn);
- }
-
- if (ret == 0) {
- if (event_pending (&priv->ev, EV_READ, NULL)) {
- event_del (&priv->ev);
- }
-
- rspamd_http_connection_ref (conn);
- ret = conn->finish_handler (conn, priv->msg);
- conn->finished = TRUE;
- rspamd_http_connection_unref (conn);
- }
-
- return ret;
- }
-
- static void
- rspamd_http_simple_client_helper (struct rspamd_http_connection *conn)
- {
- struct event_base *base;
-
- base = conn->priv->ev.ev_base;
- rspamd_http_connection_reset (conn);
- /* Plan read message */
- rspamd_http_connection_read_message (conn, conn->ud, conn->fd,
- conn->priv->ptv, base);
- }
-
- static void
- rspamd_http_write_helper (struct rspamd_http_connection *conn)
- {
- struct rspamd_http_connection_private *priv;
- struct iovec *start;
- guint niov, i;
- gint flags = 0;
- gsize remain;
- gssize r;
- GError *err;
- struct iovec *cur_iov;
- struct msghdr msg;
-
- priv = conn->priv;
-
- if (priv->wr_pos == priv->wr_total) {
- goto call_finish_handler;
- }
-
- start = &priv->out[0];
- niov = priv->outlen;
- remain = priv->wr_pos;
- /* We know that niov is small enough for that */
- cur_iov = alloca (niov * sizeof (struct iovec));
- memcpy (cur_iov, priv->out, niov * sizeof (struct iovec));
- for (i = 0; i < priv->outlen && remain > 0; i++) {
- /* Find out the first iov required */
- start = &cur_iov[i];
- if (start->iov_len <= remain) {
- remain -= start->iov_len;
- start = &cur_iov[i + 1];
- niov--;
- }
- else {
- start->iov_base = (void *)((char *)start->iov_base + remain);
- start->iov_len -= remain;
- remain = 0;
- }
- }
-
- memset (&msg, 0, sizeof (msg));
- msg.msg_iov = start;
- msg.msg_iovlen = MIN (IOV_MAX, niov);
- g_assert (niov > 0);
- #ifdef MSG_NOSIGNAL
- flags = MSG_NOSIGNAL;
- #endif
- r = sendmsg (conn->fd, &msg, flags);
-
- if (r == -1) {
- err =
- g_error_new (HTTP_ERROR, errno, "IO write error: %s", strerror (
- errno));
- rspamd_http_connection_ref (conn);
- conn->error_handler (conn, err);
- rspamd_http_connection_unref (conn);
- g_error_free (err);
- return;
- }
- else {
- priv->wr_pos += r;
- }
-
- if (priv->wr_pos >= priv->wr_total) {
- goto call_finish_handler;
- }
- else {
- /* Want to write more */
- event_add (&priv->ev, priv->ptv);
- }
-
- return;
-
- call_finish_handler:
- if ((conn->opts & RSPAMD_HTTP_CLIENT_SIMPLE) == 0) {
- rspamd_http_connection_ref (conn);
- conn->finished = TRUE;
- conn->finish_handler (conn, priv->msg);
- rspamd_http_connection_unref (conn);
- }
- else {
- /* Plan read message */
- rspamd_http_simple_client_helper (conn);
- }
- }
-
- static gssize
- rspamd_http_try_read (gint fd,
- struct rspamd_http_connection *conn,
- struct rspamd_http_connection_private *priv,
- struct _rspamd_http_privbuf *pbuf)
- {
- gssize r;
- rspamd_fstring_t *buf;
-
- buf = priv->buf->data;
- r = read (fd, buf->str, buf->allocated);
-
- if (r <= 0) {
- return r;
- }
- else {
- buf->len = r;
- }
-
- return r;
- }
-
- static void
- rspamd_http_event_handler (int fd, short what, gpointer ud)
- {
- struct rspamd_http_connection *conn = (struct rspamd_http_connection *)ud;
- struct rspamd_http_connection_private *priv;
- struct _rspamd_http_privbuf *pbuf;
- rspamd_fstring_t *buf;
- gssize r;
- GError *err;
-
- priv = conn->priv;
- pbuf = priv->buf;
- REF_RETAIN (pbuf);
- rspamd_http_connection_ref (conn);
- buf = priv->buf->data;
-
- if (what == EV_READ) {
- r = rspamd_http_try_read (fd, conn, priv, pbuf);
-
- if (r > 0) {
- if (http_parser_execute (&priv->parser, &priv->parser_cb,
- buf->str, r) != (size_t)r || priv->parser.http_errno != 0) {
- err = g_error_new (HTTP_ERROR, priv->parser.http_errno,
- "HTTP parser error: %s",
- http_errno_description (priv->parser.http_errno));
- conn->error_handler (conn, err);
- g_error_free (err);
-
- REF_RELEASE (pbuf);
- rspamd_http_connection_unref (conn);
-
- return;
- }
- }
- else if (r == 0) {
- if (!conn->finished) {
- err = g_error_new (HTTP_ERROR,
- errno,
- "IO read error: unexpected EOF");
- conn->error_handler (conn, err);
- g_error_free (err);
-
-
- }
- REF_RELEASE (pbuf);
- rspamd_http_connection_unref (conn);
-
- return;
- }
- else {
- err = g_error_new (HTTP_ERROR,
- errno,
- "IO read error: %s",
- strerror (errno));
- conn->error_handler (conn, err);
- g_error_free (err);
-
- REF_RELEASE (pbuf);
- rspamd_http_connection_unref (conn);
-
- return;
- }
- }
- else if (what == EV_TIMEOUT) {
- /* Let's try to read from the socket first */
- r = rspamd_http_try_read (fd, conn, priv, pbuf);
-
- if (r > 0) {
- if (http_parser_execute (&priv->parser, &priv->parser_cb,
- buf->str, r) != (size_t)r || priv->parser.http_errno != 0) {
- err = g_error_new (HTTP_ERROR, priv->parser.http_errno,
- "HTTP parser error: %s",
- http_errno_description (priv->parser.http_errno));
- conn->error_handler (conn, err);
- g_error_free (err);
-
- REF_RELEASE (pbuf);
- rspamd_http_connection_unref (conn);
-
- return;
- }
- }
- else if (r == 0) {
- if (!conn->finished) {
- err = g_error_new (HTTP_ERROR, ETIMEDOUT,
- "IO timeout");
- conn->error_handler (conn, err);
- g_error_free (err);
-
- }
- REF_RELEASE (pbuf);
- rspamd_http_connection_unref (conn);
-
- return;
- }
- else {
- err = g_error_new (HTTP_ERROR, ETIMEDOUT,
- "IO timeout");
- conn->error_handler (conn, err);
- g_error_free (err);
-
- REF_RELEASE (pbuf);
- rspamd_http_connection_unref (conn);
-
- return;
- }
- }
- else if (what == EV_WRITE) {
- rspamd_http_write_helper (conn);
- }
-
- REF_RELEASE (pbuf);
- rspamd_http_connection_unref (conn);
- }
-
- static void
- rspamd_http_parser_reset (struct rspamd_http_connection *conn)
- {
- struct rspamd_http_connection_private *priv = conn->priv;
-
- http_parser_init (&priv->parser,
- conn->type == RSPAMD_HTTP_SERVER ? HTTP_REQUEST : HTTP_RESPONSE);
-
- priv->parser_cb.on_url = rspamd_http_on_url;
- priv->parser_cb.on_status = rspamd_http_on_status;
- priv->parser_cb.on_header_field = rspamd_http_on_header_field;
- priv->parser_cb.on_header_value = rspamd_http_on_header_value;
- priv->parser_cb.on_headers_complete = rspamd_http_on_headers_complete;
- priv->parser_cb.on_body = rspamd_http_on_body;
- priv->parser_cb.on_message_complete = rspamd_http_on_message_complete;
- }
-
- struct rspamd_http_connection *
- rspamd_http_connection_new (rspamd_http_body_handler_t body_handler,
- rspamd_http_error_handler_t error_handler,
- rspamd_http_finish_handler_t finish_handler,
- unsigned opts,
- enum rspamd_http_connection_type type,
- struct rspamd_keypair_cache *cache)
- {
- struct rspamd_http_connection *new;
- struct rspamd_http_connection_private *priv;
-
- if (error_handler == NULL || finish_handler == NULL) {
- return NULL;
- }
-
- new = g_slice_alloc0 (sizeof (struct rspamd_http_connection));
- new->opts = opts;
- new->type = type;
- new->body_handler = body_handler;
- new->error_handler = error_handler;
- new->finish_handler = finish_handler;
- new->fd = -1;
- new->ref = 1;
- new->finished = FALSE;
- new->cache = cache;
-
- /* Init priv */
- priv = g_slice_alloc0 (sizeof (struct rspamd_http_connection_private));
- new->priv = priv;
-
- rspamd_http_parser_reset (new);
- priv->parser.data = new;
-
- return new;
- }
-
- void
- rspamd_http_connection_reset (struct rspamd_http_connection *conn)
- {
- struct rspamd_http_connection_private *priv;
- struct rspamd_http_message *msg;
-
- priv = conn->priv;
- msg = priv->msg;
-
- /* Clear request */
- if (msg != NULL) {
- if (msg->peer_key) {
- priv->peer_key = msg->peer_key;
- msg->peer_key = NULL;
- }
- rspamd_http_message_free (msg);
- priv->msg = NULL;
- }
-
- conn->finished = FALSE;
- /* Clear priv */
- event_del (&priv->ev);
-
- if (priv->buf != NULL) {
- REF_RELEASE (priv->buf);
- priv->buf = NULL;
- }
-
- rspamd_http_parser_reset (conn);
-
- if (priv->out != NULL) {
- g_slice_free1 (sizeof (struct iovec) * priv->outlen, priv->out);
- priv->out = NULL;
- }
- }
-
- struct rspamd_http_message *
- rspamd_http_connection_steal_msg (struct rspamd_http_connection *conn)
- {
- struct rspamd_http_connection_private *priv;
- struct rspamd_http_message *msg;
-
- priv = conn->priv;
- msg = priv->msg;
-
- /* Clear request */
- if (msg != NULL) {
- if (msg->peer_key) {
- priv->peer_key = msg->peer_key;
- msg->peer_key = NULL;
- }
- priv->msg = NULL;
- }
-
- return msg;
- }
-
- void
- rspamd_http_connection_free (struct rspamd_http_connection *conn)
- {
- struct rspamd_http_connection_private *priv;
- struct rspamd_http_keypair *peer_key;
-
- priv = conn->priv;
-
- if (priv != NULL) {
- rspamd_http_connection_reset (conn);
-
- if (priv->local_key) {
- REF_RELEASE (priv->local_key);
- }
- if (priv->peer_key) {
- peer_key = (struct rspamd_http_keypair *)priv->peer_key;
- REF_RELEASE (peer_key);
- }
-
- g_slice_free1 (sizeof (struct rspamd_http_connection_private), priv);
- }
-
- g_slice_free1 (sizeof (struct rspamd_http_connection), conn);
- }
-
- void
- rspamd_http_connection_read_message (struct rspamd_http_connection *conn,
- gpointer ud, gint fd, struct timeval *timeout, struct event_base *base)
- {
- struct rspamd_http_connection_private *priv = conn->priv;
- struct rspamd_http_message *req;
-
- conn->fd = fd;
- conn->ud = ud;
- req = rspamd_http_new_message (
- conn->type == RSPAMD_HTTP_SERVER ? HTTP_REQUEST : HTTP_RESPONSE);
- priv->msg = req;
-
- if (priv->peer_key) {
- priv->msg->peer_key = priv->peer_key;
- priv->peer_key = NULL;
- priv->encrypted = TRUE;
- }
-
- if (timeout == NULL) {
- priv->ptv = NULL;
- }
- else if (&priv->tv != timeout) {
- memcpy (&priv->tv, timeout, sizeof (struct timeval));
- priv->ptv = &priv->tv;
- }
-
- priv->header = NULL;
- priv->buf = g_slice_alloc0 (sizeof (*priv->buf));
- REF_INIT_RETAIN (priv->buf, rspamd_http_privbuf_dtor);
- priv->buf->data = rspamd_fstring_sized_new (8192);
- priv->new_header = TRUE;
-
- event_set (&priv->ev,
- fd,
- EV_READ | EV_PERSIST,
- rspamd_http_event_handler,
- conn);
- if (base != NULL) {
- event_base_set (base, &priv->ev);
- }
- event_add (&priv->ev, priv->ptv);
- }
-
- static void
- rspamd_http_connection_encrypt_message (
- struct rspamd_http_connection *conn,
- struct rspamd_http_message *msg,
- struct rspamd_http_connection_private *priv,
- guchar *pbody,
- guint bodylen,
- guchar *pmethod,
- guint methodlen,
- guint preludelen,
- gint hdrcount,
- guchar *np,
- guchar *mp,
- struct rspamd_http_keypair *peer_key)
- {
- struct rspamd_cryptobox_segment *segments;
- guchar *crlfp = mp + rspamd_cryptobox_mac_bytes ();
- gint i, cnt;
- guint outlen;
- struct rspamd_http_header *hdr;
-
- outlen = priv->out[0].iov_len + priv->out[1].iov_len;
- /*
- * Create segments from the following:
- * Method, [URL], CRLF, nheaders, CRLF, body
- */
- segments = g_new (struct rspamd_cryptobox_segment, hdrcount + 5);
-
- segments[0].data = pmethod;
- segments[0].len = methodlen;
-
- if (conn->type != RSPAMD_HTTP_SERVER) {
- segments[1].data = msg->url->str;
- segments[1].len = msg->url->len;
- /* space + HTTP version + crlf */
- segments[2].data = crlfp;
- segments[2].len = preludelen - 2;
- crlfp += segments[2].len;
- i = 3;
- }
- else {
- /* Here we send just CRLF */
- segments[1].data = crlfp;
- segments[1].len = 2;
- crlfp += segments[1].len;
-
- i = 2;
- }
-
-
- LL_FOREACH (msg->headers, hdr) {
- segments[i].data = hdr->combined->str;
- segments[i++].len = hdr->combined->len;
- }
-
- /* crlfp should point now at the second crlf */
- segments[i].data = crlfp;
- segments[i++].len = 2;
-
- if (pbody) {
- segments[i].data = pbody;
- segments[i++].len = bodylen;
- }
-
- cnt = i;
-
- if (!peer_key->has_nm) {
- rspamd_cryptobox_nm (peer_key->nm, peer_key->pk, priv->local_key->sk);
- peer_key->has_nm = TRUE;
- }
-
- rspamd_cryptobox_encryptv_nm_inplace (segments,
- cnt,
- np,
- peer_key->nm, mp);
-
- /*
- * iov[0] = base HTTP request
- * iov[1] = CRLF
- * iov[2] = nonce
- * iov[3] = mac
- * iov[4..i] = encrypted HTTP request/reply
- */
- priv->out[2].iov_base = np;
- priv->out[2].iov_len = rspamd_cryptobox_nonce_bytes ();
- priv->out[3].iov_base = mp;
- priv->out[3].iov_len = rspamd_cryptobox_mac_bytes ();
-
- outlen += rspamd_cryptobox_nonce_bytes () + rspamd_cryptobox_mac_bytes ();
-
- for (i = 0; i < cnt; i ++) {
- priv->out[i + 4].iov_base = segments[i].data;
- priv->out[i + 4].iov_len = segments[i].len;
- outlen += segments[i].len;
- }
-
- priv->wr_total = outlen;
-
- g_free (segments);
- }
-
- void
- rspamd_http_connection_write_message (struct rspamd_http_connection *conn,
- struct rspamd_http_message *msg, const gchar *host, const gchar *mime_type,
- gpointer ud, gint fd, struct timeval *timeout, struct event_base *base)
- {
- struct rspamd_http_connection_private *priv = conn->priv;
- struct rspamd_http_header *hdr;
- struct tm t, *ptm;
- gchar datebuf[64], repbuf[512], *pbody;
- gint i, hdrcount, meth_len = 0, preludelen = 0;
- gsize bodylen, enclen = 0;
- rspamd_fstring_t *buf;
- gboolean encrypted = FALSE;
- gchar *b32_key, *b32_id;
- guchar nonce[rspamd_cryptobox_MAX_NONCEBYTES], mac[rspamd_cryptobox_MAX_MACBYTES],
- id[rspamd_cryptobox_HASHBYTES];
- guchar *np = NULL, *mp = NULL, *meth_pos = NULL;
- struct rspamd_http_keypair *peer_key = NULL;
-
- conn->fd = fd;
- conn->ud = ud;
- priv->msg = msg;
-
- if (timeout == NULL) {
- priv->ptv = NULL;
- }
- else if (timeout != &priv->tv) {
- memcpy (&priv->tv, timeout, sizeof (struct timeval));
- priv->ptv = &priv->tv;
- }
-
- priv->header = NULL;
- priv->buf = g_slice_alloc0 (sizeof (*priv->buf));
- REF_INIT_RETAIN (priv->buf, rspamd_http_privbuf_dtor);
- priv->buf->data = rspamd_fstring_sized_new (512);
- buf = priv->buf->data;
-
- if (priv->peer_key && priv->local_key) {
- priv->msg->peer_key = priv->peer_key;
- priv->peer_key = NULL;
- priv->encrypted = TRUE;
- }
-
- if (priv->local_key != NULL && msg->peer_key != NULL) {
- encrypted = TRUE;
- if (conn->cache) {
- rspamd_keypair_cache_process (conn->cache,
- priv->local_key, priv->msg->peer_key);
- }
- }
-
- if (encrypted) {
- if (msg->body == NULL || msg->body->len == 0) {
- pbody = NULL;
- bodylen = 0;
- msg->method = HTTP_GET;
- }
- else {
- pbody = msg->body->str;
- bodylen = msg->body->len;
- msg->method = HTTP_POST;
- }
-
- if (conn->type == RSPAMD_HTTP_SERVER) {
- /*
- * iov[0] = base reply
- * iov[1] = CRLF
- * iov[2] = nonce
- * iov[3] = mac
- * iov[4] = encrypted reply
- * iov[6] = encrypted crlf
- * iov[7..n] = encrypted headers
- * iov[n + 1] = encrypted crlf
- * [iov[n + 2] = encrypted body]
- */
- priv->outlen = 7;
- enclen = rspamd_cryptobox_nonce_bytes () +
- rspamd_cryptobox_mac_bytes () +
- 4 + /* 2 * CRLF */
- bodylen;
- }
- else {
- /*
- * iov[0] = base request
- * iov[1] = CRLF
- * iov[2] = nonce
- * iov[3] = mac
- * iov[4] = encrypted method + space
- * iov[5] = encrypted url
- * iov[7] = encrypted prelude
- * iov[8..n] = encrypted headers
- * iov[n + 1] = encrypted crlf
- * [iov[n + 2] = encrypted body]
- */
- priv->outlen = 8;
-
- if (bodylen > 0) {
- preludelen = rspamd_snprintf (repbuf, sizeof (repbuf), "%s\r\n"
- "Content-Length: %z\r\n\r\n", ENCRYPTED_VERSION, bodylen);
- }
- else {
- preludelen = rspamd_snprintf (repbuf, sizeof (repbuf), "%s\r\n\r\n",
- ENCRYPTED_VERSION);
- }
-
- enclen = rspamd_cryptobox_nonce_bytes () +
- rspamd_cryptobox_mac_bytes () +
- preludelen + /* version [content-length] + 2 * CRLF */
- bodylen;
- }
-
- if (bodylen > 0) {
- priv->outlen ++;
- }
- }
- else {
- if (msg->method < HTTP_SYMBOLS) {
- if (msg->body == NULL || msg->body->len == 0) {
- pbody = NULL;
- bodylen = 0;
- priv->outlen = 2;
- msg->method = HTTP_GET;
- }
- else {
- pbody = msg->body->str;
- bodylen = msg->body->len;
- priv->outlen = 3;
- msg->method = HTTP_POST;
- }
- }
- else if (msg->body != NULL) {
- pbody = msg->body->str;
- bodylen = msg->body->len;
- priv->outlen = 2;
- }
- else {
- /* Invalid body for spamc method */
- return;
- }
- }
-
- peer_key = (struct rspamd_http_keypair *)msg->peer_key;
-
- priv->wr_total = bodylen + buf->len + 2;
- hdrcount = 0;
-
- DL_FOREACH (msg->headers, hdr) {
- /* <name: value\r\n> */
- priv->wr_total += hdr->combined->len;
- enclen += hdr->combined->len;
- priv->outlen ++;
- hdrcount ++;
- }
-
- /* Allocate iov */
- priv->out = g_slice_alloc (sizeof (struct iovec) * priv->outlen);
- priv->wr_pos = 0;
-
- if (conn->type == RSPAMD_HTTP_SERVER) {
- /* Format reply */
- if (msg->method < HTTP_SYMBOLS) {
- ptm = gmtime (&msg->date);
- t = *ptm;
- rspamd_snprintf (datebuf,
- sizeof (datebuf),
- "%s, %02d %s %4d %02d:%02d:%02d GMT",
- http_week[t.tm_wday],
- t.tm_mday,
- http_month[t.tm_mon],
- t.tm_year + 1900,
- t.tm_hour,
- t.tm_min,
- t.tm_sec);
- if (mime_type == NULL) {
- mime_type = encrypted ? "application/octet-stream" : "text/plain";
- }
- if (encrypted) {
- /* Internal reply (encrypted) */
- meth_len = rspamd_snprintf (repbuf, sizeof (repbuf),
- "HTTP/1.1 %d %V\r\n"
- "Connection: close\r\n"
- "Server: %s\r\n"
- "Date: %s\r\n"
- "Content-Length: %z\r\n"
- "Content-Type: %s", /* NO \r\n at the end ! */
- msg->code,
- msg->status,
- "rspamd/" RVERSION,
- datebuf,
- bodylen,
- mime_type);
- enclen += meth_len;
- /* External reply */
- rspamd_printf_fstring (&buf, "HTTP/1.1 200 OK\r\n"
- "Connection: close\r\n"
- "Server: rspamd\r\n"
- "Date: %s\r\n"
- "Content-Length: %z\r\n"
- "Content-Type: application/octet-stream\r\n",
- datebuf,
- enclen);
- }
- else {
- meth_len = rspamd_printf_fstring (&buf, "HTTP/1.1 %d %V\r\n"
- "Connection: close\r\n"
- "Server: %s\r\n"
- "Date: %s\r\n"
- "Content-Length: %z\r\n"
- "Content-Type: %s\r\n",
- msg->code,
- msg->status,
- "rspamd/" RVERSION,
- datebuf,
- bodylen,
- mime_type);
- }
- }
- else {
- /* Legacy spamd reply */
- if (msg->flags & RSPAMD_HTTP_FLAG_SPAMC) {
- rspamd_printf_fstring (&buf, "SPAMD/1.1 0 EX_OK\r\n");
- }
- else {
- rspamd_printf_fstring (&buf, "RSPAMD/1.3 0 EX_OK\r\n");
- }
- }
- }
- else {
- /* Format request */
- enclen += msg->url->len +
- strlen (http_method_str (msg->method)) + 1 /* method + space */;
- if (host == NULL && msg->host == NULL) {
- /* Fallback to HTTP/1.0 */
- if (encrypted) {
- rspamd_printf_fstring (&buf, "%s %s HTTP/1.0\r\n"
- "Content-Length: %z\r\n",
- "POST",
- "/post",
- enclen);
- }
- else {
- rspamd_printf_fstring (&buf, "%s %V HTTP/1.0\r\n"
- "Content-Length: %z\r\n",
- http_method_str (msg->method),
- msg->url,
- bodylen);
- }
- }
- else {
- if (encrypted) {
- if (host != NULL) {
- rspamd_printf_fstring (&buf, "%s %s HTTP/1.1\r\n"
- "Connection: close\r\n"
- "Host: %s\r\n"
- "Content-Length: %z\r\n",
- "POST",
- "/post",
- host,
- enclen);
- }
- else {
- rspamd_printf_fstring (&buf, "%s %s HTTP/1.1\r\n"
- "Connection: close\r\n"
- "Host: %V\r\n"
- "Content-Length: %z\r\n",
- "POST",
- "/post",
- msg->host,
- enclen);
- }
- }
- else {
- if (host != NULL) {
- rspamd_printf_fstring (&buf, "%s %V HTTP/1.1\r\n"
- "Connection: close\r\n"
- "Host: %s\r\n"
- "Content-Length: %z\r\n",
- http_method_str (msg->method),
- msg->url,
- host,
- bodylen);
- }
- else {
- rspamd_printf_fstring (&buf, "%s %V HTTP/1.1\r\n"
- "Connection: close\r\n"
- "Host: %V\r\n"
- "Content-Length: %z\r\n",
- http_method_str (msg->method),
- msg->url,
- msg->host,
- bodylen);
- }
- }
-
- }
- if (encrypted) {
- memcpy (id, peer_key->id, sizeof (id));
- b32_key = rspamd_encode_base32 (priv->local_key->pk,
- rspamd_cryptobox_pk_bytes ());
- b32_id = rspamd_encode_base32 (id, RSPAMD_HTTP_KEY_ID_LEN);
- /* XXX: add some fuzz here */
- rspamd_printf_fstring (&buf, "Key: %s=%s\r\n", b32_id, b32_key);
- g_free (b32_key);
- g_free (b32_id);
- }
- }
-
- /* Setup external request body */
- priv->out[0].iov_base = buf->str;
- priv->out[0].iov_len = buf->len;
-
- /* Buf will be used eventually for encryption */
- if (encrypted) {
- gint meth_offset, nonce_offset, mac_offset;
-
- ottery_rand_bytes (nonce, rspamd_cryptobox_nonce_bytes ());
- memset (mac, 0, rspamd_cryptobox_mac_bytes ());
- meth_offset = buf->len;
-
- if (conn->type == RSPAMD_HTTP_SERVER) {
- buf = rspamd_fstring_append (buf, repbuf, meth_len);
- }
- else {
- meth_len = strlen (http_method_str (msg->method)) + 1; /* + space */
- buf = rspamd_fstring_append (buf, http_method_str (msg->method),
- meth_len - 1);
- buf = rspamd_fstring_append (buf, " ", 1);
- }
-
- nonce_offset = buf->len;
- buf = rspamd_fstring_append (buf, nonce, rspamd_cryptobox_nonce_bytes ());
- mac_offset = buf->len;
- buf = rspamd_fstring_append (buf, mac, rspamd_cryptobox_mac_bytes ());
-
- /* Need to be encrypted */
- if (conn->type == RSPAMD_HTTP_SERVER) {
- buf = rspamd_fstring_append (buf, "\r\n\r\n", 4);
- }
- else {
- buf = rspamd_fstring_append (buf, repbuf, preludelen);
- }
-
- meth_pos = buf->str + meth_offset;
- np = buf->str + nonce_offset;
- mp = buf->str + mac_offset;
- }
-
- /* During previous writes, buf might be reallocated and changed */
- priv->buf->data = buf;
-
- if (encrypted) {
- /* Finish external HTTP request */
- priv->out[1].iov_base = "\r\n";
- priv->out[1].iov_len = 2;
- /* Encrypt the real request */
- rspamd_http_connection_encrypt_message (conn, msg, priv, pbody, bodylen,
- meth_pos, meth_len, preludelen, hdrcount, np, mp, peer_key);
- }
- else {
- i = 1;
- LL_FOREACH (msg->headers, hdr) {
- priv->out[i].iov_base = hdr->combined->str;
- priv->out[i++].iov_len = hdr->combined->len;
- }
- if (msg->method < HTTP_SYMBOLS) {
- priv->out[i].iov_base = "\r\n";
- priv->out[i++].iov_len = 2;
- }
- else {
- /* No CRLF for compatibility reply */
- priv->wr_total -= 2;
- }
-
- if (pbody != NULL) {
-
- if (msg->body_buf.begin == NULL && msg->body_buf.len == 0) {
- msg->body_buf.begin = msg->body->str;
- }
-
- priv->out[i].iov_base = pbody;
- priv->out[i++].iov_len = bodylen;
- }
- }
-
- if (base != NULL && event_get_base (&priv->ev) == base) {
- event_del (&priv->ev);
- }
-
- event_set (&priv->ev, fd, EV_WRITE, rspamd_http_event_handler, conn);
-
- if (base != NULL) {
- event_base_set (base, &priv->ev);
- }
-
- event_add (&priv->ev, priv->ptv);
- }
-
- struct rspamd_http_message *
- rspamd_http_new_message (enum http_parser_type type)
- {
- struct rspamd_http_message *new;
-
- new = g_slice_alloc (sizeof (struct rspamd_http_message));
- if (type == HTTP_REQUEST) {
- new->url = rspamd_fstring_new ();
- }
- else {
- new->url = NULL;
- new->code = 200;
- }
-
- new->headers = NULL;
- new->date = 0;
- new->body = NULL;
- memset (&new->body_buf, 0, sizeof (new->body_buf));
- new->status = NULL;
- new->host = NULL;
- new->port = 80;
- new->type = type;
- new->method = HTTP_GET;
- new->peer_key = NULL;
- new->flags = 0;
-
- return new;
- }
-
- struct rspamd_http_message*
- rspamd_http_message_from_url (const gchar *url)
- {
- struct http_parser_url pu;
- struct rspamd_http_message *msg;
- const gchar *host, *path;
- size_t pathlen, urllen;
-
- if (url == NULL) {
- return NULL;
- }
-
- urllen = strlen (url);
- memset (&pu, 0, sizeof (pu));
- if (http_parser_parse_url (url, urllen, FALSE, &pu) != 0) {
- msg_warn ("cannot parse URL: %s", url);
- return NULL;
- }
-
- if ((pu.field_set & (1 << UF_HOST)) == 0) {
- msg_warn ("no host argument in URL: %s", url);
- return NULL;
- }
- if ((pu.field_set & (1 << UF_PATH)) == 0) {
- path = "/";
- pathlen = 1;
- }
- else {
- path = url + pu.field_data[UF_PATH].off;
- pathlen = pu.field_data[UF_PATH].len;
- }
-
- msg = rspamd_http_new_message (HTTP_REQUEST);
- host = url + pu.field_data[UF_HOST].off;
-
- if ((pu.field_set & (1 << UF_PORT)) != 0) {
- msg->port = pu.port;
- }
- else {
- /* XXX: magic constant */
- msg->port = 80;
- }
-
- msg->host = rspamd_fstring_new_init (host, pu.field_data[UF_HOST].len);
- msg->url = rspamd_fstring_new_init (path, pathlen);
-
- return msg;
- }
-
- void
- rspamd_http_message_free (struct rspamd_http_message *msg)
- {
- struct rspamd_http_header *hdr, *tmp_hdr;
-
- LL_FOREACH_SAFE (msg->headers, hdr, tmp_hdr)
- {
- rspamd_fstring_free (hdr->combined);
- g_slice_free1 (sizeof (*hdr->name), hdr->name);
- g_slice_free1 (sizeof (*hdr->value), hdr->value);
- g_slice_free1 (sizeof (struct rspamd_http_header), hdr);
- }
- if (msg->body != NULL) {
- rspamd_fstring_free (msg->body);
- }
- if (msg->url != NULL) {
- rspamd_fstring_free (msg->url);
- }
- if (msg->status != NULL) {
- rspamd_fstring_free (msg->status);
- }
- if (msg->host != NULL) {
- rspamd_fstring_free (msg->host);
- }
- if (msg->peer_key != NULL) {
- rspamd_http_connection_key_unref (msg->peer_key);
- }
-
- g_slice_free1 (sizeof (struct rspamd_http_message), msg);
- }
-
- void
- rspamd_http_message_add_header (struct rspamd_http_message *msg,
- const gchar *name,
- const gchar *value)
- {
- struct rspamd_http_header *hdr;
- guint nlen, vlen;
-
- if (msg != NULL && name != NULL && value != NULL) {
- hdr = g_slice_alloc (sizeof (struct rspamd_http_header));
- nlen = strlen (name);
- vlen = strlen (value);
- hdr->combined = rspamd_fstring_sized_new (nlen + vlen + 4);
- rspamd_printf_fstring (&hdr->combined, "%s: %s\r\n", name, value);
- hdr->value = g_slice_alloc (sizeof (*hdr->value));
- hdr->name = g_slice_alloc (sizeof (*hdr->name));
- hdr->name->begin = hdr->combined->str;
- hdr->name->len = nlen;
- hdr->value->begin = hdr->combined->str + nlen + 2;
- hdr->value->len = vlen;
- DL_APPEND (msg->headers, hdr);
- }
- }
-
- const rspamd_ftok_t *
- rspamd_http_message_find_header (struct rspamd_http_message *msg,
- const gchar *name)
- {
- struct rspamd_http_header *hdr;
- const rspamd_ftok_t *res = NULL;
- rspamd_ftok_t cmp;
- guint slen = strlen (name);
-
- if (msg != NULL) {
- cmp.begin = name;
- cmp.len = slen;
-
- LL_FOREACH (msg->headers, hdr) {
- if (rspamd_ftok_casecmp (hdr->name, &cmp) == 0) {
- res = hdr->value;
- break;
- }
- }
- }
-
- return res;
- }
-
- gboolean rspamd_http_message_remove_header (struct rspamd_http_message *msg,
- const gchar *name)
- {
- struct rspamd_http_header *hdr, *tmp;
- gboolean res = FALSE;
- guint slen = strlen (name);
- rspamd_ftok_t cmp;
-
- if (msg != NULL) {
- cmp.begin = name;
- cmp.len = slen;
-
- DL_FOREACH_SAFE (msg->headers, hdr, tmp) {
- if (rspamd_ftok_casecmp (hdr->name, &cmp) == 0) {
- res = TRUE;
- DL_DELETE (msg->headers, hdr);
-
- rspamd_fstring_free (hdr->combined);
- g_slice_free1 (sizeof (*hdr->value), hdr->value);
- g_slice_free1 (sizeof (*hdr->name), hdr->name);
- g_slice_free1 (sizeof (*hdr), hdr);
- }
- }
- }
-
- return res;
- }
-
- /*
- * HTTP router functions
- */
-
- static void
- rspamd_http_entry_free (struct rspamd_http_connection_entry *entry)
- {
- if (entry != NULL) {
- close (entry->conn->fd);
- rspamd_http_connection_unref (entry->conn);
- if (entry->rt->finish_handler) {
- entry->rt->finish_handler (entry);
- }
-
- DL_DELETE (entry->rt->conns, entry);
- g_slice_free1 (sizeof (struct rspamd_http_connection_entry), entry);
- }
- }
-
- static void
- rspamd_http_router_error_handler (struct rspamd_http_connection *conn,
- GError *err)
- {
- struct rspamd_http_connection_entry *entry = conn->ud;
- struct rspamd_http_message *msg;
-
- if (entry->is_reply) {
- /* At this point we need to finish this session and close owned socket */
- if (entry->rt->error_handler != NULL) {
- entry->rt->error_handler (entry, err);
- }
- rspamd_http_entry_free (entry);
- }
- else {
- /* Here we can write a reply to a client */
- if (entry->rt->error_handler != NULL) {
- entry->rt->error_handler (entry, err);
- }
- msg = rspamd_http_new_message (HTTP_RESPONSE);
- msg->date = time (NULL);
- msg->code = err->code;
- msg->body = rspamd_fstring_new_init (err->message, strlen (err->message));
- rspamd_http_connection_reset (entry->conn);
- rspamd_http_connection_write_message (entry->conn,
- msg,
- NULL,
- "text/plain",
- entry,
- entry->conn->fd,
- entry->rt->ptv,
- entry->rt->ev_base);
- entry->is_reply = TRUE;
- }
- }
-
- static const gchar *
- rspamd_http_router_detect_ct (const gchar *path)
- {
- const gchar *dot;
- guint i;
-
- dot = strrchr (path, '.');
- if (dot == NULL) {
- return http_file_types[HTTP_MAGIC_PLAIN].ct;
- }
- dot++;
-
- for (i = 0; i < G_N_ELEMENTS (http_file_types); i++) {
- if (strcmp (http_file_types[i].ext, dot) == 0) {
- return http_file_types[i].ct;
- }
- }
-
- return http_file_types[HTTP_MAGIC_PLAIN].ct;
- }
-
- static gboolean
- rspamd_http_router_is_subdir (const gchar *parent, const gchar *sub)
- {
- if (parent == NULL || sub == NULL || *parent == '\0') {
- return FALSE;
- }
-
- while (*parent != '\0') {
- if (*sub != *parent) {
- return FALSE;
- }
- parent++;
- sub++;
- }
-
- parent--;
- if (*parent == G_DIR_SEPARATOR) {
- return TRUE;
- }
-
- return (*sub == G_DIR_SEPARATOR || *sub == '\0');
- }
-
- static gboolean
- rspamd_http_router_try_file (struct rspamd_http_connection_entry *entry,
- rspamd_ftok_t *lookup, gboolean expand_path)
- {
- struct stat st;
- gint fd;
- gchar filebuf[PATH_MAX], realbuf[PATH_MAX], *dir;
- struct rspamd_http_message *reply_msg;
-
- rspamd_snprintf (filebuf, sizeof (filebuf), "%s%c%T",
- entry->rt->default_fs_path, G_DIR_SEPARATOR, lookup);
-
- if (realpath (filebuf, realbuf) == NULL ||
- lstat (realbuf, &st) == -1) {
- return FALSE;
- }
-
- if (S_ISDIR (st.st_mode) && expand_path) {
- /* Try to append 'index.html' to the url */
- rspamd_fstring_t *nlookup;
- rspamd_ftok_t tok;
- gboolean ret;
-
- nlookup = rspamd_fstring_sized_new (lookup->len + sizeof ("index.html"));
- rspamd_printf_fstring (&nlookup, "%T%c%s", lookup, G_DIR_SEPARATOR,
- "index.html");
- tok.begin = nlookup->str;
- tok.len = nlookup->len;
- ret = rspamd_http_router_try_file (entry, &tok, FALSE);
- rspamd_fstring_free (nlookup);
-
- return ret;
- }
- else if (!S_ISREG (st.st_mode)) {
- return FALSE;
- }
-
- /* We also need to ensure that file is inside the defined dir */
- rspamd_strlcpy (filebuf, realbuf, sizeof (filebuf));
- dir = dirname (filebuf);
-
- if (dir == NULL ||
- !rspamd_http_router_is_subdir (entry->rt->default_fs_path,
- dir)) {
- return FALSE;
- }
-
- fd = open (realbuf, O_RDONLY);
- if (fd == -1) {
- return FALSE;
- }
-
- reply_msg = rspamd_http_new_message (HTTP_RESPONSE);
- reply_msg->date = time (NULL);
- reply_msg->code = 200;
-
- reply_msg->body = rspamd_fstring_sized_new (st.st_size);
- reply_msg->body->len = st.st_size;
- reply_msg->body_buf.len = st.st_size;
- reply_msg->body_buf.begin = reply_msg->body->str;
-
- if (read (fd, reply_msg->body->str, st.st_size) != st.st_size) {
- close (fd);
- rspamd_http_message_free (reply_msg);
- return FALSE;
- }
-
- close (fd);
-
- rspamd_http_connection_reset (entry->conn);
-
- msg_debug ("requested file %s", realbuf);
- rspamd_http_connection_write_message (entry->conn, reply_msg, NULL,
- rspamd_http_router_detect_ct (realbuf), entry, entry->conn->fd,
- entry->rt->ptv, entry->rt->ev_base);
-
- return TRUE;
- }
-
- static int
- rspamd_http_router_finish_handler (struct rspamd_http_connection *conn,
- struct rspamd_http_message *msg)
- {
- struct rspamd_http_connection_entry *entry = conn->ud;
- rspamd_http_router_handler_t handler = NULL;
- gpointer found;
- struct rspamd_http_message *err_msg;
- GError *err;
- rspamd_ftok_t lookup;
- struct http_parser_url u;
-
- G_STATIC_ASSERT (sizeof (rspamd_http_router_handler_t) ==
- sizeof (gpointer));
-
- memset (&lookup, 0, sizeof (lookup));
-
- if (entry->is_reply) {
- /* Request is finished, it is safe to free a connection */
- rspamd_http_entry_free (entry);
- }
- else {
- /* Search for path */
- if (msg->url != NULL && msg->url->len != 0) {
-
- http_parser_parse_url (msg->url->str, msg->url->len, TRUE, &u);
-
- if (u.field_set & (1 << UF_PATH)) {
- lookup.begin = msg->url->str + u.field_data[UF_PATH].off;
- lookup.len = u.field_data[UF_PATH].len;
- }
- else {
- lookup.begin = msg->url->str;
- lookup.len = msg->url->len;
- }
-
- found = g_hash_table_lookup (entry->rt->paths, &lookup);
- memcpy (&handler, &found, sizeof (found));
- msg_debug ("requested known path: %T", &lookup);
- }
- entry->is_reply = TRUE;
- if (handler != NULL) {
- return handler (entry, msg);
- }
- else {
- if (entry->rt->default_fs_path == NULL || lookup.len == 0 ||
- !rspamd_http_router_try_file (entry, &lookup, TRUE)) {
- err = g_error_new (HTTP_ERROR, 404,
- "Not found");
- if (entry->rt->error_handler != NULL) {
- entry->rt->error_handler (entry, err);
- }
- msg_info ("path: %T not found", &lookup);
- err_msg = rspamd_http_new_message (HTTP_RESPONSE);
- err_msg->date = time (NULL);
- err_msg->code = err->code;
- err_msg->body = rspamd_fstring_new_init (err->message,
- strlen (err->message));
- rspamd_http_connection_reset (entry->conn);
- rspamd_http_connection_write_message (entry->conn,
- err_msg,
- NULL,
- "text/plain",
- entry,
- entry->conn->fd,
- entry->rt->ptv,
- entry->rt->ev_base);
- g_error_free (err);
- }
- }
- }
-
- return 0;
- }
-
- struct rspamd_http_connection_router *
- rspamd_http_router_new (rspamd_http_router_error_handler_t eh,
- rspamd_http_router_finish_handler_t fh,
- struct timeval *timeout, struct event_base *base,
- const char *default_fs_path,
- struct rspamd_keypair_cache *cache)
- {
- struct rspamd_http_connection_router * new;
- struct stat st;
-
- new = g_slice_alloc0 (sizeof (struct rspamd_http_connection_router));
- new->paths = g_hash_table_new_full (rspamd_ftok_icase_hash,
- rspamd_ftok_icase_equal, rspamd_fstring_mapped_ftok_free, NULL);
- new->conns = NULL;
- new->error_handler = eh;
- new->finish_handler = fh;
- new->ev_base = base;
-
- if (timeout) {
- new->tv = *timeout;
- new->ptv = &new->tv;
- }
- else {
- new->ptv = NULL;
- }
-
- new->default_fs_path = NULL;
-
- if (default_fs_path != NULL) {
- if (stat (default_fs_path, &st) == -1) {
- msg_err ("cannot stat %s", default_fs_path);
- }
- else {
- if (!S_ISDIR (st.st_mode)) {
- msg_err ("path %s is not a directory", default_fs_path);
- }
- else {
- new->default_fs_path = realpath (default_fs_path, NULL);
- }
- }
- }
-
- new->cache = cache;
-
- return new;
- }
-
- void
- rspamd_http_router_set_key (struct rspamd_http_connection_router *router,
- gpointer key)
- {
- struct rspamd_http_keypair *kp = (struct rspamd_http_keypair *)key;
-
- g_assert (key != NULL);
- REF_RETAIN (kp);
-
- router->key = key;
- }
-
- void
- rspamd_http_router_add_path (struct rspamd_http_connection_router *router,
- const gchar *path, rspamd_http_router_handler_t handler)
- {
- gpointer ptr;
- rspamd_ftok_t *key;
- rspamd_fstring_t *storage;
- G_STATIC_ASSERT (sizeof (rspamd_http_router_handler_t) ==
- sizeof (gpointer));
-
- if (path != NULL && handler != NULL && router != NULL) {
- memcpy (&ptr, &handler, sizeof (ptr));
- storage = rspamd_fstring_new_init (path, strlen (path));
- key = g_slice_alloc0 (sizeof (*key));
- key->begin = storage->str;
- key->len = storage->len;
- g_hash_table_insert (router->paths, key, ptr);
- }
- }
-
- void
- rspamd_http_router_handle_socket (struct rspamd_http_connection_router *router,
- gint fd, gpointer ud)
- {
- struct rspamd_http_connection_entry *conn;
-
- conn = g_slice_alloc (sizeof (struct rspamd_http_connection_entry));
- conn->rt = router;
- conn->ud = ud;
- conn->is_reply = FALSE;
-
- conn->conn = rspamd_http_connection_new (NULL,
- rspamd_http_router_error_handler,
- rspamd_http_router_finish_handler,
- 0,
- RSPAMD_HTTP_SERVER, router->cache);
-
- if (router->key) {
- rspamd_http_connection_set_key (conn->conn, router->key);
- }
-
- rspamd_http_connection_read_message (conn->conn, conn, fd, router->ptv,
- router->ev_base);
- DL_PREPEND (router->conns, conn);
- }
-
- void
- rspamd_http_router_free (struct rspamd_http_connection_router *router)
- {
- struct rspamd_http_connection_entry *conn, *tmp;
- struct rspamd_http_keypair *kp;
-
- if (router) {
- DL_FOREACH_SAFE (router->conns, conn, tmp)
- {
- rspamd_http_entry_free (conn);
- }
-
- if (router->key) {
- kp = (struct rspamd_http_keypair *)router->key;
- REF_RELEASE (kp);
- }
-
- if (router->cache) {
- rspamd_keypair_cache_destroy (router->cache);
- }
-
- if (router->default_fs_path != NULL) {
- g_free (router->default_fs_path);
- }
- g_hash_table_unref (router->paths);
- g_slice_free1 (sizeof (struct rspamd_http_connection_router), router);
- }
- }
-
- gpointer
- rspamd_http_connection_make_key (gchar *key, gsize keylen)
- {
- guchar *decoded_sk, *decoded_pk;
- gchar *semicolon;
- gsize decoded_len;
- struct rspamd_http_keypair *kp;
- guchar kh[rspamd_cryptobox_HASHBYTES];
-
- semicolon = memchr (key, ':', keylen);
-
- if (semicolon) {
- decoded_sk = rspamd_decode_base32 (key, semicolon - key, &decoded_len);
- decoded_pk = rspamd_decode_base32 (semicolon + 1,
- keylen - (semicolon - key + 1),
- &decoded_len);
- }
- else {
- decoded_sk = rspamd_decode_base32 (key, keylen / 2, &decoded_len);
- decoded_pk = rspamd_decode_base32 (key + keylen / 2,
- keylen / 2,
- &decoded_len);
- }
-
- if (decoded_pk != NULL && decoded_sk != NULL) {
- if (decoded_len == rspamd_cryptobox_pk_bytes ()) {
- kp = g_slice_alloc (sizeof (*kp));
- REF_INIT_RETAIN (kp, rspamd_http_keypair_dtor);
- memcpy (kp->sk, decoded_sk, rspamd_cryptobox_sk_bytes ());
- memcpy (kp->pk, decoded_pk, rspamd_cryptobox_pk_bytes ());
- rspamd_cryptobox_hash (kh, kp->pk, rspamd_cryptobox_pk_bytes (),
- NULL, 0);
- memcpy (kp->id, kh, sizeof (kp->id));
-
- return (gpointer) kp;
- }
- g_free (decoded_pk);
- g_free (decoded_sk);
- }
-
- return NULL;
- }
-
- gpointer
- rspamd_http_connection_gen_key (void)
- {
- struct rspamd_http_keypair *kp;
- guchar kh[rspamd_cryptobox_HASHBYTES];
-
- kp = g_slice_alloc (sizeof (*kp));
- REF_INIT_RETAIN (kp, rspamd_http_keypair_dtor);
-
- rspamd_cryptobox_keypair (kp->pk, kp->sk);
- rspamd_cryptobox_hash (kh, kp->pk, rspamd_cryptobox_pk_bytes (),
- NULL, 0);
- memcpy (kp->id, kh, sizeof (kp->id));
-
- return (gpointer)kp;
- }
-
- static void
- rspamd_http_print_key_component (guchar *data, gsize datalen,
- GString *res, guint how, const gchar *description)
- {
- gchar *b32;
-
- if (how & RSPAMD_KEYPAIR_HUMAN) {
- g_string_append_printf (res, "%s: ", description);
- }
-
- if (how & RSPAMD_KEYPAIR_BASE32) {
- b32 = rspamd_encode_base32 (data, datalen);
- g_string_append_printf (res, "%s", b32);
- g_free (b32);
- }
- else if (how & RSPAMD_KEYPAIR_HEX) {
- rspamd_printf_gstring (res, "%*xs", (gint)datalen, data);
- }
- else {
- g_string_append_len (res, data, datalen);
- }
-
- if (how & RSPAMD_KEYPAIR_HUMAN) {
- g_string_append_c (res, '\n');
- }
- }
-
- GString *
- rspamd_http_connection_print_key (gpointer key, guint how)
- {
- struct rspamd_http_keypair *kp = (struct rspamd_http_keypair *)key;
- GString *res;
-
- g_assert (key != NULL);
-
- res = g_string_new (NULL);
-
- if ((how & RSPAMD_KEYPAIR_PUBKEY)) {
- rspamd_http_print_key_component (kp->pk,
- rspamd_cryptobox_pk_bytes (), res, how,
- "Public key");
- }
- if ((how & RSPAMD_KEYPAIR_PRIVKEY)) {
- rspamd_http_print_key_component (kp->sk, rspamd_cryptobox_sk_bytes (),
- res, how,
- "Private key");
- }
- if ((how & RSPAMD_KEYPAIR_ID)) {
- rspamd_http_print_key_component (kp->id, RSPAMD_HTTP_KEY_ID_LEN, res, how,
- "Key ID");
- }
-
- return res;
- }
-
- void
- rspamd_http_connection_set_key (struct rspamd_http_connection *conn,
- gpointer key)
- {
- struct rspamd_http_connection_private *priv = conn->priv;
- struct rspamd_http_keypair *kp = (struct rspamd_http_keypair *)key;
-
- g_assert (key != NULL);
- REF_RETAIN (kp);
- priv->local_key = kp;
- }
-
- gboolean
- rspamd_http_connection_is_encrypted (struct rspamd_http_connection *conn)
- {
- struct rspamd_http_connection_private *priv = conn->priv;
-
- if (priv->peer_key != NULL) {
- return TRUE;
- }
- else if (priv->msg) {
- return priv->msg->peer_key != NULL;
- }
-
- return FALSE;
- }
-
- void
- rspamd_http_connection_key_unref (gpointer key)
- {
- struct rspamd_http_keypair *kp = (struct rspamd_http_keypair *)key;
-
- g_assert (key != NULL);
- REF_RELEASE (kp);
- }
-
- gpointer
- rspamd_http_connection_key_ref (gpointer key)
- {
- struct rspamd_http_keypair *kp = (struct rspamd_http_keypair *)key;
-
- g_assert (key != NULL);
- REF_RETAIN (kp);
-
- return kp;
- }
-
- gpointer
- rspamd_http_connection_make_peer_key (const gchar *key)
- {
- guchar *pk_decoded;
- gsize dec_len;
- struct rspamd_http_keypair *kp = NULL;
- guchar kh[rspamd_cryptobox_HASHBYTES];
-
- pk_decoded = rspamd_decode_base32 (key, strlen (key), &dec_len);
-
- if (pk_decoded != NULL && dec_len == rspamd_cryptobox_pk_bytes ()) {
- kp = g_slice_alloc0 (sizeof (*kp));
- REF_INIT_RETAIN (kp, rspamd_http_keypair_dtor);
- memcpy (kp->pk, pk_decoded, rspamd_cryptobox_pk_bytes ());
- rspamd_cryptobox_hash (kh, kp->pk, rspamd_cryptobox_pk_bytes (),
- NULL, 0);
- memcpy (kp->id, kh, sizeof (kp->id));
- }
-
- g_free (pk_decoded);
-
- return kp;
- }
-
- GHashTable *
- rspamd_http_message_parse_query (struct rspamd_http_message *msg)
- {
- GHashTable *res;
- rspamd_fstring_t *key = NULL, *value = NULL;
- rspamd_ftok_t *key_tok = NULL, *value_tok = NULL;
- const gchar *p, *c, *end;
- struct http_parser_url u;
- enum {
- parse_key,
- parse_eqsign,
- parse_value,
- parse_ampersand
- } state = parse_key;
-
- res = g_hash_table_new_full (rspamd_ftok_icase_hash,
- rspamd_ftok_icase_equal,
- rspamd_fstring_mapped_ftok_free,
- rspamd_fstring_mapped_ftok_free);
-
- if (msg->url && msg->url->len > 0) {
- http_parser_parse_url (msg->url->str, msg->url->len, TRUE, &u);
-
- if (u.field_set & (1 << UF_QUERY)) {
- p = msg->url->str + u.field_data[UF_QUERY].off;
- c = p;
- end = p + u.field_data[UF_QUERY].len;
-
- while (p <= end) {
- switch (state) {
- case parse_key:
- if ((p == end || *p == '&') && p > c) {
- /* We have a single parameter without a value */
- key = rspamd_fstring_new_init (c, p - c);
- key_tok = rspamd_ftok_map (key);
- key_tok->len = rspamd_decode_url (key->str, key->str,
- key->len);
-
- value = rspamd_fstring_new_init ("", 0);
- value_tok = rspamd_ftok_map (value);
-
- g_hash_table_replace (res, key_tok, value_tok);
- state = parse_ampersand;
- }
- else if (*p == '=' && p > c) {
- /* We have something like key=value */
- key = rspamd_fstring_new_init (c, p - c);
- key_tok = rspamd_ftok_map (key);
- key_tok->len = rspamd_decode_url (key->str, key->str,
- key->len);
-
- state = parse_eqsign;
- }
- else {
- p ++;
- }
- break;
-
- case parse_eqsign:
- if (*p != '=') {
- c = p;
- state = parse_value;
- }
- else {
- p ++;
- }
- break;
-
- case parse_value:
- if ((p == end || *p == '&') && p >= c) {
- g_assert (key != NULL);
- if (p > c) {
- value = rspamd_fstring_new_init (c, p - c);
- value_tok = rspamd_ftok_map (value);
- value_tok->len = rspamd_decode_url (value->str,
- value->str,
- value->len);
- /* Detect quotes for value */
- if (value_tok->begin[0] == '"') {
- memmove (value->str, value->str + 1,
- value_tok->len - 1);
- value_tok->len --;
- }
- if (value_tok->begin[value_tok->len - 1] == '"') {
- value_tok->len --;
- }
- }
- else {
- value = rspamd_fstring_new_init ("", 0);
- value_tok = rspamd_ftok_map (value);
- }
-
- g_hash_table_replace (res, key_tok, value_tok);
- key = value = NULL;
- key_tok = value_tok = NULL;
- state = parse_ampersand;
- }
- else {
- p ++;
- }
- break;
-
- case parse_ampersand:
- if (p != end && *p != '&') {
- c = p;
- state = parse_key;
- }
- else {
- p ++;
- }
- break;
- }
- }
- }
-
- if (state != parse_ampersand && key != NULL) {
- rspamd_fstring_free (key);
- }
- }
-
- return res;
- }
|