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.

map.c 79KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199
  1. /*
  2. * Copyright 2024 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. /*
  17. * Implementation of map files handling
  18. */
  19. #include "config.h"
  20. #include "map.h"
  21. #include "map_private.h"
  22. #include "libserver/http/http_connection.h"
  23. #include "libserver/http/http_private.h"
  24. #include "rspamd.h"
  25. #include "contrib/libev/ev.h"
  26. #include "contrib/uthash/utlist.h"
  27. #ifdef SYS_ZSTD
  28. #include "zstd.h"
  29. #else
  30. #include "contrib/zstd/zstd.h"
  31. #endif
  32. #undef MAP_DEBUG_REFS
  33. #ifdef MAP_DEBUG_REFS
  34. #define MAP_RETAIN(x, t) \
  35. do { \
  36. msg_err(G_GNUC_PRETTY_FUNCTION ": " t ": retain ref %p, refcount: %d -> %d", (x), (x)->ref.refcount, (x)->ref.refcount + 1); \
  37. REF_RETAIN(x); \
  38. } while (0)
  39. #define MAP_RELEASE(x, t) \
  40. do { \
  41. msg_err(G_GNUC_PRETTY_FUNCTION ": " t ": release ref %p, refcount: %d -> %d", (x), (x)->ref.refcount, (x)->ref.refcount - 1); \
  42. REF_RELEASE(x); \
  43. } while (0)
  44. #else
  45. #define MAP_RETAIN(x, t) REF_RETAIN(x)
  46. #define MAP_RELEASE(x, t) REF_RELEASE(x)
  47. #endif
  48. enum rspamd_map_periodic_opts {
  49. RSPAMD_MAP_SCHEDULE_NORMAL = 0,
  50. RSPAMD_MAP_SCHEDULE_ERROR = (1u << 0u),
  51. RSPAMD_MAP_SCHEDULE_LOCKED = (1u << 1u),
  52. RSPAMD_MAP_SCHEDULE_INIT = (1u << 2u),
  53. };
  54. static void free_http_cbdata_common(struct http_callback_data *cbd,
  55. gboolean plan_new);
  56. static void free_http_cbdata_dtor(gpointer p);
  57. static void free_http_cbdata(struct http_callback_data *cbd);
  58. static void rspamd_map_process_periodic(struct map_periodic_cbdata *cbd);
  59. static void rspamd_map_schedule_periodic(struct rspamd_map *map, int how);
  60. static gboolean read_map_file_chunks(struct rspamd_map *map,
  61. struct map_cb_data *cbdata,
  62. const char *fname,
  63. gsize len,
  64. goffset off);
  65. static gboolean rspamd_map_save_http_cached_file(struct rspamd_map *map,
  66. struct rspamd_map_backend *bk,
  67. struct http_map_data *htdata,
  68. const unsigned char *data,
  69. gsize len);
  70. static gboolean rspamd_map_update_http_cached_file(struct rspamd_map *map,
  71. struct rspamd_map_backend *bk,
  72. struct http_map_data *htdata);
  73. unsigned int rspamd_map_log_id = (unsigned int) -1;
  74. RSPAMD_CONSTRUCTOR(rspamd_map_log_init)
  75. {
  76. rspamd_map_log_id = rspamd_logger_add_debug_module("map");
  77. }
  78. /**
  79. * Write HTTP request
  80. */
  81. static void
  82. write_http_request(struct http_callback_data *cbd)
  83. {
  84. char datebuf[128];
  85. struct rspamd_http_message *msg;
  86. msg = rspamd_http_new_message(HTTP_REQUEST);
  87. if (cbd->check) {
  88. msg->method = HTTP_HEAD;
  89. }
  90. msg->url = rspamd_fstring_append(msg->url,
  91. cbd->data->path, strlen(cbd->data->path));
  92. if (cbd->check) {
  93. if (cbd->data->last_modified != 0) {
  94. rspamd_http_date_format(datebuf, sizeof(datebuf),
  95. cbd->data->last_modified);
  96. rspamd_http_message_add_header(msg, "If-Modified-Since",
  97. datebuf);
  98. }
  99. if (cbd->data->etag) {
  100. rspamd_http_message_add_header_len(msg, "If-None-Match",
  101. cbd->data->etag->str, cbd->data->etag->len);
  102. }
  103. }
  104. msg->url = rspamd_fstring_append(msg->url, cbd->data->rest,
  105. strlen(cbd->data->rest));
  106. if (cbd->data->userinfo) {
  107. rspamd_http_message_add_header(msg, "Authorization",
  108. cbd->data->userinfo);
  109. }
  110. MAP_RETAIN(cbd, "http_callback_data");
  111. rspamd_http_connection_write_message(cbd->conn,
  112. msg,
  113. cbd->data->host,
  114. NULL,
  115. cbd,
  116. cbd->timeout);
  117. }
  118. /**
  119. * Callback for destroying HTTP callback data
  120. */
  121. static void
  122. free_http_cbdata_common(struct http_callback_data *cbd, gboolean plan_new)
  123. {
  124. struct map_periodic_cbdata *periodic = cbd->periodic;
  125. if (cbd->shmem_data) {
  126. rspamd_http_message_shmem_unref(cbd->shmem_data);
  127. }
  128. if (cbd->pk) {
  129. rspamd_pubkey_unref(cbd->pk);
  130. }
  131. if (cbd->conn) {
  132. rspamd_http_connection_unref(cbd->conn);
  133. cbd->conn = NULL;
  134. }
  135. if (cbd->addrs) {
  136. rspamd_inet_addr_t *addr;
  137. unsigned int i;
  138. PTR_ARRAY_FOREACH(cbd->addrs, i, addr)
  139. {
  140. rspamd_inet_address_free(addr);
  141. }
  142. g_ptr_array_free(cbd->addrs, TRUE);
  143. }
  144. MAP_RELEASE(cbd->bk, "rspamd_map_backend");
  145. if (periodic) {
  146. /* Detached in case of HTTP error */
  147. MAP_RELEASE(periodic, "periodic");
  148. }
  149. g_free(cbd);
  150. }
  151. static void
  152. free_http_cbdata(struct http_callback_data *cbd)
  153. {
  154. cbd->map->tmp_dtor = NULL;
  155. cbd->map->tmp_dtor_data = NULL;
  156. free_http_cbdata_common(cbd, TRUE);
  157. }
  158. static void
  159. free_http_cbdata_dtor(gpointer p)
  160. {
  161. struct http_callback_data *cbd = p;
  162. struct rspamd_map *map;
  163. map = cbd->map;
  164. if (cbd->stage == http_map_http_conn) {
  165. REF_RELEASE(cbd);
  166. }
  167. else {
  168. /* We cannot terminate DNS requests sent */
  169. cbd->stage = http_map_terminated;
  170. }
  171. msg_warn_map("%s: "
  172. "connection with http server is terminated: worker is stopping",
  173. map->name);
  174. }
  175. /*
  176. * HTTP callbacks
  177. */
  178. static void
  179. http_map_error(struct rspamd_http_connection *conn,
  180. GError *err)
  181. {
  182. struct http_callback_data *cbd = conn->ud;
  183. struct rspamd_map *map;
  184. map = cbd->map;
  185. if (cbd->periodic) {
  186. cbd->periodic->errored = TRUE;
  187. msg_err_map("error reading %s(%s): "
  188. "connection with http server terminated incorrectly: %e",
  189. cbd->bk->uri,
  190. cbd->addr ? rspamd_inet_address_to_string_pretty(cbd->addr) : "",
  191. err);
  192. rspamd_map_process_periodic(cbd->periodic);
  193. }
  194. MAP_RELEASE(cbd, "http_callback_data");
  195. }
  196. static void
  197. rspamd_map_cache_cb(struct ev_loop *loop, ev_timer *w, int revents)
  198. {
  199. struct rspamd_http_map_cached_cbdata *cache_cbd = (struct rspamd_http_map_cached_cbdata *)
  200. w->data;
  201. struct rspamd_map *map;
  202. struct http_map_data *data;
  203. map = cache_cbd->map;
  204. data = cache_cbd->data;
  205. if (cache_cbd->gen != cache_cbd->data->gen) {
  206. /* We have another update, so this cache element is obviously expired */
  207. /*
  208. * Important!: we do not set cache availability to zero here, as there
  209. * might be fresh cache
  210. */
  211. msg_info_map("cached data is now expired (gen mismatch %L != %L) for %s; shm name=%s; refcount=%d",
  212. cache_cbd->gen, cache_cbd->data->gen, map->name, cache_cbd->shm->shm_name,
  213. cache_cbd->shm->ref.refcount);
  214. MAP_RELEASE(cache_cbd->shm, "rspamd_http_map_cached_cbdata");
  215. ev_timer_stop(loop, &cache_cbd->timeout);
  216. g_free(cache_cbd);
  217. }
  218. else if (cache_cbd->data->last_checked >= cache_cbd->last_checked) {
  219. /*
  220. * We checked map but we have not found anything more recent,
  221. * reschedule cache check
  222. */
  223. if (cache_cbd->map->poll_timeout >
  224. rspamd_get_calendar_ticks() - cache_cbd->data->last_checked) {
  225. w->repeat = cache_cbd->map->poll_timeout -
  226. (rspamd_get_calendar_ticks() - cache_cbd->data->last_checked);
  227. }
  228. else {
  229. w->repeat = cache_cbd->map->poll_timeout;
  230. }
  231. if (w->repeat < 0) {
  232. msg_info_map("cached data for %s has skewed check time: %d last checked, "
  233. "%d poll timeout, %.2f diff; shm name=%s; refcount=%d",
  234. map->name, (int) cache_cbd->data->last_checked,
  235. (int) cache_cbd->map->poll_timeout,
  236. (rspamd_get_calendar_ticks() - cache_cbd->data->last_checked),
  237. cache_cbd->shm->shm_name,
  238. cache_cbd->shm->ref.refcount);
  239. w->repeat = 0.0;
  240. }
  241. cache_cbd->last_checked = cache_cbd->data->last_checked;
  242. msg_debug_map("cached data is up to date for %s", map->name);
  243. ev_timer_again(loop, &cache_cbd->timeout);
  244. }
  245. else {
  246. data->cur_cache_cbd = NULL;
  247. g_atomic_int_set(&data->cache->available, 0);
  248. msg_info_map("cached data is now expired for %s; shm name=%s; refcount=%d",
  249. map->name,
  250. cache_cbd->shm->shm_name,
  251. cache_cbd->shm->ref.refcount);
  252. MAP_RELEASE(cache_cbd->shm, "rspamd_http_map_cached_cbdata");
  253. ev_timer_stop(loop, &cache_cbd->timeout);
  254. g_free(cache_cbd);
  255. }
  256. }
  257. static int
  258. http_map_finish(struct rspamd_http_connection *conn,
  259. struct rspamd_http_message *msg)
  260. {
  261. struct http_callback_data *cbd = conn->ud;
  262. struct rspamd_map *map;
  263. struct rspamd_map_backend *bk;
  264. struct http_map_data *data;
  265. struct rspamd_http_map_cached_cbdata *cache_cbd;
  266. const rspamd_ftok_t *expires_hdr, *etag_hdr;
  267. char next_check_date[128];
  268. unsigned char *in = NULL;
  269. gsize dlen = 0;
  270. map = cbd->map;
  271. bk = cbd->bk;
  272. data = bk->data.hd;
  273. if (msg->code == 200) {
  274. if (cbd->check) {
  275. msg_info_map("need to reread map from %s", cbd->bk->uri);
  276. cbd->periodic->need_modify = TRUE;
  277. /* Reset the whole chain */
  278. cbd->periodic->cur_backend = 0;
  279. /* Reset cache, old cached data will be cleaned on timeout */
  280. g_atomic_int_set(&data->cache->available, 0);
  281. data->cur_cache_cbd = NULL;
  282. rspamd_map_process_periodic(cbd->periodic);
  283. MAP_RELEASE(cbd, "http_callback_data");
  284. return 0;
  285. }
  286. cbd->data->last_checked = msg->date;
  287. if (msg->last_modified) {
  288. cbd->data->last_modified = msg->last_modified;
  289. }
  290. else {
  291. cbd->data->last_modified = msg->date;
  292. }
  293. /* Unsigned version - just open file */
  294. cbd->shmem_data = rspamd_http_message_shmem_ref(msg);
  295. cbd->data_len = msg->body_buf.len;
  296. if (cbd->data_len == 0) {
  297. msg_err_map("cannot read empty map");
  298. goto err;
  299. }
  300. g_assert(cbd->shmem_data != NULL);
  301. in = rspamd_shmem_xmap(cbd->shmem_data->shm_name, PROT_READ, &dlen);
  302. if (in == NULL) {
  303. msg_err_map("cannot read tempfile %s: %s",
  304. cbd->shmem_data->shm_name,
  305. strerror(errno));
  306. goto err;
  307. }
  308. /* Check for expires */
  309. double cached_timeout = map->poll_timeout * 2;
  310. expires_hdr = rspamd_http_message_find_header(msg, "Expires");
  311. if (expires_hdr) {
  312. time_t hdate;
  313. hdate = rspamd_http_parse_date(expires_hdr->begin, expires_hdr->len);
  314. if (hdate != (time_t) -1 && hdate > msg->date) {
  315. cached_timeout = map->next_check - msg->date +
  316. map->poll_timeout * 2;
  317. map->next_check = hdate;
  318. }
  319. else {
  320. msg_info_map("invalid expires header: %T, ignore it", expires_hdr);
  321. map->next_check = 0;
  322. }
  323. }
  324. /* Check for etag */
  325. etag_hdr = rspamd_http_message_find_header(msg, "ETag");
  326. if (etag_hdr) {
  327. if (cbd->data->etag) {
  328. /* Remove old etag */
  329. rspamd_fstring_free(cbd->data->etag);
  330. }
  331. cbd->data->etag = rspamd_fstring_new_init(etag_hdr->begin,
  332. etag_hdr->len);
  333. }
  334. else {
  335. if (cbd->data->etag) {
  336. /* Remove and clear old etag */
  337. rspamd_fstring_free(cbd->data->etag);
  338. cbd->data->etag = NULL;
  339. }
  340. }
  341. MAP_RETAIN(cbd->shmem_data, "shmem_data");
  342. cbd->data->gen++;
  343. /*
  344. * We know that a map is in the locked state
  345. */
  346. g_atomic_int_set(&data->cache->available, 1);
  347. /* Store cached data */
  348. rspamd_strlcpy(data->cache->shmem_name, cbd->shmem_data->shm_name,
  349. sizeof(data->cache->shmem_name));
  350. data->cache->len = cbd->data_len;
  351. data->cache->last_modified = cbd->data->last_modified;
  352. cache_cbd = g_malloc0(sizeof(*cache_cbd));
  353. cache_cbd->shm = cbd->shmem_data;
  354. cache_cbd->event_loop = cbd->event_loop;
  355. cache_cbd->map = map;
  356. cache_cbd->data = cbd->data;
  357. cache_cbd->last_checked = cbd->data->last_checked;
  358. cache_cbd->gen = cbd->data->gen;
  359. MAP_RETAIN(cache_cbd->shm, "shmem_data");
  360. msg_info_map("stored map data in a shared memory cache: %s",
  361. cache_cbd->shm->shm_name);
  362. ev_timer_init(&cache_cbd->timeout, rspamd_map_cache_cb, cached_timeout,
  363. 0.0);
  364. ev_timer_start(cbd->event_loop, &cache_cbd->timeout);
  365. cache_cbd->timeout.data = cache_cbd;
  366. data->cur_cache_cbd = cache_cbd;
  367. if (map->next_check) {
  368. rspamd_http_date_format(next_check_date, sizeof(next_check_date),
  369. map->next_check);
  370. }
  371. else {
  372. rspamd_http_date_format(next_check_date, sizeof(next_check_date),
  373. rspamd_get_calendar_ticks() + map->poll_timeout);
  374. }
  375. if (cbd->bk->is_compressed) {
  376. ZSTD_DStream *zstream;
  377. ZSTD_inBuffer zin;
  378. ZSTD_outBuffer zout;
  379. unsigned char *out;
  380. gsize outlen, r;
  381. zstream = ZSTD_createDStream();
  382. ZSTD_initDStream(zstream);
  383. zin.pos = 0;
  384. zin.src = in;
  385. zin.size = dlen;
  386. if ((outlen = ZSTD_getDecompressedSize(zin.src, zin.size)) == 0) {
  387. outlen = ZSTD_DStreamOutSize();
  388. }
  389. out = g_malloc(outlen);
  390. zout.dst = out;
  391. zout.pos = 0;
  392. zout.size = outlen;
  393. while (zin.pos < zin.size) {
  394. r = ZSTD_decompressStream(zstream, &zout, &zin);
  395. if (ZSTD_isError(r)) {
  396. msg_err_map("%s(%s): cannot decompress data: %s",
  397. cbd->bk->uri,
  398. rspamd_inet_address_to_string_pretty(cbd->addr),
  399. ZSTD_getErrorName(r));
  400. ZSTD_freeDStream(zstream);
  401. g_free(out);
  402. MAP_RELEASE(cbd->shmem_data, "shmem_data");
  403. goto err;
  404. }
  405. if (zout.pos == zout.size) {
  406. /* We need to extend output buffer */
  407. zout.size = zout.size * 2 + 1.0;
  408. out = g_realloc(zout.dst, zout.size);
  409. zout.dst = out;
  410. }
  411. }
  412. ZSTD_freeDStream(zstream);
  413. msg_info_map("%s(%s): read map data %z bytes compressed, "
  414. "%z uncompressed, next check at %s",
  415. cbd->bk->uri,
  416. rspamd_inet_address_to_string_pretty(cbd->addr),
  417. dlen, zout.pos, next_check_date);
  418. map->read_callback(out, zout.pos, &cbd->periodic->cbdata, TRUE);
  419. rspamd_map_save_http_cached_file(map, bk, cbd->data, out, zout.pos);
  420. g_free(out);
  421. }
  422. else {
  423. msg_info_map("%s(%s): read map data %z bytes, next check at %s",
  424. cbd->bk->uri,
  425. rspamd_inet_address_to_string_pretty(cbd->addr),
  426. dlen, next_check_date);
  427. rspamd_map_save_http_cached_file(map, bk, cbd->data, in, cbd->data_len);
  428. map->read_callback(in, cbd->data_len, &cbd->periodic->cbdata, TRUE);
  429. }
  430. MAP_RELEASE(cbd->shmem_data, "shmem_data");
  431. cbd->periodic->cur_backend++;
  432. munmap(in, dlen);
  433. rspamd_map_process_periodic(cbd->periodic);
  434. }
  435. else if (msg->code == 304 && cbd->check) {
  436. cbd->data->last_checked = msg->date;
  437. if (msg->last_modified) {
  438. cbd->data->last_modified = msg->last_modified;
  439. }
  440. else {
  441. cbd->data->last_modified = msg->date;
  442. }
  443. expires_hdr = rspamd_http_message_find_header(msg, "Expires");
  444. if (expires_hdr) {
  445. time_t hdate;
  446. hdate = rspamd_http_parse_date(expires_hdr->begin, expires_hdr->len);
  447. if (hdate != (time_t) -1 && hdate > msg->date) {
  448. map->next_check = hdate;
  449. }
  450. else {
  451. msg_info_map("invalid expires header: %T, ignore it", expires_hdr);
  452. map->next_check = 0;
  453. }
  454. }
  455. etag_hdr = rspamd_http_message_find_header(msg, "ETag");
  456. if (etag_hdr) {
  457. if (cbd->data->etag) {
  458. /* Remove old etag */
  459. rspamd_fstring_free(cbd->data->etag);
  460. cbd->data->etag = rspamd_fstring_new_init(etag_hdr->begin,
  461. etag_hdr->len);
  462. }
  463. }
  464. if (map->next_check) {
  465. rspamd_http_date_format(next_check_date, sizeof(next_check_date),
  466. map->next_check);
  467. msg_info_map("data is not modified for server %s, next check at %s "
  468. "(http cache based: %T)",
  469. cbd->data->host, next_check_date, expires_hdr);
  470. }
  471. else {
  472. rspamd_http_date_format(next_check_date, sizeof(next_check_date),
  473. rspamd_get_calendar_ticks() + map->poll_timeout);
  474. msg_info_map("data is not modified for server %s, next check at %s "
  475. "(timer based)",
  476. cbd->data->host, next_check_date);
  477. }
  478. rspamd_map_update_http_cached_file(map, bk, cbd->data);
  479. cbd->periodic->cur_backend++;
  480. rspamd_map_process_periodic(cbd->periodic);
  481. }
  482. else {
  483. msg_info_map("cannot load map %s from %s: HTTP error %d",
  484. bk->uri, cbd->data->host, msg->code);
  485. goto err;
  486. }
  487. MAP_RELEASE(cbd, "http_callback_data");
  488. return 0;
  489. err:
  490. cbd->periodic->errored = 1;
  491. rspamd_map_process_periodic(cbd->periodic);
  492. MAP_RELEASE(cbd, "http_callback_data");
  493. return 0;
  494. }
  495. static gboolean
  496. read_map_file_chunks(struct rspamd_map *map, struct map_cb_data *cbdata,
  497. const char *fname, gsize len, goffset off)
  498. {
  499. int fd;
  500. gssize r, avail;
  501. gsize buflen = 1024 * 1024;
  502. char *pos, *bytes;
  503. fd = rspamd_file_xopen(fname, O_RDONLY, 0, TRUE);
  504. if (fd == -1) {
  505. msg_err_map("can't open map for buffered reading %s: %s",
  506. fname, strerror(errno));
  507. return FALSE;
  508. }
  509. if (lseek(fd, off, SEEK_SET) == -1) {
  510. msg_err_map("can't seek in map to pos %d for buffered reading %s: %s",
  511. (int) off, fname, strerror(errno));
  512. close(fd);
  513. return FALSE;
  514. }
  515. buflen = MIN(len, buflen);
  516. bytes = g_malloc(buflen);
  517. avail = buflen;
  518. pos = bytes;
  519. while ((r = read(fd, pos, avail)) > 0) {
  520. char *end = bytes + (pos - bytes) + r;
  521. msg_debug_map("%s: read map chunk, %z bytes", fname,
  522. r);
  523. pos = map->read_callback(bytes, end - bytes, cbdata, r == len);
  524. if (pos && pos > bytes && pos < end) {
  525. unsigned int remain = end - pos;
  526. memmove(bytes, pos, remain);
  527. pos = bytes + remain;
  528. /* Need to preserve the remain */
  529. avail = ((gssize) buflen) - remain;
  530. if (avail <= 0) {
  531. /* Try realloc, too large element */
  532. g_assert(buflen >= remain);
  533. bytes = g_realloc(bytes, buflen * 2);
  534. pos = bytes + remain; /* Adjust */
  535. avail += buflen;
  536. buflen *= 2;
  537. }
  538. }
  539. else {
  540. avail = buflen;
  541. pos = bytes;
  542. }
  543. len -= r;
  544. }
  545. if (r == -1) {
  546. msg_err_map("can't read from map %s: %s", fname, strerror(errno));
  547. close(fd);
  548. g_free(bytes);
  549. return FALSE;
  550. }
  551. close(fd);
  552. g_free(bytes);
  553. return TRUE;
  554. }
  555. static gboolean
  556. rspamd_map_check_sig_pk_mem(const unsigned char *sig,
  557. gsize siglen,
  558. struct rspamd_map *map,
  559. const unsigned char *input,
  560. gsize inlen,
  561. struct rspamd_cryptobox_pubkey *pk)
  562. {
  563. GString *b32_key;
  564. gboolean ret = TRUE;
  565. if (siglen != rspamd_cryptobox_signature_bytes(RSPAMD_CRYPTOBOX_MODE_25519)) {
  566. msg_err_map("can't open signature for %s: invalid size: %z", map->name, siglen);
  567. ret = FALSE;
  568. }
  569. if (ret && !rspamd_cryptobox_verify(sig, siglen, input, inlen,
  570. rspamd_pubkey_get_pk(pk, NULL), RSPAMD_CRYPTOBOX_MODE_25519)) {
  571. msg_err_map("can't verify signature for %s: incorrect signature", map->name);
  572. ret = FALSE;
  573. }
  574. if (ret) {
  575. b32_key = rspamd_pubkey_print(pk,
  576. RSPAMD_KEYPAIR_BASE32 | RSPAMD_KEYPAIR_PUBKEY);
  577. msg_info_map("verified signature for %s using trusted key %v",
  578. map->name, b32_key);
  579. g_string_free(b32_key, TRUE);
  580. }
  581. return ret;
  582. }
  583. static gboolean
  584. rspamd_map_check_file_sig(const char *fname,
  585. struct rspamd_map *map,
  586. struct rspamd_map_backend *bk,
  587. const unsigned char *input,
  588. gsize inlen)
  589. {
  590. unsigned char *data;
  591. struct rspamd_cryptobox_pubkey *pk = NULL;
  592. GString *b32_key;
  593. gboolean ret = TRUE;
  594. gsize len = 0;
  595. char fpath[PATH_MAX];
  596. if (bk->trusted_pubkey == NULL) {
  597. /* Try to load and check pubkey */
  598. rspamd_snprintf(fpath, sizeof(fpath), "%s.pub", fname);
  599. data = rspamd_file_xmap(fpath, PROT_READ, &len, TRUE);
  600. if (data == NULL) {
  601. msg_err_map("can't open pubkey %s: %s", fpath, strerror(errno));
  602. return FALSE;
  603. }
  604. pk = rspamd_pubkey_from_base32(data, len, RSPAMD_KEYPAIR_SIGN,
  605. RSPAMD_CRYPTOBOX_MODE_25519);
  606. munmap(data, len);
  607. if (pk == NULL) {
  608. msg_err_map("can't load pubkey %s", fpath);
  609. return FALSE;
  610. }
  611. /* We just check pk against the trusted db of keys */
  612. b32_key = rspamd_pubkey_print(pk,
  613. RSPAMD_KEYPAIR_BASE32 | RSPAMD_KEYPAIR_PUBKEY);
  614. g_assert(b32_key != NULL);
  615. if (g_hash_table_lookup(map->cfg->trusted_keys, b32_key->str) == NULL) {
  616. msg_err_map("pubkey loaded from %s is untrusted: %v", fpath,
  617. b32_key);
  618. g_string_free(b32_key, TRUE);
  619. rspamd_pubkey_unref(pk);
  620. return FALSE;
  621. }
  622. g_string_free(b32_key, TRUE);
  623. }
  624. else {
  625. pk = rspamd_pubkey_ref(bk->trusted_pubkey);
  626. }
  627. rspamd_snprintf(fpath, sizeof(fpath), "%s.sig", fname);
  628. data = rspamd_shmem_xmap(fpath, PROT_READ, &len);
  629. if (data == NULL) {
  630. msg_err_map("can't open signature %s: %s", fpath, strerror(errno));
  631. ret = FALSE;
  632. }
  633. if (ret) {
  634. ret = rspamd_map_check_sig_pk_mem(data, len, map, input, inlen, pk);
  635. munmap(data, len);
  636. }
  637. rspamd_pubkey_unref(pk);
  638. return ret;
  639. }
  640. /**
  641. * Callback for reading data from file
  642. */
  643. static gboolean
  644. read_map_file(struct rspamd_map *map, struct file_map_data *data,
  645. struct rspamd_map_backend *bk, struct map_periodic_cbdata *periodic)
  646. {
  647. char *bytes;
  648. gsize len;
  649. struct stat st;
  650. if (map->read_callback == NULL || map->fin_callback == NULL) {
  651. msg_err_map("%s: bad callback for reading map file",
  652. data->filename);
  653. return FALSE;
  654. }
  655. if (stat(data->filename, &st) == -1) {
  656. /* File does not exist, skipping */
  657. if (errno != ENOENT) {
  658. msg_err_map("%s: map file is unavailable for reading: %s",
  659. data->filename, strerror(errno));
  660. return FALSE;
  661. }
  662. else {
  663. msg_info_map("%s: map file is not found; "
  664. "it will be read automatically if created",
  665. data->filename);
  666. return TRUE;
  667. }
  668. }
  669. ev_stat_stat(map->event_loop, &data->st_ev);
  670. len = st.st_size;
  671. if (bk->is_signed) {
  672. bytes = rspamd_file_xmap(data->filename, PROT_READ, &len, TRUE);
  673. if (bytes == NULL) {
  674. msg_err_map("can't open map %s: %s", data->filename, strerror(errno));
  675. return FALSE;
  676. }
  677. if (!rspamd_map_check_file_sig(data->filename, map, bk, bytes, len)) {
  678. munmap(bytes, len);
  679. return FALSE;
  680. }
  681. munmap(bytes, len);
  682. }
  683. if (len > 0) {
  684. if (map->no_file_read) {
  685. /* We just call read callback with backend name */
  686. map->read_callback(data->filename, strlen(data->filename),
  687. &periodic->cbdata, TRUE);
  688. }
  689. else {
  690. if (bk->is_compressed) {
  691. bytes = rspamd_file_xmap(data->filename, PROT_READ, &len, TRUE);
  692. if (bytes == NULL) {
  693. msg_err_map("can't open map %s: %s", data->filename, strerror(errno));
  694. return FALSE;
  695. }
  696. ZSTD_DStream *zstream;
  697. ZSTD_inBuffer zin;
  698. ZSTD_outBuffer zout;
  699. unsigned char *out;
  700. gsize outlen, r;
  701. zstream = ZSTD_createDStream();
  702. ZSTD_initDStream(zstream);
  703. zin.pos = 0;
  704. zin.src = bytes;
  705. zin.size = len;
  706. if ((outlen = ZSTD_getDecompressedSize(zin.src, zin.size)) == 0) {
  707. outlen = ZSTD_DStreamOutSize();
  708. }
  709. out = g_malloc(outlen);
  710. zout.dst = out;
  711. zout.pos = 0;
  712. zout.size = outlen;
  713. while (zin.pos < zin.size) {
  714. r = ZSTD_decompressStream(zstream, &zout, &zin);
  715. if (ZSTD_isError(r)) {
  716. msg_err_map("%s: cannot decompress data: %s",
  717. data->filename,
  718. ZSTD_getErrorName(r));
  719. ZSTD_freeDStream(zstream);
  720. g_free(out);
  721. munmap(bytes, len);
  722. return FALSE;
  723. }
  724. if (zout.pos == zout.size) {
  725. /* We need to extend output buffer */
  726. zout.size = zout.size * 2 + 1;
  727. out = g_realloc(zout.dst, zout.size);
  728. zout.dst = out;
  729. }
  730. }
  731. ZSTD_freeDStream(zstream);
  732. msg_info_map("%s: read map data, %z bytes compressed, "
  733. "%z uncompressed)",
  734. data->filename,
  735. len, zout.pos);
  736. map->read_callback(out, zout.pos, &periodic->cbdata, TRUE);
  737. g_free(out);
  738. munmap(bytes, len);
  739. }
  740. else {
  741. /* Perform buffered read: fail-safe */
  742. if (!read_map_file_chunks(map, &periodic->cbdata, data->filename,
  743. len, 0)) {
  744. return FALSE;
  745. }
  746. }
  747. }
  748. }
  749. else {
  750. /* Empty map */
  751. map->read_callback(NULL, 0, &periodic->cbdata, TRUE);
  752. }
  753. return TRUE;
  754. }
  755. static gboolean
  756. read_map_static(struct rspamd_map *map, struct static_map_data *data,
  757. struct rspamd_map_backend *bk, struct map_periodic_cbdata *periodic)
  758. {
  759. unsigned char *bytes;
  760. gsize len;
  761. if (map->read_callback == NULL || map->fin_callback == NULL) {
  762. msg_err_map("%s: bad callback for reading map file", map->name);
  763. data->processed = TRUE;
  764. return FALSE;
  765. }
  766. bytes = data->data;
  767. len = data->len;
  768. if (len > 0) {
  769. if (bk->is_compressed) {
  770. ZSTD_DStream *zstream;
  771. ZSTD_inBuffer zin;
  772. ZSTD_outBuffer zout;
  773. unsigned char *out;
  774. gsize outlen, r;
  775. zstream = ZSTD_createDStream();
  776. ZSTD_initDStream(zstream);
  777. zin.pos = 0;
  778. zin.src = bytes;
  779. zin.size = len;
  780. if ((outlen = ZSTD_getDecompressedSize(zin.src, zin.size)) == 0) {
  781. outlen = ZSTD_DStreamOutSize();
  782. }
  783. out = g_malloc(outlen);
  784. zout.dst = out;
  785. zout.pos = 0;
  786. zout.size = outlen;
  787. while (zin.pos < zin.size) {
  788. r = ZSTD_decompressStream(zstream, &zout, &zin);
  789. if (ZSTD_isError(r)) {
  790. msg_err_map("%s: cannot decompress data: %s",
  791. map->name,
  792. ZSTD_getErrorName(r));
  793. ZSTD_freeDStream(zstream);
  794. g_free(out);
  795. return FALSE;
  796. }
  797. if (zout.pos == zout.size) {
  798. /* We need to extend output buffer */
  799. zout.size = zout.size * 2 + 1;
  800. out = g_realloc(zout.dst, zout.size);
  801. zout.dst = out;
  802. }
  803. }
  804. ZSTD_freeDStream(zstream);
  805. msg_info_map("%s: read map data, %z bytes compressed, "
  806. "%z uncompressed)",
  807. map->name,
  808. len, zout.pos);
  809. map->read_callback(out, zout.pos, &periodic->cbdata, TRUE);
  810. g_free(out);
  811. }
  812. else {
  813. msg_info_map("%s: read map data, %z bytes",
  814. map->name, len);
  815. map->read_callback(bytes, len, &periodic->cbdata, TRUE);
  816. }
  817. }
  818. else {
  819. map->read_callback(NULL, 0, &periodic->cbdata, TRUE);
  820. }
  821. data->processed = TRUE;
  822. return TRUE;
  823. }
  824. static void
  825. rspamd_map_periodic_dtor(struct map_periodic_cbdata *periodic)
  826. {
  827. struct rspamd_map *map;
  828. map = periodic->map;
  829. msg_debug_map("periodic dtor %p; need_modify=%d", periodic, periodic->need_modify);
  830. if (periodic->need_modify || periodic->cbdata.errored) {
  831. /* Need to notify the real data structure */
  832. periodic->map->fin_callback(&periodic->cbdata, periodic->map->user_data);
  833. if (map->on_load_function) {
  834. map->on_load_function(map, map->on_load_ud);
  835. }
  836. }
  837. else {
  838. /* Not modified */
  839. }
  840. if (periodic->locked) {
  841. g_atomic_int_set(periodic->map->locked, 0);
  842. msg_debug_map("unlocked map %s", periodic->map->name);
  843. if (periodic->map->wrk->state == rspamd_worker_state_running) {
  844. rspamd_map_schedule_periodic(periodic->map,
  845. RSPAMD_SYMBOL_RESULT_NORMAL);
  846. }
  847. else {
  848. msg_debug_map("stop scheduling periodics for %s; terminating state",
  849. periodic->map->name);
  850. }
  851. }
  852. g_free(periodic);
  853. }
  854. /* Called on timer execution */
  855. static void
  856. rspamd_map_periodic_callback(struct ev_loop *loop, ev_timer *w, int revents)
  857. {
  858. struct map_periodic_cbdata *cbd = (struct map_periodic_cbdata *) w->data;
  859. MAP_RETAIN(cbd, "periodic");
  860. ev_timer_stop(loop, w);
  861. rspamd_map_process_periodic(cbd);
  862. MAP_RELEASE(cbd, "periodic");
  863. }
  864. static void
  865. rspamd_map_schedule_periodic(struct rspamd_map *map, int how)
  866. {
  867. const double error_mult = 20.0, lock_mult = 0.1;
  868. static const double min_timer_interval = 2.0;
  869. const char *reason = "unknown reason";
  870. double jittered_sec;
  871. double timeout;
  872. struct map_periodic_cbdata *cbd;
  873. if (map->scheduled_check || (map->wrk &&
  874. map->wrk->state != rspamd_worker_state_running)) {
  875. /*
  876. * Do not schedule check if some check is already scheduled or
  877. * if worker is going to die
  878. */
  879. return;
  880. }
  881. if (!(how & RSPAMD_MAP_SCHEDULE_INIT) && map->static_only) {
  882. /* No need to schedule anything for static maps */
  883. return;
  884. }
  885. map->seen = true;
  886. if (map->non_trivial && map->next_check != 0) {
  887. timeout = map->next_check - rspamd_get_calendar_ticks();
  888. map->next_check = 0;
  889. if (timeout > 0 && timeout < map->poll_timeout) {
  890. /* Early check case, jitter */
  891. double poll_timeout = map->poll_timeout;
  892. if (how & RSPAMD_MAP_SCHEDULE_ERROR) {
  893. poll_timeout = map->poll_timeout * error_mult;
  894. reason = "early active non-trivial check (after error)";
  895. }
  896. else if (how & RSPAMD_MAP_SCHEDULE_LOCKED) {
  897. poll_timeout = map->poll_timeout * lock_mult;
  898. reason = "early active non-trivial check (after being locked)";
  899. }
  900. else {
  901. reason = "early active non-trivial check";
  902. }
  903. jittered_sec = MIN(timeout, poll_timeout);
  904. }
  905. else if (timeout <= 0) {
  906. /* Data is already expired, need to check */
  907. if (how & RSPAMD_MAP_SCHEDULE_ERROR) {
  908. /* In case of error we still need to increase delay */
  909. jittered_sec = map->poll_timeout * error_mult;
  910. reason = "expired non-trivial data (after error)";
  911. }
  912. else {
  913. jittered_sec = 0.0;
  914. reason = "expired non-trivial data";
  915. }
  916. }
  917. else {
  918. /* No need to check now, wait till next_check */
  919. jittered_sec = timeout;
  920. reason = "valid non-trivial data";
  921. }
  922. }
  923. else {
  924. /* No valid information when to check a map, plan a timer based check */
  925. timeout = map->poll_timeout;
  926. if (how & RSPAMD_MAP_SCHEDULE_INIT) {
  927. if (map->non_trivial && map->active_http) {
  928. /* Spill maps load to get better chances to hit ssl cache */
  929. timeout = rspamd_time_jitter(0.0, 2.0);
  930. }
  931. else {
  932. timeout = 0.0;
  933. }
  934. reason = "init scheduled check";
  935. }
  936. else {
  937. if (how & RSPAMD_MAP_SCHEDULE_ERROR) {
  938. timeout = map->poll_timeout * error_mult;
  939. reason = "errored scheduled check";
  940. }
  941. else if (how & RSPAMD_MAP_SCHEDULE_LOCKED) {
  942. timeout = map->poll_timeout * lock_mult;
  943. reason = "locked scheduled check";
  944. }
  945. else {
  946. reason = "normal scheduled check";
  947. }
  948. }
  949. jittered_sec = rspamd_time_jitter(timeout, 0);
  950. }
  951. /* Now, we do some sanity checks for jittered seconds */
  952. if (!(how & RSPAMD_MAP_SCHEDULE_INIT)) {
  953. /* Never allow too low interval between timer checks, it is expensive */
  954. if (jittered_sec < min_timer_interval) {
  955. jittered_sec = rspamd_time_jitter(min_timer_interval, 0);
  956. }
  957. if (map->non_trivial) {
  958. /*
  959. * Even if we are reported that we need to reload cache often, we
  960. * still want to be sane in terms of events...
  961. */
  962. if (jittered_sec < min_timer_interval * 2.0) {
  963. if (map->nelts > 0) {
  964. jittered_sec = min_timer_interval * 3.0;
  965. }
  966. }
  967. }
  968. }
  969. cbd = g_malloc0(sizeof(*cbd));
  970. cbd->cbdata.prev_data = *map->user_data;
  971. cbd->cbdata.cur_data = NULL;
  972. cbd->cbdata.map = map;
  973. cbd->map = map;
  974. map->scheduled_check = cbd;
  975. REF_INIT_RETAIN(cbd, rspamd_map_periodic_dtor);
  976. cbd->ev.data = cbd;
  977. ev_timer_init(&cbd->ev, rspamd_map_periodic_callback, jittered_sec, 0.0);
  978. ev_timer_start(map->event_loop, &cbd->ev);
  979. msg_debug_map("schedule new periodic event %p in %.3f seconds for %s; reason: %s",
  980. cbd, jittered_sec, map->name, reason);
  981. }
  982. static int
  983. rspamd_map_af_to_weight(const rspamd_inet_addr_t *addr)
  984. {
  985. int ret;
  986. switch (rspamd_inet_address_get_af(addr)) {
  987. case AF_UNIX:
  988. ret = 2;
  989. break;
  990. case AF_INET:
  991. ret = 1;
  992. break;
  993. default:
  994. ret = 0;
  995. break;
  996. }
  997. return ret;
  998. }
  999. static int
  1000. rspamd_map_dns_address_sort_func(gconstpointer a, gconstpointer b)
  1001. {
  1002. const rspamd_inet_addr_t *ip1 = *(const rspamd_inet_addr_t **) a,
  1003. *ip2 = *(const rspamd_inet_addr_t **) b;
  1004. int w1, w2;
  1005. w1 = rspamd_map_af_to_weight(ip1);
  1006. w2 = rspamd_map_af_to_weight(ip2);
  1007. /* Inverse order */
  1008. return w2 - w1;
  1009. }
  1010. static void
  1011. rspamd_map_dns_callback(struct rdns_reply *reply, void *arg)
  1012. {
  1013. struct http_callback_data *cbd = arg;
  1014. struct rdns_reply_entry *cur_rep;
  1015. struct rspamd_map *map;
  1016. unsigned int flags = RSPAMD_HTTP_CLIENT_SIMPLE | RSPAMD_HTTP_CLIENT_SHARED;
  1017. map = cbd->map;
  1018. msg_debug_map("got dns reply with code %s on stage %d",
  1019. rdns_strerror(reply->code), cbd->stage);
  1020. if (cbd->stage == http_map_terminated) {
  1021. MAP_RELEASE(cbd, "http_callback_data");
  1022. return;
  1023. }
  1024. if (reply->code == RDNS_RC_NOERROR) {
  1025. DL_FOREACH(reply->entries, cur_rep)
  1026. {
  1027. rspamd_inet_addr_t *addr;
  1028. addr = rspamd_inet_address_from_rnds(cur_rep);
  1029. if (addr != NULL) {
  1030. rspamd_inet_address_set_port(addr, cbd->data->port);
  1031. g_ptr_array_add(cbd->addrs, (void *) addr);
  1032. }
  1033. }
  1034. if (cbd->stage == http_map_resolve_host2) {
  1035. /* We have still one request pending */
  1036. cbd->stage = http_map_resolve_host1;
  1037. }
  1038. else if (cbd->stage == http_map_resolve_host1) {
  1039. cbd->stage = http_map_http_conn;
  1040. }
  1041. }
  1042. else if (cbd->stage < http_map_http_conn) {
  1043. if (cbd->stage == http_map_resolve_host2) {
  1044. /* We have still one request pending */
  1045. cbd->stage = http_map_resolve_host1;
  1046. }
  1047. else if (cbd->addrs->len == 0) {
  1048. /* We could not resolve host, so cowardly fail here */
  1049. msg_err_map("cannot resolve %s: %s", cbd->data->host,
  1050. rdns_strerror(reply->code));
  1051. cbd->periodic->errored = 1;
  1052. rspamd_map_process_periodic(cbd->periodic);
  1053. }
  1054. else {
  1055. /* We have at least one address, so we can continue... */
  1056. cbd->stage = http_map_http_conn;
  1057. }
  1058. }
  1059. if (cbd->stage == http_map_http_conn && cbd->addrs->len > 0) {
  1060. rspamd_ptr_array_shuffle(cbd->addrs);
  1061. int idx = 0;
  1062. /*
  1063. * For the existing addr we can just select any address as we have
  1064. * data available
  1065. */
  1066. if (cbd->map->nelts > 0 && rspamd_random_double_fast() > 0.5) {
  1067. /* Already shuffled, use whatever is the first */
  1068. cbd->addr = (rspamd_inet_addr_t *) g_ptr_array_index(cbd->addrs, idx);
  1069. }
  1070. else {
  1071. /* Always prefer IPv4 as IPv6 is almost all the time broken */
  1072. g_ptr_array_sort(cbd->addrs, rspamd_map_dns_address_sort_func);
  1073. cbd->addr = (rspamd_inet_addr_t *) g_ptr_array_index(cbd->addrs, idx);
  1074. }
  1075. retry:
  1076. msg_debug_map("try open http connection to %s",
  1077. rspamd_inet_address_to_string_pretty(cbd->addr));
  1078. if (cbd->bk->protocol == MAP_PROTO_HTTPS) {
  1079. flags |= RSPAMD_HTTP_CLIENT_SSL;
  1080. }
  1081. cbd->conn = rspamd_http_connection_new_client(NULL,
  1082. NULL,
  1083. http_map_error,
  1084. http_map_finish,
  1085. flags,
  1086. cbd->addr);
  1087. if (cbd->conn != NULL) {
  1088. write_http_request(cbd);
  1089. }
  1090. else {
  1091. if (idx < cbd->addrs->len - 1) {
  1092. /* We can retry */
  1093. idx++;
  1094. rspamd_inet_addr_t *prev_addr = cbd->addr;
  1095. cbd->addr = (rspamd_inet_addr_t *) g_ptr_array_index(cbd->addrs, idx);
  1096. msg_info_map("cannot connect to %s to get data for %s: %s, retry with %s (%d of %d)",
  1097. rspamd_inet_address_to_string_pretty(prev_addr),
  1098. cbd->bk->uri,
  1099. strerror(errno),
  1100. rspamd_inet_address_to_string_pretty(cbd->addr),
  1101. idx + 1, cbd->addrs->len);
  1102. goto retry;
  1103. }
  1104. else {
  1105. /* Nothing else left */
  1106. cbd->periodic->errored = TRUE;
  1107. msg_err_map("error reading %s(%s): "
  1108. "connection with http server terminated incorrectly: %s",
  1109. cbd->bk->uri,
  1110. cbd->addr ? rspamd_inet_address_to_string_pretty(cbd->addr) : "",
  1111. strerror(errno));
  1112. rspamd_map_process_periodic(cbd->periodic);
  1113. }
  1114. }
  1115. }
  1116. MAP_RELEASE(cbd, "http_callback_data");
  1117. }
  1118. static gboolean
  1119. rspamd_map_read_cached(struct rspamd_map *map, struct rspamd_map_backend *bk,
  1120. struct map_periodic_cbdata *periodic, const char *host)
  1121. {
  1122. gsize mmap_len, len;
  1123. gpointer in;
  1124. struct http_map_data *data;
  1125. data = bk->data.hd;
  1126. in = rspamd_shmem_xmap(data->cache->shmem_name, PROT_READ, &mmap_len);
  1127. if (in == NULL) {
  1128. msg_err("cannot map cache from %s: %s", data->cache->shmem_name,
  1129. strerror(errno));
  1130. return FALSE;
  1131. }
  1132. if (mmap_len < data->cache->len) {
  1133. msg_err("cannot map cache from %s: truncated length %z, %z expected",
  1134. data->cache->shmem_name,
  1135. mmap_len, data->cache->len);
  1136. munmap(in, mmap_len);
  1137. return FALSE;
  1138. }
  1139. /*
  1140. * Len is taken from the shmem file size that can be larger than the
  1141. * actual data length, as we use shared memory as a growing buffer for the
  1142. * HTTP input.
  1143. * Hence, we need to use len from the saved cache data, counting that it is
  1144. * at least not more than the cached file length (this is checked above).
  1145. */
  1146. len = data->cache->len;
  1147. if (bk->is_compressed) {
  1148. ZSTD_DStream *zstream;
  1149. ZSTD_inBuffer zin;
  1150. ZSTD_outBuffer zout;
  1151. unsigned char *out;
  1152. gsize outlen, r;
  1153. zstream = ZSTD_createDStream();
  1154. ZSTD_initDStream(zstream);
  1155. zin.pos = 0;
  1156. zin.src = in;
  1157. zin.size = len;
  1158. if ((outlen = ZSTD_getDecompressedSize(zin.src, zin.size)) == 0) {
  1159. outlen = ZSTD_DStreamOutSize();
  1160. }
  1161. out = g_malloc(outlen);
  1162. zout.dst = out;
  1163. zout.pos = 0;
  1164. zout.size = outlen;
  1165. while (zin.pos < zin.size) {
  1166. r = ZSTD_decompressStream(zstream, &zout, &zin);
  1167. if (ZSTD_isError(r)) {
  1168. msg_err_map("%s: cannot decompress data: %s",
  1169. bk->uri,
  1170. ZSTD_getErrorName(r));
  1171. ZSTD_freeDStream(zstream);
  1172. g_free(out);
  1173. munmap(in, mmap_len);
  1174. return FALSE;
  1175. }
  1176. if (zout.pos == zout.size) {
  1177. /* We need to extend output buffer */
  1178. zout.size = zout.size * 2 + 1;
  1179. out = g_realloc(zout.dst, zout.size);
  1180. zout.dst = out;
  1181. }
  1182. }
  1183. ZSTD_freeDStream(zstream);
  1184. msg_info_map("%s: read map data cached %z bytes compressed, "
  1185. "%z uncompressed",
  1186. bk->uri,
  1187. len, zout.pos);
  1188. map->read_callback(out, zout.pos, &periodic->cbdata, TRUE);
  1189. g_free(out);
  1190. }
  1191. else {
  1192. msg_info_map("%s: read map data cached %z bytes", bk->uri, len);
  1193. map->read_callback(in, len, &periodic->cbdata, TRUE);
  1194. }
  1195. munmap(in, mmap_len);
  1196. return TRUE;
  1197. }
  1198. static gboolean
  1199. rspamd_map_has_http_cached_file(struct rspamd_map *map,
  1200. struct rspamd_map_backend *bk)
  1201. {
  1202. char path[PATH_MAX];
  1203. unsigned char digest[rspamd_cryptobox_HASHBYTES];
  1204. struct rspamd_config *cfg = map->cfg;
  1205. struct stat st;
  1206. if (cfg->maps_cache_dir == NULL || cfg->maps_cache_dir[0] == '\0') {
  1207. return FALSE;
  1208. }
  1209. rspamd_cryptobox_hash(digest, bk->uri, strlen(bk->uri), NULL, 0);
  1210. rspamd_snprintf(path, sizeof(path), "%s%c%*xs.map", cfg->maps_cache_dir,
  1211. G_DIR_SEPARATOR, 20, digest);
  1212. if (stat(path, &st) != -1 && st.st_size >
  1213. sizeof(struct rspamd_http_file_data)) {
  1214. return TRUE;
  1215. }
  1216. return FALSE;
  1217. }
  1218. static gboolean
  1219. rspamd_map_save_http_cached_file(struct rspamd_map *map,
  1220. struct rspamd_map_backend *bk,
  1221. struct http_map_data *htdata,
  1222. const unsigned char *data,
  1223. gsize len)
  1224. {
  1225. char path[PATH_MAX];
  1226. unsigned char digest[rspamd_cryptobox_HASHBYTES];
  1227. struct rspamd_config *cfg = map->cfg;
  1228. int fd;
  1229. struct rspamd_http_file_data header;
  1230. if (cfg->maps_cache_dir == NULL || cfg->maps_cache_dir[0] == '\0') {
  1231. return FALSE;
  1232. }
  1233. rspamd_cryptobox_hash(digest, bk->uri, strlen(bk->uri), NULL, 0);
  1234. rspamd_snprintf(path, sizeof(path), "%s%c%*xs.map", cfg->maps_cache_dir,
  1235. G_DIR_SEPARATOR, 20, digest);
  1236. fd = rspamd_file_xopen(path, O_WRONLY | O_TRUNC | O_CREAT,
  1237. 00600, FALSE);
  1238. if (fd == -1) {
  1239. return FALSE;
  1240. }
  1241. if (!rspamd_file_lock(fd, FALSE)) {
  1242. msg_err_map("cannot lock file %s: %s", path, strerror(errno));
  1243. close(fd);
  1244. return FALSE;
  1245. }
  1246. memcpy(header.magic, rspamd_http_file_magic, sizeof(rspamd_http_file_magic));
  1247. header.mtime = htdata->last_modified;
  1248. header.next_check = map->next_check;
  1249. header.data_off = sizeof(header);
  1250. if (htdata->etag) {
  1251. header.data_off += RSPAMD_FSTRING_LEN(htdata->etag);
  1252. header.etag_len = RSPAMD_FSTRING_LEN(htdata->etag);
  1253. }
  1254. else {
  1255. header.etag_len = 0;
  1256. }
  1257. if (write(fd, &header, sizeof(header)) != sizeof(header)) {
  1258. msg_err_map("cannot write file %s (header stage): %s", path, strerror(errno));
  1259. rspamd_file_unlock(fd, FALSE);
  1260. close(fd);
  1261. return FALSE;
  1262. }
  1263. if (header.etag_len > 0) {
  1264. if (write(fd, RSPAMD_FSTRING_DATA(htdata->etag), header.etag_len) !=
  1265. header.etag_len) {
  1266. msg_err_map("cannot write file %s (etag stage): %s", path, strerror(errno));
  1267. rspamd_file_unlock(fd, FALSE);
  1268. close(fd);
  1269. return FALSE;
  1270. }
  1271. }
  1272. /* Now write the rest */
  1273. if (write(fd, data, len) != len) {
  1274. msg_err_map("cannot write file %s (data stage): %s", path, strerror(errno));
  1275. rspamd_file_unlock(fd, FALSE);
  1276. close(fd);
  1277. return FALSE;
  1278. }
  1279. rspamd_file_unlock(fd, FALSE);
  1280. close(fd);
  1281. msg_info_map("saved data from %s in %s, %uz bytes", bk->uri, path, len + sizeof(header) + header.etag_len);
  1282. return TRUE;
  1283. }
  1284. static gboolean
  1285. rspamd_map_update_http_cached_file(struct rspamd_map *map,
  1286. struct rspamd_map_backend *bk,
  1287. struct http_map_data *htdata)
  1288. {
  1289. char path[PATH_MAX];
  1290. unsigned char digest[rspamd_cryptobox_HASHBYTES];
  1291. struct rspamd_config *cfg = map->cfg;
  1292. int fd;
  1293. struct rspamd_http_file_data header;
  1294. if (!rspamd_map_has_http_cached_file(map, bk)) {
  1295. return FALSE;
  1296. }
  1297. rspamd_cryptobox_hash(digest, bk->uri, strlen(bk->uri), NULL, 0);
  1298. rspamd_snprintf(path, sizeof(path), "%s%c%*xs.map", cfg->maps_cache_dir,
  1299. G_DIR_SEPARATOR, 20, digest);
  1300. fd = rspamd_file_xopen(path, O_WRONLY,
  1301. 00600, FALSE);
  1302. if (fd == -1) {
  1303. return FALSE;
  1304. }
  1305. if (!rspamd_file_lock(fd, FALSE)) {
  1306. msg_err_map("cannot lock file %s: %s", path, strerror(errno));
  1307. close(fd);
  1308. return FALSE;
  1309. }
  1310. memcpy(header.magic, rspamd_http_file_magic, sizeof(rspamd_http_file_magic));
  1311. header.mtime = htdata->last_modified;
  1312. header.next_check = map->next_check;
  1313. header.data_off = sizeof(header);
  1314. if (htdata->etag) {
  1315. header.data_off += RSPAMD_FSTRING_LEN(htdata->etag);
  1316. header.etag_len = RSPAMD_FSTRING_LEN(htdata->etag);
  1317. }
  1318. else {
  1319. header.etag_len = 0;
  1320. }
  1321. if (write(fd, &header, sizeof(header)) != sizeof(header)) {
  1322. msg_err_map("cannot update file %s (header stage): %s", path, strerror(errno));
  1323. rspamd_file_unlock(fd, FALSE);
  1324. close(fd);
  1325. return FALSE;
  1326. }
  1327. if (header.etag_len > 0) {
  1328. if (write(fd, RSPAMD_FSTRING_DATA(htdata->etag), header.etag_len) !=
  1329. header.etag_len) {
  1330. msg_err_map("cannot update file %s (etag stage): %s", path, strerror(errno));
  1331. rspamd_file_unlock(fd, FALSE);
  1332. close(fd);
  1333. return FALSE;
  1334. }
  1335. }
  1336. rspamd_file_unlock(fd, FALSE);
  1337. close(fd);
  1338. return TRUE;
  1339. }
  1340. static gboolean
  1341. rspamd_map_read_http_cached_file(struct rspamd_map *map,
  1342. struct rspamd_map_backend *bk,
  1343. struct http_map_data *htdata,
  1344. struct map_cb_data *cbdata)
  1345. {
  1346. char path[PATH_MAX];
  1347. unsigned char digest[rspamd_cryptobox_HASHBYTES];
  1348. struct rspamd_config *cfg = map->cfg;
  1349. int fd;
  1350. struct stat st;
  1351. struct rspamd_http_file_data header;
  1352. if (cfg->maps_cache_dir == NULL || cfg->maps_cache_dir[0] == '\0') {
  1353. return FALSE;
  1354. }
  1355. rspamd_cryptobox_hash(digest, bk->uri, strlen(bk->uri), NULL, 0);
  1356. rspamd_snprintf(path, sizeof(path), "%s%c%*xs.map", cfg->maps_cache_dir,
  1357. G_DIR_SEPARATOR, 20, digest);
  1358. fd = rspamd_file_xopen(path, O_RDONLY, 00600, FALSE);
  1359. if (fd == -1) {
  1360. return FALSE;
  1361. }
  1362. if (!rspamd_file_lock(fd, FALSE)) {
  1363. msg_err_map("cannot lock file %s: %s", path, strerror(errno));
  1364. close(fd);
  1365. return FALSE;
  1366. }
  1367. (void) fstat(fd, &st);
  1368. if (read(fd, &header, sizeof(header)) != sizeof(header)) {
  1369. msg_err_map("cannot read file %s (header stage): %s", path, strerror(errno));
  1370. rspamd_file_unlock(fd, FALSE);
  1371. close(fd);
  1372. return FALSE;
  1373. }
  1374. if (memcmp(header.magic, rspamd_http_file_magic,
  1375. sizeof(rspamd_http_file_magic)) != 0) {
  1376. msg_warn_map("invalid or old version magic in file %s; ignore it", path);
  1377. rspamd_file_unlock(fd, FALSE);
  1378. close(fd);
  1379. return FALSE;
  1380. }
  1381. double now = rspamd_get_calendar_ticks();
  1382. if (header.next_check > now) {
  1383. map->next_check = header.next_check;
  1384. }
  1385. else {
  1386. map->next_check = now;
  1387. }
  1388. htdata->last_modified = header.mtime;
  1389. if (header.etag_len > 0) {
  1390. rspamd_fstring_t *etag = rspamd_fstring_sized_new(header.etag_len);
  1391. if (read(fd, RSPAMD_FSTRING_DATA(etag), header.etag_len) != header.etag_len) {
  1392. msg_err_map("cannot read file %s (etag stage): %s", path,
  1393. strerror(errno));
  1394. rspamd_file_unlock(fd, FALSE);
  1395. rspamd_fstring_free(etag);
  1396. close(fd);
  1397. return FALSE;
  1398. }
  1399. etag->len = header.etag_len;
  1400. if (htdata->etag) {
  1401. /* FIXME: should be dealt somehow better */
  1402. msg_warn_map("etag is already defined as %V; cached is %V; ignore cached",
  1403. htdata->etag, etag);
  1404. rspamd_fstring_free(etag);
  1405. }
  1406. else {
  1407. htdata->etag = etag;
  1408. }
  1409. }
  1410. rspamd_file_unlock(fd, FALSE);
  1411. close(fd);
  1412. /* Now read file data */
  1413. /* Perform buffered read: fail-safe */
  1414. if (!read_map_file_chunks(map, cbdata, path,
  1415. st.st_size - header.data_off, header.data_off)) {
  1416. return FALSE;
  1417. }
  1418. struct tm tm;
  1419. char ncheck_buf[32], lm_buf[32];
  1420. rspamd_localtime(map->next_check, &tm);
  1421. strftime(ncheck_buf, sizeof(ncheck_buf) - 1, "%Y-%m-%d %H:%M:%S", &tm);
  1422. rspamd_localtime(htdata->last_modified, &tm);
  1423. strftime(lm_buf, sizeof(lm_buf) - 1, "%Y-%m-%d %H:%M:%S", &tm);
  1424. msg_info_map("read cached data for %s from %s, %uz bytes; next check at: %s;"
  1425. " last modified on: %s; etag: %V",
  1426. bk->uri,
  1427. path,
  1428. (size_t) (st.st_size - header.data_off),
  1429. ncheck_buf,
  1430. lm_buf,
  1431. htdata->etag);
  1432. return TRUE;
  1433. }
  1434. /**
  1435. * Async HTTP callback
  1436. */
  1437. static void
  1438. rspamd_map_common_http_callback(struct rspamd_map *map,
  1439. struct rspamd_map_backend *bk,
  1440. struct map_periodic_cbdata *periodic,
  1441. gboolean check)
  1442. {
  1443. struct http_map_data *data;
  1444. struct http_callback_data *cbd;
  1445. unsigned int flags = RSPAMD_HTTP_CLIENT_SIMPLE | RSPAMD_HTTP_CLIENT_SHARED;
  1446. data = bk->data.hd;
  1447. if (g_atomic_int_get(&data->cache->available) == 1) {
  1448. /* Read cached data */
  1449. if (check) {
  1450. if (data->last_modified < data->cache->last_modified) {
  1451. msg_info_map("need to reread cached map triggered by %s "
  1452. "(%d our modify time, %d cached modify time)",
  1453. bk->uri,
  1454. (int) data->last_modified,
  1455. (int) data->cache->last_modified);
  1456. periodic->need_modify = TRUE;
  1457. /* Reset the whole chain */
  1458. periodic->cur_backend = 0;
  1459. rspamd_map_process_periodic(periodic);
  1460. }
  1461. else {
  1462. if (map->active_http) {
  1463. /* Check even if there is a cached version */
  1464. goto check;
  1465. }
  1466. else {
  1467. /* Switch to the next backend */
  1468. periodic->cur_backend++;
  1469. rspamd_map_process_periodic(periodic);
  1470. }
  1471. }
  1472. return;
  1473. }
  1474. else {
  1475. if (map->active_http &&
  1476. data->last_modified > data->cache->last_modified) {
  1477. goto check;
  1478. }
  1479. else if (rspamd_map_read_cached(map, bk, periodic, data->host)) {
  1480. /* Switch to the next backend */
  1481. periodic->cur_backend++;
  1482. data->last_modified = data->cache->last_modified;
  1483. rspamd_map_process_periodic(periodic);
  1484. return;
  1485. }
  1486. }
  1487. }
  1488. else if (!map->active_http) {
  1489. /* Switch to the next backend */
  1490. periodic->cur_backend++;
  1491. rspamd_map_process_periodic(periodic);
  1492. return;
  1493. }
  1494. check:
  1495. cbd = g_malloc0(sizeof(struct http_callback_data));
  1496. cbd->event_loop = map->event_loop;
  1497. cbd->addrs = g_ptr_array_sized_new(4);
  1498. cbd->map = map;
  1499. cbd->data = data;
  1500. cbd->check = check;
  1501. cbd->periodic = periodic;
  1502. MAP_RETAIN(periodic, "periodic");
  1503. cbd->bk = bk;
  1504. MAP_RETAIN(bk, "rspamd_map_backend");
  1505. cbd->stage = http_map_terminated;
  1506. REF_INIT_RETAIN(cbd, free_http_cbdata);
  1507. msg_debug_map("%s map data from %s", check ? "checking" : "reading",
  1508. data->host);
  1509. /* Try address */
  1510. rspamd_inet_addr_t *addr = NULL;
  1511. if (rspamd_parse_inet_address(&addr, data->host,
  1512. strlen(data->host), RSPAMD_INET_ADDRESS_PARSE_DEFAULT)) {
  1513. rspamd_inet_address_set_port(addr, cbd->data->port);
  1514. g_ptr_array_add(cbd->addrs, (void *) addr);
  1515. if (bk->protocol == MAP_PROTO_HTTPS) {
  1516. flags |= RSPAMD_HTTP_CLIENT_SSL;
  1517. }
  1518. cbd->conn = rspamd_http_connection_new_client(
  1519. NULL,
  1520. NULL,
  1521. http_map_error,
  1522. http_map_finish,
  1523. flags,
  1524. addr);
  1525. if (cbd->conn != NULL) {
  1526. cbd->stage = http_map_http_conn;
  1527. write_http_request(cbd);
  1528. cbd->addr = addr;
  1529. MAP_RELEASE(cbd, "http_callback_data");
  1530. }
  1531. else {
  1532. msg_warn_map("cannot load map: cannot connect to %s: %s",
  1533. data->host, strerror(errno));
  1534. MAP_RELEASE(cbd, "http_callback_data");
  1535. }
  1536. return;
  1537. }
  1538. else if (map->r->r) {
  1539. /* Send both A and AAAA requests */
  1540. unsigned int nreq = 0;
  1541. if (rdns_make_request_full(map->r->r, rspamd_map_dns_callback, cbd,
  1542. map->cfg->dns_timeout, map->cfg->dns_retransmits, 1,
  1543. data->host, RDNS_REQUEST_A)) {
  1544. MAP_RETAIN(cbd, "http_callback_data");
  1545. nreq++;
  1546. }
  1547. if (rdns_make_request_full(map->r->r, rspamd_map_dns_callback, cbd,
  1548. map->cfg->dns_timeout, map->cfg->dns_retransmits, 1,
  1549. data->host, RDNS_REQUEST_AAAA)) {
  1550. MAP_RETAIN(cbd, "http_callback_data");
  1551. nreq++;
  1552. }
  1553. if (nreq == 2) {
  1554. cbd->stage = http_map_resolve_host2;
  1555. }
  1556. else if (nreq == 1) {
  1557. cbd->stage = http_map_resolve_host1;
  1558. }
  1559. map->tmp_dtor = free_http_cbdata_dtor;
  1560. map->tmp_dtor_data = cbd;
  1561. }
  1562. else {
  1563. msg_warn_map("cannot load map: DNS resolver is not initialized");
  1564. cbd->periodic->errored = TRUE;
  1565. }
  1566. MAP_RELEASE(cbd, "http_callback_data");
  1567. }
  1568. static void
  1569. rspamd_map_http_check_callback(struct map_periodic_cbdata *cbd)
  1570. {
  1571. struct rspamd_map *map;
  1572. struct rspamd_map_backend *bk;
  1573. map = cbd->map;
  1574. bk = g_ptr_array_index(cbd->map->backends, cbd->cur_backend);
  1575. rspamd_map_common_http_callback(map, bk, cbd, TRUE);
  1576. }
  1577. static void
  1578. rspamd_map_http_read_callback(struct map_periodic_cbdata *cbd)
  1579. {
  1580. struct rspamd_map *map;
  1581. struct rspamd_map_backend *bk;
  1582. map = cbd->map;
  1583. bk = g_ptr_array_index(cbd->map->backends, cbd->cur_backend);
  1584. rspamd_map_common_http_callback(map, bk, cbd, FALSE);
  1585. }
  1586. static void
  1587. rspamd_map_file_check_callback(struct map_periodic_cbdata *periodic)
  1588. {
  1589. struct rspamd_map *map;
  1590. struct file_map_data *data;
  1591. struct rspamd_map_backend *bk;
  1592. map = periodic->map;
  1593. bk = g_ptr_array_index(map->backends, periodic->cur_backend);
  1594. data = bk->data.fd;
  1595. if (data->need_modify) {
  1596. periodic->need_modify = TRUE;
  1597. periodic->cur_backend = 0;
  1598. data->need_modify = FALSE;
  1599. rspamd_map_process_periodic(periodic);
  1600. return;
  1601. }
  1602. map = periodic->map;
  1603. /* Switch to the next backend as the rest is handled by ev_stat */
  1604. periodic->cur_backend++;
  1605. rspamd_map_process_periodic(periodic);
  1606. }
  1607. static void
  1608. rspamd_map_static_check_callback(struct map_periodic_cbdata *periodic)
  1609. {
  1610. struct rspamd_map *map;
  1611. struct static_map_data *data;
  1612. struct rspamd_map_backend *bk;
  1613. map = periodic->map;
  1614. bk = g_ptr_array_index(map->backends, periodic->cur_backend);
  1615. data = bk->data.sd;
  1616. if (!data->processed) {
  1617. periodic->need_modify = TRUE;
  1618. periodic->cur_backend = 0;
  1619. rspamd_map_process_periodic(periodic);
  1620. return;
  1621. }
  1622. /* Switch to the next backend */
  1623. periodic->cur_backend++;
  1624. rspamd_map_process_periodic(periodic);
  1625. }
  1626. static void
  1627. rspamd_map_file_read_callback(struct map_periodic_cbdata *periodic)
  1628. {
  1629. struct rspamd_map *map;
  1630. struct file_map_data *data;
  1631. struct rspamd_map_backend *bk;
  1632. map = periodic->map;
  1633. bk = g_ptr_array_index(map->backends, periodic->cur_backend);
  1634. data = bk->data.fd;
  1635. msg_info_map("rereading map file %s", data->filename);
  1636. if (!read_map_file(map, data, bk, periodic)) {
  1637. periodic->errored = TRUE;
  1638. }
  1639. /* Switch to the next backend */
  1640. periodic->cur_backend++;
  1641. rspamd_map_process_periodic(periodic);
  1642. }
  1643. static void
  1644. rspamd_map_static_read_callback(struct map_periodic_cbdata *periodic)
  1645. {
  1646. struct rspamd_map *map;
  1647. struct static_map_data *data;
  1648. struct rspamd_map_backend *bk;
  1649. map = periodic->map;
  1650. bk = g_ptr_array_index(map->backends, periodic->cur_backend);
  1651. data = bk->data.sd;
  1652. msg_info_map("rereading static map");
  1653. if (!read_map_static(map, data, bk, periodic)) {
  1654. periodic->errored = TRUE;
  1655. }
  1656. /* Switch to the next backend */
  1657. periodic->cur_backend++;
  1658. rspamd_map_process_periodic(periodic);
  1659. }
  1660. static void
  1661. rspamd_map_process_periodic(struct map_periodic_cbdata *cbd)
  1662. {
  1663. struct rspamd_map_backend *bk;
  1664. struct rspamd_map *map;
  1665. map = cbd->map;
  1666. map->scheduled_check = NULL;
  1667. if (!map->file_only && !cbd->locked) {
  1668. if (!g_atomic_int_compare_and_exchange(cbd->map->locked,
  1669. 0, 1)) {
  1670. msg_debug_map(
  1671. "don't try to reread map %s as it is locked by other process, "
  1672. "will reread it later",
  1673. cbd->map->name);
  1674. rspamd_map_schedule_periodic(map, RSPAMD_MAP_SCHEDULE_LOCKED);
  1675. MAP_RELEASE(cbd, "periodic");
  1676. return;
  1677. }
  1678. else {
  1679. msg_debug_map("locked map %s", cbd->map->name);
  1680. cbd->locked = TRUE;
  1681. }
  1682. }
  1683. if (cbd->errored) {
  1684. /* We should not check other backends if some backend has failed*/
  1685. rspamd_map_schedule_periodic(cbd->map, RSPAMD_MAP_SCHEDULE_ERROR);
  1686. if (cbd->locked) {
  1687. g_atomic_int_set(cbd->map->locked, 0);
  1688. cbd->locked = FALSE;
  1689. }
  1690. /* Also set error flag for the map consumer */
  1691. cbd->cbdata.errored = true;
  1692. msg_debug_map("unlocked map %s, refcount=%d", cbd->map->name,
  1693. cbd->ref.refcount);
  1694. MAP_RELEASE(cbd, "periodic");
  1695. return;
  1696. }
  1697. /* For each backend we need to check for modifications */
  1698. if (cbd->cur_backend >= cbd->map->backends->len) {
  1699. /* Last backend */
  1700. msg_debug_map("finished map: %d of %d", cbd->cur_backend,
  1701. cbd->map->backends->len);
  1702. MAP_RELEASE(cbd, "periodic");
  1703. return;
  1704. }
  1705. if (cbd->map->wrk && cbd->map->wrk->state == rspamd_worker_state_running) {
  1706. bk = g_ptr_array_index(cbd->map->backends, cbd->cur_backend);
  1707. g_assert(bk != NULL);
  1708. if (cbd->need_modify) {
  1709. /* Load data from the next backend */
  1710. switch (bk->protocol) {
  1711. case MAP_PROTO_HTTP:
  1712. case MAP_PROTO_HTTPS:
  1713. rspamd_map_http_read_callback(cbd);
  1714. break;
  1715. case MAP_PROTO_FILE:
  1716. rspamd_map_file_read_callback(cbd);
  1717. break;
  1718. case MAP_PROTO_STATIC:
  1719. rspamd_map_static_read_callback(cbd);
  1720. break;
  1721. }
  1722. }
  1723. else {
  1724. /* Check the next backend */
  1725. switch (bk->protocol) {
  1726. case MAP_PROTO_HTTP:
  1727. case MAP_PROTO_HTTPS:
  1728. rspamd_map_http_check_callback(cbd);
  1729. break;
  1730. case MAP_PROTO_FILE:
  1731. rspamd_map_file_check_callback(cbd);
  1732. break;
  1733. case MAP_PROTO_STATIC:
  1734. rspamd_map_static_check_callback(cbd);
  1735. break;
  1736. }
  1737. }
  1738. }
  1739. }
  1740. static void
  1741. rspamd_map_on_stat(struct ev_loop *loop, ev_stat *w, int revents)
  1742. {
  1743. struct rspamd_map *map = (struct rspamd_map *) w->data;
  1744. if (w->attr.st_nlink > 0) {
  1745. msg_info_map("old mtime is %t (size = %Hz), "
  1746. "new mtime is %t (size = %Hz) for map file %s",
  1747. w->prev.st_mtime, (gsize) w->prev.st_size,
  1748. w->attr.st_mtime, (gsize) w->attr.st_size,
  1749. w->path);
  1750. /* Fire need modify flag */
  1751. struct rspamd_map_backend *bk;
  1752. unsigned int i;
  1753. PTR_ARRAY_FOREACH(map->backends, i, bk)
  1754. {
  1755. if (bk->protocol == MAP_PROTO_FILE) {
  1756. bk->data.fd->need_modify = TRUE;
  1757. }
  1758. }
  1759. map->next_check = 0;
  1760. if (map->scheduled_check) {
  1761. ev_timer_stop(map->event_loop, &map->scheduled_check->ev);
  1762. MAP_RELEASE(map->scheduled_check, "rspamd_map_on_stat");
  1763. map->scheduled_check = NULL;
  1764. }
  1765. rspamd_map_schedule_periodic(map, RSPAMD_MAP_SCHEDULE_INIT);
  1766. }
  1767. }
  1768. /* Start watching event for all maps */
  1769. void rspamd_map_watch(struct rspamd_config *cfg,
  1770. struct ev_loop *event_loop,
  1771. struct rspamd_dns_resolver *resolver,
  1772. struct rspamd_worker *worker,
  1773. enum rspamd_map_watch_type how)
  1774. {
  1775. GList *cur = cfg->maps;
  1776. struct rspamd_map *map;
  1777. struct rspamd_map_backend *bk;
  1778. unsigned int i;
  1779. g_assert(how > RSPAMD_MAP_WATCH_MIN && how < RSPAMD_MAP_WATCH_MAX);
  1780. /* First of all do synced read of data */
  1781. while (cur) {
  1782. map = cur->data;
  1783. map->event_loop = event_loop;
  1784. map->r = resolver;
  1785. if (map->wrk == NULL && how != RSPAMD_MAP_WATCH_WORKER) {
  1786. /* Generic scanner map */
  1787. map->wrk = worker;
  1788. if (how == RSPAMD_MAP_WATCH_PRIMARY_CONTROLLER) {
  1789. map->active_http = TRUE;
  1790. }
  1791. else {
  1792. map->active_http = FALSE;
  1793. }
  1794. }
  1795. else if (map->wrk != NULL && map->wrk == worker) {
  1796. /* Map is bound to a specific worker */
  1797. map->active_http = TRUE;
  1798. }
  1799. else {
  1800. /* Skip map for this worker as irrelevant */
  1801. cur = g_list_next(cur);
  1802. continue;
  1803. }
  1804. if (!map->active_http) {
  1805. /* Check cached version more frequently as it is cheap */
  1806. if (map->poll_timeout >= cfg->map_timeout &&
  1807. cfg->map_file_watch_multiplier < 1.0) {
  1808. map->poll_timeout =
  1809. map->poll_timeout * cfg->map_file_watch_multiplier;
  1810. }
  1811. }
  1812. map->file_only = TRUE;
  1813. map->static_only = TRUE;
  1814. PTR_ARRAY_FOREACH(map->backends, i, bk)
  1815. {
  1816. bk->event_loop = event_loop;
  1817. if (bk->protocol == MAP_PROTO_FILE) {
  1818. struct file_map_data *data;
  1819. data = bk->data.fd;
  1820. if (!map->seen || map->user_data == NULL || *map->user_data == NULL) {
  1821. /* Map has not been read, init it's reading if possible */
  1822. struct stat st;
  1823. if (stat(data->filename, &st) != -1) {
  1824. data->need_modify = TRUE;
  1825. }
  1826. }
  1827. ev_stat_init(&data->st_ev, rspamd_map_on_stat,
  1828. data->filename, map->poll_timeout * cfg->map_file_watch_multiplier);
  1829. data->st_ev.data = map;
  1830. ev_stat_start(event_loop, &data->st_ev);
  1831. map->static_only = FALSE;
  1832. }
  1833. else if ((bk->protocol == MAP_PROTO_HTTP ||
  1834. bk->protocol == MAP_PROTO_HTTPS)) {
  1835. if (map->active_http) {
  1836. map->non_trivial = TRUE;
  1837. }
  1838. map->static_only = FALSE;
  1839. map->file_only = FALSE;
  1840. }
  1841. }
  1842. rspamd_map_schedule_periodic(map, RSPAMD_MAP_SCHEDULE_INIT);
  1843. cur = g_list_next(cur);
  1844. }
  1845. }
  1846. void rspamd_map_preload(struct rspamd_config *cfg)
  1847. {
  1848. GList *cur = cfg->maps;
  1849. struct rspamd_map *map;
  1850. struct rspamd_map_backend *bk;
  1851. unsigned int i;
  1852. gboolean map_ok;
  1853. /* First of all do synced read of data */
  1854. while (cur) {
  1855. map = cur->data;
  1856. map_ok = TRUE;
  1857. PTR_ARRAY_FOREACH(map->backends, i, bk)
  1858. {
  1859. if (!(bk->protocol == MAP_PROTO_FILE ||
  1860. bk->protocol == MAP_PROTO_STATIC)) {
  1861. if (bk->protocol == MAP_PROTO_HTTP ||
  1862. bk->protocol == MAP_PROTO_HTTPS) {
  1863. if (!rspamd_map_has_http_cached_file(map, bk)) {
  1864. if (!map->fallback_backend) {
  1865. map_ok = FALSE;
  1866. }
  1867. break;
  1868. }
  1869. else {
  1870. continue; /* We are yet fine */
  1871. }
  1872. }
  1873. map_ok = FALSE;
  1874. break;
  1875. }
  1876. }
  1877. if (map_ok) {
  1878. struct map_periodic_cbdata fake_cbd;
  1879. gboolean succeed = TRUE;
  1880. memset(&fake_cbd, 0, sizeof(fake_cbd));
  1881. fake_cbd.cbdata.state = 0;
  1882. fake_cbd.cbdata.prev_data = *map->user_data;
  1883. fake_cbd.cbdata.cur_data = NULL;
  1884. fake_cbd.cbdata.map = map;
  1885. fake_cbd.map = map;
  1886. PTR_ARRAY_FOREACH(map->backends, i, bk)
  1887. {
  1888. fake_cbd.cur_backend = i;
  1889. if (bk->protocol == MAP_PROTO_FILE) {
  1890. if (!read_map_file(map, bk->data.fd, bk, &fake_cbd)) {
  1891. succeed = FALSE;
  1892. break;
  1893. }
  1894. }
  1895. else if (bk->protocol == MAP_PROTO_STATIC) {
  1896. if (!read_map_static(map, bk->data.sd, bk, &fake_cbd)) {
  1897. succeed = FALSE;
  1898. break;
  1899. }
  1900. }
  1901. else if (bk->protocol == MAP_PROTO_HTTP ||
  1902. bk->protocol == MAP_PROTO_HTTPS) {
  1903. if (!rspamd_map_read_http_cached_file(map, bk, bk->data.hd,
  1904. &fake_cbd.cbdata)) {
  1905. if (map->fallback_backend) {
  1906. /* Try fallback */
  1907. g_assert(map->fallback_backend->protocol ==
  1908. MAP_PROTO_FILE);
  1909. if (!read_map_file(map,
  1910. map->fallback_backend->data.fd,
  1911. map->fallback_backend, &fake_cbd)) {
  1912. succeed = FALSE;
  1913. break;
  1914. }
  1915. }
  1916. else {
  1917. succeed = FALSE;
  1918. break;
  1919. }
  1920. }
  1921. }
  1922. else {
  1923. g_assert_not_reached();
  1924. }
  1925. }
  1926. if (succeed) {
  1927. map->fin_callback(&fake_cbd.cbdata, map->user_data);
  1928. if (map->on_load_function) {
  1929. map->on_load_function(map, map->on_load_ud);
  1930. }
  1931. map->seen = true;
  1932. }
  1933. else {
  1934. msg_info_map("preload of %s failed", map->name);
  1935. }
  1936. }
  1937. cur = g_list_next(cur);
  1938. }
  1939. }
  1940. void rspamd_map_remove_all(struct rspamd_config *cfg)
  1941. {
  1942. struct rspamd_map *map;
  1943. GList *cur;
  1944. struct rspamd_map_backend *bk;
  1945. struct map_cb_data cbdata;
  1946. unsigned int i;
  1947. for (cur = cfg->maps; cur != NULL; cur = g_list_next(cur)) {
  1948. map = cur->data;
  1949. if (map->tmp_dtor) {
  1950. map->tmp_dtor(map->tmp_dtor_data);
  1951. }
  1952. if (map->dtor) {
  1953. cbdata.prev_data = NULL;
  1954. cbdata.map = map;
  1955. cbdata.cur_data = *map->user_data;
  1956. map->dtor(&cbdata);
  1957. *map->user_data = NULL;
  1958. }
  1959. if (map->on_load_ud_dtor) {
  1960. map->on_load_ud_dtor(map->on_load_ud);
  1961. }
  1962. for (i = 0; i < map->backends->len; i++) {
  1963. bk = g_ptr_array_index(map->backends, i);
  1964. MAP_RELEASE(bk, "rspamd_map_backend");
  1965. }
  1966. if (map->fallback_backend) {
  1967. MAP_RELEASE(map->fallback_backend, "rspamd_map_backend");
  1968. }
  1969. }
  1970. g_list_free(cfg->maps);
  1971. cfg->maps = NULL;
  1972. }
  1973. static const char *
  1974. rspamd_map_check_proto(struct rspamd_config *cfg,
  1975. const char *map_line, struct rspamd_map_backend *bk)
  1976. {
  1977. const char *pos = map_line, *end, *end_key;
  1978. g_assert(bk != NULL);
  1979. g_assert(pos != NULL);
  1980. end = pos + strlen(pos);
  1981. /* Static check */
  1982. if (g_ascii_strcasecmp(pos, "static") == 0) {
  1983. bk->protocol = MAP_PROTO_STATIC;
  1984. bk->uri = g_strdup(pos);
  1985. return pos;
  1986. }
  1987. else if (g_ascii_strcasecmp(pos, "zst+static") == 0) {
  1988. bk->protocol = MAP_PROTO_STATIC;
  1989. bk->uri = g_strdup(pos + 4);
  1990. bk->is_compressed = TRUE;
  1991. return pos + 4;
  1992. }
  1993. for (;;) {
  1994. if (g_ascii_strncasecmp(pos, "sign+", sizeof("sign+") - 1) == 0) {
  1995. bk->is_signed = TRUE;
  1996. pos += sizeof("sign+") - 1;
  1997. }
  1998. else if (g_ascii_strncasecmp(pos, "fallback+", sizeof("fallback+") - 1) == 0) {
  1999. bk->is_fallback = TRUE;
  2000. pos += sizeof("fallback+") - 1;
  2001. }
  2002. else if (g_ascii_strncasecmp(pos, "key=", sizeof("key=") - 1) == 0) {
  2003. pos += sizeof("key=") - 1;
  2004. end_key = memchr(pos, '+', end - pos);
  2005. if (end_key != NULL) {
  2006. bk->trusted_pubkey = rspamd_pubkey_from_base32(pos, end_key - pos,
  2007. RSPAMD_KEYPAIR_SIGN, RSPAMD_CRYPTOBOX_MODE_25519);
  2008. if (bk->trusted_pubkey == NULL) {
  2009. msg_err_config("cannot read pubkey from map: %s",
  2010. map_line);
  2011. return NULL;
  2012. }
  2013. pos = end_key + 1;
  2014. }
  2015. else if (end - pos > 64) {
  2016. /* Try hex encoding */
  2017. bk->trusted_pubkey = rspamd_pubkey_from_hex(pos, 64,
  2018. RSPAMD_KEYPAIR_SIGN, RSPAMD_CRYPTOBOX_MODE_25519);
  2019. if (bk->trusted_pubkey == NULL) {
  2020. msg_err_config("cannot read pubkey from map: %s",
  2021. map_line);
  2022. return NULL;
  2023. }
  2024. pos += 64;
  2025. }
  2026. else {
  2027. msg_err_config("cannot read pubkey from map: %s",
  2028. map_line);
  2029. return NULL;
  2030. }
  2031. if (*pos == '+' || *pos == ':') {
  2032. pos++;
  2033. }
  2034. }
  2035. else {
  2036. /* No known flags */
  2037. break;
  2038. }
  2039. }
  2040. bk->protocol = MAP_PROTO_FILE;
  2041. if (g_ascii_strncasecmp(pos, "http://", sizeof("http://") - 1) == 0) {
  2042. bk->protocol = MAP_PROTO_HTTP;
  2043. /* Include http:// */
  2044. bk->uri = g_strdup(pos);
  2045. pos += sizeof("http://") - 1;
  2046. }
  2047. else if (g_ascii_strncasecmp(pos, "https://", sizeof("https://") - 1) == 0) {
  2048. bk->protocol = MAP_PROTO_HTTPS;
  2049. /* Include https:// */
  2050. bk->uri = g_strdup(pos);
  2051. pos += sizeof("https://") - 1;
  2052. }
  2053. else if (g_ascii_strncasecmp(pos, "file://", sizeof("file://") - 1) == 0) {
  2054. pos += sizeof("file://") - 1;
  2055. /* Exclude file:// */
  2056. bk->uri = g_strdup(pos);
  2057. }
  2058. else if (*pos == '/') {
  2059. /* Trivial file case */
  2060. bk->uri = g_strdup(pos);
  2061. }
  2062. else {
  2063. msg_err_config("invalid map fetching protocol: %s", map_line);
  2064. return NULL;
  2065. }
  2066. if (bk->protocol != MAP_PROTO_FILE && bk->is_signed) {
  2067. msg_err_config("signed maps are no longer supported for HTTP(s): %s", map_line);
  2068. }
  2069. return pos;
  2070. }
  2071. gboolean
  2072. rspamd_map_is_map(const char *map_line)
  2073. {
  2074. gboolean ret = FALSE;
  2075. g_assert(map_line != NULL);
  2076. if (map_line[0] == '/') {
  2077. ret = TRUE;
  2078. }
  2079. else if (g_ascii_strncasecmp(map_line, "sign+", sizeof("sign+") - 1) == 0) {
  2080. ret = TRUE;
  2081. }
  2082. else if (g_ascii_strncasecmp(map_line, "fallback+", sizeof("fallback+") - 1) == 0) {
  2083. ret = TRUE;
  2084. }
  2085. else if (g_ascii_strncasecmp(map_line, "file://", sizeof("file://") - 1) == 0) {
  2086. ret = TRUE;
  2087. }
  2088. else if (g_ascii_strncasecmp(map_line, "http://", sizeof("http://") - 1) == 0) {
  2089. ret = TRUE;
  2090. }
  2091. else if (g_ascii_strncasecmp(map_line, "https://", sizeof("https://") - 1) == 0) {
  2092. ret = TRUE;
  2093. }
  2094. return ret;
  2095. }
  2096. static void
  2097. rspamd_map_backend_dtor(struct rspamd_map_backend *bk)
  2098. {
  2099. switch (bk->protocol) {
  2100. case MAP_PROTO_FILE:
  2101. if (bk->data.fd) {
  2102. ev_stat_stop(bk->event_loop, &bk->data.fd->st_ev);
  2103. g_free(bk->data.fd->filename);
  2104. g_free(bk->data.fd);
  2105. }
  2106. break;
  2107. case MAP_PROTO_STATIC:
  2108. if (bk->data.sd) {
  2109. if (bk->data.sd->data) {
  2110. g_free(bk->data.sd->data);
  2111. }
  2112. g_free(bk->data.sd);
  2113. }
  2114. break;
  2115. case MAP_PROTO_HTTP:
  2116. case MAP_PROTO_HTTPS:
  2117. if (bk->data.hd) {
  2118. struct http_map_data *data = bk->data.hd;
  2119. g_free(data->host);
  2120. g_free(data->path);
  2121. g_free(data->rest);
  2122. if (data->userinfo) {
  2123. g_free(data->userinfo);
  2124. }
  2125. if (data->etag) {
  2126. rspamd_fstring_free(data->etag);
  2127. }
  2128. /*
  2129. * Clear cached file, but check if a worker is an active http worker
  2130. * as cur_cache_cbd is meaningful merely for active worker, who actually
  2131. * owns the cache
  2132. */
  2133. if (bk->map && bk->map->active_http) {
  2134. if (g_atomic_int_compare_and_exchange(&data->cache->available, 1, 0)) {
  2135. if (data->cur_cache_cbd) {
  2136. msg_info("clear shared memory cache for a map in %s as backend \"%s\" is closing",
  2137. data->cur_cache_cbd->shm->shm_name,
  2138. bk->uri);
  2139. MAP_RELEASE(data->cur_cache_cbd->shm,
  2140. "rspamd_http_map_cached_cbdata");
  2141. ev_timer_stop(data->cur_cache_cbd->event_loop,
  2142. &data->cur_cache_cbd->timeout);
  2143. g_free(data->cur_cache_cbd);
  2144. data->cur_cache_cbd = NULL;
  2145. }
  2146. }
  2147. }
  2148. g_free(bk->data.hd);
  2149. }
  2150. break;
  2151. }
  2152. if (bk->trusted_pubkey) {
  2153. rspamd_pubkey_unref(bk->trusted_pubkey);
  2154. }
  2155. g_free(bk->uri);
  2156. g_free(bk);
  2157. }
  2158. static struct rspamd_map_backend *
  2159. rspamd_map_parse_backend(struct rspamd_config *cfg, const char *map_line)
  2160. {
  2161. struct rspamd_map_backend *bk;
  2162. struct file_map_data *fdata = NULL;
  2163. struct http_map_data *hdata = NULL;
  2164. struct static_map_data *sdata = NULL;
  2165. struct http_parser_url up;
  2166. const char *end, *p;
  2167. rspamd_ftok_t tok;
  2168. bk = g_malloc0(sizeof(*bk));
  2169. REF_INIT_RETAIN(bk, rspamd_map_backend_dtor);
  2170. if (!rspamd_map_check_proto(cfg, map_line, bk)) {
  2171. goto err;
  2172. }
  2173. if (bk->is_fallback && bk->protocol != MAP_PROTO_FILE) {
  2174. msg_err_config("fallback backend must be file for %s", bk->uri);
  2175. goto err;
  2176. }
  2177. end = map_line + strlen(map_line);
  2178. if (end - map_line > 5) {
  2179. p = end - 5;
  2180. if (g_ascii_strcasecmp(p, ".zstd") == 0) {
  2181. bk->is_compressed = TRUE;
  2182. }
  2183. p = end - 4;
  2184. if (g_ascii_strcasecmp(p, ".zst") == 0) {
  2185. bk->is_compressed = TRUE;
  2186. }
  2187. }
  2188. /* Now check for each proto separately */
  2189. if (bk->protocol == MAP_PROTO_FILE) {
  2190. fdata = g_malloc0(sizeof(struct file_map_data));
  2191. if (access(bk->uri, R_OK) == -1) {
  2192. if (errno != ENOENT) {
  2193. msg_err_config("cannot open file '%s': %s", bk->uri, strerror(errno));
  2194. goto err;
  2195. }
  2196. msg_info_config(
  2197. "map '%s' is not found, but it can be loaded automatically later",
  2198. bk->uri);
  2199. }
  2200. fdata->filename = g_strdup(bk->uri);
  2201. bk->data.fd = fdata;
  2202. }
  2203. else if (bk->protocol == MAP_PROTO_HTTP || bk->protocol == MAP_PROTO_HTTPS) {
  2204. hdata = g_malloc0(sizeof(struct http_map_data));
  2205. memset(&up, 0, sizeof(up));
  2206. if (http_parser_parse_url(bk->uri, strlen(bk->uri), FALSE,
  2207. &up) != 0) {
  2208. msg_err_config("cannot parse HTTP url: %s", bk->uri);
  2209. goto err;
  2210. }
  2211. else {
  2212. if (!(up.field_set & 1u << UF_HOST)) {
  2213. msg_err_config("cannot parse HTTP url: %s: no host", bk->uri);
  2214. goto err;
  2215. }
  2216. tok.begin = bk->uri + up.field_data[UF_HOST].off;
  2217. tok.len = up.field_data[UF_HOST].len;
  2218. hdata->host = rspamd_ftokdup(&tok);
  2219. if (up.field_set & (1u << UF_PORT)) {
  2220. hdata->port = up.port;
  2221. }
  2222. else {
  2223. if (bk->protocol == MAP_PROTO_HTTP) {
  2224. hdata->port = 80;
  2225. }
  2226. else {
  2227. hdata->port = 443;
  2228. }
  2229. }
  2230. if (up.field_set & (1u << UF_PATH)) {
  2231. tok.begin = bk->uri + up.field_data[UF_PATH].off;
  2232. tok.len = up.field_data[UF_PATH].len;
  2233. hdata->path = rspamd_ftokdup(&tok);
  2234. /* We also need to check query + fragment */
  2235. if (up.field_set & ((1u << UF_QUERY) | (1u << UF_FRAGMENT))) {
  2236. tok.begin = bk->uri + up.field_data[UF_PATH].off +
  2237. up.field_data[UF_PATH].len;
  2238. tok.len = strlen(tok.begin);
  2239. hdata->rest = rspamd_ftokdup(&tok);
  2240. }
  2241. else {
  2242. hdata->rest = g_strdup("");
  2243. }
  2244. }
  2245. if (up.field_set & (1u << UF_USERINFO)) {
  2246. /* Create authorisation header for basic auth */
  2247. unsigned int len = sizeof("Basic ") +
  2248. up.field_data[UF_USERINFO].len * 8 / 5 + 4;
  2249. hdata->userinfo = g_malloc(len);
  2250. rspamd_snprintf(hdata->userinfo, len, "Basic %*Bs",
  2251. (int) up.field_data[UF_USERINFO].len,
  2252. bk->uri + up.field_data[UF_USERINFO].off);
  2253. msg_debug("added userinfo for the map from the URL: %s", hdata->host);
  2254. }
  2255. else {
  2256. /* Try to obtain authentication data from options in the configuration */
  2257. const ucl_object_t *auth_obj, *opts_obj;
  2258. opts_obj = ucl_object_lookup(cfg->cfg_ucl_obj, "options");
  2259. if (opts_obj != NULL) {
  2260. auth_obj = ucl_object_lookup(opts_obj, "http_auth");
  2261. if (auth_obj != NULL && ucl_object_type(auth_obj) == UCL_OBJECT) {
  2262. const ucl_object_t *host_obj;
  2263. /*
  2264. * Search first by the full URL and then by the host part
  2265. */
  2266. host_obj = ucl_object_lookup(auth_obj, map_line);
  2267. if (host_obj == NULL) {
  2268. host_obj = ucl_object_lookup(auth_obj, hdata->host);
  2269. }
  2270. if (host_obj != NULL && ucl_object_type(host_obj) == UCL_OBJECT) {
  2271. const ucl_object_t *user_obj, *password_obj;
  2272. user_obj = ucl_object_lookup(host_obj, "user");
  2273. password_obj = ucl_object_lookup(host_obj, "password");
  2274. if (user_obj != NULL && password_obj != NULL &&
  2275. ucl_object_type(user_obj) == UCL_STRING &&
  2276. ucl_object_type(password_obj) == UCL_STRING) {
  2277. char *tmpbuf;
  2278. unsigned tlen;
  2279. /* User + password + ':' */
  2280. tlen = strlen(ucl_object_tostring(user_obj)) +
  2281. strlen(ucl_object_tostring(password_obj)) + 1;
  2282. tmpbuf = g_malloc(tlen + 1);
  2283. rspamd_snprintf(tmpbuf, tlen + 1, "%s:%s",
  2284. ucl_object_tostring(user_obj),
  2285. ucl_object_tostring(password_obj));
  2286. /* Base64 encoding is not so greedy, but we add some space for simplicity */
  2287. tlen *= 2;
  2288. tlen += sizeof("Basic ") - 1;
  2289. hdata->userinfo = g_malloc(tlen + 1);
  2290. rspamd_snprintf(hdata->userinfo, tlen + 1, "Basic %Bs", tmpbuf);
  2291. g_free(tmpbuf);
  2292. msg_debug("added userinfo for the map from the configuration: %s", map_line);
  2293. }
  2294. }
  2295. }
  2296. }
  2297. }
  2298. }
  2299. hdata->cache = rspamd_mempool_alloc0_shared(cfg->cfg_pool,
  2300. sizeof(*hdata->cache));
  2301. bk->data.hd = hdata;
  2302. }
  2303. else if (bk->protocol == MAP_PROTO_STATIC) {
  2304. sdata = g_malloc0(sizeof(*sdata));
  2305. bk->data.sd = sdata;
  2306. }
  2307. bk->id = rspamd_cryptobox_fast_hash_specific(RSPAMD_CRYPTOBOX_T1HA,
  2308. bk->uri, strlen(bk->uri),
  2309. 0xdeadbabe);
  2310. return bk;
  2311. err:
  2312. MAP_RELEASE(bk, "rspamd_map_backend");
  2313. if (hdata) {
  2314. g_free(hdata);
  2315. }
  2316. if (fdata) {
  2317. g_free(fdata);
  2318. }
  2319. if (sdata) {
  2320. g_free(sdata);
  2321. }
  2322. return NULL;
  2323. }
  2324. static void
  2325. rspamd_map_calculate_hash(struct rspamd_map *map)
  2326. {
  2327. struct rspamd_map_backend *bk;
  2328. unsigned int i;
  2329. rspamd_cryptobox_hash_state_t st;
  2330. char *cksum_encoded, cksum[rspamd_cryptobox_HASHBYTES];
  2331. rspamd_cryptobox_hash_init(&st, NULL, 0);
  2332. for (i = 0; i < map->backends->len; i++) {
  2333. bk = g_ptr_array_index(map->backends, i);
  2334. rspamd_cryptobox_hash_update(&st, bk->uri, strlen(bk->uri));
  2335. }
  2336. rspamd_cryptobox_hash_final(&st, cksum);
  2337. cksum_encoded = rspamd_encode_base32(cksum, sizeof(cksum), RSPAMD_BASE32_DEFAULT);
  2338. rspamd_strlcpy(map->tag, cksum_encoded, sizeof(map->tag));
  2339. g_free(cksum_encoded);
  2340. }
  2341. static gboolean
  2342. rspamd_map_add_static_string(struct rspamd_config *cfg,
  2343. const ucl_object_t *elt,
  2344. GString *target)
  2345. {
  2346. gsize sz;
  2347. const char *dline;
  2348. if (ucl_object_type(elt) != UCL_STRING) {
  2349. msg_err_config("map has static backend but `data` is "
  2350. "not string like: %s",
  2351. ucl_object_type_to_string(elt->type));
  2352. return FALSE;
  2353. }
  2354. /* Otherwise, we copy data to the backend */
  2355. dline = ucl_object_tolstring(elt, &sz);
  2356. if (sz == 0) {
  2357. msg_err_config("map has static backend but empty no data");
  2358. return FALSE;
  2359. }
  2360. g_string_append_len(target, dline, sz);
  2361. g_string_append_c(target, '\n');
  2362. return TRUE;
  2363. }
  2364. struct rspamd_map *
  2365. rspamd_map_add(struct rspamd_config *cfg,
  2366. const char *map_line,
  2367. const char *description,
  2368. map_cb_t read_callback,
  2369. map_fin_cb_t fin_callback,
  2370. map_dtor_t dtor,
  2371. void **user_data,
  2372. struct rspamd_worker *worker,
  2373. int flags)
  2374. {
  2375. struct rspamd_map *map;
  2376. struct rspamd_map_backend *bk;
  2377. bk = rspamd_map_parse_backend(cfg, map_line);
  2378. if (bk == NULL) {
  2379. return NULL;
  2380. }
  2381. if (bk->is_fallback) {
  2382. msg_err_config("cannot add map with fallback only backend: %s", bk->uri);
  2383. REF_RELEASE(bk);
  2384. return NULL;
  2385. }
  2386. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(struct rspamd_map));
  2387. map->read_callback = read_callback;
  2388. map->fin_callback = fin_callback;
  2389. map->dtor = dtor;
  2390. map->user_data = user_data;
  2391. map->cfg = cfg;
  2392. map->id = rspamd_random_uint64_fast();
  2393. map->locked =
  2394. rspamd_mempool_alloc0_shared(cfg->cfg_pool, sizeof(int));
  2395. map->backends = g_ptr_array_sized_new(1);
  2396. map->wrk = worker;
  2397. rspamd_mempool_add_destructor(cfg->cfg_pool, rspamd_ptr_array_free_hard,
  2398. map->backends);
  2399. g_ptr_array_add(map->backends, bk);
  2400. map->name = rspamd_mempool_strdup(cfg->cfg_pool, map_line);
  2401. map->no_file_read = (flags & RSPAMD_MAP_FILE_NO_READ);
  2402. if (bk->protocol == MAP_PROTO_FILE) {
  2403. map->poll_timeout = (cfg->map_timeout * cfg->map_file_watch_multiplier);
  2404. }
  2405. else {
  2406. map->poll_timeout = cfg->map_timeout;
  2407. }
  2408. if (description != NULL) {
  2409. map->description = rspamd_mempool_strdup(cfg->cfg_pool, description);
  2410. }
  2411. rspamd_map_calculate_hash(map);
  2412. msg_info_map("added map %s", bk->uri);
  2413. bk->map = map;
  2414. cfg->maps = g_list_prepend(cfg->maps, map);
  2415. return map;
  2416. }
  2417. struct rspamd_map *
  2418. rspamd_map_add_fake(struct rspamd_config *cfg,
  2419. const char *description,
  2420. const char *name)
  2421. {
  2422. struct rspamd_map *map;
  2423. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(struct rspamd_map));
  2424. map->cfg = cfg;
  2425. map->id = rspamd_random_uint64_fast();
  2426. map->name = rspamd_mempool_strdup(cfg->cfg_pool, name);
  2427. map->user_data = (void **) &map; /* to prevent null pointer dereferencing */
  2428. if (description != NULL) {
  2429. map->description = rspamd_mempool_strdup(cfg->cfg_pool, description);
  2430. }
  2431. return map;
  2432. }
  2433. static inline void
  2434. rspamd_map_add_backend(struct rspamd_map *map, struct rspamd_map_backend *bk)
  2435. {
  2436. if (bk->is_fallback) {
  2437. if (map->fallback_backend) {
  2438. msg_warn_map("redefining fallback backend from %s to %s",
  2439. map->fallback_backend->uri, bk->uri);
  2440. }
  2441. map->fallback_backend = bk;
  2442. }
  2443. else {
  2444. g_ptr_array_add(map->backends, bk);
  2445. }
  2446. bk->map = map;
  2447. }
  2448. struct rspamd_map *
  2449. rspamd_map_add_from_ucl(struct rspamd_config *cfg,
  2450. const ucl_object_t *obj,
  2451. const char *description,
  2452. map_cb_t read_callback,
  2453. map_fin_cb_t fin_callback,
  2454. map_dtor_t dtor,
  2455. void **user_data,
  2456. struct rspamd_worker *worker,
  2457. int flags)
  2458. {
  2459. ucl_object_iter_t it = NULL;
  2460. const ucl_object_t *cur, *elt;
  2461. struct rspamd_map *map;
  2462. struct rspamd_map_backend *bk;
  2463. unsigned int i;
  2464. g_assert(obj != NULL);
  2465. if (ucl_object_type(obj) == UCL_STRING) {
  2466. /* Just a plain string */
  2467. return rspamd_map_add(cfg, ucl_object_tostring(obj), description,
  2468. read_callback, fin_callback, dtor, user_data, worker, flags);
  2469. }
  2470. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(struct rspamd_map));
  2471. map->read_callback = read_callback;
  2472. map->fin_callback = fin_callback;
  2473. map->dtor = dtor;
  2474. map->user_data = user_data;
  2475. map->cfg = cfg;
  2476. map->id = rspamd_random_uint64_fast();
  2477. map->locked =
  2478. rspamd_mempool_alloc0_shared(cfg->cfg_pool, sizeof(int));
  2479. map->backends = g_ptr_array_new();
  2480. map->wrk = worker;
  2481. map->no_file_read = (flags & RSPAMD_MAP_FILE_NO_READ);
  2482. rspamd_mempool_add_destructor(cfg->cfg_pool, rspamd_ptr_array_free_hard,
  2483. map->backends);
  2484. map->poll_timeout = cfg->map_timeout;
  2485. if (description) {
  2486. map->description = rspamd_mempool_strdup(cfg->cfg_pool, description);
  2487. }
  2488. if (ucl_object_type(obj) == UCL_ARRAY) {
  2489. /* Add array of maps as multiple backends */
  2490. while ((cur = ucl_object_iterate(obj, &it, true)) != NULL) {
  2491. if (ucl_object_type(cur) == UCL_STRING) {
  2492. bk = rspamd_map_parse_backend(cfg, ucl_object_tostring(cur));
  2493. if (bk != NULL) {
  2494. rspamd_map_add_backend(map, bk);
  2495. if (!map->name) {
  2496. map->name = rspamd_mempool_strdup(cfg->cfg_pool,
  2497. ucl_object_tostring(cur));
  2498. }
  2499. }
  2500. }
  2501. else {
  2502. msg_err_config("bad map element type: %s",
  2503. ucl_object_type_to_string(ucl_object_type(cur)));
  2504. }
  2505. }
  2506. if (map->backends->len == 0) {
  2507. msg_err_config("map has no urls to be loaded: empty list");
  2508. goto err;
  2509. }
  2510. }
  2511. else if (ucl_object_type(obj) == UCL_OBJECT) {
  2512. elt = ucl_object_lookup(obj, "name");
  2513. if (elt && ucl_object_type(elt) == UCL_STRING) {
  2514. map->name = rspamd_mempool_strdup(cfg->cfg_pool,
  2515. ucl_object_tostring(elt));
  2516. }
  2517. elt = ucl_object_lookup(obj, "description");
  2518. if (elt && ucl_object_type(elt) == UCL_STRING) {
  2519. map->description = rspamd_mempool_strdup(cfg->cfg_pool,
  2520. ucl_object_tostring(elt));
  2521. }
  2522. elt = ucl_object_lookup_any(obj, "timeout", "poll", "poll_time",
  2523. "watch_interval", NULL);
  2524. if (elt) {
  2525. map->poll_timeout = ucl_object_todouble(elt);
  2526. }
  2527. elt = ucl_object_lookup_any(obj, "upstreams", "url", "urls", NULL);
  2528. if (elt == NULL) {
  2529. msg_err_config("map has no urls to be loaded: no elt");
  2530. goto err;
  2531. }
  2532. if (ucl_object_type(elt) == UCL_ARRAY) {
  2533. /* Add array of maps as multiple backends */
  2534. it = ucl_object_iterate_new(elt);
  2535. while ((cur = ucl_object_iterate_safe(it, true)) != NULL) {
  2536. if (ucl_object_type(cur) == UCL_STRING) {
  2537. bk = rspamd_map_parse_backend(cfg, ucl_object_tostring(cur));
  2538. if (bk != NULL) {
  2539. rspamd_map_add_backend(map, bk);
  2540. if (!map->name) {
  2541. map->name = rspamd_mempool_strdup(cfg->cfg_pool,
  2542. ucl_object_tostring(cur));
  2543. }
  2544. }
  2545. }
  2546. else {
  2547. msg_err_config("bad map element type: %s",
  2548. ucl_object_type_to_string(ucl_object_type(cur)));
  2549. ucl_object_iterate_free(it);
  2550. goto err;
  2551. }
  2552. }
  2553. ucl_object_iterate_free(it);
  2554. if (map->backends->len == 0) {
  2555. msg_err_config("map has no urls to be loaded: empty object list");
  2556. goto err;
  2557. }
  2558. }
  2559. else if (ucl_object_type(elt) == UCL_STRING) {
  2560. bk = rspamd_map_parse_backend(cfg, ucl_object_tostring(elt));
  2561. if (bk != NULL) {
  2562. rspamd_map_add_backend(map, bk);
  2563. if (!map->name) {
  2564. map->name = rspamd_mempool_strdup(cfg->cfg_pool,
  2565. ucl_object_tostring(elt));
  2566. }
  2567. }
  2568. }
  2569. if (!map->backends || map->backends->len == 0) {
  2570. msg_err_config("map has no urls to be loaded: no valid backends");
  2571. goto err;
  2572. }
  2573. }
  2574. else {
  2575. msg_err_config("map has invalid type for value: %s",
  2576. ucl_object_type_to_string(ucl_object_type(obj)));
  2577. goto err;
  2578. }
  2579. gboolean all_local = TRUE;
  2580. PTR_ARRAY_FOREACH(map->backends, i, bk)
  2581. {
  2582. if (bk->protocol == MAP_PROTO_STATIC) {
  2583. GString *map_data;
  2584. /* We need data field in ucl */
  2585. elt = ucl_object_lookup(obj, "data");
  2586. if (elt == NULL) {
  2587. msg_err_config("map has static backend but no `data` field");
  2588. goto err;
  2589. }
  2590. if (ucl_object_type(elt) == UCL_STRING) {
  2591. map_data = g_string_sized_new(32);
  2592. if (rspamd_map_add_static_string(cfg, elt, map_data)) {
  2593. bk->data.sd->data = map_data->str;
  2594. bk->data.sd->len = map_data->len;
  2595. g_string_free(map_data, FALSE);
  2596. }
  2597. else {
  2598. g_string_free(map_data, TRUE);
  2599. msg_err_config("map has static backend with invalid `data` field");
  2600. goto err;
  2601. }
  2602. }
  2603. else if (ucl_object_type(elt) == UCL_ARRAY) {
  2604. map_data = g_string_sized_new(32);
  2605. it = ucl_object_iterate_new(elt);
  2606. while ((cur = ucl_object_iterate_safe(it, true))) {
  2607. if (!rspamd_map_add_static_string(cfg, cur, map_data)) {
  2608. g_string_free(map_data, TRUE);
  2609. msg_err_config("map has static backend with invalid "
  2610. "`data` field");
  2611. ucl_object_iterate_free(it);
  2612. goto err;
  2613. }
  2614. }
  2615. ucl_object_iterate_free(it);
  2616. bk->data.sd->data = map_data->str;
  2617. bk->data.sd->len = map_data->len;
  2618. g_string_free(map_data, FALSE);
  2619. }
  2620. }
  2621. else if (bk->protocol != MAP_PROTO_FILE) {
  2622. all_local = FALSE;
  2623. }
  2624. }
  2625. if (all_local) {
  2626. map->poll_timeout = (map->poll_timeout *
  2627. cfg->map_file_watch_multiplier);
  2628. }
  2629. rspamd_map_calculate_hash(map);
  2630. msg_debug_map("added map from ucl");
  2631. cfg->maps = g_list_prepend(cfg->maps, map);
  2632. return map;
  2633. err:
  2634. if (map) {
  2635. PTR_ARRAY_FOREACH(map->backends, i, bk)
  2636. {
  2637. MAP_RELEASE(bk, "rspamd_map_backend");
  2638. }
  2639. }
  2640. return NULL;
  2641. }
  2642. rspamd_map_traverse_function
  2643. rspamd_map_get_traverse_function(struct rspamd_map *map)
  2644. {
  2645. if (map) {
  2646. return map->traverse_function;
  2647. }
  2648. return NULL;
  2649. }
  2650. void rspamd_map_traverse(struct rspamd_map *map, rspamd_map_traverse_cb cb,
  2651. gpointer cbdata, gboolean reset_hits)
  2652. {
  2653. if (*map->user_data && map->traverse_function) {
  2654. map->traverse_function(*map->user_data, cb, cbdata, reset_hits);
  2655. }
  2656. }
  2657. void rspamd_map_set_on_load_function(struct rspamd_map *map, rspamd_map_on_load_function cb,
  2658. gpointer cbdata, GDestroyNotify dtor)
  2659. {
  2660. if (map) {
  2661. map->on_load_function = cb;
  2662. map->on_load_ud = cbdata;
  2663. map->on_load_ud_dtor = dtor;
  2664. }
  2665. }