1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882 |
- /*
- * $Id$
- *
- * resolver related functions
- *
- * Copyright (C) 2006 iptelorg GmbH
- *
- * This file is part of ser, a free SIP server.
- *
- * ser is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * For a license to use the ser software under conditions
- * other than those described here, or to purchase support for this
- * software, please contact iptel.org by e-mail at the following addresses:
- * [email protected]
- *
- * ser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- /* History:
- * --------
- * 2006-07-13 created by andrei
- * 2006-10-06 port fix (andrei)
- * 2007-06-14 dns iterate through A & AAAA records fix (andrei)
- * 2007-06-15 srv rr weight based load balancing support (andrei)
- * 2007-06-16 naptr support (andrei)
- * 2008-07-18 DNS watchdog support -- can be used to inform the core
- * that the DNS servers are down (Miklos)
- * 2008-07-25 various rpc commands to manipulate the content
- * of the cache (Miklos)
- * 2007-07-30 DNS cache measurements added (Gergo)
- * 2007-08-17 dns_cache_del_nonexp config option is introduced (Miklos)
- * 2008-02-04 DNS cache options are adapted for the configuration
- * framework (Miklos)
- * 2008-02-11 dns_cache_init cfg parameter is introduced (Miklos)
- * 2008-10-17 fixed srv continue with 0 hostname (when falling back to
- aaaa) (andrei)
- * 2009-03-30 TXT record support, more rpcs (andrei)
- * 2009-03-30 EBL record support (andrei)
- * 2009-04-01 PTR record support (andrei)
- */
- /*!
- * \file
- * \brief SIP-router core ::
- * \ingroup core
- * Module: \ref core
- */
- #ifdef USE_DNS_CACHE
- #ifdef DNS_SRV_LB
- #include <stdlib.h> /* FIXME: rand() */
- #endif
- #include <string.h>
- #include "globals.h"
- #include "cfg_core.h"
- #include "dns_cache.h"
- #include "dns_wrappers.h"
- #include "compiler_opt.h"
- #include "mem/shm_mem.h"
- #include "hashes.h"
- #include "clist.h"
- #include "locking.h"
- #include "atomic_ops.h"
- #include "ut.h"
- #include "timer.h"
- #include "timer_ticks.h"
- #include "error.h"
- #include "rpc.h"
- #include "rand/fastrand.h"
- #ifdef USE_DNS_CACHE_STATS
- #include "pt.h"
- #endif
- #define DNS_CACHE_DEBUG /* extra sanity checks and debugging */
- #ifndef MAX
- #define MAX(a,b) ( ((a)>(b))?(a):(b))
- #endif
- #define MAX_DNS_RECORDS 255 /* maximum dns records number received in a
- dns answer*/
- #define DNS_HASH_SIZE 1024 /* must be <= 65535 */
- #define DEFAULT_DNS_TIMER_INTERVAL 120 /* 2 min. */
- #define DNS_HE_MAX_ADDR 10 /* maxium addresses returne in a hostent struct */
- #define MAX_CNAME_CHAIN 10
- #define SPACE_FORMAT " " /* format of view output */
- #define DNS_SRV_ZERO_W_CHANCE 1000 /* one in a 1000*weight_sum chance for
- selecting a 0-weight record */
- int dns_cache_init=1; /* if 0, the DNS cache is not initialized at startup */
- static gen_lock_t* dns_hash_lock=0;
- static volatile unsigned int *dns_cache_mem_used=0; /* current mem. use */
- unsigned int dns_timer_interval=DEFAULT_DNS_TIMER_INTERVAL; /* in s */
- int dns_flags=0; /* default flags used for the dns_*resolvehost
- (compatibility wrappers) */
- #ifdef USE_DNS_CACHE_STATS
- struct t_dns_cache_stats* dns_cache_stats=0;
- #endif
- #define LOCK_DNS_HASH() lock_get(dns_hash_lock)
- #define UNLOCK_DNS_HASH() lock_release(dns_hash_lock)
- #define FIX_TTL(t) \
- (((t)<cfg_get(core, core_cfg, dns_cache_min_ttl))? \
- cfg_get(core, core_cfg, dns_cache_min_ttl): \
- (((t)>cfg_get(core, core_cfg, dns_cache_max_ttl))? \
- cfg_get(core, core_cfg, dns_cache_max_ttl): \
- (t)))
- struct dns_hash_head{
- struct dns_hash_entry* next;
- struct dns_hash_entry* prev;
- };
- #ifdef DNS_LU_LST
- struct dns_lu_lst* dns_last_used_lst=0;
- #endif
- static struct dns_hash_head* dns_hash=0;
- static struct timer_ln* dns_timer_h=0;
- #ifdef DNS_WATCHDOG_SUPPORT
- static atomic_t *dns_servers_up = NULL;
- #endif
- static const char* dns_str_errors[]={
- "no error",
- "no more records", /* not an error, but and end condition */
- "unknown error",
- "internal error",
- "bad SRV entry",
- "unresolvable SRV request",
- "bad A or AAAA entry",
- "unresolvable A or AAAA request",
- "invalid ip in A or AAAA record",
- "blacklisted ip",
- "name too long ", /* try again with a shorter name */
- "ip AF mismatch", /* address family mismatch */
- "unresolvable NAPTR request",
- "bug - critical error"
- };
- /* param: err (negative error number) */
- const char* dns_strerror(int err)
- {
- err=-err;
- if ((err>=0) && (err<sizeof(dns_str_errors)/sizeof(char*)))
- return dns_str_errors[err];
- return "bug -- bad error number";
- }
- /* "internal" only, don't use unless you really know waht you're doing */
- inline static void dns_destroy_entry(struct dns_hash_entry* e)
- {
- #ifdef DNS_CACHE_DEBUG
- memset(e, 0, e->total_size);
- #endif
- shm_free(e); /* nice having it in one block isn't it? :-) */
- }
- /* "internal" only, same as above, asumes shm_lock() held (tm optimization) */
- inline static void dns_destroy_entry_shm_unsafe(struct dns_hash_entry* e)
- {
- #ifdef DNS_CACHE_DEBUG
- memset(e, 0, e->total_size);
- #endif
- shm_free_unsafe(e); /* nice having it in one block isn't it? :-) */
- }
- /* dec. the internal refcnt and if 0 deletes the entry */
- void dns_hash_put(struct dns_hash_entry* e)
- {
- if(e && atomic_dec_and_test(&e->refcnt)){
- /* atomic_sub_long(dns_cache_total_used, e->total_size); */
- dns_destroy_entry(e);
- }
- }
- /* same as above but uses dns_destroy_unsafe (assumes shm_lock held -- tm
- * optimization) */
- void dns_hash_put_shm_unsafe(struct dns_hash_entry* e)
- {
- if(e && atomic_dec_and_test(&e->refcnt)){
- /* atomic_sub_long(dns_cache_total_used, e->total_size); */
- dns_destroy_entry_shm_unsafe(e);
- }
- }
- inline static int dns_cache_clean(unsigned int no, int expired_only);
- inline static int dns_cache_free_mem(unsigned int target, int expired_only);
- static ticks_t dns_timer(ticks_t ticks, struct timer_ln* tl, void* data)
- {
- #ifdef DNS_WATCHDOG_SUPPORT
- /* do not clean the hash table if the servers are down */
- if (atomic_get(dns_servers_up) == 0)
- return (ticks_t)(-1);
- #endif
- if (*dns_cache_mem_used>12*(cfg_get(core, core_cfg, dns_cache_max_mem)/16)){ /* ~ 75% used */
- dns_cache_free_mem(cfg_get(core, core_cfg, dns_cache_max_mem)/2, 1);
- }else{
- dns_cache_clean(-1, 1); /* all the table, only expired entries */
- /* TODO: better strategy? */
- }
- return (ticks_t)(-1);
- }
- void destroy_dns_cache()
- {
- if (dns_timer_h){
- timer_del(dns_timer_h);
- timer_free(dns_timer_h);
- dns_timer_h=0;
- }
- #ifdef DNS_WATCHDOG_SUPPORT
- if (dns_servers_up){
- shm_free(dns_servers_up);
- dns_servers_up=0;
- }
- #endif
- if (dns_hash_lock){
- lock_destroy(dns_hash_lock);
- lock_dealloc(dns_hash_lock);
- dns_hash_lock=0;
- }
- if (dns_hash){
- shm_free(dns_hash);
- dns_hash=0;
- }
- #ifdef DNS_LU_LST
- if (dns_last_used_lst){
- shm_free(dns_last_used_lst);
- dns_last_used_lst=0;
- }
- #endif
- #ifdef USE_DNS_CACHE_STATS
- if (dns_cache_stats)
- shm_free(dns_cache_stats);
- #endif
- if (dns_cache_mem_used){
- shm_free((void*)dns_cache_mem_used);
- dns_cache_mem_used=0;
- }
- }
- /* set the value of dns_flags */
- void fix_dns_flags(str *gname, str *name)
- {
- /* restore the original value of dns_cache_flags first
- * (DNS_IPV4_ONLY may have been set only because dns_try_ipv6
- * was disabled, and the flag must be cleared when
- * dns_try_ipv6 is enabled) (Miklos)
- */
- dns_flags = cfg_get(core, core_cfg, dns_cache_flags) & 7;
- if (cfg_get(core, core_cfg, dns_try_ipv6)==0){
- dns_flags|=DNS_IPV4_ONLY;
- }
- if (dns_flags & DNS_IPV4_ONLY){
- dns_flags&=~(DNS_IPV6_ONLY|DNS_IPV6_FIRST);
- }
- if (cfg_get(core, core_cfg, dns_srv_lb)){
- #ifdef DNS_SRV_LB
- dns_flags|=DNS_SRV_RR_LB;
- #else
- LOG(L_WARN, "WARNING: fix_dns_flags: SRV loadbalaning is set, but"
- " support for it is not compiled -- ignoring\n");
- #endif
- }
- if (cfg_get(core, core_cfg, dns_try_naptr)) {
- #ifndef USE_NAPTR
- LOG(L_WARN, "WARNING: fix_dns_flags: NAPTR support is enabled, but"
- " support for it is not compiled -- ignoring\n");
- #endif
- dns_flags|=DNS_TRY_NAPTR;
- }
- }
- /* fixup function for use_dns_failover
- * verifies that use_dns_cache is set to 1
- */
- int use_dns_failover_fixup(void *handle, str *gname, str *name, void **val)
- {
- if ((int)(long)(*val) && !cfg_get(core, handle, use_dns_cache)) {
- LOG(L_ERR, "ERROR: use_dns_failover_fixup(): "
- "DNS cache is turned off, failover cannot be enabled. "
- "(set use_dns_cache to 1)\n");
- return -1;
- }
- return 0;
- }
- /* fixup function for use_dns_cache
- * verifies that dns_cache_init is set to 1
- */
- int use_dns_cache_fixup(void *handle, str *gname, str *name, void **val)
- {
- if ((int)(long)(*val) && !dns_cache_init) {
- LOG(L_ERR, "ERROR: use_dns_cache_fixup(): "
- "DNS cache is turned off by dns_cache_init=0, "
- "it cannot be enabled runtime.\n");
- return -1;
- }
- if (((int)(long)(*val)==0) && cfg_get(core, handle, use_dns_failover)) {
- LOG(L_ERR, "ERROR: use_dns_failover_fixup(): "
- "DNS failover depends on use_dns_cache, set use_dns_failover "
- "to 0 before disabling the DNS cache\n");
- return -1;
- }
- return 0;
- }
- /* KByte to Byte conversion */
- int dns_cache_max_mem_fixup(void *handle, str *gname, str *name, void **val)
- {
- unsigned int u;
- u = ((unsigned int)(long)(*val))<<10;
- (*val) = (void *)(long)u;
- return 0;
- }
- int init_dns_cache()
- {
- int r;
- int ret;
- if (dns_cache_init==0) {
- /* the DNS cache is turned off */
- default_core_cfg.use_dns_cache=0;
- default_core_cfg.use_dns_failover=0;
- return 0;
- }
- ret=0;
- /* sanity check */
- if (E_DNS_CRITICAL>=sizeof(dns_str_errors)/sizeof(char*)){
- LOG(L_CRIT, "BUG: dns_cache_init: bad dns error table\n");
- ret=E_BUG;
- goto error;
- }
- dns_cache_mem_used=shm_malloc(sizeof(*dns_cache_mem_used));
- if (dns_cache_mem_used==0){
- ret=E_OUT_OF_MEM;
- goto error;
- }
- #ifdef DNS_LU_LST
- dns_last_used_lst=shm_malloc(sizeof(*dns_last_used_lst));
- if (dns_last_used_lst==0){
- ret=E_OUT_OF_MEM;
- goto error;
- }
- clist_init(dns_last_used_lst, next, prev);
- #endif
- dns_hash=shm_malloc(sizeof(struct dns_hash_head)*DNS_HASH_SIZE);
- if (dns_hash==0){
- ret=E_OUT_OF_MEM;
- goto error;
- }
- for (r=0; r<DNS_HASH_SIZE; r++)
- clist_init(&dns_hash[r], next, prev);
- dns_hash_lock=lock_alloc();
- if (dns_hash_lock==0){
- ret=E_OUT_OF_MEM;
- goto error;
- }
- if (lock_init(dns_hash_lock)==0){
- lock_dealloc(dns_hash_lock);
- dns_hash_lock=0;
- ret=-1;
- goto error;
- }
- #ifdef DNS_WATCHDOG_SUPPORT
- dns_servers_up=shm_malloc(sizeof(atomic_t));
- if (dns_servers_up==0){
- ret=E_OUT_OF_MEM;
- goto error;
- }
- atomic_set(dns_servers_up, 1);
- #endif
- /* fix options */
- default_core_cfg.dns_cache_max_mem<<=10; /* Kb */ /* TODO: test with 0 */
- if (default_core_cfg.use_dns_cache==0)
- default_core_cfg.use_dns_failover=0; /* cannot work w/o dns_cache support */
- /* fix flags */
- fix_dns_flags(NULL, NULL);
- dns_timer_h=timer_alloc();
- if (dns_timer_h==0){
- ret=E_OUT_OF_MEM;
- goto error;
- }
- if (dns_timer_interval){
- timer_init(dns_timer_h, dns_timer, 0, 0); /* "slow" timer */
- if (timer_add(dns_timer_h, S_TO_TICKS(dns_timer_interval))<0){
- LOG(L_CRIT, "BUG: dns_cache_init: failed to add the timer\n");
- timer_free(dns_timer_h);
- dns_timer_h=0;
- goto error;
- }
- }
- return 0;
- error:
- destroy_dns_cache();
- return ret;
- }
- #ifdef USE_DNS_CACHE_STATS
- int init_dns_cache_stats(int iproc_num)
- {
- /* do not initialize the stats array if the DNS cache will not be used */
- if (dns_cache_init==0) return 0;
- /* if it is already initialized */
- if (dns_cache_stats)
- shm_free(dns_cache_stats);
- dns_cache_stats=shm_malloc(sizeof(*dns_cache_stats) * iproc_num);
- if (dns_cache_stats==0){
- return E_OUT_OF_MEM;
- }
- memset(dns_cache_stats, 0, sizeof(*dns_cache_stats) * iproc_num);
- return 0;
- }
- #endif
- /* hash function, type is not used (obsolete)
- * params: char* s, int len, int type
- * returns the hash value
- */
- #define dns_hash_no(s, len, type) \
- (get_hash1_case_raw((s),(len)) % DNS_HASH_SIZE)
- #ifdef DNS_CACHE_DEBUG
- #define DEBUG_LU_LST
- #ifdef DEBUG_LU_LST
- #include <stdlib.h> /* abort() */
- #define check_lu_lst(l) ((((l)->next==(l)) || ((l)->prev==(l))) && \
- ((l)!=dns_last_used_lst))
- #define dbg_lu_lst(txt, l) \
- LOG(L_CRIT, "BUG: %s: crt(%p, %p, %p)," \
- " prev(%p, %p, %p), next(%p, %p, %p)\n", txt, \
- (l), (l)->next, (l)->prev, \
- (l)->prev, (l)->prev->next, (l)->prev->prev, \
- (l)->next, (l)->next->next, (l)->next->prev \
- )
- #define debug_lu_lst( txt, l) \
- do{ \
- if (check_lu_lst((l))){ \
- dbg_lu_lst(txt " crt:", (l)); \
- abort(); \
- } \
- if (check_lu_lst((l)->next)){ \
- dbg_lu_lst(txt " next:", (l)); \
- abort(); \
- } \
- if (check_lu_lst((l)->prev)){ \
- dbg_lu_lst(txt " prev:", (l)); \
- abort(); \
- } \
- }while(0)
- #endif
- #endif /* DNS_CACHE_DEBUG */
- /* must be called with the DNS_LOCK hold
- * remove and entry from the hash, dec. its refcnt and if not referenced
- * anymore deletes it */
- inline static void _dns_hash_remove(struct dns_hash_entry* e)
- {
- clist_rm(e, next, prev);
- #ifdef DNS_CACHE_DEBUG
- e->next=e->prev=0;
- #endif
- #ifdef DNS_LU_LST
- #ifdef DEBUG_LU_LST
- debug_lu_lst("_dns_hash_remove: pre rm:", &e->last_used_lst);
- #endif
- clist_rm(&e->last_used_lst, next, prev);
- #ifdef DEBUG_LU_LST
- debug_lu_lst("_dns_hash_remove: post rm:", &e->last_used_lst);
- #endif
- #ifdef DNS_CACHE_DEBUG
- e->last_used_lst.next=e->last_used_lst.prev=0;
- #endif
- #endif
- *dns_cache_mem_used-=e->total_size;
- dns_hash_put(e);
- }
- /* non locking version (the dns hash must _be_ locked externally)
- * returns 0 when not found, or the entry on success (an entry with a
- * similar name but with a CNAME type will always match).
- * it doesn't increase the internal refcnt
- * returns the entry when found, 0 when not found and sets *err to !=0
- * on error (e.g. recursive cnames)
- * WARNING: - internal use only
- * - always check if the returned entry type is CNAME */
- inline static struct dns_hash_entry* _dns_hash_find(str* name, int type,
- int* h, int* err)
- {
- struct dns_hash_entry* e;
- struct dns_hash_entry* tmp;
- struct dns_hash_entry* ret;
- ticks_t now;
- int cname_chain;
- str cname;
- #ifdef DNS_WATCHDOG_SUPPORT
- int servers_up;
- servers_up = atomic_get(dns_servers_up);
- #endif
- cname_chain=0;
- ret=0;
- now=get_ticks_raw();
- *err=0;
- again:
- *h=dns_hash_no(name->s, name->len, type);
- #ifdef DNS_CACHE_DEBUG
- DBG("dns_hash_find(%.*s(%d), %d), h=%d\n", name->len, name->s,
- name->len, type, *h);
- #endif
- clist_foreach_safe(&dns_hash[*h], e, tmp, next){
- if (
- #ifdef DNS_WATCHDOG_SUPPORT
- /* remove expired elements only when the dns servers are up */
- servers_up &&
- #endif
- /* automatically remove expired elements */
- ((e->ent_flags & DNS_FLAG_PERMANENT) == 0) &&
- ((s_ticks_t)(now-e->expire)>=0)
- ) {
- _dns_hash_remove(e);
- }else if ((e->type==type) && (e->name_len==name->len) &&
- (strncasecmp(e->name, name->s, e->name_len)==0)){
- e->last_used=now;
- #ifdef DNS_LU_LST
- /* add it at the end */
- #ifdef DEBUG_LU_LST
- debug_lu_lst("_dns_hash_find: pre rm:", &e->last_used_lst);
- #endif
- clist_rm(&e->last_used_lst, next, prev);
- clist_append(dns_last_used_lst, &e->last_used_lst, next, prev);
- #ifdef DEBUG_LU_LST
- debug_lu_lst("_dns_hash_find: post append:", &e->last_used_lst);
- #endif
- #endif
- return e;
- }else if ((e->type==T_CNAME) &&
- !((e->rr_lst==0) || (e->ent_flags & DNS_FLAG_BAD_NAME)) &&
- (e->name_len==name->len) &&
- (strncasecmp(e->name, name->s, e->name_len)==0)){
- /*if CNAME matches and CNAME is entry is not a neg. cache entry
- (could be produced by a specific CNAME lookup)*/
- e->last_used=now;
- #ifdef DNS_LU_LST
- /* add it at the end */
- #ifdef DEBUG_LU_LST
- debug_lu_lst("_dns_hash_find: cname: pre rm:", &e->last_used_lst);
- #endif
- clist_rm(&e->last_used_lst, next, prev);
- clist_append(dns_last_used_lst, &e->last_used_lst, next, prev);
- #ifdef DEBUG_LU_LST
- debug_lu_lst("_dns_hash_find: cname: post append:",
- &e->last_used_lst);
- #endif
- #endif
- ret=e; /* if this is an unfinished cname chain, we try to
- return the last cname */
- /* this is a cname => retry using its value */
- if (cname_chain> MAX_CNAME_CHAIN){
- LOG(L_ERR, "ERROR: _dns_hash_find: cname chain too long "
- "or recursive (\"%.*s\")\n", name->len, name->s);
- ret=0; /* error*/
- *err=-1;
- break;
- }
- cname_chain++;
- cname.s=((struct cname_rdata*)e->rr_lst->rdata)->name;
- cname.len= ((struct cname_rdata*)e->rr_lst->rdata)->name_len;
- name=&cname;
- goto again;
- }
- }
- return ret;
- }
- /* frees cache entries, if expired_only=0 only expired entries will be
- * removed, else all of them
- * it will process maximum no entries (to process all of them use -1)
- * returns the number of deleted entries
- * This should be called from a timer process*/
- inline static int dns_cache_clean(unsigned int no, int expired_only)
- {
- struct dns_hash_entry* e;
- ticks_t now;
- unsigned int n;
- unsigned int deleted;
- #ifdef DNS_LU_LST
- struct dns_lu_lst* l;
- struct dns_lu_lst* tmp;
- #else
- struct dns_hash_entry* t;
- unsigned int h;
- static unsigned int start=0;
- #endif
- n=0;
- deleted=0;
- now=get_ticks_raw();
- LOCK_DNS_HASH();
- #ifdef DNS_LU_LST
- clist_foreach_safe(dns_last_used_lst, l, tmp, next){
- e=(struct dns_hash_entry*)(((char*)l)-
- (char*)&((struct dns_hash_entry*)(0))->last_used_lst);
- if (((e->ent_flags & DNS_FLAG_PERMANENT) == 0)
- && (!expired_only || ((s_ticks_t)(now-e->expire)>=0))
- ) {
- _dns_hash_remove(e);
- deleted++;
- }
- n++;
- if (n>=no) break;
- }
- #else
- for(h=start; h!=(start+DNS_HASH_SIZE); h++){
- clist_foreach_safe(&dns_hash[h%DNS_HASH_SIZE], e, t, next){
- if (((e->ent_flags & DNS_FLAG_PERMANENT) == 0)
- && ((s_ticks_t)(now-e->expire)>=0)
- ) {
- _dns_hash_remove(e);
- deleted++;
- }
- n++;
- if (n>=no) goto skip;
- }
- }
- /* not fair, but faster then random() */
- if (!expired_only){
- for(h=start; h!=(start+DNS_HASH_SIZE); h++){
- clist_foreach_safe(&dns_hash[h%DNS_HASH_SIZE], e, t, next){
- if ((e->ent_flags & DNS_FLAG_PERMANENT) == 0) {
- _dns_hash_remove(e);
- deleted++;
- }
- n++;
- if (n>=no) goto skip;
- }
- }
- }
- skip:
- start=h;
- #endif
- UNLOCK_DNS_HASH();
- return deleted;
- }
- /* frees cache entries, if expired_only=0 only expired entries will be
- * removed, else all of them
- * it will stop when the dns cache used memory reaches target (to process all
- * of them use 0)
- * returns the number of deleted entries */
- inline static int dns_cache_free_mem(unsigned int target, int expired_only)
- {
- struct dns_hash_entry* e;
- ticks_t now;
- unsigned int deleted;
- #ifdef DNS_LU_LST
- struct dns_lu_lst* l;
- struct dns_lu_lst* tmp;
- #else
- struct dns_hash_entry* t;
- unsigned int h;
- static unsigned int start=0;
- #endif
- deleted=0;
- now=get_ticks_raw();
- LOCK_DNS_HASH();
- #ifdef DNS_LU_LST
- clist_foreach_safe(dns_last_used_lst, l, tmp, next){
- if (*dns_cache_mem_used<=target) break;
- e=(struct dns_hash_entry*)(((char*)l)-
- (char*)&((struct dns_hash_entry*)(0))->last_used_lst);
- if (((e->ent_flags & DNS_FLAG_PERMANENT) == 0)
- && (!expired_only || ((s_ticks_t)(now-e->expire)>=0))
- ) {
- _dns_hash_remove(e);
- deleted++;
- }
- }
- #else
- for(h=start; h!=(start+DNS_HASH_SIZE); h++){
- clist_foreach_safe(&dns_hash[h%DNS_HASH_SIZE], e, t, next){
- if (*dns_cache_mem_used<=target)
- goto skip;
- if (((e->ent_flags & DNS_FLAG_PERMANENT) == 0)
- && ((s_ticks_t)(now-e->expire)>=0)
- ) {
- _dns_hash_remove(e);
- deleted++;
- }
- }
- }
- /* not fair, but faster then random() */
- if (!expired_only){
- for(h=start; h!=(start+DNS_HASH_SIZE); h++){
- clist_foreach_safe(&dns_hash[h%DNS_HASH_SIZE], e, t, next){
- if (*dns_cache_mem_used<=target)
- goto skip;
- if (((e->ent_flags & DNS_FLAG_PERMANENT) == 0)
- && ((s_ticks_t)(now-e->expire)>=0)
- ) {
- _dns_hash_remove(e);
- deleted++;
- }
- }
- }
- }
- skip:
- start=h;
- #endif
- UNLOCK_DNS_HASH();
- return deleted;
- }
- /* locking version (the dns hash must _not_be locked externally)
- * returns 0 when not found, the searched entry on success (with CNAMEs
- * followed) or the last CNAME entry from an unfinished CNAME chain,
- * if the search matches a CNAME. On error sets *err (e.g. recursive CNAMEs).
- * it increases the internal refcnt => when finished dns_hash_put() must
- * be called on the returned entry
- * WARNING: - the return might be a CNAME even if type!=CNAME, see above */
- inline static struct dns_hash_entry* dns_hash_get(str* name, int type, int* h,
- int* err)
- {
- struct dns_hash_entry* e;
- LOCK_DNS_HASH();
- e=_dns_hash_find(name, type, h, err);
- if (e){
- atomic_inc(&e->refcnt);
- }
- UNLOCK_DNS_HASH();
- return e;
- }
- /* adds a fully created and init. entry (see dns_cache_mk_entry()) to the hash
- * table
- * returns 0 on success, -1 on error */
- inline static int dns_cache_add(struct dns_hash_entry* e)
- {
- int h;
- /* check space */
- /* atomic_add_long(dns_cache_total_used, e->size); */
- if ((*dns_cache_mem_used+e->total_size)>=cfg_get(core, core_cfg, dns_cache_max_mem)){
- #ifdef USE_DNS_CACHE_STATS
- dns_cache_stats[process_no].dc_lru_cnt++;
- #endif
- LOG(L_WARN, "WARNING: dns_cache_add: cache full, trying to free...\n");
- /* free ~ 12% of the cache */
- dns_cache_free_mem(*dns_cache_mem_used/16*14,
- !cfg_get(core, core_cfg, dns_cache_del_nonexp));
- if ((*dns_cache_mem_used+e->total_size)>=cfg_get(core, core_cfg, dns_cache_max_mem)){
- LOG(L_ERR, "ERROR: dns_cache_add: max. cache mem size exceeded\n");
- return -1;
- }
- }
- atomic_inc(&e->refcnt);
- h=dns_hash_no(e->name, e->name_len, e->type);
- #ifdef DNS_CACHE_DEBUG
- DBG("dns_cache_add: adding %.*s(%d) %d (flags=%0x) at %d\n",
- e->name_len, e->name, e->name_len, e->type, e->ent_flags, h);
- #endif
- LOCK_DNS_HASH();
- *dns_cache_mem_used+=e->total_size; /* no need for atomic ops, written
- only from within a lock */
- clist_append(&dns_hash[h], e, next, prev);
- #ifdef DNS_LU_LST
- clist_append(dns_last_used_lst, &e->last_used_lst, next, prev);
- #endif
- UNLOCK_DNS_HASH();
- return 0;
- }
- /* same as above, but it must be called with the dns hash lock held
- * returns 0 on success, -1 on error */
- inline static int dns_cache_add_unsafe(struct dns_hash_entry* e)
- {
- int h;
- /* check space */
- /* atomic_add_long(dns_cache_total_used, e->size); */
- if ((*dns_cache_mem_used+e->total_size)>=cfg_get(core, core_cfg, dns_cache_max_mem)){
- #ifdef USE_DNS_CACHE_STATS
- dns_cache_stats[process_no].dc_lru_cnt++;
- #endif
- LOG(L_WARN, "WARNING: dns_cache_add: cache full, trying to free...\n");
- /* free ~ 12% of the cache */
- UNLOCK_DNS_HASH();
- dns_cache_free_mem(*dns_cache_mem_used/16*14,
- !cfg_get(core, core_cfg, dns_cache_del_nonexp));
- LOCK_DNS_HASH();
- if ((*dns_cache_mem_used+e->total_size)>=cfg_get(core, core_cfg, dns_cache_max_mem)){
- LOG(L_ERR, "ERROR: dns_cache_add: max. cache mem size exceeded\n");
- return -1;
- }
- }
- atomic_inc(&e->refcnt);
- h=dns_hash_no(e->name, e->name_len, e->type);
- #ifdef DNS_CACHE_DEBUG
- DBG("dns_cache_add: adding %.*s(%d) %d (flags=%0x) at %d\n",
- e->name_len, e->name, e->name_len, e->type, e->ent_flags, h);
- #endif
- *dns_cache_mem_used+=e->total_size; /* no need for atomic ops, written
- only from within a lock */
- clist_append(&dns_hash[h], e, next, prev);
- #ifdef DNS_LU_LST
- clist_append(dns_last_used_lst, &e->last_used_lst, next, prev);
- #endif
- return 0;
- }
- /* creates a "negative" entry which will be valid for ttl seconds */
- inline static struct dns_hash_entry* dns_cache_mk_bad_entry(str* name,
- int type,
- int ttl,
- int flags)
- {
- struct dns_hash_entry* e;
- int size;
- ticks_t now;
- #ifdef DNS_CACHE_DEBUG
- DBG("dns_cache_mk_bad_entry(%.*s, %d, %d, %d)\n", name->len, name->s,
- type, ttl, flags);
- #endif
- size=sizeof(struct dns_hash_entry)+name->len-1+1;
- e=shm_malloc(size);
- if (e==0){
- LOG(L_ERR, "ERROR: dns_cache_mk_bad_entry: out of memory\n");
- return 0;
- }
- memset(e, 0, size); /* init with 0*/
- e->total_size=size;
- e->name_len=name->len;
- e->type=type;
- now=get_ticks_raw();
- e->last_used=now;
- e->expire=now+S_TO_TICKS(ttl);
- memcpy(e->name, name->s, name->len);
- e->ent_flags=flags;
- return e;
- }
- /* create a a/aaaa hash entry from a name and ip address
- * returns 0 on error */
- inline static struct dns_hash_entry* dns_cache_mk_ip_entry(str* name,
- struct ip_addr* ip)
- {
- struct dns_hash_entry* e;
- int size;
- ticks_t now;
- /* everything is allocated in one block: dns_hash_entry + name +
- * + dns_rr + rdata; dns_rr must start at an aligned adress,
- * hence we need to round dns_hash_entry+name size to a sizeof(long)
- * multiple.
- * Memory image:
- * struct dns_hash_entry
- * name (name_len+1 bytes)
- * padding to multiple of sizeof(long)
- * dns_rr
- * rdata (no padding needed, since for ip is just an array of chars)
- */
- size=ROUND_POINTER(sizeof(struct dns_hash_entry)+name->len-1+1)+
- sizeof(struct dns_rr)+ ip->len;
- e=shm_malloc(size);
- if (e==0){
- LOG(L_ERR, "ERROR: dns_cache_mk_ip_entry: out of memory\n");
- return 0;
- }
- memset(e, 0, size); /* init with 0*/
- e->total_size=size;
- e->name_len=name->len;
- e->type=(ip->af==AF_INET)?T_A:T_AAAA;
- now=get_ticks_raw();
- e->last_used=now;
- e->expire=now-1; /* maximum expire */
- memcpy(e->name, name->s, name->len); /* memset makes sure is 0-term. */
- e->rr_lst=(void*)((char*)e+
- ROUND_POINTER(sizeof(struct dns_hash_entry)+name->len-1+1));
- e->rr_lst->rdata=(void*)((char*)e->rr_lst+sizeof(struct dns_rr));
- e->rr_lst->expire=now-1; /* maximum expire */
- /* no need to align rr_lst->rdata for a or aaaa records */
- memcpy(e->rr_lst->rdata, ip->u.addr, ip->len);
- return e;
- }
- /* creates an srv hash entry from the given parameters
- * returns 0 on error */
- static struct dns_hash_entry* dns_cache_mk_srv_entry(str* name,
- unsigned short priority,
- unsigned short weight,
- unsigned short port,
- str* rr_name,
- int ttl)
- {
- struct dns_hash_entry* e;
- int size;
- ticks_t now;
- /* everything is allocated in one block: dns_hash_entry + name +
- * + dns_rr + rdata; dns_rr must start at an aligned adress,
- * hence we need to round dns_hash_entry+name size to a sizeof(long),
- * and similarly, dns_rr must be rounded to sizeof(short).
- * multiple.
- * Memory image:
- * struct dns_hash_entry
- * name (name_len+1 bytes)
- * padding to multiple of sizeof(long)
- * dns_rr
- * padding to multiple of sizeof(short)
- * rdata
- */
- size=ROUND_POINTER(sizeof(struct dns_hash_entry)+name->len-1+1) +
- ROUND_SHORT(sizeof(struct dns_rr)) +
- sizeof(struct srv_rdata)-1 +
- rr_name->len+1;
- e=shm_malloc(size);
- if (e==0){
- LOG(L_ERR, "ERROR: dns_cache_srv_ip_entry: out of memory\n");
- return 0;
- }
- memset(e, 0, size); /* init with 0*/
- e->total_size=size;
- e->name_len=name->len;
- e->type=T_SRV;
- now=get_ticks_raw();
- e->last_used=now;
- e->expire=now+S_TO_TICKS(ttl);
- memcpy(e->name, name->s, name->len); /* memset makes sure is 0-term. */
- e->rr_lst=(void*)((char*)e+
- ROUND_POINTER(sizeof(struct dns_hash_entry)+name->len-1+1));
- e->rr_lst->rdata=(void*)((char*)e->rr_lst+ROUND_SHORT(sizeof(struct dns_rr)));
- e->rr_lst->expire=e->expire;
- ((struct srv_rdata*)e->rr_lst->rdata)->priority = priority;
- ((struct srv_rdata*)e->rr_lst->rdata)->weight = weight;
- ((struct srv_rdata*)e->rr_lst->rdata)->port = port;
- ((struct srv_rdata*)e->rr_lst->rdata)->name_len = rr_name->len;
- memcpy(((struct srv_rdata*)e->rr_lst->rdata)->name, rr_name->s, rr_name->len);
- return e;
- }
- /* create a dns hash entry from a name and a rdata list (pkg_malloc'ed)
- * (it will use only the type records with the name "name" from the
- * rdata list with one exception: if a matching CNAME with the same
- * name is found, the search will stop and this will be the record used)
- * returns 0 on error and removes the used elements from the rdata list*/
- inline static struct dns_hash_entry* dns_cache_mk_rd_entry(str* name, int type,
- struct rdata** rd_lst)
- {
- struct dns_hash_entry* e;
- struct dns_rr* rr;
- struct dns_rr** tail_rr;
- struct rdata** p;
- struct rdata* tmp_lst;
- struct rdata** tail;
- struct rdata* l;
- int size;
- ticks_t now;
- unsigned int max_ttl;
- unsigned int ttl;
- int i;
- #define rec_matches(rec, t, n) /*(struct rdata* record, int type, str* name)*/\
- ( ((rec)->name_len==(n)->len) && ((rec)->type==(t)) && \
- (strncasecmp((rec)->name, (n)->s, (n)->len)==0))
- /* init */
- tmp_lst=0;
- tail=&tmp_lst;
- /* everything is allocated in one block: dns_hash_entry + name +
- * + dns_rr + rdata_raw+ ....; dns_rr must start at an aligned adress,
- * hence we need to round dns_hash_entry+name size to a sizeof(long)
- * multiple. If rdata type requires it, rdata_raw might need to be also
- * aligned.
- * Memory image:
- * struct dns_hash_entry (e)
- * name (name_len+1 bytes) (&e->name[0])
- * padding to multiple of sizeof(char*)
- * dns_rr1 (e->rr_lst)
- * possible padding: no padding for a_rdata or aaaa_rdata,
- * multipe of sizeof(short) for srv_rdata,
- * multiple of sizeof(long) for naptr_rdata and others
- * dns_rr1->rdata (e->rr_lst->rdata)
- * padding to multipe of sizeof long
- * dns_rr2 (e->rr_lst->next)
- * ....
- *
- */
- size=0;
- if (*rd_lst==0)
- return 0;
- /* find the first matching rr, if it's a CNAME use CNAME as type,
- * if not continue with the original type */
- for(p=rd_lst; *p; p=&(*p)->next){
- if (((*p)->name_len==name->len) &&
- (((*p)->type==type) || ((*p)->type==T_CNAME)) &&
- (strncasecmp((*p)->name, name->s, name->len)==0)){
- type=(*p)->type;
- break;
- }
- }
- /* continue, we found the type we are looking for */
- switch(type){
- case T_A:
- for(; *p;){
- if (!rec_matches((*p), type, name)){
- /* skip this record */
- p=&(*p)->next; /* advance */
- continue;
- }
- size+=ROUND_POINTER(sizeof(struct dns_rr)+
- sizeof(struct a_rdata));
- /* add it to our tmp. lst */
- *tail=*p;
- tail=&(*p)->next;
- /* detach it from the rd list */
- *p=(*p)->next;
- /* don't advance p, because the crt. elem. has
- * just been elimintated */
- }
- break;
- case T_AAAA:
- for(; *p;){
- if (!rec_matches((*p), type, name)){
- /* skip this record */
- p=&(*p)->next; /* advance */
- continue;
- }
- /* no padding */
- size+=ROUND_POINTER(sizeof(struct dns_rr)+
- sizeof(struct aaaa_rdata));
- /* add it to our tmp. lst */
- *tail=*p;
- tail=&(*p)->next;
- /* detach it from the rd list */
- *p=(*p)->next;
- /* don't advance p, because the crt. elem. has
- * just been elimintated */
- }
- break;
- case T_SRV:
- for(; *p;){
- if (!rec_matches((*p), type, name)){
- /* skip this record */
- p=&(*p)->next; /* advance */
- continue;
- }
- /* padding to short */
- size+=ROUND_POINTER(ROUND_SHORT(sizeof(struct dns_rr))+
- SRV_RDATA_SIZE(*(struct srv_rdata*)(*p)->rdata));
- /* add it to our tmp. lst */
- *tail=*p;
- tail=&(*p)->next;
- /* detach it from the rd list */
- *p=(*p)->next;
- /* don't advance p, because the crt. elem. has
- * just been elimintated */
- }
- break;
- case T_NAPTR:
- for(; *p;){
- if (!rec_matches((*p), type, name)){
- /* skip this record */
- p=&(*p)->next; /* advance */
- continue;
- }
- /* padding to char* */
- size+=ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
- NAPTR_RDATA_SIZE(*(struct naptr_rdata*)(*p)->rdata));
- /* add it to our tmp. lst */
- *tail=*p;
- tail=&(*p)->next;
- /* detach it from the rd list */
- *p=(*p)->next;
- /* don't advance p, because the crt. elem. has
- * just been elimintated */
- }
- break;
- case T_CNAME:
- for(; *p;){
- if (!rec_matches((*p), type, name)){
- /* skip this record */
- p=&(*p)->next; /* advance */
- continue;
- }
- /* no padding */
- size+=ROUND_POINTER(sizeof(struct dns_rr)+
- CNAME_RDATA_SIZE(*(struct cname_rdata*)(*p)->rdata));
- /* add it to our tmp. lst */
- *tail=*p;
- tail=&(*p)->next;
- /* detach it from the rd list */
- *p=(*p)->next;
- /* don't advance p, because the crt. elem. has
- * just been elimintated */
- }
- break;
- case T_TXT:
- for(; *p;){
- if (!rec_matches((*p), type, name)){
- /* skip this record */
- p=&(*p)->next; /* advance */
- continue;
- }
- /* padding to char* (because of txt[]->cstr*/
- size+=ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
- TXT_RDATA_SIZE(*(struct txt_rdata*)(*p)->rdata));
- /* add it to our tmp. lst */
- *tail=*p;
- tail=&(*p)->next;
- /* detach it from the rd list */
- *p=(*p)->next;
- /* don't advance p, because the crt. elem. has
- * just been elimintated */
- }
- break;
- case T_EBL:
- for(; *p;){
- if (!rec_matches((*p), type, name)){
- /* skip this record */
- p=&(*p)->next; /* advance */
- continue;
- }
- /* padding to char* (because of the char* pointers */
- size+=ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
- EBL_RDATA_SIZE(*(struct ebl_rdata*)(*p)->rdata));
- /* add it to our tmp. lst */
- *tail=*p;
- tail=&(*p)->next;
- /* detach it from the rd list */
- *p=(*p)->next;
- /* don't advance p, because the crt. elem. has
- * just been elimintated */
- }
- break;
- case T_PTR:
- for(; *p;){
- if (!rec_matches((*p), type, name)){
- /* skip this record */
- p=&(*p)->next; /* advance */
- continue;
- }
- /* no padding */
- size+=ROUND_POINTER(sizeof(struct dns_rr)+
- PTR_RDATA_SIZE(*(struct ptr_rdata*)(*p)->rdata));
- /* add it to our tmp. lst */
- *tail=*p;
- tail=&(*p)->next;
- /* detach it from the rd list */
- *p=(*p)->next;
- /* don't advance p, because the crt. elem. has
- * just been elimintated */
- }
- break;
- default:
- LOG(L_CRIT, "BUG: dns_cache_mk_rd_entry: type %d not "
- "supported\n", type);
- /* we don't know what to do with it, so don't
- * add it to the tmp_lst */
- return 0; /* error */
- }
- *tail=0; /* mark the end of our tmp_lst */
- if (size==0){
- #ifdef DNS_CACHE_DEBUG
- DBG("dns_cache_mk_rd_entry: entry %.*s (%d) not found\n",
- name->len, name->s, type);
- #endif
- return 0;
- }
- /* compute size */
- size+=ROUND_POINTER(sizeof(struct dns_hash_entry)+name->len-1+1);
- e=shm_malloc(size);
- if (e==0){
- LOG(L_ERR, "ERROR: dns_cache_mk_rd_entry: out of memory\n");
- return 0;
- }
- memset(e, 0, size); /* init with 0 */
- clist_init(e, next, prev);
- e->total_size=size;
- e->name_len=name->len;
- e->type=type;
- now=get_ticks_raw();
- e->last_used=now;
- memcpy(e->name, name->s, name->len); /* memset makes sure is 0-term. */
- e->rr_lst=(struct dns_rr*)((char*)e+
- ROUND_POINTER(sizeof(struct dns_hash_entry)+name->len-1+1));
- tail_rr=&(e->rr_lst);
- rr=e->rr_lst;
- max_ttl=0;
- /* copy the actual data */
- switch(type){
- case T_A:
- for(l=tmp_lst; l; l=l->next){
- ttl=FIX_TTL(l->ttl);
- rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- max_ttl=MAX(max_ttl, ttl);
- rr->rdata=(void*)((char*)rr+sizeof(struct dns_rr));
- memcpy(rr->rdata, l->rdata, sizeof(struct a_rdata));
- rr->next=(void*)((char*)rr+ROUND_POINTER(sizeof(struct dns_rr)+
- sizeof(struct a_rdata)));
- tail_rr=&(rr->next);
- rr=rr->next;
- }
- break;
- case T_AAAA:
- for(l=tmp_lst; l; l=l->next){
- ttl=FIX_TTL(l->ttl);
- rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- max_ttl=MAX(max_ttl, ttl);
- rr->rdata=(void*)((char*)rr+sizeof(struct dns_rr));
- memcpy(rr->rdata, l->rdata, sizeof(struct aaaa_rdata));
- rr->next=(void*)((char*)rr+ROUND_POINTER(sizeof(struct dns_rr)+
- sizeof(struct aaaa_rdata)));
- tail_rr=&(rr->next);
- rr=rr->next;
- }
- break;
- case T_SRV:
- for(l=tmp_lst; l; l=l->next){
- ttl=FIX_TTL(l->ttl);
- rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- max_ttl=MAX(max_ttl, ttl);
- rr->rdata=(void*)((char*)rr+
- ROUND_SHORT(sizeof(struct dns_rr)));
- /* copy the whole srv_rdata block*/
- memcpy(rr->rdata, l->rdata,
- SRV_RDATA_SIZE(*(struct srv_rdata*)l->rdata) );
- rr->next=(void*)((char*)rr+
- ROUND_POINTER( ROUND_SHORT(sizeof(struct dns_rr))+
- SRV_RDATA_SIZE(
- *(struct srv_rdata*)l->rdata)));
- tail_rr=&(rr->next);
- rr=rr->next;
- }
- break;
- case T_NAPTR:
- for(l=tmp_lst; l; l=l->next){
- ttl=FIX_TTL(l->ttl);
- rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- max_ttl=MAX(max_ttl, ttl);
- rr->rdata=(void*)((char*)rr+
- ROUND_POINTER(sizeof(struct dns_rr)));
- /* copy the whole naptr_rdata block*/
- memcpy(rr->rdata, l->rdata,
- NAPTR_RDATA_SIZE(*(struct naptr_rdata*)l->rdata) );
- /* adjust the string pointer */
- ((struct naptr_rdata*)rr->rdata)->flags=
- translate_pointer((char*)rr->rdata, (char*)l->rdata,
- (((struct naptr_rdata*)l->rdata)->flags));
- ((struct naptr_rdata*)rr->rdata)->services=
- translate_pointer((char*)rr->rdata, (char*)l->rdata,
- (((struct naptr_rdata*)l->rdata)->services));
- ((struct naptr_rdata*)rr->rdata)->regexp=
- translate_pointer((char*)rr->rdata, (char*)l->rdata,
- (((struct naptr_rdata*)l->rdata)->regexp));
- ((struct naptr_rdata*)rr->rdata)->repl=
- translate_pointer((char*)rr->rdata, (char*)l->rdata,
- (((struct naptr_rdata*)l->rdata)->repl));
- rr->next=(void*)((char*)rr+
- ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
- NAPTR_RDATA_SIZE(
- *(struct naptr_rdata*)l->rdata)));
- tail_rr=&(rr->next);
- rr=rr->next;
- }
- break;
- case T_CNAME:
- for(l=tmp_lst; l; l=l->next){
- ttl=FIX_TTL(l->ttl);
- rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- max_ttl=MAX(max_ttl, ttl);
- rr->rdata=(void*)((char*)rr+sizeof(struct dns_rr));
- memcpy(rr->rdata, l->rdata,
- CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata));
- rr->next=(void*)((char*)rr+ROUND_POINTER(sizeof(struct dns_rr)+
- CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata)));
- tail_rr=&(rr->next);
- rr=rr->next;
- }
- break;
- case T_TXT:
- for(l=tmp_lst; l; l=l->next){
- ttl=FIX_TTL(l->ttl);
- rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- max_ttl=MAX(max_ttl, ttl);
- rr->rdata=(void*)((char*)rr+
- ROUND_POINTER(sizeof(struct dns_rr)));
- memcpy(rr->rdata, l->rdata,
- TXT_RDATA_SIZE(*(struct txt_rdata*)l->rdata));
- /* adjust the string pointers */
- for (i=0; i<((struct txt_rdata*)l->rdata)->cstr_no; i++){
- ((struct txt_rdata*)rr->rdata)->txt[i].cstr=
- translate_pointer((char*)rr->rdata, (char*)l->rdata,
- ((struct txt_rdata*)l->rdata)->txt[i].cstr);
- }
- rr->next=(void*)((char*)rr+
- ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
- TXT_RDATA_SIZE(*(struct txt_rdata*)l->rdata)));
- tail_rr=&(rr->next);
- rr=rr->next;
- }
- break;
- case T_EBL:
- for(l=tmp_lst; l; l=l->next){
- ttl=FIX_TTL(l->ttl);
- rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- max_ttl=MAX(max_ttl, ttl);
- rr->rdata=(void*)((char*)rr+
- ROUND_POINTER(sizeof(struct dns_rr)));
- memcpy(rr->rdata, l->rdata,
- EBL_RDATA_SIZE(*(struct ebl_rdata*)l->rdata));
- /* adjust the string pointers */
- ((struct ebl_rdata*)rr->rdata)->separator=
- translate_pointer((char*)rr->rdata, (char*)l->rdata,
- ((struct ebl_rdata*)l->rdata)->separator);
- ((struct ebl_rdata*)rr->rdata)->separator=
- translate_pointer((char*)rr->rdata, (char*)l->rdata,
- ((struct ebl_rdata*)l->rdata)->separator);
- ((struct ebl_rdata*)rr->rdata)->apex=
- translate_pointer((char*)rr->rdata, (char*)l->rdata,
- ((struct ebl_rdata*)l->rdata)->apex);
- rr->next=(void*)((char*)rr+
- ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
- EBL_RDATA_SIZE(*(struct ebl_rdata*)l->rdata)));
- tail_rr=&(rr->next);
- rr=rr->next;
- }
- break;
- case T_PTR:
- for(l=tmp_lst; l; l=l->next){
- ttl=FIX_TTL(l->ttl);
- rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- max_ttl=MAX(max_ttl, ttl);
- rr->rdata=(void*)((char*)rr+sizeof(struct dns_rr));
- memcpy(rr->rdata, l->rdata,
- PTR_RDATA_SIZE(*(struct ptr_rdata*)l->rdata));
- rr->next=(void*)((char*)rr+ROUND_POINTER(sizeof(struct dns_rr)+
- PTR_RDATA_SIZE(*(struct ptr_rdata*)l->rdata)));
- tail_rr=&(rr->next);
- rr=rr->next;
- }
- break;
- default:
- /* do nothing */
- LOG(L_CRIT, "BUG: dns_cache_mk_rd_entry: create: type %d not "
- "supported\n", type);
- ;
- }
- *tail_rr=0; /* terminate the list */
- e->expire=now+S_TO_TICKS(max_ttl);
- free_rdata_list(tmp_lst);
- return e;
- }
- /* structure used only inside dns_cache_mk_rd_entry2 to break
- * the list of records into records of the same type */
- struct tmp_rec{
- struct rdata* rd;
- struct dns_hash_entry* e;
- struct dns_rr* rr;
- struct dns_rr** tail_rr;
- int max_ttl;
- int size;
- };
- /* create several dns hash entries from a list of rdata structs
- * returns 0 on error */
- inline static struct dns_hash_entry* dns_cache_mk_rd_entry2(struct rdata* rd)
- {
- struct rdata* l;
- ticks_t now;
- struct tmp_rec rec[MAX_DNS_RECORDS];
- int rec_idx[MAX_DNS_RECORDS];
- int r, i, j;
- int no_records; /* number of different records */
- unsigned int ttl;
- no_records=0;
- rec[0].e=0;
- /* everything is allocated in one block: dns_hash_entry + name +
- * + dns_rr + rdata_raw+ ....; dns_rr must start at an aligned adress,
- * hence we need to round dns_hash_entry+name size to a sizeof(long)
- * multiple. If rdata type requires it, rdata_raw might need to be also
- * aligned.
- * Memory image:
- * struct dns_hash_entry (e)
- * name (name_len+1 bytes) (&e->name[0])
- * padding to multiple of sizeof(char*)
- * dns_rr1 (e->rr_lst)
- * possible padding: no padding for a_rdata or aaaa_rdata,
- * multipe of sizeof(short) for srv_rdata,
- * multiple of sizeof(long) for naptr_rdata and others
- * dns_rr1->rdata (e->rr_lst->rdata)
- * padding to multipe of sizeof long
- * dns_rr2 (e->rr_lst->next)
- * ....
- *
- */
- /* compute size */
- for(l=rd, i=0; l && (i<MAX_DNS_RECORDS); l=l->next, i++){
- for (r=0; r<no_records; r++){
- if ((l->type==rec[r].rd->type) &&
- (l->name_len==rec[r].rd->name_len)
- && (strncasecmp(l->name, rec[r].rd->name, l->name_len)==0)){
- /* found */
- goto found;
- }
- }
- /* not found, create new */
- if (no_records<MAX_DNS_RECORDS){
- rec[r].rd=l;
- rec[r].e=0;
- rec[r].size=ROUND_POINTER(sizeof(struct dns_hash_entry)+
- rec[r].rd->name_len-1+1);
- no_records++;
- }else{
- LOG(L_ERR, "ERROR: dns_cache_mk_rd_entry2: too many records: %d\n",
- no_records);
- /* skip */
- continue;
- }
- found:
- rec_idx[i]=r;
- switch(l->type){
- case T_A:
- /* no padding */
- rec[r].size+=ROUND_POINTER(sizeof(struct dns_rr)+
- sizeof(struct a_rdata));
- break;
- case T_AAAA:
- /* no padding */
- rec[r].size+=ROUND_POINTER(sizeof(struct dns_rr)+
- sizeof(struct aaaa_rdata));
- break;
- case T_SRV:
- /* padding to short */
- rec[r].size+=ROUND_POINTER(ROUND_SHORT(sizeof(struct dns_rr))+
- SRV_RDATA_SIZE(*(struct srv_rdata*)l->rdata));
- break;
- case T_NAPTR:
- /* padding to char* */
- rec[r].size+=ROUND_POINTER(ROUND_POINTER(
- sizeof(struct dns_rr))+
- NAPTR_RDATA_SIZE(*(struct naptr_rdata*)l->rdata));
- break;
- case T_CNAME:
- /* no padding */
- rec[r].size+=ROUND_POINTER(sizeof(struct dns_rr)+
- CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata));
- break;
- case T_TXT:
- /* padding to char* (because of txt[]->cstr)*/
- rec[r].size+=ROUND_POINTER(ROUND_POINTER(
- sizeof(struct dns_rr))+
- TXT_RDATA_SIZE(*(struct txt_rdata*)l->rdata));
- break;
- case T_EBL:
- /* padding to char* (because of char* pointers)*/
- rec[r].size+=ROUND_POINTER(ROUND_POINTER(
- sizeof(struct dns_rr))+
- EBL_RDATA_SIZE(*(struct ebl_rdata*)l->rdata));
- break;
- case T_PTR:
- /* no padding */
- rec[r].size+=ROUND_POINTER(sizeof(struct dns_rr)+
- PTR_RDATA_SIZE(*(struct ptr_rdata*)l->rdata));
- break;
- default:
- LOG(L_CRIT, "BUG: dns_cache_mk_rd_entry: type %d not "
- "supported\n", l->type);
- }
- }
- now=get_ticks_raw();
- /* alloc & init the entries */
- for (r=0; r<no_records; r++){
- rec[r].e=shm_malloc(rec[r].size);
- if (rec[r].e==0){
- LOG(L_ERR, "ERROR: dns_cache_mk_rd_entry: out of memory\n");
- goto error;
- }
- memset(rec[r].e, 0, rec[r].size); /* init with 0*/
- rec[r].e->total_size=rec[r].size;
- rec[r].e->name_len=rec[r].rd->name_len;
- rec[r].e->type=rec[r].rd->type;
- rec[r].e->last_used=now;
- /* memset makes sure is 0-term. */
- memcpy(rec[r].e->name, rec[r].rd->name, rec[r].rd->name_len);
- rec[r].e->rr_lst=(struct dns_rr*)((char*)rec[r].e+
- ROUND_POINTER(sizeof(struct dns_hash_entry)+rec[r].e->name_len
- -1+1));
- rec[r].tail_rr=&(rec[r].e->rr_lst);
- rec[r].rr=rec[r].e->rr_lst;
- rec[r].max_ttl=0;
- /* link them in a list */
- if (r==0){
- clist_init(rec[r].e, next, prev);
- }else{
- clist_append(rec[0].e, rec[r].e, next, prev);
- }
- }
- /* copy the actual data */
- for(l=rd, i=0; l && (i<MAX_DNS_RECORDS); l=l->next, i++){
- r=rec_idx[i];
- ttl=FIX_TTL(l->ttl);
- switch(l->type){
- case T_A:
- rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
- rec[r].rr->rdata=(void*)((char*)rec[r].rr+
- sizeof(struct dns_rr));
- memcpy(rec[r].rr->rdata, l->rdata, sizeof(struct a_rdata));
- rec[r].rr->next=(void*)((char*)rec[r].rr+
- ROUND_POINTER(sizeof(struct dns_rr)+
- sizeof(struct a_rdata)));
- rec[r].tail_rr=&(rec[r].rr->next);
- rec[r].rr=rec[r].rr->next;
- break;
- case T_AAAA:
- rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
- rec[r].rr->rdata=(void*)((char*)rec[r].rr+
- sizeof(struct dns_rr));
- memcpy(rec[r].rr->rdata, l->rdata, sizeof(struct aaaa_rdata));
- rec[r].rr->next=(void*)((char*)rec[r].rr+
- ROUND_POINTER(sizeof(struct dns_rr)+
- sizeof(struct aaaa_rdata)));
- rec[r].tail_rr=&(rec[r].rr->next);
- rec[r].rr=rec[r].rr->next;
- break;
- case T_SRV:
- rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
- rec[r].rr->rdata=(void*)((char*)rec[r].rr+
- ROUND_SHORT(sizeof(struct dns_rr)));
- /* copy the whole srv_rdata block*/
- memcpy(rec[r].rr->rdata, l->rdata,
- SRV_RDATA_SIZE(*(struct srv_rdata*)l->rdata) );
- rec[r].rr->next=(void*)((char*)rec[r].rr+
- ROUND_POINTER( ROUND_SHORT(sizeof(struct dns_rr))+
- SRV_RDATA_SIZE(
- *(struct srv_rdata*)l->rdata)));
- rec[r].tail_rr=&(rec[r].rr->next);
- rec[r].rr=rec[r].rr->next;
- break;
- case T_NAPTR:
- rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
- rec[r].rr->rdata=(void*)((char*)rec[r].rr+
- ROUND_POINTER(sizeof(struct dns_rr)));
- /* copy the whole srv_rdata block*/
- memcpy(rec[r].rr->rdata, l->rdata,
- NAPTR_RDATA_SIZE(*(struct naptr_rdata*)l->rdata) );
- /* adjust the string pointer */
- ((struct naptr_rdata*)rec[r].rr->rdata)->flags=
- translate_pointer((char*)rec[r].rr->rdata, (char*)l->rdata,
- (((struct naptr_rdata*)l->rdata)->flags));
- ((struct naptr_rdata*)rec[r].rr->rdata)->services=
- translate_pointer((char*)rec[r].rr->rdata, (char*)l->rdata,
- (((struct naptr_rdata*)l->rdata)->services));
- ((struct naptr_rdata*)rec[r].rr->rdata)->regexp=
- translate_pointer((char*)rec[r].rr->rdata, (char*)l->rdata,
- (((struct naptr_rdata*)l->rdata)->regexp));
- ((struct naptr_rdata*)rec[r].rr->rdata)->repl=
- translate_pointer((char*)rec[r].rr->rdata, (char*)l->rdata,
- (((struct naptr_rdata*)l->rdata)->repl));
- rec[r].rr->next=(void*)((char*)rec[r].rr+
- ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
- NAPTR_RDATA_SIZE(
- *(struct naptr_rdata*)l->rdata)));
- rec[r].tail_rr=&(rec[r].rr->next);
- rec[r].rr=rec[r].rr->next;
- break;
- case T_CNAME:
- rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
- rec[r].rr->rdata=(void*)((char*)rec[r].rr
- +sizeof(struct dns_rr));
- memcpy(rec[r].rr->rdata, l->rdata,
- CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata));
- rec[r].rr->next=(void*)((char*)rec[r].rr+
- ROUND_POINTER(sizeof(struct dns_rr)+
- CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata)));
- rec[r].tail_rr=&(rec[r].rr->next);
- rec[r].rr=rec[r].rr->next;
- break;
- case T_TXT:
- rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
- rec[r].rr->rdata=(void*)((char*)rec[r].rr+
- ROUND_POINTER(sizeof(struct dns_rr)));
- memcpy(rec[r].rr->rdata, l->rdata,
- TXT_RDATA_SIZE(*(struct txt_rdata*)l->rdata));
- /* adjust the string pointers */
- for (j=0; j<((struct txt_rdata*)l->rdata)->cstr_no; j++){
- ((struct txt_rdata*)rec[r].rr->rdata)->txt[j].cstr=
- translate_pointer((char*)rec[r].rr->rdata,
- (char*)l->rdata,
- ((struct txt_rdata*)l->rdata)->txt[j].cstr);
- }
- rec[r].rr->next=(void*)((char*)rec[r].rr+
- ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
- TXT_RDATA_SIZE(*(struct txt_rdata*)l->rdata)));
- rec[r].tail_rr=&(rec[r].rr->next);
- rec[r].rr=rec[r].rr->next;
- break;
- case T_EBL:
- rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
- rec[r].rr->rdata=(void*)((char*)rec[r].rr+
- ROUND_POINTER(sizeof(struct dns_rr)));
- memcpy(rec[r].rr->rdata, l->rdata,
- EBL_RDATA_SIZE(*(struct ebl_rdata*)l->rdata));
- /* adjust the string pointers */
- ((struct ebl_rdata*)rec[r].rr->rdata)->separator=
- translate_pointer((char*)rec[r].rr->rdata,
- (char*)l->rdata,
- ((struct ebl_rdata*)l->rdata)->separator);
- ((struct ebl_rdata*)rec[r].rr->rdata)->apex=
- translate_pointer((char*)rec[r].rr->rdata,
- (char*)l->rdata,
- ((struct ebl_rdata*)l->rdata)->apex);
- rec[r].rr->next=(void*)((char*)rec[r].rr+
- ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
- EBL_RDATA_SIZE(*(struct ebl_rdata*)l->rdata)));
- rec[r].tail_rr=&(rec[r].rr->next);
- rec[r].rr=rec[r].rr->next;
- break;
- case T_PTR:
- rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
- rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
- rec[r].rr->rdata=(void*)((char*)rec[r].rr
- +sizeof(struct dns_rr));
- memcpy(rec[r].rr->rdata, l->rdata,
- PTR_RDATA_SIZE(*(struct ptr_rdata*)l->rdata));
- rec[r].rr->next=(void*)((char*)rec[r].rr+
- ROUND_POINTER(sizeof(struct dns_rr)+
- PTR_RDATA_SIZE(*(struct ptr_rdata*)l->rdata)));
- rec[r].tail_rr=&(rec[r].rr->next);
- rec[r].rr=rec[r].rr->next;
- break;
- default:
- /* do nothing */
- ;
- }
- }
- for (r=0; r<no_records; r++){
- *rec[r].tail_rr=0; /* terminate the list */
- rec[r].e->expire=now+S_TO_TICKS(rec[r].max_ttl);
- }
- return rec[0].e;
- error:
- for (r=0; r<no_records; r++){
- dns_destroy_entry(rec[r].e);
- }
- return 0;
- }
- inline static struct dns_hash_entry* dns_get_entry(str* name, int type);
- #define CACHE_RELEVANT_RECS_ONLY
- #ifdef CACHE_RELEVANT_RECS_ONLY
- /* internal only: gets related entries from a rdata list, appends them
- * to e (list) and returns:
- * - e if e is of the requested type
- * - if e is a CNAME, tries to get to the end of the CNAME chain and returns
- * the final entry if the types match or 0 if the chain is unfinished
- * - 0 on error/not found
- * records is modified (the used records are removed from the list and freed)
- *
- * WARNING: - records must be pkg_malloc'ed
- * Notes: - if the return is 0 and e->type==T_CNAME, the list will contain
- * the CNAME chain (the last element being the last CNAME)
- * */
- inline static struct dns_hash_entry* dns_get_related(struct dns_hash_entry* e,
- int type,
- struct rdata** records)
- {
- struct dns_hash_entry* ret;
- struct dns_hash_entry* l;
- struct dns_hash_entry* t;
- struct dns_hash_entry* lst_end;
- struct dns_rr* rr;
- static int cname_chain_len=0;
- str tmp;
- ret=0;
- l=e;
- #ifdef DNS_CACHE_DEBUG
- DBG("dns_get_related(%p (%.*s, %d), %d, *%p) (%d)\n", e,
- e->name_len, e->name, e->type, type, *records, cname_chain_len);
- #endif
- clist_init(l, next, prev);
- if (type==e->type){
- ret=e;
- switch(e->type){
- case T_SRV:
- for (rr=e->rr_lst; rr && *records; rr=rr->next){
- tmp.s=((struct srv_rdata*)rr->rdata)->name;
- tmp.len=((struct srv_rdata*)rr->rdata)->name_len;
- if (!(dns_flags&DNS_IPV6_ONLY)){
- t=dns_cache_mk_rd_entry(&tmp, T_A, records);
- if (t){
- if ((t->type==T_CNAME) && *records)
- dns_get_related(t, T_A, records);
- lst_end=t->prev; /* needed for clist_append*/
- clist_append_sublist(l, t, lst_end, next, prev);
- }
- }
- if (!(dns_flags&DNS_IPV4_ONLY)){
- t=dns_cache_mk_rd_entry(&tmp, T_AAAA, records);
- if (t){
- if ((t->type==T_CNAME) && *records)
- dns_get_related(t, T_AAAA, records);
- lst_end=t->prev; /* needed for clist_append*/
- clist_append_sublist(l, t, lst_end, next, prev);
- }
- }
- }
- break;
- #ifdef USE_NAPTR
- case T_NAPTR:
- #ifdef NAPTR_CACHE_ALL_ARS
- if (*records)
- dns_cache_mk_rd_entry2(*records);
- #else
- for (rr=e->rr_lst; rr && *records; rr=rr->next){
- if (naptr_get_sip_proto((struct naptr_rdata*)rr->rdata)>0){
- tmp.s=((struct naptr_rdata*)rr->rdata)->repl;
- tmp.len=((struct naptr_rdata*)rr->rdata)->repl_len;
- t=dns_cache_mk_rd_entry(&tmp, T_SRV, records);
- if (t){
- if (*records)
- dns_get_related(t, T_SRV, records);
- lst_end=t->prev; /* needed for clist_append*/
- clist_append_sublist(l, t, lst_end, next, prev);
- }
- }
- }
- #endif /* NAPTR_CACHE_ALL_ARS */
- #endif /* USE_NAPTR */
- break;
- default:
- /* nothing extra */
- break;
- }
- }else if ((e->type==T_CNAME) && (cname_chain_len<MAX_CNAME_CHAIN)){
- /* only one cname is allowed (rfc2181), so we ignore
- * the others (we take only the first one) */
- tmp.s=((struct cname_rdata*)e->rr_lst->rdata)->name;
- tmp.len=((struct cname_rdata*)e->rr_lst->rdata)->name_len;
- t=dns_cache_mk_rd_entry(&tmp, type, records);
- if (t){
- if (*records){
- cname_chain_len++;
- ret=dns_get_related(t, type, records);
- cname_chain_len--;
- lst_end=t->prev;
- clist_append_sublist(l, t, lst_end, next, prev);
- }else{
- /* if no more recs, but we found the orig. target anyway,
- * return it (e.g. recs are only CNAME x & x A 1.2.3.4 or
- * CNAME & SRV) */
- if (t->type==type)
- ret=t;
- clist_append(l, t, next, prev);
- }
- }
- }
- return ret;
- }
- #endif
- /* calls the external resolver and populates the cache with the result
- * returns: 0 on error, pointer to hash entry on success
- * WARNING: make sure you use dns_hash_entry_put() when you're
- * finished with the result)
- * */
- inline static struct dns_hash_entry* dns_cache_do_request(str* name, int type)
- {
- struct rdata* records;
- struct dns_hash_entry* e;
- struct dns_hash_entry* l;
- struct dns_hash_entry* r;
- struct dns_hash_entry* t;
- struct ip_addr* ip;
- str cname_val;
- char name_buf[MAX_DNS_NAME];
- struct dns_hash_entry* old;
- str rec_name;
- int add_record, h, err;
- e=0;
- l=0;
- cname_val.s=0;
- old = NULL;
- #ifdef USE_DNS_CACHE_STATS
- if (dns_cache_stats)
- dns_cache_stats[process_no].dns_req_cnt++;
- #endif /* USE_DNS_CACHE_STATS */
- if (type==T_A){
- #ifdef USE_IPV6
- if (str2ip6(name)!=0)
- goto end;
- #endif /* USE_IPV6 */
- if ((ip=str2ip(name))!=0){
- e=dns_cache_mk_ip_entry(name, ip);
- if (likely(e))
- atomic_set(&e->refcnt, 1);/* because we ret. a ref. to it*/
- goto end; /* we do not cache obvious stuff */
- }
- }
- #ifdef USE_IPV6
- else if (type==T_AAAA){
- if (str2ip(name)!=0)
- goto end;
- if ((ip=str2ip6(name))!=0){
- e=dns_cache_mk_ip_entry(name, ip);
- if (likely(e))
- atomic_set(&e->refcnt, 1);/* because we ret. a ref. to it*/
- goto end;/* we do not cache obvious stuff */
- }
- }
- #endif /* USE_IPV6 */
- #ifdef DNS_WATCHDOG_SUPPORT
- if (atomic_get(dns_servers_up)==0)
- goto end; /* the servers are down, needless to perform the query */
- #endif
- if (name->len>=MAX_DNS_NAME){
- LOG(L_ERR, "ERROR: dns_cache_do_request: name too long (%d chars)\n",
- name->len);
- goto end;
- }
- /* null terminate the string, needed by get_record */
- memcpy(name_buf, name->s, name->len);
- name_buf[name->len]=0;
- records=get_record(name_buf, type, RES_AR);
- if (records){
- #ifdef CACHE_RELEVANT_RECS_ONLY
- e=dns_cache_mk_rd_entry(name, type, &records);
- if (likely(e)){
- l=e;
- e=dns_get_related(l, type, &records);
- /* e should contain the searched entry (if found) and l
- * all the entries (e and related) */
- if (likely(e)){
- atomic_set(&e->refcnt, 1); /* 1 because we return a
- ref. to it */
- }else{
- /* e==0 => l contains a cname list => we use the last
- * cname from the chain for a new resolve attempt (l->prev) */
- /* only one cname record is allowed (rfc2181), so we ignore
- * the others (we take only the first one) */
- cname_val.s=
- ((struct cname_rdata*)l->prev->rr_lst->rdata)->name;
- cname_val.len=
- ((struct cname_rdata*)l->prev->rr_lst->rdata)->name_len;
- DBG("dns_cache_do_request: cname detected: %.*s (%d)\n",
- cname_val.len, cname_val.s, cname_val.len);
- }
- /* add all the records to the hash */
- l->prev->next=0; /* we break the double linked list for easier
- searching */
- LOCK_DNS_HASH(); /* optimization */
- for (r=l; r; r=t){
- t=r->next;
- /* add the new record to the cache by default */
- add_record = 1;
- if (cfg_get(core, core_cfg, dns_cache_rec_pref) > 0) {
- /* check whether there is an old record with the
- * same type in the cache */
- rec_name.s = r->name;
- rec_name.len = r->name_len;
- old = _dns_hash_find(&rec_name, r->type, &h, &err);
- if (old) {
- if (old->type != r->type) {
- /* probably CNAME found */
- old = NULL;
- } else if (old->ent_flags & DNS_FLAG_PERMANENT) {
- /* never overwrite permanent entries */
- add_record = 0;
- } else if ((old->ent_flags & DNS_FLAG_BAD_NAME) == 0) {
- /* Non-negative, non-permanent entry found with
- * the same type. */
- add_record =
- /* prefer new records */
- ((cfg_get(core, core_cfg, dns_cache_rec_pref) == 2)
- /* prefer the record with the longer lifetime */
- || ((cfg_get(core, core_cfg, dns_cache_rec_pref) == 3)
- && TICKS_LT(old->expire, r->expire)));
- }
- }
- }
- if (add_record) {
- dns_cache_add_unsafe(r); /* refcnt++ inside */
- if (atomic_get(&r->refcnt)==0){
- /* if cache adding failed and nobody else is interested
- * destroy this entry */
- dns_destroy_entry(r);
- }
- if (old) {
- _dns_hash_remove(old);
- old = NULL;
- }
- } else {
- if (old) {
- if (r == e) {
- /* this entry has to be returned */
- e = old;
- atomic_inc(&e->refcnt);
- }
- old = NULL;
- }
- dns_destroy_entry(r);
- }
- }
- UNLOCK_DNS_HASH();
- /* if only cnames found => try to resolve the last one */
- if (cname_val.s){
- DBG("dns_cache_do_request: dns_get_entry(cname: %.*s (%d))\n",
- cname_val.len, cname_val.s, cname_val.len);
- e=dns_get_entry(&cname_val, type);
- }
- }
- #else
- l=dns_cache_mk_rd_entry2(records);
- #endif
- free_rdata_list(records);
- }else if (cfg_get(core, core_cfg, dns_neg_cache_ttl)){
- e=dns_cache_mk_bad_entry(name, type,
- cfg_get(core, core_cfg, dns_neg_cache_ttl), DNS_FLAG_BAD_NAME);
- if (likely(e)) {
- atomic_set(&e->refcnt, 1); /* 1 because we return a ref. to it */
- dns_cache_add(e); /* refcnt++ inside*/
- }
- goto end;
- }
- #ifndef CACHE_RELEVANT_RECS_ONLY
- if (l){
- /* add all the records to the cache, but return only the record
- * we are looking for */
- l->prev->next=0; /* we break the double linked list for easier
- searching */
- LOCK_DNS_HASH(); /* optimization */
- for (r=l; r; r=t){
- t=r->next;
- if (e==0){ /* no entry found yet */
- if (r->type==T_CNAME){
- if ((r->name_len==name->len) && (r->rr_lst) &&
- (strncasecmp(r->name, name->s, name->len)==0)){
- /* update the name with the name from the cname rec. */
- cname_val.s=
- ((struct cname_rdata*)r->rr_lst->rdata)->name;
- cname_val.len=
- ((struct cname_rdata*)r->rr_lst->rdata)->name_len;
- name=&cname_val;
- }
- }else if ((r->type==type) && (r->name_len==name->len) &&
- (strncasecmp(r->name, name->s, name->len)==0)){
- e=r;
- atomic_set(&e->refcnt, 1); /* 1 because we return a ref.
- to it */
- }
- }
- /* add the new record to the cache by default */
- add_record = 1;
- if (cfg_get(core, core_cfg, dns_cache_rec_pref) > 0) {
- /* check whether there is an old record with the
- * same type in the cache */
- rec_name.s = r->name;
- rec_name.len = r->name_len;
- old = _dns_hash_find(&rec_name, r->type, &h, &err);
- if (old) {
- if (old->type != r->type) {
- /* probably CNAME found */
- old = NULL;
- } else if (old->ent_flags & DNS_FLAG_PERMANENT) {
- /* never overwrite permanent entries */
- add_record = 0;
- } else if ((old->ent_flags & DNS_FLAG_BAD_NAME) == 0) {
- /* Non-negative, non-permanent entry found with
- * the same type. */
- add_record =
- /* prefer new records */
- ((cfg_get(core, core_cfg, dns_cache_rec_pref) == 2)
- /* prefer the record with the longer lifetime */
- || ((cfg_get(core, core_cfg, dns_cache_rec_pref) == 3)
- && TICKS_LT(old->expire, r->expire)));
- }
- }
- }
- if (add_record) {
- dns_cache_add_unsafe(r); /* refcnt++ inside */
- if (atomic_get(&r->refcnt)==0){
- /* if cache adding failed and nobody else is interested
- * destroy this entry */
- dns_destroy_entry(r);
- }
- if (old) {
- _dns_hash_remove(old);
- old = NULL;
- }
- } else {
- if (old) {
- if (r == e) {
- /* this entry has to be returned */
- e = old;
- atomic_inc(&e->refcnt);
- }
- old = NULL;
- }
- dns_destroy_entry(r);
- }
- }
- UNLOCK_DNS_HASH();
- if ((e==0) && (cname_val.s)){ /* not found, but found a cname */
- /* only one cname is allowed (rfc2181), so we ignore the
- * others (we take only the first one) */
- e=dns_get_entry(&cname_val, type);
- }
- }
- #endif
- end:
- return e;
- }
- /* tries to lookup (name, type) in the hash and if not found tries to make
- * a dns request
- * return: 0 on error, pointer to a dns_hash_entry on success
- * WARNING: when not needed anymore dns_hash_put() must be called! */
- inline static struct dns_hash_entry* dns_get_entry(str* name, int type)
- {
- int h;
- struct dns_hash_entry* e;
- str cname_val;
- int err;
- static int rec_cnt=0; /* recursion protection */
- e=0;
- if (rec_cnt>MAX_CNAME_CHAIN){
- LOG(L_WARN, "WARNING: dns_get_entry: CNAME chain too long or"
- " recursive CNAMEs (\"%.*s\")\n", name->len, name->s);
- goto error;
- }
- rec_cnt++;
- e=dns_hash_get(name, type, &h, &err);
- #ifdef USE_DNS_CACHE_STATS
- if (e) {
- if ((e->ent_flags & DNS_FLAG_BAD_NAME) && dns_cache_stats)
- /* negative DNS cache hit */
- dns_cache_stats[process_no].dc_neg_hits_cnt++;
- else if (((e->ent_flags & DNS_FLAG_BAD_NAME) == 0)
- && dns_cache_stats
- ) /* DNS cache hit */
- dns_cache_stats[process_no].dc_hits_cnt++;
- if (dns_cache_stats)
- dns_cache_stats[process_no].dns_req_cnt++;
- }
- #endif /* USE_DNS_CACHE_STATS */
- if ((e==0) && ((err) || ((e=dns_cache_do_request(name, type))==0))){
- goto error;
- }else if ((e->type==T_CNAME) && (type!=T_CNAME)){
- /* cname found instead which couldn't be resolved with the cached
- * info => try a dns request */
- /* only one cname record is allowed (rfc2181), so we ignore
- * the others (we take only the first one) */
- cname_val.s= ((struct cname_rdata*)e->rr_lst->rdata)->name;
- cname_val.len=((struct cname_rdata*)e->rr_lst->rdata)->name_len;
- dns_hash_put(e); /* not interested in the cname anymore */
- if ((e=dns_cache_do_request(&cname_val, type))==0)
- goto error; /* could not resolve cname */
- }
- /* found */
- if ((e->rr_lst==0) || (e->ent_flags & DNS_FLAG_BAD_NAME)){
- /* negative cache => not resolvable */
- dns_hash_put(e);
- e=0;
- }
- error:
- rec_cnt--;
- return e;
- }
- /* gets the first non-expired record starting with record no
- * from the dns_hash_entry struct e
- * params: e - dns_hash_entry struct
- * *no - it must contain the start record number (0 initially);
- * it will be filled with the returned record number
- * now - current time/ticks value
- * returns pointer to the rr on success and sets no to the rr number
- * 0 on error and fills the error flags
- *
- * Example usage:
- * list all non-expired non-bad-marked ips for name:
- * e=dns_get_entry(name, T_A);
- * if (e){
- * *no=0;
- * now=get_ticks_raw();
- * while(rr=dns_entry_get_rr(e, no, now){
- * DBG("address %d\n", *no);
- * *no++; ( get the next address next time )
- * }
- * }
- */
- inline static struct dns_rr* dns_entry_get_rr( struct dns_hash_entry* e,
- unsigned char* no, ticks_t now)
- {
- struct dns_rr* rr;
- int n;
- #ifdef DNS_WATCHDOG_SUPPORT
- int servers_up;
- servers_up = atomic_get(dns_servers_up);
- #endif
- for(rr=e->rr_lst, n=0;rr && (n<*no);rr=rr->next, n++);/* skip *no records*/
- for(;rr;rr=rr->next){
- if (
- #ifdef DNS_WATCHDOG_SUPPORT
- /* check the expiration time only when the servers are up */
- servers_up &&
- #endif
- ((e->ent_flags & DNS_FLAG_PERMANENT) == 0) &&
- ((s_ticks_t)(now-rr->expire)>=0) /* expired rr */
- )
- continue;
- /* everything is ok now */
- *no=n;
- return rr;
- }
- *no=n;
- return 0;
- }
- #ifdef DNS_SRV_LB
- #define srv_reset_tried(p) (*(p)=0)
- #define srv_marked(p, i) (*(p)&(1UL<<(i)))
- #define srv_mark_tried(p, i) \
- do{ \
- (*(p)|=(1UL<<(i))); \
- }while(0)
- #define srv_next_rr(n, f, i) srv_mark_tried(f, i)
- /* returns a random number between 0 and max inclusive (0<=r<=max) */
- inline static unsigned dns_srv_random(unsigned max)
- {
- return fastrand_max(max);
- }
- /* for a SRV record it will return the next entry to be tried according
- * to the RFC2782 server selection mechanism
- * params:
- * e is a dns srv hash entry
- * no is the start index of the current group (a group is a set of SRV
- * rrs with the same priority)
- * tried is a bitmap where the tried srv rrs of the same priority are
- * marked
- * now - current time/ticks value
- * returns pointer to the rr on success and sets no to the rr number
- * 0 on error and fills the error flags
- * WARNING: unlike dns_entry_get_rr() this will always return another
- * another rr automatically (*no must not be incremented)
- *
- * Example usage:
- * list all non-expired, non-bad-marked, never tried before srv records
- * using the rfc2782 algo:
- * e=dns_get_entry(name, T_SRV);
- * if (e){
- * no=0;
- * srv_reset_tried(&tried);
- * now=get_ticks_raw();
- * while(rr=dns_srv_get_nxt_rr(e, &tried, &no, now){
- * DBG("address %d\n", *no);
- * }
- * }
- *
- */
- inline static struct dns_rr* dns_srv_get_nxt_rr(struct dns_hash_entry* e,
- srv_flags_t* tried,
- unsigned char* no, ticks_t now)
- {
- #define MAX_SRV_GRP_IDX (sizeof(srv_flags_t)*8)
- struct dns_rr* rr;
- struct dns_rr* start_grp;
- int n;
- unsigned sum;
- unsigned prio;
- unsigned rand_w;
- int found;
- int saved_idx;
- int zero_weight; /* number of records with 0 weight */
- int i, idx;
- struct r_sums_entry{
- unsigned r_sum;
- struct dns_rr* rr;
- }r_sums[MAX_SRV_GRP_IDX];
- #ifdef DNS_WATCHDOG_SUPPORT
- int servers_up;
- servers_up = atomic_get(dns_servers_up);
- #endif
- memset(r_sums, 0, sizeof(struct r_sums_entry) * MAX_SRV_GRP_IDX);
- rand_w=0;
- for(rr=e->rr_lst, n=0;rr && (n<*no);rr=rr->next, n++);/* skip *no records*/
- retry:
- if (unlikely(rr==0))
- goto no_more_rrs;
- start_grp=rr;
- prio=((struct srv_rdata*)start_grp->rdata)->priority;
- sum=0;
- saved_idx=-1;
- zero_weight = 0;
- found=0;
- for (idx=0;rr && (prio==((struct srv_rdata*)rr->rdata)->priority) &&
- (idx < MAX_SRV_GRP_IDX); idx++, rr=rr->next){
- if ((
- #ifdef DNS_WATCHDOG_SUPPORT
- /* check the expiration time only when the servers are up */
- servers_up &&
- #endif
- ((e->ent_flags & DNS_FLAG_PERMANENT) == 0) &&
- ((s_ticks_t)(now-rr->expire)>=0) /* expired entry */) ||
- (srv_marked(tried, idx)) ) /* already tried */{
- r_sums[idx].r_sum=0; /* 0 sum, to skip over it */
- r_sums[idx].rr=0; /* debug: mark it as unused */
- continue;
- }
- /* special case, 0 weight records should be "first":
- * remember the first rr int the "virtual" list: A 0 weight must
- * come first if present, else get the first one */
- if ((saved_idx==-1) || (((struct srv_rdata*)rr->rdata)->weight==0)){
- saved_idx=idx;
- }
- zero_weight += (((struct srv_rdata*)rr->rdata)->weight == 0);
- sum+=((struct srv_rdata*)rr->rdata)->weight;
- r_sums[idx].r_sum=sum;
- r_sums[idx].rr=rr;
- found++;
- }
- if (found==0){
- /* try in the next priority group */
- n+=idx; /* next group start idx, last rr */
- srv_reset_tried(tried);
- goto retry;
- }else if ((found==1) || (sum==0) ||
- (((rand_w=(dns_srv_random(sum-1)+1))==1) && zero_weight &&
- (dns_srv_random(DNS_SRV_ZERO_W_CHANCE)==0))){
- /* 1. if only one found, avoid a useless random() call
- and select it (saved_idx will point to it).
- * 2. if the sum of weights is 0 (all have 0 weight) or
- * 3. rand_w==1 and there are records with 0 weight and
- * random(probab. of selecting a 0-weight)
- * immediately select a 0 weight record.
- * (this takes care of the 0-weight at the beginning requirement) */
- i=saved_idx; /* saved idx contains either first 0 weight or first
- valid record */
- goto found;
- }
- /* if we are here => rand_w is not 0 and we have at least 2 valid options
- * => we can safely iterate on the whole r_sums[] whithout any other
- * extra checks */
- for (i=0; (i<idx) && (r_sums[i].r_sum<rand_w); i++);
- found:
- #ifdef DNS_CACHE_DEBUG
- DBG("dns_srv_get_nxt_rr(%p, %lx, %d, %u): selected %d/%d in grp. %d"
- " (rand_w=%d, rr=%p rd=%p p=%d w=%d rsum=%d)\n",
- e, (unsigned long)*tried, *no, now, i, idx, n, rand_w, r_sums[i].rr,
- (r_sums[i].rr)?r_sums[i].rr->rdata:0,
- (r_sums[i].rr&&r_sums[i].rr->rdata)?((struct srv_rdata*)r_sums[i].rr->rdata)->priority:0,
- (r_sums[i].rr&&r_sums[i].rr->rdata)?((struct srv_rdata*)r_sums[i].rr->rdata)->weight:0,
- r_sums[i].r_sum);
- #endif
- /* i is the winner */
- *no=n; /* grp. start */
- srv_mark_tried(tried, i); /* mark it */
- return r_sums[i].rr;
- no_more_rrs:
- *no=n;
- return 0;
- }
- #endif /* DNS_SRV_LB */
- /* gethostbyname compatibility: converts a dns_hash_entry structure
- * to a statical internal hostent structure
- * returns a pointer to the internal hostent structure on success or
- * 0 on error
- */
- inline static struct hostent* dns_entry2he(struct dns_hash_entry* e)
- {
- static struct hostent he;
- static char hostname[256];
- static char* p_aliases[1];
- static char* p_addr[DNS_HE_MAX_ADDR+1];
- static char address[16*DNS_HE_MAX_ADDR]; /* max 10 ipv6 addresses */
- int af, len;
- struct dns_rr* rr;
- unsigned char rr_no;
- ticks_t now;
- int i;
- switch(e->type){
- case T_A:
- af=AF_INET;
- len=4;
- break;
- case T_AAAA:
- #ifdef USE_IPV6
- af=AF_INET6;
- len=16;
- break;
- #else /* USE_IPV6 */
- LOG(L_ERR, "ERROR: dns_entry2he: IPv6 dns cache entry, but "
- "IPv6 support disabled at compile time"
- " (recompile with -DUSE_IPV6)\n");
- return 0;
- #endif /* USE_IPV6 */
- default:
- LOG(L_CRIT, "BUG: dns_entry2he: wrong entry type %d for %.*s\n",
- e->type, e->name_len, e->name);
- return 0;
- }
- rr_no=0;
- now=get_ticks_raw();
- /* if the entry has already expired use the time at the end of lifetime */
- if (unlikely((s_ticks_t)(now-e->expire)>=0)) now=e->expire-1;
- rr=dns_entry_get_rr(e, &rr_no, now);
- for(i=0; rr && (i<DNS_HE_MAX_ADDR); i++,
- rr=dns_entry_get_rr(e, &rr_no, now)){
- p_addr[i]=&address[i*len];
- memcpy(p_addr[i], ((struct a_rdata*)rr->rdata)->ip, len);
- }
- if (i==0){
- DBG("DEBUG: dns_entry2he: no good records found (%d) for %.*s (%d)\n",
- rr_no, e->name_len, e->name, e->type);
- return 0; /* no good record found */
- }
- p_addr[i]=0; /* mark the end of the addresses */
- p_aliases[0]=0; /* no aliases */
- memcpy(hostname, e->name, e->name_len);
- hostname[e->name_len]=0;
- he.h_addrtype=af;
- he.h_length=len;
- he.h_addr_list=p_addr;
- he.h_aliases=p_aliases;
- he.h_name=hostname;
- return &he;
- }
- /* gethostbyname compatibility: performs an a_lookup and returns a pointer
- * to a statical internal hostent structure
- * returns 0 on success, <0 on error (see the error codes)
- */
- inline static struct hostent* dns_a_get_he(str* name)
- {
- struct dns_hash_entry* e;
- struct ip_addr* ip;
- struct hostent* he;
- e=0;
- #ifdef USE_IPV6
- if (str2ip6(name)!=0)
- return 0;
- #endif
- if ((ip=str2ip(name))!=0){
- return ip_addr2he(name, ip);
- }
- if ((e=dns_get_entry(name, T_A))==0)
- return 0;
- /* found */
- he=dns_entry2he(e);
- dns_hash_put(e);
- return he;
- }
- #ifdef USE_IPV6
- /* gethostbyname compatibility: performs an aaaa_lookup and returns a pointer
- * to a statical internal hostent structure
- * returns 0 on success, <0 on error (see the error codes)
- */
- inline static struct hostent* dns_aaaa_get_he(str* name)
- {
- struct dns_hash_entry* e;
- struct ip_addr* ip;
- struct hostent* he;
- e=0;
- if (str2ip(name)!=0)
- return 0;
- if ((ip=str2ip6(name))!=0){
- return ip_addr2he(name, ip);
- }
- if ((e=dns_get_entry(name, T_AAAA))==0)
- return 0;
- /* found */
- he=dns_entry2he(e);
- dns_hash_put(e);
- return he;
- }
- #endif
- /* returns 0 on success, -1 on error (rr type does not contain an ip) */
- inline static int dns_rr2ip(int type, struct dns_rr* rr, struct ip_addr* ip)
- {
- switch(type){
- case T_A:
- ip->af=AF_INET;
- ip->len=4;
- memcpy(ip->u.addr, ((struct a_rdata*)rr->rdata)->ip, 4);
- return 0;
- break;
- case T_AAAA:
- #ifdef USE_IPV6
- ip->af=AF_INET6;
- ip->len=16;
- memcpy(ip->u.addr, ((struct aaaa_rdata*)rr->rdata)->ip6, 16);
- return 0;
- #else /* USE_IPV6 */
- LOG(L_ERR, "ERROR: dns_rr2ip: IPv6 dns rr, but IPv6 support"
- "disabled at compile time (recompile with "
- "-DUSE_IPV6)\n" );
- #endif /*USE_IPV6 */
- break;
- }
- return -1;
- }
- /* gethostbyname compatibility:
- * performs an a or aaaa dns lookup, returns 0 on error and a pointer to a
- * static hostent structure on success
- * flags: - none set: tries first an a_lookup and if it fails an aaaa_lookup
- * - DNS_IPV6_FIRST: tries first an aaaa_lookup and then an a_lookup
- * - DNS_IPV4_ONLY: tries only an a_lookup
- * - DNS_IPV6_ONLY: tries only an aaaa_lookup
- */
- struct hostent* dns_get_he(str* name, int flags)
- {
- #ifdef USE_IPV6
- struct hostent* he;
- if ((flags&(DNS_IPV6_FIRST|DNS_IPV6_ONLY))){
- he=dns_aaaa_get_he(name);
- if (he) return he;
- }else{
- he=dns_a_get_he(name);
- if (he) return he;
- }
- if (flags&DNS_IPV6_FIRST){
- he=dns_a_get_he(name);
- }else if (!(flags&(DNS_IPV6_ONLY|DNS_IPV4_ONLY))){
- he=dns_aaaa_get_he(name);
- }
- return he;
- #else /* USE_IPV6 */
- return dns_a_get_he(name);
- #endif /* USE_IPV6 */
- }
- /* sip_resolvehost helper: gets the first good hostent/port combination
- * returns 0 on error, pointer to static hostent structure on success
- * (and sets port)*/
- struct hostent* dns_srv_get_he(str* name, unsigned short* port, int flags)
- {
- struct dns_hash_entry* e;
- struct dns_rr* rr;
- str rr_name;
- struct hostent* he;
- ticks_t now;
- unsigned char rr_no;
- rr=0;
- he=0;
- now=get_ticks_raw();
- if ((e=dns_get_entry(name, T_SRV))==0)
- goto error;
- /* look inside the RRs for a good one (not expired or marked bad) */
- rr_no=0;
- while( (rr=dns_entry_get_rr(e, &rr_no, now))!=0){
- /* everything is ok now, we can try to resolve the ip */
- rr_name.s=((struct srv_rdata*)rr->rdata)->name;
- rr_name.len=((struct srv_rdata*)rr->rdata)->name_len;
- if ((he=dns_get_he(&rr_name, flags))!=0){
- /* success, at least one good ip found */
- *port=((struct srv_rdata*)rr->rdata)->port;
- goto end;
- }
- rr_no++; /* try from the next record, the current one was not good */
- }
- /* if we reach this point => error, we couldn't find any good rr */
- end:
- if (e) dns_hash_put(e);
- error:
- return he;
- }
- struct hostent* dns_resolvehost(char* name)
- {
- str host;
- struct hostent* ret;
- if ((cfg_get(core, core_cfg, use_dns_cache)==0) || (dns_hash==0)){ /* not init yet */
- ret = _resolvehost(name);
- if(unlikely(!ret)){
- /* increment dns error counter */
- if(counters_initialized())
- counter_inc(dns_cnts_h.failed_dns_req);
- }
- return ret;
- }
- host.s=name;
- host.len=strlen(name);
- return dns_get_he(&host, dns_flags);
- }
- #if 0
- /* resolves a host name trying NAPTR, SRV, A & AAAA lookups, for details
- * see dns_sip_resolve()
- * FIXME: this version will return only the first ip
- * returns: hostent struct & *port filled with the port from the SRV record;
- * 0 on error
- */
- struct hostent* dns_sip_resolvehost(str* name, unsigned short* port,
- char* proto)
- {
- struct dns_srv_handle h;
- struct ip_addr ip;
- int ret;
- if ((cfg_get(core, core_cfg, use_dns_cache==0)) || (dns_hash==0)){
- /* not init or off => use normal, non-cached version */
- return _sip_resolvehost(name, port, proto);
- }
- dns_srv_handle_init(&h);
- ret=dns_sip_resolve(&h, name, &ip, port, proto, dns_flags);
- dns_srv_handle_put(&h);
- if (ret>=0)
- return ip_addr2he(name, &ip);
- return 0;
- }
- #endif
- /* resolves a host name trying SRV lookup if *port==0 or normal A/AAAA lookup
- * if *port!=0.
- * when performing SRV lookup (*port==0) it will use proto to look for
- * tcp or udp hosts, otherwise proto is unused; if proto==0 => no SRV lookup
- * returns: hostent struct & *port filled with the port from the SRV record;
- * 0 on error
- */
- struct hostent* dns_srv_sip_resolvehost(str* name, unsigned short* port,
- char* proto)
- {
- struct hostent* he;
- struct ip_addr* ip;
- static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
- str srv_name;
- char srv_proto;
- if ((cfg_get(core, core_cfg, use_dns_cache)==0) || (dns_hash==0)){
- /* not init or off => use normal, non-cached version */
- return _sip_resolvehost(name, port, proto);
- }
- if (proto){ /* makes sure we have a protocol set*/
- if (*proto==0)
- *proto=srv_proto=PROTO_UDP; /* default */
- else
- srv_proto=*proto;
- }else{
- srv_proto=PROTO_UDP;
- }
- /* try SRV if no port specified (draft-ietf-sip-srv-06) */
- if ((port)&&(*port==0)){
- *port=(srv_proto==PROTO_TLS)?SIPS_PORT:SIP_PORT; /* just in case we
- don't find another */
- if ((name->len+SRV_MAX_PREFIX_LEN+1)>MAX_DNS_NAME){
- LOG(L_WARN, "WARNING: dns_sip_resolvehost: domain name too long"
- " (%d), unable to perform SRV lookup\n", name->len);
- }else{
- /* check if it's an ip address */
- if ( ((ip=str2ip(name))!=0)
- #ifdef USE_IPV6
- || ((ip=str2ip6(name))!=0)
- #endif
- ){
- /* we are lucky, this is an ip address */
- return ip_addr2he(name,ip);
- }
- switch(srv_proto){
- case PROTO_NONE: /* no proto specified, use udp */
- if (proto)
- *proto=PROTO_UDP;
- /* no break */
- case PROTO_UDP:
- memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN);
- memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len);
- tmp[SRV_UDP_PREFIX_LEN + name->len] = '\0';
- break;
- case PROTO_TCP:
- memcpy(tmp, SRV_TCP_PREFIX, SRV_TCP_PREFIX_LEN);
- memcpy(tmp+SRV_TCP_PREFIX_LEN, name->s, name->len);
- tmp[SRV_TCP_PREFIX_LEN + name->len] = '\0';
- break;
- case PROTO_TLS:
- memcpy(tmp, SRV_TLS_PREFIX, SRV_TLS_PREFIX_LEN);
- memcpy(tmp+SRV_TLS_PREFIX_LEN, name->s, name->len);
- tmp[SRV_TLS_PREFIX_LEN + name->len] = '\0';
- break;
- case PROTO_SCTP:
- memcpy(tmp, SRV_SCTP_PREFIX, SRV_SCTP_PREFIX_LEN);
- memcpy(tmp+SRV_SCTP_PREFIX_LEN, name->s, name->len);
- tmp[SRV_SCTP_PREFIX_LEN + name->len] = '\0';
- break;
- default:
- LOG(L_CRIT, "BUG: sip_resolvehost: unknown proto %d\n",
- (int)srv_proto);
- return 0;
- }
- srv_name.s=tmp;
- srv_name.len=strlen(tmp);
- if ((he=dns_srv_get_he(&srv_name, port, dns_flags))!=0)
- return he;
- }
- }
- /*skip_srv:*/
- if (name->len >= MAX_DNS_NAME) {
- LOG(L_ERR, "dns_sip_resolvehost: domain name too long\n");
- return 0;
- }
- he=dns_get_he(name, dns_flags);
- return he;
- }
- #ifdef USE_NAPTR
- /* iterates over a naptr rr list, returning each time a "good" naptr record
- * is found.( srv type, no regex and a supported protocol)
- * params:
- * naptr_head - naptr dns_rr list head
- * tried - bitmap used to keep track of the already tried records
- * (no more then sizeof(tried)*8 valid records are
- * ever walked
- * srv_name - if succesfull, it will be set to the selected record
- * srv name (naptr repl.)
- * proto - if succesfull it will be set to the selected record
- * protocol
- * returns 0 if no more records found or a pointer to the selected record
- * and sets protocol and srv_name
- * WARNING: when calling first time make sure you run first
- * naptr_iterate_init(&tried)
- */
- struct naptr_rdata* dns_naptr_sip_iterate(struct dns_rr* naptr_head,
- naptr_bmp_t* tried,
- str* srv_name, char* proto)
- {
- int i, idx;
- struct dns_rr* l;
- struct naptr_rdata* naptr;
- struct naptr_rdata* naptr_saved;
- char saved_proto;
- char naptr_proto;
- idx=0;
- naptr_proto=PROTO_NONE;
- naptr_saved=0;
- saved_proto=0;
- i=0;
- for(l=naptr_head; l && (i<MAX_NAPTR_RRS); l=l->next){
- naptr=(struct naptr_rdata*) l->rdata;
- if (naptr==0){
- LOG(L_CRIT, "naptr_iterate: BUG: null rdata\n");
- goto end;
- }
- /* check if valid and get proto */
- if ((naptr_proto=naptr_get_sip_proto(naptr))<=0) continue;
- if (*tried& (1<<i)){
- i++;
- continue; /* already tried */
- }
- #ifdef DNS_CACHE_DEBUG
- DBG("naptr_iterate: found a valid sip NAPTR rr %.*s,"
- " proto %d\n", naptr->repl_len, naptr->repl,
- (int)naptr_proto);
- #endif
- if ((naptr_proto_supported(naptr_proto))){
- if (naptr_choose(&naptr_saved, &saved_proto,
- naptr, naptr_proto))
- idx=i;
- }
- i++;
- }
- if (naptr_saved){
- /* found something */
- #ifdef DNS_CACHE_DEBUG
- DBG("naptr_iterate: choosed NAPTR rr %.*s, proto %d"
- " tried: 0x%x\n", naptr_saved->repl_len,
- naptr_saved->repl, (int)saved_proto, *tried);
- #endif
- *tried|=1<<idx;
- *proto=saved_proto;
- srv_name->s=naptr_saved->repl;
- srv_name->len=naptr_saved->repl_len;
- return naptr_saved;
- }
- end:
- return 0;
- }
- /* resolves a host name trying NAPTR lookup if *proto==0 and *port==0, SRV
- * lookup if *port==0 or normal A/AAAA lookup
- * if *port!=0.
- * when performing SRV lookup (*port==0) it will use proto to look for
- * tcp or udp hosts; if proto==0 => no SRV lookup
- * returns: hostent struct & *port filled with the port from the SRV record;
- * 0 on error
- */
- struct hostent* dns_naptr_sip_resolvehost(str* name, unsigned short* port,
- char* proto)
- {
- struct hostent* he;
- struct ip_addr* tmp_ip;
- naptr_bmp_t tried_bmp;
- struct dns_hash_entry* e;
- char n_proto;
- char origproto;
- str srv_name;
- origproto=*proto;
- he=0;
- if (dns_hash==0){ /* not init => use normal, non-cached version */
- LOG(L_WARN, "WARNING: dns_sip_resolvehost: called before dns cache"
- " initialization\n");
- return _sip_resolvehost(name, port, proto);
- }
- if (proto && port && (*proto==0) && (*port==0)){
- *proto=PROTO_UDP; /* just in case we don't find another */
- /* check if it's an ip address */
- if ( ((tmp_ip=str2ip(name))!=0)
- #ifdef USE_IPV6
- || ((tmp_ip=str2ip6(name))!=0)
- #endif
- ){
- /* we are lucky, this is an ip address */
- #ifdef USE_IPV6
- if (((dns_flags&DNS_IPV4_ONLY) && (tmp_ip->af==AF_INET6))||
- ((dns_flags&DNS_IPV6_ONLY) && (tmp_ip->af==AF_INET))){
- return 0;
- }
- #endif
- *port=SIP_PORT;
- return ip_addr2he(name, tmp_ip);
- }
- /* do naptr lookup */
- if ((e=dns_get_entry(name, T_NAPTR))==0)
- goto naptr_not_found;
- naptr_iterate_init(&tried_bmp);
- while(dns_naptr_sip_iterate(e->rr_lst, &tried_bmp,
- &srv_name, &n_proto)){
- if ((he=dns_srv_get_he(&srv_name, port, dns_flags))!=0){
- #ifdef DNS_CACHE_DEBUG
- DBG("dns_naptr_sip_resolvehost(%.*s, %d, %d) srv, ret=%p\n",
- name->len, name->s, (int)*port, (int)*proto, he);
- #endif
- dns_hash_put(e);
- *proto=n_proto;
- return he;
- }
- }
- /* no acceptable naptr record found, fallback to srv */
- dns_hash_put(e);
- }
- naptr_not_found:
- *proto = origproto;
- he = no_naptr_srv_sip_resolvehost(name,port,proto);
- /* fallback all the way down to A/AAAA */
- if (he==0) {
- he=dns_get_he(name,dns_flags);
- }
- return he;
- }
- #endif /* USE_NAPTR */
- /* resolves a host name trying NAPTR lookup if *proto==0 and *port==0, SRV
- * lookup if *port==0 or normal A/AAAA lookup
- * if *port!=0.
- * when performing SRV lookup (*port==0) it will use proto to look for
- * tcp or udp hosts; if proto==0 => no SRV lookup
- * returns: hostent struct & *port filled with the port from the SRV record;
- * 0 on error
- */
- struct hostent* dns_sip_resolvehost(str* name, unsigned short* port,
- char* proto)
- {
- #ifdef USE_NAPTR
- if (dns_flags&DNS_TRY_NAPTR)
- return dns_naptr_sip_resolvehost(name, port, proto);
- #endif
- return dns_srv_sip_resolvehost(name, port, proto);
- }
- /* performs an a lookup, fills the dns_entry pointer and the ip addr.
- * (with the first good ip). if *e ==0 does the a lookup, and changes it
- * to the result, if not it uses the current value and tries to use
- * the rr_no record from it.
- * params: e - must contain the "in-use" dns_hash_entry pointer (from
- * a previous call) or *e==0 (for the first call)
- * name - host name for which we do the lookup (required only
- * when *e==0)
- * ip - will be filled with the first good resolved ip started
- * at *rr_no
- * rr_no - record number to start searching for a good ip from
- * (e.g. value from previous call + 1), filled on return
- * with the number of the record corresponding to the
- * returned ip
- * returns 0 on success, <0 on error (see the error codes),
- * fills e, ip and rr_no
- * On end of records (when used to iterate on all the ips) it
- * will return E_DNS_EOR (you should not log an error for this
- * value, is just a signal that the address list end has been reached)
- * Note: either e or name must be different from 0 (name.s !=0 also)
- * WARNING: dns_hash_put(*e) must be called when you don't need
- * the entry anymore and *e!=0 (failling to do so => mem. leak)
- * Example:
- * dns_entry=0;
- * ret=dns_a_get_ip(&dns_entry, name, &ip, &rr_no); -- get the first rr.
- * ...
- * rr_no++;
- * while((ret>=0) && dns_entry)
- * dns_a_get_ip(&dns_entry, name, &ip, &rr_no); -- get the next rr
- * if (ret!=-E_DNS_EOR) ERROR(....);
- * ...
- * dns_hash_put(dns_entry); -- finished with the entry
- */
- inline static int dns_a_resolve( struct dns_hash_entry** e,
- unsigned char* rr_no,
- str* name,
- struct ip_addr* ip)
- {
- struct dns_rr* rr;
- int ret;
- ticks_t now;
- struct ip_addr* tmp;
- rr=0;
- ret=-E_DNS_NO_IP;
- if (*e==0){ /* do lookup */
- /* if ip don't set *e */
- #ifdef USE_IPV6
- if (str2ip6(name)!=0)
- goto error;
- #endif
- if ((tmp=str2ip(name))!=0){
- *ip=*tmp;
- *rr_no=0;
- return 0;
- }
- if ((*e=dns_get_entry(name, T_A))==0)
- goto error;
- /* found */
- *rr_no=0;
- ret=-E_DNS_BAD_IP_ENTRY;
- }
- now=get_ticks_raw();
- /* if the entry has already expired use the time at the end of lifetime */
- if (unlikely((s_ticks_t)(now-(*e)->expire)>=0)) now=(*e)->expire-1;
- rr=dns_entry_get_rr(*e, rr_no, now);
- if (rr){
- /* everything is ok now, we can try to "convert" the ip */
- dns_rr2ip((*e)->type, rr, ip);
- ret=0;
- }else{
- ret=-E_DNS_EOR;
- }
- error:
- DBG("dns_a_resolve(%.*s, %d) returning %d\n",
- name->len, name->s, *rr_no, ret);
- return ret;
- }
- #ifdef USE_IPV6
- /* lookup, fills the dns_entry pointer and the ip addr.
- * (with the first good ip). if *e ==0 does the a lookup, and changes it
- * to the result, if not it uses the current value and tries to use
- * Same as dns_a_resolve but for aaaa records (see above).
- */
- inline static int dns_aaaa_resolve( struct dns_hash_entry** e,
- unsigned char* rr_no,
- str* name,
- struct ip_addr* ip)
- {
- struct dns_rr* rr;
- int ret;
- ticks_t now;
- struct ip_addr* tmp;
- rr=0;
- ret=-E_DNS_NO_IP;
- if (*e==0){ /* do lookup */
- /* if ip don't set *e */
- if (str2ip(name)!=0)
- goto error;
- if ((tmp=str2ip6(name))!=0){
- *ip=*tmp;
- *rr_no=0;
- return 0;
- }
- if ((*e=dns_get_entry(name, T_AAAA))==0)
- goto error;
- /* found */
- *rr_no=0;
- ret=-E_DNS_BAD_IP_ENTRY;
- }
- now=get_ticks_raw();
- /* if the entry has already expired use the time at the end of lifetime */
- if (unlikely((s_ticks_t)(now-(*e)->expire)>=0)) now=(*e)->expire-1;
- rr=dns_entry_get_rr(*e, rr_no, now);
- if (rr){
- /* everything is ok now, we can try to "convert" the ip */
- dns_rr2ip((*e)->type, rr, ip);
- ret=0;
- }else{
- ret=-E_DNS_EOR; /* no more records */
- }
- error:
- return ret;
- }
- #endif /* USE_IPV6 */
- /* performs an a or aaaa dns lookup, returns <0 on error (see the
- * dns error codes) and 0 on success
- * flags: - none set: tries first an a_lookup and if it fails an aaaa_lookup
- * - DNS_IPV6_FIRST: tries first an aaaa_lookup and then an a_lookup
- * - DNS_IPV4_ONLY: tries only an a_lookup
- * - DNS_IPV6_ONLY: tries only an aaaa_lookup
- * see dns_a_resolve() for the rest of the params., examples a.s.o
- * WARNING: don't forget dns_hash_put(*e) when e is not needed anymore
- */
- inline static int dns_ip_resolve( struct dns_hash_entry** e,
- unsigned char* rr_no,
- str* name,
- struct ip_addr* ip,
- int flags)
- {
- int ret;
- str host;
- struct dns_hash_entry* orig;
- ret=-E_DNS_NO_IP;
- if (*e==0){ /* first call */
- #ifdef USE_IPV6
- if ((flags&(DNS_IPV6_FIRST|DNS_IPV6_ONLY))){
- ret=dns_aaaa_resolve(e, rr_no, name, ip);
- if (ret>=0) return ret;
- }else{
- ret=dns_a_resolve(e, rr_no, name, ip);
- if (ret>=0) return ret;
- }
- if (flags&DNS_IPV6_FIRST){
- ret=dns_a_resolve(e, rr_no, name, ip);
- }else if (!(flags&(DNS_IPV6_ONLY|DNS_IPV4_ONLY))){
- ret=dns_aaaa_resolve(e, rr_no, name, ip);
- }
- #else /* USE_IPV6 */
- ret=dns_a_resolve(e, rr_no, name, ip);
- #endif /* USE_IPV6 */
- }else if ((*e)->type==T_A){
- /* continue A resolving */
- /* retrieve host name from the hash entry (ignore name which might
- be null when continuing a srv lookup) */
- host.s=(*e)->name;
- host.len=(*e)->name_len;
- ret=dns_a_resolve(e, rr_no, &host, ip);
- #ifdef USE_IPV6
- if (ret>=0) return ret;
- if (!(flags&(DNS_IPV6_ONLY|DNS_IPV6_FIRST|DNS_IPV4_ONLY))){
- /* not found, try with AAAA */
- orig=*e;
- *e=0;
- *rr_no=0;
- ret=dns_aaaa_resolve(e, rr_no, &host, ip);
- /* delay original record release until we're finished with host*/
- dns_hash_put(orig);
- }
- #endif /* USE_IPV6 */
- }else if ((*e)->type==T_AAAA){
- /* retrieve host name from the hash entry (ignore name which might
- be null when continuing a srv lookup) */
- host.s=(*e)->name;
- host.len=(*e)->name_len;
- #ifdef USE_IPV6
- /* continue AAAA resolving */
- ret=dns_aaaa_resolve(e, rr_no, &host, ip);
- if (ret>=0) return ret;
- if ((flags&DNS_IPV6_FIRST) && !(flags&DNS_IPV6_ONLY)){
- /* not found, try with A */
- orig=*e;
- *e=0;
- *rr_no=0;
- ret=dns_a_resolve(e, rr_no, &host, ip);
- /* delay original record release until we're finished with host*/
- dns_hash_put(orig);
- }
- #else /* USE_IPV6 */
- /* ipv6 disabled, try with A */
- orig=*e;
- *e=0;
- *rr_no=0;
- ret=dns_a_resolve(e, rr_no, &host, ip);
- /* delay original record release until we're finished with host*/
- dns_hash_put(orig);
- #endif /* USE_IPV6 */
- }else{
- LOG(L_CRIT, "BUG: dns_ip_resolve: invalid record type %d\n",
- (*e)->type);
- }
- return ret;
- }
- /* gets the first srv record starting at rr_no
- * Next call will return the next record a.s.o.
- * (similar to dns_a_resolve but for srv, sets host, port and automatically
- * switches to the next record in the future)
- *
- * if DNS_SRV_LB and tried!=NULL will do random weight based selection
- * for choosing between SRV RRs with the same priority (as described in
- * RFC2782).
- * If tried==NULL or DNS_SRV_LB is not defined => always returns next
- * record in the priority order and for records with the same priority
- * the record with the higher weight (from the remaining ones)
- */
- inline static int dns_srv_resolve_nxt(struct dns_hash_entry** e,
- #ifdef DNS_SRV_LB
- srv_flags_t* tried,
- #endif
- unsigned char* rr_no,
- str* name, str* host, unsigned short* port)
- {
- struct dns_rr* rr;
- int ret;
- ticks_t now;
- rr=0;
- ret=-E_DNS_NO_SRV;
- if (*e==0){
- if ((*e=dns_get_entry(name, T_SRV))==0)
- goto error;
- /* found it */
- *rr_no=0;
- #ifdef DNS_SRV_LB
- if (tried)
- srv_reset_tried(tried);
- #endif
- ret=-E_DNS_BAD_SRV_ENTRY;
- }
- now=get_ticks_raw();
- /* if the entry has already expired use the time at the end of lifetime */
- if (unlikely((s_ticks_t)(now-(*e)->expire)>=0)) now=(*e)->expire-1;
- #ifdef DNS_SRV_LB
- if (tried){
- rr=dns_srv_get_nxt_rr(*e, tried, rr_no, now);
- }else
- #endif
- {
- rr=dns_entry_get_rr(*e, rr_no, now);
- (*rr_no)++; /* try next record next time */
- }
- if (rr){
- host->s=((struct srv_rdata*)rr->rdata)->name;
- host->len=((struct srv_rdata*)rr->rdata)->name_len;
- *port=((struct srv_rdata*)rr->rdata)->port;
- ret=0;
- }else{
- ret=-E_DNS_EOR; /* no more records */
- }
- error:
- return ret;
- }
- /* gets the first srv record starting at h->srv_no, resolve it
- * and get the first ip address (starting at h->ip_no)
- * (similar to dns_a_resolve but for srv, sets host, port)
- * WARNING: don't forget to init h prior to calling this function the first
- * time and dns_srv_handle_put(h), even if error is returned
- */
- inline static int dns_srv_resolve_ip(struct dns_srv_handle* h,
- str* name, struct ip_addr* ip, unsigned short* port,
- int flags)
- {
- int ret;
- str host;
- host.len=0;
- host.s=0;
- do{
- if (h->a==0){
- #ifdef DNS_SRV_LB
- if ((ret=dns_srv_resolve_nxt(&h->srv,
- (flags & DNS_SRV_RR_LB)?&h->srv_tried_rrs:0,
- &h->srv_no,
- name, &host, port))<0)
- goto error;
- #else
- if ((ret=dns_srv_resolve_nxt(&h->srv, &h->srv_no,
- name, &host, port))<0)
- goto error;
- #endif
- h->port=*port; /* store new port */
- }else{
- *port=h->port; /* return the stored port */
- }
- if ((ret=dns_ip_resolve(&h->a, &h->ip_no, &host, ip, flags))<0){
- /* couldn't find any good ip for this record, try the next one */
- if (h->a){
- dns_hash_put(h->a);
- h->a=0;
- }
- }else if (h->a==0){
- /* this was an ip, try the next srv record in the future */
- }
- }while(ret<0);
- error:
- #ifdef DNS_CACHE_DEBUG
- DBG("dns_srv_resolve_ip(\"%.*s\", %d, %d), ret=%d, ip=%s\n",
- name->len, name->s, h->srv_no, h->ip_no, ret,
- ip?ZSW(ip_addr2a(ip)):"");
- #endif
- return ret;
- }
- /* resolves a host name trying SRV lookup if *port==0 or normal A/AAAA lookup
- * if *port!=0.
- * when performing SRV lookup (*port==0) it will use proto to look for
- * tcp or udp hosts, otherwise proto is unused; if proto==0 => no SRV lookup
- * h must be initialized prior to calling this function and can be used to
- * get the subsequent ips
- * returns: <0 on error
- * 0 on success and it fills *ip, *port, dns_sip_resolve_h
- * WARNING: when finished, dns_sip_resolve_put(h) must be called!
- */
- inline static int dns_srv_sip_resolve(struct dns_srv_handle* h, str* name,
- struct ip_addr* ip, unsigned short* port, char* proto,
- int flags)
- {
- static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
- int len;
- str srv_name;
- struct ip_addr* tmp_ip;
- int ret;
- struct hostent* he;
- char srv_proto;
- if (dns_hash==0){ /* not init => use normal, non-cached version */
- LOG(L_WARN, "WARNING: dns_sip_resolve: called before dns cache"
- " initialization\n");
- h->srv=h->a=0;
- he=_sip_resolvehost(name, port, proto);
- if (he){
- hostent2ip_addr(ip, he, 0);
- return 0;
- }
- return -E_DNS_NO_SRV;
- }
- len=0;
- if ((h->srv==0) && (h->a==0)){ /* first call */
- if (proto){ /* makes sure we have a protocol set*/
- if (*proto==0)
- *proto=srv_proto=PROTO_UDP; /* default */
- else
- srv_proto=*proto;
- }else{
- srv_proto=PROTO_UDP;
- }
- h->port=(srv_proto==PROTO_TLS)?SIPS_PORT:SIP_PORT; /* just in case we
- don't find another */
- h->proto=srv_proto; /* store initial protocol */
- if (port){
- if (*port==0){
- /* try SRV if initial call & no port specified
- * (draft-ietf-sip-srv-06) */
- if ((name->len+SRV_MAX_PREFIX_LEN+1)>MAX_DNS_NAME){
- LOG(L_WARN, "WARNING: dns_sip_resolvehost: domain name too"
- " long (%d), unable to perform SRV lookup\n",
- name->len);
- }else{
- /* check if it's an ip address */
- if ( ((tmp_ip=str2ip(name))!=0)
- #ifdef USE_IPV6
- || ((tmp_ip=str2ip6(name))!=0)
- #endif
- ){
- /* we are lucky, this is an ip address */
- #ifdef USE_IPV6
- if (((flags&DNS_IPV4_ONLY) && (tmp_ip->af==AF_INET6))||
- ((flags&DNS_IPV6_ONLY) && (tmp_ip->af==AF_INET))){
- return -E_DNS_AF_MISMATCH;
- }
- #endif
- *ip=*tmp_ip;
- *port=h->port;
- /* proto already set */
- return 0;
- }
- switch(srv_proto){
- case PROTO_NONE: /* no proto specified, use udp */
- if (proto)
- *proto=PROTO_UDP;
- /* no break */
- case PROTO_UDP:
- memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN);
- memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len);
- tmp[SRV_UDP_PREFIX_LEN + name->len] = '\0';
- len=SRV_UDP_PREFIX_LEN + name->len;
- break;
- case PROTO_TCP:
- memcpy(tmp, SRV_TCP_PREFIX, SRV_TCP_PREFIX_LEN);
- memcpy(tmp+SRV_TCP_PREFIX_LEN, name->s, name->len);
- tmp[SRV_TCP_PREFIX_LEN + name->len] = '\0';
- len=SRV_TCP_PREFIX_LEN + name->len;
- break;
- case PROTO_TLS:
- memcpy(tmp, SRV_TLS_PREFIX, SRV_TLS_PREFIX_LEN);
- memcpy(tmp+SRV_TLS_PREFIX_LEN, name->s, name->len);
- tmp[SRV_TLS_PREFIX_LEN + name->len] = '\0';
- len=SRV_TLS_PREFIX_LEN + name->len;
- break;
- case PROTO_SCTP:
- memcpy(tmp, SRV_SCTP_PREFIX, SRV_SCTP_PREFIX_LEN);
- memcpy(tmp+SRV_SCTP_PREFIX_LEN, name->s, name->len);
- tmp[SRV_SCTP_PREFIX_LEN + name->len] = '\0';
- len=SRV_SCTP_PREFIX_LEN + name->len;
- break;
- default:
- LOG(L_CRIT, "BUG: sip_resolvehost: "
- "unknown proto %d\n", (int)srv_proto);
- return -E_DNS_CRITICAL;
- }
- srv_name.s=tmp;
- srv_name.len=len;
- if ((ret=dns_srv_resolve_ip(h, &srv_name, ip,
- port, flags))>=0)
- {
- #ifdef DNS_CACHE_DEBUG
- DBG("dns_sip_resolve(%.*s, %d, %d), srv0, ret=%d\n",
- name->len, name->s, h->srv_no, h->ip_no, ret);
- #endif
- /* proto already set */
- return ret;
- }
- }
- }else{ /* if (*port==0) */
- h->port=*port; /* store initial port */
- /* proto already set */
- }
- } /* if (port) */
- }else if (h->srv){
- srv_name.s=h->srv->name;
- srv_name.len=h->srv->name_len;
- /* continue srv resolving */
- ret=dns_srv_resolve_ip(h, &srv_name, ip, port, flags);
- if (proto)
- *proto=h->proto;
- DBG("dns_sip_resolve(%.*s, %d, %d), srv, ret=%d\n",
- name->len, name->s, h->srv_no, h->ip_no, ret);
- return ret;
- }
- /*skip_srv:*/
- if (name->len >= MAX_DNS_NAME) {
- LOG(L_ERR, "dns_sip_resolve: domain name too long\n");
- return -E_DNS_NAME_TOO_LONG;
- }
- ret=dns_ip_resolve(&h->a, &h->ip_no, name, ip, flags);
- if (port)
- *port=h->port;
- if (proto)
- *proto=h->proto;
- #ifdef DNS_CACHE_DEBUG
- DBG("dns_sip_resolve(%.*s, %d, %d), ip, ret=%d\n",
- name->len, name->s, h->srv_no, h->ip_no, ret);
- #endif
- return ret;
- }
- #ifdef USE_NAPTR
- /* resolves a host name trying:
- * - NAPTR lookup if the address is not an ip and proto!=0, port!=0
- * *port==0 and *proto=0 and if flags allow NAPTR lookups
- * -SRV lookup if port!=0 and *port==0
- * - normal A/AAAA lookup if *port!=0, or port==0
- * when performing SRV lookup (*port==0) it will use proto to look for
- * tcp or udp hosts, otherwise proto is unused; if proto==0 => no SRV lookup
- * h must be initialized prior to calling this function and can be used to
- * get the subsequent ips
- * returns: <0 on error
- * 0 on success and it fills *ip, *port, dns_sip_resolve_h
- * WARNING: when finished, dns_sip_resolve_put(h) must be called!
- */
- inline static int dns_naptr_sip_resolve(struct dns_srv_handle* h, str* name,
- struct ip_addr* ip, unsigned short* port, char* proto,
- int flags)
- {
- struct hostent* he;
- struct ip_addr* tmp_ip;
- naptr_bmp_t tried_bmp;
- struct dns_hash_entry* e;
- char n_proto;
- str srv_name;
- int ret;
- ret=-E_DNS_NO_NAPTR;
- if (dns_hash==0){ /* not init => use normal, non-cached version */
- LOG(L_WARN, "WARNING: dns_sip_resolve: called before dns cache"
- " initialization\n");
- h->srv=h->a=0;
- he=_sip_resolvehost(name, port, proto);
- if (he){
- hostent2ip_addr(ip, he, 0);
- return 0;
- }
- return -E_DNS_NO_NAPTR;
- }
- if (((h->srv==0) && (h->a==0)) && /* first call */
- proto && port && (*proto==0) && (*port==0)){
- *proto=PROTO_UDP; /* just in case we don't find another */
- /* check if it's an ip address */
- if ( ((tmp_ip=str2ip(name))!=0)
- #ifdef USE_IPV6
- || ((tmp_ip=str2ip6(name))!=0)
- #endif
- ){
- /* we are lucky, this is an ip address */
- #ifdef USE_IPV6
- if (((flags&DNS_IPV4_ONLY) && (tmp_ip->af==AF_INET6))||
- ((flags&DNS_IPV6_ONLY) && (tmp_ip->af==AF_INET))){
- return -E_DNS_AF_MISMATCH;
- }
- #endif
- *ip=*tmp_ip;
- h->port=SIP_PORT;
- h->proto=*proto;
- *port=h->port;
- return 0;
- }
- /* do naptr lookup */
- if ((e=dns_get_entry(name, T_NAPTR))==0)
- goto naptr_not_found;
- naptr_iterate_init(&tried_bmp);
- while(dns_naptr_sip_iterate(e->rr_lst, &tried_bmp,
- &srv_name, &n_proto)){
- dns_srv_handle_init(h); /* make sure h does not contain garbage
- from previous dns_srv_sip_resolve calls */
- if ((ret=dns_srv_resolve_ip(h, &srv_name, ip, port, flags))>=0){
- #ifdef DNS_CACHE_DEBUG
- DBG("dns_naptr_sip_resolve(%.*s, %d, %d), srv0, ret=%d\n",
- name->len, name->s, h->srv_no, h->ip_no, ret);
- #endif
- dns_hash_put(e);
- *proto=n_proto;
- h->proto=*proto;
- return ret;
- }
- }
- /* no acceptable naptr record found, fallback to srv */
- dns_hash_put(e);
- dns_srv_handle_init(h); /* make sure h does not contain garbage
- from previous dns_srv_sip_resolve calls */
- }
- naptr_not_found:
- return dns_srv_sip_resolve(h, name, ip, port, proto, flags);
- }
- #endif /* USE_NAPTR */
- /* resolves a host name trying:
- * - NAPTR lookup if the address is not an ip and proto!=0, port!=0
- * *port==0 and *proto=0 and if flags allow NAPTR lookups
- * -SRV lookup if port!=0 and *port==0
- * - normal A/AAAA lookup if *port!=0, or port==0
- * when performing SRV lookup (*port==0) it will use proto to look for
- * tcp or udp hosts, otherwise proto is unused; if proto==0 => no SRV lookup
- * h must be initialized prior to calling this function and can be used to
- * get the subsequent ips
- * returns: <0 on error
- * 0 on success and it fills *ip, *port, dns_sip_resolve_h
- * WARNING: when finished, dns_sip_resolve_put(h) must be called!
- */
- int dns_sip_resolve(struct dns_srv_handle* h, str* name,
- struct ip_addr* ip, unsigned short* port, char* proto,
- int flags)
- {
- #ifdef USE_NAPTR
- if (flags&DNS_TRY_NAPTR)
- return dns_naptr_sip_resolve(h, name, ip, port, proto, flags);
- #endif
- return dns_srv_sip_resolve(h, name, ip, port, proto, flags);
- }
- /* performs an a lookup and fills ip with the first good ip address
- * returns 0 on success, <0 on error (see the error codes)
- */
- inline static int dns_a_get_ip(str* name, struct ip_addr* ip)
- {
- struct dns_hash_entry* e;
- int ret;
- unsigned char rr_no;
- e=0;
- rr_no=0;
- ret=dns_a_resolve(&e, &rr_no, name, ip);
- if (e) dns_hash_put(e);
- return ret;
- }
- #ifdef USE_IPV6
- inline static int dns_aaaa_get_ip(str* name, struct ip_addr* ip)
- {
- struct dns_hash_entry* e;
- int ret;
- unsigned char rr_no;
- e=0;
- rr_no=0;
- ret=dns_aaaa_resolve(&e, &rr_no, name, ip);
- if (e) dns_hash_put(e);
- return ret;
- }
- #endif /* USE_IPV6 */
- /* performs an a or aaaa dns lookup, returns <0 on error (see the
- * dns error codes) and 0 on success
- * flags: - none set: tries first an a_lookup and if it fails an aaaa_lookup
- * - DNS_IPV6_FIRST: tries first an aaaa_lookup and then an a_lookup
- * - DNS_IPV4_ONLY: tries only an a_lookup
- * - DNS_IPV6_ONLY: tries only an aaaa_lookup
- */
- int dns_get_ip(str* name, struct ip_addr* ip, int flags)
- {
- int ret;
- struct dns_hash_entry* e;
- unsigned char rr_no;
- e=0;
- rr_no=0;
- ret=dns_ip_resolve(&e, &rr_no, name, ip, flags);
- if (e)
- dns_hash_put(e);
- return ret;
- }
- /* fast "inline" version, gets the first good ip:port */
- int dns_srv_get_ip(str* name, struct ip_addr* ip, unsigned short* port,
- int flags)
- {
- int ret;
- struct dns_srv_handle h;
- dns_srv_handle_init(&h);
- ret=dns_srv_resolve_ip(&h, name, ip, port, flags);
- dns_srv_handle_put(&h);
- return ret;
- }
- #ifdef DNS_WATCHDOG_SUPPORT
- /* sets the state of the DNS servers:
- * 1: at least one server is up
- * 0: all the servers are down
- */
- void dns_set_server_state(int state)
- {
- atomic_set(dns_servers_up, state);
- }
- /* returns the state of the DNS servers */
- int dns_get_server_state(void)
- {
- return atomic_get(dns_servers_up);
- }
- #endif /* DNS_WATCHDOG_SUPPORT */
- /* rpc functions */
- void dns_cache_mem_info(rpc_t* rpc, void* ctx)
- {
- if (!cfg_get(core, core_cfg, use_dns_cache)){
- rpc->fault(ctx, 500, "dns cache support disabled (see use_dns_cache)");
- return;
- }
- rpc->add(ctx, "dd", *dns_cache_mem_used, cfg_get(core, core_cfg, dns_cache_max_mem));
- }
- void dns_cache_debug(rpc_t* rpc, void* ctx)
- {
- int h;
- struct dns_hash_entry* e;
- ticks_t now;
- if (!cfg_get(core, core_cfg, use_dns_cache)){
- rpc->fault(ctx, 500, "dns cache support disabled (see use_dns_cache)");
- return;
- }
- now=get_ticks_raw();
- LOCK_DNS_HASH();
- for (h=0; h<DNS_HASH_SIZE; h++){
- clist_foreach(&dns_hash[h], e, next){
- rpc->add(ctx, "sdddddd",
- e->name, e->type, e->total_size, e->refcnt.val,
- (s_ticks_t)(e->expire-now)<0?-1:
- TICKS_TO_S(e->expire-now),
- TICKS_TO_S(now-e->last_used),
- e->ent_flags);
- }
- }
- UNLOCK_DNS_HASH();
- }
- #ifdef USE_DNS_CACHE_STATS
- static unsigned long stat_sum(int ivar, int breset)
- {
- unsigned long isum=0;
- int i1=0;
- for (; i1 < get_max_procs(); i1++)
- switch (ivar) {
- case 0:
- isum+=dns_cache_stats[i1].dns_req_cnt;
- if (breset)
- dns_cache_stats[i1].dns_req_cnt=0;
- break;
- case 1:
- isum+=dns_cache_stats[i1].dc_hits_cnt;
- if (breset)
- dns_cache_stats[i1].dc_hits_cnt=0;
- break;
- case 2:
- isum+=dns_cache_stats[i1].dc_neg_hits_cnt;
- if (breset)
- dns_cache_stats[i1].dc_neg_hits_cnt=0;
- break;
- case 3:
- isum+=dns_cache_stats[i1].dc_lru_cnt;
- if (breset)
- dns_cache_stats[i1].dc_lru_cnt=0;
- break;
- }
- return isum;
- }
- void dns_cache_stats_get(rpc_t* rpc, void* c)
- {
- char *name=NULL;
- void *handle;
- int found=0,i=0;
- int reset=0;
- char* dns_cache_stats_names[] = {
- "dns_req_cnt",
- "dc_hits_cnt",
- "dc_neg_hits_cnt",
- "dc_lru_cnt",
- NULL
- };
- if (!cfg_get(core, core_cfg, use_dns_cache)) {
- rpc->fault(c, 500, "dns cache support disabled");
- return;
- }
- if (rpc->scan(c, "s", &name) < 0)
- return;
- if (rpc->scan(c, "d", &reset) < 0)
- return;
- if (!strcasecmp(name, DNS_CACHE_ALL_STATS)) {
- /* dump all the dns cache stat values */
- rpc->add(c, "{", &handle);
- for (i=0; dns_cache_stats_names[i]; i++)
- rpc->struct_add(handle, "d",
- dns_cache_stats_names[i],
- stat_sum(i, reset));
- found=1;
- } else {
- for (i=0; dns_cache_stats_names[i]; i++)
- if (!strcasecmp(dns_cache_stats_names[i], name)) {
- rpc->add(c, "{", &handle);
- rpc->struct_add(handle, "d",
- dns_cache_stats_names[i],
- stat_sum(i, reset));
- found=1;
- break;
- }
- }
- if(!found)
- rpc->fault(c, 500, "unknown dns cache stat parameter");
- return;
- }
- #endif /* USE_DNS_CACHE_STATS */
- /* rpc functions */
- void dns_cache_debug_all(rpc_t* rpc, void* ctx)
- {
- int h;
- struct dns_hash_entry* e;
- struct dns_rr* rr;
- struct ip_addr ip;
- int i;
- ticks_t now;
- if (!cfg_get(core, core_cfg, use_dns_cache)){
- rpc->fault(ctx, 500, "dns cache support disabled (see use_dns_cache)");
- return;
- }
- now=get_ticks_raw();
- LOCK_DNS_HASH();
- for (h=0; h<DNS_HASH_SIZE; h++){
- clist_foreach(&dns_hash[h], e, next){
- for (i=0, rr=e->rr_lst; rr; i++, rr=rr->next){
- rpc->add(ctx, "sddddddd",
- e->name, (int)e->type, i, (int)e->total_size,
- (int)e->refcnt.val,
- (int)(s_ticks_t)(e->expire-now)<0?-1:
- TICKS_TO_S(e->expire-now),
- (int)TICKS_TO_S(now-e->last_used),
- (int)e->ent_flags);
- switch(e->type){
- case T_A:
- case T_AAAA:
- if (dns_rr2ip(e->type, rr, &ip)==0){
- rpc->add(ctx, "ss", "ip", ip_addr2a(&ip) );
- }else{
- rpc->add(ctx, "ss", "ip", "<error: bad rr>");
- }
- break;
- case T_SRV:
- rpc->add(ctx, "ss", "srv",
- ((struct srv_rdata*)(rr->rdata))->name);
- break;
- case T_NAPTR:
- rpc->add(ctx, "ss", "naptr ",
- ((struct naptr_rdata*)(rr->rdata))->flags);
- break;
- case T_CNAME:
- rpc->add(ctx, "ss", "cname",
- ((struct cname_rdata*)(rr->rdata))->name);
- break;
- case T_TXT:
- rpc->add(ctx, "ss", "txt",
- ((struct txt_rdata*)(rr->rdata))->cstr_no?
- ((struct txt_rdata*)(rr->rdata))->txt[0].cstr:
- "");
- break;
- case T_EBL:
- rpc->add(ctx, "ss", "ebl",
- ((struct ebl_rdata*)(rr->rdata))->apex);
- break;
- case T_PTR:
- rpc->add(ctx, "ss", "ptr",
- ((struct ptr_rdata*)(rr->rdata))->ptrdname);
- break;
- default:
- rpc->add(ctx, "ss", "unknown", "?");
- }
- rpc->add(ctx, "d",
- (int)(s_ticks_t)(rr->expire-now)<0?-1:
- TICKS_TO_S(rr->expire-now));
- }
- }
- }
- UNLOCK_DNS_HASH();
- }
- static char *print_type(unsigned short type)
- {
- switch (type) {
- case T_A:
- return "A";
- case T_AAAA:
- return "AAAA";
- case T_SRV:
- return "SRV";
- case T_NAPTR:
- return "NAPTR";
- case T_CNAME:
- return "CNAME";
- case T_TXT:
- return "TXT";
- case T_EBL:
- return "EBL";
- case T_PTR:
- return "PTR";
- default:
- return "unknown";
- }
- }
- /** convert string type to dns integer T_*.
- * used for rpc type translation.
- * @return T_* on success, -1 on error.
- */
- static int dns_get_type(str* s)
- {
- char *t;
- int len;
-
- t=s->s;
- len=s->len;
- /* skip over a T_ or t_ prefix */
- if ((len>2) && (t[0]=='T' || t[0]=='t') && (t[1]=='_')){
- t+=2;
- len-=2;
- }
- switch(len){
- case 1:
- if (t[0]=='A' || t[0]=='a')
- return T_A;
- break;
- case 4:
- if (strncasecmp(t, "AAAA", len)==0)
- return T_AAAA;
- break;
- case 3:
- if (strncasecmp(t, "SRV", len)==0)
- return T_SRV;
- else if (strncasecmp(t, "TXT", len)==0)
- return T_TXT;
- else if (strncasecmp(t, "EBL", len)==0)
- return T_EBL;
- else if (strncasecmp(t, "PTR", len)==0)
- return T_PTR;
- break;
- case 5:
- if (strncasecmp(t, "NAPTR", len)==0)
- return T_NAPTR;
- else if (strncasecmp(t, "CNAME", len)==0)
- return T_CNAME;
- break;
- }
- return -1;
- }
- /** rpc-prints a dns cache entry.
- */
- void dns_cache_print_entry(rpc_t* rpc, void* ctx, struct dns_hash_entry* e)
- {
- int expires;
- struct dns_rr* rr;
- struct ip_addr ip;
- ticks_t now;
- str s;
- int i;
- now=get_ticks_raw();
- expires = (s_ticks_t)(e->expire-now)<0?-1: TICKS_TO_S(e->expire-now);
-
- rpc->printf(ctx, "%sname: %s", SPACE_FORMAT, e->name);
- rpc->printf(ctx, "%stype: %s", SPACE_FORMAT, print_type(e->type));
- rpc->printf(ctx, "%ssize (bytes): %d", SPACE_FORMAT,
- e->total_size);
- rpc->printf(ctx, "%sreference counter: %d", SPACE_FORMAT,
- e->refcnt.val);
- if (e->ent_flags & DNS_FLAG_PERMANENT) {
- rpc->printf(ctx, "%spermanent: yes", SPACE_FORMAT);
- } else {
- rpc->printf(ctx, "%spermanent: no", SPACE_FORMAT);
- rpc->printf(ctx, "%sexpires in (s): %d", SPACE_FORMAT, expires);
- }
- rpc->printf(ctx, "%slast used (s): %d", SPACE_FORMAT,
- TICKS_TO_S(now-e->last_used));
- rpc->printf(ctx, "%snegative entry: %s", SPACE_FORMAT,
- (e->ent_flags & DNS_FLAG_BAD_NAME) ? "yes" : "no");
-
- for (rr=e->rr_lst; rr; rr=rr->next) {
- switch(e->type) {
- case T_A:
- case T_AAAA:
- if (dns_rr2ip(e->type, rr, &ip)==0){
- rpc->printf(ctx, "%srr ip: %s", SPACE_FORMAT,
- ip_addr2a(&ip) );
- }else{
- rpc->printf(ctx, "%srr ip: <error: bad rr>",
- SPACE_FORMAT);
- }
- break;
- case T_SRV:
- rpc->printf(ctx, "%srr name: %s", SPACE_FORMAT,
- ((struct srv_rdata*)(rr->rdata))->name);
- rpc->printf(ctx, "%srr port: %d", SPACE_FORMAT,
- ((struct srv_rdata*)(rr->rdata))->port);
- rpc->printf(ctx, "%srr priority: %d", SPACE_FORMAT,
- ((struct srv_rdata*)(rr->rdata))->priority);
- rpc->printf(ctx, "%srr weight: %d", SPACE_FORMAT,
- ((struct srv_rdata*)(rr->rdata))->weight);
- break;
- case T_NAPTR:
- rpc->printf(ctx, "%srr order: %d", SPACE_FORMAT,
- ((struct naptr_rdata*)(rr->rdata))->order);
- rpc->printf(ctx, "%srr preference: %d", SPACE_FORMAT,
- ((struct naptr_rdata*)(rr->rdata))->pref);
- s.s = ((struct naptr_rdata*)(rr->rdata))->flags;
- s.len = ((struct naptr_rdata*)(rr->rdata))->flags_len;
- rpc->printf(ctx, "%srr flags: %.*s", SPACE_FORMAT,
- s.len, s.s);
- s.s=((struct naptr_rdata*)(rr->rdata))->services;
- s.len=((struct naptr_rdata*)(rr->rdata))->services_len;
- rpc->printf(ctx, "%srr service: %.*s", SPACE_FORMAT,
- s.len, s.s);
- s.s = ((struct naptr_rdata*)(rr->rdata))->regexp;
- s.len = ((struct naptr_rdata*)(rr->rdata))->regexp_len;
- rpc->printf(ctx, "%srr regexp: %.*s", SPACE_FORMAT,
- s.len, s.s);
- s.s = ((struct naptr_rdata*)(rr->rdata))->repl;
- s.len = ((struct naptr_rdata*)(rr->rdata))->repl_len;
- rpc->printf(ctx, "%srr replacement: %.*s",
- SPACE_FORMAT, s.len, s.s);
- break;
- case T_CNAME:
- rpc->printf(ctx, "%srr name: %s", SPACE_FORMAT,
- ((struct cname_rdata*)(rr->rdata))->name);
- break;
- case T_TXT:
- for (i=0; i<((struct txt_rdata*)(rr->rdata))->cstr_no;
- i++){
- rpc->printf(ctx, "%stxt[%d]: %s", SPACE_FORMAT, i,
- ((struct txt_rdata*)(rr->rdata))->txt[i].cstr);
- }
- break;
- case T_EBL:
- rpc->printf(ctx, "%srr position: %d", SPACE_FORMAT,
- ((struct ebl_rdata*)(rr->rdata))->position);
- rpc->printf(ctx, "%srr separator: %s", SPACE_FORMAT,
- ((struct ebl_rdata*)(rr->rdata))->separator);
- rpc->printf(ctx, "%srr apex: %s", SPACE_FORMAT,
- ((struct ebl_rdata*)(rr->rdata))->apex);
- break;
- case T_PTR:
- rpc->printf(ctx, "%srr name: %s", SPACE_FORMAT,
- ((struct ptr_rdata*)(rr->rdata))->ptrdname);
- break;
- default:
- rpc->printf(ctx, "%sresource record: unknown",
- SPACE_FORMAT);
- }
- if ((e->ent_flags & DNS_FLAG_PERMANENT) == 0)
- rpc->printf(ctx, "%srr expires in (s): %d", SPACE_FORMAT,
- (s_ticks_t)(rr->expire-now)<0?-1 :
- TICKS_TO_S(rr->expire-now));
- }
- }
- /* dumps the content of the cache in a human-readable format */
- void dns_cache_view(rpc_t* rpc, void* ctx)
- {
- int h;
- struct dns_hash_entry* e;
- ticks_t now;
- if (!cfg_get(core, core_cfg, use_dns_cache)){
- rpc->fault(ctx, 500, "dns cache support disabled (see use_dns_cache)");
- return;
- }
- now=get_ticks_raw();
- LOCK_DNS_HASH();
- for (h=0; h<DNS_HASH_SIZE; h++){
- clist_foreach(&dns_hash[h], e, next){
- if (((e->ent_flags & DNS_FLAG_PERMANENT) == 0)
- && TICKS_LT(e->expire, now)
- ) {
- continue;
- }
- rpc->printf(ctx, "{\n");
- dns_cache_print_entry(rpc, ctx, e);
- rpc->printf(ctx, "}");
- }
- }
- UNLOCK_DNS_HASH();
- }
- /* Delete all the entries from the cache.
- * If del_permanent is 0, then only the
- * non-permanent entries are deleted.
- */
- void dns_cache_flush(int del_permanent)
- {
- int h;
- struct dns_hash_entry* e;
- struct dns_hash_entry* tmp;
- DBG("dns_cache_flush(): removing elements from the cache\n");
- LOCK_DNS_HASH();
- for (h=0; h<DNS_HASH_SIZE; h++){
- clist_foreach_safe(&dns_hash[h], e, tmp, next){
- if (del_permanent || ((e->ent_flags & DNS_FLAG_PERMANENT) == 0))
- _dns_hash_remove(e);
- }
- }
- UNLOCK_DNS_HASH();
- }
- /* deletes all the non-permanent entries from the cache */
- void dns_cache_delete_all(rpc_t* rpc, void* ctx)
- {
- if (!cfg_get(core, core_cfg, use_dns_cache)){
- rpc->fault(ctx, 500, "dns cache support disabled (see use_dns_cache)");
- return;
- }
- dns_cache_flush(0);
- rpc->printf(ctx, "OK");
- }
- /* deletes all the entries from the cache,
- * even the permanent ones */
- void dns_cache_delete_all_force(rpc_t* rpc, void* ctx)
- {
- if (!cfg_get(core, core_cfg, use_dns_cache)){
- rpc->fault(ctx, 500, "dns cache support disabled (see use_dns_cache)");
- return;
- }
- dns_cache_flush(1);
- rpc->printf(ctx, "OK");
- }
- /* clones an entry and extends its memory area to hold a new rr.
- * if rdata_size>0 the new dns_rr struct is initialized, but the rdata is
- * only filled with 0.
- */
- static struct dns_hash_entry *dns_cache_clone_entry(struct dns_hash_entry *e,
- int rdata_size,
- int ttl,
- struct dns_rr **_new_rr)
- {
- struct dns_hash_entry *new;
- struct dns_rr *rr, *last_rr, *new_rr;
- int size, rounded_size, rr_size;
- ticks_t now;
- int i;
- now=get_ticks_raw();
- size = e->total_size;
- if (rdata_size) {
- /* we have to extend the entry */
- rounded_size = ROUND_POINTER(size); /* size may not have been
- rounded previously */
- switch (e->type) {
- case T_A:
- case T_AAAA:
- case T_CNAME:
- rr_size = sizeof(struct dns_rr);
- break;
- case T_SRV:
- rr_size = ROUND_SHORT(sizeof(struct dns_rr));
- break;
- case T_NAPTR:
- rr_size = ROUND_POINTER(sizeof(struct dns_rr));
- break;
- case T_TXT:
- rr_size = ROUND_POINTER(sizeof(struct dns_rr));
- break;
- case T_EBL:
- rr_size = ROUND_POINTER(sizeof(struct dns_rr));
- break;
- case T_PTR:
- rr_size = sizeof(struct dns_rr);
- break;
- default:
- LOG(L_ERR, "ERROR: dns_cache_clone_entry: type %d not "
- "supported\n", e->type);
- return NULL;
- }
- } else {
- rounded_size = size; /* no need to round the size, we just clone
- the entry without extending it */
- rr_size = 0;
- }
- new=shm_malloc(rounded_size+rr_size+rdata_size);
- if (!new) {
- LOG(L_ERR, "ERROR: dns_cache_clone_entry: out of memory\n");
- return NULL;
- }
- memset(new, 0, rounded_size+rr_size+rdata_size);
- /* clone the entry */
- memcpy(new, e, size);
- /* fix the values and pointers */
- new->next = new->prev = NULL;
- #ifdef DNS_LU_LST
- new->last_used_lst.next = new->last_used_lst.prev = NULL;
- #endif
- new->rr_lst = (struct dns_rr*)translate_pointer((char*)new, (char*)e,
- (char*)new->rr_lst);
- atomic_set(&new->refcnt, 0);
- new->last_used = now;
- /* expire and total_size are fixed later if needed */
- /* fix the pointers inside the rr structures */
- last_rr = NULL;
- for (rr=new->rr_lst; rr; rr=rr->next) {
- rr->rdata = (void*)translate_pointer((char*)new, (char*)e,
- (char*)rr->rdata);
- if (rr->next)
- rr->next = (struct dns_rr*)translate_pointer((char*)new, (char*)e,
- (char*)rr->next);
- else
- last_rr = rr;
- switch(e->type){
- case T_NAPTR:
- /* there are pointers inside the NAPTR rdata stucture */
- ((struct naptr_rdata*)rr->rdata)->flags =
- translate_pointer((char*)new, (char*)e,
- ((struct naptr_rdata*)rr->rdata)->flags);
- ((struct naptr_rdata*)rr->rdata)->services =
- translate_pointer((char*)new, (char*)e,
- ((struct naptr_rdata*)rr->rdata)->services);
- ((struct naptr_rdata*)rr->rdata)->regexp =
- translate_pointer((char*)new, (char*)e,
- ((struct naptr_rdata*)rr->rdata)->regexp);
- ((struct naptr_rdata*)rr->rdata)->repl =
- translate_pointer((char*)new, (char*)e,
- ((struct naptr_rdata*)rr->rdata)->repl);
- break;
- case T_TXT:
- /* there are pointers inside the TXT structure */
- for (i=0; i<((struct txt_rdata*)rr->rdata)->cstr_no; i++){
- ((struct txt_rdata*)rr->rdata)->txt[i].cstr=
- translate_pointer((char*) new, (char*) e,
- ((struct txt_rdata*)rr->rdata)->txt[i].cstr);
- }
- break;
- case T_EBL:
- /* there are pointers inside the EBL structure */
- ((struct ebl_rdata*)rr->rdata)->separator =
- translate_pointer((char*)new, (char*)e,
- ((struct ebl_rdata*)rr->rdata)->separator);
- ((struct ebl_rdata*)rr->rdata)->apex =
- translate_pointer((char*)new, (char*)e,
- ((struct ebl_rdata*)rr->rdata)->apex);
- break;
- }
- }
- if (rdata_size) {
- /* set the pointer to the new rr structure */
- new_rr = (void*)((char*)new + rounded_size);
- new_rr->rdata = (void*)((char*)new_rr+rr_size);
- new_rr->expire = now + S_TO_TICKS(ttl);
- /* link the rr to the previous one */
- last_rr->next = new_rr;
- /* fix the total_size and expires values */
- new->total_size=rounded_size+rr_size+rdata_size;
- new->expire = MAX(new->expire, new_rr->expire);
- if (_new_rr)
- *_new_rr = new_rr;
- } else {
- if (_new_rr)
- *_new_rr = NULL;
- }
- return new;
- }
- /* Adds a new record to the cache.
- * If there is an existing record with the same name and value
- * (ip address in case of A/AAAA record, name in case of SRV record)
- * only the remaining fields are updated.
- *
- * Note that permanent records cannot be overwritten unless
- * the new record is also permanent. A permanent record
- * completely replaces a non-permanent one.
- *
- * Currently only A, AAAA, and SRV records are supported.
- */
- int dns_cache_add_record(unsigned short type,
- str *name,
- int ttl,
- str *value,
- int priority,
- int weight,
- int port,
- int flags)
- {
- struct dns_hash_entry *old=NULL, *new=NULL;
- struct dns_rr *rr;
- str rr_name;
- struct ip_addr *ip_addr;
- ticks_t expire;
- int err, h;
- int size;
- struct dns_rr *new_rr, **rr_p, **rr_iter;
- struct srv_rdata *srv_rd;
- /* eliminate gcc warnings */
- ip_addr = 0;
- size = 0;
- rr_name.s = NULL;
- rr_name.len = 0;
- if (!cfg_get(core, core_cfg, use_dns_cache)){
- LOG(L_ERR, "ERROR: dns cache support disabled (see use_dns_cache)\n");
- return -1;
- }
-
- if ((type != T_A) && (type != T_AAAA) && (type != T_SRV)) {
- LOG(L_ERR, "ERROR: rr type %d is not implemented\n",
- type);
- return -1;
- }
- if ((flags & DNS_FLAG_BAD_NAME) == 0) {
- /* fix-up the values */
- switch(type) {
- case T_A:
- ip_addr = str2ip(value);
- if (!ip_addr) {
- LOG(L_ERR, "ERROR: Malformed ip address: %.*s\n",
- value->len, value->s);
- return -1;
- }
- break;
- case T_AAAA:
- #ifdef USE_IPV6
- ip_addr = str2ip6(value);
- if (!ip_addr) {
- LOG(L_ERR, "ERROR: Malformed ip address: %.*s\n",
- value->len, value->s);
- return -1;
- }
- break;
- #else /* USE_IPV6 */
- LOG(L_ERR, "ERROR: IPv6 support is disabled\n");
- return -1;
- #endif /* USE_IPV6 */
- case T_SRV:
- rr_name = *value;
- break;
- }
- }
- /* check whether there is a matching entry in the cache */
- old = dns_hash_get(name, type, &h, &err);
- if (old && old->type!=type) {
- /* probably we found a CNAME instead of the specified type,
- it is not needed */
- dns_hash_put(old);
- old=NULL;
- }
- if (old
- && (old->ent_flags & DNS_FLAG_PERMANENT)
- && ((flags & DNS_FLAG_PERMANENT) == 0)
- ) {
- LOG(L_ERR, "ERROR: A non-permanent record cannot overwrite "
- "a permanent entry\n");
- goto error;
- }
- /* prepare the entry */
- if (flags & DNS_FLAG_BAD_NAME) {
- /* negative entry */
- new = dns_cache_mk_bad_entry(name, type, ttl, flags);
- if (!new) {
- LOG(L_ERR, "ERROR: Failed to create a negative "
- "DNS cache entry\n");
- goto error;
- }
- } else {
- if (!old
- || (old->ent_flags & DNS_FLAG_BAD_NAME)
- || (((old->ent_flags & DNS_FLAG_PERMANENT) == 0)
- && (flags & DNS_FLAG_PERMANENT))
- ) {
- /* There was no matching entry in the hash table,
- * the entry is a negative record with inefficient space,
- * or a permanent entry overwrites a non-permanent one.
- * Let us create a new one.
- */
- switch(type) {
- case T_A:
- case T_AAAA:
- new = dns_cache_mk_ip_entry(name, ip_addr);
- if (!new) {
- LOG(L_ERR, "ERROR: Failed to create an A/AAAA record\n");
- goto error;
- }
- /* fix the expiration time, dns_cache_mk_ip_entry() sets it
- * to now-1 */
- expire = get_ticks_raw() + S_TO_TICKS(ttl);
- new->expire = expire;
- new->rr_lst->expire = expire;
- break;
- case T_SRV:
- new = dns_cache_mk_srv_entry(name, priority, weight, port,
- &rr_name, ttl);
- if (!new) {
- LOG(L_ERR, "ERROR: Failed to create an SRV record\n");
- goto error;
- }
- }
- new->ent_flags = flags;
- } else {
- /* we must modify the entry, so better to clone it, modify the new
- * one, and replace the old with the new entry in the hash table,
- * because the entry might be in use (even if the dns hash is
- * locked). The old entry will be removed from the hash and
- * automatically destroyed when its refcnt will be 0*/
- /* check whether there is an rr with the same value */
- for (rr=old->rr_lst; rr; rr=rr->next)
- if ((((type == T_A) || (type == T_AAAA)) &&
- (memcmp(ip_addr->u.addr, ((struct a_rdata*)rr->rdata)->ip,
- ip_addr->len)==0))
- || ((type == T_SRV) &&
- (((struct srv_rdata*)rr->rdata)->name_len == rr_name.len)&&
- (memcmp(rr_name.s, ((struct srv_rdata*)rr->rdata)->name,
- rr_name.len)==0)))
- break;
- if (rr) {
- /* the rr was found in the list */
- new = dns_cache_clone_entry(old, 0, 0, 0);
- if (!new) {
- LOG(L_ERR, "ERROR: Failed to clone an existing "
- "DNS cache entry\n");
- goto error;
- }
- /* let the rr point to the new structure */
- rr = (struct dns_rr*)translate_pointer((char*)new, (char*)old,
- (char*)rr);
- new_rr = rr;
- if (type == T_SRV) {
- /* fix the priority, weight, and port */
- ((struct srv_rdata*)rr->rdata)->priority = priority;
- ((struct srv_rdata*)rr->rdata)->weight = weight;
- ((struct srv_rdata*)rr->rdata)->port = port;
- }
- /* fix the expire value */
- rr->expire = get_ticks_raw() + S_TO_TICKS(ttl);
- new->expire = 0;
- for (rr=new->rr_lst; rr; rr=rr->next)
- new->expire = MAX(new->expire, rr->expire);
- } else {
- /* there was no matching rr, extend the structure with a new
- * one */
- switch(type) {
- case T_A:
- size = sizeof(struct a_rdata);
- break;
- case T_AAAA:
- size = sizeof(struct aaaa_rdata);
- break;
- case T_SRV:
- size = sizeof(struct srv_rdata)-1 +
- rr_name.len+1;
- break;
- }
- new = dns_cache_clone_entry(old, size, ttl, &rr);
- if (!new) {
- LOG(L_ERR, "ERROR: Failed to clone an existing "
- "DNS cache entry\n");
- goto error;
- }
- new_rr = rr;
- switch(type) {
- case T_A:
- case T_AAAA:
- memcpy(rr->rdata, ip_addr->u.addr, ip_addr->len);
- break;
- case T_SRV:
- ((struct srv_rdata*)rr->rdata)->priority = priority;
- ((struct srv_rdata*)rr->rdata)->weight = weight;
- ((struct srv_rdata*)rr->rdata)->port = port;
- ((struct srv_rdata*)rr->rdata)->name_len = rr_name.len;
- memcpy(((struct srv_rdata*)rr->rdata)->name, rr_name.s,
- rr_name.len);
- }
- /* maximum expire value has been already fixed by
- * dns_cache_clone_entry() */
- }
- if (type == T_SRV) {
- /* SRV records must be ordered by their priority and weight.
- * With modifying an exising rr, or adding new rr to the DNS entry,
- * the ordered list might got broken which needs to be fixed.
- */
- rr_p = NULL;
- for ( rr_iter = &new->rr_lst;
- *rr_iter;
- rr_iter = &((*rr_iter)->next)
- ) {
- if (*rr_iter == new_rr) {
- rr_p = rr_iter;
- continue;
- }
- srv_rd = (struct srv_rdata*)(*rr_iter)->rdata;
- if ((priority < srv_rd->priority) ||
- ((priority == srv_rd->priority) && (weight > srv_rd->weight))
- )
- break; /* insert here */
- }
- if (!rr_p)
- for ( rr_p = rr_iter;
- *rr_p && (*rr_p != new_rr);
- rr_p = &((*rr_p)->next)
- );
- if (!rr_p) {
- LOG(L_ERR, "ERROR: Failed to correct the orderd list of SRV resource records\n");
- goto error;
- }
- if (*rr_iter != new_rr->next) {
- /* unlink rr from the list */
- *rr_p = (*rr_p)->next;
- /* link it before *rr_iter */
- new_rr->next = *rr_iter;
- *rr_iter = new_rr;
- }
- }
- }
- }
- LOCK_DNS_HASH();
- if (dns_cache_add_unsafe(new)) {
- LOG(L_ERR, "ERROR: Failed to add the entry to the cache\n");
- UNLOCK_DNS_HASH();
- goto error;
- } else {
- /* remove the old entry from the list */
- if (old)
- _dns_hash_remove(old);
- }
- UNLOCK_DNS_HASH();
- if (old)
- dns_hash_put(old);
- return 0;
- error:
- /* leave the old entry in the list, and free the new one */
- if (old)
- dns_hash_put(old);
- if (new)
- dns_destroy_entry(new);
- return -1;
- }
- /* deletes a record from the cache */
- static void dns_cache_delete_record(rpc_t* rpc, void* ctx, unsigned short type)
- {
- struct dns_hash_entry *e;
- str name;
- int err, h, found=0, permanent=0;
- if (!cfg_get(core, core_cfg, use_dns_cache)){
- rpc->fault(ctx, 500, "dns cache support disabled (see use_dns_cache)");
- return;
- }
-
- if (rpc->scan(ctx, "S", &name) < 1)
- return;
- LOCK_DNS_HASH();
- e=_dns_hash_find(&name, type, &h, &err);
- if (e && (e->type==type)) {
- if ((e->ent_flags & DNS_FLAG_PERMANENT) == 0)
- _dns_hash_remove(e);
- else
- permanent = 1;
- found = 1;
- }
- UNLOCK_DNS_HASH();
- if (permanent)
- rpc->fault(ctx, 400, "Permanent entries cannot be deleted");
- else if (!found)
- rpc->fault(ctx, 400, "Not found");
- }
- /* Delete a single record from the cache,
- * i.e. the record with the same name and value
- * (ip address in case of A/AAAA record, name in case of SRV record).
- *
- * Currently only A, AAAA, and SRV records are supported.
- */
- int dns_cache_delete_single_record(unsigned short type,
- str *name,
- str *value,
- int flags)
- {
- struct dns_hash_entry *old=NULL, *new=NULL;
- struct dns_rr *rr, **next_p;
- str rr_name;
- struct ip_addr *ip_addr;
- int err, h;
- /* eliminate gcc warnings */
- rr_name.s = NULL;
- rr_name.len = 0;
- ip_addr = 0;
- if (!cfg_get(core, core_cfg, use_dns_cache)){
- LOG(L_ERR, "ERROR: dns cache support disabled (see use_dns_cache)\n");
- return -1;
- }
-
- if ((type != T_A) && (type != T_AAAA) && (type != T_SRV)) {
- LOG(L_ERR, "ERROR: rr type %d is not implemented\n",
- type);
- return -1;
- }
- if ((flags & DNS_FLAG_BAD_NAME) == 0) {
- /* fix-up the values */
- switch(type) {
- case T_A:
- ip_addr = str2ip(value);
- if (!ip_addr) {
- LOG(L_ERR, "ERROR: Malformed ip address: %.*s\n",
- value->len, value->s);
- return -1;
- }
- break;
- case T_AAAA:
- #ifdef USE_IPV6
- ip_addr = str2ip6(value);
- if (!ip_addr) {
- LOG(L_ERR, "ERROR: Malformed ip address: %.*s\n",
- value->len, value->s);
- return -1;
- }
- break;
- #else /* USE_IPV6 */
- LOG(L_ERR, "ERROR: IPv6 support is disabled\n");
- return -1;
- #endif /* USE_IPV6 */
- case T_SRV:
- rr_name = *value;
- break;
- }
- }
- /* check whether there is a matching entry in the cache */
- if ((old = dns_hash_get(name, type, &h, &err)) == NULL)
- goto not_found;
- if ((old->type != type) /* may be CNAME */
- || (old->ent_flags != flags)
- )
- goto not_found;
- if (flags & DNS_FLAG_BAD_NAME) /* negative record, there is no value */
- goto delete;
- /* check whether there is an rr with the same value */
- for (rr=old->rr_lst, next_p=&old->rr_lst;
- rr;
- next_p=&rr->next, rr=rr->next
- )
- if ((((type == T_A) || (type == T_AAAA)) &&
- (memcmp(ip_addr->u.addr, ((struct a_rdata*)rr->rdata)->ip,
- ip_addr->len)==0))
- || ((type == T_SRV) &&
- (((struct srv_rdata*)rr->rdata)->name_len == rr_name.len) &&
- (memcmp(rr_name.s, ((struct srv_rdata*)rr->rdata)->name,
- rr_name.len)==0)))
- break;
- if (!rr)
- goto not_found;
- if ((rr == old->rr_lst) && (rr->next == NULL)) {
- /* There is a single rr value, hence the whole
- * hash entry can be deleted */
- goto delete;
- } else {
- /* we must modify the entry, so better to clone it, modify the new
- * one, and replace the old with the new entry in the hash table,
- * because the entry might be in use (even if the dns hash is
- * locked). The old entry will be removed from the hash and
- * automatically destroyed when its refcnt will be 0*/
- new = dns_cache_clone_entry(old, 0, 0, 0);
- if (!new) {
- LOG(L_ERR, "ERROR: Failed to clone an existing "
- "DNS cache entry\n");
- dns_hash_put(old);
- return -1;
- }
- /* let rr and next_p point to the new structure */
- rr = (struct dns_rr*)translate_pointer((char*)new,
- (char*)old,
- (char*)rr);
- next_p = (struct dns_rr**)translate_pointer((char*)new,
- (char*)old,
- (char*)next_p);
- /* unlink rr from the list. The memory will be freed
- * when the whole record is freed */
- *next_p = rr->next;
- }
- delete:
- LOCK_DNS_HASH();
- if (new) {
- /* delete the old entry only if the new one can be added */
- if (dns_cache_add_unsafe(new)) {
- LOG(L_ERR, "ERROR: Failed to add the entry to the cache\n");
- UNLOCK_DNS_HASH();
- if (old)
- dns_hash_put(old);
- return -1;
- } else {
- /* remove the old entry from the list */
- if (old)
- _dns_hash_remove(old);
- }
- } else if (old) {
- _dns_hash_remove(old);
- }
- UNLOCK_DNS_HASH();
- if (old)
- dns_hash_put(old);
- return 0;
- not_found:
- LOG(L_ERR, "ERROR: No matching record found\n");
- if (old)
- dns_hash_put(old);
- return -1;
- }
- /* performs a dns lookup over rpc */
- void dns_cache_rpc_lookup(rpc_t* rpc, void* ctx)
- {
- struct dns_hash_entry *e;
- str name;
- str type;
- int t;
- if (!cfg_get(core, core_cfg, use_dns_cache)){
- rpc->fault(ctx, 500, "dns cache support disabled (see use_dns_cache)");
- return;
- }
-
- if (rpc->scan(ctx, "SS", &type, &name) < 1)
- return;
- t=dns_get_type(&type);
- if (t<0){
- rpc->fault(ctx, 400, "Invalid type");
- return;
- }
- e=dns_get_entry(&name, t);
- if (e==0){
- rpc->fault(ctx, 400, "Not found");
- return;
- }
- dns_cache_print_entry(rpc, ctx, e);
- dns_hash_put(e);
- }
- /* wrapper functions for adding and deleting records */
- void dns_cache_add_a(rpc_t* rpc, void* ctx)
- {
- str name;
- int ttl;
- str ip;
- int flags;
- if (rpc->scan(ctx, "SdSd", &name, &ttl, &ip, &flags) < 4)
- return;
- if (dns_cache_add_record(T_A,
- &name,
- ttl,
- &ip,
- 0 /* priority */,
- 0 /* weight */,
- 0 /* port */,
- flags)
- )
- rpc->fault(ctx, 400, "Failed to add the entry to the cache");
- }
- void dns_cache_add_aaaa(rpc_t* rpc, void* ctx)
- {
- str name;
- int ttl;
- str ip;
- int flags;
- if (rpc->scan(ctx, "SdSd", &name, &ttl, &ip, &flags) < 4)
- return;
- if (dns_cache_add_record(T_AAAA,
- &name,
- ttl,
- &ip,
- 0 /* priority */,
- 0 /* weight */,
- 0 /* port */,
- flags)
- )
- rpc->fault(ctx, 400, "Failed to add the entry to the cache");
- }
- void dns_cache_add_srv(rpc_t* rpc, void* ctx)
- {
- str name;
- int ttl, priority, weight, port;
- str rr_name;
- int flags;
- if (rpc->scan(ctx, "SddddSd", &name, &ttl, &priority, &weight, &port,
- &rr_name, &flags) < 7
- )
- return;
- if (dns_cache_add_record(T_SRV,
- &name,
- ttl,
- &rr_name,
- priority,
- weight,
- port,
- flags)
- )
- rpc->fault(ctx, 400, "Failed to add the entry to the cache");
- }
- void dns_cache_delete_a(rpc_t* rpc, void* ctx)
- {
- dns_cache_delete_record(rpc, ctx, T_A);
- }
- void dns_cache_delete_aaaa(rpc_t* rpc, void* ctx)
- {
- dns_cache_delete_record(rpc, ctx, T_AAAA);
- }
- void dns_cache_delete_srv(rpc_t* rpc, void* ctx)
- {
- dns_cache_delete_record(rpc, ctx, T_SRV);
- }
- void dns_cache_delete_naptr(rpc_t* rpc, void* ctx)
- {
- dns_cache_delete_record(rpc, ctx, T_NAPTR);
- }
- void dns_cache_delete_cname(rpc_t* rpc, void* ctx)
- {
- dns_cache_delete_record(rpc, ctx, T_CNAME);
- }
- void dns_cache_delete_txt(rpc_t* rpc, void* ctx)
- {
- dns_cache_delete_record(rpc, ctx, T_TXT);
- }
- void dns_cache_delete_ebl(rpc_t* rpc, void* ctx)
- {
- dns_cache_delete_record(rpc, ctx, T_EBL);
- }
- void dns_cache_delete_ptr(rpc_t* rpc, void* ctx)
- {
- dns_cache_delete_record(rpc, ctx, T_PTR);
- }
- #ifdef DNS_WATCHDOG_SUPPORT
- /* sets the DNS server states */
- void dns_set_server_state_rpc(rpc_t* rpc, void* ctx)
- {
- int state;
- if (!cfg_get(core, core_cfg, use_dns_cache)){
- rpc->fault(ctx, 500, "dns cache support disabled (see use_dns_cache)");
- return;
- }
- if (rpc->scan(ctx, "d", &state) < 1)
- return;
- dns_set_server_state(state);
- }
- /* prints the DNS server state */
- void dns_get_server_state_rpc(rpc_t* rpc, void* ctx)
- {
- if (!cfg_get(core, core_cfg, use_dns_cache)){
- rpc->fault(ctx, 500, "dns cache support disabled (see use_dns_cache)");
- return;
- }
- rpc->add(ctx, "d", dns_get_server_state());
- }
- #endif /* DNS_WATCHDOG_SUPPORT */
- #endif
|