12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693 |
- /*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2006, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
- /*! \file
- *
- * \brief Implementation of Inter-Asterisk eXchange Version 2
- * as specified in RFC 5456
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \par See also
- * \arg \ref Config_iax
- *
- * \ingroup channel_drivers
- */
- /*** MODULEINFO
- <use>dahdi</use>
- <use>crypto</use>
- ***/
- #include "asterisk.h"
- ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
- #include <sys/mman.h>
- #include <dirent.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <netinet/in_systm.h>
- #include <netinet/ip.h>
- #include <sys/time.h>
- #include <sys/signal.h>
- #include <signal.h>
- #include <strings.h>
- #include <netdb.h>
- #include <fcntl.h>
- #include <sys/stat.h>
- #include <regex.h>
- #include <sys/ioctl.h>
- #if defined(HAVE_DAHDI)
- #include <dahdi/user.h>
- #endif
- #include "asterisk/paths.h" /* need ast_config_AST_DATA_DIR for firmware */
- #include "asterisk/lock.h"
- #include "asterisk/frame.h"
- #include "asterisk/channel.h"
- #include "asterisk/module.h"
- #include "asterisk/pbx.h"
- #include "asterisk/sched.h"
- #include "asterisk/io.h"
- #include "asterisk/config.h"
- #include "asterisk/cli.h"
- #include "asterisk/translate.h"
- #include "asterisk/md5.h"
- #include "asterisk/cdr.h"
- #include "asterisk/crypto.h"
- #include "asterisk/acl.h"
- #include "asterisk/manager.h"
- #include "asterisk/callerid.h"
- #include "asterisk/app.h"
- #include "asterisk/astdb.h"
- #include "asterisk/musiconhold.h"
- #include "asterisk/features.h"
- #include "asterisk/utils.h"
- #include "asterisk/causes.h"
- #include "asterisk/localtime.h"
- #include "asterisk/aes.h"
- #include "asterisk/dnsmgr.h"
- #include "asterisk/devicestate.h"
- #include "asterisk/netsock.h"
- #include "asterisk/stringfields.h"
- #include "asterisk/linkedlists.h"
- #include "asterisk/event.h"
- #include "asterisk/astobj2.h"
- #include "iax2.h"
- #include "iax2-parser.h"
- #include "iax2-provision.h"
- #include "jitterbuf.h"
- /* Define SCHED_MULTITHREADED to run the scheduler in a special
- multithreaded mode. */
- #define SCHED_MULTITHREADED
- /* Define DEBUG_SCHED_MULTITHREADED to keep track of where each
- thread is actually doing. */
- #define DEBUG_SCHED_MULTITHREAD
- #ifdef SO_NO_CHECK
- static int nochecksums = 0;
- #endif
- #define PTR_TO_CALLNO(a) ((unsigned short)(unsigned long)(a))
- #define CALLNO_TO_PTR(a) ((void *)(unsigned long)(a))
- #define DEFAULT_THREAD_COUNT 10
- #define DEFAULT_MAX_THREAD_COUNT 100
- #define DEFAULT_RETRY_TIME 1000
- #define MEMORY_SIZE 100
- #define DEFAULT_DROP 3
- #define DEBUG_SUPPORT
- #define MIN_REUSE_TIME 60 /* Don't reuse a call number within 60 seconds */
- /* Sample over last 100 units to determine historic jitter */
- #define GAMMA (0.01)
- static struct ast_codec_pref prefs;
- static const char tdesc[] = "Inter Asterisk eXchange Driver (Ver 2)";
- /*! \brief Maximum transmission unit for the UDP packet in the trunk not to be
- fragmented. This is based on 1516 - ethernet - ip - udp - iax minus one g711 frame = 1240 */
- #define MAX_TRUNK_MTU 1240
- static int global_max_trunk_mtu; /*!< Maximum MTU, 0 if not used */
- static int trunk_timed, trunk_untimed, trunk_maxmtu, trunk_nmaxmtu ; /*!< Trunk MTU statistics */
- static char context[80] = "default";
- static char language[MAX_LANGUAGE] = "";
- static char regcontext[AST_MAX_CONTEXT] = "";
- static int maxauthreq = 3;
- static int max_retries = 4;
- static int ping_time = 21;
- static int lagrq_time = 10;
- static int maxjitterbuffer=1000;
- static int resyncthreshold=1000;
- static int maxjitterinterps=10;
- static int jittertargetextra = 40; /* number of milliseconds the new jitter buffer adds on to its size */
- #define MAX_TRUNKDATA 640 * 200 /*!< 40ms, uncompressed linear * 200 channels */
- static int trunkfreq = 20;
- static int trunkmaxsize = MAX_TRUNKDATA;
- static int authdebug = 1;
- static int autokill = 0;
- static int iaxcompat = 0;
- static int last_authmethod = 0;
- static int iaxdefaultdpcache=10 * 60; /* Cache dialplan entries for 10 minutes by default */
- static int iaxdefaulttimeout = 5; /* Default to wait no more than 5 seconds for a reply to come back */
- static unsigned int tos = 0;
- static unsigned int cos = 0;
- static int min_reg_expire;
- static int max_reg_expire;
- static int srvlookup = 0;
- static int timingfd = -1; /* Timing file descriptor */
- static struct ast_netsock_list *netsock;
- static struct ast_netsock_list *outsock; /*!< used if sourceaddress specified and bindaddr == INADDR_ANY */
- static int defaultsockfd = -1;
- int (*iax2_regfunk)(const char *username, int onoff) = NULL;
- /* Ethernet, etc */
- #define IAX_CAPABILITY_FULLBANDWIDTH (0xFFFF & ~AST_FORMAT_AUDIO_UNDEFINED)
- /* T1, maybe ISDN */
- #define IAX_CAPABILITY_MEDBANDWIDTH (IAX_CAPABILITY_FULLBANDWIDTH & \
- ~AST_FORMAT_SLINEAR & \
- ~AST_FORMAT_SLINEAR16 & \
- ~AST_FORMAT_ULAW & \
- ~AST_FORMAT_ALAW & \
- ~AST_FORMAT_G722)
- /* A modem */
- #define IAX_CAPABILITY_LOWBANDWIDTH (IAX_CAPABILITY_MEDBANDWIDTH & \
- ~AST_FORMAT_G726 & \
- ~AST_FORMAT_G726_AAL2 & \
- ~AST_FORMAT_ADPCM)
- #define IAX_CAPABILITY_LOWFREE (IAX_CAPABILITY_LOWBANDWIDTH & \
- ~AST_FORMAT_G723_1)
- #define DEFAULT_MAXMS 2000 /* Must be faster than 2 seconds by default */
- #define DEFAULT_FREQ_OK 60 * 1000 /* How often to check for the host to be up */
- #define DEFAULT_FREQ_NOTOK 10 * 1000 /* How often to check, if the host is down... */
- static struct io_context *io;
- static struct sched_context *sched;
- static int iax2_capability = IAX_CAPABILITY_FULLBANDWIDTH;
- static int iaxdebug = 0;
- static int iaxtrunkdebug = 0;
- static int test_losspct = 0;
- #ifdef IAXTESTS
- static int test_late = 0;
- static int test_resync = 0;
- static int test_jit = 0;
- static int test_jitpct = 0;
- #endif /* IAXTESTS */
- static char accountcode[AST_MAX_ACCOUNT_CODE];
- static char mohinterpret[MAX_MUSICCLASS];
- static char mohsuggest[MAX_MUSICCLASS];
- static int amaflags = 0;
- static int adsi = 0;
- static int delayreject = 0;
- static int iax2_encryption = 0;
- static struct ast_flags globalflags = { 0 };
- static pthread_t netthreadid = AST_PTHREADT_NULL;
- static pthread_t schedthreadid = AST_PTHREADT_NULL;
- AST_MUTEX_DEFINE_STATIC(sched_lock);
- static ast_cond_t sched_cond;
- enum iax2_state {
- IAX_STATE_STARTED = (1 << 0),
- IAX_STATE_AUTHENTICATED = (1 << 1),
- IAX_STATE_TBD = (1 << 2),
- };
- struct iax2_context {
- char context[AST_MAX_CONTEXT];
- struct iax2_context *next;
- };
- enum iax2_flags {
- IAX_HASCALLERID = (1 << 0), /*!< CallerID has been specified */
- IAX_DELME = (1 << 1), /*!< Needs to be deleted */
- IAX_TEMPONLY = (1 << 2), /*!< Temporary (realtime) */
- IAX_TRUNK = (1 << 3), /*!< Treat as a trunk */
- IAX_NOTRANSFER = (1 << 4), /*!< Don't native bridge */
- IAX_USEJITTERBUF = (1 << 5), /*!< Use jitter buffer */
- IAX_DYNAMIC = (1 << 6), /*!< dynamic peer */
- IAX_SENDANI = (1 << 7), /*!< Send ANI along with CallerID */
- /* (1 << 8) is currently unused due to the deprecation of an old option. Go ahead, take it! */
- IAX_ALREADYGONE = (1 << 9), /*!< Already disconnected */
- IAX_PROVISION = (1 << 10), /*!< This is a provisioning request */
- IAX_QUELCH = (1 << 11), /*!< Whether or not we quelch audio */
- IAX_ENCRYPTED = (1 << 12), /*!< Whether we should assume encrypted tx/rx */
- IAX_KEYPOPULATED = (1 << 13), /*!< Whether we have a key populated */
- IAX_CODEC_USER_FIRST = (1 << 14), /*!< are we willing to let the other guy choose the codec? */
- IAX_CODEC_NOPREFS = (1 << 15), /*!< Force old behaviour by turning off prefs */
- IAX_CODEC_NOCAP = (1 << 16), /*!< only consider requested format and ignore capabilities*/
- IAX_RTCACHEFRIENDS = (1 << 17), /*!< let realtime stay till your reload */
- IAX_RTUPDATE = (1 << 18), /*!< Send a realtime update */
- IAX_RTAUTOCLEAR = (1 << 19), /*!< erase me on expire */
- IAX_FORCEJITTERBUF = (1 << 20), /*!< Force jitterbuffer, even when bridged to a channel that can take jitter */
- IAX_RTIGNOREREGEXPIRE = (1 << 21), /*!< When using realtime, ignore registration expiration */
- IAX_TRUNKTIMESTAMPS = (1 << 22), /*!< Send trunk timestamps */
- IAX_TRANSFERMEDIA = (1 << 23), /*!< When doing IAX2 transfers, transfer media only */
- IAX_MAXAUTHREQ = (1 << 24), /*!< Maximum outstanding AUTHREQ restriction is in place */
- IAX_DELAYPBXSTART = (1 << 25), /*!< Don't start a PBX on the channel until the peer sends us a
- response, so that we've achieved a three-way handshake with
- them before sending voice or anything else*/
- IAX_ALLOWFWDOWNLOAD = (1 << 26), /*!< Allow the FWDOWNL command? */
- IAX_SHRINKCALLERID = (1 << 27), /*!< Turn on and off caller id shrinking */
- };
- static int global_rtautoclear = 120;
- static int reload_config(void);
- /*!
- * \brief Call token validation settings.
- */
- enum calltoken_peer_enum {
- /*! \brief Default calltoken required unless the ip is in the ignorelist */
- CALLTOKEN_DEFAULT = 0,
- /*! \brief Require call token validation. */
- CALLTOKEN_YES = 1,
- /*! \brief Require call token validation after a successful registration
- * using call token validation occurs. */
- CALLTOKEN_AUTO = 2,
- /*! \brief Do not require call token validation. */
- CALLTOKEN_NO = 3,
- };
- struct iax2_user {
- AST_DECLARE_STRING_FIELDS(
- AST_STRING_FIELD(name);
- AST_STRING_FIELD(secret);
- AST_STRING_FIELD(dbsecret);
- AST_STRING_FIELD(accountcode);
- AST_STRING_FIELD(mohinterpret);
- AST_STRING_FIELD(mohsuggest);
- AST_STRING_FIELD(inkeys); /*!< Key(s) this user can use to authenticate to us */
- AST_STRING_FIELD(language);
- AST_STRING_FIELD(cid_num);
- AST_STRING_FIELD(cid_name);
- );
-
- int authmethods;
- int encmethods;
- int amaflags;
- int adsi;
- unsigned int flags;
- int capability;
- int maxauthreq; /*!< Maximum allowed outstanding AUTHREQs */
- int curauthreq; /*!< Current number of outstanding AUTHREQs */
- struct ast_codec_pref prefs;
- struct ast_ha *ha;
- struct iax2_context *contexts;
- struct ast_variable *vars;
- enum calltoken_peer_enum calltoken_required; /*!< Is calltoken validation required or not, can be YES, NO, or AUTO */
- };
- struct iax2_peer {
- AST_DECLARE_STRING_FIELDS(
- AST_STRING_FIELD(name);
- AST_STRING_FIELD(username);
- AST_STRING_FIELD(secret);
- AST_STRING_FIELD(dbsecret);
- AST_STRING_FIELD(outkey); /*!< What key we use to talk to this peer */
- AST_STRING_FIELD(regexten); /*!< Extension to register (if regcontext is used) */
- AST_STRING_FIELD(context); /*!< For transfers only */
- AST_STRING_FIELD(peercontext); /*!< Context to pass to peer */
- AST_STRING_FIELD(mailbox); /*!< Mailbox */
- AST_STRING_FIELD(mohinterpret);
- AST_STRING_FIELD(mohsuggest);
- AST_STRING_FIELD(inkeys); /*!< Key(s) this peer can use to authenticate to us */
- /* Suggested caller id if registering */
- AST_STRING_FIELD(cid_num); /*!< Default context (for transfer really) */
- AST_STRING_FIELD(cid_name); /*!< Default context (for transfer really) */
- AST_STRING_FIELD(zonetag); /*!< Time Zone */
- );
- struct ast_codec_pref prefs;
- struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager */
- struct sockaddr_in addr;
- int formats;
- int sockfd; /*!< Socket to use for transmission */
- struct in_addr mask;
- int adsi;
- unsigned int flags;
- /* Dynamic Registration fields */
- struct sockaddr_in defaddr; /*!< Default address if there is one */
- int authmethods; /*!< Authentication methods (IAX_AUTH_*) */
- int encmethods; /*!< Encryption methods (IAX_ENCRYPT_*) */
- int expire; /*!< Schedule entry for expiry */
- int expiry; /*!< How soon to expire */
- int capability; /*!< Capability */
- /* Qualification */
- int callno; /*!< Call number of POKE request */
- int pokeexpire; /*!< Scheduled qualification-related task (ie iax2_poke_peer_s or iax2_poke_noanswer) */
- int lastms; /*!< How long last response took (in ms), or -1 for no response */
- int maxms; /*!< Max ms we will accept for the host to be up, 0 to not monitor */
- int pokefreqok; /*!< How often to check if the host is up */
- int pokefreqnotok; /*!< How often to check when the host has been determined to be down */
- int historicms; /*!< How long recent average responses took */
- int smoothing; /*!< Sample over how many units to determine historic ms */
- uint16_t maxcallno; /*!< Max call number limit for this peer. Set on registration */
- struct ast_event_sub *mwi_event_sub;
- struct ast_ha *ha;
- enum calltoken_peer_enum calltoken_required; /*!< Is calltoken validation required or not, can be YES, NO, or AUTO */
- };
- #define IAX2_TRUNK_PREFACE (sizeof(struct iax_frame) + sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr))
- struct iax2_trunk_peer {
- ast_mutex_t lock;
- int sockfd;
- struct sockaddr_in addr;
- struct timeval txtrunktime; /*!< Transmit trunktime */
- struct timeval rxtrunktime; /*!< Receive trunktime */
- struct timeval lasttxtime; /*!< Last transmitted trunktime */
- struct timeval trunkact; /*!< Last trunk activity */
- unsigned int lastsent; /*!< Last sent time */
- /* Trunk data and length */
- unsigned char *trunkdata;
- unsigned int trunkdatalen;
- unsigned int trunkdataalloc;
- int trunkmaxmtu;
- int trunkerror;
- int calls;
- AST_LIST_ENTRY(iax2_trunk_peer) list;
- };
- static AST_LIST_HEAD_STATIC(tpeers, iax2_trunk_peer);
- struct iax_firmware {
- AST_LIST_ENTRY(iax_firmware) list;
- int fd;
- int mmaplen;
- int dead;
- struct ast_iax2_firmware_header *fwh;
- unsigned char *buf;
- };
- enum iax_reg_state {
- REG_STATE_UNREGISTERED = 0,
- REG_STATE_REGSENT,
- REG_STATE_AUTHSENT,
- REG_STATE_REGISTERED,
- REG_STATE_REJECTED,
- REG_STATE_TIMEOUT,
- REG_STATE_NOAUTH
- };
- enum iax_transfer_state {
- TRANSFER_NONE = 0,
- TRANSFER_BEGIN,
- TRANSFER_READY,
- TRANSFER_RELEASED,
- TRANSFER_PASSTHROUGH,
- TRANSFER_MBEGIN,
- TRANSFER_MREADY,
- TRANSFER_MRELEASED,
- TRANSFER_MPASSTHROUGH,
- TRANSFER_MEDIA,
- TRANSFER_MEDIAPASS
- };
- struct iax2_registry {
- struct sockaddr_in addr; /*!< Who we connect to for registration purposes */
- char username[80];
- char secret[80]; /*!< Password or key name in []'s */
- int expire; /*!< Sched ID of expiration */
- int refresh; /*!< How often to refresh */
- enum iax_reg_state regstate;
- int messages; /*!< Message count, low 8 bits = new, high 8 bits = old */
- int callno; /*!< Associated call number if applicable */
- struct sockaddr_in us; /*!< Who the server thinks we are */
- struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager */
- AST_LIST_ENTRY(iax2_registry) entry;
- };
- static AST_LIST_HEAD_STATIC(registrations, iax2_registry);
- /* Don't retry more frequently than every 10 ms, or less frequently than every 5 seconds */
- #define MIN_RETRY_TIME 100
- #define MAX_RETRY_TIME 10000
- #define MAX_JITTER_BUFFER 50
- #define MIN_JITTER_BUFFER 10
- #define DEFAULT_TRUNKDATA 640 * 10 /*!< 40ms, uncompressed linear * 10 channels */
- #define MAX_TIMESTAMP_SKEW 160 /*!< maximum difference between actual and predicted ts for sending */
- /* If consecutive voice frame timestamps jump by more than this many milliseconds, then jitter buffer will resync */
- #define TS_GAP_FOR_JB_RESYNC 5000
- /* used for first_iax_message and last_iax_message. If this bit is set it was TX, else RX */
- #define MARK_IAX_SUBCLASS_TX 0x8000
- static int iaxthreadcount = DEFAULT_THREAD_COUNT;
- static int iaxmaxthreadcount = DEFAULT_MAX_THREAD_COUNT;
- static int iaxdynamicthreadcount = 0;
- static int iaxdynamicthreadnum = 0;
- static int iaxactivethreadcount = 0;
- struct iax_rr {
- int jitter;
- int losspct;
- int losscnt;
- int packets;
- int delay;
- int dropped;
- int ooo;
- };
- struct iax2_pvt_ref;
- struct chan_iax2_pvt {
- /*! Socket to send/receive on for this call */
- int sockfd;
- /*! Last received voice format */
- int voiceformat;
- /*! Last received video format */
- int videoformat;
- /*! Last sent voice format */
- int svoiceformat;
- /*! Last sent video format */
- int svideoformat;
- /*! What we are capable of sending */
- int capability;
- /*! Last received timestamp */
- unsigned int last;
- /*! Last sent timestamp - never send the same timestamp twice in a single call */
- unsigned int lastsent;
- /*! Timestamp of the last video frame sent */
- unsigned int lastvsent;
- /*! Next outgoing timestamp if everything is good */
- unsigned int nextpred;
- /*! iax frame subclass that began iax2_pvt entry. 0x8000 bit is set on TX */
- int first_iax_message;
- /*! Last iax frame subclass sent or received for a iax2_pvt. 0x8000 bit is set on TX */
- int last_iax_message;
- /*! True if the last voice we transmitted was not silence/CNG */
- unsigned int notsilenttx:1;
- /*! Ping time */
- unsigned int pingtime;
- /*! Max time for initial response */
- int maxtime;
- /*! Peer Address */
- struct sockaddr_in addr;
- /*! Actual used codec preferences */
- struct ast_codec_pref prefs;
- /*! Requested codec preferences */
- struct ast_codec_pref rprefs;
- /*! Our call number */
- unsigned short callno;
- /*! Our callno_entry entry */
- struct callno_entry *callno_entry;
- /*! Peer callno */
- unsigned short peercallno;
- /*! Negotiated format, this is only used to remember what format was
- chosen for an unauthenticated call so that the channel can get
- created later using the right format */
- int chosenformat;
- /*! Peer selected format */
- int peerformat;
- /*! Peer capability */
- int peercapability;
- /*! timeval that we base our transmission on */
- struct timeval offset;
- /*! timeval that we base our delivery on */
- struct timeval rxcore;
- /*! The jitterbuffer */
- jitterbuf *jb;
- /*! active jb read scheduler id */
- int jbid;
- /*! LAG */
- int lag;
- /*! Error, as discovered by the manager */
- int error;
- /*! Owner if we have one */
- struct ast_channel *owner;
- /*! What's our state? */
- struct ast_flags state;
- /*! Expiry (optional) */
- int expiry;
- /*! Next outgoing sequence number */
- unsigned char oseqno;
- /*! Next sequence number they have not yet acknowledged */
- unsigned char rseqno;
- /*! Next incoming sequence number */
- unsigned char iseqno;
- /*! Last incoming sequence number we have acknowledged */
- unsigned char aseqno;
- AST_DECLARE_STRING_FIELDS(
- /*! Peer name */
- AST_STRING_FIELD(peer);
- /*! Default Context */
- AST_STRING_FIELD(context);
- /*! Caller ID if available */
- AST_STRING_FIELD(cid_num);
- AST_STRING_FIELD(cid_name);
- /*! Hidden Caller ID (i.e. ANI) if appropriate */
- AST_STRING_FIELD(ani);
- /*! DNID */
- AST_STRING_FIELD(dnid);
- /*! RDNIS */
- AST_STRING_FIELD(rdnis);
- /*! Requested Extension */
- AST_STRING_FIELD(exten);
- /*! Expected Username */
- AST_STRING_FIELD(username);
- /*! Expected Secret */
- AST_STRING_FIELD(secret);
- /*! MD5 challenge */
- AST_STRING_FIELD(challenge);
- /*! Public keys permitted keys for incoming authentication */
- AST_STRING_FIELD(inkeys);
- /*! Private key for outgoing authentication */
- AST_STRING_FIELD(outkey);
- /*! Preferred language */
- AST_STRING_FIELD(language);
- /*! Hostname/peername for naming purposes */
- AST_STRING_FIELD(host);
- AST_STRING_FIELD(dproot);
- AST_STRING_FIELD(accountcode);
- AST_STRING_FIELD(mohinterpret);
- AST_STRING_FIELD(mohsuggest);
- /*! received OSP token */
- AST_STRING_FIELD(osptoken);
- );
- /*! AUTHREJ all AUTHREP frames */
- int authrej;
- /*! permitted authentication methods */
- int authmethods;
- /*! permitted encryption methods */
- int encmethods;
- /*! Encryption AES-128 Key */
- ast_aes_encrypt_key ecx;
- /*! Decryption AES-128 Key corresponding to ecx */
- ast_aes_decrypt_key mydcx;
- /*! Decryption AES-128 Key used to decrypt peer frames */
- ast_aes_decrypt_key dcx;
- /*! 32 bytes of semi-random data */
- unsigned char semirand[32];
- /*! Associated registry */
- struct iax2_registry *reg;
- /*! Associated peer for poking */
- struct iax2_peer *peerpoke;
- /*! IAX_ flags */
- unsigned int flags;
- int adsi;
- /*! Transferring status */
- enum iax_transfer_state transferring;
- /*! Transfer identifier */
- int transferid;
- /*! Who we are IAX transferring to */
- struct sockaddr_in transfer;
- /*! What's the new call number for the transfer */
- unsigned short transfercallno;
- /*! Transfer encrypt AES-128 Key */
- ast_aes_encrypt_key tdcx;
- /*! Status of knowledge of peer ADSI capability */
- int peeradsicpe;
- /*! Who we are bridged to */
- unsigned short bridgecallno;
-
- int pingid; /*!< Transmit PING request */
- int lagid; /*!< Retransmit lag request */
- int autoid; /*!< Auto hangup for Dialplan requestor */
- int authid; /*!< Authentication rejection ID */
- int authfail; /*!< Reason to report failure */
- int initid; /*!< Initial peer auto-congest ID (based on qualified peers) */
- int calling_ton;
- int calling_tns;
- int calling_pres;
- int amaflags;
- AST_LIST_HEAD_NOLOCK(, iax2_dpcache) dpentries;
- /*! variables inherited from the user definition */
- struct ast_variable *vars;
- /*! variables transmitted in a NEW packet */
- struct ast_variable *iaxvars;
- /*! last received remote rr */
- struct iax_rr remote_rr;
- /*! Current base time: (just for stats) */
- int min;
- /*! Dropped frame count: (just for stats) */
- int frames_dropped;
- /*! received frame count: (just for stats) */
- int frames_received;
- /*! num bytes used for calltoken ie, even an empty ie should contain 2 */
- unsigned char calltoken_ie_len;
- /*! hold all signaling frames from the pbx thread until we have a destination callno */
- char hold_signaling;
- /*! frame queue for signaling frames from pbx thread waiting for destination callno */
- AST_LIST_HEAD_NOLOCK(signaling_queue, signaling_queue_entry) signaling_queue;
- };
- struct signaling_queue_entry {
- struct ast_frame f;
- AST_LIST_ENTRY(signaling_queue_entry) next;
- };
- /*! table of available call numbers */
- static struct ao2_container *callno_pool;
- /*! table of available trunk call numbers */
- static struct ao2_container *callno_pool_trunk;
- static const unsigned int CALLNO_POOL_BUCKETS = 2699;
- /*!
- * \brief a list of frames that may need to be retransmitted
- *
- * \note The contents of this list do not need to be explicitly destroyed
- * on module unload. This is because all active calls are destroyed, and
- * all frames in this queue will get destroyed as a part of that process.
- */
- static AST_LIST_HEAD_STATIC(frame_queue, iax_frame);
- static int randomcalltokendata;
- static const time_t MAX_CALLTOKEN_DELAY = 10;
- /*!
- * This module will get much higher performance when doing a lot of
- * user and peer lookups if the number of buckets is increased from 1.
- * However, to maintain old behavior for Asterisk 1.4, these are set to
- * 1 by default. When using multiple buckets, search order through these
- * containers is considered random, so you will not be able to depend on
- * the order the entires are specified in iax.conf for matching order. */
- #ifdef LOW_MEMORY
- #define MAX_PEER_BUCKETS 17
- #else
- #define MAX_PEER_BUCKETS 563
- #endif
- static struct ao2_container *peers;
- #define MAX_USER_BUCKETS MAX_PEER_BUCKETS
- static struct ao2_container *users;
- /*! Table containing peercnt objects for every ip address consuming a callno */
- static struct ao2_container *peercnts;
- /*! Table containing custom callno limit rules for a range of ip addresses. */
- static struct ao2_container *callno_limits;
- /*! Table containing ip addresses not requiring calltoken validation */
- static struct ao2_container *calltoken_ignores;
- static uint16_t DEFAULT_MAXCALLNO_LIMIT = 2048;
- static uint16_t DEFAULT_MAXCALLNO_LIMIT_NONVAL = 8192;
- static uint16_t global_maxcallno;
- /*! Total num of call numbers allowed to be allocated without calltoken validation */
- static uint16_t global_maxcallno_nonval;
- static uint16_t total_nonval_callno_used = 0;
- /*! peer connection private, keeps track of all the call numbers
- * consumed by a single ip address */
- struct peercnt {
- /*! ip address consuming call numbers */
- unsigned long addr;
- /*! Number of call numbers currently used by this ip address */
- uint16_t cur;
- /*! Max call numbers allowed for this ip address */
- uint16_t limit;
- /*! Specifies whether limit is set by a registration or not, if so normal
- * limit setting rules do not apply to this address. */
- unsigned char reg;
- };
- /*! used by both callno_limits and calltoken_ignores containers */
- struct addr_range {
- /*! ip address range for custom callno limit rule */
- struct ast_ha ha;
- /*! callno limit for this ip address range, only used in callno_limits container */
- uint16_t limit;
- /*! delete me marker for reloads */
- unsigned char delme;
- };
- struct callno_entry {
- /*! callno used for this entry */
- uint16_t callno;
- /*! was this callno calltoken validated or not */
- unsigned char validated;
- };
- static AST_LIST_HEAD_STATIC(firmwares, iax_firmware);
- enum {
- /*! Extension exists */
- CACHE_FLAG_EXISTS = (1 << 0),
- /*! Extension is nonexistent */
- CACHE_FLAG_NONEXISTENT = (1 << 1),
- /*! Extension can exist */
- CACHE_FLAG_CANEXIST = (1 << 2),
- /*! Waiting to hear back response */
- CACHE_FLAG_PENDING = (1 << 3),
- /*! Timed out */
- CACHE_FLAG_TIMEOUT = (1 << 4),
- /*! Request transmitted */
- CACHE_FLAG_TRANSMITTED = (1 << 5),
- /*! Timeout */
- CACHE_FLAG_UNKNOWN = (1 << 6),
- /*! Matchmore */
- CACHE_FLAG_MATCHMORE = (1 << 7),
- };
- struct iax2_dpcache {
- char peercontext[AST_MAX_CONTEXT];
- char exten[AST_MAX_EXTENSION];
- struct timeval orig;
- struct timeval expiry;
- int flags;
- unsigned short callno;
- int waiters[256];
- AST_LIST_ENTRY(iax2_dpcache) cache_list;
- AST_LIST_ENTRY(iax2_dpcache) peer_list;
- };
- static AST_LIST_HEAD_STATIC(dpcache, iax2_dpcache);
- static void reg_source_db(struct iax2_peer *p);
- static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in *sin);
- static struct iax2_user *realtime_user(const char *username, struct sockaddr_in *sin);
- static int ast_cli_netstats(struct mansession *s, int fd, int limit_fmt);
- static char *complete_iax2_show_peer(const char *line, const char *word, int pos, int state);
- static char *complete_iax2_unregister(const char *line, const char *word, int pos, int state);
- enum iax2_thread_iostate {
- IAX_IOSTATE_IDLE,
- IAX_IOSTATE_READY,
- IAX_IOSTATE_PROCESSING,
- IAX_IOSTATE_SCHEDREADY,
- };
- enum iax2_thread_type {
- IAX_THREAD_TYPE_POOL,
- IAX_THREAD_TYPE_DYNAMIC,
- };
- struct iax2_pkt_buf {
- AST_LIST_ENTRY(iax2_pkt_buf) entry;
- size_t len;
- unsigned char buf[1];
- };
- struct iax2_thread {
- AST_LIST_ENTRY(iax2_thread) list;
- enum iax2_thread_type type;
- enum iax2_thread_iostate iostate;
- #ifdef SCHED_MULTITHREADED
- void (*schedfunc)(const void *);
- const void *scheddata;
- #endif
- #ifdef DEBUG_SCHED_MULTITHREAD
- char curfunc[80];
- #endif
- int actions;
- pthread_t threadid;
- int threadnum;
- struct sockaddr_in iosin;
- unsigned char readbuf[4096];
- unsigned char *buf;
- ssize_t buf_len;
- size_t buf_size;
- int iofd;
- time_t checktime;
- ast_mutex_t lock;
- ast_cond_t cond;
- unsigned int ready_for_signal:1;
- /*! if this thread is processing a full frame,
- some information about that frame will be stored
- here, so we can avoid dispatching any more full
- frames for that callno to other threads */
- struct {
- unsigned short callno;
- struct sockaddr_in sin;
- unsigned char type;
- unsigned char csub;
- } ffinfo;
- /*! Queued up full frames for processing. If more full frames arrive for
- * a call which this thread is already processing a full frame for, they
- * are queued up here. */
- AST_LIST_HEAD_NOLOCK(, iax2_pkt_buf) full_frames;
- };
- /* Thread lists */
- static AST_LIST_HEAD_STATIC(idle_list, iax2_thread);
- static AST_LIST_HEAD_STATIC(active_list, iax2_thread);
- static AST_LIST_HEAD_STATIC(dynamic_list, iax2_thread);
- static void *iax2_process_thread(void *data);
- static void iax2_destroy(int callno);
- static void signal_condition(ast_mutex_t *lock, ast_cond_t *cond)
- {
- ast_mutex_lock(lock);
- ast_cond_signal(cond);
- ast_mutex_unlock(lock);
- }
- static void iax_debug_output(const char *data)
- {
- if (iaxdebug)
- ast_verbose("%s", data);
- }
- static void iax_error_output(const char *data)
- {
- ast_log(LOG_WARNING, "%s", data);
- }
- static void __attribute__((format (printf, 1, 2))) jb_error_output(const char *fmt, ...)
- {
- va_list args;
- char buf[1024];
- va_start(args, fmt);
- vsnprintf(buf, sizeof(buf), fmt, args);
- va_end(args);
- ast_log(LOG_ERROR, "%s", buf);
- }
- static void __attribute__((format (printf, 1, 2))) jb_warning_output(const char *fmt, ...)
- {
- va_list args;
- char buf[1024];
- va_start(args, fmt);
- vsnprintf(buf, sizeof(buf), fmt, args);
- va_end(args);
- ast_log(LOG_WARNING, "%s", buf);
- }
- static void __attribute__((format (printf, 1, 2))) jb_debug_output(const char *fmt, ...)
- {
- va_list args;
- char buf[1024];
- va_start(args, fmt);
- vsnprintf(buf, sizeof(buf), fmt, args);
- va_end(args);
- ast_verbose("%s", buf);
- }
- /*!
- * \brief an array of iax2 pvt structures
- *
- * The container for active chan_iax2_pvt structures is implemented as an
- * array for extremely quick direct access to the correct pvt structure
- * based on the local call number. The local call number is used as the
- * index into the array where the associated pvt structure is stored.
- */
- static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS + 1];
- /*!
- * \brief Another container of iax2_pvt structures
- *
- * Active IAX2 pvt structs are also stored in this container, if they are a part
- * of an active call where we know the remote side's call number. The reason
- * for this is that incoming media frames do not contain our call number. So,
- * instead of having to iterate the entire iaxs array, we use this container to
- * look up calls where the remote side is using a given call number.
- */
- static struct ao2_container *iax_peercallno_pvts;
- /*!
- * \brief chan_iax2_pvt structure locks
- *
- * These locks are used when accessing a pvt structure in the iaxs array.
- * The index used here is the same as used in the iaxs array. It is the
- * local call number for the associated pvt struct.
- */
- static ast_mutex_t iaxsl[ARRAY_LEN(iaxs)];
- /*!
- * * \brief Another container of iax2_pvt structures
- *
- * Active IAX2 pvt stucts used during transfering a call are stored here.
- */
- static struct ao2_container *iax_transfercallno_pvts;
- /* Flag to use with trunk calls, keeping these calls high up. It halves our effective use
- but keeps the division between trunked and non-trunked better. */
- #define TRUNK_CALL_START IAX_MAX_CALLS / 2
- static int maxtrunkcall = TRUNK_CALL_START;
- static int maxnontrunkcall = 1;
- static enum ast_bridge_result iax2_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms);
- static int expire_registry(const void *data);
- static int iax2_answer(struct ast_channel *c);
- static int iax2_call(struct ast_channel *c, char *dest, int timeout);
- static int iax2_devicestate(void *data);
- static int iax2_digit_begin(struct ast_channel *c, char digit);
- static int iax2_digit_end(struct ast_channel *c, char digit, unsigned int duration);
- static int iax2_do_register(struct iax2_registry *reg);
- static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan);
- static int iax2_hangup(struct ast_channel *c);
- static int iax2_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen);
- static int iax2_poke_peer(struct iax2_peer *peer, int heldcall);
- static int iax2_provision(struct sockaddr_in *end, int sockfd, char *dest, const char *template, int force);
- static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final);
- static int iax2_sendhtml(struct ast_channel *c, int subclass, const char *data, int datalen);
- static int iax2_sendimage(struct ast_channel *c, struct ast_frame *img);
- static int iax2_sendtext(struct ast_channel *c, const char *text);
- static int iax2_setoption(struct ast_channel *c, int option, void *data, int datalen);
- static int iax2_transfer(struct ast_channel *c, const char *dest);
- static int iax2_write(struct ast_channel *c, struct ast_frame *f);
- static int send_trunk(struct iax2_trunk_peer *tpeer, struct timeval *now);
- static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
- static int send_command_final(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
- static int send_command_immediate(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
- static int send_command_locked(unsigned short callno, char, int, unsigned int, const unsigned char *, int, int);
- static int send_command_transfer(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int);
- static struct ast_channel *iax2_request(const char *type, int format, void *data, int *cause);
- static struct ast_frame *iax2_read(struct ast_channel *c);
- static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly);
- static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly);
- static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, time_t regtime);
- static void *iax2_dup_variable_datastore(void *);
- static void prune_peers(void);
- static void prune_users(void);
- static void iax2_free_variable_datastore(void *);
- static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen);
- static int decode_frame(ast_aes_decrypt_key *dcx, struct ast_iax2_full_hdr *fh, struct ast_frame *f, int *datalen);
- static int encrypt_frame(ast_aes_encrypt_key *ecx, struct ast_iax2_full_hdr *fh, unsigned char *poo, int *datalen);
- static void build_ecx_key(const unsigned char *digest, struct chan_iax2_pvt *pvt);
- static void build_rand_pad(unsigned char *buf, ssize_t len);
- static struct callno_entry *get_unused_callno(int trunk, int validated);
- static int replace_callno(const void *obj);
- static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry);
- static const struct ast_channel_tech iax2_tech = {
- .type = "IAX2",
- .description = tdesc,
- .capabilities = IAX_CAPABILITY_FULLBANDWIDTH,
- .properties = AST_CHAN_TP_WANTSJITTER,
- .requester = iax2_request,
- .devicestate = iax2_devicestate,
- .send_digit_begin = iax2_digit_begin,
- .send_digit_end = iax2_digit_end,
- .send_text = iax2_sendtext,
- .send_image = iax2_sendimage,
- .send_html = iax2_sendhtml,
- .call = iax2_call,
- .hangup = iax2_hangup,
- .answer = iax2_answer,
- .read = iax2_read,
- .write = iax2_write,
- .write_video = iax2_write,
- .indicate = iax2_indicate,
- .setoption = iax2_setoption,
- .bridge = iax2_bridge,
- .transfer = iax2_transfer,
- .fixup = iax2_fixup,
- .func_channel_read = acf_channel_read,
- };
- static void mwi_event_cb(const struct ast_event *event, void *userdata)
- {
- /* The MWI subscriptions exist just so the core knows we care about those
- * mailboxes. However, we just grab the events out of the cache when it
- * is time to send MWI, since it is only sent with a REGACK. */
- }
- /*! \brief Send manager event at call setup to link between Asterisk channel name
- and IAX2 call identifiers */
- static void iax2_ami_channelupdate(struct chan_iax2_pvt *pvt)
- {
- manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
- "Channel: %s\r\nChanneltype: IAX2\r\nIAX2-callno-local: %d\r\nIAX2-callno-remote: %d\r\nIAX2-peer: %s\r\n",
- pvt->owner ? pvt->owner->name : "",
- pvt->callno, pvt->peercallno, pvt->peer ? pvt->peer : "");
- }
- static struct ast_datastore_info iax2_variable_datastore_info = {
- .type = "IAX2_VARIABLE",
- .duplicate = iax2_dup_variable_datastore,
- .destroy = iax2_free_variable_datastore,
- };
- static void *iax2_dup_variable_datastore(void *old)
- {
- AST_LIST_HEAD(, ast_var_t) *oldlist = old, *newlist;
- struct ast_var_t *oldvar, *newvar;
- newlist = ast_calloc(sizeof(*newlist), 1);
- if (!newlist) {
- ast_log(LOG_ERROR, "Unable to duplicate iax2 variables\n");
- return NULL;
- }
- AST_LIST_HEAD_INIT(newlist);
- AST_LIST_LOCK(oldlist);
- AST_LIST_TRAVERSE(oldlist, oldvar, entries) {
- newvar = ast_var_assign(ast_var_name(oldvar), ast_var_value(oldvar));
- if (newvar)
- AST_LIST_INSERT_TAIL(newlist, newvar, entries);
- else
- ast_log(LOG_ERROR, "Unable to duplicate iax2 variable '%s'\n", ast_var_name(oldvar));
- }
- AST_LIST_UNLOCK(oldlist);
- return newlist;
- }
- static void iax2_free_variable_datastore(void *old)
- {
- AST_LIST_HEAD(, ast_var_t) *oldlist = old;
- struct ast_var_t *oldvar;
- AST_LIST_LOCK(oldlist);
- while ((oldvar = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
- ast_free(oldvar);
- }
- AST_LIST_UNLOCK(oldlist);
- AST_LIST_HEAD_DESTROY(oldlist);
- ast_free(oldlist);
- }
- /* WARNING: insert_idle_thread should only ever be called within the
- * context of an iax2_process_thread() thread.
- */
- static void insert_idle_thread(struct iax2_thread *thread)
- {
- if (thread->type == IAX_THREAD_TYPE_DYNAMIC) {
- AST_LIST_LOCK(&dynamic_list);
- AST_LIST_INSERT_TAIL(&dynamic_list, thread, list);
- AST_LIST_UNLOCK(&dynamic_list);
- } else {
- AST_LIST_LOCK(&idle_list);
- AST_LIST_INSERT_TAIL(&idle_list, thread, list);
- AST_LIST_UNLOCK(&idle_list);
- }
- return;
- }
- static struct iax2_thread *find_idle_thread(void)
- {
- struct iax2_thread *thread = NULL;
- /* Pop the head of the idle list off */
- AST_LIST_LOCK(&idle_list);
- thread = AST_LIST_REMOVE_HEAD(&idle_list, list);
- AST_LIST_UNLOCK(&idle_list);
- /* If we popped a thread off the idle list, just return it */
- if (thread) {
- memset(&thread->ffinfo, 0, sizeof(thread->ffinfo));
- return thread;
- }
- /* Pop the head of the dynamic list off */
- AST_LIST_LOCK(&dynamic_list);
- thread = AST_LIST_REMOVE_HEAD(&dynamic_list, list);
- AST_LIST_UNLOCK(&dynamic_list);
- /* If we popped a thread off the dynamic list, just return it */
- if (thread) {
- memset(&thread->ffinfo, 0, sizeof(thread->ffinfo));
- return thread;
- }
- /* If we can't create a new dynamic thread for any reason, return no thread at all */
- if (iaxdynamicthreadcount >= iaxmaxthreadcount || !(thread = ast_calloc(1, sizeof(*thread))))
- return NULL;
- /* Set default values */
- ast_atomic_fetchadd_int(&iaxdynamicthreadcount, 1);
- thread->threadnum = ast_atomic_fetchadd_int(&iaxdynamicthreadnum, 1);
- thread->type = IAX_THREAD_TYPE_DYNAMIC;
- /* Initialize lock and condition */
- ast_mutex_init(&thread->lock);
- ast_cond_init(&thread->cond, NULL);
- /* Create thread and send it on it's way */
- if (ast_pthread_create_detached_background(&thread->threadid, NULL, iax2_process_thread, thread)) {
- ast_cond_destroy(&thread->cond);
- ast_mutex_destroy(&thread->lock);
- ast_free(thread);
- return NULL;
- }
- /* this thread is not processing a full frame (since it is idle),
- so ensure that the field for the full frame call number is empty */
- memset(&thread->ffinfo, 0, sizeof(thread->ffinfo));
- /* Wait for the thread to be ready before returning it to the caller */
- while (!thread->ready_for_signal)
- usleep(1);
- return thread;
- }
- #ifdef SCHED_MULTITHREADED
- static int __schedule_action(void (*func)(const void *data), const void *data, const char *funcname)
- {
- struct iax2_thread *thread = NULL;
- static time_t lasterror;
- static time_t t;
- thread = find_idle_thread();
- if (thread != NULL) {
- thread->schedfunc = func;
- thread->scheddata = data;
- thread->iostate = IAX_IOSTATE_SCHEDREADY;
- #ifdef DEBUG_SCHED_MULTITHREAD
- ast_copy_string(thread->curfunc, funcname, sizeof(thread->curfunc));
- #endif
- signal_condition(&thread->lock, &thread->cond);
- return 0;
- }
- time(&t);
- if (t != lasterror)
- ast_debug(1, "Out of idle IAX2 threads for scheduling!\n");
- lasterror = t;
- return -1;
- }
- #define schedule_action(func, data) __schedule_action(func, data, __PRETTY_FUNCTION__)
- #endif
- static int iax2_sched_replace(int id, struct sched_context *con, int when, ast_sched_cb callback, const void *data)
- {
- AST_SCHED_REPLACE(id, con, when, callback, data);
- signal_condition(&sched_lock, &sched_cond);
- return id;
- }
- static int iax2_sched_add(struct sched_context *con, int when, ast_sched_cb callback, const void *data)
- {
- int res;
- res = ast_sched_add(con, when, callback, data);
- signal_condition(&sched_lock, &sched_cond);
- return res;
- }
- static int send_ping(const void *data);
- static void __send_ping(const void *data)
- {
- int callno = (long) data;
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno]) {
- if (iaxs[callno]->peercallno) {
- send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1);
- iaxs[callno]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, data);
- } else {
- /* I am the schedule, so I'm allowed to do this */
- iaxs[callno]->pingid = -1;
- }
- } else {
- ast_debug(1, "I was supposed to send a PING with callno %d, but no such call exists.\n", callno);
- }
- ast_mutex_unlock(&iaxsl[callno]);
- }
- static int send_ping(const void *data)
- {
- #ifdef SCHED_MULTITHREADED
- if (schedule_action(__send_ping, data))
- #endif
- __send_ping(data);
- return 0;
- }
- static int get_encrypt_methods(const char *s)
- {
- int e;
- if (!strcasecmp(s, "aes128"))
- e = IAX_ENCRYPT_AES128;
- else if (ast_true(s))
- e = IAX_ENCRYPT_AES128;
- else
- e = 0;
- return e;
- }
- static int send_lagrq(const void *data);
- static void __send_lagrq(const void *data)
- {
- int callno = (long) data;
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno]) {
- if (iaxs[callno]->peercallno) {
- send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
- iaxs[callno]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, data);
- } else {
- /* I am the schedule, so I'm allowed to do this */
- iaxs[callno]->lagid = -1;
- }
- } else {
- ast_debug(1, "I was supposed to send a LAGRQ with callno %d, but no such call exists.\n", callno);
- }
- ast_mutex_unlock(&iaxsl[callno]);
- }
- static int send_lagrq(const void *data)
- {
- #ifdef SCHED_MULTITHREADED
- if (schedule_action(__send_lagrq, data))
- #endif
- __send_lagrq(data);
-
- return 0;
- }
- static unsigned char compress_subclass(int subclass)
- {
- int x;
- int power=-1;
- /* If it's 128 or smaller, just return it */
- if (subclass < IAX_FLAG_SC_LOG)
- return subclass;
- /* Otherwise find its power */
- for (x = 0; x < IAX_MAX_SHIFT; x++) {
- if (subclass & (1 << x)) {
- if (power > -1) {
- ast_log(LOG_WARNING, "Can't compress subclass %d\n", subclass);
- return 0;
- } else
- power = x;
- }
- }
- return power | IAX_FLAG_SC_LOG;
- }
- static int uncompress_subclass(unsigned char csub)
- {
- /* If the SC_LOG flag is set, return 2^csub otherwise csub */
- if (csub & IAX_FLAG_SC_LOG) {
- /* special case for 'compressed' -1 */
- if (csub == 0xff)
- return -1;
- else
- return 1 << (csub & ~IAX_FLAG_SC_LOG & IAX_MAX_SHIFT);
- }
- else
- return csub;
- }
- /*!
- * \note The only member of the peer passed here guaranteed to be set is the name field
- */
- static int peer_hash_cb(const void *obj, const int flags)
- {
- const struct iax2_peer *peer = obj;
- return ast_str_hash(peer->name);
- }
- /*!
- * \note The only member of the peer passed here guaranteed to be set is the name field
- */
- static int peer_cmp_cb(void *obj, void *arg, int flags)
- {
- struct iax2_peer *peer = obj, *peer2 = arg;
- return !strcmp(peer->name, peer2->name) ? CMP_MATCH : 0;
- }
- /*!
- * \note The only member of the user passed here guaranteed to be set is the name field
- */
- static int user_hash_cb(const void *obj, const int flags)
- {
- const struct iax2_user *user = obj;
- return ast_str_hash(user->name);
- }
- /*!
- * \note The only member of the user passed here guaranteed to be set is the name field
- */
- static int user_cmp_cb(void *obj, void *arg, int flags)
- {
- struct iax2_user *user = obj, *user2 = arg;
- return !strcmp(user->name, user2->name) ? CMP_MATCH : 0;
- }
- /*!
- * \note This funtion calls realtime_peer -> reg_source_db -> iax2_poke_peer -> find_callno,
- * so do not call it with a pvt lock held.
- */
- static struct iax2_peer *find_peer(const char *name, int realtime)
- {
- struct iax2_peer *peer = NULL;
- struct iax2_peer tmp_peer = {
- .name = name,
- };
- peer = ao2_find(peers, &tmp_peer, OBJ_POINTER);
- /* Now go for realtime if applicable */
- if(!peer && realtime)
- peer = realtime_peer(name, NULL);
- return peer;
- }
- static struct iax2_peer *peer_ref(struct iax2_peer *peer)
- {
- ao2_ref(peer, +1);
- return peer;
- }
- static inline struct iax2_peer *peer_unref(struct iax2_peer *peer)
- {
- ao2_ref(peer, -1);
- return NULL;
- }
- static struct iax2_user *find_user(const char *name)
- {
- struct iax2_user tmp_user = {
- .name = name,
- };
- return ao2_find(users, &tmp_user, OBJ_POINTER);
- }
- static inline struct iax2_user *user_ref(struct iax2_user *user)
- {
- ao2_ref(user, +1);
- return user;
- }
- static inline struct iax2_user *user_unref(struct iax2_user *user)
- {
- ao2_ref(user, -1);
- return NULL;
- }
- static int iax2_getpeername(struct sockaddr_in sin, char *host, int len)
- {
- struct iax2_peer *peer = NULL;
- int res = 0;
- struct ao2_iterator i;
- i = ao2_iterator_init(peers, 0);
- while ((peer = ao2_iterator_next(&i))) {
- if ((peer->addr.sin_addr.s_addr == sin.sin_addr.s_addr) &&
- (peer->addr.sin_port == sin.sin_port)) {
- ast_copy_string(host, peer->name, len);
- peer_unref(peer);
- res = 1;
- break;
- }
- peer_unref(peer);
- }
- ao2_iterator_destroy(&i);
- if (!peer) {
- peer = realtime_peer(NULL, &sin);
- if (peer) {
- ast_copy_string(host, peer->name, len);
- peer_unref(peer);
- res = 1;
- }
- }
- return res;
- }
- static void iax2_destroy_helper(struct chan_iax2_pvt *pvt)
- {
- /* Decrement AUTHREQ count if needed */
- if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) {
- struct iax2_user *user;
- struct iax2_user tmp_user = {
- .name = pvt->username,
- };
- user = ao2_find(users, &tmp_user, OBJ_POINTER);
- if (user) {
- ast_atomic_fetchadd_int(&user->curauthreq, -1);
- user_unref(user);
- }
- ast_clear_flag(pvt, IAX_MAXAUTHREQ);
- }
- /* No more pings or lagrq's */
- AST_SCHED_DEL(sched, pvt->pingid);
- AST_SCHED_DEL(sched, pvt->lagid);
- AST_SCHED_DEL(sched, pvt->autoid);
- AST_SCHED_DEL(sched, pvt->authid);
- AST_SCHED_DEL(sched, pvt->initid);
- AST_SCHED_DEL(sched, pvt->jbid);
- }
- static void iax2_frame_free(struct iax_frame *fr)
- {
- AST_SCHED_DEL(sched, fr->retrans);
- iax_frame_free(fr);
- }
- static int scheduled_destroy(const void *vid)
- {
- unsigned short callno = PTR_TO_CALLNO(vid);
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno]) {
- if (option_debug) {
- ast_log(LOG_DEBUG, "Really destroying %d now...\n", callno);
- }
- iax2_destroy(callno);
- }
- ast_mutex_unlock(&iaxsl[callno]);
- return 0;
- }
- static void free_signaling_queue_entry(struct signaling_queue_entry *s)
- {
- ast_free(s->f.data);
- ast_free(s);
- }
- /*! \brief This function must be called once we are sure the other side has
- * given us a call number. All signaling is held here until that point. */
- static void send_signaling(struct chan_iax2_pvt *pvt)
- {
- struct signaling_queue_entry *s = NULL;
- while ((s = AST_LIST_REMOVE_HEAD(&pvt->signaling_queue, next))) {
- iax2_send(pvt, &s->f, 0, -1, 0, 0, 0);
- free_signaling_queue_entry(s);
- }
- pvt->hold_signaling = 0;
- }
- /*! \brief All frames other than that of type AST_FRAME_IAX must be held until
- * we have received a destination call number. */
- static int queue_signalling(struct chan_iax2_pvt *pvt, struct ast_frame *f)
- {
- struct signaling_queue_entry *new;
- if (f->frametype == AST_FRAME_IAX || !pvt->hold_signaling) {
- return 1; /* do not queue this frame */
- } else if (!(new = ast_calloc(1, sizeof(struct signaling_queue_entry)))) {
- return -1; /* out of memory */
- }
- memcpy(&new->f, f, sizeof(new->f)); /* copy ast_frame into our queue entry */
- if (new->f.datalen) { /* if there is data in this frame copy it over as well */
- if (!(new->f.data = ast_calloc(1, new->f.datalen))) {
- free_signaling_queue_entry(new);
- return -1;
- }
- memcpy(new->f.data, f->data, sizeof(*new->f.data));
- }
- AST_LIST_INSERT_TAIL(&pvt->signaling_queue, new, next);
- return 0;
- }
- static void pvt_destructor(void *obj)
- {
- struct chan_iax2_pvt *pvt = obj;
- struct iax_frame *cur = NULL;
- struct signaling_queue_entry *s = NULL;
- iax2_destroy_helper(pvt);
- sched_delay_remove(&pvt->addr, pvt->callno_entry);
- pvt->callno_entry = NULL;
- /* Already gone */
- ast_set_flag(pvt, IAX_ALREADYGONE);
- AST_LIST_LOCK(&frame_queue);
- AST_LIST_TRAVERSE(&frame_queue, cur, list) {
- /* Cancel any pending transmissions */
- if (cur->callno == pvt->callno) {
- cur->retries = -1;
- }
- }
- AST_LIST_UNLOCK(&frame_queue);
- while ((s = AST_LIST_REMOVE_HEAD(&pvt->signaling_queue, next))) {
- free_signaling_queue_entry(s);
- }
- if (pvt->reg) {
- pvt->reg->callno = 0;
- }
- if (!pvt->owner) {
- jb_frame frame;
- if (pvt->vars) {
- ast_variables_destroy(pvt->vars);
- pvt->vars = NULL;
- }
- while (jb_getall(pvt->jb, &frame) == JB_OK) {
- iax2_frame_free(frame.data);
- }
- jb_destroy(pvt->jb);
- ast_string_field_free_memory(pvt);
- }
- }
- static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, const char *host)
- {
- struct chan_iax2_pvt *tmp;
- jb_conf jbconf;
- if (!(tmp = ao2_alloc(sizeof(*tmp), pvt_destructor))) {
- return NULL;
- }
- if (ast_string_field_init(tmp, 32)) {
- ao2_ref(tmp, -1);
- tmp = NULL;
- return NULL;
- }
-
- tmp->prefs = prefs;
- tmp->pingid = -1;
- tmp->lagid = -1;
- tmp->autoid = -1;
- tmp->authid = -1;
- tmp->initid = -1;
- ast_string_field_set(tmp,exten, "s");
- ast_string_field_set(tmp,host, host);
- tmp->jb = jb_new();
- tmp->jbid = -1;
- jbconf.max_jitterbuf = maxjitterbuffer;
- jbconf.resync_threshold = resyncthreshold;
- jbconf.max_contig_interp = maxjitterinterps;
- jbconf.target_extra = jittertargetextra;
- jb_setconf(tmp->jb,&jbconf);
- AST_LIST_HEAD_INIT_NOLOCK(&tmp->dpentries);
- tmp->hold_signaling = 1;
- AST_LIST_HEAD_INIT_NOLOCK(&tmp->signaling_queue);
- return tmp;
- }
- static struct iax_frame *iaxfrdup2(struct iax_frame *fr)
- {
- struct iax_frame *new = iax_frame_new(DIRECTION_INGRESS, fr->af.datalen, fr->cacheable);
- if (new) {
- size_t afdatalen = new->afdatalen;
- memcpy(new, fr, sizeof(*new));
- iax_frame_wrap(new, &fr->af);
- new->afdatalen = afdatalen;
- new->data = NULL;
- new->datalen = 0;
- new->direction = DIRECTION_INGRESS;
- new->retrans = -1;
- }
- return new;
- }
- /* keep these defined in this order. They are used in find_callno to
- * determine whether or not a new call number should be allowed. */
- enum {
- /* do not allow a new call number, only search ones in use for match */
- NEW_PREVENT = 0,
- /* search for match first, then allow a new one to be allocated */
- NEW_ALLOW = 1,
- /* do not search for match, force a new call number */
- NEW_FORCE = 2,
- /* do not search for match, force a new call number. Signifies call number
- * has been calltoken validated */
- NEW_ALLOW_CALLTOKEN_VALIDATED = 3,
- };
- static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short dcallno, const struct chan_iax2_pvt *cur, int check_dcallno)
- {
- if ((cur->addr.sin_addr.s_addr == sin->sin_addr.s_addr) &&
- (cur->addr.sin_port == sin->sin_port)) {
- /* This is the main host */
- if ( (cur->peercallno == 0 || cur->peercallno == callno) &&
- (check_dcallno ? dcallno == cur->callno : 1) ) {
- /* That's us. Be sure we keep track of the peer call number */
- return 1;
- }
- }
- if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) &&
- (cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) {
- /* We're transferring */
- if ((dcallno == cur->callno) || (cur->transferring == TRANSFER_MEDIAPASS && cur->transfercallno == callno))
- return 1;
- }
- return 0;
- }
- static void update_max_trunk(void)
- {
- int max = TRUNK_CALL_START;
- int x;
- /* XXX Prolly don't need locks here XXX */
- for (x = TRUNK_CALL_START; x < ARRAY_LEN(iaxs) - 1; x++) {
- if (iaxs[x]) {
- max = x + 1;
- }
- }
- maxtrunkcall = max;
- if (iaxdebug)
- ast_debug(1, "New max trunk callno is %d\n", max);
- }
- static void update_max_nontrunk(void)
- {
- int max = 1;
- int x;
- /* XXX Prolly don't need locks here XXX */
- for (x=1;x<TRUNK_CALL_START - 1; x++) {
- if (iaxs[x])
- max = x + 1;
- }
- maxnontrunkcall = max;
- if (iaxdebug)
- ast_debug(1, "New max nontrunk callno is %d\n", max);
- }
- static int make_trunk(unsigned short callno, int locked)
- {
- int x;
- int res= 0;
- struct callno_entry *callno_entry;
- if (iaxs[callno]->oseqno) {
- ast_log(LOG_WARNING, "Can't make trunk once a call has started!\n");
- return -1;
- }
- if (callno & TRUNK_CALL_START) {
- ast_log(LOG_WARNING, "Call %d is already a trunk\n", callno);
- return -1;
- }
- if (!(callno_entry = get_unused_callno(1, iaxs[callno]->callno_entry->validated))) {
- ast_log(LOG_WARNING, "Unable to trunk call: Insufficient space\n");
- return -1;
- }
- x = callno_entry->callno;
- ast_mutex_lock(&iaxsl[x]);
- /*!
- * \note We delete these before switching the slot, because if
- * they fire in the meantime, they will generate a warning.
- */
- AST_SCHED_DEL(sched, iaxs[callno]->pingid);
- AST_SCHED_DEL(sched, iaxs[callno]->lagid);
- iaxs[x] = iaxs[callno];
- iaxs[x]->callno = x;
- /* since we copied over the pvt from a different callno, make sure the old entry is replaced
- * before assigning the new one */
- if (iaxs[x]->callno_entry) {
- iax2_sched_add(sched, MIN_REUSE_TIME * 1000, replace_callno, iaxs[x]->callno_entry);
- }
- iaxs[x]->callno_entry = callno_entry;
- iaxs[callno] = NULL;
- /* Update the two timers that should have been started */
- iaxs[x]->pingid = iax2_sched_add(sched,
- ping_time * 1000, send_ping, (void *)(long)x);
- iaxs[x]->lagid = iax2_sched_add(sched,
- lagrq_time * 1000, send_lagrq, (void *)(long)x);
- if (locked)
- ast_mutex_unlock(&iaxsl[callno]);
- res = x;
- if (!locked)
- ast_mutex_unlock(&iaxsl[x]);
- ast_debug(1, "Made call %d into trunk call %d\n", callno, x);
- /* We move this call from a non-trunked to a trunked call */
- update_max_trunk();
- update_max_nontrunk();
- return res;
- }
- static void store_by_transfercallno(struct chan_iax2_pvt *pvt)
- {
- if (!pvt->transfercallno) {
- ast_log(LOG_ERROR, "This should not be called without a transfer call number.\n");
- return;
- }
- ao2_link(iax_transfercallno_pvts, pvt);
- }
- static void remove_by_transfercallno(struct chan_iax2_pvt *pvt)
- {
- if (!pvt->transfercallno) {
- ast_log(LOG_ERROR, "This should not be called without a transfer call number.\n");
- return;
- }
- ao2_unlink(iax_transfercallno_pvts, pvt);
- }
- static void store_by_peercallno(struct chan_iax2_pvt *pvt)
- {
- if (!pvt->peercallno) {
- ast_log(LOG_ERROR, "This should not be called without a peer call number.\n");
- return;
- }
- ao2_link(iax_peercallno_pvts, pvt);
- }
- static void remove_by_peercallno(struct chan_iax2_pvt *pvt)
- {
- if (!pvt->peercallno) {
- ast_log(LOG_ERROR, "This should not be called without a peer call number.\n");
- return;
- }
- ao2_unlink(iax_peercallno_pvts, pvt);
- }
- static int addr_range_delme_cb(void *obj, void *arg, int flags)
- {
- struct addr_range *lim = obj;
- lim->delme = 1;
- return 0;
- }
- static int addr_range_hash_cb(const void *obj, const int flags)
- {
- const struct addr_range *lim = obj;
- return abs((int) lim->ha.netaddr.s_addr);
- }
- static int addr_range_cmp_cb(void *obj, void *arg, int flags)
- {
- struct addr_range *lim1 = obj, *lim2 = arg;
- return ((lim1->ha.netaddr.s_addr == lim2->ha.netaddr.s_addr) &&
- (lim1->ha.netmask.s_addr == lim2->ha.netmask.s_addr)) ?
- CMP_MATCH | CMP_STOP : 0;
- }
- static int peercnt_hash_cb(const void *obj, const int flags)
- {
- const struct peercnt *peercnt = obj;
- return abs((int) peercnt->addr);
- }
- static int peercnt_cmp_cb(void *obj, void *arg, int flags)
- {
- struct peercnt *peercnt1 = obj, *peercnt2 = arg;
- return (peercnt1->addr == peercnt2->addr) ? CMP_MATCH | CMP_STOP : 0;
- }
- static int addr_range_match_address_cb(void *obj, void *arg, int flags)
- {
- struct addr_range *addr_range = obj;
- struct sockaddr_in *sin = arg;
- if ((sin->sin_addr.s_addr & addr_range->ha.netmask.s_addr) == addr_range->ha.netaddr.s_addr) {
- return CMP_MATCH | CMP_STOP;
- }
- return 0;
- }
- /*!
- * \internal
- *
- * \brief compares sin to calltoken_ignores table to determine if validation is required.
- */
- static int calltoken_required(struct sockaddr_in *sin, const char *name, int subclass)
- {
- struct addr_range *addr_range;
- struct iax2_peer *peer = NULL;
- struct iax2_user *user = NULL;
- /* if no username is given, check for guest accounts */
- const char *find = S_OR(name, "guest");
- int res = 1; /* required by default */
- int optional = 0;
- enum calltoken_peer_enum calltoken_required = CALLTOKEN_DEFAULT;
- /* There are only two cases in which calltoken validation is not required.
- * Case 1. sin falls within the list of address ranges specified in the calltoken optional table and
- * the peer definition has not set the requirecalltoken option.
- * Case 2. Username is a valid peer/user, and that peer has requirecalltoken set either auto or no.
- */
- /* ----- Case 1 ----- */
- if ((addr_range = ao2_callback(calltoken_ignores, 0, addr_range_match_address_cb, sin))) {
- ao2_ref(addr_range, -1);
- optional = 1;
- }
- /* ----- Case 2 ----- */
- if ((subclass == IAX_COMMAND_NEW) && (user = find_user(find))) {
- calltoken_required = user->calltoken_required;
- } else if ((subclass == IAX_COMMAND_NEW) && (user = realtime_user(find, sin))) {
- calltoken_required = user->calltoken_required;
- } else if ((subclass != IAX_COMMAND_NEW) && (peer = find_peer(find, 0))) {
- calltoken_required = peer->calltoken_required;
- } else if ((subclass != IAX_COMMAND_NEW) && (peer = realtime_peer(find, sin))) {
- calltoken_required = peer->calltoken_required;
- }
- if (peer) {
- peer_unref(peer);
- }
- if (user) {
- user_unref(user);
- }
- ast_debug(1, "Determining if address %s with username %s requires calltoken validation. Optional = %d calltoken_required = %d \n", ast_inet_ntoa(sin->sin_addr), name, optional, calltoken_required);
- if (((calltoken_required == CALLTOKEN_NO) || (calltoken_required == CALLTOKEN_AUTO)) ||
- (optional && (calltoken_required == CALLTOKEN_DEFAULT))) {
- res = 0;
- }
- return res;
- }
- /*!
- * \internal
- *
- * \brief set peercnt callno limit.
- *
- * \details
- * First looks in custom definitions. If not found, global limit
- * is used. Entries marked as reg already have
- * a custom limit set by a registration and are not modified.
- */
- static void set_peercnt_limit(struct peercnt *peercnt)
- {
- uint16_t limit = global_maxcallno;
- struct addr_range *addr_range;
- struct sockaddr_in sin = {
- .sin_addr.s_addr = peercnt->addr,
- };
- if (peercnt->reg && peercnt->limit) {
- return; /* this peercnt has a custom limit set by a registration */
- }
- if ((addr_range = ao2_callback(callno_limits, 0, addr_range_match_address_cb, &sin))) {
- limit = addr_range->limit;
- ast_debug(1, "custom addr_range %d found for %s\n", limit, ast_inet_ntoa(sin.sin_addr));
- ao2_ref(addr_range, -1);
- }
- peercnt->limit = limit;
- }
- /*!
- * \internal
- * \brief sets limits for all peercnts in table. done on reload to reflect changes in conf.
- */
- static int set_peercnt_limit_all_cb(void *obj, void *arg, int flags)
- {
- struct peercnt *peercnt = obj;
- set_peercnt_limit(peercnt);
- ast_debug(1, "Reset limits for peercnts table\n");
- return 0;
- }
- /*!
- * \internal
- * \brief returns match if delme is set.
- */
- static int prune_addr_range_cb(void *obj, void *arg, int flags)
- {
- struct addr_range *addr_range = obj;
- return addr_range->delme ? CMP_MATCH : 0;
- }
- /*!
- * \internal
- * \brief modifies peercnt entry in peercnts table. Used to set custom limit or mark a registered ip
- */
- static void peercnt_modify(unsigned char reg, uint16_t limit, struct sockaddr_in *sin)
- {
- /* this function turns off and on custom callno limits set by peer registration */
- struct peercnt *peercnt;
- struct peercnt tmp = {
- .addr = sin->sin_addr.s_addr,
- };
- if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
- peercnt->reg = reg;
- if (limit) {
- peercnt->limit = limit;
- } else {
- set_peercnt_limit(peercnt);
- }
- ast_debug(1, "peercnt entry %s modified limit:%d registered:%d", ast_inet_ntoa(sin->sin_addr), peercnt->limit, peercnt->reg);
- ao2_ref(peercnt, -1); /* decrement ref from find */
- }
- }
- /*!
- * \internal
- * \brief adds an ip to the peercnts table, increments connection count if it already exists
- *
- * \details First searches for the address in the peercnts table. If found
- * the current count is incremented. If not found a new peercnt is allocated
- * and linked into the peercnts table with a call number count of 1.
- */
- static int peercnt_add(struct sockaddr_in *sin)
- {
- struct peercnt *peercnt;
- unsigned long addr = sin->sin_addr.s_addr;
- int res = 0;
- struct peercnt tmp = {
- .addr = addr,
- };
- /* Reasoning for peercnts container lock: Two identical ip addresses
- * could be added by different threads at the "same time". Without the container
- * lock, both threads could alloc space for the same object and attempt
- * to link to table. With the lock, one would create the object and link
- * to table while the other would find the already created peercnt object
- * rather than creating a new one. */
- ao2_lock(peercnts);
- if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
- ao2_lock(peercnt);
- } else if ((peercnt = ao2_alloc(sizeof(*peercnt), NULL))) {
- ao2_lock(peercnt);
- /* create and set defaults */
- peercnt->addr = addr;
- set_peercnt_limit(peercnt);
- /* guarantees it does not go away after unlocking table
- * ao2_find automatically adds this */
- ao2_link(peercnts, peercnt);
- } else {
- ao2_unlock(peercnts);
- return -1;
- }
- /* check to see if the address has hit its callno limit. If not increment cur. */
- if (peercnt->limit > peercnt->cur) {
- peercnt->cur++;
- ast_debug(1, "ip callno count incremented to %d for %s\n", peercnt->cur, ast_inet_ntoa(sin->sin_addr));
- } else { /* max num call numbers for this peer has been reached! */
- ast_log(LOG_ERROR, "maxcallnumber limit of %d for %s has been reached!\n", peercnt->limit, ast_inet_ntoa(sin->sin_addr));
- res = -1;
- }
- /* clean up locks and ref count */
- ao2_unlock(peercnt);
- ao2_unlock(peercnts);
- ao2_ref(peercnt, -1); /* decrement ref from find/alloc, only the container ref remains. */
- return res;
- }
- /*!
- * \internal
- * \brief decrements a peercnts table entry
- */
- static void peercnt_remove(struct peercnt *peercnt)
- {
- struct sockaddr_in sin = {
- .sin_addr.s_addr = peercnt->addr,
- };
- if (peercnt) {
- /* Container locked here since peercnt may be unlinked from list. If left unlocked,
- * peercnt_add could try and grab this entry from the table and modify it at the
- * "same time" this thread attemps to unlink it.*/
- ao2_lock(peercnts);
- peercnt->cur--;
- ast_debug(1, "ip callno count decremented to %d for %s\n", peercnt->cur, ast_inet_ntoa(sin.sin_addr));
- /* if this was the last connection from the peer remove it from table */
- if (peercnt->cur == 0) {
- ao2_unlink(peercnts, peercnt);/* decrements ref from table, last ref is left to scheduler */
- }
- ao2_unlock(peercnts);
- }
- }
- /*!
- * \internal
- * \brief called by scheduler to decrement object
- */
- static int peercnt_remove_cb(const void *obj)
- {
- struct peercnt *peercnt = (struct peercnt *) obj;
- peercnt_remove(peercnt);
- ao2_ref(peercnt, -1); /* decrement ref from scheduler */
- return 0;
- }
- /*!
- * \internal
- * \brief decrements peercnts connection count, finds by addr
- */
- static int peercnt_remove_by_addr(struct sockaddr_in *sin)
- {
- struct peercnt *peercnt;
- struct peercnt tmp = {
- .addr = sin->sin_addr.s_addr,
- };
- if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
- peercnt_remove(peercnt);
- ao2_ref(peercnt, -1); /* decrement ref from find */
- }
- return 0;
- }
- /*!
- * \internal
- * \brief Create callno_limit entry based on configuration
- */
- static void build_callno_limits(struct ast_variable *v)
- {
- struct addr_range *addr_range = NULL;
- struct addr_range tmp;
- struct ast_ha *ha;
- int limit;
- int error;
- int found;
- for (; v; v = v->next) {
- limit = -1;
- error = 0;
- found = 0;
- ha = ast_append_ha("permit", v->name, NULL, &error);
- /* check for valid config information */
- if (error) {
- ast_log(LOG_ERROR, "Call number limit for %s could not be added, Invalid address range\n.", v->name);
- continue;
- } else if ((sscanf(v->value, "%d", &limit) != 1) || (limit < 0)) {
- ast_log(LOG_ERROR, "Call number limit for %s could not be added. Invalid limit %s\n.", v->name, v->value);
- ast_free_ha(ha);
- continue;
- }
- ast_copy_ha(ha, &tmp.ha);
- /* find or create the addr_range */
- if ((addr_range = ao2_find(callno_limits, &tmp, OBJ_POINTER))) {
- ao2_lock(addr_range);
- found = 1;
- } else if (!(addr_range = ao2_alloc(sizeof(*addr_range), NULL))) {
- ast_free_ha(ha);
- return; /* out of memory */
- }
- /* copy over config data into addr_range object */
- ast_copy_ha(ha, &addr_range->ha); /* this is safe because only one ha is possible for each limit */
- ast_free_ha(ha); /* cleanup the tmp ha */
- addr_range->limit = limit;
- addr_range->delme = 0;
- /* cleanup */
- if (found) {
- ao2_unlock(addr_range);
- } else {
- ao2_link(callno_limits, addr_range);
- }
- ao2_ref(addr_range, -1); /* decrement ref from ao2_find and ao2_alloc, only container ref remains */
- }
- }
- /*!
- * \internal
- * \brief Create calltoken_ignores entry based on configuration
- */
- static int add_calltoken_ignore(const char *addr)
- {
- struct addr_range tmp;
- struct addr_range *addr_range = NULL;
- struct ast_ha *ha = NULL;
- int error = 0;
- if (ast_strlen_zero(addr)) {
- ast_log(LOG_WARNING, "invalid calltokenoptional %s\n", addr);
- return -1;
- }
- ha = ast_append_ha("permit", addr, NULL, &error);
- /* check for valid config information */
- if (error) {
- ast_log(LOG_WARNING, "Error %d creating calltokenoptional entry %s\n", error, addr);
- return -1;
- }
- ast_copy_ha(ha, &tmp.ha);
- /* find or create the addr_range */
- if ((addr_range = ao2_find(calltoken_ignores, &tmp, OBJ_POINTER))) {
- ao2_lock(addr_range);
- addr_range->delme = 0;
- ao2_unlock(addr_range);
- } else if ((addr_range = ao2_alloc(sizeof(*addr_range), NULL))) {
- /* copy over config data into addr_range object */
- ast_copy_ha(ha, &addr_range->ha); /* this is safe because only one ha is possible */
- ao2_link(calltoken_ignores, addr_range);
- } else {
- ast_free_ha(ha);
- return -1;
- }
- ast_free_ha(ha);
- ao2_ref(addr_range, -1); /* decrement ref from ao2_find and ao2_alloc, only container ref remains */
- return 0;
- }
- static char *handle_cli_iax2_show_callno_limits(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct ao2_iterator i;
- struct peercnt *peercnt;
- struct sockaddr_in sin;
- int found = 0;
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 show callnumber usage";
- e->usage =
- "Usage: iax2 show callnumber usage <ip address (optional)>\n"
- " Shows current ip addresses which are consuming iax2 call numbers\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- case CLI_HANDLER:
- if (a->argc < 4 || a->argc > 5)
- return CLI_SHOWUSAGE;
- ast_cli(a->fd, "%-15s %-12s %-12s\n", "Address", "Callno Usage", "Callno Limit");
- i = ao2_iterator_init(peercnts, 0);
- while ((peercnt = ao2_iterator_next(&i))) {
- sin.sin_addr.s_addr = peercnt->addr;
- if (a->argc == 5 && (!strcasecmp(a->argv[4], ast_inet_ntoa(sin.sin_addr)))) {
- ast_cli(a->fd, "%-15s %-12d %-12d\n", ast_inet_ntoa(sin.sin_addr), peercnt->cur, peercnt->limit);
- found = 1;
- break;
- } else {
- ast_cli(a->fd, "%-15s %-12d %-12d\n", ast_inet_ntoa(sin.sin_addr), peercnt->cur, peercnt->limit);
- }
- ao2_ref(peercnt, -1);
- }
- ao2_iterator_destroy(&i);
- if (a->argc == 4) {
- ast_cli(a->fd, "\nNon-CallToken Validation Limit: %d\nNon-CallToken Validated: %d\n", global_maxcallno_nonval, total_nonval_callno_used);
- } else if (a->argc == 5 && !found) {
- ast_cli(a->fd, "No callnumber table entries for %s found\n", a->argv[4] );
- }
- return CLI_SUCCESS;
- default:
- return NULL;
- }
- }
- static struct callno_entry *get_unused_callno(int trunk, int validated)
- {
- struct callno_entry *callno_entry = NULL;
- if ((!ao2_container_count(callno_pool) && !trunk) || (!ao2_container_count(callno_pool_trunk) && trunk)) {
- ast_log(LOG_WARNING, "Out of CallNumbers\n");
- /* Minor optimization for the extreme case. */
- return NULL;
- }
- /* the callno_pool container is locked here primarily to ensure thread
- * safety of the total_nonval_callno_used check and increment */
- ao2_lock(callno_pool);
- /* only a certain number of nonvalidated call numbers should be allocated.
- * If there ever is an attack, this separates the calltoken validating
- * users from the non calltoken validating users. */
- if (!validated && (total_nonval_callno_used >= global_maxcallno_nonval)) {
- ast_log(LOG_WARNING, "NON-CallToken callnumber limit is reached. Current:%d Max:%d\n", total_nonval_callno_used, global_maxcallno_nonval);
- ao2_unlock(callno_pool);
- return NULL;
- }
- /* unlink the object from the container, taking over ownership
- * of the reference the container had to the object */
- callno_entry = ao2_find((trunk ? callno_pool_trunk : callno_pool), NULL, OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE);
- if (callno_entry) {
- callno_entry->validated = validated;
- if (!validated) {
- total_nonval_callno_used++;
- }
- }
- ao2_unlock(callno_pool);
- return callno_entry;
- }
- static int replace_callno(const void *obj)
- {
- struct callno_entry *callno_entry = (struct callno_entry *) obj;
- /* the callno_pool container is locked here primarily to ensure thread
- * safety of the total_nonval_callno_used check and decrement */
- ao2_lock(callno_pool);
- if (!callno_entry->validated && (total_nonval_callno_used != 0)) {
- total_nonval_callno_used--;
- } else if (!callno_entry->validated && (total_nonval_callno_used == 0)) {
- ast_log(LOG_ERROR, "Attempted to decrement total non calltoken validated callnumbers below zero... Callno is:%d \n", callno_entry->callno);
- }
- if (callno_entry->callno < TRUNK_CALL_START) {
- ao2_link(callno_pool, callno_entry);
- } else {
- ao2_link(callno_pool_trunk, callno_entry);
- }
- ao2_ref(callno_entry, -1); /* only container ref remains */
- ao2_unlock(callno_pool);
- return 0;
- }
- static int callno_hash(const void *obj, const int flags)
- {
- return abs(ast_random());
- }
- static int create_callno_pools(void)
- {
- uint16_t i;
- if (!(callno_pool = ao2_container_alloc(CALLNO_POOL_BUCKETS, callno_hash, NULL))) {
- return -1;
- }
- if (!(callno_pool_trunk = ao2_container_alloc(CALLNO_POOL_BUCKETS, callno_hash, NULL))) {
- return -1;
- }
- /* start at 2, 0 and 1 are reserved */
- for (i = 2; i <= IAX_MAX_CALLS; i++) {
- struct callno_entry *callno_entry;
- if (!(callno_entry = ao2_alloc(sizeof(*callno_entry), NULL))) {
- return -1;
- }
- callno_entry->callno = i;
- if (i < TRUNK_CALL_START) {
- ao2_link(callno_pool, callno_entry);
- } else {
- ao2_link(callno_pool_trunk, callno_entry);
- }
- ao2_ref(callno_entry, -1);
- }
- return 0;
- }
- /*!
- * \internal
- * \brief Schedules delayed removal of iax2_pvt call number data
- *
- * \note After MIN_REUSE_TIME has passed for a destroyed iax2_pvt, the callno is
- * avaliable again, and the address from the previous connection must be decremented
- * from the peercnts table. This function schedules these operations to take place.
- */
- static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry)
- {
- int i;
- struct peercnt *peercnt;
- struct peercnt tmp = {
- .addr = sin->sin_addr.s_addr,
- };
- if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
- /* refcount is incremented with ao2_find. keep that ref for the scheduler */
- ast_debug(1, "schedule decrement of callno used for %s in %d seconds\n", ast_inet_ntoa(sin->sin_addr), MIN_REUSE_TIME);
- i = iax2_sched_add(sched, MIN_REUSE_TIME * 1000, peercnt_remove_cb, peercnt);
- if (i == -1) {
- ao2_ref(peercnt, -1);
- }
- }
- iax2_sched_add(sched, MIN_REUSE_TIME * 1000, replace_callno, callno_entry);
- }
- /*!
- * \internal
- * \brief returns whether or not a frame is capable of starting a new IAX2 dialog.
- *
- * \note For this implementation, inbound pokes should _NOT_ be capable of allocating
- * a new callno.
- */
- static inline int attribute_pure iax2_allow_new(int frametype, int subclass, int inbound)
- {
- if (frametype != AST_FRAME_IAX) {
- return 0;
- }
- switch (subclass) {
- case IAX_COMMAND_NEW:
- case IAX_COMMAND_REGREQ:
- case IAX_COMMAND_FWDOWNL:
- case IAX_COMMAND_REGREL:
- return 1;
- case IAX_COMMAND_POKE:
- if (!inbound) {
- return 1;
- }
- break;
- }
- return 0;
- }
- /*
- * \note Calling this function while holding another pvt lock can cause a deadlock.
- */
- static int __find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd, int return_locked, int check_dcallno)
- {
- int res = 0;
- int x;
- /* this call is calltoken validated as long as it is either NEW_FORCE
- * or NEW_ALLOW_CALLTOKEN_VALIDATED */
- int validated = (new > NEW_ALLOW) ? 1 : 0;
- char host[80];
- if (new <= NEW_ALLOW) {
- if (callno) {
- struct chan_iax2_pvt *pvt;
- struct chan_iax2_pvt tmp_pvt = {
- .callno = dcallno,
- .peercallno = callno,
- .transfercallno = callno,
- /* hack!! */
- .frames_received = check_dcallno,
- };
- memcpy(&tmp_pvt.addr, sin, sizeof(tmp_pvt.addr));
- /* this works for finding normal call numbers not involving transfering */
- if ((pvt = ao2_find(iax_peercallno_pvts, &tmp_pvt, OBJ_POINTER))) {
- if (return_locked) {
- ast_mutex_lock(&iaxsl[pvt->callno]);
- }
- res = pvt->callno;
- ao2_ref(pvt, -1);
- pvt = NULL;
- return res;
- }
- /* this searches for transfer call numbers that might not get caught otherwise */
- memset(&tmp_pvt.addr, 0, sizeof(tmp_pvt.addr));
- memcpy(&tmp_pvt.transfer, sin, sizeof(tmp_pvt.transfer));
- if ((pvt = ao2_find(iax_transfercallno_pvts, &tmp_pvt, OBJ_POINTER))) {
- if (return_locked) {
- ast_mutex_lock(&iaxsl[pvt->callno]);
- }
- res = pvt->callno;
- ao2_ref(pvt, -1);
- pvt = NULL;
- return res;
- }
- }
- /* This will occur on the first response to a message that we initiated,
- * such as a PING. */
- if (dcallno) {
- ast_mutex_lock(&iaxsl[dcallno]);
- }
- if (callno && dcallno && iaxs[dcallno] && !iaxs[dcallno]->peercallno && match(sin, callno, dcallno, iaxs[dcallno], check_dcallno)) {
- iaxs[dcallno]->peercallno = callno;
- res = dcallno;
- store_by_peercallno(iaxs[dcallno]);
- if (!res || !return_locked) {
- ast_mutex_unlock(&iaxsl[dcallno]);
- }
- return res;
- }
- if (dcallno) {
- ast_mutex_unlock(&iaxsl[dcallno]);
- }
- #ifdef IAX_OLD_FIND
- /* If we get here, we SHOULD NOT find a call structure for this
- callno; if we do, it means that there is a call structure that
- has a peer callno but did NOT get entered into the hash table,
- which is bad.
- If we find a call structure using this old, slow method, output a log
- message so we'll know about it. After a few months of leaving this in
- place, if we don't hear about people seeing these messages, we can
- remove this code for good.
- */
- for (x = 1; !res && x < maxnontrunkcall; x++) {
- ast_mutex_lock(&iaxsl[x]);
- if (iaxs[x]) {
- /* Look for an exact match */
- if (match(sin, callno, dcallno, iaxs[x], check_dcallno)) {
- res = x;
- }
- }
- if (!res || !return_locked)
- ast_mutex_unlock(&iaxsl[x]);
- }
- for (x = TRUNK_CALL_START; !res && x < maxtrunkcall; x++) {
- ast_mutex_lock(&iaxsl[x]);
- if (iaxs[x]) {
- /* Look for an exact match */
- if (match(sin, callno, dcallno, iaxs[x], check_dcallno)) {
- res = x;
- }
- }
- if (!res || !return_locked)
- ast_mutex_unlock(&iaxsl[x]);
- }
- #endif
- }
- if (!res && (new >= NEW_ALLOW)) {
- struct callno_entry *callno_entry;
- /* It may seem odd that we look through the peer list for a name for
- * this *incoming* call. Well, it is weird. However, users don't
- * have an IP address/port number that we can match against. So,
- * this is just checking for a peer that has that IP/port and
- * assuming that we have a user of the same name. This isn't always
- * correct, but it will be changed if needed after authentication. */
- if (!iax2_getpeername(*sin, host, sizeof(host)))
- snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
- if (peercnt_add(sin)) {
- /* This address has hit its callnumber limit. When the limit
- * is reached, the connection is not added to the peercnts table.*/
- return 0;
- }
- if (!(callno_entry = get_unused_callno(0, validated))) {
- /* since we ran out of space, remove the peercnt
- * entry we added earlier */
- peercnt_remove_by_addr(sin);
- ast_log(LOG_WARNING, "No more space\n");
- return 0;
- }
- x = callno_entry->callno;
- ast_mutex_lock(&iaxsl[x]);
- iaxs[x] = new_iax(sin, host);
- update_max_nontrunk();
- if (iaxs[x]) {
- if (iaxdebug)
- ast_debug(1, "Creating new call structure %d\n", x);
- iaxs[x]->callno_entry = callno_entry;
- iaxs[x]->sockfd = sockfd;
- iaxs[x]->addr.sin_port = sin->sin_port;
- iaxs[x]->addr.sin_family = sin->sin_family;
- iaxs[x]->addr.sin_addr.s_addr = sin->sin_addr.s_addr;
- iaxs[x]->peercallno = callno;
- iaxs[x]->callno = x;
- iaxs[x]->pingtime = DEFAULT_RETRY_TIME;
- iaxs[x]->expiry = min_reg_expire;
- iaxs[x]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x);
- iaxs[x]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x);
- iaxs[x]->amaflags = amaflags;
- ast_copy_flags(iaxs[x], (&globalflags), IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
-
- ast_string_field_set(iaxs[x], accountcode, accountcode);
- ast_string_field_set(iaxs[x], mohinterpret, mohinterpret);
- ast_string_field_set(iaxs[x], mohsuggest, mohsuggest);
- if (iaxs[x]->peercallno) {
- store_by_peercallno(iaxs[x]);
- }
- } else {
- ast_log(LOG_WARNING, "Out of resources\n");
- ast_mutex_unlock(&iaxsl[x]);
- replace_callno(callno_entry);
- return 0;
- }
- if (!return_locked)
- ast_mutex_unlock(&iaxsl[x]);
- res = x;
- }
- return res;
- }
- static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd, int full_frame) {
- return __find_callno(callno, dcallno, sin, new, sockfd, 0, full_frame);
- }
- static int find_callno_locked(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd, int full_frame) {
- return __find_callno(callno, dcallno, sin, new, sockfd, 1, full_frame);
- }
- /*!
- * \brief Queue a frame to a call's owning asterisk channel
- *
- * \pre This function assumes that iaxsl[callno] is locked when called.
- *
- * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno]
- * was valid before calling it, it may no longer be valid after calling it.
- * This function may unlock and lock the mutex associated with this callno,
- * meaning that another thread may grab it and destroy the call.
- */
- static int iax2_queue_frame(int callno, struct ast_frame *f)
- {
- for (;;) {
- if (iaxs[callno] && iaxs[callno]->owner) {
- if (ast_channel_trylock(iaxs[callno]->owner)) {
- /* Avoid deadlock by pausing and trying again */
- DEADLOCK_AVOIDANCE(&iaxsl[callno]);
- } else {
- ast_queue_frame(iaxs[callno]->owner, f);
- ast_channel_unlock(iaxs[callno]->owner);
- break;
- }
- } else
- break;
- }
- return 0;
- }
- /*!
- * \brief Queue a hangup frame on the ast_channel owner
- *
- * This function queues a hangup frame on the owner of the IAX2 pvt struct that
- * is active for the given call number.
- *
- * \pre Assumes lock for callno is already held.
- *
- * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno]
- * was valid before calling it, it may no longer be valid after calling it.
- * This function may unlock and lock the mutex associated with this callno,
- * meaning that another thread may grab it and destroy the call.
- */
- static int iax2_queue_hangup(int callno)
- {
- for (;;) {
- if (iaxs[callno] && iaxs[callno]->owner) {
- if (ast_channel_trylock(iaxs[callno]->owner)) {
- /* Avoid deadlock by pausing and trying again */
- DEADLOCK_AVOIDANCE(&iaxsl[callno]);
- } else {
- ast_queue_hangup(iaxs[callno]->owner);
- ast_channel_unlock(iaxs[callno]->owner);
- break;
- }
- } else
- break;
- }
- return 0;
- }
- /*!
- * \brief Queue a control frame on the ast_channel owner
- *
- * This function queues a control frame on the owner of the IAX2 pvt struct that
- * is active for the given call number.
- *
- * \pre Assumes lock for callno is already held.
- *
- * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno]
- * was valid before calling it, it may no longer be valid after calling it.
- * This function may unlock and lock the mutex associated with this callno,
- * meaning that another thread may grab it and destroy the call.
- */
- static int iax2_queue_control_data(int callno,
- enum ast_control_frame_type control, const void *data, size_t datalen)
- {
- for (;;) {
- if (iaxs[callno] && iaxs[callno]->owner) {
- if (ast_channel_trylock(iaxs[callno]->owner)) {
- /* Avoid deadlock by pausing and trying again */
- DEADLOCK_AVOIDANCE(&iaxsl[callno]);
- } else {
- ast_queue_control_data(iaxs[callno]->owner, control, data, datalen);
- ast_channel_unlock(iaxs[callno]->owner);
- break;
- }
- } else
- break;
- }
- return 0;
- }
- static void destroy_firmware(struct iax_firmware *cur)
- {
- /* Close firmware */
- if (cur->fwh) {
- munmap((void*)cur->fwh, ntohl(cur->fwh->datalen) + sizeof(*(cur->fwh)));
- }
- close(cur->fd);
- ast_free(cur);
- }
- static int try_firmware(char *s)
- {
- struct stat stbuf;
- struct iax_firmware *cur = NULL;
- int ifd, fd, res, len, chunk;
- struct ast_iax2_firmware_header *fwh, fwh2;
- struct MD5Context md5;
- unsigned char sum[16], buf[1024];
- char *s2, *last;
- if (!(s2 = alloca(strlen(s) + 100))) {
- ast_log(LOG_WARNING, "Alloca failed!\n");
- return -1;
- }
- last = strrchr(s, '/');
- if (last)
- last++;
- else
- last = s;
- snprintf(s2, strlen(s) + 100, "/var/tmp/%s-%ld", last, (unsigned long)ast_random());
- if ((res = stat(s, &stbuf) < 0)) {
- ast_log(LOG_WARNING, "Failed to stat '%s': %s\n", s, strerror(errno));
- return -1;
- }
- /* Make sure it's not a directory */
- if (S_ISDIR(stbuf.st_mode))
- return -1;
- ifd = open(s, O_RDONLY);
- if (ifd < 0) {
- ast_log(LOG_WARNING, "Cannot open '%s': %s\n", s, strerror(errno));
- return -1;
- }
- fd = open(s2, O_RDWR | O_CREAT | O_EXCL, AST_FILE_MODE);
- if (fd < 0) {
- ast_log(LOG_WARNING, "Cannot open '%s' for writing: %s\n", s2, strerror(errno));
- close(ifd);
- return -1;
- }
- /* Unlink our newly created file */
- unlink(s2);
-
- /* Now copy the firmware into it */
- len = stbuf.st_size;
- while(len) {
- chunk = len;
- if (chunk > sizeof(buf))
- chunk = sizeof(buf);
- res = read(ifd, buf, chunk);
- if (res != chunk) {
- ast_log(LOG_WARNING, "Only read %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
- close(ifd);
- close(fd);
- return -1;
- }
- res = write(fd, buf, chunk);
- if (res != chunk) {
- ast_log(LOG_WARNING, "Only write %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
- close(ifd);
- close(fd);
- return -1;
- }
- len -= chunk;
- }
- close(ifd);
- /* Return to the beginning */
- lseek(fd, 0, SEEK_SET);
- if ((res = read(fd, &fwh2, sizeof(fwh2))) != sizeof(fwh2)) {
- ast_log(LOG_WARNING, "Unable to read firmware header in '%s'\n", s);
- close(fd);
- return -1;
- }
- if (ntohl(fwh2.magic) != IAX_FIRMWARE_MAGIC) {
- ast_log(LOG_WARNING, "'%s' is not a valid firmware file\n", s);
- close(fd);
- return -1;
- }
- if (ntohl(fwh2.datalen) != (stbuf.st_size - sizeof(fwh2))) {
- ast_log(LOG_WARNING, "Invalid data length in firmware '%s'\n", s);
- close(fd);
- return -1;
- }
- if (fwh2.devname[sizeof(fwh2.devname) - 1] || ast_strlen_zero((char *)fwh2.devname)) {
- ast_log(LOG_WARNING, "No or invalid device type specified for '%s'\n", s);
- close(fd);
- return -1;
- }
- fwh = (struct ast_iax2_firmware_header*)mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (fwh == MAP_FAILED) {
- ast_log(LOG_WARNING, "mmap failed: %s\n", strerror(errno));
- close(fd);
- return -1;
- }
- MD5Init(&md5);
- MD5Update(&md5, fwh->data, ntohl(fwh->datalen));
- MD5Final(sum, &md5);
- if (memcmp(sum, fwh->chksum, sizeof(sum))) {
- ast_log(LOG_WARNING, "Firmware file '%s' fails checksum\n", s);
- munmap((void*)fwh, stbuf.st_size);
- close(fd);
- return -1;
- }
- AST_LIST_TRAVERSE(&firmwares, cur, list) {
- if (!strcmp((char *)cur->fwh->devname, (char *)fwh->devname)) {
- /* Found a candidate */
- if (cur->dead || (ntohs(cur->fwh->version) < ntohs(fwh->version)))
- /* The version we have on loaded is older, load this one instead */
- break;
- /* This version is no newer than what we have. Don't worry about it.
- We'll consider it a proper load anyhow though */
- munmap((void*)fwh, stbuf.st_size);
- close(fd);
- return 0;
- }
- }
-
- if (!cur && ((cur = ast_calloc(1, sizeof(*cur))))) {
- cur->fd = -1;
- AST_LIST_INSERT_TAIL(&firmwares, cur, list);
- }
-
- if (cur) {
- if (cur->fwh)
- munmap((void*)cur->fwh, cur->mmaplen);
- if (cur->fd > -1)
- close(cur->fd);
- cur->fwh = fwh;
- cur->fd = fd;
- cur->mmaplen = stbuf.st_size;
- cur->dead = 0;
- }
-
- return 0;
- }
- static int iax_check_version(char *dev)
- {
- int res = 0;
- struct iax_firmware *cur = NULL;
- if (ast_strlen_zero(dev))
- return 0;
- AST_LIST_LOCK(&firmwares);
- AST_LIST_TRAVERSE(&firmwares, cur, list) {
- if (!strcmp(dev, (char *)cur->fwh->devname)) {
- res = ntohs(cur->fwh->version);
- break;
- }
- }
- AST_LIST_UNLOCK(&firmwares);
- return res;
- }
- static int iax_firmware_append(struct iax_ie_data *ied, const unsigned char *dev, unsigned int desc)
- {
- int res = -1;
- unsigned int bs = desc & 0xff;
- unsigned int start = (desc >> 8) & 0xffffff;
- unsigned int bytes;
- struct iax_firmware *cur;
- if (ast_strlen_zero((char *)dev) || !bs)
- return -1;
- start *= bs;
-
- AST_LIST_LOCK(&firmwares);
- AST_LIST_TRAVERSE(&firmwares, cur, list) {
- if (strcmp((char *)dev, (char *)cur->fwh->devname))
- continue;
- iax_ie_append_int(ied, IAX_IE_FWBLOCKDESC, desc);
- if (start < ntohl(cur->fwh->datalen)) {
- bytes = ntohl(cur->fwh->datalen) - start;
- if (bytes > bs)
- bytes = bs;
- iax_ie_append_raw(ied, IAX_IE_FWBLOCKDATA, cur->fwh->data + start, bytes);
- } else {
- bytes = 0;
- iax_ie_append(ied, IAX_IE_FWBLOCKDATA);
- }
- if (bytes == bs)
- res = 0;
- else
- res = 1;
- break;
- }
- AST_LIST_UNLOCK(&firmwares);
- return res;
- }
- static void reload_firmware(int unload)
- {
- struct iax_firmware *cur = NULL;
- DIR *fwd;
- struct dirent *de;
- char dir[256], fn[256];
- AST_LIST_LOCK(&firmwares);
- /* Mark all as dead */
- AST_LIST_TRAVERSE(&firmwares, cur, list)
- cur->dead = 1;
- /* Now that we have marked them dead... load new ones */
- if (!unload) {
- snprintf(dir, sizeof(dir), "%s/firmware/iax", ast_config_AST_DATA_DIR);
- fwd = opendir(dir);
- if (fwd) {
- while((de = readdir(fwd))) {
- if (de->d_name[0] != '.') {
- snprintf(fn, sizeof(fn), "%s/%s", dir, de->d_name);
- if (!try_firmware(fn)) {
- ast_verb(2, "Loaded firmware '%s'\n", de->d_name);
- }
- }
- }
- closedir(fwd);
- } else
- ast_log(LOG_WARNING, "Error opening firmware directory '%s': %s\n", dir, strerror(errno));
- }
- /* Clean up leftovers */
- AST_LIST_TRAVERSE_SAFE_BEGIN(&firmwares, cur, list) {
- if (!cur->dead)
- continue;
- AST_LIST_REMOVE_CURRENT(list);
- destroy_firmware(cur);
- }
- AST_LIST_TRAVERSE_SAFE_END;
- AST_LIST_UNLOCK(&firmwares);
- }
- /*!
- * \note This function assumes that iaxsl[callno] is locked when called.
- *
- * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno]
- * was valid before calling it, it may no longer be valid after calling it.
- * This function calls iax2_queue_frame(), which may unlock and lock the mutex
- * associated with this callno, meaning that another thread may grab it and destroy the call.
- */
- static int __do_deliver(void *data)
- {
- /* Just deliver the packet by using queueing. This is called by
- the IAX thread with the iaxsl lock held. */
- struct iax_frame *fr = data;
- fr->retrans = -1;
- ast_clear_flag(&fr->af, AST_FRFLAG_HAS_TIMING_INFO);
- if (iaxs[fr->callno] && !ast_test_flag(iaxs[fr->callno], IAX_ALREADYGONE))
- iax2_queue_frame(fr->callno, &fr->af);
- /* Free our iax frame */
- iax2_frame_free(fr);
- /* And don't run again */
- return 0;
- }
- static int handle_error(void)
- {
- /* XXX Ideally we should figure out why an error occurred and then abort those
- rather than continuing to try. Unfortunately, the published interface does
- not seem to work XXX */
- #if 0
- struct sockaddr_in *sin;
- int res;
- struct msghdr m;
- struct sock_extended_err e;
- m.msg_name = NULL;
- m.msg_namelen = 0;
- m.msg_iov = NULL;
- m.msg_control = &e;
- m.msg_controllen = sizeof(e);
- m.msg_flags = 0;
- res = recvmsg(netsocket, &m, MSG_ERRQUEUE);
- if (res < 0)
- ast_log(LOG_WARNING, "Error detected, but unable to read error: %s\n", strerror(errno));
- else {
- if (m.msg_controllen) {
- sin = (struct sockaddr_in *)SO_EE_OFFENDER(&e);
- if (sin)
- ast_log(LOG_WARNING, "Receive error from %s\n", ast_inet_ntoa(sin->sin_addr));
- else
- ast_log(LOG_WARNING, "No address detected??\n");
- } else {
- ast_log(LOG_WARNING, "Local error: %s\n", strerror(e.ee_errno));
- }
- }
- #endif
- return 0;
- }
- static int transmit_trunk(struct iax_frame *f, struct sockaddr_in *sin, int sockfd)
- {
- int res;
- res = sendto(sockfd, f->data, f->datalen, 0,(struct sockaddr *)sin,
- sizeof(*sin));
- if (res < 0) {
- ast_debug(1, "Received error: %s\n", strerror(errno));
- handle_error();
- } else
- res = 0;
- return res;
- }
- static int send_packet(struct iax_frame *f)
- {
- int res;
- int callno = f->callno;
- /* Don't send if there was an error, but return error instead */
- if (!callno || !iaxs[callno] || iaxs[callno]->error)
- return -1;
-
- /* Called with iaxsl held */
- if (iaxdebug)
- ast_debug(3, "Sending %d on %d/%d to %s:%d\n", f->ts, callno, iaxs[callno]->peercallno, ast_inet_ntoa(iaxs[callno]->addr.sin_addr), ntohs(iaxs[callno]->addr.sin_port));
- if (f->transfer) {
- if (iaxdebug)
- iax_showframe(f, NULL, 0, &iaxs[callno]->transfer, f->datalen - sizeof(struct ast_iax2_full_hdr));
- res = sendto(iaxs[callno]->sockfd, f->data, f->datalen, 0,(struct sockaddr *)&iaxs[callno]->transfer,
- sizeof(iaxs[callno]->transfer));
- } else {
- if (iaxdebug)
- iax_showframe(f, NULL, 0, &iaxs[callno]->addr, f->datalen - sizeof(struct ast_iax2_full_hdr));
- res = sendto(iaxs[callno]->sockfd, f->data, f->datalen, 0,(struct sockaddr *)&iaxs[callno]->addr,
- sizeof(iaxs[callno]->addr));
- }
- if (res < 0) {
- if (iaxdebug)
- ast_debug(1, "Received error: %s\n", strerror(errno));
- handle_error();
- } else
- res = 0;
- return res;
- }
- /*!
- * \note Since this function calls iax2_queue_hangup(), the pvt struct
- * for the given call number may disappear during its execution.
- */
- static int iax2_predestroy(int callno)
- {
- struct ast_channel *c = NULL;
- struct chan_iax2_pvt *pvt = iaxs[callno];
- if (!pvt)
- return -1;
- if (!ast_test_flag(pvt, IAX_ALREADYGONE)) {
- iax2_destroy_helper(pvt);
- ast_set_flag(pvt, IAX_ALREADYGONE);
- }
- if ((c = pvt->owner)) {
- c->tech_pvt = NULL;
- iax2_queue_hangup(callno);
- pvt->owner = NULL;
- ast_module_unref(ast_module_info->self);
- }
- return 0;
- }
- static void iax2_destroy(int callno)
- {
- struct chan_iax2_pvt *pvt = NULL;
- struct ast_channel *owner = NULL;
- retry:
- pvt = iaxs[callno];
- owner = pvt ? pvt->owner : NULL;
- if (owner) {
- if (ast_channel_trylock(owner)) {
- ast_debug(3, "Avoiding IAX destroy deadlock\n");
- ast_mutex_unlock(&iaxsl[callno]);
- usleep(1);
- ast_mutex_lock(&iaxsl[callno]);
- goto retry;
- }
- }
- if (!owner) {
- iaxs[callno] = NULL;
- }
- if (pvt) {
- if (!owner) {
- pvt->owner = NULL;
- } else {
- /* If there's an owner, prod it to give up */
- /* It is ok to use ast_queue_hangup() here instead of iax2_queue_hangup()
- * because we already hold the owner channel lock. */
- ast_queue_hangup(owner);
- }
- if (pvt->peercallno) {
- remove_by_peercallno(pvt);
- }
- if (pvt->transfercallno) {
- remove_by_transfercallno(pvt);
- }
- if (!owner) {
- ao2_ref(pvt, -1);
- pvt = NULL;
- }
- }
- if (owner) {
- ast_channel_unlock(owner);
- }
- if (callno & 0x4000) {
- update_max_trunk();
- }
- }
- static int update_packet(struct iax_frame *f)
- {
- /* Called with iaxsl lock held, and iaxs[callno] non-NULL */
- struct ast_iax2_full_hdr *fh = f->data;
- struct ast_frame af;
- /* if frame is encrypted. decrypt before updating it. */
- if (f->encmethods) {
- decode_frame(&f->mydcx, fh, &af, &f->datalen);
- }
- /* Mark this as a retransmission */
- fh->dcallno = ntohs(IAX_FLAG_RETRANS | f->dcallno);
- /* Update iseqno */
- f->iseqno = iaxs[f->callno]->iseqno;
- fh->iseqno = f->iseqno;
- /* Now re-encrypt the frame */
- if (f->encmethods) {
- /* since this is a retransmit frame, create a new random padding
- * before re-encrypting. */
- build_rand_pad(f->semirand, sizeof(f->semirand));
- encrypt_frame(&f->ecx, fh, f->semirand, &f->datalen);
- }
- return 0;
- }
- static int attempt_transmit(const void *data);
- static void __attempt_transmit(const void *data)
- {
- /* Attempt to transmit the frame to the remote peer...
- Called without iaxsl held. */
- struct iax_frame *f = (struct iax_frame *)data;
- int freeme = 0;
- int callno = f->callno;
- /* Make sure this call is still active */
- if (callno)
- ast_mutex_lock(&iaxsl[callno]);
- if (callno && iaxs[callno]) {
- if ((f->retries < 0) /* Already ACK'd */ ||
- (f->retries >= max_retries) /* Too many attempts */) {
- /* Record an error if we've transmitted too many times */
- if (f->retries >= max_retries) {
- if (f->transfer) {
- /* Transfer timeout */
- send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, NULL, 0, -1);
- } else if (f->final) {
- if (f->final)
- iax2_destroy(callno);
- } else {
- if (iaxs[callno]->owner)
- ast_log(LOG_WARNING, "Max retries exceeded to host %s on %s (type = %d, subclass = %d, ts=%d, seqno=%d)\n", ast_inet_ntoa(iaxs[f->callno]->addr.sin_addr),iaxs[f->callno]->owner->name , f->af.frametype, f->af.subclass, f->ts, f->oseqno);
- iaxs[callno]->error = ETIMEDOUT;
- if (iaxs[callno]->owner) {
- struct ast_frame fr = { 0, };
- /* Hangup the fd */
- fr.frametype = AST_FRAME_CONTROL;
- fr.subclass = AST_CONTROL_HANGUP;
- iax2_queue_frame(callno, &fr); /* XXX */
- /* Remember, owner could disappear */
- if (iaxs[callno] && iaxs[callno]->owner)
- iaxs[callno]->owner->hangupcause = AST_CAUSE_DESTINATION_OUT_OF_ORDER;
- } else {
- if (iaxs[callno]->reg) {
- memset(&iaxs[callno]->reg->us, 0, sizeof(iaxs[callno]->reg->us));
- iaxs[callno]->reg->regstate = REG_STATE_TIMEOUT;
- iaxs[callno]->reg->refresh = IAX_DEFAULT_REG_EXPIRE;
- }
- iax2_destroy(callno);
- }
- }
- }
- freeme = 1;
- } else {
- /* Update it if it needs it */
- update_packet(f);
- /* Attempt transmission */
- send_packet(f);
- f->retries++;
- /* Try again later after 10 times as long */
- f->retrytime *= 10;
- if (f->retrytime > MAX_RETRY_TIME)
- f->retrytime = MAX_RETRY_TIME;
- /* Transfer messages max out at one second */
- if (f->transfer && (f->retrytime > 1000))
- f->retrytime = 1000;
- f->retrans = iax2_sched_add(sched, f->retrytime, attempt_transmit, f);
- }
- } else {
- /* Make sure it gets freed */
- f->retries = -1;
- freeme = 1;
- }
- if (callno)
- ast_mutex_unlock(&iaxsl[callno]);
- /* Do not try again */
- if (freeme) {
- /* Don't attempt delivery, just remove it from the queue */
- AST_LIST_LOCK(&frame_queue);
- AST_LIST_REMOVE(&frame_queue, f, list);
- AST_LIST_UNLOCK(&frame_queue);
- f->retrans = -1; /* this is safe because this is the scheduled function */
- /* Free the IAX frame */
- iax2_frame_free(f);
- }
- }
- static int attempt_transmit(const void *data)
- {
- #ifdef SCHED_MULTITHREADED
- if (schedule_action(__attempt_transmit, data))
- #endif
- __attempt_transmit(data);
- return 0;
- }
- static char *handle_cli_iax2_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct iax2_peer *peer = NULL;
- struct iax2_user *user = NULL;
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 prune realtime";
- e->usage =
- "Usage: iax2 prune realtime [<peername>|all]\n"
- " Prunes object(s) from the cache\n";
- return NULL;
- case CLI_GENERATE:
- return complete_iax2_show_peer(a->line, a->word, a->pos, a->n);
- }
- if (a->argc != 4)
- return CLI_SHOWUSAGE;
- if (!strcmp(a->argv[3], "all")) {
- prune_users();
- prune_peers();
- ast_cli(a->fd, "Cache flushed successfully.\n");
- return CLI_SUCCESS;
- }
- peer = find_peer(a->argv[3], 0);
- user = find_user(a->argv[3]);
- if (peer || user) {
- if (peer) {
- if (ast_test_flag(peer, IAX_RTCACHEFRIENDS)) {
- ast_set_flag(peer, IAX_RTAUTOCLEAR);
- expire_registry(peer_ref(peer));
- ast_cli(a->fd, "Peer %s was removed from the cache.\n", a->argv[3]);
- } else {
- ast_cli(a->fd, "Peer %s is not eligible for this operation.\n", a->argv[3]);
- }
- peer_unref(peer);
- }
- if (user) {
- if (ast_test_flag(user, IAX_RTCACHEFRIENDS)) {
- ast_set_flag(user, IAX_RTAUTOCLEAR);
- ast_cli(a->fd, "User %s was removed from the cache.\n", a->argv[3]);
- } else {
- ast_cli(a->fd, "User %s is not eligible for this operation.\n", a->argv[3]);
- }
- ao2_unlink(users,user);
- user_unref(user);
- }
- } else {
- ast_cli(a->fd, "%s was not found in the cache.\n", a->argv[3]);
- }
- return CLI_SUCCESS;
- }
- static char *handle_cli_iax2_test_losspct(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 test losspct";
- e->usage =
- "Usage: iax2 test losspct <percentage>\n"
- " For testing, throws away <percentage> percent of incoming packets\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 4)
- return CLI_SHOWUSAGE;
- test_losspct = atoi(a->argv[3]);
- return CLI_SUCCESS;
- }
- #ifdef IAXTESTS
- static char *handle_cli_iax2_test_late(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 test late";
- e->usage =
- "Usage: iax2 test late <ms>\n"
- " For testing, count the next frame as <ms> ms late\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 4)
- return CLI_SHOWUSAGE;
- test_late = atoi(a->argv[3]);
- return CLI_SUCCESS;
- }
- static char *handle_cli_iax2_test_resync(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 test resync";
- e->usage =
- "Usage: iax2 test resync <ms>\n"
- " For testing, adjust all future frames by <ms> ms\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 4)
- return CLI_SHOWUSAGE;
- test_resync = atoi(a->argv[3]);
- return CLI_SUCCESS;
- }
- static char *handle_cli_iax2_test_jitter(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 test jitter";
- e->usage =
- "Usage: iax2 test jitter <ms> <pct>\n"
- " For testing, simulate maximum jitter of +/- <ms> on <pct>\n"
- " percentage of packets. If <pct> is not specified, adds\n"
- " jitter to all packets.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc < 4 || a->argc > 5)
- return CLI_SHOWUSAGE;
- test_jit = atoi(a->argv[3]);
- if (a->argc == 5)
- test_jitpct = atoi(a->argv[4]);
- return CLI_SUCCESS;
- }
- #endif /* IAXTESTS */
- /*! \brief peer_status: Report Peer status in character string */
- /* returns 1 if peer is online, -1 if unmonitored */
- static int peer_status(struct iax2_peer *peer, char *status, int statuslen)
- {
- int res = 0;
- if (peer->maxms) {
- if (peer->lastms < 0) {
- ast_copy_string(status, "UNREACHABLE", statuslen);
- } else if (peer->lastms > peer->maxms) {
- snprintf(status, statuslen, "LAGGED (%d ms)", peer->lastms);
- res = 1;
- } else if (peer->lastms) {
- snprintf(status, statuslen, "OK (%d ms)", peer->lastms);
- res = 1;
- } else {
- ast_copy_string(status, "UNKNOWN", statuslen);
- }
- } else {
- ast_copy_string(status, "Unmonitored", statuslen);
- res = -1;
- }
- return res;
- }
- /*! \brief Show one peer in detail */
- static char *handle_cli_iax2_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- char status[30];
- char cbuf[256];
- struct iax2_peer *peer;
- char codec_buf[512];
- int x = 0, codec = 0, load_realtime = 0;
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 show peer";
- e->usage =
- "Usage: iax2 show peer <name>\n"
- " Display details on specific IAX peer\n";
- return NULL;
- case CLI_GENERATE:
- return complete_iax2_show_peer(a->line, a->word, a->pos, a->n);
- }
- if (a->argc < 4)
- return CLI_SHOWUSAGE;
- load_realtime = (a->argc == 5 && !strcmp(a->argv[4], "load")) ? 1 : 0;
- peer = find_peer(a->argv[3], load_realtime);
- if (peer) {
- ast_cli(a->fd, "\n\n");
- ast_cli(a->fd, " * Name : %s\n", peer->name);
- ast_cli(a->fd, " Secret : %s\n", ast_strlen_zero(peer->secret) ? "<Not set>" : "<Set>");
- ast_cli(a->fd, " Context : %s\n", peer->context);
- ast_cli(a->fd, " Mailbox : %s\n", peer->mailbox);
- ast_cli(a->fd, " Dynamic : %s\n", ast_test_flag(peer, IAX_DYNAMIC) ? "Yes" : "No");
- ast_cli(a->fd, " Callnum limit: %d\n", peer->maxcallno);
- ast_cli(a->fd, " Calltoken req: %s\n", (peer->calltoken_required == CALLTOKEN_YES) ? "Yes" : ((peer->calltoken_required == CALLTOKEN_AUTO) ? "Auto" : "No"));
- ast_cli(a->fd, " Trunk : %s\n", ast_test_flag(peer, IAX_TRUNK) ? "Yes" : "No");
- ast_cli(a->fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "<unspecified>"));
- ast_cli(a->fd, " Expire : %d\n", peer->expire);
- ast_cli(a->fd, " ACL : %s\n", (peer->ha ? "Yes" : "No"));
- ast_cli(a->fd, " Addr->IP : %s Port %d\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)", ntohs(peer->addr.sin_port));
- ast_cli(a->fd, " Defaddr->IP : %s Port %d\n", ast_inet_ntoa(peer->defaddr.sin_addr), ntohs(peer->defaddr.sin_port));
- ast_cli(a->fd, " Username : %s\n", peer->username);
- ast_cli(a->fd, " Codecs : ");
- ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, peer->capability);
- ast_cli(a->fd, "%s\n", codec_buf);
- ast_cli(a->fd, " Codec Order : (");
- for(x = 0; x < 32 ; x++) {
- codec = ast_codec_pref_index(&peer->prefs,x);
- if(!codec)
- break;
- ast_cli(a->fd, "%s", ast_getformatname(codec));
- if(x < 31 && ast_codec_pref_index(&peer->prefs,x+1))
- ast_cli(a->fd, "|");
- }
- if (!x)
- ast_cli(a->fd, "none");
- ast_cli(a->fd, ")\n");
- ast_cli(a->fd, " Status : ");
- peer_status(peer, status, sizeof(status));
- ast_cli(a->fd, "%s\n",status);
- ast_cli(a->fd, " Qualify : every %dms when OK, every %dms when UNREACHABLE (sample smoothing %s)\n", peer->pokefreqok, peer->pokefreqnotok, peer->smoothing ? "On" : "Off");
- ast_cli(a->fd, "\n");
- peer_unref(peer);
- } else {
- ast_cli(a->fd, "Peer %s not found.\n", a->argv[3]);
- ast_cli(a->fd, "\n");
- }
- return CLI_SUCCESS;
- }
- static char *complete_iax2_show_peer(const char *line, const char *word, int pos, int state)
- {
- int which = 0;
- struct iax2_peer *peer;
- char *res = NULL;
- int wordlen = strlen(word);
- struct ao2_iterator i;
- /* 0 - iax2; 1 - show; 2 - peer; 3 - <peername> */
- if (pos != 3)
- return NULL;
- i = ao2_iterator_init(peers, 0);
- while ((peer = ao2_iterator_next(&i))) {
- if (!strncasecmp(peer->name, word, wordlen) && ++which > state) {
- res = ast_strdup(peer->name);
- peer_unref(peer);
- break;
- }
- peer_unref(peer);
- }
- ao2_iterator_destroy(&i);
- return res;
- }
- static char *handle_cli_iax2_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct iax_frame *cur;
- int cnt = 0, dead = 0, final = 0;
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 show stats";
- e->usage =
- "Usage: iax2 show stats\n"
- " Display statistics on IAX channel driver.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
- AST_LIST_LOCK(&frame_queue);
- AST_LIST_TRAVERSE(&frame_queue, cur, list) {
- if (cur->retries < 0)
- dead++;
- if (cur->final)
- final++;
- cnt++;
- }
- AST_LIST_UNLOCK(&frame_queue);
- ast_cli(a->fd, " IAX Statistics\n");
- ast_cli(a->fd, "---------------------\n");
- ast_cli(a->fd, "Outstanding frames: %d (%d ingress, %d egress)\n", iax_get_frames(), iax_get_iframes(), iax_get_oframes());
- ast_cli(a->fd, "%d timed and %d untimed transmits; MTU %d/%d/%d\n", trunk_timed, trunk_untimed,
- trunk_maxmtu, trunk_nmaxmtu, global_max_trunk_mtu);
- ast_cli(a->fd, "Packets in transmit queue: %d dead, %d final, %d total\n\n", dead, final, cnt);
- trunk_timed = trunk_untimed = 0;
- if (trunk_maxmtu > trunk_nmaxmtu)
- trunk_nmaxmtu = trunk_maxmtu;
- return CLI_SUCCESS;
- }
- /*! \brief Set trunk MTU from CLI */
- static char *handle_cli_iax2_set_mtu(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- int mtuv;
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 set mtu";
- e->usage =
- "Usage: iax2 set mtu <value>\n"
- " Set the system-wide IAX IP mtu to <value> bytes net or\n"
- " zero to disable. Disabling means that the operating system\n"
- " must handle fragmentation of UDP packets when the IAX2 trunk\n"
- " packet exceeds the UDP payload size. This is substantially\n"
- " below the IP mtu. Try 1240 on ethernets. Must be 172 or\n"
- " greater for G.711 samples.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 4)
- return CLI_SHOWUSAGE;
- if (strncasecmp(a->argv[3], "default", strlen(a->argv[3])) == 0)
- mtuv = MAX_TRUNK_MTU;
- else
- mtuv = atoi(a->argv[3]);
- if (mtuv == 0) {
- ast_cli(a->fd, "Trunk MTU control disabled (mtu was %d)\n", global_max_trunk_mtu);
- global_max_trunk_mtu = 0;
- return CLI_SUCCESS;
- }
- if (mtuv < 172 || mtuv > 4000) {
- ast_cli(a->fd, "Trunk MTU must be between 172 and 4000\n");
- return CLI_SHOWUSAGE;
- }
- ast_cli(a->fd, "Trunk MTU changed from %d to %d\n", global_max_trunk_mtu, mtuv);
- global_max_trunk_mtu = mtuv;
- return CLI_SUCCESS;
- }
- static char *handle_cli_iax2_show_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct iax2_dpcache *dp = NULL;
- char tmp[1024], *pc = NULL;
- int s, x, y;
- struct timeval tv = ast_tvnow();
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 show cache";
- e->usage =
- "Usage: iax2 show cache\n"
- " Display currently cached IAX Dialplan results.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- AST_LIST_LOCK(&dpcache);
- ast_cli(a->fd, "%-20.20s %-12.12s %-9.9s %-8.8s %s\n", "Peer/Context", "Exten", "Exp.", "Wait.", "Flags");
- AST_LIST_TRAVERSE(&dpcache, dp, cache_list) {
- s = dp->expiry.tv_sec - tv.tv_sec;
- tmp[0] = '\0';
- if (dp->flags & CACHE_FLAG_EXISTS)
- strncat(tmp, "EXISTS|", sizeof(tmp) - strlen(tmp) - 1);
- if (dp->flags & CACHE_FLAG_NONEXISTENT)
- strncat(tmp, "NONEXISTENT|", sizeof(tmp) - strlen(tmp) - 1);
- if (dp->flags & CACHE_FLAG_CANEXIST)
- strncat(tmp, "CANEXIST|", sizeof(tmp) - strlen(tmp) - 1);
- if (dp->flags & CACHE_FLAG_PENDING)
- strncat(tmp, "PENDING|", sizeof(tmp) - strlen(tmp) - 1);
- if (dp->flags & CACHE_FLAG_TIMEOUT)
- strncat(tmp, "TIMEOUT|", sizeof(tmp) - strlen(tmp) - 1);
- if (dp->flags & CACHE_FLAG_TRANSMITTED)
- strncat(tmp, "TRANSMITTED|", sizeof(tmp) - strlen(tmp) - 1);
- if (dp->flags & CACHE_FLAG_MATCHMORE)
- strncat(tmp, "MATCHMORE|", sizeof(tmp) - strlen(tmp) - 1);
- if (dp->flags & CACHE_FLAG_UNKNOWN)
- strncat(tmp, "UNKNOWN|", sizeof(tmp) - strlen(tmp) - 1);
- /* Trim trailing pipe */
- if (!ast_strlen_zero(tmp)) {
- tmp[strlen(tmp) - 1] = '\0';
- } else {
- ast_copy_string(tmp, "(none)", sizeof(tmp));
- }
- y = 0;
- pc = strchr(dp->peercontext, '@');
- if (!pc) {
- pc = dp->peercontext;
- } else {
- pc++;
- }
- for (x = 0; x < ARRAY_LEN(dp->waiters); x++) {
- if (dp->waiters[x] > -1)
- y++;
- }
- if (s > 0) {
- ast_cli(a->fd, "%-20.20s %-12.12s %-9d %-8d %s\n", pc, dp->exten, s, y, tmp);
- } else {
- ast_cli(a->fd, "%-20.20s %-12.12s %-9.9s %-8d %s\n", pc, dp->exten, "(expired)", y, tmp);
- }
- }
- AST_LIST_UNLOCK(&dpcache);
- return CLI_SUCCESS;
- }
- static unsigned int calc_rxstamp(struct chan_iax2_pvt *p, unsigned int offset);
- static void unwrap_timestamp(struct iax_frame *fr)
- {
- /* Video mini frames only encode the lower 15 bits of the session
- * timestamp, but other frame types (e.g. audio) encode 16 bits. */
- const int ts_shift = (fr->af.frametype == AST_FRAME_VIDEO) ? 15 : 16;
- const int lower_mask = (1 << ts_shift) - 1;
- const int upper_mask = ~lower_mask;
- const int last_upper = iaxs[fr->callno]->last & upper_mask;
- if ( (fr->ts & upper_mask) == last_upper ) {
- const int x = fr->ts - iaxs[fr->callno]->last;
- const int threshold = (ts_shift == 15) ? 25000 : 50000;
- if (x < -threshold) {
- /* Sudden big jump backwards in timestamp:
- What likely happened here is that miniframe timestamp has circled but we haven't
- gotten the update from the main packet. We'll just pretend that we did, and
- update the timestamp appropriately. */
- fr->ts = (last_upper + (1 << ts_shift)) | (fr->ts & lower_mask);
- if (iaxdebug)
- ast_debug(1, "schedule_delivery: pushed forward timestamp\n");
- } else if (x > threshold) {
- /* Sudden apparent big jump forwards in timestamp:
- What's likely happened is this is an old miniframe belonging to the previous
- top 15 or 16-bit timestamp that has turned up out of order.
- Adjust the timestamp appropriately. */
- fr->ts = (last_upper - (1 << ts_shift)) | (fr->ts & lower_mask);
- if (iaxdebug)
- ast_debug(1, "schedule_delivery: pushed back timestamp\n");
- }
- }
- }
- static int get_from_jb(const void *p);
- static void update_jbsched(struct chan_iax2_pvt *pvt)
- {
- int when;
-
- when = ast_tvdiff_ms(ast_tvnow(), pvt->rxcore);
-
- when = jb_next(pvt->jb) - when;
- if (when <= 0) {
- /* XXX should really just empty until when > 0.. */
- when = 1;
- }
-
- pvt->jbid = iax2_sched_replace(pvt->jbid, sched, when, get_from_jb,
- CALLNO_TO_PTR(pvt->callno));
- }
- static void __get_from_jb(const void *p)
- {
- int callno = PTR_TO_CALLNO(p);
- struct chan_iax2_pvt *pvt = NULL;
- struct iax_frame *fr;
- jb_frame frame;
- int ret;
- long now;
- long next;
- struct timeval tv = ast_tvnow();
-
- /* Make sure we have a valid private structure before going on */
- ast_mutex_lock(&iaxsl[callno]);
- pvt = iaxs[callno];
- if (!pvt) {
- /* No go! */
- ast_mutex_unlock(&iaxsl[callno]);
- return;
- }
- pvt->jbid = -1;
-
- /* round up a millisecond since ast_sched_runq does; */
- /* prevents us from spinning while waiting for our now */
- /* to catch up with runq's now */
- tv.tv_usec += 1000;
-
- now = ast_tvdiff_ms(tv, pvt->rxcore);
-
- if(now >= (next = jb_next(pvt->jb))) {
- ret = jb_get(pvt->jb,&frame,now,ast_codec_interp_len(pvt->voiceformat));
- switch(ret) {
- case JB_OK:
- fr = frame.data;
- __do_deliver(fr);
- /* __do_deliver() can cause the call to disappear */
- pvt = iaxs[callno];
- break;
- case JB_INTERP:
- {
- struct ast_frame af = { 0, };
-
- /* create an interpolation frame */
- af.frametype = AST_FRAME_VOICE;
- af.subclass = pvt->voiceformat;
- af.samples = frame.ms * (ast_format_rate(pvt->voiceformat) / 1000);
- af.src = "IAX2 JB interpolation";
- af.delivery = ast_tvadd(pvt->rxcore, ast_samp2tv(next, 1000));
- af.offset = AST_FRIENDLY_OFFSET;
-
- /* queue the frame: For consistency, we would call __do_deliver here, but __do_deliver wants an iax_frame,
- * which we'd need to malloc, and then it would free it. That seems like a drag */
- if (!ast_test_flag(iaxs[callno], IAX_ALREADYGONE)) {
- iax2_queue_frame(callno, &af);
- /* iax2_queue_frame() could cause the call to disappear */
- pvt = iaxs[callno];
- }
- }
- break;
- case JB_DROP:
- iax2_frame_free(frame.data);
- break;
- case JB_NOFRAME:
- case JB_EMPTY:
- /* do nothing */
- break;
- default:
- /* shouldn't happen */
- break;
- }
- }
- if (pvt)
- update_jbsched(pvt);
- ast_mutex_unlock(&iaxsl[callno]);
- }
- static int get_from_jb(const void *data)
- {
- #ifdef SCHED_MULTITHREADED
- if (schedule_action(__get_from_jb, data))
- #endif
- __get_from_jb(data);
- return 0;
- }
- /*!
- * \note This function assumes fr->callno is locked
- *
- * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno]
- * was valid before calling it, it may no longer be valid after calling it.
- */
- static int schedule_delivery(struct iax_frame *fr, int updatehistory, int fromtrunk, unsigned int *tsout)
- {
- int type, len;
- int ret;
- int needfree = 0;
- struct ast_channel *owner = NULL;
- struct ast_channel *bridge = NULL;
-
- /* Attempt to recover wrapped timestamps */
- unwrap_timestamp(fr);
- /* delivery time is sender's sent timestamp converted back into absolute time according to our clock */
- if ( !fromtrunk && !ast_tvzero(iaxs[fr->callno]->rxcore))
- fr->af.delivery = ast_tvadd(iaxs[fr->callno]->rxcore, ast_samp2tv(fr->ts, 1000));
- else {
- #if 0
- ast_debug(1, "schedule_delivery: set delivery to 0 as we don't have an rxcore yet, or frame is from trunk.\n");
- #endif
- fr->af.delivery = ast_tv(0,0);
- }
- type = JB_TYPE_CONTROL;
- len = 0;
- if(fr->af.frametype == AST_FRAME_VOICE) {
- type = JB_TYPE_VOICE;
- len = ast_codec_get_samples(&fr->af) / (ast_format_rate(fr->af.subclass) / 1000);
- } else if(fr->af.frametype == AST_FRAME_CNG) {
- type = JB_TYPE_SILENCE;
- }
- if ( (!ast_test_flag(iaxs[fr->callno], IAX_USEJITTERBUF)) ) {
- if (tsout)
- *tsout = fr->ts;
- __do_deliver(fr);
- return -1;
- }
- if ((owner = iaxs[fr->callno]->owner))
- bridge = ast_bridged_channel(owner);
- /* if the user hasn't requested we force the use of the jitterbuffer, and we're bridged to
- * a channel that can accept jitter, then flush and suspend the jb, and send this frame straight through */
- if ( (!ast_test_flag(iaxs[fr->callno], IAX_FORCEJITTERBUF)) && owner && bridge && (bridge->tech->properties & AST_CHAN_TP_WANTSJITTER) ) {
- jb_frame frame;
- /* deliver any frames in the jb */
- while (jb_getall(iaxs[fr->callno]->jb, &frame) == JB_OK) {
- __do_deliver(frame.data);
- /* __do_deliver() can make the call disappear */
- if (!iaxs[fr->callno])
- return -1;
- }
- jb_reset(iaxs[fr->callno]->jb);
- AST_SCHED_DEL(sched, iaxs[fr->callno]->jbid);
- /* deliver this frame now */
- if (tsout)
- *tsout = fr->ts;
- __do_deliver(fr);
- return -1;
- }
- /* insert into jitterbuffer */
- /* TODO: Perhaps we could act immediately if it's not droppable and late */
- ret = jb_put(iaxs[fr->callno]->jb, fr, type, len, fr->ts,
- calc_rxstamp(iaxs[fr->callno],fr->ts));
- if (ret == JB_DROP) {
- needfree++;
- } else if (ret == JB_SCHED) {
- update_jbsched(iaxs[fr->callno]);
- }
- if (tsout)
- *tsout = fr->ts;
- if (needfree) {
- /* Free our iax frame */
- iax2_frame_free(fr);
- return -1;
- }
- return 0;
- }
- static int iax2_transmit(struct iax_frame *fr)
- {
- /* Lock the queue and place this packet at the end */
- /* By setting this to 0, the network thread will send it for us, and
- queue retransmission if necessary */
- fr->sentyet = 0;
- AST_LIST_LOCK(&frame_queue);
- AST_LIST_INSERT_TAIL(&frame_queue, fr, list);
- AST_LIST_UNLOCK(&frame_queue);
- /* Wake up the network and scheduler thread */
- if (netthreadid != AST_PTHREADT_NULL)
- pthread_kill(netthreadid, SIGURG);
- signal_condition(&sched_lock, &sched_cond);
- return 0;
- }
- static int iax2_digit_begin(struct ast_channel *c, char digit)
- {
- return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF_BEGIN, digit, 0, NULL, 0, -1);
- }
- static int iax2_digit_end(struct ast_channel *c, char digit, unsigned int duration)
- {
- return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF_END, digit, 0, NULL, 0, -1);
- }
- static int iax2_sendtext(struct ast_channel *c, const char *text)
- {
-
- return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_TEXT,
- 0, 0, (unsigned char *)text, strlen(text) + 1, -1);
- }
- static int iax2_sendimage(struct ast_channel *c, struct ast_frame *img)
- {
- return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_IMAGE, img->subclass, 0, img->data, img->datalen, -1);
- }
- static int iax2_sendhtml(struct ast_channel *c, int subclass, const char *data, int datalen)
- {
- return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_HTML, subclass, 0, (unsigned char *)data, datalen, -1);
- }
- static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan)
- {
- unsigned short callno = PTR_TO_CALLNO(newchan->tech_pvt);
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno])
- iaxs[callno]->owner = newchan;
- else
- ast_log(LOG_WARNING, "Uh, this isn't a good sign...\n");
- ast_mutex_unlock(&iaxsl[callno]);
- return 0;
- }
- /*!
- * \note This function calls reg_source_db -> iax2_poke_peer -> find_callno,
- * so do not call this with a pvt lock held.
- */
- static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in *sin)
- {
- struct ast_variable *var = NULL;
- struct ast_variable *tmp;
- struct iax2_peer *peer=NULL;
- time_t regseconds = 0, nowtime;
- int dynamic=0;
- if (peername) {
- var = ast_load_realtime("iaxpeers", "name", peername, "host", "dynamic", NULL);
- if (!var && sin)
- var = ast_load_realtime("iaxpeers", "name", peername, "host", ast_inet_ntoa(sin->sin_addr), NULL);
- } else if (sin) {
- char porta[25];
- sprintf(porta, "%d", ntohs(sin->sin_port));
- var = ast_load_realtime("iaxpeers", "ipaddr", ast_inet_ntoa(sin->sin_addr), "port", porta, NULL);
- if (var) {
- /* We'll need the peer name in order to build the structure! */
- for (tmp = var; tmp; tmp = tmp->next) {
- if (!strcasecmp(tmp->name, "name"))
- peername = tmp->value;
- }
- }
- }
- if (!var && peername) { /* Last ditch effort */
- var = ast_load_realtime("iaxpeers", "name", peername, NULL);
- /*!\note
- * If this one loaded something, then we need to ensure that the host
- * field matched. The only reason why we can't have this as a criteria
- * is because we only have the IP address and the host field might be
- * set as a name (and the reverse PTR might not match).
- */
- if (var && sin) {
- for (tmp = var; tmp; tmp = tmp->next) {
- if (!strcasecmp(tmp->name, "host")) {
- struct ast_hostent ahp;
- struct hostent *hp;
- if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) {
- /* No match */
- ast_variables_destroy(var);
- var = NULL;
- }
- break;
- }
- }
- }
- }
- if (!var)
- return NULL;
- peer = build_peer(peername, var, NULL, ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS) ? 0 : 1);
-
- if (!peer) {
- ast_variables_destroy(var);
- return NULL;
- }
- for (tmp = var; tmp; tmp = tmp->next) {
- /* Make sure it's not a user only... */
- if (!strcasecmp(tmp->name, "type")) {
- if (strcasecmp(tmp->value, "friend") &&
- strcasecmp(tmp->value, "peer")) {
- /* Whoops, we weren't supposed to exist! */
- peer = peer_unref(peer);
- break;
- }
- } else if (!strcasecmp(tmp->name, "regseconds")) {
- ast_get_time_t(tmp->value, ®seconds, 0, NULL);
- } else if (!strcasecmp(tmp->name, "ipaddr")) {
- inet_aton(tmp->value, &(peer->addr.sin_addr));
- } else if (!strcasecmp(tmp->name, "port")) {
- peer->addr.sin_port = htons(atoi(tmp->value));
- } else if (!strcasecmp(tmp->name, "host")) {
- if (!strcasecmp(tmp->value, "dynamic"))
- dynamic = 1;
- }
- }
- ast_variables_destroy(var);
- if (!peer)
- return NULL;
- if (ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS)) {
- ast_copy_flags(peer, &globalflags, IAX_RTAUTOCLEAR|IAX_RTCACHEFRIENDS);
- if (ast_test_flag(peer, IAX_RTAUTOCLEAR)) {
- if (peer->expire > -1) {
- if (!ast_sched_del(sched, peer->expire)) {
- peer->expire = -1;
- peer_unref(peer);
- }
- }
- peer->expire = iax2_sched_add(sched, (global_rtautoclear) * 1000, expire_registry, peer_ref(peer));
- if (peer->expire == -1)
- peer_unref(peer);
- }
- ao2_link(peers, peer);
- if (ast_test_flag(peer, IAX_DYNAMIC))
- reg_source_db(peer);
- } else {
- ast_set_flag(peer, IAX_TEMPONLY);
- }
- if (!ast_test_flag(&globalflags, IAX_RTIGNOREREGEXPIRE) && dynamic) {
- time(&nowtime);
- if ((nowtime - regseconds) > IAX_DEFAULT_REG_EXPIRE) {
- memset(&peer->addr, 0, sizeof(peer->addr));
- realtime_update_peer(peer->name, &peer->addr, 0);
- ast_debug(1, "realtime_peer: Bah, '%s' is expired (%d/%d/%d)!\n",
- peername, (int)(nowtime - regseconds), (int)regseconds, (int)nowtime);
- }
- else {
- ast_debug(1, "realtime_peer: Registration for '%s' still active (%d/%d/%d)!\n",
- peername, (int)(nowtime - regseconds), (int)regseconds, (int)nowtime);
- }
- }
- return peer;
- }
- static struct iax2_user *realtime_user(const char *username, struct sockaddr_in *sin)
- {
- struct ast_variable *var;
- struct ast_variable *tmp;
- struct iax2_user *user=NULL;
- var = ast_load_realtime("iaxusers", "name", username, "host", "dynamic", NULL);
- if (!var)
- var = ast_load_realtime("iaxusers", "name", username, "host", ast_inet_ntoa(sin->sin_addr), NULL);
- if (!var && sin) {
- char porta[6];
- snprintf(porta, sizeof(porta), "%d", ntohs(sin->sin_port));
- var = ast_load_realtime("iaxusers", "name", username, "ipaddr", ast_inet_ntoa(sin->sin_addr), "port", porta, NULL);
- if (!var)
- var = ast_load_realtime("iaxusers", "ipaddr", ast_inet_ntoa(sin->sin_addr), "port", porta, NULL);
- }
- if (!var) { /* Last ditch effort */
- var = ast_load_realtime("iaxusers", "name", username, NULL);
- /*!\note
- * If this one loaded something, then we need to ensure that the host
- * field matched. The only reason why we can't have this as a criteria
- * is because we only have the IP address and the host field might be
- * set as a name (and the reverse PTR might not match).
- */
- if (var) {
- for (tmp = var; tmp; tmp = tmp->next) {
- if (!strcasecmp(tmp->name, "host")) {
- struct ast_hostent ahp;
- struct hostent *hp;
- if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) {
- /* No match */
- ast_variables_destroy(var);
- var = NULL;
- }
- break;
- }
- }
- }
- }
- if (!var)
- return NULL;
- tmp = var;
- while(tmp) {
- /* Make sure it's not a peer only... */
- if (!strcasecmp(tmp->name, "type")) {
- if (strcasecmp(tmp->value, "friend") &&
- strcasecmp(tmp->value, "user")) {
- return NULL;
- }
- }
- tmp = tmp->next;
- }
- user = build_user(username, var, NULL, !ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS));
- ast_variables_destroy(var);
- if (!user)
- return NULL;
- if (ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS)) {
- ast_set_flag(user, IAX_RTCACHEFRIENDS);
- ao2_link(users, user);
- } else {
- ast_set_flag(user, IAX_TEMPONLY);
- }
- return user;
- }
- static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, time_t regtime)
- {
- char port[10];
- char regseconds[20];
-
- snprintf(regseconds, sizeof(regseconds), "%d", (int)regtime);
- snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port));
- ast_update_realtime("iaxpeers", "name", peername,
- "ipaddr", ast_inet_ntoa(sin->sin_addr), "port", port,
- "regseconds", regseconds, NULL);
- }
- struct create_addr_info {
- int capability;
- unsigned int flags;
- int maxtime;
- int encmethods;
- int found;
- int sockfd;
- int adsi;
- char username[80];
- char secret[80];
- char outkey[80];
- char timezone[80];
- char prefs[32];
- char context[AST_MAX_CONTEXT];
- char peercontext[AST_MAX_CONTEXT];
- char mohinterpret[MAX_MUSICCLASS];
- char mohsuggest[MAX_MUSICCLASS];
- };
- static int create_addr(const char *peername, struct ast_channel *c, struct sockaddr_in *sin, struct create_addr_info *cai)
- {
- struct iax2_peer *peer;
- int res = -1;
- struct ast_codec_pref ourprefs;
- ast_clear_flag(cai, IAX_SENDANI | IAX_TRUNK);
- cai->sockfd = defaultsockfd;
- cai->maxtime = 0;
- sin->sin_family = AF_INET;
- if (!(peer = find_peer(peername, 1))) {
- cai->found = 0;
- if (ast_get_ip_or_srv(sin, peername, srvlookup ? "_iax._udp" : NULL)) {
- ast_log(LOG_WARNING, "No such host: %s\n", peername);
- return -1;
- }
- sin->sin_port = htons(IAX_DEFAULT_PORTNO);
- /* use global iax prefs for unknown peer/user */
- /* But move the calling channel's native codec to the top of the preference list */
- memcpy(&ourprefs, &prefs, sizeof(ourprefs));
- if (c)
- ast_codec_pref_prepend(&ourprefs, c->nativeformats, 1);
- ast_codec_pref_convert(&ourprefs, cai->prefs, sizeof(cai->prefs), 1);
- return 0;
- }
- cai->found = 1;
-
- /* if the peer has no address (current or default), return failure */
- if (!(peer->addr.sin_addr.s_addr || peer->defaddr.sin_addr.s_addr))
- goto return_unref;
- /* if the peer is being monitored and is currently unreachable, return failure */
- if (peer->maxms && ((peer->lastms > peer->maxms) || (peer->lastms < 0)))
- goto return_unref;
- ast_copy_flags(cai, peer, IAX_SENDANI | IAX_TRUNK | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
- cai->maxtime = peer->maxms;
- cai->capability = peer->capability;
- cai->encmethods = peer->encmethods;
- cai->sockfd = peer->sockfd;
- cai->adsi = peer->adsi;
- memcpy(&ourprefs, &peer->prefs, sizeof(ourprefs));
- /* Move the calling channel's native codec to the top of the preference list */
- if (c) {
- ast_debug(1, "prepending %x to prefs\n", c->nativeformats);
- ast_codec_pref_prepend(&ourprefs, c->nativeformats, 1);
- }
- ast_codec_pref_convert(&ourprefs, cai->prefs, sizeof(cai->prefs), 1);
- ast_copy_string(cai->context, peer->context, sizeof(cai->context));
- ast_copy_string(cai->peercontext, peer->peercontext, sizeof(cai->peercontext));
- ast_copy_string(cai->username, peer->username, sizeof(cai->username));
- ast_copy_string(cai->timezone, peer->zonetag, sizeof(cai->timezone));
- ast_copy_string(cai->outkey, peer->outkey, sizeof(cai->outkey));
- ast_copy_string(cai->mohinterpret, peer->mohinterpret, sizeof(cai->mohinterpret));
- ast_copy_string(cai->mohsuggest, peer->mohsuggest, sizeof(cai->mohsuggest));
- if (ast_strlen_zero(peer->dbsecret)) {
- ast_copy_string(cai->secret, peer->secret, sizeof(cai->secret));
- } else {
- char *family;
- char *key = NULL;
- family = ast_strdupa(peer->dbsecret);
- key = strchr(family, '/');
- if (key)
- *key++ = '\0';
- if (!key || ast_db_get(family, key, cai->secret, sizeof(cai->secret))) {
- ast_log(LOG_WARNING, "Unable to retrieve database password for family/key '%s'!\n", peer->dbsecret);
- goto return_unref;
- }
- }
- if (peer->addr.sin_addr.s_addr) {
- sin->sin_addr = peer->addr.sin_addr;
- sin->sin_port = peer->addr.sin_port;
- } else {
- sin->sin_addr = peer->defaddr.sin_addr;
- sin->sin_port = peer->defaddr.sin_port;
- }
- res = 0;
- return_unref:
- peer_unref(peer);
- return res;
- }
- static void __auto_congest(const void *nothing)
- {
- int callno = PTR_TO_CALLNO(nothing);
- struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_CONGESTION };
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno]) {
- iaxs[callno]->initid = -1;
- iax2_queue_frame(callno, &f);
- ast_log(LOG_NOTICE, "Auto-congesting call due to slow response\n");
- }
- ast_mutex_unlock(&iaxsl[callno]);
- }
- static int auto_congest(const void *data)
- {
- #ifdef SCHED_MULTITHREADED
- if (schedule_action(__auto_congest, data))
- #endif
- __auto_congest(data);
- return 0;
- }
- static unsigned int iax2_datetime(const char *tz)
- {
- struct timeval t = ast_tvnow();
- struct ast_tm tm;
- unsigned int tmp;
- ast_localtime(&t, &tm, ast_strlen_zero(tz) ? NULL : tz);
- tmp = (tm.tm_sec >> 1) & 0x1f; /* 5 bits of seconds */
- tmp |= (tm.tm_min & 0x3f) << 5; /* 6 bits of minutes */
- tmp |= (tm.tm_hour & 0x1f) << 11; /* 5 bits of hours */
- tmp |= (tm.tm_mday & 0x1f) << 16; /* 5 bits of day of month */
- tmp |= ((tm.tm_mon + 1) & 0xf) << 21; /* 4 bits of month */
- tmp |= ((tm.tm_year - 100) & 0x7f) << 25; /* 7 bits of year */
- return tmp;
- }
- struct parsed_dial_string {
- char *username;
- char *password;
- char *key;
- char *peer;
- char *port;
- char *exten;
- char *context;
- char *options;
- };
- static int send_apathetic_reply(unsigned short callno, unsigned short dcallno,
- struct sockaddr_in *sin, int command, int ts, unsigned char seqno,
- int sockfd, struct iax_ie_data *ied)
- {
- struct {
- struct ast_iax2_full_hdr f;
- struct iax_ie_data ied;
- } data;
- size_t size = sizeof(struct ast_iax2_full_hdr);
- if (ied) {
- size += ied->pos;
- memcpy(&data.ied, ied->buf, ied->pos);
- }
- data.f.scallno = htons(0x8000 | callno);
- data.f.dcallno = htons(dcallno);
- data.f.ts = htonl(ts);
- data.f.iseqno = seqno;
- data.f.oseqno = 0;
- data.f.type = AST_FRAME_IAX;
- data.f.csub = compress_subclass(command);
- return sendto(sockfd, &data, size, 0, (struct sockaddr *)sin, sizeof(*sin));
- }
- static void add_empty_calltoken_ie(struct chan_iax2_pvt *pvt, struct iax_ie_data *ied)
- {
- /* first make sure their are two empty bytes left in ied->buf */
- if (pvt && ied && (2 < ((int) sizeof(ied->buf) - ied->pos))) {
- ied->buf[ied->pos++] = IAX_IE_CALLTOKEN; /* type */
- ied->buf[ied->pos++] = 0; /* data size, ZERO in this case */
- pvt->calltoken_ie_len = 2;
- }
- }
- static void resend_with_token(int callno, struct iax_frame *f, const char *newtoken)
- {
- struct chan_iax2_pvt *pvt = iaxs[callno];
- int frametype = f->af.frametype;
- int subclass = f->af.subclass;
- struct {
- struct ast_iax2_full_hdr fh;
- struct iax_ie_data ied;
- } data = {
- .ied.buf = { 0 },
- .ied.pos = 0,
- };
- /* total len - header len gives us the frame's IE len */
- int ie_data_pos = f->datalen - sizeof(struct ast_iax2_full_hdr);
- if (!pvt) {
- return; /* this should not be possible if called from socket_process() */
- }
- /*
- * Check to make sure last frame sent is valid for call token resend
- * 1. Frame should _NOT_ be encrypted since it starts the IAX dialog
- * 2. Frame should _NOT_ already have a destination callno
- * 3. Frame must be a valid iax_frame subclass capable of starting dialog
- * 4. Pvt must have a calltoken_ie_len which represents the number of
- * bytes at the end of the frame used for the previous calltoken ie.
- * 5. Pvt's calltoken_ie_len must be _LESS_ than the total IE length
- * 6. Total length of f->data must be _LESS_ than size of our data struct
- * because f->data must be able to fit within data.
- */
- if (f->encmethods || f->dcallno || !iax2_allow_new(frametype, subclass, 0)
- || !pvt->calltoken_ie_len || (pvt->calltoken_ie_len > ie_data_pos) ||
- (f->datalen > sizeof(data))) {
- return; /* ignore resend, token was not valid for the dialog */
- }
- /* token is valid
- * 1. Copy frame data over
- * 2. Redo calltoken IE, it will always be the last ie in the frame.
- * NOTE: Having the ie always be last is not protocol specified,
- * it is only an implementation choice. Since we only expect the ie to
- * be last for frames we have sent, this can no way be affected by
- * another end point.
- * 3. Remove frame from queue
- * 4. Free old frame
- * 5. Clear previous seqnos
- * 6. Resend with CALLTOKEN ie.
- */
- /* ---1.--- */
- memcpy(&data, f->data, f->datalen);
- data.ied.pos = ie_data_pos;
- /* ---2.--- */
- /* move to the beginning of the calltoken ie so we can write over it */
- data.ied.pos -= pvt->calltoken_ie_len;
- iax_ie_append_str(&data.ied, IAX_IE_CALLTOKEN, newtoken);
- /* make sure to update token length incase it ever has to be stripped off again */
- pvt->calltoken_ie_len = data.ied.pos - ie_data_pos; /* new pos minus old pos tells how big token ie is */
- /* ---3.--- */
- AST_LIST_LOCK(&frame_queue);
- AST_LIST_REMOVE(&frame_queue, f, list);
- AST_LIST_UNLOCK(&frame_queue);
- /* ---4.--- */
- iax2_frame_free(f);
- /* ---5.--- */
- pvt->oseqno = 0;
- pvt->rseqno = 0;
- pvt->iseqno = 0;
- pvt->aseqno = 0;
- if (pvt->peercallno) {
- remove_by_peercallno(pvt);
- pvt->peercallno = 0;
- }
- /* ---6.--- */
- send_command(pvt, AST_FRAME_IAX, subclass, 0, data.ied.buf, data.ied.pos, -1);
- }
- static void requirecalltoken_mark_auto(const char *name, int subclass)
- {
- struct iax2_user *user = NULL;
- struct iax2_peer *peer = NULL;
- if (ast_strlen_zero(name)) {
- return; /* no username given */
- }
- if ((subclass == IAX_COMMAND_NEW) && (user = find_user(name)) && (user->calltoken_required == CALLTOKEN_AUTO)) {
- user->calltoken_required = CALLTOKEN_YES;
- } else if ((subclass != IAX_COMMAND_NEW) && (peer = find_peer(name, 1)) && (peer->calltoken_required == CALLTOKEN_AUTO)) {
- peer->calltoken_required = CALLTOKEN_YES;
- }
- if (peer) {
- peer_unref(peer);
- }
- if (user) {
- user_unref(user);
- }
- }
- /*!
- * \internal
- *
- * \brief handles calltoken logic for a received iax_frame.
- *
- * \note frametype must be AST_FRAME_IAX.
- *
- * \note
- * Three different cases are possible here.
- * Case 1. An empty calltoken is provided. This means the client supports
- * calltokens but has not yet received one from us. In this case
- * a full calltoken IE is created and sent in a calltoken fullframe.
- * Case 2. A full calltoken is received and must be checked for validity.
- * Case 3. No calltoken is received indicating that the client does not
- * support calltokens. In this case it is up to the configuration
- * to decide how this should be handled (reject or permit without calltoken)
- */
- static int handle_call_token(struct ast_iax2_full_hdr *fh, struct iax_ies *ies,
- struct sockaddr_in *sin, int fd)
- {
- #define CALLTOKEN_HASH_FORMAT "%s%d%u%d" /* address + port + ts + randomcalldata */
- #define CALLTOKEN_IE_FORMAT "%u?%s" /* time + ? + (40 char hash) */
- struct ast_str *buf = ast_str_alloca(256);
- time_t t = time(NULL);
- char hash[41]; /* 40 char sha1 hash */
- int subclass = uncompress_subclass(fh->csub);
- /* ----- Case 1 ----- */
- if (ies->calltoken && !ies->calltokendata) { /* empty calltoken is provided, client supports calltokens */
- struct iax_ie_data ied = {
- .buf = { 0 },
- .pos = 0,
- };
- /* create the hash with their address data and our timestamp */
- ast_str_set(&buf, 0, CALLTOKEN_HASH_FORMAT, ast_inet_ntoa(sin->sin_addr), sin->sin_port, (unsigned int) t, randomcalltokendata);
- ast_sha1_hash(hash, ast_str_buffer(buf));
- ast_str_set(&buf, 0, CALLTOKEN_IE_FORMAT, (unsigned int) t, hash);
- iax_ie_append_str(&ied, IAX_IE_CALLTOKEN, ast_str_buffer(buf));
- send_apathetic_reply(1, ntohs(fh->scallno), sin, IAX_COMMAND_CALLTOKEN, ntohl(fh->ts), fh->iseqno + 1, fd, &ied);
- return 1;
- /* ----- Case 2 ----- */
- } else if (ies->calltoken && ies->calltokendata) { /* calltoken received, check to see if it is valid */
- char *rec_hash = NULL; /* the received hash, make sure it matches with ours. */
- char *rec_ts = NULL; /* received timestamp */
- unsigned int rec_time; /* received time_t */
- /* split the timestamp from the hash data */
- rec_hash = strchr((char *) ies->calltokendata, '?');
- if (rec_hash) {
- *rec_hash++ = '\0';
- rec_ts = (char *) ies->calltokendata;
- }
- /* check that we have valid data before we do any comparisons */
- if (!rec_hash || !rec_ts) {
- goto reject;
- } else if (sscanf(rec_ts, "%u", &rec_time) != 1) {
- goto reject;
- }
- /* create a hash with their address and the _TOKEN'S_ timestamp */
- ast_str_set(&buf, 0, CALLTOKEN_HASH_FORMAT, ast_inet_ntoa(sin->sin_addr), sin->sin_port, (unsigned int) rec_time, randomcalltokendata);
- ast_sha1_hash(hash, ast_str_buffer(buf));
- /* compare hashes and then check timestamp delay */
- if (strcmp(hash, rec_hash)) {
- ast_log(LOG_WARNING, "Address %s failed CallToken hash inspection\n", ast_inet_ntoa(sin->sin_addr));
- goto reject; /* received hash does not match ours, reject */
- } else if ((t < rec_time) || ((t - rec_time) >= MAX_CALLTOKEN_DELAY)) {
- ast_log(LOG_WARNING, "Too much delay in IAX2 calltoken timestamp from address %s\n", ast_inet_ntoa(sin->sin_addr));
- goto reject; /* too much delay, reject */
- }
- /* at this point the call token is valid, returning 0
- * will allow socket_process to continue as usual */
- requirecalltoken_mark_auto(ies->username, subclass);
- return 0;
- /* ----- Case 3 ----- */
- } else { /* calltokens are not supported for this client, how do we respond? */
- if (calltoken_required(sin, ies->username, subclass)) {
- ast_log(LOG_ERROR, "Call rejected, CallToken Support required. If unexpected, resolve by placing address %s in the calltokenoptional list or setting user %s requirecalltoken=no\n", ast_inet_ntoa(sin->sin_addr), S_OR(ies->username, "guest"));
- goto reject;
- }
- return 0; /* calltoken is not required for this addr, so permit it. */
- }
- reject:
- /* received frame has failed calltoken inspection, send apathetic reject messages */
- if (subclass == IAX_COMMAND_REGREQ || subclass == IAX_COMMAND_REGREL) {
- send_apathetic_reply(1, ntohs(fh->scallno), sin, IAX_COMMAND_REGREJ, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
- } else {
- send_apathetic_reply(1, ntohs(fh->scallno), sin, IAX_COMMAND_REJECT, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
- }
- return 1;
- }
- /*!
- * \brief Parses an IAX dial string into its component parts.
- * \param data the string to be parsed
- * \param pds pointer to a \c struct \c parsed_dial_string to be filled in
- * \return nothing
- *
- * This function parses the string and fills the structure
- * with pointers to its component parts. The input string
- * will be modified.
- *
- * \note This function supports both plaintext passwords and RSA
- * key names; if the password string is formatted as '[keyname]',
- * then the keyname will be placed into the key field, and the
- * password field will be set to NULL.
- *
- * \note The dial string format is:
- * [username[:password]@]peer[:port][/exten[@@context]][/options]
- */
- static void parse_dial_string(char *data, struct parsed_dial_string *pds)
- {
- if (ast_strlen_zero(data))
- return;
- pds->peer = strsep(&data, "/");
- pds->exten = strsep(&data, "/");
- pds->options = data;
- if (pds->exten) {
- data = pds->exten;
- pds->exten = strsep(&data, "@");
- pds->context = data;
- }
- if (strchr(pds->peer, '@')) {
- data = pds->peer;
- pds->username = strsep(&data, "@");
- pds->peer = data;
- }
- if (pds->username) {
- data = pds->username;
- pds->username = strsep(&data, ":");
- pds->password = data;
- }
- data = pds->peer;
- pds->peer = strsep(&data, ":");
- pds->port = data;
- /* check for a key name wrapped in [] in the secret position, if found,
- move it to the key field instead
- */
- if (pds->password && (pds->password[0] == '[')) {
- pds->key = ast_strip_quoted(pds->password, "[", "]");
- pds->password = NULL;
- }
- }
- static int iax2_call(struct ast_channel *c, char *dest, int timeout)
- {
- struct sockaddr_in sin;
- char *l=NULL, *n=NULL, *tmpstr;
- struct iax_ie_data ied;
- char *defaultrdest = "s";
- unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
- struct parsed_dial_string pds;
- struct create_addr_info cai;
- struct ast_var_t *var;
- struct ast_datastore *variablestore = ast_channel_datastore_find(c, &iax2_variable_datastore_info, NULL);
- const char* osp_token_ptr;
- unsigned int osp_token_length;
- unsigned char osp_block_index;
- unsigned int osp_block_length;
- unsigned char osp_buffer[256];
- if ((c->_state != AST_STATE_DOWN) && (c->_state != AST_STATE_RESERVED)) {
- ast_log(LOG_WARNING, "Channel is already in use (%s)?\n", c->name);
- return -1;
- }
- memset(&cai, 0, sizeof(cai));
- cai.encmethods = iax2_encryption;
- memset(&pds, 0, sizeof(pds));
- tmpstr = ast_strdupa(dest);
- parse_dial_string(tmpstr, &pds);
- if (ast_strlen_zero(pds.peer)) {
- ast_log(LOG_WARNING, "No peer provided in the IAX2 dial string '%s'\n", dest);
- return -1;
- }
- if (!pds.exten) {
- pds.exten = defaultrdest;
- }
- if (create_addr(pds.peer, c, &sin, &cai)) {
- ast_log(LOG_WARNING, "No address associated with '%s'\n", pds.peer);
- return -1;
- }
- if (!pds.username && !ast_strlen_zero(cai.username))
- pds.username = cai.username;
- if (!pds.password && !ast_strlen_zero(cai.secret))
- pds.password = cai.secret;
- if (!pds.key && !ast_strlen_zero(cai.outkey))
- pds.key = cai.outkey;
- if (!pds.context && !ast_strlen_zero(cai.peercontext))
- pds.context = cai.peercontext;
- /* Keep track of the context for outgoing calls too */
- ast_copy_string(c->context, cai.context, sizeof(c->context));
- if (pds.port)
- sin.sin_port = htons(atoi(pds.port));
- l = c->cid.cid_num;
- n = c->cid.cid_name;
- /* Now build request */
- memset(&ied, 0, sizeof(ied));
- /* On new call, first IE MUST be IAX version of caller */
- iax_ie_append_short(&ied, IAX_IE_VERSION, IAX_PROTO_VERSION);
- iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, pds.exten);
- if (pds.options && strchr(pds.options, 'a')) {
- /* Request auto answer */
- iax_ie_append(&ied, IAX_IE_AUTOANSWER);
- }
- iax_ie_append_str(&ied, IAX_IE_CODEC_PREFS, cai.prefs);
- if (l) {
- iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, l);
- iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, c->cid.cid_pres);
- } else {
- if (n)
- iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, c->cid.cid_pres);
- else
- iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, AST_PRES_NUMBER_NOT_AVAILABLE);
- }
- iax_ie_append_byte(&ied, IAX_IE_CALLINGTON, c->cid.cid_ton);
- iax_ie_append_short(&ied, IAX_IE_CALLINGTNS, c->cid.cid_tns);
- if (n)
- iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, n);
- if (ast_test_flag(iaxs[callno], IAX_SENDANI) && c->cid.cid_ani)
- iax_ie_append_str(&ied, IAX_IE_CALLING_ANI, c->cid.cid_ani);
- if (!ast_strlen_zero(c->language))
- iax_ie_append_str(&ied, IAX_IE_LANGUAGE, c->language);
- if (!ast_strlen_zero(c->cid.cid_dnid))
- iax_ie_append_str(&ied, IAX_IE_DNID, c->cid.cid_dnid);
- if (!ast_strlen_zero(c->cid.cid_rdnis))
- iax_ie_append_str(&ied, IAX_IE_RDNIS, c->cid.cid_rdnis);
- if (pds.context)
- iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, pds.context);
- if (pds.username)
- iax_ie_append_str(&ied, IAX_IE_USERNAME, pds.username);
- if (cai.encmethods)
- iax_ie_append_short(&ied, IAX_IE_ENCRYPTION, cai.encmethods);
- ast_mutex_lock(&iaxsl[callno]);
- if (!ast_strlen_zero(c->context))
- ast_string_field_set(iaxs[callno], context, c->context);
- if (pds.username)
- ast_string_field_set(iaxs[callno], username, pds.username);
- iaxs[callno]->encmethods = cai.encmethods;
- iaxs[callno]->adsi = cai.adsi;
-
- ast_string_field_set(iaxs[callno], mohinterpret, cai.mohinterpret);
- ast_string_field_set(iaxs[callno], mohsuggest, cai.mohsuggest);
- if (pds.key)
- ast_string_field_set(iaxs[callno], outkey, pds.key);
- if (pds.password)
- ast_string_field_set(iaxs[callno], secret, pds.password);
- iax_ie_append_int(&ied, IAX_IE_FORMAT, c->nativeformats);
- iax_ie_append_int(&ied, IAX_IE_CAPABILITY, iaxs[callno]->capability);
- iax_ie_append_short(&ied, IAX_IE_ADSICPE, c->adsicpe);
- iax_ie_append_int(&ied, IAX_IE_DATETIME, iax2_datetime(cai.timezone));
- if (iaxs[callno]->maxtime) {
- /* Initialize pingtime and auto-congest time */
- iaxs[callno]->pingtime = iaxs[callno]->maxtime / 2;
- iaxs[callno]->initid = iax2_sched_add(sched, iaxs[callno]->maxtime * 2, auto_congest, CALLNO_TO_PTR(callno));
- } else if (autokill) {
- iaxs[callno]->pingtime = autokill / 2;
- iaxs[callno]->initid = iax2_sched_add(sched, autokill * 2, auto_congest, CALLNO_TO_PTR(callno));
- }
- /* Check if there is an OSP token */
- osp_token_ptr = pbx_builtin_getvar_helper(c, "IAX2OSPTOKEN");
- if (!ast_strlen_zero(osp_token_ptr)) {
- if ((osp_token_length = strlen(osp_token_ptr)) <= IAX_MAX_OSPTOKEN_SIZE) {
- osp_block_index = 0;
- while (osp_token_length > 0) {
- osp_block_length = IAX_MAX_OSPBLOCK_SIZE < osp_token_length ? IAX_MAX_OSPBLOCK_SIZE : osp_token_length;
- osp_buffer[0] = osp_block_index;
- memcpy(osp_buffer + 1, osp_token_ptr, osp_block_length);
- iax_ie_append_raw(&ied, IAX_IE_OSPTOKEN, osp_buffer, osp_block_length + 1);
- osp_block_index++;
- osp_token_ptr += osp_block_length;
- osp_token_length -= osp_block_length;
- }
- } else
- ast_log(LOG_WARNING, "OSP token is too long\n");
- } else if (iaxdebug)
- ast_debug(1, "OSP token is undefined\n");
- /* send the command using the appropriate socket for this peer */
- iaxs[callno]->sockfd = cai.sockfd;
- /* Add remote vars */
- if (variablestore) {
- AST_LIST_HEAD(, ast_var_t) *variablelist = variablestore->data;
- ast_debug(1, "Found an IAX variable store on this channel\n");
- AST_LIST_LOCK(variablelist);
- AST_LIST_TRAVERSE(variablelist, var, entries) {
- char tmp[256];
- int i;
- ast_debug(1, "Found IAXVAR '%s' with value '%s' (to transmit)\n", ast_var_name(var), ast_var_value(var));
- /* Automatically divide the value up into sized chunks */
- for (i = 0; i < strlen(ast_var_value(var)); i += 255 - (strlen(ast_var_name(var)) + 1)) {
- snprintf(tmp, sizeof(tmp), "%s=%s", ast_var_name(var), ast_var_value(var) + i);
- iax_ie_append_str(&ied, IAX_IE_VARIABLE, tmp);
- }
- }
- AST_LIST_UNLOCK(variablelist);
- }
- /* Transmit the string in a "NEW" request */
- add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
- send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1);
- ast_mutex_unlock(&iaxsl[callno]);
- ast_setstate(c, AST_STATE_RINGING);
- return 0;
- }
- static int iax2_hangup(struct ast_channel *c)
- {
- unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
- struct iax_ie_data ied;
- int alreadygone;
- memset(&ied, 0, sizeof(ied));
- ast_mutex_lock(&iaxsl[callno]);
- if (callno && iaxs[callno]) {
- ast_debug(1, "We're hanging up %s now...\n", c->name);
- alreadygone = ast_test_flag(iaxs[callno], IAX_ALREADYGONE);
- /* Send the hangup unless we have had a transmission error or are already gone */
- iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, (unsigned char)c->hangupcause);
- if (!iaxs[callno]->error && !alreadygone) {
- if (send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_HANGUP, 0, ied.buf, ied.pos, -1)) {
- ast_log(LOG_WARNING, "No final packet could be sent for callno %d\n", callno);
- }
- if (!iaxs[callno]) {
- ast_mutex_unlock(&iaxsl[callno]);
- return 0;
- }
- }
- /* Explicitly predestroy it */
- iax2_predestroy(callno);
- /* If we were already gone to begin with, destroy us now */
- if (iaxs[callno] && alreadygone) {
- ast_debug(1, "Really destroying %s now...\n", c->name);
- iax2_destroy(callno);
- } else if (iaxs[callno]) {
- if (ast_sched_add(sched, 10000, scheduled_destroy, CALLNO_TO_PTR(callno)) < 0) {
- ast_log(LOG_ERROR, "Unable to schedule iax2 callno %d destruction?!! Destroying immediately.\n", callno);
- iax2_destroy(callno);
- }
- }
- } else if (c->tech_pvt) {
- /* If this call no longer exists, but the channel still
- * references it we need to set the channel's tech_pvt to null
- * to avoid ast_channel_free() trying to free it.
- */
- c->tech_pvt = NULL;
- }
- ast_mutex_unlock(&iaxsl[callno]);
- ast_verb(3, "Hungup '%s'\n", c->name);
- return 0;
- }
- /*!
- * \note expects the pvt to be locked
- */
- static int wait_for_peercallno(struct chan_iax2_pvt *pvt)
- {
- unsigned short callno = pvt->callno;
- if (!pvt->peercallno) {
- /* We don't know the remote side's call number, yet. :( */
- int count = 10;
- while (count-- && pvt && !pvt->peercallno) {
- DEADLOCK_AVOIDANCE(&iaxsl[callno]);
- pvt = iaxs[callno];
- }
- if (!pvt->peercallno) {
- return -1;
- }
- }
- return 0;
- }
- static int iax2_setoption(struct ast_channel *c, int option, void *data, int datalen)
- {
- struct ast_option_header *h;
- int res;
- switch (option) {
- case AST_OPTION_TXGAIN:
- case AST_OPTION_RXGAIN:
- /* these two cannot be sent, because they require a result */
- errno = ENOSYS;
- return -1;
- default:
- {
- unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
- struct chan_iax2_pvt *pvt;
- ast_mutex_lock(&iaxsl[callno]);
- pvt = iaxs[callno];
- if (wait_for_peercallno(pvt)) {
- ast_mutex_unlock(&iaxsl[callno]);
- return -1;
- }
- ast_mutex_unlock(&iaxsl[callno]);
- if (!(h = ast_malloc(datalen + sizeof(*h)))) {
- return -1;
- }
- h->flag = AST_OPTION_FLAG_REQUEST;
- h->option = htons(option);
- memcpy(h->data, data, datalen);
- res = send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_CONTROL,
- AST_CONTROL_OPTION, 0, (unsigned char *) h,
- datalen + sizeof(*h), -1);
- ast_free(h);
- return res;
- }
- }
- }
- static struct ast_frame *iax2_read(struct ast_channel *c)
- {
- ast_log(LOG_NOTICE, "I should never be called!\n");
- return &ast_null_frame;
- }
- static int iax2_start_transfer(unsigned short callno0, unsigned short callno1, int mediaonly)
- {
- int res;
- struct iax_ie_data ied0;
- struct iax_ie_data ied1;
- unsigned int transferid = (unsigned int)ast_random();
- memset(&ied0, 0, sizeof(ied0));
- iax_ie_append_addr(&ied0, IAX_IE_APPARENT_ADDR, &iaxs[callno1]->addr);
- iax_ie_append_short(&ied0, IAX_IE_CALLNO, iaxs[callno1]->peercallno);
- iax_ie_append_int(&ied0, IAX_IE_TRANSFERID, transferid);
- memset(&ied1, 0, sizeof(ied1));
- iax_ie_append_addr(&ied1, IAX_IE_APPARENT_ADDR, &iaxs[callno0]->addr);
- iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[callno0]->peercallno);
- iax_ie_append_int(&ied1, IAX_IE_TRANSFERID, transferid);
-
- res = send_command(iaxs[callno0], AST_FRAME_IAX, IAX_COMMAND_TXREQ, 0, ied0.buf, ied0.pos, -1);
- if (res)
- return -1;
- res = send_command(iaxs[callno1], AST_FRAME_IAX, IAX_COMMAND_TXREQ, 0, ied1.buf, ied1.pos, -1);
- if (res)
- return -1;
- iaxs[callno0]->transferring = mediaonly ? TRANSFER_MBEGIN : TRANSFER_BEGIN;
- iaxs[callno1]->transferring = mediaonly ? TRANSFER_MBEGIN : TRANSFER_BEGIN;
- return 0;
- }
- static void lock_both(unsigned short callno0, unsigned short callno1)
- {
- ast_mutex_lock(&iaxsl[callno0]);
- while (ast_mutex_trylock(&iaxsl[callno1])) {
- DEADLOCK_AVOIDANCE(&iaxsl[callno0]);
- }
- }
- static void unlock_both(unsigned short callno0, unsigned short callno1)
- {
- ast_mutex_unlock(&iaxsl[callno1]);
- ast_mutex_unlock(&iaxsl[callno0]);
- }
- static enum ast_bridge_result iax2_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
- {
- struct ast_channel *cs[3];
- struct ast_channel *who, *other;
- int to = -1;
- int res = -1;
- int transferstarted=0;
- struct ast_frame *f;
- unsigned short callno0 = PTR_TO_CALLNO(c0->tech_pvt);
- unsigned short callno1 = PTR_TO_CALLNO(c1->tech_pvt);
- struct timeval waittimer = {0, 0}, tv;
- /* We currently do not support native bridging if a timeoutms value has been provided */
- if (timeoutms > 0) {
- return AST_BRIDGE_FAILED;
- }
- timeoutms = -1;
- lock_both(callno0, callno1);
- if (!iaxs[callno0] || !iaxs[callno1]) {
- unlock_both(callno0, callno1);
- return AST_BRIDGE_FAILED;
- }
- /* Put them in native bridge mode */
- if (!(flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))) {
- iaxs[callno0]->bridgecallno = callno1;
- iaxs[callno1]->bridgecallno = callno0;
- }
- /* If the bridge got retried, don't queue up more packets - the transfer request will be retransmitted as necessary */
- if (iaxs[callno0]->transferring && iaxs[callno1]->transferring) {
- transferstarted = 1;
- }
- unlock_both(callno0, callno1);
- /* If not, try to bridge until we can execute a transfer, if we can */
- cs[0] = c0;
- cs[1] = c1;
- for (/* ever */;;) {
- /* Check in case we got masqueraded into */
- if ((c0->tech != &iax2_tech) || (c1->tech != &iax2_tech)) {
- ast_verb(3, "Can't masquerade, we're different...\n");
- /* Remove from native mode */
- if (c0->tech == &iax2_tech) {
- ast_mutex_lock(&iaxsl[callno0]);
- iaxs[callno0]->bridgecallno = 0;
- ast_mutex_unlock(&iaxsl[callno0]);
- }
- if (c1->tech == &iax2_tech) {
- ast_mutex_lock(&iaxsl[callno1]);
- iaxs[callno1]->bridgecallno = 0;
- ast_mutex_unlock(&iaxsl[callno1]);
- }
- return AST_BRIDGE_FAILED_NOWARN;
- }
- if (c0->nativeformats != c1->nativeformats) {
- char buf0[255];
- char buf1[255];
- ast_getformatname_multiple(buf0, sizeof(buf0) -1, c0->nativeformats);
- ast_getformatname_multiple(buf1, sizeof(buf1) -1, c1->nativeformats);
- ast_verb(3, "Operating with different codecs %d[%s] %d[%s] , can't native bridge...\n", c0->nativeformats, buf0, c1->nativeformats, buf1);
- /* Remove from native mode */
- lock_both(callno0, callno1);
- if (iaxs[callno0])
- iaxs[callno0]->bridgecallno = 0;
- if (iaxs[callno1])
- iaxs[callno1]->bridgecallno = 0;
- unlock_both(callno0, callno1);
- return AST_BRIDGE_FAILED_NOWARN;
- }
- /* check if transfered and if we really want native bridging */
- if (!transferstarted && !ast_test_flag(iaxs[callno0], IAX_NOTRANSFER) && !ast_test_flag(iaxs[callno1], IAX_NOTRANSFER)) {
- /* Try the transfer */
- if (iax2_start_transfer(callno0, callno1, (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1)) ||
- ast_test_flag(iaxs[callno0], IAX_TRANSFERMEDIA) | ast_test_flag(iaxs[callno1], IAX_TRANSFERMEDIA)))
- ast_log(LOG_WARNING, "Unable to start the transfer\n");
- transferstarted = 1;
- }
- if ((iaxs[callno0]->transferring == TRANSFER_RELEASED) && (iaxs[callno1]->transferring == TRANSFER_RELEASED)) {
- /* Call has been transferred. We're no longer involved */
- tv = ast_tvnow();
- if (ast_tvzero(waittimer)) {
- waittimer = tv;
- } else if (tv.tv_sec - waittimer.tv_sec > IAX_LINGER_TIMEOUT) {
- c0->_softhangup |= AST_SOFTHANGUP_DEV;
- c1->_softhangup |= AST_SOFTHANGUP_DEV;
- *fo = NULL;
- *rc = c0;
- res = AST_BRIDGE_COMPLETE;
- break;
- }
- }
- to = 1000;
- who = ast_waitfor_n(cs, 2, &to);
- if (timeoutms > -1) {
- timeoutms -= (1000 - to);
- if (timeoutms < 0)
- timeoutms = 0;
- }
- if (!who) {
- if (!timeoutms) {
- res = AST_BRIDGE_RETRY;
- break;
- }
- if (ast_check_hangup(c0) || ast_check_hangup(c1)) {
- res = AST_BRIDGE_FAILED;
- break;
- }
- continue;
- }
- f = ast_read(who);
- if (!f) {
- *fo = NULL;
- *rc = who;
- res = AST_BRIDGE_COMPLETE;
- break;
- }
- if ((f->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS) && (f->subclass != AST_CONTROL_SRCUPDATE)) {
- *fo = f;
- *rc = who;
- res = AST_BRIDGE_COMPLETE;
- break;
- }
- other = (who == c0) ? c1 : c0; /* the 'other' channel */
- if ((f->frametype == AST_FRAME_VOICE) ||
- (f->frametype == AST_FRAME_TEXT) ||
- (f->frametype == AST_FRAME_VIDEO) ||
- (f->frametype == AST_FRAME_IMAGE) ||
- (f->frametype == AST_FRAME_DTMF) ||
- (f->frametype == AST_FRAME_CONTROL)) {
- /* monitored dtmf take out of the bridge.
- * check if we monitor the specific source.
- */
- int monitored_source = (who == c0) ? AST_BRIDGE_DTMF_CHANNEL_0 : AST_BRIDGE_DTMF_CHANNEL_1;
- if (f->frametype == AST_FRAME_DTMF && (flags & monitored_source)) {
- *rc = who;
- *fo = f;
- res = AST_BRIDGE_COMPLETE;
- /* Remove from native mode */
- break;
- }
- /* everything else goes to the other side */
- ast_write(other, f);
- }
- ast_frfree(f);
- /* Swap who gets priority */
- cs[2] = cs[0];
- cs[0] = cs[1];
- cs[1] = cs[2];
- }
- lock_both(callno0, callno1);
- if(iaxs[callno0])
- iaxs[callno0]->bridgecallno = 0;
- if(iaxs[callno1])
- iaxs[callno1]->bridgecallno = 0;
- unlock_both(callno0, callno1);
- return res;
- }
- static int iax2_answer(struct ast_channel *c)
- {
- unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
- ast_debug(1, "Answering IAX2 call\n");
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno])
- iax2_ami_channelupdate(iaxs[callno]);
- ast_mutex_unlock(&iaxsl[callno]);
- return send_command_locked(callno, AST_FRAME_CONTROL, AST_CONTROL_ANSWER, 0, NULL, 0, -1);
- }
- static int iax2_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen)
- {
- unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
- struct chan_iax2_pvt *pvt;
- int res = 0;
- if (iaxdebug)
- ast_debug(1, "Indicating condition %d\n", condition);
- ast_mutex_lock(&iaxsl[callno]);
- pvt = iaxs[callno];
- if (wait_for_peercallno(pvt)) {
- res = -1;
- goto done;
- }
- switch (condition) {
- case AST_CONTROL_HOLD:
- if (strcasecmp(pvt->mohinterpret, "passthrough")) {
- ast_moh_start(c, data, pvt->mohinterpret);
- goto done;
- }
- break;
- case AST_CONTROL_UNHOLD:
- if (strcasecmp(pvt->mohinterpret, "passthrough")) {
- ast_moh_stop(c);
- goto done;
- }
- }
- res = send_command(pvt, AST_FRAME_CONTROL, condition, 0, data, datalen, -1);
- done:
- ast_mutex_unlock(&iaxsl[callno]);
- return res;
- }
-
- static int iax2_transfer(struct ast_channel *c, const char *dest)
- {
- unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
- struct iax_ie_data ied = { "", };
- char tmp[256], *context;
- ast_copy_string(tmp, dest, sizeof(tmp));
- context = strchr(tmp, '@');
- if (context) {
- *context = '\0';
- context++;
- }
- iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, tmp);
- if (context)
- iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, context);
- ast_debug(1, "Transferring '%s' to '%s'\n", c->name, dest);
- return send_command_locked(callno, AST_FRAME_IAX, IAX_COMMAND_TRANSFER, 0, ied.buf, ied.pos, -1);
- }
-
- static int iax2_getpeertrunk(struct sockaddr_in sin)
- {
- struct iax2_peer *peer;
- int res = 0;
- struct ao2_iterator i;
- i = ao2_iterator_init(peers, 0);
- while ((peer = ao2_iterator_next(&i))) {
- if ((peer->addr.sin_addr.s_addr == sin.sin_addr.s_addr) &&
- (peer->addr.sin_port == sin.sin_port)) {
- res = ast_test_flag(peer, IAX_TRUNK);
- peer_unref(peer);
- break;
- }
- peer_unref(peer);
- }
- ao2_iterator_destroy(&i);
- return res;
- }
- /*! \brief Create new call, interface with the PBX core */
- static struct ast_channel *ast_iax2_new(int callno, int state, int capability)
- {
- struct ast_channel *tmp;
- struct chan_iax2_pvt *i;
- struct ast_variable *v = NULL;
- if (!(i = iaxs[callno])) {
- ast_log(LOG_WARNING, "No IAX2 pvt found for callno '%d' !\n", callno);
- return NULL;
- }
- /* Don't hold call lock */
- ast_mutex_unlock(&iaxsl[callno]);
- tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "IAX2/%s-%d", i->host, i->callno);
- ast_mutex_lock(&iaxsl[callno]);
- if (i != iaxs[callno]) {
- if (tmp) {
- /* unlock and relock iaxsl[callno] to preserve locking order */
- ast_mutex_unlock(&iaxsl[callno]);
- ast_channel_free(tmp);
- ast_mutex_lock(&iaxsl[callno]);
- }
- return NULL;
- }
- iax2_ami_channelupdate(i);
- if (!tmp)
- return NULL;
- tmp->tech = &iax2_tech;
- /* We can support any format by default, until we get restricted */
- tmp->nativeformats = capability;
- tmp->readformat = tmp->rawreadformat = ast_best_codec(capability);
- tmp->writeformat = tmp->rawwriteformat = ast_best_codec(capability);
- tmp->tech_pvt = CALLNO_TO_PTR(i->callno);
- /* Don't use ast_set_callerid() here because it will
- * generate a NewCallerID event before the NewChannel event */
- if (!ast_strlen_zero(i->ani))
- tmp->cid.cid_ani = ast_strdup(i->ani);
- else
- tmp->cid.cid_ani = ast_strdup(i->cid_num);
- tmp->cid.cid_dnid = ast_strdup(i->dnid);
- tmp->cid.cid_rdnis = ast_strdup(i->rdnis);
- tmp->cid.cid_pres = i->calling_pres;
- tmp->cid.cid_ton = i->calling_ton;
- tmp->cid.cid_tns = i->calling_tns;
- if (!ast_strlen_zero(i->language))
- ast_string_field_set(tmp, language, i->language);
- if (!ast_strlen_zero(i->accountcode))
- ast_string_field_set(tmp, accountcode, i->accountcode);
- if (i->amaflags)
- tmp->amaflags = i->amaflags;
- ast_copy_string(tmp->context, i->context, sizeof(tmp->context));
- ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));
- if (i->adsi)
- tmp->adsicpe = i->peeradsicpe;
- else
- tmp->adsicpe = AST_ADSI_UNAVAILABLE;
- i->owner = tmp;
- i->capability = capability;
- /* Set inherited variables */
- if (i->vars) {
- for (v = i->vars ; v ; v = v->next)
- pbx_builtin_setvar_helper(tmp, v->name, v->value);
- }
- if (i->iaxvars) {
- struct ast_datastore *variablestore;
- struct ast_variable *var, *prev = NULL;
- AST_LIST_HEAD(, ast_var_t) *varlist;
- ast_debug(1, "Loading up the channel with IAXVARs\n");
- varlist = ast_calloc(1, sizeof(*varlist));
- variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
- if (variablestore && varlist) {
- variablestore->data = varlist;
- variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
- AST_LIST_HEAD_INIT(varlist);
- for (var = i->iaxvars; var; var = var->next) {
- struct ast_var_t *newvar = ast_var_assign(var->name, var->value);
- if (prev)
- ast_free(prev);
- prev = var;
- if (!newvar) {
- /* Don't abort list traversal, as this would leave i->iaxvars in an inconsistent state. */
- ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
- } else {
- AST_LIST_INSERT_TAIL(varlist, newvar, entries);
- }
- }
- if (prev)
- ast_free(prev);
- i->iaxvars = NULL;
- ast_channel_datastore_add(i->owner, variablestore);
- } else {
- if (variablestore) {
- ast_channel_datastore_free(variablestore);
- }
- if (varlist) {
- ast_free(varlist);
- }
- }
- }
- if (state != AST_STATE_DOWN) {
- if (ast_pbx_start(tmp)) {
- ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
- ast_hangup(tmp);
- i->owner = NULL;
- return NULL;
- }
- }
- ast_module_ref(ast_module_info->self);
- return tmp;
- }
- static unsigned int calc_txpeerstamp(struct iax2_trunk_peer *tpeer, int sampms, struct timeval *tv)
- {
- unsigned long int mssincetx; /* unsigned to handle overflows */
- long int ms, pred;
- tpeer->trunkact = *tv;
- mssincetx = ast_tvdiff_ms(*tv, tpeer->lasttxtime);
- if (mssincetx > 5000 || ast_tvzero(tpeer->txtrunktime)) {
- /* If it's been at least 5 seconds since the last time we transmitted on this trunk, reset our timers */
- tpeer->txtrunktime = *tv;
- tpeer->lastsent = 999999;
- }
- /* Update last transmit time now */
- tpeer->lasttxtime = *tv;
-
- /* Calculate ms offset */
- ms = ast_tvdiff_ms(*tv, tpeer->txtrunktime);
- /* Predict from last value */
- pred = tpeer->lastsent + sampms;
- if (abs(ms - pred) < MAX_TIMESTAMP_SKEW)
- ms = pred;
-
- /* We never send the same timestamp twice, so fudge a little if we must */
- if (ms == tpeer->lastsent)
- ms = tpeer->lastsent + 1;
- tpeer->lastsent = ms;
- return ms;
- }
- static unsigned int fix_peerts(struct timeval *tv, int callno, unsigned int ts)
- {
- long ms; /* NOT unsigned */
- if (ast_tvzero(iaxs[callno]->rxcore)) {
- /* Initialize rxcore time if appropriate */
- iaxs[callno]->rxcore = ast_tvnow();
- /* Round to nearest 20ms so traces look pretty */
- iaxs[callno]->rxcore.tv_usec -= iaxs[callno]->rxcore.tv_usec % 20000;
- }
- /* Calculate difference between trunk and channel */
- ms = ast_tvdiff_ms(*tv, iaxs[callno]->rxcore);
- /* Return as the sum of trunk time and the difference between trunk and real time */
- return ms + ts;
- }
- static unsigned int calc_timestamp(struct chan_iax2_pvt *p, unsigned int ts, struct ast_frame *f)
- {
- int ms;
- int voice = 0;
- int genuine = 0;
- int adjust;
- int rate = ast_format_rate(f->subclass) / 1000;
- struct timeval *delivery = NULL;
- /* What sort of frame do we have?: voice is self-explanatory
- "genuine" means an IAX frame - things like LAGRQ/RP, PING/PONG, ACK
- non-genuine frames are CONTROL frames [ringing etc], DTMF
- The "genuine" distinction is needed because genuine frames must get a clock-based timestamp,
- the others need a timestamp slaved to the voice frames so that they go in sequence
- */
- if (f) {
- if (f->frametype == AST_FRAME_VOICE) {
- voice = 1;
- delivery = &f->delivery;
- } else if (f->frametype == AST_FRAME_IAX) {
- genuine = 1;
- } else if (f->frametype == AST_FRAME_CNG) {
- p->notsilenttx = 0;
- }
- }
- if (ast_tvzero(p->offset)) {
- p->offset = ast_tvnow();
- /* Round to nearest 20ms for nice looking traces */
- p->offset.tv_usec -= p->offset.tv_usec % 20000;
- }
- /* If the timestamp is specified, just send it as is */
- if (ts)
- return ts;
- /* If we have a time that the frame arrived, always use it to make our timestamp */
- if (delivery && !ast_tvzero(*delivery)) {
- ms = ast_tvdiff_ms(*delivery, p->offset);
- if (ms < 0) {
- ms = 0;
- }
- if (iaxdebug)
- ast_debug(3, "calc_timestamp: call %d/%d: Timestamp slaved to delivery time\n", p->callno, iaxs[p->callno]->peercallno);
- } else {
- ms = ast_tvdiff_ms(ast_tvnow(), p->offset);
- if (ms < 0)
- ms = 0;
- if (voice) {
- /* On a voice frame, use predicted values if appropriate */
- if (p->notsilenttx && abs(ms - p->nextpred) <= MAX_TIMESTAMP_SKEW) {
- /* Adjust our txcore, keeping voice and non-voice synchronized */
- /* AN EXPLANATION:
- When we send voice, we usually send "calculated" timestamps worked out
- on the basis of the number of samples sent. When we send other frames,
- we usually send timestamps worked out from the real clock.
- The problem is that they can tend to drift out of step because the
- source channel's clock and our clock may not be exactly at the same rate.
- We fix this by continuously "tweaking" p->offset. p->offset is "time zero"
- for this call. Moving it adjusts timestamps for non-voice frames.
- We make the adjustment in the style of a moving average. Each time we
- adjust p->offset by 10% of the difference between our clock-derived
- timestamp and the predicted timestamp. That's why you see "10000"
- below even though IAX2 timestamps are in milliseconds.
- The use of a moving average avoids offset moving too radically.
- Generally, "adjust" roams back and forth around 0, with offset hardly
- changing at all. But if a consistent different starts to develop it
- will be eliminated over the course of 10 frames (200-300msecs)
- */
- adjust = (ms - p->nextpred);
- if (adjust < 0)
- p->offset = ast_tvsub(p->offset, ast_samp2tv(abs(adjust), 10000));
- else if (adjust > 0)
- p->offset = ast_tvadd(p->offset, ast_samp2tv(adjust, 10000));
- if (!p->nextpred) {
- p->nextpred = ms; /*f->samples / rate;*/
- if (p->nextpred <= p->lastsent)
- p->nextpred = p->lastsent + 3;
- }
- ms = p->nextpred;
- } else {
- /* in this case, just use the actual
- * time, since we're either way off
- * (shouldn't happen), or we're ending a
- * silent period -- and seed the next
- * predicted time. Also, round ms to the
- * next multiple of frame size (so our
- * silent periods are multiples of
- * frame size too) */
- if (iaxdebug && abs(ms - p->nextpred) > MAX_TIMESTAMP_SKEW )
- ast_debug(1, "predicted timestamp skew (%u) > max (%u), using real ts instead.\n",
- abs(ms - p->nextpred), MAX_TIMESTAMP_SKEW);
- if (f->samples >= rate) /* check to make sure we dont core dump */
- {
- int diff = ms % (f->samples / rate);
- if (diff)
- ms += f->samples/rate - diff;
- }
- p->nextpred = ms;
- p->notsilenttx = 1;
- }
- } else if ( f->frametype == AST_FRAME_VIDEO ) {
- /*
- * IAX2 draft 03 says that timestamps MUST be in order.
- * It does not say anything about several frames having the same timestamp
- * When transporting video, we can have a frame that spans multiple iax packets
- * (so called slices), so it would make sense to use the same timestamp for all of
- * them
- * We do want to make sure that frames don't go backwards though
- */
- if ( (unsigned int)ms < p->lastsent )
- ms = p->lastsent;
- } else {
- /* On a dataframe, use last value + 3 (to accomodate jitter buffer shrinking) if appropriate unless
- it's a genuine frame */
- if (genuine) {
- /* genuine (IAX LAGRQ etc) must keep their clock-based stamps */
- if (ms <= p->lastsent)
- ms = p->lastsent + 3;
- } else if (abs(ms - p->lastsent) <= MAX_TIMESTAMP_SKEW) {
- /* non-genuine frames (!?) (DTMF, CONTROL) should be pulled into the predicted stream stamps */
- ms = p->lastsent + 3;
- }
- }
- }
- p->lastsent = ms;
- if (voice)
- p->nextpred = p->nextpred + f->samples / rate;
- return ms;
- }
- static unsigned int calc_rxstamp(struct chan_iax2_pvt *p, unsigned int offset)
- {
- /* Returns where in "receive time" we are. That is, how many ms
- since we received (or would have received) the frame with timestamp 0 */
- int ms;
- #ifdef IAXTESTS
- int jit;
- #endif /* IAXTESTS */
- /* Setup rxcore if necessary */
- if (ast_tvzero(p->rxcore)) {
- p->rxcore = ast_tvnow();
- if (iaxdebug)
- ast_debug(1, "calc_rxstamp: call=%d: rxcore set to %d.%6.6d - %dms\n",
- p->callno, (int)(p->rxcore.tv_sec), (int)(p->rxcore.tv_usec), offset);
- p->rxcore = ast_tvsub(p->rxcore, ast_samp2tv(offset, 1000));
- #if 1
- if (iaxdebug)
- ast_debug(1, "calc_rxstamp: call=%d: works out as %d.%6.6d\n",
- p->callno, (int)(p->rxcore.tv_sec),(int)( p->rxcore.tv_usec));
- #endif
- }
- ms = ast_tvdiff_ms(ast_tvnow(), p->rxcore);
- #ifdef IAXTESTS
- if (test_jit) {
- if (!test_jitpct || ((100.0 * ast_random() / (RAND_MAX + 1.0)) < test_jitpct)) {
- jit = (int)((float)test_jit * ast_random() / (RAND_MAX + 1.0));
- if ((int)(2.0 * ast_random() / (RAND_MAX + 1.0)))
- jit = -jit;
- ms += jit;
- }
- }
- if (test_late) {
- ms += test_late;
- test_late = 0;
- }
- #endif /* IAXTESTS */
- return ms;
- }
- static struct iax2_trunk_peer *find_tpeer(struct sockaddr_in *sin, int fd)
- {
- struct iax2_trunk_peer *tpeer = NULL;
-
- /* Finds and locks trunk peer */
- AST_LIST_LOCK(&tpeers);
- AST_LIST_TRAVERSE(&tpeers, tpeer, list) {
- if (!inaddrcmp(&tpeer->addr, sin)) {
- ast_mutex_lock(&tpeer->lock);
- break;
- }
- }
- if (!tpeer) {
- if ((tpeer = ast_calloc(1, sizeof(*tpeer)))) {
- ast_mutex_init(&tpeer->lock);
- tpeer->lastsent = 9999;
- memcpy(&tpeer->addr, sin, sizeof(tpeer->addr));
- tpeer->trunkact = ast_tvnow();
- ast_mutex_lock(&tpeer->lock);
- tpeer->sockfd = fd;
- #ifdef SO_NO_CHECK
- setsockopt(tpeer->sockfd, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums));
- #endif
- ast_debug(1, "Created trunk peer for '%s:%d'\n", ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port));
- AST_LIST_INSERT_TAIL(&tpeers, tpeer, list);
- }
- }
- AST_LIST_UNLOCK(&tpeers);
- return tpeer;
- }
- static int iax2_trunk_queue(struct chan_iax2_pvt *pvt, struct iax_frame *fr)
- {
- struct ast_frame *f;
- struct iax2_trunk_peer *tpeer;
- void *tmp, *ptr;
- struct timeval now;
- int res;
- struct ast_iax2_meta_trunk_entry *met;
- struct ast_iax2_meta_trunk_mini *mtm;
- f = &fr->af;
- tpeer = find_tpeer(&pvt->addr, pvt->sockfd);
- if (tpeer) {
- if (tpeer->trunkdatalen + f->datalen + 4 >= tpeer->trunkdataalloc) {
- /* Need to reallocate space */
- if (tpeer->trunkdataalloc < trunkmaxsize) {
- if (!(tmp = ast_realloc(tpeer->trunkdata, tpeer->trunkdataalloc + DEFAULT_TRUNKDATA + IAX2_TRUNK_PREFACE))) {
- ast_mutex_unlock(&tpeer->lock);
- return -1;
- }
-
- tpeer->trunkdataalloc += DEFAULT_TRUNKDATA;
- tpeer->trunkdata = tmp;
- ast_debug(1, "Expanded trunk '%s:%d' to %d bytes\n", ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port), tpeer->trunkdataalloc);
- } else {
- ast_log(LOG_WARNING, "Maximum trunk data space exceeded to %s:%d\n", ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port));
- ast_mutex_unlock(&tpeer->lock);
- return -1;
- }
- }
- /* Append to meta frame */
- ptr = tpeer->trunkdata + IAX2_TRUNK_PREFACE + tpeer->trunkdatalen;
- if (ast_test_flag(&globalflags, IAX_TRUNKTIMESTAMPS)) {
- mtm = (struct ast_iax2_meta_trunk_mini *)ptr;
- mtm->len = htons(f->datalen);
- mtm->mini.callno = htons(pvt->callno);
- mtm->mini.ts = htons(0xffff & fr->ts);
- ptr += sizeof(struct ast_iax2_meta_trunk_mini);
- tpeer->trunkdatalen += sizeof(struct ast_iax2_meta_trunk_mini);
- } else {
- met = (struct ast_iax2_meta_trunk_entry *)ptr;
- /* Store call number and length in meta header */
- met->callno = htons(pvt->callno);
- met->len = htons(f->datalen);
- /* Advance pointers/decrease length past trunk entry header */
- ptr += sizeof(struct ast_iax2_meta_trunk_entry);
- tpeer->trunkdatalen += sizeof(struct ast_iax2_meta_trunk_entry);
- }
- /* Copy actual trunk data */
- memcpy(ptr, f->data, f->datalen);
- tpeer->trunkdatalen += f->datalen;
- tpeer->calls++;
- /* track the largest mtu we actually have sent */
- if (tpeer->trunkdatalen + f->datalen + 4 > trunk_maxmtu)
- trunk_maxmtu = tpeer->trunkdatalen + f->datalen + 4 ;
- /* if we have enough for a full MTU, ship it now without waiting */
- if (global_max_trunk_mtu > 0 && tpeer->trunkdatalen + f->datalen + 4 >= global_max_trunk_mtu) {
- now = ast_tvnow();
- res = send_trunk(tpeer, &now);
- trunk_untimed ++;
- }
- ast_mutex_unlock(&tpeer->lock);
- }
- return 0;
- }
- /* IAX2 encryption requires 16 to 32 bytes of random padding to be present
- * before the encryption data. This function randomizes that data. */
- static void build_rand_pad(unsigned char *buf, ssize_t len)
- {
- long tmp;
- for (tmp = ast_random(); len > 0; tmp = ast_random()) {
- memcpy(buf, (unsigned char *) &tmp, (len > sizeof(tmp)) ? sizeof(tmp) : len);
- buf += sizeof(tmp);
- len -= sizeof(tmp);
- }
- }
- static void build_encryption_keys(const unsigned char *digest, struct chan_iax2_pvt *pvt)
- {
- build_ecx_key(digest, pvt);
- ast_aes_decrypt_key(digest, &pvt->dcx);
- }
- static void build_ecx_key(const unsigned char *digest, struct chan_iax2_pvt *pvt)
- {
- /* it is required to hold the corresponding decrypt key to our encrypt key
- * in the pvt struct because queued frames occasionally need to be decrypted and
- * re-encrypted when updated for a retransmission */
- build_rand_pad(pvt->semirand, sizeof(pvt->semirand));
- ast_aes_encrypt_key(digest, &pvt->ecx);
- ast_aes_decrypt_key(digest, &pvt->mydcx);
- }
- static void memcpy_decrypt(unsigned char *dst, const unsigned char *src, int len, ast_aes_decrypt_key *dcx)
- {
- #if 0
- /* Debug with "fake encryption" */
- int x;
- if (len % 16)
- ast_log(LOG_WARNING, "len should be multiple of 16, not %d!\n", len);
- for (x=0;x<len;x++)
- dst[x] = src[x] ^ 0xff;
- #else
- unsigned char lastblock[16] = { 0 };
- int x;
- while(len > 0) {
- ast_aes_decrypt(src, dst, dcx);
- for (x=0;x<16;x++)
- dst[x] ^= lastblock[x];
- memcpy(lastblock, src, sizeof(lastblock));
- dst += 16;
- src += 16;
- len -= 16;
- }
- #endif
- }
- static void memcpy_encrypt(unsigned char *dst, const unsigned char *src, int len, ast_aes_encrypt_key *ecx)
- {
- #if 0
- /* Debug with "fake encryption" */
- int x;
- if (len % 16)
- ast_log(LOG_WARNING, "len should be multiple of 16, not %d!\n", len);
- for (x=0;x<len;x++)
- dst[x] = src[x] ^ 0xff;
- #else
- unsigned char curblock[16] = { 0 };
- int x;
- while(len > 0) {
- for (x=0;x<16;x++)
- curblock[x] ^= src[x];
- ast_aes_encrypt(curblock, dst, ecx);
- memcpy(curblock, dst, sizeof(curblock));
- dst += 16;
- src += 16;
- len -= 16;
- }
- #endif
- }
- static int decode_frame(ast_aes_decrypt_key *dcx, struct ast_iax2_full_hdr *fh, struct ast_frame *f, int *datalen)
- {
- int padding;
- unsigned char *workspace;
- workspace = alloca(*datalen);
- memset(f, 0, sizeof(*f));
- if (ntohs(fh->scallno) & IAX_FLAG_FULL) {
- struct ast_iax2_full_enc_hdr *efh = (struct ast_iax2_full_enc_hdr *)fh;
- if (*datalen < 16 + sizeof(struct ast_iax2_full_hdr))
- return -1;
- /* Decrypt */
- memcpy_decrypt(workspace, efh->encdata, *datalen - sizeof(struct ast_iax2_full_enc_hdr), dcx);
- padding = 16 + (workspace[15] & 0x0f);
- if (iaxdebug)
- ast_debug(1, "Decoding full frame with length %d (padding = %d) (15=%02x)\n", *datalen, padding, workspace[15]);
- if (*datalen < padding + sizeof(struct ast_iax2_full_hdr))
- return -1;
- *datalen -= padding;
- memcpy(efh->encdata, workspace + padding, *datalen - sizeof(struct ast_iax2_full_enc_hdr));
- f->frametype = fh->type;
- if (f->frametype == AST_FRAME_VIDEO) {
- f->subclass = uncompress_subclass(fh->csub & ~0x40) | ((fh->csub >> 6) & 0x1);
- } else {
- f->subclass = uncompress_subclass(fh->csub);
- }
- } else {
- struct ast_iax2_mini_enc_hdr *efh = (struct ast_iax2_mini_enc_hdr *)fh;
- if (iaxdebug)
- ast_debug(1, "Decoding mini with length %d\n", *datalen);
- if (*datalen < 16 + sizeof(struct ast_iax2_mini_hdr))
- return -1;
- /* Decrypt */
- memcpy_decrypt(workspace, efh->encdata, *datalen - sizeof(struct ast_iax2_mini_enc_hdr), dcx);
- padding = 16 + (workspace[15] & 0x0f);
- if (*datalen < padding + sizeof(struct ast_iax2_mini_hdr))
- return -1;
- *datalen -= padding;
- memcpy(efh->encdata, workspace + padding, *datalen - sizeof(struct ast_iax2_mini_enc_hdr));
- }
- return 0;
- }
- static int encrypt_frame(ast_aes_encrypt_key *ecx, struct ast_iax2_full_hdr *fh, unsigned char *poo, int *datalen)
- {
- int padding;
- unsigned char *workspace;
- workspace = alloca(*datalen + 32);
- if (!workspace)
- return -1;
- if (ntohs(fh->scallno) & IAX_FLAG_FULL) {
- struct ast_iax2_full_enc_hdr *efh = (struct ast_iax2_full_enc_hdr *)fh;
- if (iaxdebug)
- ast_debug(1, "Encoding full frame %d/%d with length %d\n", fh->type, fh->csub, *datalen);
- padding = 16 - ((*datalen - sizeof(struct ast_iax2_full_enc_hdr)) % 16);
- padding = 16 + (padding & 0xf);
- memcpy(workspace, poo, padding);
- memcpy(workspace + padding, efh->encdata, *datalen - sizeof(struct ast_iax2_full_enc_hdr));
- workspace[15] &= 0xf0;
- workspace[15] |= (padding & 0xf);
- if (iaxdebug)
- ast_debug(1, "Encoding full frame %d/%d with length %d + %d padding (15=%02x)\n", fh->type, fh->csub, *datalen, padding, workspace[15]);
- *datalen += padding;
- memcpy_encrypt(efh->encdata, workspace, *datalen - sizeof(struct ast_iax2_full_enc_hdr), ecx);
- if (*datalen >= 32 + sizeof(struct ast_iax2_full_enc_hdr))
- memcpy(poo, workspace + *datalen - 32, 32);
- } else {
- struct ast_iax2_mini_enc_hdr *efh = (struct ast_iax2_mini_enc_hdr *)fh;
- if (iaxdebug)
- ast_debug(1, "Encoding mini frame with length %d\n", *datalen);
- padding = 16 - ((*datalen - sizeof(struct ast_iax2_mini_enc_hdr)) % 16);
- padding = 16 + (padding & 0xf);
- memcpy(workspace, poo, padding);
- memcpy(workspace + padding, efh->encdata, *datalen - sizeof(struct ast_iax2_mini_enc_hdr));
- workspace[15] &= 0xf0;
- workspace[15] |= (padding & 0x0f);
- *datalen += padding;
- memcpy_encrypt(efh->encdata, workspace, *datalen - sizeof(struct ast_iax2_mini_enc_hdr), ecx);
- if (*datalen >= 32 + sizeof(struct ast_iax2_mini_enc_hdr))
- memcpy(poo, workspace + *datalen - 32, 32);
- }
- return 0;
- }
- static int decrypt_frame(int callno, struct ast_iax2_full_hdr *fh, struct ast_frame *f, int *datalen)
- {
- int res=-1;
- if (!ast_test_flag(iaxs[callno], IAX_KEYPOPULATED)) {
- /* Search for possible keys, given secrets */
- struct MD5Context md5;
- unsigned char digest[16];
- char *tmppw, *stringp;
-
- tmppw = ast_strdupa(iaxs[callno]->secret);
- stringp = tmppw;
- while ((tmppw = strsep(&stringp, ";"))) {
- MD5Init(&md5);
- MD5Update(&md5, (unsigned char *)iaxs[callno]->challenge, strlen(iaxs[callno]->challenge));
- MD5Update(&md5, (unsigned char *)tmppw, strlen(tmppw));
- MD5Final(digest, &md5);
- build_encryption_keys(digest, iaxs[callno]);
- res = decode_frame(&iaxs[callno]->dcx, fh, f, datalen);
- if (!res) {
- ast_set_flag(iaxs[callno], IAX_KEYPOPULATED);
- break;
- }
- }
- } else
- res = decode_frame(&iaxs[callno]->dcx, fh, f, datalen);
- return res;
- }
- static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final)
- {
- /* Queue a packet for delivery on a given private structure. Use "ts" for
- timestamp, or calculate if ts is 0. Send immediately without retransmission
- or delayed, with retransmission */
- struct ast_iax2_full_hdr *fh;
- struct ast_iax2_mini_hdr *mh;
- struct ast_iax2_video_hdr *vh;
- struct {
- struct iax_frame fr2;
- unsigned char buffer[4096];
- } frb;
- struct iax_frame *fr;
- int res;
- int sendmini=0;
- unsigned int lastsent;
- unsigned int fts;
- frb.fr2.afdatalen = sizeof(frb.buffer);
- if (!pvt) {
- ast_log(LOG_WARNING, "No private structure for packet?\n");
- return -1;
- }
-
- lastsent = pvt->lastsent;
- /* Calculate actual timestamp */
- fts = calc_timestamp(pvt, ts, f);
- /* Bail here if this is an "interp" frame; we don't want or need to send these placeholders out
- * (the endpoint should detect the lost packet itself). But, we want to do this here, so that we
- * increment the "predicted timestamps" for voice, if we're predicting */
- if(f->frametype == AST_FRAME_VOICE && f->datalen == 0)
- return 0;
- if ((ast_test_flag(pvt, IAX_TRUNK) ||
- (((fts & 0xFFFF0000L) == (lastsent & 0xFFFF0000L)) ||
- ((fts & 0xFFFF0000L) == ((lastsent + 0x10000) & 0xFFFF0000L))))
- /* High two bytes are the same on timestamp, or sending on a trunk */ &&
- (f->frametype == AST_FRAME_VOICE)
- /* is a voice frame */ &&
- (f->subclass == pvt->svoiceformat)
- /* is the same type */ ) {
- /* Force immediate rather than delayed transmission */
- now = 1;
- /* Mark that mini-style frame is appropriate */
- sendmini = 1;
- }
- if ( f->frametype == AST_FRAME_VIDEO ) {
- /*
- * If the lower 15 bits of the timestamp roll over, or if
- * the video format changed then send a full frame.
- * Otherwise send a mini video frame
- */
- if (((fts & 0xFFFF8000L) == (pvt->lastvsent & 0xFFFF8000L)) &&
- ((f->subclass & ~0x1) == pvt->svideoformat)
- ) {
- now = 1;
- sendmini = 1;
- } else {
- now = 0;
- sendmini = 0;
- }
- pvt->lastvsent = fts;
- }
- if (f->frametype == AST_FRAME_IAX) {
- /* 0x8000 marks this message as TX:, this bit will be stripped later */
- pvt->last_iax_message = f->subclass | MARK_IAX_SUBCLASS_TX;
- if (!pvt->first_iax_message) {
- pvt->first_iax_message = pvt->last_iax_message;
- }
- }
- /* Allocate an iax_frame */
- if (now) {
- fr = &frb.fr2;
- } else
- fr = iax_frame_new(DIRECTION_OUTGRESS, ast_test_flag(pvt, IAX_ENCRYPTED) ? f->datalen + 32 : f->datalen, (f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_VIDEO));
- if (!fr) {
- ast_log(LOG_WARNING, "Out of memory\n");
- return -1;
- }
- /* Copy our prospective frame into our immediate or retransmitted wrapper */
- iax_frame_wrap(fr, f);
- fr->ts = fts;
- fr->callno = pvt->callno;
- fr->transfer = transfer;
- fr->final = final;
- fr->encmethods = 0;
- if (!sendmini) {
- /* We need a full frame */
- if (seqno > -1)
- fr->oseqno = seqno;
- else
- fr->oseqno = pvt->oseqno++;
- fr->iseqno = pvt->iseqno;
- fh = (struct ast_iax2_full_hdr *)(fr->af.data - sizeof(struct ast_iax2_full_hdr));
- fh->scallno = htons(fr->callno | IAX_FLAG_FULL);
- fh->ts = htonl(fr->ts);
- fh->oseqno = fr->oseqno;
- if (transfer) {
- fh->iseqno = 0;
- } else
- fh->iseqno = fr->iseqno;
- /* Keep track of the last thing we've acknowledged */
- if (!transfer)
- pvt->aseqno = fr->iseqno;
- fh->type = fr->af.frametype & 0xFF;
- if (fr->af.frametype == AST_FRAME_VIDEO)
- fh->csub = compress_subclass(fr->af.subclass & ~0x1) | ((fr->af.subclass & 0x1) << 6);
- else
- fh->csub = compress_subclass(fr->af.subclass);
- if (transfer) {
- fr->dcallno = pvt->transfercallno;
- } else
- fr->dcallno = pvt->peercallno;
- fh->dcallno = htons(fr->dcallno);
- fr->datalen = fr->af.datalen + sizeof(struct ast_iax2_full_hdr);
- fr->data = fh;
- fr->retries = 0;
- /* Retry after 2x the ping time has passed */
- fr->retrytime = pvt->pingtime * 2;
- if (fr->retrytime < MIN_RETRY_TIME)
- fr->retrytime = MIN_RETRY_TIME;
- if (fr->retrytime > MAX_RETRY_TIME)
- fr->retrytime = MAX_RETRY_TIME;
- /* Acks' don't get retried */
- if ((f->frametype == AST_FRAME_IAX) && (f->subclass == IAX_COMMAND_ACK))
- fr->retries = -1;
- else if (f->frametype == AST_FRAME_VOICE)
- pvt->svoiceformat = f->subclass;
- else if (f->frametype == AST_FRAME_VIDEO)
- pvt->svideoformat = f->subclass & ~0x1;
- if (ast_test_flag(pvt, IAX_ENCRYPTED)) {
- if (ast_test_flag(pvt, IAX_KEYPOPULATED)) {
- if (iaxdebug) {
- if (fr->transfer)
- iax_showframe(fr, NULL, 2, &pvt->transfer, fr->datalen - sizeof(struct ast_iax2_full_hdr));
- else
- iax_showframe(fr, NULL, 2, &pvt->addr, fr->datalen - sizeof(struct ast_iax2_full_hdr));
- }
- encrypt_frame(&pvt->ecx, fh, pvt->semirand, &fr->datalen);
- fr->encmethods = pvt->encmethods;
- fr->ecx = pvt->ecx;
- fr->mydcx = pvt->mydcx;
- memcpy(fr->semirand, pvt->semirand, sizeof(fr->semirand));
- } else
- ast_log(LOG_WARNING, "Supposed to send packet encrypted, but no key?\n");
- }
- if (now) {
- res = send_packet(fr);
- } else
- res = iax2_transmit(fr);
- } else {
- if (ast_test_flag(pvt, IAX_TRUNK)) {
- iax2_trunk_queue(pvt, fr);
- res = 0;
- } else if (fr->af.frametype == AST_FRAME_VIDEO) {
- /* Video frame have no sequence number */
- fr->oseqno = -1;
- fr->iseqno = -1;
- vh = (struct ast_iax2_video_hdr *)(fr->af.data - sizeof(struct ast_iax2_video_hdr));
- vh->zeros = 0;
- vh->callno = htons(0x8000 | fr->callno);
- vh->ts = htons((fr->ts & 0x7FFF) | (fr->af.subclass & 0x1 ? 0x8000 : 0));
- fr->datalen = fr->af.datalen + sizeof(struct ast_iax2_video_hdr);
- fr->data = vh;
- fr->retries = -1;
- res = send_packet(fr);
- } else {
- /* Mini-frames have no sequence number */
- fr->oseqno = -1;
- fr->iseqno = -1;
- /* Mini frame will do */
- mh = (struct ast_iax2_mini_hdr *)(fr->af.data - sizeof(struct ast_iax2_mini_hdr));
- mh->callno = htons(fr->callno);
- mh->ts = htons(fr->ts & 0xFFFF);
- fr->datalen = fr->af.datalen + sizeof(struct ast_iax2_mini_hdr);
- fr->data = mh;
- fr->retries = -1;
- if (pvt->transferring == TRANSFER_MEDIAPASS)
- fr->transfer = 1;
- if (ast_test_flag(pvt, IAX_ENCRYPTED)) {
- if (ast_test_flag(pvt, IAX_KEYPOPULATED)) {
- encrypt_frame(&pvt->ecx, (struct ast_iax2_full_hdr *)mh, pvt->semirand, &fr->datalen);
- } else
- ast_log(LOG_WARNING, "Supposed to send packet encrypted, but no key?\n");
- }
- res = send_packet(fr);
- }
- }
- return res;
- }
- static char *handle_cli_iax2_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- regex_t regexbuf;
- int havepattern = 0;
- #define FORMAT "%-15.15s %-20.20s %-15.15s %-15.15s %-5.5s %-5.10s\n"
- #define FORMAT2 "%-15.15s %-20.20s %-15.15d %-15.15s %-5.5s %-5.10s\n"
- struct iax2_user *user = NULL;
- char auth[90];
- char *pstr = "";
- struct ao2_iterator i;
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 show users [like]";
- e->usage =
- "Usage: iax2 show users [like <pattern>]\n"
- " Lists all known IAX2 users.\n"
- " Optional regular expression pattern is used to filter the user list.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- switch (a->argc) {
- case 5:
- if (!strcasecmp(a->argv[3], "like")) {
- if (regcomp(®exbuf, a->argv[4], REG_EXTENDED | REG_NOSUB))
- return CLI_SHOWUSAGE;
- havepattern = 1;
- } else
- return CLI_SHOWUSAGE;
- case 3:
- break;
- default:
- return CLI_SHOWUSAGE;
- }
- ast_cli(a->fd, FORMAT, "Username", "Secret", "Authen", "Def.Context", "A/C","Codec Pref");
- i = ao2_iterator_init(users, 0);
- for (user = ao2_iterator_next(&i); user;
- user_unref(user), user = ao2_iterator_next(&i)) {
- if (havepattern && regexec(®exbuf, user->name, 0, NULL, 0))
- continue;
-
- if (!ast_strlen_zero(user->secret)) {
- ast_copy_string(auth,user->secret, sizeof(auth));
- } else if (!ast_strlen_zero(user->inkeys)) {
- snprintf(auth, sizeof(auth), "Key: %-15.15s ", user->inkeys);
- } else
- ast_copy_string(auth, "-no secret-", sizeof(auth));
-
- if(ast_test_flag(user,IAX_CODEC_NOCAP))
- pstr = "REQ Only";
- else if(ast_test_flag(user,IAX_CODEC_NOPREFS))
- pstr = "Disabled";
- else
- pstr = ast_test_flag(user,IAX_CODEC_USER_FIRST) ? "Caller" : "Host";
-
- ast_cli(a->fd, FORMAT2, user->name, auth, user->authmethods,
- user->contexts ? user->contexts->context : context,
- user->ha ? "Yes" : "No", pstr);
- }
- ao2_iterator_destroy(&i);
- if (havepattern)
- regfree(®exbuf);
- return CLI_SUCCESS;
- #undef FORMAT
- #undef FORMAT2
- }
- static int __iax2_show_peers(int manager, int fd, struct mansession *s, int argc, char *argv[])
- {
- regex_t regexbuf;
- int havepattern = 0;
- int total_peers = 0;
- int online_peers = 0;
- int offline_peers = 0;
- int unmonitored_peers = 0;
- struct ao2_iterator i;
- #define FORMAT2 "%-15.15s %-15.15s %s %-15.15s %-8s %s %-10s%s"
- #define FORMAT "%-15.15s %-15.15s %s %-15.15s %-5d%s %s %-10s%s"
- struct iax2_peer *peer = NULL;
- char name[256];
- int registeredonly=0;
- char *term = manager ? "\r\n" : "\n";
- char idtext[256] = "";
- switch (argc) {
- case 6:
- if (!strcasecmp(argv[3], "registered"))
- registeredonly = 1;
- else
- return RESULT_SHOWUSAGE;
- if (!strcasecmp(argv[4], "like")) {
- if (regcomp(®exbuf, argv[5], REG_EXTENDED | REG_NOSUB))
- return RESULT_SHOWUSAGE;
- havepattern = 1;
- } else
- return RESULT_SHOWUSAGE;
- break;
- case 5:
- if (!strcasecmp(argv[3], "like")) {
- if (regcomp(®exbuf, argv[4], REG_EXTENDED | REG_NOSUB))
- return RESULT_SHOWUSAGE;
- havepattern = 1;
- } else
- return RESULT_SHOWUSAGE;
- break;
- case 4:
- if (!strcasecmp(argv[3], "registered"))
- registeredonly = 1;
- else
- return RESULT_SHOWUSAGE;
- break;
- case 3:
- break;
- default:
- return RESULT_SHOWUSAGE;
- }
- if (!s)
- ast_cli(fd, FORMAT2, "Name/Username", "Host", " ", "Mask", "Port", " ", "Status", term);
- i = ao2_iterator_init(peers, 0);
- for (peer = ao2_iterator_next(&i); peer;
- peer_unref(peer), peer = ao2_iterator_next(&i)) {
- char nm[20];
- char status[20];
- char srch[2000];
- int retstatus;
- if (registeredonly && !peer->addr.sin_addr.s_addr)
- continue;
- if (havepattern && regexec(®exbuf, peer->name, 0, NULL, 0))
- continue;
- if (!ast_strlen_zero(peer->username))
- snprintf(name, sizeof(name), "%s/%s", peer->name, peer->username);
- else
- ast_copy_string(name, peer->name, sizeof(name));
-
- retstatus = peer_status(peer, status, sizeof(status));
- if (retstatus > 0)
- online_peers++;
- else if (!retstatus)
- offline_peers++;
- else
- unmonitored_peers++;
-
- ast_copy_string(nm, ast_inet_ntoa(peer->mask), sizeof(nm));
-
- snprintf(srch, sizeof(srch), FORMAT, name,
- peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
- ast_test_flag(peer, IAX_DYNAMIC) ? "(D)" : "(S)",
- nm,
- ntohs(peer->addr.sin_port), ast_test_flag(peer, IAX_TRUNK) ? "(T)" : " ",
- peer->encmethods ? "(E)" : " ", status, term);
-
- if (s)
- astman_append(s,
- "Event: PeerEntry\r\n%s"
- "Channeltype: IAX2\r\n"
- "ChanObjectType: peer\r\n"
- "ObjectName: %s\r\n"
- "IPaddress: %s\r\n"
- "IPport: %d\r\n"
- "Dynamic: %s\r\n"
- "Status: %s\r\n\r\n",
- idtext,
- name,
- peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "-none-",
- ntohs(peer->addr.sin_port),
- ast_test_flag(peer, IAX_DYNAMIC) ? "yes" : "no",
- status);
-
- else
- ast_cli(fd, FORMAT, name,
- peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
- ast_test_flag(peer, IAX_DYNAMIC) ? "(D)" : "(S)",
- nm,
- ntohs(peer->addr.sin_port), ast_test_flag(peer, IAX_TRUNK) ? "(T)" : " ",
- peer->encmethods ? "(E)" : " ", status, term);
- total_peers++;
- }
- ao2_iterator_destroy(&i);
- if (!s)
- ast_cli(fd,"%d iax2 peers [%d online, %d offline, %d unmonitored]%s", total_peers, online_peers, offline_peers, unmonitored_peers, term);
- if (havepattern)
- regfree(®exbuf);
- return RESULT_SUCCESS;
- #undef FORMAT
- #undef FORMAT2
- }
- static char *handle_cli_iax2_show_threads(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct iax2_thread *thread = NULL;
- time_t t;
- int threadcount = 0, dynamiccount = 0;
- char type;
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 show threads";
- e->usage =
- "Usage: iax2 show threads\n"
- " Lists status of IAX helper threads\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
-
- ast_cli(a->fd, "IAX2 Thread Information\n");
- time(&t);
- ast_cli(a->fd, "Idle Threads:\n");
- AST_LIST_LOCK(&idle_list);
- AST_LIST_TRAVERSE(&idle_list, thread, list) {
- #ifdef DEBUG_SCHED_MULTITHREAD
- ast_cli(a->fd, "Thread %d: state=%d, update=%d, actions=%d, func='%s'\n",
- thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions, thread->curfunc);
- #else
- ast_cli(a->fd, "Thread %d: state=%d, update=%d, actions=%d\n",
- thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions);
- #endif
- threadcount++;
- }
- AST_LIST_UNLOCK(&idle_list);
- ast_cli(a->fd, "Active Threads:\n");
- AST_LIST_LOCK(&active_list);
- AST_LIST_TRAVERSE(&active_list, thread, list) {
- if (thread->type == IAX_THREAD_TYPE_DYNAMIC)
- type = 'D';
- else
- type = 'P';
- #ifdef DEBUG_SCHED_MULTITHREAD
- ast_cli(a->fd, "Thread %c%d: state=%d, update=%d, actions=%d, func='%s'\n",
- type, thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions, thread->curfunc);
- #else
- ast_cli(a->fd, "Thread %c%d: state=%d, update=%d, actions=%d\n",
- type, thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions);
- #endif
- threadcount++;
- }
- AST_LIST_UNLOCK(&active_list);
- ast_cli(a->fd, "Dynamic Threads:\n");
- AST_LIST_LOCK(&dynamic_list);
- AST_LIST_TRAVERSE(&dynamic_list, thread, list) {
- #ifdef DEBUG_SCHED_MULTITHREAD
- ast_cli(a->fd, "Thread %d: state=%d, update=%d, actions=%d, func='%s'\n",
- thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions, thread->curfunc);
- #else
- ast_cli(a->fd, "Thread %d: state=%d, update=%d, actions=%d\n",
- thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions);
- #endif
- dynamiccount++;
- }
- AST_LIST_UNLOCK(&dynamic_list);
- ast_cli(a->fd, "%d of %d threads accounted for with %d dynamic threads\n", threadcount, iaxthreadcount, dynamiccount);
- return CLI_SUCCESS;
- }
- static char *handle_cli_iax2_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct iax2_peer *p;
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 unregister";
- e->usage =
- "Usage: iax2 unregister <peername>\n"
- " Unregister (force expiration) an IAX2 peer from the registry.\n";
- return NULL;
- case CLI_GENERATE:
- return complete_iax2_unregister(a->line, a->word, a->pos, a->n);
- }
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
- p = find_peer(a->argv[2], 1);
- if (p) {
- if (p->expire > 0) {
- struct iax2_peer tmp_peer = {
- .name = a->argv[2],
- };
- struct iax2_peer *peer;
- peer = ao2_find(peers, &tmp_peer, OBJ_POINTER);
- if (peer) {
- expire_registry(peer_ref(peer)); /* will release its own reference when done */
- peer_unref(peer); /* ref from ao2_find() */
- ast_cli(a->fd, "Peer %s unregistered\n", a->argv[2]);
- } else {
- ast_cli(a->fd, "Peer %s not found\n", a->argv[2]);
- }
- } else {
- ast_cli(a->fd, "Peer %s not registered\n", a->argv[2]);
- }
- } else {
- ast_cli(a->fd, "Peer unknown: %s. Not unregistered\n", a->argv[2]);
- }
- return CLI_SUCCESS;
- }
- static char *complete_iax2_unregister(const char *line, const char *word, int pos, int state)
- {
- int which = 0;
- struct iax2_peer *p = NULL;
- char *res = NULL;
- int wordlen = strlen(word);
- /* 0 - iax2; 1 - unregister; 2 - <peername> */
- if (pos == 2) {
- struct ao2_iterator i = ao2_iterator_init(peers, 0);
- while ((p = ao2_iterator_next(&i))) {
- if (!strncasecmp(p->name, word, wordlen) &&
- ++which > state && p->expire > 0) {
- res = ast_strdup(p->name);
- peer_unref(p);
- break;
- }
- peer_unref(p);
- }
- ao2_iterator_destroy(&i);
- }
- return res;
- }
- static char *handle_cli_iax2_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 show peers";
- e->usage =
- "Usage: iax2 show peers [registered] [like <pattern>]\n"
- " Lists all known IAX2 peers.\n"
- " Optional 'registered' argument lists only peers with known addresses.\n"
- " Optional regular expression pattern is used to filter the peer list.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- switch (__iax2_show_peers(0, a->fd, NULL, a->argc, a->argv)) {
- case RESULT_SHOWUSAGE:
- return CLI_SHOWUSAGE;
- case RESULT_FAILURE:
- return CLI_FAILURE;
- default:
- return CLI_SUCCESS;
- }
- }
- static int manager_iax2_show_netstats(struct mansession *s, const struct message *m)
- {
- ast_cli_netstats(s, -1, 0);
- astman_append(s, "\r\n");
- return RESULT_SUCCESS;
- }
- static char *handle_cli_iax2_show_firmware(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct iax_firmware *cur = NULL;
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 show firmware";
- e->usage =
- "Usage: iax2 show firmware\n"
- " Lists all known IAX firmware images.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 3 && a->argc != 4)
- return CLI_SHOWUSAGE;
- ast_cli(a->fd, "%-15.15s %-15.15s %-15.15s\n", "Device", "Version", "Size");
- AST_LIST_LOCK(&firmwares);
- AST_LIST_TRAVERSE(&firmwares, cur, list) {
- if ((a->argc == 3) || (!strcasecmp(a->argv[3], (char *) cur->fwh->devname))) {
- ast_cli(a->fd, "%-15.15s %-15d %-15d\n", cur->fwh->devname,
- ntohs(cur->fwh->version), (int)ntohl(cur->fwh->datalen));
- }
- }
- AST_LIST_UNLOCK(&firmwares);
- return CLI_SUCCESS;
- }
- /*! \brief callback to display iax peers in manager */
- static int manager_iax2_show_peers(struct mansession *s, const struct message *m)
- {
- char *a[] = { "iax2", "show", "users" };
- const char *id = astman_get_header(m,"ActionID");
- char idtext[256] = "";
- if (!ast_strlen_zero(id))
- snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
- astman_send_ack(s, m, "Peer status list will follow");
- return __iax2_show_peers(1, -1, s, 3, a );
- }
- /*! \brief callback to display iax peers in manager format */
- static int manager_iax2_show_peer_list(struct mansession *s, const struct message *m)
- {
- struct iax2_peer *peer = NULL;
- int peer_count = 0;
- char nm[20];
- char status[20];
- const char *id = astman_get_header(m,"ActionID");
- char idtext[256] = "";
- struct ao2_iterator i;
- if (!ast_strlen_zero(id))
- snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
- astman_append(s, "Response: Success\r\n%sMessage: IAX Peer status list will follow\r\n\r\n", idtext);
- i = ao2_iterator_init(peers, 0);
- for (peer = ao2_iterator_next(&i); peer; peer_unref(peer), peer = ao2_iterator_next(&i)) {
- astman_append(s, "Event: PeerEntry\r\n%sChanneltype: IAX\r\n", idtext);
- if (!ast_strlen_zero(peer->username)) {
- astman_append(s, "ObjectName: %s\r\nObjectUsername: %s\r\n", peer->name, peer->username);
- } else {
- astman_append(s, "ObjectName: %s\r\n", peer->name);
- }
- astman_append(s, "ChanObjectType: peer\r\n");
- astman_append(s, "IPaddress: %s\r\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "-none-");
- ast_copy_string(nm, ast_inet_ntoa(peer->mask), sizeof(nm));
- astman_append(s, "Mask: %s\r\n", nm);
- astman_append(s, "Port: %d\r\n", ntohs(peer->addr.sin_port));
- astman_append(s, "Dynamic: %s\r\n", ast_test_flag(peer, IAX_DYNAMIC) ? "Yes" : "No");
- peer_status(peer, status, sizeof(status));
- astman_append(s, "Status: %s\r\n\r\n", status);
- peer_count++;
- }
- ao2_iterator_destroy(&i);
- astman_append(s, "Event: PeerlistComplete\r\n%sListItems: %d\r\n\r\n", idtext, peer_count);
- return RESULT_SUCCESS;
- }
- static char *regstate2str(int regstate)
- {
- switch(regstate) {
- case REG_STATE_UNREGISTERED:
- return "Unregistered";
- case REG_STATE_REGSENT:
- return "Request Sent";
- case REG_STATE_AUTHSENT:
- return "Auth. Sent";
- case REG_STATE_REGISTERED:
- return "Registered";
- case REG_STATE_REJECTED:
- return "Rejected";
- case REG_STATE_TIMEOUT:
- return "Timeout";
- case REG_STATE_NOAUTH:
- return "No Authentication";
- default:
- return "Unknown";
- }
- }
- static char *handle_cli_iax2_show_registry(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- #define FORMAT2 "%-20.20s %-6.6s %-10.10s %-20.20s %8.8s %s\n"
- #define FORMAT "%-20.20s %-6.6s %-10.10s %-20.20s %8d %s\n"
- struct iax2_registry *reg = NULL;
- char host[80];
- char perceived[80];
- int counter = 0;
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 show registry";
- e->usage =
- "Usage: iax2 show registry\n"
- " Lists all registration requests and status.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
- ast_cli(a->fd, FORMAT2, "Host", "dnsmgr", "Username", "Perceived", "Refresh", "State");
- AST_LIST_LOCK(®istrations);
- AST_LIST_TRAVERSE(®istrations, reg, entry) {
- snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(reg->addr.sin_addr), ntohs(reg->addr.sin_port));
- if (reg->us.sin_addr.s_addr)
- snprintf(perceived, sizeof(perceived), "%s:%d", ast_inet_ntoa(reg->us.sin_addr), ntohs(reg->us.sin_port));
- else
- ast_copy_string(perceived, "<Unregistered>", sizeof(perceived));
- ast_cli(a->fd, FORMAT, host,
- (reg->dnsmgr) ? "Y" : "N",
- reg->username, perceived, reg->refresh, regstate2str(reg->regstate));
- counter++;
- }
- AST_LIST_UNLOCK(®istrations);
- ast_cli(a->fd, "%d IAX2 registrations.\n", counter);
- return CLI_SUCCESS;
- #undef FORMAT
- #undef FORMAT2
- }
- static char *handle_cli_iax2_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-11.11s %-11.11s %-7.7s %-6.6s %-6.6s %s %s %9s\n"
- #define FORMAT "%-20.20s %-15.15s %-10.10s %5.5d/%5.5d %5.5d/%5.5d %-5.5dms %-4.4dms %-4.4dms %-6.6s %s%s %3s%s\n"
- #define FORMATB "%-20.20s %-15.15s %-10.10s %5.5d/%5.5d %5.5d/%5.5d [Native Bridged to ID=%5.5d]\n"
- int x;
- int numchans = 0;
- char first_message[10] = { 0, };
- char last_message[10] = { 0, };
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 show channels";
- e->usage =
- "Usage: iax2 show channels\n"
- " Lists all currently active IAX channels.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
- ast_cli(a->fd, FORMAT2, "Channel", "Peer", "Username", "ID (Lo/Rem)", "Seq (Tx/Rx)", "Lag", "Jitter", "JitBuf", "Format", "FirstMsg", "LastMsg");
- for (x = 0; x < ARRAY_LEN(iaxs); x++) {
- ast_mutex_lock(&iaxsl[x]);
- if (iaxs[x]) {
- int lag, jitter, localdelay;
- jb_info jbinfo;
- if (ast_test_flag(iaxs[x], IAX_USEJITTERBUF)) {
- jb_getinfo(iaxs[x]->jb, &jbinfo);
- jitter = jbinfo.jitter;
- localdelay = jbinfo.current - jbinfo.min;
- } else {
- jitter = -1;
- localdelay = 0;
- }
- iax_frame_subclass2str(iaxs[x]->first_iax_message & ~MARK_IAX_SUBCLASS_TX, first_message, sizeof(first_message));
- iax_frame_subclass2str(iaxs[x]->last_iax_message & ~MARK_IAX_SUBCLASS_TX, last_message, sizeof(last_message));
- lag = iaxs[x]->remote_rr.delay;
- ast_cli(a->fd, FORMAT,
- iaxs[x]->owner ? iaxs[x]->owner->name : "(None)",
- ast_inet_ntoa(iaxs[x]->addr.sin_addr),
- S_OR(iaxs[x]->username, "(None)"),
- iaxs[x]->callno, iaxs[x]->peercallno,
- iaxs[x]->oseqno, iaxs[x]->iseqno,
- lag,
- jitter,
- localdelay,
- ast_getformatname(iaxs[x]->voiceformat),
- (iaxs[x]->first_iax_message & MARK_IAX_SUBCLASS_TX) ? "Tx:" : "Rx:",
- first_message,
- (iaxs[x]->last_iax_message & MARK_IAX_SUBCLASS_TX) ? "Tx:" : "Rx:",
- last_message);
- numchans++;
- }
- ast_mutex_unlock(&iaxsl[x]);
- }
- ast_cli(a->fd, "%d active IAX channel%s\n", numchans, (numchans != 1) ? "s" : "");
- return CLI_SUCCESS;
- #undef FORMAT
- #undef FORMAT2
- #undef FORMATB
- }
- static int ast_cli_netstats(struct mansession *s, int fd, int limit_fmt)
- {
- int x;
- int numchans = 0;
- char first_message[10] = { 0, };
- char last_message[10] = { 0, };
- #define ACN_FORMAT1 "%-20.25s %4d %4d %4d %5d %3d %5d %4d %6d %4d %4d %5d %3d %5d %4d %6d %s%s %4s%s\n"
- #define ACN_FORMAT2 "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %s%s %s%s\n"
- for (x = 0; x < ARRAY_LEN(iaxs); x++) {
- ast_mutex_lock(&iaxsl[x]);
- if (iaxs[x]) {
- int localjitter, localdelay, locallost, locallosspct, localdropped, localooo;
- jb_info jbinfo;
- iax_frame_subclass2str(iaxs[x]->first_iax_message & ~MARK_IAX_SUBCLASS_TX, first_message, sizeof(first_message));
- iax_frame_subclass2str(iaxs[x]->last_iax_message & ~MARK_IAX_SUBCLASS_TX, last_message, sizeof(last_message));
- if(ast_test_flag(iaxs[x], IAX_USEJITTERBUF)) {
- jb_getinfo(iaxs[x]->jb, &jbinfo);
- localjitter = jbinfo.jitter;
- localdelay = jbinfo.current - jbinfo.min;
- locallost = jbinfo.frames_lost;
- locallosspct = jbinfo.losspct/1000;
- localdropped = jbinfo.frames_dropped;
- localooo = jbinfo.frames_ooo;
- } else {
- localjitter = -1;
- localdelay = 0;
- locallost = -1;
- locallosspct = -1;
- localdropped = 0;
- localooo = -1;
- }
- if (s)
- astman_append(s, limit_fmt ? ACN_FORMAT1 : ACN_FORMAT2,
- iaxs[x]->owner ? iaxs[x]->owner->name : "(None)",
- iaxs[x]->pingtime,
- localjitter,
- localdelay,
- locallost,
- locallosspct,
- localdropped,
- localooo,
- iaxs[x]->frames_received/1000,
- iaxs[x]->remote_rr.jitter,
- iaxs[x]->remote_rr.delay,
- iaxs[x]->remote_rr.losscnt,
- iaxs[x]->remote_rr.losspct,
- iaxs[x]->remote_rr.dropped,
- iaxs[x]->remote_rr.ooo,
- iaxs[x]->remote_rr.packets/1000,
- (iaxs[x]->first_iax_message & MARK_IAX_SUBCLASS_TX) ? "Tx:" : "Rx:",
- first_message,
- (iaxs[x]->last_iax_message & MARK_IAX_SUBCLASS_TX) ? "Tx:" : "Rx:",
- last_message);
- else
- ast_cli(fd, limit_fmt ? ACN_FORMAT1 : ACN_FORMAT2,
- iaxs[x]->owner ? iaxs[x]->owner->name : "(None)",
- iaxs[x]->pingtime,
- localjitter,
- localdelay,
- locallost,
- locallosspct,
- localdropped,
- localooo,
- iaxs[x]->frames_received/1000,
- iaxs[x]->remote_rr.jitter,
- iaxs[x]->remote_rr.delay,
- iaxs[x]->remote_rr.losscnt,
- iaxs[x]->remote_rr.losspct,
- iaxs[x]->remote_rr.dropped,
- iaxs[x]->remote_rr.ooo,
- iaxs[x]->remote_rr.packets/1000,
- (iaxs[x]->first_iax_message & MARK_IAX_SUBCLASS_TX) ? "Tx:" : "Rx:",
- first_message,
- (iaxs[x]->last_iax_message & MARK_IAX_SUBCLASS_TX) ? "Tx:" : "Rx:",
- last_message);
- numchans++;
- }
- ast_mutex_unlock(&iaxsl[x]);
- }
- return numchans;
- }
- static char *handle_cli_iax2_show_netstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- int numchans = 0;
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 show netstats";
- e->usage =
- "Usage: iax2 show netstats\n"
- " Lists network status for all currently active IAX channels.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
- ast_cli(a->fd, " -------- LOCAL --------------------- -------- REMOTE --------------------\n");
- ast_cli(a->fd, "Channel RTT Jit Del Lost %% Drop OOO Kpkts Jit Del Lost %% Drop OOO Kpkts FirstMsg LastMsg\n");
- numchans = ast_cli_netstats(NULL, a->fd, 1);
- ast_cli(a->fd, "%d active IAX channel%s\n", numchans, (numchans != 1) ? "s" : "");
- return CLI_SUCCESS;
- }
- static char *handle_cli_iax2_set_debug_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 set debug [off]";
- e->usage =
- "Usage: iax2 set debug [off]\n"
- " Enables/Disables dumping of IAX packets for debugging purposes.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc < 3 || a->argc > 4)
- return CLI_SHOWUSAGE;
- if (a->argc == 3) {
- iaxdebug = 1;
- ast_cli(a->fd, "IAX2 Debugging Enabled\n");
- } else {
- iaxdebug = 0;
- ast_cli(a->fd, "IAX2 Debugging Disabled\n");
- }
- return CLI_SUCCESS;
- }
- static char *handle_cli_iax2_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 set debug {on|off}";
- e->usage =
- "Usage: iax2 set debug {on|off}\n"
- " Enables/Disables dumping of IAX packets for debugging purposes.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != e->args)
- return CLI_SHOWUSAGE;
- if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
- iaxdebug = 1;
- ast_cli(a->fd, "IAX2 Debugging Enabled\n");
- } else {
- iaxdebug = 0;
- ast_cli(a->fd, "IAX2 Debugging Disabled\n");
- }
- return CLI_SUCCESS;
- }
- static char *handle_cli_iax2_set_debug_trunk_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 set debug trunk [off]";
- e->usage =
- "Usage: iax2 set debug trunk [off]\n"
- " Enables/Disables debugging of IAX trunking\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc < 4 || a->argc > 5)
- return CLI_SHOWUSAGE;
- if (a->argc == 4) {
- iaxtrunkdebug = 1;
- ast_cli(a->fd, "IAX2 Trunk Debugging Enabled\n");
- } else {
- iaxtrunkdebug = 0;
- ast_cli(a->fd, "IAX2 Trunk Debugging Disabled\n");
- }
- return CLI_SUCCESS;
- }
- static char *handle_cli_iax2_set_debug_trunk(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 set debug trunk {on|off}";
- e->usage =
- "Usage: iax2 set debug trunk {on|off}\n"
- " Enables/Disables debugging of IAX trunking\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != e->args)
- return CLI_SHOWUSAGE;
- if (!strncasecmp(a->argv[e->args - 1], "on", 2)) {
- iaxtrunkdebug = 1;
- ast_cli(a->fd, "IAX2 Trunk Debugging Enabled\n");
- } else {
- iaxtrunkdebug = 0;
- ast_cli(a->fd, "IAX2 Trunk Debugging Disabled\n");
- }
- return CLI_SUCCESS;
- }
- static char *handle_cli_iax2_set_debug_jb_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 set debug jb [off]";
- e->usage =
- "Usage: iax2 set debug jb [off]\n"
- " Enables/Disables jitterbuffer debugging information\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc < 4 || a->argc > 5)
- return CLI_SHOWUSAGE;
-
- if (a->argc == 4) {
- jb_setoutput(jb_error_output, jb_warning_output, jb_debug_output);
- ast_cli(a->fd, "IAX2 Jitterbuffer Debugging Enabled\n");
- } else {
- jb_setoutput(jb_error_output, jb_warning_output, NULL);
- ast_cli(a->fd, "IAX2 Jitterbuffer Debugging Disabled\n");
- }
- return CLI_SUCCESS;
- }
- static char *handle_cli_iax2_set_debug_jb(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 set debug jb {on|off}";
- e->usage =
- "Usage: iax2 set debug jb {on|off}\n"
- " Enables/Disables jitterbuffer debugging information\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != e->args)
- return CLI_SHOWUSAGE;
-
- if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
- jb_setoutput(jb_error_output, jb_warning_output, jb_debug_output);
- ast_cli(a->fd, "IAX2 Jitterbuffer Debugging Enabled\n");
- } else {
- jb_setoutput(jb_error_output, jb_warning_output, NULL);
- ast_cli(a->fd, "IAX2 Jitterbuffer Debugging Disabled\n");
- }
- return CLI_SUCCESS;
- }
- static int iax2_write(struct ast_channel *c, struct ast_frame *f)
- {
- unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
- int res = -1;
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno]) {
- /* If there's an outstanding error, return failure now */
- if (!iaxs[callno]->error) {
- if (ast_test_flag(iaxs[callno], IAX_ALREADYGONE))
- res = 0;
- /* Don't waste bandwidth sending null frames */
- else if (f->frametype == AST_FRAME_NULL)
- res = 0;
- else if ((f->frametype == AST_FRAME_VOICE) && ast_test_flag(iaxs[callno], IAX_QUELCH))
- res = 0;
- else if (!ast_test_flag(&iaxs[callno]->state, IAX_STATE_STARTED))
- res = 0;
- else
- /* Simple, just queue for transmission */
- res = iax2_send(iaxs[callno], f, 0, -1, 0, 0, 0);
- } else {
- ast_debug(1, "Write error: %s\n", strerror(errno));
- }
- }
- /* If it's already gone, just return */
- ast_mutex_unlock(&iaxsl[callno]);
- return res;
- }
- static int __send_command(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno,
- int now, int transfer, int final)
- {
- struct ast_frame f = { 0, };
- int res = 0;
- f.frametype = type;
- f.subclass = command;
- f.datalen = datalen;
- f.src = __FUNCTION__;
- f.data = (void *) data;
- if ((res = queue_signalling(i, &f)) <= 0) {
- return res;
- }
- return iax2_send(i, &f, ts, seqno, now, transfer, final);
- }
- static int send_command(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno)
- {
- return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 0);
- }
- static int send_command_locked(unsigned short callno, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno)
- {
- int res;
- ast_mutex_lock(&iaxsl[callno]);
- res = send_command(iaxs[callno], type, command, ts, data, datalen, seqno);
- ast_mutex_unlock(&iaxsl[callno]);
- return res;
- }
- /*!
- * \note Since this function calls iax2_predestroy() -> iax2_queue_hangup(),
- * the pvt struct for the given call number may disappear during its
- * execution.
- */
- static int send_command_final(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno)
- {
- int call_num = i->callno;
- /* It is assumed that the callno has already been locked */
- iax2_predestroy(i->callno);
- if (!iaxs[call_num])
- return -1;
- return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 1);
- }
- static int send_command_immediate(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno)
- {
- return __send_command(i, type, command, ts, data, datalen, seqno, 1, 0, 0);
- }
- static int send_command_transfer(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen)
- {
- return __send_command(i, type, command, ts, data, datalen, 0, 0, 1, 0);
- }
- static int apply_context(struct iax2_context *con, const char *context)
- {
- while(con) {
- if (!strcmp(con->context, context) || !strcmp(con->context, "*"))
- return -1;
- con = con->next;
- }
- return 0;
- }
- static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies)
- {
- /* Start pessimistic */
- int res = -1;
- int version = 2;
- struct iax2_user *user = NULL, *best = NULL;
- int bestscore = 0;
- int gotcapability = 0;
- struct ast_variable *v = NULL, *tmpvar = NULL;
- struct ao2_iterator i;
- if (!iaxs[callno])
- return res;
- if (ies->called_number)
- ast_string_field_set(iaxs[callno], exten, ies->called_number);
- if (ies->calling_number) {
- if (ast_test_flag(&globalflags, IAX_SHRINKCALLERID)) {
- ast_shrink_phone_number(ies->calling_number);
- }
- ast_string_field_set(iaxs[callno], cid_num, ies->calling_number);
- }
- if (ies->calling_name)
- ast_string_field_set(iaxs[callno], cid_name, ies->calling_name);
- if (ies->calling_ani)
- ast_string_field_set(iaxs[callno], ani, ies->calling_ani);
- if (ies->dnid)
- ast_string_field_set(iaxs[callno], dnid, ies->dnid);
- if (ies->rdnis)
- ast_string_field_set(iaxs[callno], rdnis, ies->rdnis);
- if (ies->called_context)
- ast_string_field_set(iaxs[callno], context, ies->called_context);
- if (ies->language)
- ast_string_field_set(iaxs[callno], language, ies->language);
- if (ies->username)
- ast_string_field_set(iaxs[callno], username, ies->username);
- if (ies->calling_ton > -1)
- iaxs[callno]->calling_ton = ies->calling_ton;
- if (ies->calling_tns > -1)
- iaxs[callno]->calling_tns = ies->calling_tns;
- if (ies->calling_pres > -1)
- iaxs[callno]->calling_pres = ies->calling_pres;
- if (ies->format)
- iaxs[callno]->peerformat = ies->format;
- if (ies->adsicpe)
- iaxs[callno]->peeradsicpe = ies->adsicpe;
- if (ies->capability) {
- gotcapability = 1;
- iaxs[callno]->peercapability = ies->capability;
- }
- if (ies->version)
- version = ies->version;
- /* Use provided preferences until told otherwise for actual preferences */
- if(ies->codec_prefs) {
- ast_codec_pref_convert(&iaxs[callno]->rprefs, ies->codec_prefs, 32, 0);
- ast_codec_pref_convert(&iaxs[callno]->prefs, ies->codec_prefs, 32, 0);
- }
- if (!gotcapability)
- iaxs[callno]->peercapability = iaxs[callno]->peerformat;
- if (version > IAX_PROTO_VERSION) {
- ast_log(LOG_WARNING, "Peer '%s' has too new a protocol version (%d) for me\n",
- ast_inet_ntoa(sin->sin_addr), version);
- return res;
- }
- /* Search the userlist for a compatible entry, and fill in the rest */
- i = ao2_iterator_init(users, 0);
- while ((user = ao2_iterator_next(&i))) {
- if ((ast_strlen_zero(iaxs[callno]->username) || /* No username specified */
- !strcmp(iaxs[callno]->username, user->name)) /* Or this username specified */
- && ast_apply_ha(user->ha, sin) /* Access is permitted from this IP */
- && (ast_strlen_zero(iaxs[callno]->context) || /* No context specified */
- apply_context(user->contexts, iaxs[callno]->context))) { /* Context is permitted */
- if (!ast_strlen_zero(iaxs[callno]->username)) {
- /* Exact match, stop right now. */
- if (best)
- user_unref(best);
- best = user;
- break;
- } else if (ast_strlen_zero(user->secret) && ast_strlen_zero(user->dbsecret) && ast_strlen_zero(user->inkeys)) {
- /* No required authentication */
- if (user->ha) {
- /* There was host authentication and we passed, bonus! */
- if (bestscore < 4) {
- bestscore = 4;
- if (best)
- user_unref(best);
- best = user;
- continue;
- }
- } else {
- /* No host access, but no secret, either, not bad */
- if (bestscore < 3) {
- bestscore = 3;
- if (best)
- user_unref(best);
- best = user;
- continue;
- }
- }
- } else {
- if (user->ha) {
- /* Authentication, but host access too, eh, it's something.. */
- if (bestscore < 2) {
- bestscore = 2;
- if (best)
- user_unref(best);
- best = user;
- continue;
- }
- } else {
- /* Authentication and no host access... This is our baseline */
- if (bestscore < 1) {
- bestscore = 1;
- if (best)
- user_unref(best);
- best = user;
- continue;
- }
- }
- }
- }
- user_unref(user);
- }
- ao2_iterator_destroy(&i);
- user = best;
- if (!user && !ast_strlen_zero(iaxs[callno]->username)) {
- user = realtime_user(iaxs[callno]->username, sin);
- if (user && !ast_strlen_zero(iaxs[callno]->context) && /* No context specified */
- !apply_context(user->contexts, iaxs[callno]->context)) { /* Context is permitted */
- user = user_unref(user);
- }
- }
- if (user) {
- /* We found our match (use the first) */
- /* copy vars */
- for (v = user->vars ; v ; v = v->next) {
- if((tmpvar = ast_variable_new(v->name, v->value, v->file))) {
- tmpvar->next = iaxs[callno]->vars;
- iaxs[callno]->vars = tmpvar;
- }
- }
- /* If a max AUTHREQ restriction is in place, activate it */
- if (user->maxauthreq > 0)
- ast_set_flag(iaxs[callno], IAX_MAXAUTHREQ);
- iaxs[callno]->prefs = user->prefs;
- ast_copy_flags(iaxs[callno], user, IAX_CODEC_USER_FIRST);
- ast_copy_flags(iaxs[callno], user, IAX_CODEC_NOPREFS);
- ast_copy_flags(iaxs[callno], user, IAX_CODEC_NOCAP);
- iaxs[callno]->encmethods = user->encmethods;
- /* Store the requested username if not specified */
- if (ast_strlen_zero(iaxs[callno]->username))
- ast_string_field_set(iaxs[callno], username, user->name);
- /* Store whether this is a trunked call, too, of course, and move if appropriate */
- ast_copy_flags(iaxs[callno], user, IAX_TRUNK);
- iaxs[callno]->capability = user->capability;
- /* And use the default context */
- if (ast_strlen_zero(iaxs[callno]->context)) {
- if (user->contexts)
- ast_string_field_set(iaxs[callno], context, user->contexts->context);
- else
- ast_string_field_set(iaxs[callno], context, context);
- }
- /* And any input keys */
- ast_string_field_set(iaxs[callno], inkeys, user->inkeys);
- /* And the permitted authentication methods */
- iaxs[callno]->authmethods = user->authmethods;
- iaxs[callno]->adsi = user->adsi;
- /* If the user has callerid, override the remote caller id. */
- if (ast_test_flag(user, IAX_HASCALLERID)) {
- iaxs[callno]->calling_tns = 0;
- iaxs[callno]->calling_ton = 0;
- ast_string_field_set(iaxs[callno], cid_num, user->cid_num);
- ast_string_field_set(iaxs[callno], cid_name, user->cid_name);
- ast_string_field_set(iaxs[callno], ani, user->cid_num);
- iaxs[callno]->calling_pres = AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN;
- } else if (ast_strlen_zero(iaxs[callno]->cid_num) && ast_strlen_zero(iaxs[callno]->cid_name)) {
- iaxs[callno]->calling_pres = AST_PRES_NUMBER_NOT_AVAILABLE;
- } /* else user is allowed to set their own CID settings */
- if (!ast_strlen_zero(user->accountcode))
- ast_string_field_set(iaxs[callno], accountcode, user->accountcode);
- if (!ast_strlen_zero(user->mohinterpret))
- ast_string_field_set(iaxs[callno], mohinterpret, user->mohinterpret);
- if (!ast_strlen_zero(user->mohsuggest))
- ast_string_field_set(iaxs[callno], mohsuggest, user->mohsuggest);
- if (user->amaflags)
- iaxs[callno]->amaflags = user->amaflags;
- if (!ast_strlen_zero(user->language))
- ast_string_field_set(iaxs[callno], language, user->language);
- ast_copy_flags(iaxs[callno], user, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
- /* Keep this check last */
- if (!ast_strlen_zero(user->dbsecret)) {
- char *family, *key=NULL;
- char buf[80];
- family = ast_strdupa(user->dbsecret);
- key = strchr(family, '/');
- if (key) {
- *key = '\0';
- key++;
- }
- if (!key || ast_db_get(family, key, buf, sizeof(buf)))
- ast_log(LOG_WARNING, "Unable to retrieve database password for family/key '%s'!\n", user->dbsecret);
- else
- ast_string_field_set(iaxs[callno], secret, buf);
- } else
- ast_string_field_set(iaxs[callno], secret, user->secret);
- res = 0;
- user = user_unref(user);
- } else {
- /* user was not found, but we should still fake an AUTHREQ.
- * Set authmethods to the last known authmethod used by the system
- * Set a fake secret, it's not looked at, just required to attempt authentication.
- * Set authrej so the AUTHREP is rejected without even looking at its contents */
- iaxs[callno]->authmethods = last_authmethod ? last_authmethod : (IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT);
- ast_string_field_set(iaxs[callno], secret, "badsecret");
- iaxs[callno]->authrej = 1;
- if (!ast_strlen_zero(iaxs[callno]->username)) {
- /* only send the AUTHREQ if a username was specified. */
- res = 0;
- }
- }
- ast_set2_flag(iaxs[callno], iax2_getpeertrunk(*sin), IAX_TRUNK);
- return res;
- }
- static int raw_hangup(struct sockaddr_in *sin, unsigned short src, unsigned short dst, int sockfd)
- {
- struct ast_iax2_full_hdr fh;
- fh.scallno = htons(src | IAX_FLAG_FULL);
- fh.dcallno = htons(dst);
- fh.ts = 0;
- fh.oseqno = 0;
- fh.iseqno = 0;
- fh.type = AST_FRAME_IAX;
- fh.csub = compress_subclass(IAX_COMMAND_INVAL);
- if (iaxdebug)
- iax_showframe(NULL, &fh, 0, sin, 0);
- #if 0
- if (option_debug)
- #endif
- ast_debug(1, "Raw Hangup %s:%d, src=%d, dst=%d\n",
- ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), src, dst);
- return sendto(sockfd, &fh, sizeof(fh), 0, (struct sockaddr *)sin, sizeof(*sin));
- }
- static void merge_encryption(struct chan_iax2_pvt *p, unsigned int enc)
- {
- /* Select exactly one common encryption if there are any */
- p->encmethods &= enc;
- if (p->encmethods) {
- if (p->encmethods & IAX_ENCRYPT_AES128)
- p->encmethods = IAX_ENCRYPT_AES128;
- else
- p->encmethods = 0;
- }
- }
- /*!
- * \pre iaxsl[call_num] is locked
- *
- * \note Since this function calls send_command_final(), the pvt struct for the given
- * call number may disappear while executing this function.
- */
- static int authenticate_request(int call_num)
- {
- struct iax_ie_data ied;
- int res = -1, authreq_restrict = 0;
- char challenge[10];
- struct chan_iax2_pvt *p = iaxs[call_num];
- memset(&ied, 0, sizeof(ied));
- /* If an AUTHREQ restriction is in place, make sure we can send an AUTHREQ back */
- if (ast_test_flag(p, IAX_MAXAUTHREQ)) {
- struct iax2_user *user, tmp_user = {
- .name = p->username,
- };
- user = ao2_find(users, &tmp_user, OBJ_POINTER);
- if (user) {
- if (user->curauthreq == user->maxauthreq)
- authreq_restrict = 1;
- else
- user->curauthreq++;
- user = user_unref(user);
- }
- }
- /* If the AUTHREQ limit test failed, send back an error */
- if (authreq_restrict) {
- iax_ie_append_str(&ied, IAX_IE_CAUSE, "Unauthenticated call limit reached");
- iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, AST_CAUSE_CALL_REJECTED);
- send_command_final(p, AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied.buf, ied.pos, -1);
- return 0;
- }
- iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods);
- if (p->authmethods & (IAX_AUTH_MD5 | IAX_AUTH_RSA)) {
- snprintf(challenge, sizeof(challenge), "%d", (int)ast_random());
- ast_string_field_set(p, challenge, challenge);
- /* snprintf(p->challenge, sizeof(p->challenge), "%d", (int)ast_random()); */
- iax_ie_append_str(&ied, IAX_IE_CHALLENGE, p->challenge);
- }
- if (p->encmethods)
- iax_ie_append_short(&ied, IAX_IE_ENCRYPTION, p->encmethods);
- iax_ie_append_str(&ied,IAX_IE_USERNAME, p->username);
- res = send_command(p, AST_FRAME_IAX, IAX_COMMAND_AUTHREQ, 0, ied.buf, ied.pos, -1);
- if (p->encmethods)
- ast_set_flag(p, IAX_ENCRYPTED);
- return res;
- }
- static int authenticate_verify(struct chan_iax2_pvt *p, struct iax_ies *ies)
- {
- char requeststr[256];
- char md5secret[256] = "";
- char secret[256] = "";
- char rsasecret[256] = "";
- int res = -1;
- int x;
- struct iax2_user *user, tmp_user = {
- .name = p->username,
- };
- if (p->authrej) {
- return res;
- }
- user = ao2_find(users, &tmp_user, OBJ_POINTER);
- if (user) {
- if (ast_test_flag(p, IAX_MAXAUTHREQ)) {
- ast_atomic_fetchadd_int(&user->curauthreq, -1);
- ast_clear_flag(p, IAX_MAXAUTHREQ);
- }
- ast_string_field_set(p, host, user->name);
- user = user_unref(user);
- }
- if (!ast_test_flag(&p->state, IAX_STATE_AUTHENTICATED))
- return res;
- if (ies->password)
- ast_copy_string(secret, ies->password, sizeof(secret));
- if (ies->md5_result)
- ast_copy_string(md5secret, ies->md5_result, sizeof(md5secret));
- if (ies->rsa_result)
- ast_copy_string(rsasecret, ies->rsa_result, sizeof(rsasecret));
- if ((p->authmethods & IAX_AUTH_RSA) && !ast_strlen_zero(rsasecret) && !ast_strlen_zero(p->inkeys)) {
- struct ast_key *key;
- char *keyn;
- char tmpkey[256];
- char *stringp=NULL;
- ast_copy_string(tmpkey, p->inkeys, sizeof(tmpkey));
- stringp=tmpkey;
- keyn = strsep(&stringp, ":");
- while(keyn) {
- key = ast_key_get(keyn, AST_KEY_PUBLIC);
- if (key && !ast_check_signature(key, p->challenge, rsasecret)) {
- res = 0;
- break;
- } else if (!key)
- ast_log(LOG_WARNING, "requested inkey '%s' for RSA authentication does not exist\n", keyn);
- keyn = strsep(&stringp, ":");
- }
- } else if (p->authmethods & IAX_AUTH_MD5) {
- struct MD5Context md5;
- unsigned char digest[16];
- char *tmppw, *stringp;
-
- tmppw = ast_strdupa(p->secret);
- stringp = tmppw;
- while((tmppw = strsep(&stringp, ";"))) {
- MD5Init(&md5);
- MD5Update(&md5, (unsigned char *)p->challenge, strlen(p->challenge));
- MD5Update(&md5, (unsigned char *)tmppw, strlen(tmppw));
- MD5Final(digest, &md5);
- /* If they support md5, authenticate with it. */
- for (x=0;x<16;x++)
- sprintf(requeststr + (x << 1), "%2.2x", digest[x]); /* safe */
- if (!strcasecmp(requeststr, md5secret)) {
- res = 0;
- break;
- }
- }
- } else if (p->authmethods & IAX_AUTH_PLAINTEXT) {
- if (!strcmp(secret, p->secret))
- res = 0;
- }
- return res;
- }
- /*! \brief Verify inbound registration */
- static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *ies)
- {
- char requeststr[256] = "";
- char peer[256] = "";
- char md5secret[256] = "";
- char rsasecret[256] = "";
- char secret[256] = "";
- struct iax2_peer *p = NULL;
- struct ast_key *key;
- char *keyn;
- int x;
- int expire = 0;
- int res = -1;
- ast_clear_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED);
- /* iaxs[callno]->peer[0] = '\0'; not necc. any more-- stringfield is pre-inited to null string */
- if (ies->username)
- ast_copy_string(peer, ies->username, sizeof(peer));
- if (ies->password)
- ast_copy_string(secret, ies->password, sizeof(secret));
- if (ies->md5_result)
- ast_copy_string(md5secret, ies->md5_result, sizeof(md5secret));
- if (ies->rsa_result)
- ast_copy_string(rsasecret, ies->rsa_result, sizeof(rsasecret));
- if (ies->refresh)
- expire = ies->refresh;
- if (ast_strlen_zero(peer)) {
- ast_log(LOG_NOTICE, "Empty registration from %s\n", ast_inet_ntoa(sin->sin_addr));
- return -1;
- }
- /* SLD: first call to lookup peer during registration */
- ast_mutex_unlock(&iaxsl[callno]);
- p = find_peer(peer, 1);
- ast_mutex_lock(&iaxsl[callno]);
- if (!p || !iaxs[callno]) {
- if (iaxs[callno]) {
- int plaintext = ((last_authmethod & IAX_AUTH_PLAINTEXT) | (iaxs[callno]->authmethods & IAX_AUTH_PLAINTEXT));
- /* Anything, as long as it's non-blank */
- ast_string_field_set(iaxs[callno], secret, "badsecret");
- /* An AUTHREQ must be sent in response to a REGREQ of an invalid peer unless
- * 1. A challenge already exists indicating a AUTHREQ was already sent out.
- * 2. A plaintext secret is present in ie as result of a previous AUTHREQ requesting it.
- * 3. A plaintext secret is present in the ie and the last_authmethod used by a peer happened
- * to be plaintext, indicating it is an authmethod used by other peers on the system.
- *
- * If none of these cases exist, res will be returned as 0 without authentication indicating
- * an AUTHREQ needs to be sent out. */
- if (ast_strlen_zero(iaxs[callno]->challenge) &&
- !(!ast_strlen_zero(secret) && plaintext)) {
- /* by setting res to 0, an REGAUTH will be sent */
- res = 0;
- }
- }
- if (authdebug && !p)
- ast_log(LOG_NOTICE, "No registration for peer '%s' (from %s)\n", peer, ast_inet_ntoa(sin->sin_addr));
- goto return_unref;
- }
- if (!ast_test_flag(p, IAX_DYNAMIC)) {
- if (authdebug)
- ast_log(LOG_NOTICE, "Peer '%s' is not dynamic (from %s)\n", peer, ast_inet_ntoa(sin->sin_addr));
- goto return_unref;
- }
- if (!ast_apply_ha(p->ha, sin)) {
- if (authdebug)
- ast_log(LOG_NOTICE, "Host %s denied access to register peer '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name);
- goto return_unref;
- }
- ast_string_field_set(iaxs[callno], secret, p->secret);
- ast_string_field_set(iaxs[callno], inkeys, p->inkeys);
- /* Check secret against what we have on file */
- if (!ast_strlen_zero(rsasecret) && (p->authmethods & IAX_AUTH_RSA) && !ast_strlen_zero(iaxs[callno]->challenge)) {
- if (!ast_strlen_zero(p->inkeys)) {
- char tmpkeys[256];
- char *stringp=NULL;
- ast_copy_string(tmpkeys, p->inkeys, sizeof(tmpkeys));
- stringp=tmpkeys;
- keyn = strsep(&stringp, ":");
- while(keyn) {
- key = ast_key_get(keyn, AST_KEY_PUBLIC);
- if (key && !ast_check_signature(key, iaxs[callno]->challenge, rsasecret)) {
- ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED);
- break;
- } else if (!key)
- ast_log(LOG_WARNING, "requested inkey '%s' does not exist\n", keyn);
- keyn = strsep(&stringp, ":");
- }
- if (!keyn) {
- if (authdebug)
- ast_log(LOG_NOTICE, "Host %s failed RSA authentication with inkeys '%s'\n", peer, p->inkeys);
- goto return_unref;
- }
- } else {
- if (authdebug)
- ast_log(LOG_NOTICE, "Host '%s' trying to do RSA authentication, but we have no inkeys\n", peer);
- goto return_unref;
- }
- } else if (!ast_strlen_zero(md5secret) && (p->authmethods & IAX_AUTH_MD5) && !ast_strlen_zero(iaxs[callno]->challenge)) {
- struct MD5Context md5;
- unsigned char digest[16];
- char *tmppw, *stringp;
- tmppw = ast_strdupa(p->secret);
- stringp = tmppw;
- while((tmppw = strsep(&stringp, ";"))) {
- MD5Init(&md5);
- MD5Update(&md5, (unsigned char *)iaxs[callno]->challenge, strlen(iaxs[callno]->challenge));
- MD5Update(&md5, (unsigned char *)tmppw, strlen(tmppw));
- MD5Final(digest, &md5);
- for (x=0;x<16;x++)
- sprintf(requeststr + (x << 1), "%2.2x", digest[x]); /* safe */
- if (!strcasecmp(requeststr, md5secret))
- break;
- }
- if (tmppw) {
- ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED);
- } else {
- if (authdebug)
- ast_log(LOG_NOTICE, "Host %s failed MD5 authentication for '%s' (%s != %s)\n", ast_inet_ntoa(sin->sin_addr), p->name, requeststr, md5secret);
- goto return_unref;
- }
- } else if (!ast_strlen_zero(secret) && (p->authmethods & IAX_AUTH_PLAINTEXT)) {
- /* They've provided a plain text password and we support that */
- if (strcmp(secret, p->secret)) {
- if (authdebug)
- ast_log(LOG_NOTICE, "Host %s did not provide proper plaintext password for '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name);
- goto return_unref;
- } else
- ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED);
- } else if (!ast_strlen_zero(iaxs[callno]->challenge) && ast_strlen_zero(md5secret) && ast_strlen_zero(rsasecret)) {
- /* if challenge has been sent, but no challenge response if given, reject. */
- goto return_unref;
- }
- ast_devstate_changed(AST_DEVICE_UNKNOWN, "IAX2/%s", p->name); /* Activate notification */
- /* either Authentication has taken place, or a REGAUTH must be sent before verifying registration */
- res = 0;
- return_unref:
- if (iaxs[callno]) {
- ast_string_field_set(iaxs[callno], peer, peer);
- /* Choose lowest expiry number */
- if (expire && (expire < iaxs[callno]->expiry)) {
- iaxs[callno]->expiry = expire;
- }
- }
- if (p) {
- peer_unref(p);
- }
- return res;
- }
- static int authenticate(const char *challenge, const char *secret, const char *keyn, int authmethods, struct iax_ie_data *ied, struct sockaddr_in *sin, struct chan_iax2_pvt *pvt)
- {
- int res = -1;
- int x;
- if (!ast_strlen_zero(keyn)) {
- if (!(authmethods & IAX_AUTH_RSA)) {
- if (ast_strlen_zero(secret))
- ast_log(LOG_NOTICE, "Asked to authenticate to %s with an RSA key, but they don't allow RSA authentication\n", ast_inet_ntoa(sin->sin_addr));
- } else if (ast_strlen_zero(challenge)) {
- ast_log(LOG_NOTICE, "No challenge provided for RSA authentication to %s\n", ast_inet_ntoa(sin->sin_addr));
- } else {
- char sig[256];
- struct ast_key *key;
- key = ast_key_get(keyn, AST_KEY_PRIVATE);
- if (!key) {
- ast_log(LOG_NOTICE, "Unable to find private key '%s'\n", keyn);
- } else {
- if (ast_sign(key, (char*)challenge, sig)) {
- ast_log(LOG_NOTICE, "Unable to sign challenge with key\n");
- res = -1;
- } else {
- iax_ie_append_str(ied, IAX_IE_RSA_RESULT, sig);
- res = 0;
- }
- }
- }
- }
- /* Fall back */
- if (res && !ast_strlen_zero(secret)) {
- if ((authmethods & IAX_AUTH_MD5) && !ast_strlen_zero(challenge)) {
- struct MD5Context md5;
- unsigned char digest[16];
- char digres[128];
- MD5Init(&md5);
- MD5Update(&md5, (unsigned char *)challenge, strlen(challenge));
- MD5Update(&md5, (unsigned char *)secret, strlen(secret));
- MD5Final(digest, &md5);
- /* If they support md5, authenticate with it. */
- for (x=0;x<16;x++)
- sprintf(digres + (x << 1), "%2.2x", digest[x]); /* safe */
- if (pvt) {
- build_encryption_keys(digest, pvt);
- }
- iax_ie_append_str(ied, IAX_IE_MD5_RESULT, digres);
- res = 0;
- } else if (authmethods & IAX_AUTH_PLAINTEXT) {
- iax_ie_append_str(ied, IAX_IE_PASSWORD, secret);
- res = 0;
- } else
- ast_log(LOG_NOTICE, "No way to send secret to peer '%s' (their methods: %d)\n", ast_inet_ntoa(sin->sin_addr), authmethods);
- }
- return res;
- }
- /*!
- * \note This function calls realtime_peer -> reg_source_db -> iax2_poke_peer -> find_callno,
- * so do not call this function with a pvt lock held.
- */
- static int authenticate_reply(struct chan_iax2_pvt *p, struct sockaddr_in *sin, struct iax_ies *ies, const char *override, const char *okey)
- {
- struct iax2_peer *peer = NULL;
- /* Start pessimistic */
- int res = -1;
- int authmethods = 0;
- struct iax_ie_data ied;
- uint16_t callno = p->callno;
- memset(&ied, 0, sizeof(ied));
-
- if (ies->username)
- ast_string_field_set(p, username, ies->username);
- if (ies->challenge)
- ast_string_field_set(p, challenge, ies->challenge);
- if (ies->authmethods)
- authmethods = ies->authmethods;
- if (authmethods & IAX_AUTH_MD5)
- merge_encryption(p, ies->encmethods);
- else
- p->encmethods = 0;
- /* Check for override RSA authentication first */
- if (!ast_strlen_zero(override) || !ast_strlen_zero(okey)) {
- /* Normal password authentication */
- res = authenticate(p->challenge, override, okey, authmethods, &ied, sin, p);
- } else {
- struct ao2_iterator i = ao2_iterator_init(peers, 0);
- while ((peer = ao2_iterator_next(&i))) {
- if ((ast_strlen_zero(p->peer) || !strcmp(p->peer, peer->name))
- /* No peer specified at our end, or this is the peer */
- && (ast_strlen_zero(peer->username) || (!strcmp(peer->username, p->username)))
- /* No username specified in peer rule, or this is the right username */
- && (!peer->addr.sin_addr.s_addr || ((sin->sin_addr.s_addr & peer->mask.s_addr) == (peer->addr.sin_addr.s_addr & peer->mask.s_addr)))
- /* No specified host, or this is our host */
- ) {
- res = authenticate(p->challenge, peer->secret, peer->outkey, authmethods, &ied, sin, p);
- if (!res) {
- peer_unref(peer);
- break;
- }
- }
- peer_unref(peer);
- }
- ao2_iterator_destroy(&i);
- if (!peer) {
- /* We checked our list and didn't find one. It's unlikely, but possible,
- that we're trying to authenticate *to* a realtime peer */
- const char *peer_name = ast_strdupa(p->peer);
- ast_mutex_unlock(&iaxsl[callno]);
- if ((peer = realtime_peer(peer_name, NULL))) {
- ast_mutex_lock(&iaxsl[callno]);
- if (!(p = iaxs[callno])) {
- peer_unref(peer);
- return -1;
- }
- res = authenticate(p->challenge, peer->secret,peer->outkey, authmethods, &ied, sin, p);
- peer_unref(peer);
- }
- if (!peer) {
- ast_mutex_lock(&iaxsl[callno]);
- if (!(p = iaxs[callno]))
- return -1;
- }
- }
- }
- if (ies->encmethods)
- ast_set_flag(p, IAX_ENCRYPTED | IAX_KEYPOPULATED);
- if (!res) {
- struct ast_datastore *variablestore;
- struct ast_variable *var, *prev = NULL;
- AST_LIST_HEAD(, ast_var_t) *varlist;
- varlist = ast_calloc(1, sizeof(*varlist));
- variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
- if (variablestore && varlist && p->owner) {
- variablestore->data = varlist;
- variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
- AST_LIST_HEAD_INIT(varlist);
- for (var = ies->vars; var; var = var->next) {
- struct ast_var_t *newvar = ast_var_assign(var->name, var->value);
- if (prev)
- ast_free(prev);
- prev = var;
- if (!newvar) {
- /* Don't abort list traversal, as this would leave ies->vars in an inconsistent state. */
- ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
- } else {
- AST_LIST_INSERT_TAIL(varlist, newvar, entries);
- }
- }
- if (prev)
- ast_free(prev);
- ies->vars = NULL;
- ast_channel_datastore_add(p->owner, variablestore);
- } else {
- if (p->owner)
- ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
- if (variablestore)
- ast_channel_datastore_free(variablestore);
- if (varlist)
- ast_free(varlist);
- }
- }
- if (!res)
- res = send_command(p, AST_FRAME_IAX, IAX_COMMAND_AUTHREP, 0, ied.buf, ied.pos, -1);
- return res;
- }
- static int iax2_do_register(struct iax2_registry *reg);
- static void __iax2_do_register_s(const void *data)
- {
- struct iax2_registry *reg = (struct iax2_registry *)data;
- reg->expire = -1;
- iax2_do_register(reg);
- }
- static int iax2_do_register_s(const void *data)
- {
- #ifdef SCHED_MULTITHREADED
- if (schedule_action(__iax2_do_register_s, data))
- #endif
- __iax2_do_register_s(data);
- return 0;
- }
- static int try_transfer(struct chan_iax2_pvt *pvt, struct iax_ies *ies)
- {
- int newcall = 0;
- char newip[256];
- struct iax_ie_data ied;
- struct sockaddr_in new;
-
-
- memset(&ied, 0, sizeof(ied));
- if (ies->apparent_addr)
- memmove(&new, ies->apparent_addr, sizeof(new));
- if (ies->callno)
- newcall = ies->callno;
- if (!newcall || !new.sin_addr.s_addr || !new.sin_port) {
- ast_log(LOG_WARNING, "Invalid transfer request\n");
- return -1;
- }
- pvt->transfercallno = newcall;
- memcpy(&pvt->transfer, &new, sizeof(pvt->transfer));
- inet_aton(newip, &pvt->transfer.sin_addr);
- pvt->transfer.sin_family = AF_INET;
- pvt->transferid = ies->transferid;
- /* only store by transfercallno if this is a new transfer,
- * just in case we get a duplicate TXREQ */
- if (pvt->transferring == TRANSFER_NONE) {
- store_by_transfercallno(pvt);
- }
- pvt->transferring = TRANSFER_BEGIN;
- if (ies->transferid)
- iax_ie_append_int(&ied, IAX_IE_TRANSFERID, ies->transferid);
- send_command_transfer(pvt, AST_FRAME_IAX, IAX_COMMAND_TXCNT, 0, ied.buf, ied.pos);
- return 0;
- }
- static int complete_dpreply(struct chan_iax2_pvt *pvt, struct iax_ies *ies)
- {
- char exten[256] = "";
- int status = CACHE_FLAG_UNKNOWN, expiry = iaxdefaultdpcache, x, matchmore = 0;
- struct iax2_dpcache *dp = NULL;
-
- if (ies->called_number)
- ast_copy_string(exten, ies->called_number, sizeof(exten));
-
- if (ies->dpstatus & IAX_DPSTATUS_EXISTS)
- status = CACHE_FLAG_EXISTS;
- else if (ies->dpstatus & IAX_DPSTATUS_CANEXIST)
- status = CACHE_FLAG_CANEXIST;
- else if (ies->dpstatus & IAX_DPSTATUS_NONEXISTENT)
- status = CACHE_FLAG_NONEXISTENT;
- if (ies->refresh)
- expiry = ies->refresh;
- if (ies->dpstatus & IAX_DPSTATUS_MATCHMORE)
- matchmore = CACHE_FLAG_MATCHMORE;
-
- AST_LIST_LOCK(&dpcache);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&dpcache, dp, peer_list) {
- if (strcmp(dp->exten, exten))
- continue;
- AST_LIST_REMOVE_CURRENT(peer_list);
- dp->callno = 0;
- dp->expiry.tv_sec = dp->orig.tv_sec + expiry;
- if (dp->flags & CACHE_FLAG_PENDING) {
- dp->flags &= ~CACHE_FLAG_PENDING;
- dp->flags |= status;
- dp->flags |= matchmore;
- }
- /* Wake up waiters */
- for (x = 0; x < ARRAY_LEN(dp->waiters); x++) {
- if (dp->waiters[x] > -1) {
- if (write(dp->waiters[x], "asdf", 4) < 0) {
- }
- }
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
- AST_LIST_UNLOCK(&dpcache);
- return 0;
- }
- static int complete_transfer(int callno, struct iax_ies *ies)
- {
- int peercallno = 0;
- struct chan_iax2_pvt *pvt = iaxs[callno];
- struct iax_frame *cur;
- jb_frame frame;
- if (ies->callno)
- peercallno = ies->callno;
- if (peercallno < 1) {
- ast_log(LOG_WARNING, "Invalid transfer request\n");
- return -1;
- }
- remove_by_transfercallno(pvt);
- /* since a transfer has taken place, the address will change.
- * This must be accounted for in the peercnts table. Remove
- * the old address and add the new one */
- peercnt_remove_by_addr(&pvt->addr);
- peercnt_add(&pvt->transfer);
- /* now copy over the new address */
- memcpy(&pvt->addr, &pvt->transfer, sizeof(pvt->addr));
- memset(&pvt->transfer, 0, sizeof(pvt->transfer));
- /* Reset sequence numbers */
- pvt->oseqno = 0;
- pvt->rseqno = 0;
- pvt->iseqno = 0;
- pvt->aseqno = 0;
- if (pvt->peercallno) {
- remove_by_peercallno(pvt);
- }
- pvt->peercallno = peercallno;
- /*this is where the transfering call swiches hash tables */
- store_by_peercallno(pvt);
- pvt->transferring = TRANSFER_NONE;
- pvt->svoiceformat = -1;
- pvt->voiceformat = 0;
- pvt->svideoformat = -1;
- pvt->videoformat = 0;
- pvt->transfercallno = 0;
- memset(&pvt->rxcore, 0, sizeof(pvt->rxcore));
- memset(&pvt->offset, 0, sizeof(pvt->offset));
- /* reset jitterbuffer */
- while(jb_getall(pvt->jb,&frame) == JB_OK)
- iax2_frame_free(frame.data);
- jb_reset(pvt->jb);
- pvt->lag = 0;
- pvt->last = 0;
- pvt->lastsent = 0;
- pvt->nextpred = 0;
- pvt->pingtime = DEFAULT_RETRY_TIME;
- AST_LIST_LOCK(&frame_queue);
- AST_LIST_TRAVERSE(&frame_queue, cur, list) {
- /* We must cancel any packets that would have been transmitted
- because now we're talking to someone new. It's okay, they
- were transmitted to someone that didn't care anyway. */
- if (callno == cur->callno)
- cur->retries = -1;
- }
- AST_LIST_UNLOCK(&frame_queue);
- return 0;
- }
- /*! \brief Acknowledgment received for OUR registration */
- static int iax2_ack_registry(struct iax_ies *ies, struct sockaddr_in *sin, int callno)
- {
- struct iax2_registry *reg;
- /* Start pessimistic */
- char peer[256] = "";
- char msgstatus[60];
- int refresh = 60;
- char ourip[256] = "<Unspecified>";
- struct sockaddr_in oldus;
- struct sockaddr_in us;
- int oldmsgs;
- memset(&us, 0, sizeof(us));
- if (ies->apparent_addr)
- memmove(&us, ies->apparent_addr, sizeof(us));
- if (ies->username)
- ast_copy_string(peer, ies->username, sizeof(peer));
- if (ies->refresh)
- refresh = ies->refresh;
- if (ies->calling_number) {
- /* We don't do anything with it really, but maybe we should */
- }
- reg = iaxs[callno]->reg;
- if (!reg) {
- ast_log(LOG_WARNING, "Registry acknowledge on unknown registry '%s'\n", peer);
- return -1;
- }
- memcpy(&oldus, ®->us, sizeof(oldus));
- oldmsgs = reg->messages;
- if (inaddrcmp(®->addr, sin)) {
- ast_log(LOG_WARNING, "Received unsolicited registry ack from '%s'\n", ast_inet_ntoa(sin->sin_addr));
- return -1;
- }
- memcpy(®->us, &us, sizeof(reg->us));
- if (ies->msgcount >= 0)
- reg->messages = ies->msgcount & 0xffff; /* only low 16 bits are used in the transmission of the IE */
- /* always refresh the registration at the interval requested by the server
- we are registering to
- */
- reg->refresh = refresh;
- reg->expire = iax2_sched_replace(reg->expire, sched,
- (5 * reg->refresh / 6) * 1000, iax2_do_register_s, reg);
- if (inaddrcmp(&oldus, ®->us) || (reg->messages != oldmsgs)) {
- if (reg->messages > 255)
- snprintf(msgstatus, sizeof(msgstatus), " with %d new and %d old messages waiting", reg->messages & 0xff, reg->messages >> 8);
- else if (reg->messages > 1)
- snprintf(msgstatus, sizeof(msgstatus), " with %d new messages waiting\n", reg->messages);
- else if (reg->messages > 0)
- ast_copy_string(msgstatus, " with 1 new message waiting\n", sizeof(msgstatus));
- else
- ast_copy_string(msgstatus, " with no messages waiting\n", sizeof(msgstatus));
- snprintf(ourip, sizeof(ourip), "%s:%d", ast_inet_ntoa(reg->us.sin_addr), ntohs(reg->us.sin_port));
- ast_verb(3, "Registered IAX2 to '%s', who sees us as %s%s\n", ast_inet_ntoa(sin->sin_addr), ourip, msgstatus);
- manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelType: IAX2\r\nDomain: %s\r\nStatus: Registered\r\n", ast_inet_ntoa(sin->sin_addr));
- }
- reg->regstate = REG_STATE_REGISTERED;
- return 0;
- }
- static int iax2_append_register(const char *hostname, const char *username,
- const char *secret, const char *porta)
- {
- struct iax2_registry *reg;
- if (!(reg = ast_calloc(1, sizeof(*reg))))
- return -1;
- if (ast_dnsmgr_lookup(hostname, ®->addr.sin_addr, ®->dnsmgr) < 0) {
- ast_free(reg);
- return -1;
- }
- ast_copy_string(reg->username, username, sizeof(reg->username));
- if (secret)
- ast_copy_string(reg->secret, secret, sizeof(reg->secret));
- reg->expire = -1;
- reg->refresh = IAX_DEFAULT_REG_EXPIRE;
- reg->addr.sin_family = AF_INET;
- reg->addr.sin_port = porta ? htons(atoi(porta)) : htons(IAX_DEFAULT_PORTNO);
- AST_LIST_LOCK(®istrations);
- AST_LIST_INSERT_HEAD(®istrations, reg, entry);
- AST_LIST_UNLOCK(®istrations);
-
- return 0;
- }
- static int iax2_register(const char *value, int lineno)
- {
- char copy[256];
- char *username, *hostname, *secret;
- char *porta;
- char *stringp=NULL;
-
- if (!value)
- return -1;
- ast_copy_string(copy, value, sizeof(copy));
- stringp = copy;
- username = strsep(&stringp, "@");
- hostname = strsep(&stringp, "@");
- if (!hostname) {
- ast_log(LOG_WARNING, "Format for registration is user[:secret]@host[:port] at line %d\n", lineno);
- return -1;
- }
- stringp = username;
- username = strsep(&stringp, ":");
- secret = strsep(&stringp, ":");
- stringp = hostname;
- hostname = strsep(&stringp, ":");
- porta = strsep(&stringp, ":");
-
- if (porta && !atoi(porta)) {
- ast_log(LOG_WARNING, "%s is not a valid port number at line %d\n", porta, lineno);
- return -1;
- }
- return iax2_append_register(hostname, username, secret, porta);
- }
- static void register_peer_exten(struct iax2_peer *peer, int onoff)
- {
- char multi[256];
- char *stringp, *ext;
- if (!ast_strlen_zero(regcontext)) {
- ast_copy_string(multi, S_OR(peer->regexten, peer->name), sizeof(multi));
- stringp = multi;
- while((ext = strsep(&stringp, "&"))) {
- if (onoff) {
- if (!ast_exists_extension(NULL, regcontext, ext, 1, NULL))
- ast_add_extension(regcontext, 1, ext, 1, NULL, NULL,
- "Noop", ast_strdup(peer->name), ast_free_ptr, "IAX2");
- } else
- ast_context_remove_extension(regcontext, ext, 1, NULL);
- }
- }
- }
- static void prune_peers(void);
- static void unlink_peer(struct iax2_peer *peer)
- {
- if (peer->expire > -1) {
- if (!ast_sched_del(sched, peer->expire)) {
- peer->expire = -1;
- peer_unref(peer);
- }
- }
- if (peer->pokeexpire > -1) {
- if (!ast_sched_del(sched, peer->pokeexpire)) {
- peer->pokeexpire = -1;
- peer_unref(peer);
- }
- }
- ao2_unlink(peers, peer);
- }
- static void __expire_registry(const void *data)
- {
- struct iax2_peer *peer = (struct iax2_peer *) data;
- if (!peer)
- return;
- peer->expire = -1;
- ast_debug(1, "Expiring registration for peer '%s'\n", peer->name);
- if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(peer, IAX_TEMPONLY|IAX_RTCACHEFRIENDS)))
- realtime_update_peer(peer->name, &peer->addr, 0);
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
- /* modify entry in peercnts table as _not_ registered */
- peercnt_modify(0, 0, &peer->addr);
- /* Reset the address */
- memset(&peer->addr, 0, sizeof(peer->addr));
- /* Reset expiry value */
- peer->expiry = min_reg_expire;
- if (!ast_test_flag(peer, IAX_TEMPONLY))
- ast_db_del("IAX/Registry", peer->name);
- register_peer_exten(peer, 0);
- ast_device_state_changed("IAX2/%s", peer->name); /* Activate notification */
- if (iax2_regfunk)
- iax2_regfunk(peer->name, 0);
- if (ast_test_flag(peer, IAX_RTAUTOCLEAR))
- unlink_peer(peer);
- peer_unref(peer);
- }
- static int expire_registry(const void *data)
- {
- #ifdef SCHED_MULTITHREADED
- if (schedule_action(__expire_registry, data))
- #endif
- __expire_registry(data);
- return 0;
- }
- static int iax2_poke_peer(struct iax2_peer *peer, int heldcall);
- static void reg_source_db(struct iax2_peer *p)
- {
- char data[80];
- struct in_addr in;
- char *c, *d;
- if (!ast_test_flag(p, IAX_TEMPONLY) && (!ast_db_get("IAX/Registry", p->name, data, sizeof(data)))) {
- c = strchr(data, ':');
- if (c) {
- *c = '\0';
- c++;
- if (inet_aton(data, &in)) {
- d = strchr(c, ':');
- if (d) {
- *d = '\0';
- d++;
- ast_verb(3, "Seeding '%s' at %s:%d for %d\n", p->name,
- ast_inet_ntoa(in), atoi(c), atoi(d));
- iax2_poke_peer(p, 0);
- p->expiry = atoi(d);
- memset(&p->addr, 0, sizeof(p->addr));
- p->addr.sin_family = AF_INET;
- p->addr.sin_addr = in;
- p->addr.sin_port = htons(atoi(c));
- if (p->expire > -1) {
- if (!ast_sched_del(sched, p->expire)) {
- p->expire = -1;
- peer_unref(p);
- }
- }
- ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
- p->expire = iax2_sched_add(sched, (p->expiry + 10) * 1000, expire_registry, peer_ref(p));
- if (p->expire == -1)
- peer_unref(p);
- if (iax2_regfunk)
- iax2_regfunk(p->name, 1);
- register_peer_exten(p, 1);
- }
-
- }
- }
- }
- }
- /*!
- * \pre iaxsl[callno] is locked
- *
- * \note Since this function calls send_command_final(), the pvt struct for
- * the given call number may disappear while executing this function.
- */
- static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, int fd, unsigned short refresh)
- {
- /* Called from IAX thread only, with proper iaxsl lock */
- struct iax_ie_data ied;
- struct iax2_peer *p;
- int msgcount;
- char data[80];
- int version;
- const char *peer_name;
- int res = -1;
- memset(&ied, 0, sizeof(ied));
- peer_name = ast_strdupa(iaxs[callno]->peer);
- /* SLD: Another find_peer call during registration - this time when we are really updating our registration */
- ast_mutex_unlock(&iaxsl[callno]);
- if (!(p = find_peer(peer_name, 1))) {
- ast_mutex_lock(&iaxsl[callno]);
- ast_log(LOG_WARNING, "No such peer '%s'\n", peer_name);
- return -1;
- }
- ast_mutex_lock(&iaxsl[callno]);
- if (!iaxs[callno])
- goto return_unref;
- if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(p, IAX_TEMPONLY|IAX_RTCACHEFRIENDS))) {
- if (sin->sin_addr.s_addr) {
- time_t nowtime;
- time(&nowtime);
- realtime_update_peer(peer_name, sin, nowtime);
- } else {
- realtime_update_peer(peer_name, sin, 0);
- }
- }
- if (inaddrcmp(&p->addr, sin)) {
- if (iax2_regfunk)
- iax2_regfunk(p->name, 1);
- /* modify entry in peercnts table as _not_ registered */
- peercnt_modify(0, 0, &p->addr);
- /* Stash the IP address from which they registered */
- memcpy(&p->addr, sin, sizeof(p->addr));
- snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), p->expiry);
- if (!ast_test_flag(p, IAX_TEMPONLY) && sin->sin_addr.s_addr) {
- ast_db_put("IAX/Registry", p->name, data);
- ast_verb(3, "Registered IAX2 '%s' (%s) at %s:%d\n", p->name,
- ast_test_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED) ? "AUTHENTICATED" : "UNAUTHENTICATED", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Registered\r\n", p->name);
- register_peer_exten(p, 1);
- ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
- } else if (!ast_test_flag(p, IAX_TEMPONLY)) {
- ast_verb(3, "Unregistered IAX2 '%s' (%s)\n", p->name,
- ast_test_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED) ? "AUTHENTICATED" : "UNAUTHENTICATED");
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unregistered\r\n", p->name);
- register_peer_exten(p, 0);
- ast_db_del("IAX/Registry", p->name);
- ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
- }
- /* Update the host */
- /* Verify that the host is really there */
- iax2_poke_peer(p, callno);
- }
- /* modify entry in peercnts table as registered */
- if (p->maxcallno) {
- peercnt_modify(1, p->maxcallno, &p->addr);
- }
- /* Make sure our call still exists, an INVAL at the right point may make it go away */
- if (!iaxs[callno]) {
- res = -1;
- goto return_unref;
- }
- /* Store socket fd */
- p->sockfd = fd;
- /* Setup the expiry */
- if (p->expire > -1) {
- if (!ast_sched_del(sched, p->expire)) {
- p->expire = -1;
- peer_unref(p);
- }
- }
- /* treat an unspecified refresh interval as the minimum */
- if (!refresh)
- refresh = min_reg_expire;
- if (refresh > max_reg_expire) {
- ast_log(LOG_NOTICE, "Restricting registration for peer '%s' to %d seconds (requested %d)\n",
- p->name, max_reg_expire, refresh);
- p->expiry = max_reg_expire;
- } else if (refresh < min_reg_expire) {
- ast_log(LOG_NOTICE, "Restricting registration for peer '%s' to %d seconds (requested %d)\n",
- p->name, min_reg_expire, refresh);
- p->expiry = min_reg_expire;
- } else {
- p->expiry = refresh;
- }
- if (p->expiry && sin->sin_addr.s_addr) {
- p->expire = iax2_sched_add(sched, (p->expiry + 10) * 1000, expire_registry, peer_ref(p));
- if (p->expire == -1)
- peer_unref(p);
- }
- iax_ie_append_str(&ied, IAX_IE_USERNAME, p->name);
- iax_ie_append_int(&ied, IAX_IE_DATETIME, iax2_datetime(p->zonetag));
- if (sin->sin_addr.s_addr) {
- iax_ie_append_short(&ied, IAX_IE_REFRESH, p->expiry);
- iax_ie_append_addr(&ied, IAX_IE_APPARENT_ADDR, &p->addr);
- if (!ast_strlen_zero(p->mailbox)) {
- struct ast_event *event;
- int new, old;
- char *mailbox, *context;
- context = mailbox = ast_strdupa(p->mailbox);
- strsep(&context, "@");
- if (ast_strlen_zero(context))
- context = "default";
- event = ast_event_get_cached(AST_EVENT_MWI,
- AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
- AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
- AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
- AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
- AST_EVENT_IE_END);
- if (event) {
- new = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
- old = ast_event_get_ie_uint(event, AST_EVENT_IE_OLDMSGS);
- ast_event_destroy(event);
- } else /* Fall back on checking the mailbox directly */
- ast_app_inboxcount(p->mailbox, &new, &old);
- if (new > 255)
- new = 255;
- if (old > 255)
- old = 255;
- msgcount = (old << 8) | new;
- iax_ie_append_short(&ied, IAX_IE_MSGCOUNT, msgcount);
- }
- if (ast_test_flag(p, IAX_HASCALLERID)) {
- iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, p->cid_num);
- iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, p->cid_name);
- }
- }
- version = iax_check_version(devtype);
- if (version)
- iax_ie_append_short(&ied, IAX_IE_FIRMWAREVER, version);
- res = 0;
- return_unref:
- peer_unref(p);
- return res ? res : send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGACK, 0, ied.buf, ied.pos, -1);
- }
- static int registry_authrequest(int callno)
- {
- struct iax_ie_data ied;
- struct iax2_peer *p;
- char challenge[10];
- const char *peer_name;
- int sentauthmethod;
- peer_name = ast_strdupa(iaxs[callno]->peer);
- /* SLD: third call to find_peer in registration */
- ast_mutex_unlock(&iaxsl[callno]);
- if ((p = find_peer(peer_name, 1))) {
- last_authmethod = p->authmethods;
- }
- ast_mutex_lock(&iaxsl[callno]);
- if (!iaxs[callno])
- goto return_unref;
- memset(&ied, 0, sizeof(ied));
- /* The selection of which delayed reject is sent may leak information,
- * if it sets a static response. For example, if a host is known to only
- * use MD5 authentication, then an RSA response would indicate that the
- * peer does not exist, and vice-versa.
- * Therefore, we use whatever the last peer used (which may vary over the
- * course of a server, which should leak minimal information). */
- sentauthmethod = p ? p->authmethods : last_authmethod ? last_authmethod : (IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT);
- if (!p) {
- iaxs[callno]->authmethods = sentauthmethod;
- }
- iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, sentauthmethod);
- if (sentauthmethod & (IAX_AUTH_RSA | IAX_AUTH_MD5)) {
- /* Build the challenge */
- snprintf(challenge, sizeof(challenge), "%d", (int)ast_random());
- ast_string_field_set(iaxs[callno], challenge, challenge);
- iax_ie_append_str(&ied, IAX_IE_CHALLENGE, iaxs[callno]->challenge);
- }
- iax_ie_append_str(&ied, IAX_IE_USERNAME, peer_name);
- return_unref:
- if (p) {
- peer_unref(p);
- }
- return iaxs[callno] ? send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1) : -1;
- }
- static int registry_rerequest(struct iax_ies *ies, int callno, struct sockaddr_in *sin)
- {
- struct iax2_registry *reg;
- /* Start pessimistic */
- struct iax_ie_data ied;
- char peer[256] = "";
- char challenge[256] = "";
- int res;
- int authmethods = 0;
- if (ies->authmethods)
- authmethods = ies->authmethods;
- if (ies->username)
- ast_copy_string(peer, ies->username, sizeof(peer));
- if (ies->challenge)
- ast_copy_string(challenge, ies->challenge, sizeof(challenge));
- memset(&ied, 0, sizeof(ied));
- reg = iaxs[callno]->reg;
- if (reg) {
- if (inaddrcmp(®->addr, sin)) {
- ast_log(LOG_WARNING, "Received unsolicited registry authenticate request from '%s'\n", ast_inet_ntoa(sin->sin_addr));
- return -1;
- }
- if (ast_strlen_zero(reg->secret)) {
- ast_log(LOG_NOTICE, "No secret associated with peer '%s'\n", reg->username);
- reg->regstate = REG_STATE_NOAUTH;
- return -1;
- }
- iax_ie_append_str(&ied, IAX_IE_USERNAME, reg->username);
- iax_ie_append_short(&ied, IAX_IE_REFRESH, reg->refresh);
- if (reg->secret[0] == '[') {
- char tmpkey[256];
- ast_copy_string(tmpkey, reg->secret + 1, sizeof(tmpkey));
- tmpkey[strlen(tmpkey) - 1] = '\0';
- res = authenticate(challenge, NULL, tmpkey, authmethods, &ied, sin, NULL);
- } else
- res = authenticate(challenge, reg->secret, NULL, authmethods, &ied, sin, NULL);
- if (!res) {
- reg->regstate = REG_STATE_AUTHSENT;
- add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
- return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1);
- } else
- return -1;
- ast_log(LOG_WARNING, "Registry acknowledge on unknown registery '%s'\n", peer);
- } else
- ast_log(LOG_NOTICE, "Can't reregister without a reg\n");
- return -1;
- }
- static void stop_stuff(int callno)
- {
- iax2_destroy_helper(iaxs[callno]);
- }
- static void __auth_reject(const void *nothing)
- {
- /* Called from IAX thread only, without iaxs lock */
- int callno = (int)(long)(nothing);
- struct iax_ie_data ied;
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno]) {
- memset(&ied, 0, sizeof(ied));
- if (iaxs[callno]->authfail == IAX_COMMAND_REGREJ) {
- iax_ie_append_str(&ied, IAX_IE_CAUSE, "Registration Refused");
- iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, AST_CAUSE_FACILITY_REJECTED);
- } else if (iaxs[callno]->authfail == IAX_COMMAND_REJECT) {
- iax_ie_append_str(&ied, IAX_IE_CAUSE, "No authority found");
- iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, AST_CAUSE_FACILITY_NOT_SUBSCRIBED);
- }
- send_command_final(iaxs[callno], AST_FRAME_IAX, iaxs[callno]->authfail, 0, ied.buf, ied.pos, -1);
- }
- ast_mutex_unlock(&iaxsl[callno]);
- }
- static int auth_reject(const void *data)
- {
- int callno = (int)(long)(data);
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno])
- iaxs[callno]->authid = -1;
- ast_mutex_unlock(&iaxsl[callno]);
- #ifdef SCHED_MULTITHREADED
- if (schedule_action(__auth_reject, data))
- #endif
- __auth_reject(data);
- return 0;
- }
- static int auth_fail(int callno, int failcode)
- {
- /* Schedule sending the authentication failure in one second, to prevent
- guessing */
- if (iaxs[callno]) {
- iaxs[callno]->authfail = failcode;
- if (delayreject) {
- iaxs[callno]->authid = iax2_sched_replace(iaxs[callno]->authid,
- sched, 1000, auth_reject, (void *)(long)callno);
- } else
- auth_reject((void *)(long)callno);
- }
- return 0;
- }
- static void __auto_hangup(const void *nothing)
- {
- /* Called from IAX thread only, without iaxs lock */
- int callno = (int)(long)(nothing);
- struct iax_ie_data ied;
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno]) {
- memset(&ied, 0, sizeof(ied));
- iax_ie_append_str(&ied, IAX_IE_CAUSE, "Timeout");
- iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, AST_CAUSE_NO_USER_RESPONSE);
- send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_HANGUP, 0, ied.buf, ied.pos, -1);
- }
- ast_mutex_unlock(&iaxsl[callno]);
- }
- static int auto_hangup(const void *data)
- {
- int callno = (int)(long)(data);
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno]) {
- iaxs[callno]->autoid = -1;
- }
- ast_mutex_unlock(&iaxsl[callno]);
- #ifdef SCHED_MULTITHREADED
- if (schedule_action(__auto_hangup, data))
- #endif
- __auto_hangup(data);
- return 0;
- }
- static void iax2_dprequest(struct iax2_dpcache *dp, int callno)
- {
- struct iax_ie_data ied;
- /* Auto-hangup with 30 seconds of inactivity */
- iaxs[callno]->autoid = iax2_sched_replace(iaxs[callno]->autoid,
- sched, 30000, auto_hangup, (void *)(long)callno);
- memset(&ied, 0, sizeof(ied));
- iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, dp->exten);
- send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_DPREQ, 0, ied.buf, ied.pos, -1);
- dp->flags |= CACHE_FLAG_TRANSMITTED;
- }
- static int iax2_vnak(int callno)
- {
- return send_command_immediate(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_VNAK, 0, NULL, 0, iaxs[callno]->iseqno);
- }
- static void vnak_retransmit(int callno, int last)
- {
- struct iax_frame *f;
- AST_LIST_LOCK(&frame_queue);
- AST_LIST_TRAVERSE(&frame_queue, f, list) {
- /* Send a copy immediately */
- if ((f->callno == callno) && iaxs[f->callno] &&
- ((unsigned char ) (f->oseqno - last) < 128) &&
- (f->retries >= 0)) {
- send_packet(f);
- }
- }
- AST_LIST_UNLOCK(&frame_queue);
- }
- static void __iax2_poke_peer_s(const void *data)
- {
- struct iax2_peer *peer = (struct iax2_peer *)data;
- iax2_poke_peer(peer, 0);
- peer_unref(peer);
- }
- static int iax2_poke_peer_s(const void *data)
- {
- struct iax2_peer *peer = (struct iax2_peer *)data;
- peer->pokeexpire = -1;
- #ifdef SCHED_MULTITHREADED
- if (schedule_action(__iax2_poke_peer_s, data))
- #endif
- __iax2_poke_peer_s(data);
- return 0;
- }
- static int send_trunk(struct iax2_trunk_peer *tpeer, struct timeval *now)
- {
- int res = 0;
- struct iax_frame *fr;
- struct ast_iax2_meta_hdr *meta;
- struct ast_iax2_meta_trunk_hdr *mth;
- int calls = 0;
-
- /* Point to frame */
- fr = (struct iax_frame *)tpeer->trunkdata;
- /* Point to meta data */
- meta = (struct ast_iax2_meta_hdr *)fr->afdata;
- mth = (struct ast_iax2_meta_trunk_hdr *)meta->data;
- if (tpeer->trunkdatalen) {
- /* We're actually sending a frame, so fill the meta trunk header and meta header */
- meta->zeros = 0;
- meta->metacmd = IAX_META_TRUNK;
- if (ast_test_flag(&globalflags, IAX_TRUNKTIMESTAMPS))
- meta->cmddata = IAX_META_TRUNK_MINI;
- else
- meta->cmddata = IAX_META_TRUNK_SUPERMINI;
- mth->ts = htonl(calc_txpeerstamp(tpeer, trunkfreq, now));
- /* And the rest of the ast_iax2 header */
- fr->direction = DIRECTION_OUTGRESS;
- fr->retrans = -1;
- fr->transfer = 0;
- /* Any appropriate call will do */
- fr->data = fr->afdata;
- fr->datalen = tpeer->trunkdatalen + sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr);
- res = transmit_trunk(fr, &tpeer->addr, tpeer->sockfd);
- calls = tpeer->calls;
- #if 0
- ast_debug(1, "Trunking %d call chunks in %d bytes to %s:%d, ts=%d\n", calls, fr->datalen, ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port), ntohl(mth->ts));
- #endif
- /* Reset transmit trunk side data */
- tpeer->trunkdatalen = 0;
- tpeer->calls = 0;
- }
- if (res < 0)
- return res;
- return calls;
- }
- static inline int iax2_trunk_expired(struct iax2_trunk_peer *tpeer, struct timeval *now)
- {
- /* Drop when trunk is about 5 seconds idle */
- if (now->tv_sec > tpeer->trunkact.tv_sec + 5)
- return 1;
- return 0;
- }
- static int timing_read(int *id, int fd, short events, void *cbdata)
- {
- char buf[1024];
- int res, processed = 0, totalcalls = 0;
- struct iax2_trunk_peer *tpeer = NULL, *drop = NULL;
- #ifdef DAHDI_TIMERACK
- int x = 1;
- #endif
- struct timeval now = ast_tvnow();
- if (iaxtrunkdebug)
- ast_verbose("Beginning trunk processing. Trunk queue ceiling is %d bytes per host\n", trunkmaxsize);
- if (events & AST_IO_PRI) {
- #ifdef DAHDI_TIMERACK
- /* Great, this is a timing interface, just call the ioctl */
- if (ioctl(fd, DAHDI_TIMERACK, &x)) {
- ast_log(LOG_WARNING, "Unable to acknowledge DAHDI timer. IAX trunking will fail!\n");
- usleep(1);
- return -1;
- }
- #endif
- } else {
- /* Read and ignore from the pseudo channel for timing */
- res = read(fd, buf, sizeof(buf));
- if (res < 1) {
- ast_log(LOG_WARNING, "Unable to read from timing fd\n");
- return 1;
- }
- }
- /* For each peer that supports trunking... */
- AST_LIST_LOCK(&tpeers);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&tpeers, tpeer, list) {
- processed++;
- res = 0;
- ast_mutex_lock(&tpeer->lock);
- /* We can drop a single tpeer per pass. That makes all this logic
- substantially easier */
- if (!drop && iax2_trunk_expired(tpeer, &now)) {
- /* Take it out of the list, but don't free it yet, because it
- could be in use */
- AST_LIST_REMOVE_CURRENT(list);
- drop = tpeer;
- } else {
- res = send_trunk(tpeer, &now);
- trunk_timed++;
- if (iaxtrunkdebug)
- ast_verbose(" - Trunk peer (%s:%d) has %d call chunk%s in transit, %d bytes backloged and has hit a high water mark of %d bytes\n", ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port), res, (res != 1) ? "s" : "", tpeer->trunkdatalen, tpeer->trunkdataalloc);
- }
- totalcalls += res;
- res = 0;
- ast_mutex_unlock(&tpeer->lock);
- }
- AST_LIST_TRAVERSE_SAFE_END;
- AST_LIST_UNLOCK(&tpeers);
- if (drop) {
- ast_mutex_lock(&drop->lock);
- /* Once we have this lock, we're sure nobody else is using it or could use it once we release it,
- because by the time they could get tpeerlock, we've already grabbed it */
- ast_debug(1, "Dropping unused iax2 trunk peer '%s:%d'\n", ast_inet_ntoa(drop->addr.sin_addr), ntohs(drop->addr.sin_port));
- if (drop->trunkdata) {
- ast_free(drop->trunkdata);
- drop->trunkdata = NULL;
- }
- ast_mutex_unlock(&drop->lock);
- ast_mutex_destroy(&drop->lock);
- ast_free(drop);
-
- }
- if (iaxtrunkdebug)
- ast_verbose("Ending trunk processing with %d peers and %d call chunks processed\n", processed, totalcalls);
- iaxtrunkdebug = 0;
- return 1;
- }
- struct dpreq_data {
- int callno;
- char context[AST_MAX_EXTENSION];
- char callednum[AST_MAX_EXTENSION];
- char *callerid;
- };
- static void dp_lookup(int callno, const char *context, const char *callednum, const char *callerid, int skiplock)
- {
- unsigned short dpstatus = 0;
- struct iax_ie_data ied1;
- int mm;
- memset(&ied1, 0, sizeof(ied1));
- mm = ast_matchmore_extension(NULL, context, callednum, 1, callerid);
- /* Must be started */
- if (!strcmp(callednum, ast_parking_ext()) || ast_exists_extension(NULL, context, callednum, 1, callerid)) {
- dpstatus = IAX_DPSTATUS_EXISTS;
- } else if (ast_canmatch_extension(NULL, context, callednum, 1, callerid)) {
- dpstatus = IAX_DPSTATUS_CANEXIST;
- } else {
- dpstatus = IAX_DPSTATUS_NONEXISTENT;
- }
- if (ast_ignore_pattern(context, callednum))
- dpstatus |= IAX_DPSTATUS_IGNOREPAT;
- if (mm)
- dpstatus |= IAX_DPSTATUS_MATCHMORE;
- if (!skiplock)
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno]) {
- iax_ie_append_str(&ied1, IAX_IE_CALLED_NUMBER, callednum);
- iax_ie_append_short(&ied1, IAX_IE_DPSTATUS, dpstatus);
- iax_ie_append_short(&ied1, IAX_IE_REFRESH, iaxdefaultdpcache);
- send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_DPREP, 0, ied1.buf, ied1.pos, -1);
- }
- if (!skiplock)
- ast_mutex_unlock(&iaxsl[callno]);
- }
- static void *dp_lookup_thread(void *data)
- {
- /* Look up for dpreq */
- struct dpreq_data *dpr = data;
- dp_lookup(dpr->callno, dpr->context, dpr->callednum, dpr->callerid, 0);
- if (dpr->callerid)
- ast_free(dpr->callerid);
- ast_free(dpr);
- return NULL;
- }
- static void spawn_dp_lookup(int callno, const char *context, const char *callednum, const char *callerid)
- {
- pthread_t newthread;
- struct dpreq_data *dpr;
-
- if (!(dpr = ast_calloc(1, sizeof(*dpr))))
- return;
- dpr->callno = callno;
- ast_copy_string(dpr->context, context, sizeof(dpr->context));
- ast_copy_string(dpr->callednum, callednum, sizeof(dpr->callednum));
- if (callerid)
- dpr->callerid = ast_strdup(callerid);
- if (ast_pthread_create_detached(&newthread, NULL, dp_lookup_thread, dpr)) {
- ast_log(LOG_WARNING, "Unable to start lookup thread!\n");
- }
- }
- struct iax_dual {
- struct ast_channel *chan1;
- struct ast_channel *chan2;
- };
- static void *iax_park_thread(void *stuff)
- {
- struct ast_channel *chan1, *chan2;
- struct iax_dual *d;
- struct ast_frame *f;
- int ext;
- int res;
- d = stuff;
- chan1 = d->chan1;
- chan2 = d->chan2;
- ast_free(d);
- f = ast_read(chan1);
- if (f)
- ast_frfree(f);
- res = ast_park_call(chan1, chan2, 0, &ext);
- ast_hangup(chan2);
- ast_log(LOG_NOTICE, "Parked on extension '%d'\n", ext);
- return NULL;
- }
- static int iax_park(struct ast_channel *chan1, struct ast_channel *chan2)
- {
- struct iax_dual *d;
- struct ast_channel *chan1m, *chan2m;
- pthread_t th;
- chan1m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan1->exten, chan1->context, chan1->amaflags, "Parking/%s", chan1->name);
- chan2m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->amaflags, "IAXPeer/%s",chan2->name);
- if (chan2m && chan1m) {
- /* Make formats okay */
- chan1m->readformat = chan1->readformat;
- chan1m->writeformat = chan1->writeformat;
- ast_channel_masquerade(chan1m, chan1);
- /* Setup the extensions and such */
- ast_copy_string(chan1m->context, chan1->context, sizeof(chan1m->context));
- ast_copy_string(chan1m->exten, chan1->exten, sizeof(chan1m->exten));
- chan1m->priority = chan1->priority;
-
- /* We make a clone of the peer channel too, so we can play
- back the announcement */
- /* Make formats okay */
- chan2m->readformat = chan2->readformat;
- chan2m->writeformat = chan2->writeformat;
- ast_channel_masquerade(chan2m, chan2);
- /* Setup the extensions and such */
- ast_copy_string(chan2m->context, chan2->context, sizeof(chan2m->context));
- ast_copy_string(chan2m->exten, chan2->exten, sizeof(chan2m->exten));
- chan2m->priority = chan2->priority;
- if (ast_do_masquerade(chan2m)) {
- ast_log(LOG_WARNING, "Masquerade failed :(\n");
- ast_hangup(chan2m);
- return -1;
- }
- } else {
- if (chan1m)
- ast_hangup(chan1m);
- if (chan2m)
- ast_hangup(chan2m);
- return -1;
- }
- if ((d = ast_calloc(1, sizeof(*d)))) {
- d->chan1 = chan1m;
- d->chan2 = chan2m;
- if (!ast_pthread_create_detached_background(&th, NULL, iax_park_thread, d)) {
- return 0;
- }
- ast_free(d);
- }
- return -1;
- }
- static int iax2_provision(struct sockaddr_in *end, int sockfd, char *dest, const char *template, int force);
- static int check_provisioning(struct sockaddr_in *sin, int sockfd, char *si, unsigned int ver)
- {
- unsigned int ourver;
- char rsi[80];
- snprintf(rsi, sizeof(rsi), "si-%s", si);
- if (iax_provision_version(&ourver, rsi, 1))
- return 0;
- ast_debug(1, "Service identifier '%s', we think '%08x', they think '%08x'\n", si, ourver, ver);
- if (ourver != ver)
- iax2_provision(sin, sockfd, NULL, rsi, 1);
- return 0;
- }
- static void construct_rr(struct chan_iax2_pvt *pvt, struct iax_ie_data *iep)
- {
- jb_info stats;
- jb_getinfo(pvt->jb, &stats);
-
- memset(iep, 0, sizeof(*iep));
- iax_ie_append_int(iep,IAX_IE_RR_JITTER, stats.jitter);
- if(stats.frames_in == 0) stats.frames_in = 1;
- iax_ie_append_int(iep,IAX_IE_RR_LOSS, ((0xff & (stats.losspct/1000)) << 24 | (stats.frames_lost & 0x00ffffff)));
- iax_ie_append_int(iep,IAX_IE_RR_PKTS, stats.frames_in);
- iax_ie_append_short(iep,IAX_IE_RR_DELAY, stats.current - stats.min);
- iax_ie_append_int(iep,IAX_IE_RR_DROPPED, stats.frames_dropped);
- iax_ie_append_int(iep,IAX_IE_RR_OOO, stats.frames_ooo);
- }
- static void save_rr(struct iax_frame *fr, struct iax_ies *ies)
- {
- iaxs[fr->callno]->remote_rr.jitter = ies->rr_jitter;
- iaxs[fr->callno]->remote_rr.losspct = ies->rr_loss >> 24;
- iaxs[fr->callno]->remote_rr.losscnt = ies->rr_loss & 0xffffff;
- iaxs[fr->callno]->remote_rr.packets = ies->rr_pkts;
- iaxs[fr->callno]->remote_rr.delay = ies->rr_delay;
- iaxs[fr->callno]->remote_rr.dropped = ies->rr_dropped;
- iaxs[fr->callno]->remote_rr.ooo = ies->rr_ooo;
- }
- static void save_osptoken(struct iax_frame *fr, struct iax_ies *ies)
- {
- int i;
- unsigned int length, offset = 0;
- char full_osptoken[IAX_MAX_OSPBUFF_SIZE];
- for (i = 0; i < IAX_MAX_OSPBLOCK_NUM; i++) {
- length = ies->ospblocklength[i];
- if (length != 0) {
- if (length > IAX_MAX_OSPBLOCK_SIZE) {
- /* OSP token block length wrong, clear buffer */
- offset = 0;
- break;
- } else {
- memcpy(full_osptoken + offset, ies->osptokenblock[i], length);
- offset += length;
- }
- } else {
- break;
- }
- }
- *(full_osptoken + offset) = '\0';
- if (strlen(full_osptoken) != offset) {
- /* OSP token length wrong, clear buffer */
- *full_osptoken = '\0';
- }
- ast_string_field_set(iaxs[fr->callno], osptoken, full_osptoken);
- }
- static void log_jitterstats(unsigned short callno)
- {
- int localjitter = -1, localdelay = 0, locallost = -1, locallosspct = -1, localdropped = 0, localooo = -1, localpackets = -1;
- jb_info jbinfo;
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno] && iaxs[callno]->owner && iaxs[callno]->owner->name) {
- if(ast_test_flag(iaxs[callno], IAX_USEJITTERBUF)) {
- jb_getinfo(iaxs[callno]->jb, &jbinfo);
- localjitter = jbinfo.jitter;
- localdelay = jbinfo.current - jbinfo.min;
- locallost = jbinfo.frames_lost;
- locallosspct = jbinfo.losspct/1000;
- localdropped = jbinfo.frames_dropped;
- localooo = jbinfo.frames_ooo;
- localpackets = jbinfo.frames_in;
- }
- ast_debug(3, "JB STATS:%s ping=%d ljitterms=%d ljbdelayms=%d ltotlost=%d lrecentlosspct=%d ldropped=%d looo=%d lrecvd=%d rjitterms=%d rjbdelayms=%d rtotlost=%d rrecentlosspct=%d rdropped=%d rooo=%d rrecvd=%d\n",
- iaxs[callno]->owner->name,
- iaxs[callno]->pingtime,
- localjitter,
- localdelay,
- locallost,
- locallosspct,
- localdropped,
- localooo,
- localpackets,
- iaxs[callno]->remote_rr.jitter,
- iaxs[callno]->remote_rr.delay,
- iaxs[callno]->remote_rr.losscnt,
- iaxs[callno]->remote_rr.losspct/1000,
- iaxs[callno]->remote_rr.dropped,
- iaxs[callno]->remote_rr.ooo,
- iaxs[callno]->remote_rr.packets);
- manager_event(EVENT_FLAG_REPORTING, "JitterBufStats", "Owner: %s\r\nPing: %d\r\nLocalJitter: %d\r\nLocalJBDelay: %d\r\nLocalTotalLost: %d\r\nLocalLossPercent: %d\r\nLocalDropped: %d\r\nLocalooo: %d\r\nLocalReceived: %d\r\nRemoteJitter: %d\r\nRemoteJBDelay: %d\r\nRemoteTotalLost: %d\r\nRemoteLossPercent: %d\r\nRemoteDropped: %d\r\nRemoteooo: %d\r\nRemoteReceived: %d\r\n",
- iaxs[callno]->owner->name,
- iaxs[callno]->pingtime,
- localjitter,
- localdelay,
- locallost,
- locallosspct,
- localdropped,
- localooo,
- localpackets,
- iaxs[callno]->remote_rr.jitter,
- iaxs[callno]->remote_rr.delay,
- iaxs[callno]->remote_rr.losscnt,
- iaxs[callno]->remote_rr.losspct/1000,
- iaxs[callno]->remote_rr.dropped,
- iaxs[callno]->remote_rr.ooo,
- iaxs[callno]->remote_rr.packets);
- }
- ast_mutex_unlock(&iaxsl[callno]);
- }
- static int socket_process(struct iax2_thread *thread);
- /*!
- * \brief Handle any deferred full frames for this thread
- */
- static void handle_deferred_full_frames(struct iax2_thread *thread)
- {
- struct iax2_pkt_buf *pkt_buf;
- ast_mutex_lock(&thread->lock);
- while ((pkt_buf = AST_LIST_REMOVE_HEAD(&thread->full_frames, entry))) {
- ast_mutex_unlock(&thread->lock);
- thread->buf = pkt_buf->buf;
- thread->buf_len = pkt_buf->len;
- thread->buf_size = pkt_buf->len + 1;
-
- socket_process(thread);
- thread->buf = NULL;
- ast_free(pkt_buf);
- ast_mutex_lock(&thread->lock);
- }
- ast_mutex_unlock(&thread->lock);
- }
- /*!
- * \brief Queue the last read full frame for processing by a certain thread
- *
- * If there are already any full frames queued, they are sorted
- * by sequence number.
- */
- static void defer_full_frame(struct iax2_thread *from_here, struct iax2_thread *to_here)
- {
- struct iax2_pkt_buf *pkt_buf, *cur_pkt_buf;
- struct ast_iax2_full_hdr *fh, *cur_fh;
- if (!(pkt_buf = ast_calloc(1, sizeof(*pkt_buf) + from_here->buf_len)))
- return;
- pkt_buf->len = from_here->buf_len;
- memcpy(pkt_buf->buf, from_here->buf, pkt_buf->len);
- fh = (struct ast_iax2_full_hdr *) pkt_buf->buf;
- ast_mutex_lock(&to_here->lock);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&to_here->full_frames, cur_pkt_buf, entry) {
- cur_fh = (struct ast_iax2_full_hdr *) cur_pkt_buf->buf;
- if (fh->oseqno < cur_fh->oseqno) {
- AST_LIST_INSERT_BEFORE_CURRENT(pkt_buf, entry);
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END
- if (!cur_pkt_buf)
- AST_LIST_INSERT_TAIL(&to_here->full_frames, pkt_buf, entry);
-
- ast_mutex_unlock(&to_here->lock);
- }
- static int socket_read(int *id, int fd, short events, void *cbdata)
- {
- struct iax2_thread *thread;
- socklen_t len;
- time_t t;
- static time_t last_errtime = 0;
- struct ast_iax2_full_hdr *fh;
- if (!(thread = find_idle_thread())) {
- time(&t);
- if (t != last_errtime)
- ast_debug(1, "Out of idle IAX2 threads for I/O, pausing!\n");
- last_errtime = t;
- usleep(1);
- return 1;
- }
- len = sizeof(thread->iosin);
- thread->iofd = fd;
- thread->buf_len = recvfrom(fd, thread->readbuf, sizeof(thread->readbuf), 0, (struct sockaddr *) &thread->iosin, &len);
- thread->buf_size = sizeof(thread->readbuf);
- thread->buf = thread->readbuf;
- if (thread->buf_len < 0) {
- if (errno != ECONNREFUSED && errno != EAGAIN)
- ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
- handle_error();
- thread->iostate = IAX_IOSTATE_IDLE;
- signal_condition(&thread->lock, &thread->cond);
- return 1;
- }
- if (test_losspct && ((100.0 * ast_random() / (RAND_MAX + 1.0)) < test_losspct)) { /* simulate random loss condition */
- thread->iostate = IAX_IOSTATE_IDLE;
- signal_condition(&thread->lock, &thread->cond);
- return 1;
- }
-
- /* Determine if this frame is a full frame; if so, and any thread is currently
- processing a full frame for the same callno from this peer, then drop this
- frame (and the peer will retransmit it) */
- fh = (struct ast_iax2_full_hdr *) thread->buf;
- if (ntohs(fh->scallno) & IAX_FLAG_FULL) {
- struct iax2_thread *cur = NULL;
- uint16_t callno = ntohs(fh->scallno) & ~IAX_FLAG_FULL;
-
- AST_LIST_LOCK(&active_list);
- AST_LIST_TRAVERSE(&active_list, cur, list) {
- if ((cur->ffinfo.callno == callno) &&
- !inaddrcmp(&cur->ffinfo.sin, &thread->iosin))
- break;
- }
- if (cur) {
- /* we found another thread processing a full frame for this call,
- so queue it up for processing later. */
- defer_full_frame(thread, cur);
- AST_LIST_UNLOCK(&active_list);
- thread->iostate = IAX_IOSTATE_IDLE;
- signal_condition(&thread->lock, &thread->cond);
- return 1;
- } else {
- /* this thread is going to process this frame, so mark it */
- thread->ffinfo.callno = callno;
- memcpy(&thread->ffinfo.sin, &thread->iosin, sizeof(thread->ffinfo.sin));
- thread->ffinfo.type = fh->type;
- thread->ffinfo.csub = fh->csub;
- }
- AST_LIST_UNLOCK(&active_list);
- }
-
- /* Mark as ready and send on its way */
- thread->iostate = IAX_IOSTATE_READY;
- #ifdef DEBUG_SCHED_MULTITHREAD
- ast_copy_string(thread->curfunc, "socket_process", sizeof(thread->curfunc));
- #endif
- signal_condition(&thread->lock, &thread->cond);
- return 1;
- }
- static int socket_process_meta(int packet_len, struct ast_iax2_meta_hdr *meta, struct sockaddr_in *sin, int sockfd,
- struct iax_frame *fr)
- {
- unsigned char metatype;
- struct ast_iax2_meta_trunk_mini *mtm;
- struct ast_iax2_meta_trunk_hdr *mth;
- struct ast_iax2_meta_trunk_entry *mte;
- struct iax2_trunk_peer *tpeer;
- unsigned int ts;
- void *ptr;
- struct timeval rxtrunktime;
- struct ast_frame f = { 0, };
- if (packet_len < sizeof(*meta)) {
- ast_log(LOG_WARNING, "Rejecting packet from '%s.%d' that is flagged as a meta frame but is too short\n",
- ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
- return 1;
- }
- if (meta->metacmd != IAX_META_TRUNK)
- return 1;
- if (packet_len < (sizeof(*meta) + sizeof(*mth))) {
- ast_log(LOG_WARNING, "midget meta trunk packet received (%d of %d min)\n", packet_len,
- (int) (sizeof(*meta) + sizeof(*mth)));
- return 1;
- }
- mth = (struct ast_iax2_meta_trunk_hdr *)(meta->data);
- ts = ntohl(mth->ts);
- metatype = meta->cmddata;
- packet_len -= (sizeof(*meta) + sizeof(*mth));
- ptr = mth->data;
- tpeer = find_tpeer(sin, sockfd);
- if (!tpeer) {
- ast_log(LOG_WARNING, "Unable to accept trunked packet from '%s:%d': No matching peer\n",
- ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
- return 1;
- }
- tpeer->trunkact = ast_tvnow();
- if (!ts || ast_tvzero(tpeer->rxtrunktime))
- tpeer->rxtrunktime = tpeer->trunkact;
- rxtrunktime = tpeer->rxtrunktime;
- ast_mutex_unlock(&tpeer->lock);
- while (packet_len >= sizeof(*mte)) {
- /* Process channels */
- unsigned short callno, trunked_ts, len;
- if (metatype == IAX_META_TRUNK_MINI) {
- mtm = (struct ast_iax2_meta_trunk_mini *) ptr;
- ptr += sizeof(*mtm);
- packet_len -= sizeof(*mtm);
- len = ntohs(mtm->len);
- callno = ntohs(mtm->mini.callno);
- trunked_ts = ntohs(mtm->mini.ts);
- } else if (metatype == IAX_META_TRUNK_SUPERMINI) {
- mte = (struct ast_iax2_meta_trunk_entry *)ptr;
- ptr += sizeof(*mte);
- packet_len -= sizeof(*mte);
- len = ntohs(mte->len);
- callno = ntohs(mte->callno);
- trunked_ts = 0;
- } else {
- ast_log(LOG_WARNING, "Unknown meta trunk cmd from '%s:%d': dropping\n", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
- break;
- }
- /* Stop if we don't have enough data */
- if (len > packet_len)
- break;
- fr->callno = find_callno_locked(callno & ~IAX_FLAG_FULL, 0, sin, NEW_PREVENT, sockfd, 0);
- if (!fr->callno)
- continue;
- /* If it's a valid call, deliver the contents. If not, we
- drop it, since we don't have a scallno to use for an INVAL */
- /* Process as a mini frame */
- memset(&f, 0, sizeof(f));
- f.frametype = AST_FRAME_VOICE;
- if (!iaxs[fr->callno]) {
- /* drop it */
- } else if (iaxs[fr->callno]->voiceformat == 0) {
- ast_log(LOG_WARNING, "Received trunked frame before first full voice frame\n");
- iax2_vnak(fr->callno);
- } else {
- f.subclass = iaxs[fr->callno]->voiceformat;
- f.datalen = len;
- if (f.datalen >= 0) {
- if (f.datalen)
- f.data = ptr;
- else
- f.data = NULL;
- if (trunked_ts)
- fr->ts = (iaxs[fr->callno]->last & 0xFFFF0000L) | (trunked_ts & 0xffff);
- else
- fr->ts = fix_peerts(&rxtrunktime, fr->callno, ts);
- /* Don't pass any packets until we're started */
- if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED)) {
- struct iax_frame *duped_fr;
- /* Common things */
- f.src = "IAX2";
- f.mallocd = 0;
- f.offset = 0;
- if (f.datalen && (f.frametype == AST_FRAME_VOICE))
- f.samples = ast_codec_get_samples(&f);
- else
- f.samples = 0;
- fr->outoforder = 0;
- iax_frame_wrap(fr, &f);
- duped_fr = iaxfrdup2(fr);
- if (duped_fr)
- schedule_delivery(duped_fr, 1, 1, &fr->ts);
- if (iaxs[fr->callno] && iaxs[fr->callno]->last < fr->ts)
- iaxs[fr->callno]->last = fr->ts;
- }
- } else {
- ast_log(LOG_WARNING, "Datalen < 0?\n");
- }
- }
- ast_mutex_unlock(&iaxsl[fr->callno]);
- ptr += len;
- packet_len -= len;
- }
- return 1;
- }
- static int acf_iaxvar_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
- {
- struct ast_datastore *variablestore = ast_channel_datastore_find(chan, &iax2_variable_datastore_info, NULL);
- AST_LIST_HEAD(, ast_var_t) *varlist;
- struct ast_var_t *var;
- if (!variablestore) {
- *buf = '\0';
- return 0;
- }
- varlist = variablestore->data;
- AST_LIST_LOCK(varlist);
- AST_LIST_TRAVERSE(varlist, var, entries) {
- if (strcmp(var->name, data) == 0) {
- ast_copy_string(buf, var->value, len);
- break;
- }
- }
- AST_LIST_UNLOCK(varlist);
- return 0;
- }
- static int acf_iaxvar_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
- {
- struct ast_datastore *variablestore = ast_channel_datastore_find(chan, &iax2_variable_datastore_info, NULL);
- AST_LIST_HEAD(, ast_var_t) *varlist;
- struct ast_var_t *var;
- if (!variablestore) {
- variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
- if (!variablestore) {
- ast_log(LOG_ERROR, "Memory allocation error\n");
- return -1;
- }
- varlist = ast_calloc(1, sizeof(*varlist));
- if (!varlist) {
- ast_log(LOG_ERROR, "Unable to assign new variable '%s'\n", data);
- return -1;
- }
- AST_LIST_HEAD_INIT(varlist);
- variablestore->data = varlist;
- variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
- ast_channel_datastore_add(chan, variablestore);
- } else
- varlist = variablestore->data;
- AST_LIST_LOCK(varlist);
- AST_LIST_TRAVERSE_SAFE_BEGIN(varlist, var, entries) {
- if (strcmp(var->name, data) == 0) {
- AST_LIST_REMOVE_CURRENT(entries);
- ast_var_delete(var);
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
- var = ast_var_assign(data, value);
- if (var)
- AST_LIST_INSERT_TAIL(varlist, var, entries);
- else
- ast_log(LOG_ERROR, "Unable to assign new variable '%s'\n", data);
- AST_LIST_UNLOCK(varlist);
- return 0;
- }
- static struct ast_custom_function iaxvar_function = {
- .name = "IAXVAR",
- .synopsis = "Sets or retrieves a remote variable",
- .syntax = "IAXVAR(<varname>)",
- .read = acf_iaxvar_read,
- .write = acf_iaxvar_write,
- };
- static int socket_process(struct iax2_thread *thread)
- {
- struct sockaddr_in sin;
- int res;
- int updatehistory=1;
- int new = NEW_PREVENT;
- int dcallno = 0;
- char decrypted = 0;
- struct ast_iax2_full_hdr *fh = (struct ast_iax2_full_hdr *)thread->buf;
- struct ast_iax2_mini_hdr *mh = (struct ast_iax2_mini_hdr *)thread->buf;
- struct ast_iax2_meta_hdr *meta = (struct ast_iax2_meta_hdr *)thread->buf;
- struct ast_iax2_video_hdr *vh = (struct ast_iax2_video_hdr *)thread->buf;
- struct iax_frame *fr;
- struct iax_frame *cur;
- struct ast_frame f = { 0, };
- struct ast_channel *c = NULL;
- struct iax2_dpcache *dp;
- struct iax2_peer *peer;
- struct iax_ies ies;
- struct iax_ie_data ied0, ied1;
- int format;
- int fd;
- int exists;
- int minivid = 0;
- char empty[32]=""; /* Safety measure */
- struct iax_frame *duped_fr;
- char host_pref_buf[128];
- char caller_pref_buf[128];
- struct ast_codec_pref pref;
- char *using_prefs = "mine";
- /* allocate an iax_frame with 4096 bytes of data buffer */
- fr = alloca(sizeof(*fr) + 4096);
- memset(fr, 0, sizeof(*fr));
- fr->afdatalen = 4096; /* From alloca() above */
- /* Copy frequently used parameters to the stack */
- res = thread->buf_len;
- fd = thread->iofd;
- memcpy(&sin, &thread->iosin, sizeof(sin));
- if (res < sizeof(*mh)) {
- ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int) sizeof(*mh));
- return 1;
- }
- if ((vh->zeros == 0) && (ntohs(vh->callno) & 0x8000)) {
- if (res < sizeof(*vh)) {
- ast_log(LOG_WARNING, "Rejecting packet from '%s.%d' that is flagged as a video frame but is too short\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
- return 1;
- }
- /* This is a video frame, get call number */
- fr->callno = find_callno(ntohs(vh->callno) & ~0x8000, dcallno, &sin, new, fd, 0);
- minivid = 1;
- } else if ((meta->zeros == 0) && !(ntohs(meta->metacmd) & 0x8000))
- return socket_process_meta(res, meta, &sin, fd, fr);
- #ifdef DEBUG_SUPPORT
- if (iaxdebug && (res >= sizeof(*fh)))
- iax_showframe(NULL, fh, 1, &sin, res - sizeof(*fh));
- #endif
- if (ntohs(mh->callno) & IAX_FLAG_FULL) {
- if (res < sizeof(*fh)) {
- ast_log(LOG_WARNING, "Rejecting packet from '%s.%d' that is flagged as a full frame but is too short\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
- return 1;
- }
- /* Get the destination call number */
- dcallno = ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS;
- /* check to make sure this full frame isn't encrypted before we attempt
- * to look inside of it. If it is encrypted, decrypt it first. Its ok if the
- * callno is not found here, that just means one hasn't been allocated for
- * this connection yet. */
- if ((dcallno != 1) && (fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, NEW_PREVENT, fd, 1))) {
- ast_mutex_lock(&iaxsl[fr->callno]);
- if (iaxs[fr->callno] && ast_test_flag(iaxs[fr->callno], IAX_ENCRYPTED)) {
- if (decrypt_frame(fr->callno, fh, &f, &res)) {
- ast_log(LOG_NOTICE, "Packet Decrypt Failed!\n");
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- decrypted = 1;
- }
- ast_mutex_unlock(&iaxsl[fr->callno]);
- }
- /* Retrieve the type and subclass */
- f.frametype = fh->type;
- if (f.frametype == AST_FRAME_VIDEO) {
- f.subclass = uncompress_subclass(fh->csub & ~0x40) | ((fh->csub >> 6) & 0x1);
- } else {
- f.subclass = uncompress_subclass(fh->csub);
- }
- /* Deal with POKE/PONG without allocating a callno */
- if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_POKE) {
- /* Reply back with a PONG, but don't care about the result. */
- send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_PONG, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
- return 1;
- } else if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_ACK && dcallno == 1) {
- /* Ignore */
- return 1;
- }
- f.datalen = res - sizeof(*fh);
- if (f.datalen) {
- if (f.frametype == AST_FRAME_IAX) {
- if (iax_parse_ies(&ies, thread->buf + sizeof(*fh), f.datalen)) {
- ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(sin.sin_addr));
- return 1;
- }
- f.data = NULL;
- f.datalen = 0;
- } else {
- f.data = thread->buf + sizeof(*fh);
- memset(&ies, 0, sizeof(ies));
- }
- } else {
- if (f.frametype == AST_FRAME_IAX)
- f.data = NULL;
- else
- f.data = empty;
- memset(&ies, 0, sizeof(ies));
- }
- if (!dcallno && iax2_allow_new(f.frametype, f.subclass, 1)) {
- /* only set NEW_ALLOW if calltoken checks out */
- if (handle_call_token(fh, &ies, &sin, fd)) {
- return 1;
- }
- if (ies.calltoken && ies.calltokendata) {
- /* if we've gotten this far, and the calltoken ie data exists,
- * then calltoken validation _MUST_ have taken place. If calltoken
- * data is provided, it is always validated reguardless of any
- * calltokenoptional or requirecalltoken options */
- new = NEW_ALLOW_CALLTOKEN_VALIDATED;
- } else {
- new = NEW_ALLOW;
- }
- }
- } else {
- /* Don't know anything about it yet */
- f.frametype = AST_FRAME_NULL;
- f.subclass = 0;
- }
- if (!fr->callno) {
- int check_dcallno = 0;
- /*
- * We enforce accurate destination call numbers for ACKs. This forces the other
- * end to know the destination call number before call setup can complete.
- *
- * Discussed in the following thread:
- * http://lists.digium.com/pipermail/asterisk-dev/2008-May/033217.html
- */
- if ((ntohs(mh->callno) & IAX_FLAG_FULL) && ((f.frametype == AST_FRAME_IAX) && (f.subclass == IAX_COMMAND_ACK))) {
- check_dcallno = 1;
- }
- if (!(fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, fd, check_dcallno))) {
- if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_NEW) {
- send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_REJECT, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
- } else if (f.frametype == AST_FRAME_IAX && (f.subclass == IAX_COMMAND_REGREQ || f.subclass == IAX_COMMAND_REGREL)) {
- send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_REGREJ, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
- }
- return 1;
- }
- }
- if (fr->callno > 0)
- ast_mutex_lock(&iaxsl[fr->callno]);
- if (!fr->callno || !iaxs[fr->callno]) {
- /* A call arrived for a nonexistent destination. Unless it's an "inval"
- frame, reply with an inval */
- if (ntohs(mh->callno) & IAX_FLAG_FULL) {
- /* We can only raw hangup control frames */
- if (((f.subclass != IAX_COMMAND_INVAL) &&
- (f.subclass != IAX_COMMAND_TXCNT) &&
- (f.subclass != IAX_COMMAND_TXACC) &&
- (f.subclass != IAX_COMMAND_FWDOWNL))||
- (f.frametype != AST_FRAME_IAX))
- raw_hangup(&sin, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS, ntohs(mh->callno) & ~IAX_FLAG_FULL,
- fd);
- }
- if (fr->callno > 0)
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- if (ast_test_flag(iaxs[fr->callno], IAX_ENCRYPTED) && !decrypted) {
- if (decrypt_frame(fr->callno, fh, &f, &res)) {
- ast_log(LOG_NOTICE, "Packet Decrypt Failed!\n");
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- decrypted = 1;
- }
- #ifdef DEBUG_SUPPORT
- if (decrypted && iaxdebug)
- iax_showframe(NULL, fh, 3, &sin, res - sizeof(*fh));
- #endif
- /* count this frame */
- iaxs[fr->callno]->frames_received++;
- if (!inaddrcmp(&sin, &iaxs[fr->callno]->addr) && !minivid &&
- f.subclass != IAX_COMMAND_TXCNT && /* for attended transfer */
- f.subclass != IAX_COMMAND_TXACC) { /* for attended transfer */
- unsigned short new_peercallno;
-
- new_peercallno = (unsigned short) (ntohs(mh->callno) & ~IAX_FLAG_FULL);
- if (new_peercallno && new_peercallno != iaxs[fr->callno]->peercallno) {
- if (iaxs[fr->callno]->peercallno) {
- remove_by_peercallno(iaxs[fr->callno]);
- }
- iaxs[fr->callno]->peercallno = new_peercallno;
- store_by_peercallno(iaxs[fr->callno]);
- }
- }
- if (ntohs(mh->callno) & IAX_FLAG_FULL) {
- if (iaxdebug)
- ast_debug(1, "Received packet %d, (%d, %d)\n", fh->oseqno, f.frametype, f.subclass);
- /* Check if it's out of order (and not an ACK or INVAL) */
- fr->oseqno = fh->oseqno;
- fr->iseqno = fh->iseqno;
- fr->ts = ntohl(fh->ts);
- #ifdef IAXTESTS
- if (test_resync) {
- ast_debug(1, "Simulating frame ts resync, was %u now %u\n", fr->ts, fr->ts + test_resync);
- fr->ts += test_resync;
- }
- #endif /* IAXTESTS */
- #if 0
- if ( (ntohs(fh->dcallno) & IAX_FLAG_RETRANS) ||
- ( (f.frametype != AST_FRAME_VOICE) && ! (f.frametype == AST_FRAME_IAX &&
- (f.subclass == IAX_COMMAND_NEW ||
- f.subclass == IAX_COMMAND_AUTHREQ ||
- f.subclass == IAX_COMMAND_ACCEPT ||
- f.subclass == IAX_COMMAND_REJECT)) ) )
- #endif
- if ((ntohs(fh->dcallno) & IAX_FLAG_RETRANS) || (f.frametype != AST_FRAME_VOICE))
- updatehistory = 0;
- if ((iaxs[fr->callno]->iseqno != fr->oseqno) &&
- (iaxs[fr->callno]->iseqno ||
- ((f.subclass != IAX_COMMAND_TXCNT) &&
- (f.subclass != IAX_COMMAND_TXREADY) && /* for attended transfer */
- (f.subclass != IAX_COMMAND_TXREL) && /* for attended transfer */
- (f.subclass != IAX_COMMAND_UNQUELCH ) && /* for attended transfer */
- (f.subclass != IAX_COMMAND_TXACC)) ||
- (f.frametype != AST_FRAME_IAX))) {
- if (
- ((f.subclass != IAX_COMMAND_ACK) &&
- (f.subclass != IAX_COMMAND_INVAL) &&
- (f.subclass != IAX_COMMAND_TXCNT) &&
- (f.subclass != IAX_COMMAND_TXREADY) && /* for attended transfer */
- (f.subclass != IAX_COMMAND_TXREL) && /* for attended transfer */
- (f.subclass != IAX_COMMAND_UNQUELCH ) && /* for attended transfer */
- (f.subclass != IAX_COMMAND_TXACC) &&
- (f.subclass != IAX_COMMAND_VNAK)) ||
- (f.frametype != AST_FRAME_IAX)) {
- /* If it's not an ACK packet, it's out of order. */
- ast_debug(1, "Packet arrived out of order (expecting %d, got %d) (frametype = %d, subclass = %d)\n",
- iaxs[fr->callno]->iseqno, fr->oseqno, f.frametype, f.subclass);
- /* Check to see if we need to request retransmission,
- * and take sequence number wraparound into account */
- if ((unsigned char) (iaxs[fr->callno]->iseqno - fr->oseqno) < 128) {
- /* If we've already seen it, ack it XXX There's a border condition here XXX */
- if ((f.frametype != AST_FRAME_IAX) ||
- ((f.subclass != IAX_COMMAND_ACK) && (f.subclass != IAX_COMMAND_INVAL))) {
- ast_debug(1, "Acking anyway\n");
- /* XXX Maybe we should handle its ack to us, but then again, it's probably outdated anyway, and if
- we have anything to send, we'll retransmit and get an ACK back anyway XXX */
- send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
- }
- } else {
- /* Send a VNAK requesting retransmission */
- iax2_vnak(fr->callno);
- }
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- } else {
- /* Increment unless it's an ACK or VNAK */
- if (((f.subclass != IAX_COMMAND_ACK) &&
- (f.subclass != IAX_COMMAND_INVAL) &&
- (f.subclass != IAX_COMMAND_TXCNT) &&
- (f.subclass != IAX_COMMAND_TXACC) &&
- (f.subclass != IAX_COMMAND_VNAK)) ||
- (f.frametype != AST_FRAME_IAX))
- iaxs[fr->callno]->iseqno++;
- }
- /* Ensure text frames are NULL-terminated */
- if (f.frametype == AST_FRAME_TEXT && thread->buf[res - 1] != '\0') {
- if (res < thread->buf_size)
- thread->buf[res++] = '\0';
- else /* Trims one character from the text message, but that's better than overwriting the end of the buffer. */
- thread->buf[res - 1] = '\0';
- }
- /* Handle implicit ACKing unless this is an INVAL, and only if this is
- from the real peer, not the transfer peer */
- if (!inaddrcmp(&sin, &iaxs[fr->callno]->addr) &&
- ((f.subclass != IAX_COMMAND_INVAL) ||
- (f.frametype != AST_FRAME_IAX))) {
- unsigned char x;
- int call_to_destroy;
- /* XXX This code is not very efficient. Surely there is a better way which still
- properly handles boundary conditions? XXX */
- /* First we have to qualify that the ACKed value is within our window */
- for (x=iaxs[fr->callno]->rseqno; x != iaxs[fr->callno]->oseqno; x++)
- if (fr->iseqno == x)
- break;
- if ((x != iaxs[fr->callno]->oseqno) || (iaxs[fr->callno]->oseqno == fr->iseqno)) {
- /* The acknowledgement is within our window. Time to acknowledge everything
- that it says to */
- for (x=iaxs[fr->callno]->rseqno; x != fr->iseqno; x++) {
- /* Ack the packet with the given timestamp */
- if (iaxdebug)
- ast_debug(1, "Cancelling transmission of packet %d\n", x);
- call_to_destroy = 0;
- AST_LIST_LOCK(&frame_queue);
- AST_LIST_TRAVERSE(&frame_queue, cur, list) {
- /* If it's our call, and our timestamp, mark -1 retries */
- if ((fr->callno == cur->callno) && (x == cur->oseqno)) {
- cur->retries = -1;
- /* Destroy call if this is the end */
- if (cur->final)
- call_to_destroy = fr->callno;
- }
- }
- AST_LIST_UNLOCK(&frame_queue);
- if (call_to_destroy) {
- if (iaxdebug)
- ast_debug(1, "Really destroying %d, having been acked on final message\n", call_to_destroy);
- ast_mutex_lock(&iaxsl[call_to_destroy]);
- iax2_destroy(call_to_destroy);
- ast_mutex_unlock(&iaxsl[call_to_destroy]);
- }
- }
- /* Note how much we've received acknowledgement for */
- if (iaxs[fr->callno])
- iaxs[fr->callno]->rseqno = fr->iseqno;
- else {
- /* Stop processing now */
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- } else {
- ast_debug(1, "Received iseqno %d not within window %d->%d\n", fr->iseqno, iaxs[fr->callno]->rseqno, iaxs[fr->callno]->oseqno);
- }
- }
- if (inaddrcmp(&sin, &iaxs[fr->callno]->addr) &&
- ((f.frametype != AST_FRAME_IAX) ||
- ((f.subclass != IAX_COMMAND_TXACC) &&
- (f.subclass != IAX_COMMAND_TXCNT)))) {
- /* Only messages we accept from a transfer host are TXACC and TXCNT */
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- /* when we receive the first full frame for a new incoming channel,
- it is safe to start the PBX on the channel because we have now
- completed a 3-way handshake with the peer */
- if ((f.frametype == AST_FRAME_VOICE) ||
- (f.frametype == AST_FRAME_VIDEO) ||
- (f.frametype == AST_FRAME_IAX)) {
- if (ast_test_flag(iaxs[fr->callno], IAX_DELAYPBXSTART)) {
- ast_clear_flag(iaxs[fr->callno], IAX_DELAYPBXSTART);
- if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat)) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- }
- if (ies.vars) {
- struct ast_datastore *variablestore;
- struct ast_variable *var, *prev = NULL;
- AST_LIST_HEAD(, ast_var_t) *varlist;
- if ((c = iaxs[fr->callno]->owner)) {
- varlist = ast_calloc(1, sizeof(*varlist));
- variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
- if (variablestore && varlist) {
- variablestore->data = varlist;
- variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
- AST_LIST_HEAD_INIT(varlist);
- ast_debug(1, "I can haz IAX vars?\n");
- for (var = ies.vars; var; var = var->next) {
- struct ast_var_t *newvar = ast_var_assign(var->name, var->value);
- if (prev) {
- ast_free(prev);
- }
- prev = var;
- if (!newvar) {
- /* Don't abort list traversal, as this would leave ies.vars in an inconsistent state. */
- ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
- } else {
- AST_LIST_INSERT_TAIL(varlist, newvar, entries);
- }
- }
- if (prev) {
- ast_free(prev);
- }
- ies.vars = NULL;
- ast_channel_datastore_add(c, variablestore);
- } else {
- ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
- if (variablestore) {
- ast_channel_datastore_free(variablestore);
- }
- if (varlist) {
- ast_free(varlist);
- }
- }
- } else {
- /* No channel yet, so transfer the variables directly over to the pvt,
- * for later inheritance. */
- ast_debug(1, "No channel, so populating IAXVARs to the pvt, as an intermediate step.\n");
- for (var = ies.vars; var && var->next; var = var->next);
- if (var) {
- var->next = iaxs[fr->callno]->iaxvars;
- iaxs[fr->callno]->iaxvars = ies.vars;
- ies.vars = NULL;
- }
- }
- }
- }
- if (ies.vars) {
- ast_debug(1, "I have IAX variables, but they were not processed\n");
- }
- /* once we receive our first IAX Full Frame that is not CallToken related, send all
- * queued signaling frames that were being held. */
- if ((f.frametype == AST_FRAME_IAX) && (f.subclass != IAX_COMMAND_CALLTOKEN) && iaxs[fr->callno]->hold_signaling) {
- send_signaling(iaxs[fr->callno]);
- }
- if (f.frametype == AST_FRAME_VOICE) {
- if (f.subclass != iaxs[fr->callno]->voiceformat) {
- iaxs[fr->callno]->voiceformat = f.subclass;
- ast_debug(1, "Ooh, voice format changed to %d\n", f.subclass);
- if (iaxs[fr->callno]->owner) {
- int orignative;
- retryowner:
- if (ast_channel_trylock(iaxs[fr->callno]->owner)) {
- DEADLOCK_AVOIDANCE(&iaxsl[fr->callno]);
- if (iaxs[fr->callno] && iaxs[fr->callno]->owner) goto retryowner;
- }
- if (iaxs[fr->callno]) {
- if (iaxs[fr->callno]->owner) {
- orignative = iaxs[fr->callno]->owner->nativeformats;
- iaxs[fr->callno]->owner->nativeformats = f.subclass;
- if (iaxs[fr->callno]->owner->readformat)
- ast_set_read_format(iaxs[fr->callno]->owner, iaxs[fr->callno]->owner->readformat);
- iaxs[fr->callno]->owner->nativeformats = orignative;
- ast_channel_unlock(iaxs[fr->callno]->owner);
- }
- } else {
- ast_debug(1, "Neat, somebody took away the channel at a magical time but i found it!\n");
- /* Free remote variables (if any) */
- if (ies.vars) {
- ast_variables_destroy(ies.vars);
- ast_debug(1, "I can haz iaxvars, but they is no good. :-(\n");
- ies.vars = NULL;
- }
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- }
- }
- }
- if (f.frametype == AST_FRAME_VIDEO) {
- if (f.subclass != iaxs[fr->callno]->videoformat) {
- ast_debug(1, "Ooh, video format changed to %d\n", f.subclass & ~0x1);
- iaxs[fr->callno]->videoformat = f.subclass & ~0x1;
- }
- }
- if (f.frametype == AST_FRAME_CONTROL && iaxs[fr->callno]->owner) {
- if (f.subclass == AST_CONTROL_BUSY) {
- iaxs[fr->callno]->owner->hangupcause = AST_CAUSE_BUSY;
- } else if (f.subclass == AST_CONTROL_CONGESTION) {
- iaxs[fr->callno]->owner->hangupcause = AST_CAUSE_CONGESTION;
- }
- }
- if (f.frametype == AST_FRAME_IAX) {
- AST_SCHED_DEL(sched, iaxs[fr->callno]->initid);
- /* Handle the IAX pseudo frame itself */
- if (iaxdebug)
- ast_debug(1, "IAX subclass %d received\n", f.subclass);
- /* Update last ts unless the frame's timestamp originated with us. */
- if (iaxs[fr->callno]->last < fr->ts &&
- f.subclass != IAX_COMMAND_ACK &&
- f.subclass != IAX_COMMAND_PONG &&
- f.subclass != IAX_COMMAND_LAGRP) {
- iaxs[fr->callno]->last = fr->ts;
- if (iaxdebug)
- ast_debug(1, "For call=%d, set last=%d\n", fr->callno, fr->ts);
- }
- iaxs[fr->callno]->last_iax_message = f.subclass;
- if (!iaxs[fr->callno]->first_iax_message) {
- iaxs[fr->callno]->first_iax_message = f.subclass;
- }
- switch(f.subclass) {
- case IAX_COMMAND_ACK:
- /* Do nothing */
- break;
- case IAX_COMMAND_QUELCH:
- if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED)) {
- /* Generate Manager Hold event, if necessary*/
- if (iaxs[fr->callno]->owner) {
- manager_event(EVENT_FLAG_CALL, "Hold",
- "Status: On\r\n"
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n",
- iaxs[fr->callno]->owner->name,
- iaxs[fr->callno]->owner->uniqueid);
- }
- ast_set_flag(iaxs[fr->callno], IAX_QUELCH);
- if (ies.musiconhold) {
- if (iaxs[fr->callno]->owner && ast_bridged_channel(iaxs[fr->callno]->owner)) {
- const char *mohsuggest = iaxs[fr->callno]->mohsuggest;
- iax2_queue_control_data(fr->callno, AST_CONTROL_HOLD,
- S_OR(mohsuggest, NULL),
- !ast_strlen_zero(mohsuggest) ? strlen(mohsuggest) + 1 : 0);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- }
- }
- }
- break;
- case IAX_COMMAND_UNQUELCH:
- if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED)) {
- /* Generate Manager Unhold event, if necessary*/
- if (iaxs[fr->callno]->owner && ast_test_flag(iaxs[fr->callno], IAX_QUELCH)) {
- manager_event(EVENT_FLAG_CALL, "Hold",
- "Status: Off\r\n"
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n",
- iaxs[fr->callno]->owner->name,
- iaxs[fr->callno]->owner->uniqueid);
- }
- ast_clear_flag(iaxs[fr->callno], IAX_QUELCH);
- if (iaxs[fr->callno]->owner && ast_bridged_channel(iaxs[fr->callno]->owner)) {
- iax2_queue_control_data(fr->callno, AST_CONTROL_UNHOLD, NULL, 0);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- }
- }
- break;
- case IAX_COMMAND_TXACC:
- if (iaxs[fr->callno]->transferring == TRANSFER_BEGIN) {
- /* Ack the packet with the given timestamp */
- AST_LIST_LOCK(&frame_queue);
- AST_LIST_TRAVERSE(&frame_queue, cur, list) {
- /* Cancel any outstanding txcnt's */
- if ((fr->callno == cur->callno) && (cur->transfer))
- cur->retries = -1;
- }
- AST_LIST_UNLOCK(&frame_queue);
- memset(&ied1, 0, sizeof(ied1));
- iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->callno);
- send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXREADY, 0, ied1.buf, ied1.pos, -1);
- iaxs[fr->callno]->transferring = TRANSFER_READY;
- }
- break;
- case IAX_COMMAND_NEW:
- /* Ignore if it's already up */
- if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD))
- break;
- if (ies.provverpres && ies.serviceident && sin.sin_addr.s_addr) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- check_provisioning(&sin, fd, ies.serviceident, ies.provver);
- ast_mutex_lock(&iaxsl[fr->callno]);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- }
- /* If we're in trunk mode, do it now, and update the trunk number in our frame before continuing */
- if (ast_test_flag(iaxs[fr->callno], IAX_TRUNK)) {
- int new_callno;
- if ((new_callno = make_trunk(fr->callno, 1)) != -1)
- fr->callno = new_callno;
- }
- /* For security, always ack immediately */
- if (delayreject)
- send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
- if (check_access(fr->callno, &sin, &ies)) {
- /* They're not allowed on */
- auth_fail(fr->callno, IAX_COMMAND_REJECT);
- if (authdebug)
- ast_log(LOG_NOTICE, "Rejected connect attempt from %s, who was trying to reach '%s@%s'\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->exten, iaxs[fr->callno]->context);
- break;
- }
- if (strcasecmp(iaxs[fr->callno]->exten, "TBD")) {
- const char *context, *exten, *cid_num;
- context = ast_strdupa(iaxs[fr->callno]->context);
- exten = ast_strdupa(iaxs[fr->callno]->exten);
- cid_num = ast_strdupa(iaxs[fr->callno]->cid_num);
- /* This might re-enter the IAX code and need the lock */
- ast_mutex_unlock(&iaxsl[fr->callno]);
- exists = ast_exists_extension(NULL, context, exten, 1, cid_num);
- ast_mutex_lock(&iaxsl[fr->callno]);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- } else
- exists = 0;
- /* Get OSP token if it does exist */
- save_osptoken(fr, &ies);
- if (ast_strlen_zero(iaxs[fr->callno]->secret) && ast_strlen_zero(iaxs[fr->callno]->inkeys)) {
- if (strcmp(iaxs[fr->callno]->exten, "TBD") && !exists) {
- memset(&ied0, 0, sizeof(ied0));
- iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
- iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION);
- send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- if (authdebug)
- ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->exten, iaxs[fr->callno]->context);
- } else {
- /* Select an appropriate format */
- if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) {
- if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
- using_prefs = "reqonly";
- } else {
- using_prefs = "disabled";
- }
- format = iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability;
- memset(&pref, 0, sizeof(pref));
- strcpy(caller_pref_buf, "disabled");
- strcpy(host_pref_buf, "disabled");
- } else {
- using_prefs = "mine";
- /* If the information elements are in here... use them */
- if (ies.codec_prefs)
- ast_codec_pref_convert(&iaxs[fr->callno]->rprefs, ies.codec_prefs, 32, 0);
- if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) {
- /* If we are codec_first_choice we let the caller have the 1st shot at picking the codec.*/
- if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) {
- pref = iaxs[fr->callno]->rprefs;
- using_prefs = "caller";
- } else {
- pref = iaxs[fr->callno]->prefs;
- }
- } else
- pref = iaxs[fr->callno]->prefs;
-
- format = ast_codec_choose(&pref, iaxs[fr->callno]->capability & iaxs[fr->callno]->peercapability, 0);
- ast_codec_pref_string(&iaxs[fr->callno]->rprefs, caller_pref_buf, sizeof(caller_pref_buf) - 1);
- ast_codec_pref_string(&iaxs[fr->callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1);
- }
- if (!format) {
- if(!ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP))
- format = iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability;
- if (!format) {
- memset(&ied0, 0, sizeof(ied0));
- iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
- iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
- send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- if (authdebug) {
- if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP))
- ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability);
- else
- ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability);
- }
- } else {
- /* Pick one... */
- if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
- if(!(iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability))
- format = 0;
- } else {
- if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) {
- using_prefs = ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled";
- memset(&pref, 0, sizeof(pref));
- format = ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
- strcpy(caller_pref_buf,"disabled");
- strcpy(host_pref_buf,"disabled");
- } else {
- using_prefs = "mine";
- if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) {
- /* Do the opposite of what we tried above. */
- if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) {
- pref = iaxs[fr->callno]->prefs;
- } else {
- pref = iaxs[fr->callno]->rprefs;
- using_prefs = "caller";
- }
- format = ast_codec_choose(&pref, iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability, 1);
-
- } else /* if no codec_prefs IE do it the old way */
- format = ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
- }
- }
- if (!format) {
- memset(&ied0, 0, sizeof(ied0));
- iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
- iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
- ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
- send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- if (authdebug)
- ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability);
- ast_set_flag(iaxs[fr->callno], IAX_ALREADYGONE);
- break;
- }
- }
- }
- if (format) {
- /* No authentication required, let them in */
- memset(&ied1, 0, sizeof(ied1));
- iax_ie_append_int(&ied1, IAX_IE_FORMAT, format);
- send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1);
- if (strcmp(iaxs[fr->callno]->exten, "TBD")) {
- ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
- ast_verb(3, "Accepting UNAUTHENTICATED call from %s:\n"
- "%srequested format = %s,\n"
- "%srequested prefs = %s,\n"
- "%sactual format = %s,\n"
- "%shost prefs = %s,\n"
- "%spriority = %s\n",
- ast_inet_ntoa(sin.sin_addr),
- VERBOSE_PREFIX_4,
- ast_getformatname(iaxs[fr->callno]->peerformat),
- VERBOSE_PREFIX_4,
- caller_pref_buf,
- VERBOSE_PREFIX_4,
- ast_getformatname(format),
- VERBOSE_PREFIX_4,
- host_pref_buf,
- VERBOSE_PREFIX_4,
- using_prefs);
-
- iaxs[fr->callno]->chosenformat = format;
- ast_set_flag(iaxs[fr->callno], IAX_DELAYPBXSTART);
- } else {
- ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD);
- /* If this is a TBD call, we're ready but now what... */
- ast_verb(3, "Accepted unauthenticated TBD call from %s\n", ast_inet_ntoa(sin.sin_addr));
- }
- }
- }
- break;
- }
- if (iaxs[fr->callno]->authmethods & IAX_AUTH_MD5)
- merge_encryption(iaxs[fr->callno],ies.encmethods);
- else
- iaxs[fr->callno]->encmethods = 0;
- if (!authenticate_request(fr->callno) && iaxs[fr->callno])
- ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- break;
- case IAX_COMMAND_DPREQ:
- /* Request status in the dialplan */
- if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD) &&
- !ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED) && ies.called_number) {
- if (iaxcompat) {
- /* Spawn a thread for the lookup */
- spawn_dp_lookup(fr->callno, iaxs[fr->callno]->context, ies.called_number, iaxs[fr->callno]->cid_num);
- } else {
- /* Just look it up */
- dp_lookup(fr->callno, iaxs[fr->callno]->context, ies.called_number, iaxs[fr->callno]->cid_num, 1);
- }
- }
- break;
- case IAX_COMMAND_HANGUP:
- ast_set_flag(iaxs[fr->callno], IAX_ALREADYGONE);
- ast_debug(1, "Immediately destroying %d, having received hangup\n", fr->callno);
- /* Set hangup cause according to remote */
- if (ies.causecode && iaxs[fr->callno]->owner)
- iaxs[fr->callno]->owner->hangupcause = ies.causecode;
- /* Send ack immediately, before we destroy */
- send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
- iax2_destroy(fr->callno);
- break;
- case IAX_COMMAND_REJECT:
- /* Set hangup cause according to remote */
- if (ies.causecode && iaxs[fr->callno]->owner)
- iaxs[fr->callno]->owner->hangupcause = ies.causecode;
- if (!ast_test_flag(iaxs[fr->callno], IAX_PROVISION)) {
- if (iaxs[fr->callno]->owner && authdebug)
- ast_log(LOG_WARNING, "Call rejected by %s: %s\n",
- ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr),
- ies.cause ? ies.cause : "<Unknown>");
- ast_debug(1, "Immediately destroying %d, having received reject\n",
- fr->callno);
- }
- /* Send ack immediately, before we destroy */
- send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK,
- fr->ts, NULL, 0, fr->iseqno);
- if (!ast_test_flag(iaxs[fr->callno], IAX_PROVISION))
- iaxs[fr->callno]->error = EPERM;
- iax2_destroy(fr->callno);
- break;
- case IAX_COMMAND_TRANSFER:
- {
- struct ast_channel *bridged_chan;
- if (iaxs[fr->callno]->owner && (bridged_chan = ast_bridged_channel(iaxs[fr->callno]->owner)) && ies.called_number) {
- /* Set BLINDTRANSFER channel variables */
- ast_mutex_unlock(&iaxsl[fr->callno]);
- pbx_builtin_setvar_helper(iaxs[fr->callno]->owner, "BLINDTRANSFER", bridged_chan->name);
- ast_mutex_lock(&iaxsl[fr->callno]);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- pbx_builtin_setvar_helper(bridged_chan, "BLINDTRANSFER", iaxs[fr->callno]->owner->name);
- if (!strcmp(ies.called_number, ast_parking_ext())) {
- struct ast_channel *saved_channel = iaxs[fr->callno]->owner;
- ast_mutex_unlock(&iaxsl[fr->callno]);
- if (iax_park(bridged_chan, saved_channel)) {
- ast_log(LOG_WARNING, "Failed to park call on '%s'\n", bridged_chan->name);
- } else {
- ast_debug(1, "Parked call on '%s'\n", ast_bridged_channel(iaxs[fr->callno]->owner)->name);
- }
- ast_mutex_lock(&iaxsl[fr->callno]);
- } else {
- if (ast_async_goto(bridged_chan, iaxs[fr->callno]->context, ies.called_number, 1))
- ast_log(LOG_WARNING, "Async goto of '%s' to '%s@%s' failed\n", bridged_chan->name,
- ies.called_number, iaxs[fr->callno]->context);
- else {
- ast_debug(1, "Async goto of '%s' to '%s@%s' started\n", bridged_chan->name,
- ies.called_number, iaxs[fr->callno]->context);
- }
- }
- } else {
- ast_debug(1, "Async goto not applicable on call %d\n", fr->callno);
- }
- break;
- }
- case IAX_COMMAND_ACCEPT:
- /* Ignore if call is already up or needs authentication or is a TBD */
- if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD | IAX_STATE_AUTHENTICATED))
- break;
- if (ast_test_flag(iaxs[fr->callno], IAX_PROVISION)) {
- /* Send ack immediately, before we destroy */
- send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
- iax2_destroy(fr->callno);
- break;
- }
- if (ies.format) {
- iaxs[fr->callno]->peerformat = ies.format;
- } else {
- if (iaxs[fr->callno]->owner)
- iaxs[fr->callno]->peerformat = iaxs[fr->callno]->owner->nativeformats;
- else
- iaxs[fr->callno]->peerformat = iaxs[fr->callno]->capability;
- }
- ast_verb(3, "Call accepted by %s (format %s)\n", ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr), ast_getformatname(iaxs[fr->callno]->peerformat));
- if (!(iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability)) {
- memset(&ied0, 0, sizeof(ied0));
- iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
- iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
- send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- if (authdebug)
- ast_log(LOG_NOTICE, "Rejected call to %s, format 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability);
- } else {
- ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
- if (iaxs[fr->callno]->owner) {
- /* Switch us to use a compatible format */
- iaxs[fr->callno]->owner->nativeformats = iaxs[fr->callno]->peerformat;
- ast_verb(3, "Format for call is %s\n", ast_getformatname(iaxs[fr->callno]->owner->nativeformats));
- retryowner2:
- if (ast_channel_trylock(iaxs[fr->callno]->owner)) {
- DEADLOCK_AVOIDANCE(&iaxsl[fr->callno]);
- if (iaxs[fr->callno] && iaxs[fr->callno]->owner) goto retryowner2;
- }
-
- if (iaxs[fr->callno] && iaxs[fr->callno]->owner) {
- /* Setup read/write formats properly. */
- if (iaxs[fr->callno]->owner->writeformat)
- ast_set_write_format(iaxs[fr->callno]->owner, iaxs[fr->callno]->owner->writeformat);
- if (iaxs[fr->callno]->owner->readformat)
- ast_set_read_format(iaxs[fr->callno]->owner, iaxs[fr->callno]->owner->readformat);
- ast_channel_unlock(iaxs[fr->callno]->owner);
- }
- }
- }
- if (iaxs[fr->callno]) {
- AST_LIST_LOCK(&dpcache);
- AST_LIST_TRAVERSE(&iaxs[fr->callno]->dpentries, dp, peer_list)
- if (!(dp->flags & CACHE_FLAG_TRANSMITTED))
- iax2_dprequest(dp, fr->callno);
- AST_LIST_UNLOCK(&dpcache);
- }
- break;
- case IAX_COMMAND_POKE:
- /* Send back a pong packet with the original timestamp */
- send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_PONG, fr->ts, NULL, 0, -1);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- break;
- case IAX_COMMAND_PING:
- {
- struct iax_ie_data pingied;
- construct_rr(iaxs[fr->callno], &pingied);
- /* Send back a pong packet with the original timestamp */
- send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_PONG, fr->ts, pingied.buf, pingied.pos, -1);
- }
- break;
- case IAX_COMMAND_PONG:
- /* Calculate ping time */
- iaxs[fr->callno]->pingtime = calc_timestamp(iaxs[fr->callno], 0, &f) - fr->ts;
- /* save RR info */
- save_rr(fr, &ies);
- /* Good time to write jb stats for this call */
- log_jitterstats(fr->callno);
- if (iaxs[fr->callno]->peerpoke) {
- peer = iaxs[fr->callno]->peerpoke;
- if ((peer->lastms < 0) || (peer->historicms > peer->maxms)) {
- if (iaxs[fr->callno]->pingtime <= peer->maxms) {
- ast_log(LOG_NOTICE, "Peer '%s' is now REACHABLE! Time: %d\n", peer->name, iaxs[fr->callno]->pingtime);
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Reachable\r\nTime: %d\r\n", peer->name, iaxs[fr->callno]->pingtime);
- ast_device_state_changed("IAX2/%s", peer->name); /* Activate notification */
- }
- } else if ((peer->historicms > 0) && (peer->historicms <= peer->maxms)) {
- if (iaxs[fr->callno]->pingtime > peer->maxms) {
- ast_log(LOG_NOTICE, "Peer '%s' is now TOO LAGGED (%d ms)!\n", peer->name, iaxs[fr->callno]->pingtime);
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Lagged\r\nTime: %d\r\n", peer->name, iaxs[fr->callno]->pingtime);
- ast_device_state_changed("IAX2/%s", peer->name); /* Activate notification */
- }
- }
- peer->lastms = iaxs[fr->callno]->pingtime;
- if (peer->smoothing && (peer->lastms > -1))
- peer->historicms = (iaxs[fr->callno]->pingtime + peer->historicms) / 2;
- else if (peer->smoothing && peer->lastms < 0)
- peer->historicms = (0 + peer->historicms) / 2;
- else
- peer->historicms = iaxs[fr->callno]->pingtime;
- /* Remove scheduled iax2_poke_noanswer */
- if (peer->pokeexpire > -1) {
- if (!ast_sched_del(sched, peer->pokeexpire)) {
- peer_unref(peer);
- peer->pokeexpire = -1;
- }
- }
- /* Schedule the next cycle */
- if ((peer->lastms < 0) || (peer->historicms > peer->maxms))
- peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_peer_s, peer_ref(peer));
- else
- peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqok, iax2_poke_peer_s, peer_ref(peer));
- if (peer->pokeexpire == -1)
- peer_unref(peer);
- /* and finally send the ack */
- send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
- /* And wrap up the qualify call */
- iax2_destroy(fr->callno);
- peer->callno = 0;
- ast_debug(1, "Peer %s: got pong, lastms %d, historicms %d, maxms %d\n", peer->name, peer->lastms, peer->historicms, peer->maxms);
- }
- break;
- case IAX_COMMAND_LAGRQ:
- case IAX_COMMAND_LAGRP:
- f.src = "LAGRQ";
- f.mallocd = 0;
- f.offset = 0;
- f.samples = 0;
- iax_frame_wrap(fr, &f);
- if(f.subclass == IAX_COMMAND_LAGRQ) {
- /* Received a LAGRQ - echo back a LAGRP */
- fr->af.subclass = IAX_COMMAND_LAGRP;
- iax2_send(iaxs[fr->callno], &fr->af, fr->ts, -1, 0, 0, 0);
- } else {
- /* Received LAGRP in response to our LAGRQ */
- unsigned int ts;
- /* This is a reply we've been given, actually measure the difference */
- ts = calc_timestamp(iaxs[fr->callno], 0, &fr->af);
- iaxs[fr->callno]->lag = ts - fr->ts;
- if (iaxdebug)
- ast_debug(1, "Peer %s lag measured as %dms\n",
- ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr), iaxs[fr->callno]->lag);
- }
- break;
- case IAX_COMMAND_AUTHREQ:
- if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD)) {
- ast_log(LOG_WARNING, "Call on %s is already up, can't start on it\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>");
- break;
- }
- if (authenticate_reply(iaxs[fr->callno], &iaxs[fr->callno]->addr, &ies, iaxs[fr->callno]->secret, iaxs[fr->callno]->outkey)) {
- struct ast_frame hangup_fr = { .frametype = AST_FRAME_CONTROL,
- .subclass = AST_CONTROL_HANGUP,
- };
- ast_log(LOG_WARNING,
- "I don't know how to authenticate %s to %s\n",
- ies.username ? ies.username : "<unknown>", ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr));
- iax2_queue_frame(fr->callno, &hangup_fr);
- }
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- break;
- case IAX_COMMAND_AUTHREP:
- /* For security, always ack immediately */
- if (delayreject)
- send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
- /* Ignore once we've started */
- if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD)) {
- ast_log(LOG_WARNING, "Call on %s is already up, can't start on it\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>");
- break;
- }
- if (authenticate_verify(iaxs[fr->callno], &ies)) {
- if (authdebug)
- ast_log(LOG_NOTICE, "Host %s failed to authenticate as %s\n", ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr), iaxs[fr->callno]->username);
- memset(&ied0, 0, sizeof(ied0));
- auth_fail(fr->callno, IAX_COMMAND_REJECT);
- break;
- }
- if (strcasecmp(iaxs[fr->callno]->exten, "TBD")) {
- /* This might re-enter the IAX code and need the lock */
- exists = ast_exists_extension(NULL, iaxs[fr->callno]->context, iaxs[fr->callno]->exten, 1, iaxs[fr->callno]->cid_num);
- } else
- exists = 0;
- if (strcmp(iaxs[fr->callno]->exten, "TBD") && !exists) {
- if (authdebug)
- ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->exten, iaxs[fr->callno]->context);
- memset(&ied0, 0, sizeof(ied0));
- iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
- iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION);
- send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- } else {
- /* Select an appropriate format */
- if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) {
- if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
- using_prefs = "reqonly";
- } else {
- using_prefs = "disabled";
- }
- format = iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability;
- memset(&pref, 0, sizeof(pref));
- strcpy(caller_pref_buf, "disabled");
- strcpy(host_pref_buf, "disabled");
- } else {
- using_prefs = "mine";
- if (ies.codec_prefs)
- ast_codec_pref_convert(&iaxs[fr->callno]->rprefs, ies.codec_prefs, 32, 0);
- if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) {
- if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) {
- pref = iaxs[fr->callno]->rprefs;
- using_prefs = "caller";
- } else {
- pref = iaxs[fr->callno]->prefs;
- }
- } else /* if no codec_prefs IE do it the old way */
- pref = iaxs[fr->callno]->prefs;
-
- format = ast_codec_choose(&pref, iaxs[fr->callno]->capability & iaxs[fr->callno]->peercapability, 0);
- ast_codec_pref_string(&iaxs[fr->callno]->rprefs, caller_pref_buf, sizeof(caller_pref_buf) - 1);
- ast_codec_pref_string(&iaxs[fr->callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1);
- }
- if (!format) {
- if(!ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
- ast_debug(1, "We don't do requested format %s, falling back to peer capability %d\n", ast_getformatname(iaxs[fr->callno]->peerformat), iaxs[fr->callno]->peercapability);
- format = iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability;
- }
- if (!format) {
- if (authdebug) {
- if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP))
- ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability);
- else
- ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability);
- }
- memset(&ied0, 0, sizeof(ied0));
- iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
- iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
- send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- } else {
- /* Pick one... */
- if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
- if(!(iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability))
- format = 0;
- } else {
- if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) {
- using_prefs = ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled";
- memset(&pref, 0, sizeof(pref));
- format = ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP) ?
- iaxs[fr->callno]->peerformat : ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
- strcpy(caller_pref_buf,"disabled");
- strcpy(host_pref_buf,"disabled");
- } else {
- using_prefs = "mine";
- if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) {
- /* Do the opposite of what we tried above. */
- if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) {
- pref = iaxs[fr->callno]->prefs;
- } else {
- pref = iaxs[fr->callno]->rprefs;
- using_prefs = "caller";
- }
- format = ast_codec_choose(&pref, iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability, 1);
- } else /* if no codec_prefs IE do it the old way */
- format = ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
- }
- }
- if (!format) {
- ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
- if (authdebug) {
- if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP))
- ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability);
- else
- ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability);
- }
- memset(&ied0, 0, sizeof(ied0));
- iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec");
- iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
- send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- }
- }
- }
- if (format) {
- /* Authentication received */
- memset(&ied1, 0, sizeof(ied1));
- iax_ie_append_int(&ied1, IAX_IE_FORMAT, format);
- send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1);
- if (strcmp(iaxs[fr->callno]->exten, "TBD")) {
- ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
- ast_verb(3, "Accepting AUTHENTICATED call from %s:\n"
- "%srequested format = %s,\n"
- "%srequested prefs = %s,\n"
- "%sactual format = %s,\n"
- "%shost prefs = %s,\n"
- "%spriority = %s\n",
- ast_inet_ntoa(sin.sin_addr),
- VERBOSE_PREFIX_4,
- ast_getformatname(iaxs[fr->callno]->peerformat),
- VERBOSE_PREFIX_4,
- caller_pref_buf,
- VERBOSE_PREFIX_4,
- ast_getformatname(format),
- VERBOSE_PREFIX_4,
- host_pref_buf,
- VERBOSE_PREFIX_4,
- using_prefs);
- ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
- if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format)))
- iax2_destroy(fr->callno);
- else if (ies.vars) {
- struct ast_datastore *variablestore;
- struct ast_variable *var, *prev = NULL;
- AST_LIST_HEAD(, ast_var_t) *varlist;
- varlist = ast_calloc(1, sizeof(*varlist));
- variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
- if (variablestore && varlist) {
- variablestore->data = varlist;
- variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
- AST_LIST_HEAD_INIT(varlist);
- ast_debug(1, "I can haz IAX vars? w00t\n");
- for (var = ies.vars; var; var = var->next) {
- struct ast_var_t *newvar = ast_var_assign(var->name, var->value);
- if (prev)
- ast_free(prev);
- prev = var;
- if (!newvar) {
- /* Don't abort list traversal, as this would leave ies.vars in an inconsistent state. */
- ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
- } else {
- AST_LIST_INSERT_TAIL(varlist, newvar, entries);
- }
- }
- if (prev)
- ast_free(prev);
- ies.vars = NULL;
- ast_channel_datastore_add(c, variablestore);
- } else {
- ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
- if (variablestore)
- ast_channel_datastore_free(variablestore);
- if (varlist)
- ast_free(varlist);
- }
- }
- } else {
- ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD);
- /* If this is a TBD call, we're ready but now what... */
- ast_verb(3, "Accepted AUTHENTICATED TBD call from %s\n", ast_inet_ntoa(sin.sin_addr));
- }
- }
- }
- break;
- case IAX_COMMAND_DIAL:
- if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD)) {
- ast_clear_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD);
- ast_string_field_set(iaxs[fr->callno], exten, ies.called_number ? ies.called_number : "s");
- if (!ast_exists_extension(NULL, iaxs[fr->callno]->context, iaxs[fr->callno]->exten, 1, iaxs[fr->callno]->cid_num)) {
- if (authdebug)
- ast_log(LOG_NOTICE, "Rejected dial attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->exten, iaxs[fr->callno]->context);
- memset(&ied0, 0, sizeof(ied0));
- iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension");
- iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION);
- send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- } else {
- ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
- ast_verb(3, "Accepting DIAL from %s, formats = 0x%x\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat);
- ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
- send_command(iaxs[fr->callno], AST_FRAME_CONTROL, AST_CONTROL_PROGRESS, 0, NULL, 0, -1);
- if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat)))
- iax2_destroy(fr->callno);
- else if (ies.vars) {
- struct ast_datastore *variablestore;
- struct ast_variable *var, *prev = NULL;
- AST_LIST_HEAD(, ast_var_t) *varlist;
- varlist = ast_calloc(1, sizeof(*varlist));
- variablestore = ast_channel_datastore_alloc(&iax2_variable_datastore_info, NULL);
- if (variablestore && varlist) {
- variablestore->data = varlist;
- variablestore->inheritance = DATASTORE_INHERIT_FOREVER;
- AST_LIST_HEAD_INIT(varlist);
- for (var = ies.vars; var; var = var->next) {
- struct ast_var_t *newvar = ast_var_assign(var->name, var->value);
- if (prev)
- ast_free(prev);
- prev = var;
- if (!newvar) {
- /* Don't abort list traversal, as this would leave ies.vars in an inconsistent state. */
- ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
- } else {
- AST_LIST_INSERT_TAIL(varlist, newvar, entries);
- }
- }
- if (prev)
- ast_free(prev);
- ies.vars = NULL;
- ast_channel_datastore_add(c, variablestore);
- } else {
- ast_log(LOG_ERROR, "Memory allocation error while processing IAX2 variables\n");
- if (variablestore)
- ast_channel_datastore_free(variablestore);
- if (varlist)
- ast_free(varlist);
- }
- }
- }
- }
- break;
- case IAX_COMMAND_INVAL:
- iaxs[fr->callno]->error = ENOTCONN;
- ast_debug(1, "Immediately destroying %d, having received INVAL\n", fr->callno);
- iax2_destroy(fr->callno);
- ast_debug(1, "Destroying call %d\n", fr->callno);
- break;
- case IAX_COMMAND_VNAK:
- ast_debug(1, "Received VNAK: resending outstanding frames\n");
- /* Force retransmission */
- vnak_retransmit(fr->callno, fr->iseqno);
- break;
- case IAX_COMMAND_REGREQ:
- case IAX_COMMAND_REGREL:
- /* For security, always ack immediately */
- if (delayreject)
- send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
- if (register_verify(fr->callno, &sin, &ies)) {
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- /* Send delayed failure */
- auth_fail(fr->callno, IAX_COMMAND_REGREJ);
- break;
- }
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- if ((ast_strlen_zero(iaxs[fr->callno]->secret) && ast_strlen_zero(iaxs[fr->callno]->inkeys)) ||
- ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED)) {
- if (f.subclass == IAX_COMMAND_REGREL)
- memset(&sin, 0, sizeof(sin));
- if (update_registry(&sin, fr->callno, ies.devicetype, fd, ies.refresh))
- ast_log(LOG_WARNING, "Registry error\n");
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- if (ies.provverpres && ies.serviceident && sin.sin_addr.s_addr) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- check_provisioning(&sin, fd, ies.serviceident, ies.provver);
- ast_mutex_lock(&iaxsl[fr->callno]);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- }
- break;
- }
- registry_authrequest(fr->callno);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- break;
- case IAX_COMMAND_REGACK:
- if (iax2_ack_registry(&ies, &sin, fr->callno))
- ast_log(LOG_WARNING, "Registration failure\n");
- /* Send ack immediately, before we destroy */
- send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
- iax2_destroy(fr->callno);
- break;
- case IAX_COMMAND_REGREJ:
- if (iaxs[fr->callno]->reg) {
- if (authdebug) {
- ast_log(LOG_NOTICE, "Registration of '%s' rejected: '%s' from: '%s'\n", iaxs[fr->callno]->reg->username, ies.cause ? ies.cause : "<unknown>", ast_inet_ntoa(sin.sin_addr));
- manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelType: IAX2\r\nUsername: %s\r\nStatus: Rejected\r\nCause: %s\r\n", iaxs[fr->callno]->reg->username, ies.cause ? ies.cause : "<unknown>");
- }
- iaxs[fr->callno]->reg->regstate = REG_STATE_REJECTED;
- }
- /* Send ack immediately, before we destroy */
- send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
- iax2_destroy(fr->callno);
- break;
- case IAX_COMMAND_REGAUTH:
- /* Authentication request */
- if (registry_rerequest(&ies, fr->callno, &sin)) {
- memset(&ied0, 0, sizeof(ied0));
- iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No authority found");
- iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_FACILITY_NOT_SUBSCRIBED);
- send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- }
- break;
- case IAX_COMMAND_TXREJ:
- iaxs[fr->callno]->transferring = 0;
- ast_verb(3, "Channel '%s' unable to transfer\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>");
- memset(&iaxs[fr->callno]->transfer, 0, sizeof(iaxs[fr->callno]->transfer));
- if (iaxs[fr->callno]->bridgecallno) {
- if (iaxs[iaxs[fr->callno]->bridgecallno]->transferring) {
- iaxs[iaxs[fr->callno]->bridgecallno]->transferring = 0;
- send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, NULL, 0, -1);
- }
- }
- break;
- case IAX_COMMAND_TXREADY:
- if ((iaxs[fr->callno]->transferring == TRANSFER_BEGIN) ||
- (iaxs[fr->callno]->transferring == TRANSFER_MBEGIN)) {
- if (iaxs[fr->callno]->transferring == TRANSFER_MBEGIN)
- iaxs[fr->callno]->transferring = TRANSFER_MREADY;
- else
- iaxs[fr->callno]->transferring = TRANSFER_READY;
- ast_verb(3, "Channel '%s' ready to transfer\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>");
- if (iaxs[fr->callno]->bridgecallno) {
- if ((iaxs[iaxs[fr->callno]->bridgecallno]->transferring == TRANSFER_READY) ||
- (iaxs[iaxs[fr->callno]->bridgecallno]->transferring == TRANSFER_MREADY)) {
- /* They're both ready, now release them. */
- if (iaxs[fr->callno]->transferring == TRANSFER_MREADY) {
- ast_verb(3, "Attempting media bridge of %s and %s\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>",
- iaxs[iaxs[fr->callno]->bridgecallno]->owner ? iaxs[iaxs[fr->callno]->bridgecallno]->owner->name : "<Unknown>");
- iaxs[iaxs[fr->callno]->bridgecallno]->transferring = TRANSFER_MEDIA;
- iaxs[fr->callno]->transferring = TRANSFER_MEDIA;
- memset(&ied0, 0, sizeof(ied0));
- memset(&ied1, 0, sizeof(ied1));
- iax_ie_append_short(&ied0, IAX_IE_CALLNO, iaxs[iaxs[fr->callno]->bridgecallno]->peercallno);
- iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->peercallno);
- send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXMEDIA, 0, ied0.buf, ied0.pos, -1);
- send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXMEDIA, 0, ied1.buf, ied1.pos, -1);
- } else {
- ast_verb(3, "Releasing %s and %s\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>",
- iaxs[iaxs[fr->callno]->bridgecallno]->owner ? iaxs[iaxs[fr->callno]->bridgecallno]->owner->name : "<Unknown>");
- iaxs[iaxs[fr->callno]->bridgecallno]->transferring = TRANSFER_RELEASED;
- iaxs[fr->callno]->transferring = TRANSFER_RELEASED;
- ast_set_flag(iaxs[iaxs[fr->callno]->bridgecallno], IAX_ALREADYGONE);
- ast_set_flag(iaxs[fr->callno], IAX_ALREADYGONE);
- /* Stop doing lag & ping requests */
- stop_stuff(fr->callno);
- stop_stuff(iaxs[fr->callno]->bridgecallno);
- memset(&ied0, 0, sizeof(ied0));
- memset(&ied1, 0, sizeof(ied1));
- iax_ie_append_short(&ied0, IAX_IE_CALLNO, iaxs[iaxs[fr->callno]->bridgecallno]->peercallno);
- iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->peercallno);
- send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXREL, 0, ied0.buf, ied0.pos, -1);
- send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXREL, 0, ied1.buf, ied1.pos, -1);
- }
- }
- }
- }
- break;
- case IAX_COMMAND_TXREQ:
- try_transfer(iaxs[fr->callno], &ies);
- break;
- case IAX_COMMAND_TXCNT:
- if (iaxs[fr->callno]->transferring)
- send_command_transfer(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXACC, 0, NULL, 0);
- break;
- case IAX_COMMAND_TXREL:
- /* Send ack immediately, rather than waiting until we've changed addresses */
- send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
- complete_transfer(fr->callno, &ies);
- stop_stuff(fr->callno); /* for attended transfer to work with libiax */
- break;
- case IAX_COMMAND_TXMEDIA:
- if (iaxs[fr->callno]->transferring == TRANSFER_READY) {
- AST_LIST_LOCK(&frame_queue);
- AST_LIST_TRAVERSE(&frame_queue, cur, list) {
- /* Cancel any outstanding frames and start anew */
- if ((fr->callno == cur->callno) && (cur->transfer))
- cur->retries = -1;
- }
- AST_LIST_UNLOCK(&frame_queue);
- /* Start sending our media to the transfer address, but otherwise leave the call as-is */
- iaxs[fr->callno]->transferring = TRANSFER_MEDIAPASS;
- }
- break;
- case IAX_COMMAND_DPREP:
- complete_dpreply(iaxs[fr->callno], &ies);
- break;
- case IAX_COMMAND_UNSUPPORT:
- ast_log(LOG_NOTICE, "Peer did not understand our iax command '%d'\n", ies.iax_unknown);
- break;
- case IAX_COMMAND_FWDOWNL:
- /* Firmware download */
- if (!ast_test_flag(&globalflags, IAX_ALLOWFWDOWNLOAD)) {
- send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_UNSUPPORT, 0, NULL, 0, -1);
- break;
- }
- memset(&ied0, 0, sizeof(ied0));
- res = iax_firmware_append(&ied0, (unsigned char *)ies.devicetype, ies.fwdesc);
- if (res < 0)
- send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
- else if (res > 0)
- send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_FWDATA, 0, ied0.buf, ied0.pos, -1);
- else
- send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_FWDATA, 0, ied0.buf, ied0.pos, -1);
- if (!iaxs[fr->callno]) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- break;
- case IAX_COMMAND_CALLTOKEN:
- {
- struct iax_frame *cur;
- int found = 0;
- AST_LIST_LOCK(&frame_queue);
- AST_LIST_TRAVERSE(&frame_queue, cur, list) {
- /* find the last sent frame in our frame queue for this callno.
- * There are many things to take into account before resending this frame.
- * All of these are taken care of in resend_with_token() */
- if (cur->callno == fr->callno) {
- found = 1;
- break;
- }
- }
- AST_LIST_UNLOCK(&frame_queue);
- /* find last sent frame */
- if (cur && found && ies.calltoken && ies.calltokendata) {
- resend_with_token(fr->callno, cur, (char *) ies.calltokendata);
- }
- break;
- }
- default:
- ast_debug(1, "Unknown IAX command %d on %d/%d\n", f.subclass, fr->callno, iaxs[fr->callno]->peercallno);
- memset(&ied0, 0, sizeof(ied0));
- iax_ie_append_byte(&ied0, IAX_IE_IAX_UNKNOWN, f.subclass);
- send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_UNSUPPORT, 0, ied0.buf, ied0.pos, -1);
- }
- /* Free remote variables (if any) */
- if (ies.vars) {
- ast_variables_destroy(ies.vars);
- ast_debug(1, "I can haz IAX vars, but they is no good :-(\n");
- ies.vars = NULL;
- }
- /* Don't actually pass these frames along */
- if ((f.subclass != IAX_COMMAND_ACK) &&
- (f.subclass != IAX_COMMAND_TXCNT) &&
- (f.subclass != IAX_COMMAND_TXACC) &&
- (f.subclass != IAX_COMMAND_INVAL) &&
- (f.subclass != IAX_COMMAND_VNAK)) {
- if (iaxs[fr->callno] && iaxs[fr->callno]->aseqno != iaxs[fr->callno]->iseqno)
- send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
- }
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- /* Unless this is an ACK or INVAL frame, ack it */
- if (iaxs[fr->callno] && iaxs[fr->callno]->aseqno != iaxs[fr->callno]->iseqno)
- send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno);
- } else if (minivid) {
- f.frametype = AST_FRAME_VIDEO;
- if (iaxs[fr->callno]->videoformat > 0)
- f.subclass = iaxs[fr->callno]->videoformat | (ntohs(vh->ts) & 0x8000 ? 1 : 0);
- else {
- ast_log(LOG_WARNING, "Received mini frame before first full video frame\n");
- iax2_vnak(fr->callno);
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- f.datalen = res - sizeof(*vh);
- if (f.datalen)
- f.data = thread->buf + sizeof(*vh);
- else
- f.data = NULL;
- #ifdef IAXTESTS
- if (test_resync) {
- fr->ts = (iaxs[fr->callno]->last & 0xFFFF8000L) | ((ntohs(vh->ts) + test_resync) & 0x7fff);
- } else
- #endif /* IAXTESTS */
- fr->ts = (iaxs[fr->callno]->last & 0xFFFF8000L) | (ntohs(vh->ts) & 0x7fff);
- } else {
- /* A mini frame */
- f.frametype = AST_FRAME_VOICE;
- if (iaxs[fr->callno]->voiceformat > 0)
- f.subclass = iaxs[fr->callno]->voiceformat;
- else {
- ast_debug(1, "Received mini frame before first full voice frame\n");
- iax2_vnak(fr->callno);
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- f.datalen = res - sizeof(struct ast_iax2_mini_hdr);
- if (f.datalen < 0) {
- ast_log(LOG_WARNING, "Datalen < 0?\n");
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- if (f.datalen)
- f.data = thread->buf + sizeof(*mh);
- else
- f.data = NULL;
- #ifdef IAXTESTS
- if (test_resync) {
- fr->ts = (iaxs[fr->callno]->last & 0xFFFF0000L) | ((ntohs(mh->ts) + test_resync) & 0xffff);
- } else
- #endif /* IAXTESTS */
- fr->ts = (iaxs[fr->callno]->last & 0xFFFF0000L) | ntohs(mh->ts);
- /* FIXME? Surely right here would be the right place to undo timestamp wraparound? */
- }
- /* Don't pass any packets until we're started */
- if (!ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED)) {
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- /* Common things */
- f.src = "IAX2";
- f.mallocd = 0;
- f.offset = 0;
- f.len = 0;
- if (f.datalen && (f.frametype == AST_FRAME_VOICE)) {
- f.samples = ast_codec_get_samples(&f);
- /* We need to byteswap incoming slinear samples from network byte order */
- if (f.subclass == AST_FORMAT_SLINEAR)
- ast_frame_byteswap_be(&f);
- } else
- f.samples = 0;
- iax_frame_wrap(fr, &f);
- /* If this is our most recent packet, use it as our basis for timestamping */
- if (iaxs[fr->callno] && iaxs[fr->callno]->last < fr->ts) {
- /*iaxs[fr->callno]->last = fr->ts; (do it afterwards cos schedule/forward_delivery needs the last ts too)*/
- fr->outoforder = 0;
- } else {
- if (iaxdebug && iaxs[fr->callno])
- ast_debug(1, "Received out of order packet... (type=%d, subclass %d, ts = %d, last = %d)\n", f.frametype, f.subclass, fr->ts, iaxs[fr->callno]->last);
- fr->outoforder = -1;
- }
- fr->cacheable = ((f.frametype == AST_FRAME_VOICE) || (f.frametype == AST_FRAME_VIDEO));
- duped_fr = iaxfrdup2(fr);
- if (duped_fr) {
- schedule_delivery(duped_fr, updatehistory, 0, &fr->ts);
- }
- if (iaxs[fr->callno] && iaxs[fr->callno]->last < fr->ts) {
- iaxs[fr->callno]->last = fr->ts;
- #if 1
- if (iaxdebug)
- ast_debug(1, "For call=%d, set last=%d\n", fr->callno, fr->ts);
- #endif
- }
- /* Always run again */
- ast_mutex_unlock(&iaxsl[fr->callno]);
- return 1;
- }
- /* Function to clean up process thread if it is cancelled */
- static void iax2_process_thread_cleanup(void *data)
- {
- struct iax2_thread *thread = data;
- ast_mutex_destroy(&thread->lock);
- ast_cond_destroy(&thread->cond);
- ast_free(thread);
- ast_atomic_dec_and_test(&iaxactivethreadcount);
- }
- static void *iax2_process_thread(void *data)
- {
- struct iax2_thread *thread = data;
- struct timeval tv;
- struct timespec ts;
- int put_into_idle = 0;
- ast_atomic_fetchadd_int(&iaxactivethreadcount,1);
- pthread_cleanup_push(iax2_process_thread_cleanup, data);
- for(;;) {
- /* Wait for something to signal us to be awake */
- ast_mutex_lock(&thread->lock);
- /* Flag that we're ready to accept signals */
- thread->ready_for_signal = 1;
-
- /* Put into idle list if applicable */
- if (put_into_idle)
- insert_idle_thread(thread);
- if (thread->type == IAX_THREAD_TYPE_DYNAMIC) {
- struct iax2_thread *t = NULL;
- /* Wait to be signalled or time out */
- tv = ast_tvadd(ast_tvnow(), ast_samp2tv(30000, 1000));
- ts.tv_sec = tv.tv_sec;
- ts.tv_nsec = tv.tv_usec * 1000;
- if (ast_cond_timedwait(&thread->cond, &thread->lock, &ts) == ETIMEDOUT) {
- /* This thread was never put back into the available dynamic
- * thread list, so just go away. */
- if (!put_into_idle) {
- ast_mutex_unlock(&thread->lock);
- break;
- }
- AST_LIST_LOCK(&dynamic_list);
- /* Account for the case where this thread is acquired *right* after a timeout */
- if ((t = AST_LIST_REMOVE(&dynamic_list, thread, list)))
- ast_atomic_fetchadd_int(&iaxdynamicthreadcount, -1);
- AST_LIST_UNLOCK(&dynamic_list);
- if (t) {
- /* This dynamic thread timed out waiting for a task and was
- * not acquired immediately after the timeout,
- * so it's time to go away. */
- ast_mutex_unlock(&thread->lock);
- break;
- }
- /* Someone grabbed our thread *right* after we timed out.
- * Wait for them to set us up with something to do and signal
- * us to continue. */
- tv = ast_tvadd(ast_tvnow(), ast_samp2tv(30000, 1000));
- ts.tv_sec = tv.tv_sec;
- ts.tv_nsec = tv.tv_usec * 1000;
- if (ast_cond_timedwait(&thread->cond, &thread->lock, &ts) == ETIMEDOUT)
- {
- ast_mutex_unlock(&thread->lock);
- break;
- }
- }
- } else {
- ast_cond_wait(&thread->cond, &thread->lock);
- }
- /* Go back into our respective list */
- put_into_idle = 1;
- ast_mutex_unlock(&thread->lock);
- if (thread->iostate == IAX_IOSTATE_IDLE)
- continue;
- /* Add ourselves to the active list now */
- AST_LIST_LOCK(&active_list);
- AST_LIST_INSERT_HEAD(&active_list, thread, list);
- AST_LIST_UNLOCK(&active_list);
- /* See what we need to do */
- switch(thread->iostate) {
- case IAX_IOSTATE_READY:
- thread->actions++;
- thread->iostate = IAX_IOSTATE_PROCESSING;
- socket_process(thread);
- handle_deferred_full_frames(thread);
- break;
- case IAX_IOSTATE_SCHEDREADY:
- thread->actions++;
- thread->iostate = IAX_IOSTATE_PROCESSING;
- #ifdef SCHED_MULTITHREADED
- thread->schedfunc(thread->scheddata);
- #endif
- default:
- break;
- }
- time(&thread->checktime);
- thread->iostate = IAX_IOSTATE_IDLE;
- #ifdef DEBUG_SCHED_MULTITHREAD
- thread->curfunc[0]='\0';
- #endif
- /* Now... remove ourselves from the active list, and return to the idle list */
- AST_LIST_LOCK(&active_list);
- AST_LIST_REMOVE(&active_list, thread, list);
- AST_LIST_UNLOCK(&active_list);
- /* Make sure another frame didn't sneak in there after we thought we were done. */
- handle_deferred_full_frames(thread);
- }
- /*!\note For some reason, idle threads are exiting without being removed
- * from an idle list, which is causing memory corruption. Forcibly remove
- * it from the list, if it's there.
- */
- AST_LIST_LOCK(&idle_list);
- AST_LIST_REMOVE(&idle_list, thread, list);
- AST_LIST_UNLOCK(&idle_list);
- AST_LIST_LOCK(&dynamic_list);
- AST_LIST_REMOVE(&dynamic_list, thread, list);
- AST_LIST_UNLOCK(&dynamic_list);
- /* I am exiting here on my own volition, I need to clean up my own data structures
- * Assume that I am no longer in any of the lists (idle, active, or dynamic)
- */
- pthread_cleanup_pop(1);
- return NULL;
- }
- static int iax2_do_register(struct iax2_registry *reg)
- {
- struct iax_ie_data ied;
- if (iaxdebug)
- ast_debug(1, "Sending registration request for '%s'\n", reg->username);
- if (reg->dnsmgr &&
- ((reg->regstate == REG_STATE_TIMEOUT) || !reg->addr.sin_addr.s_addr)) {
- /* Maybe the IP has changed, force DNS refresh */
- ast_dnsmgr_refresh(reg->dnsmgr);
- }
-
- /*
- * if IP has Changed, free allocated call to create a new one with new IP
- * call has the pointer to IP and must be updated to the new one
- */
- if (reg->dnsmgr && ast_dnsmgr_changed(reg->dnsmgr) && (reg->callno > 0)) {
- int callno = reg->callno;
- ast_mutex_lock(&iaxsl[callno]);
- iax2_destroy(callno);
- ast_mutex_unlock(&iaxsl[callno]);
- reg->callno = 0;
- }
- if (!reg->addr.sin_addr.s_addr) {
- if (iaxdebug)
- ast_debug(1, "Unable to send registration request for '%s' without IP address\n", reg->username);
- /* Setup the next registration attempt */
- reg->expire = iax2_sched_replace(reg->expire, sched,
- (5 * reg->refresh / 6) * 1000, iax2_do_register_s, reg);
- return -1;
- }
- if (!reg->callno) {
- ast_debug(3, "Allocate call number\n");
- reg->callno = find_callno_locked(0, 0, ®->addr, NEW_FORCE, defaultsockfd, 0);
- if (reg->callno < 1) {
- ast_log(LOG_WARNING, "Unable to create call for registration\n");
- return -1;
- } else
- ast_debug(3, "Registration created on call %d\n", reg->callno);
- iaxs[reg->callno]->reg = reg;
- ast_mutex_unlock(&iaxsl[reg->callno]);
- }
- /* Setup the next registration a little early */
- reg->expire = iax2_sched_replace(reg->expire, sched,
- (5 * reg->refresh / 6) * 1000, iax2_do_register_s, reg);
- /* Send the request */
- memset(&ied, 0, sizeof(ied));
- iax_ie_append_str(&ied, IAX_IE_USERNAME, reg->username);
- iax_ie_append_short(&ied, IAX_IE_REFRESH, reg->refresh);
- add_empty_calltoken_ie(iaxs[reg->callno], &ied); /* this _MUST_ be the last ie added */
- send_command(iaxs[reg->callno],AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1);
- reg->regstate = REG_STATE_REGSENT;
- return 0;
- }
- static int iax2_provision(struct sockaddr_in *end, int sockfd, char *dest, const char *template, int force)
- {
- /* Returns 1 if provisioned, -1 if not able to find destination, or 0 if no provisioning
- is found for template */
- struct iax_ie_data provdata;
- struct iax_ie_data ied;
- unsigned int sig;
- struct sockaddr_in sin;
- int callno;
- struct create_addr_info cai;
- memset(&cai, 0, sizeof(cai));
- ast_debug(1, "Provisioning '%s' from template '%s'\n", dest, template);
- if (iax_provision_build(&provdata, &sig, template, force)) {
- ast_debug(1, "No provisioning found for template '%s'\n", template);
- return 0;
- }
- if (end) {
- memcpy(&sin, end, sizeof(sin));
- cai.sockfd = sockfd;
- } else if (create_addr(dest, NULL, &sin, &cai))
- return -1;
- /* Build the rest of the message */
- memset(&ied, 0, sizeof(ied));
- iax_ie_append_raw(&ied, IAX_IE_PROVISIONING, provdata.buf, provdata.pos);
- callno = find_callno_locked(0, 0, &sin, NEW_FORCE, cai.sockfd, 0);
- if (!callno)
- return -1;
- if (iaxs[callno]) {
- /* Schedule autodestruct in case they don't ever give us anything back */
- iaxs[callno]->autoid = iax2_sched_replace(iaxs[callno]->autoid,
- sched, 15000, auto_hangup, (void *)(long)callno);
- ast_set_flag(iaxs[callno], IAX_PROVISION);
- /* Got a call number now, so go ahead and send the provisioning information */
- send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PROVISION, 0, ied.buf, ied.pos, -1);
- }
- ast_mutex_unlock(&iaxsl[callno]);
- return 1;
- }
- static char *papp = "IAX2Provision";
- static char *psyn = "Provision a calling IAXy with a given template";
- static char *pdescrip =
- " IAX2Provision([template]): Provisions the calling IAXy (assuming\n"
- "the calling entity is in fact an IAXy) with the given template or\n"
- "default if one is not specified. Returns -1 on error or 0 on success.\n";
- /*! iax2provision
- \ingroup applications
- */
- static int iax2_prov_app(struct ast_channel *chan, void *data)
- {
- int res;
- char *sdata;
- char *opts;
- int force =0;
- unsigned short callno = PTR_TO_CALLNO(chan->tech_pvt);
- if (ast_strlen_zero(data))
- data = "default";
- sdata = ast_strdupa(data);
- opts = strchr(sdata, '|');
- if (opts)
- *opts='\0';
- if (chan->tech != &iax2_tech) {
- ast_log(LOG_NOTICE, "Can't provision a non-IAX device!\n");
- return -1;
- }
- if (!callno || !iaxs[callno] || !iaxs[callno]->addr.sin_addr.s_addr) {
- ast_log(LOG_NOTICE, "Can't provision something with no IP?\n");
- return -1;
- }
- res = iax2_provision(&iaxs[callno]->addr, iaxs[callno]->sockfd, NULL, sdata, force);
- ast_verb(3, "Provisioned IAXY at '%s' with '%s'= %d\n",
- ast_inet_ntoa(iaxs[callno]->addr.sin_addr),
- sdata, res);
- return res;
- }
- static char *handle_cli_iax2_provision(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- int force = 0;
- int res;
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 provision";
- e->usage =
- "Usage: iax2 provision <host> <template> [forced]\n"
- " Provisions the given peer or IP address using a template\n"
- " matching either 'template' or '*' if the template is not\n"
- " found. If 'forced' is specified, even empty provisioning\n"
- " fields will be provisioned as empty fields.\n";
- return NULL;
- case CLI_GENERATE:
- if (a->pos == 3)
- return iax_prov_complete_template(a->line, a->word, a->pos, a->n);
- return NULL;
- }
- if (a->argc < 4)
- return CLI_SHOWUSAGE;
- if (a->argc > 4) {
- if (!strcasecmp(a->argv[4], "forced"))
- force = 1;
- else
- return CLI_SHOWUSAGE;
- }
- res = iax2_provision(NULL, -1, a->argv[2], a->argv[3], force);
- if (res < 0)
- ast_cli(a->fd, "Unable to find peer/address '%s'\n", a->argv[2]);
- else if (res < 1)
- ast_cli(a->fd, "No template (including wildcard) matching '%s'\n", a->argv[3]);
- else
- ast_cli(a->fd, "Provisioning '%s' with template '%s'%s\n", a->argv[2], a->argv[3], force ? ", forced" : "");
- return CLI_SUCCESS;
- }
- static void __iax2_poke_noanswer(const void *data)
- {
- struct iax2_peer *peer = (struct iax2_peer *)data;
- int callno;
- if (peer->lastms > -1) {
- ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE! Time: %d\n", peer->name, peer->lastms);
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: IAX2\r\nPeer: IAX2/%s\r\nPeerStatus: Unreachable\r\nTime: %d\r\n", peer->name, peer->lastms);
- ast_device_state_changed("IAX2/%s", peer->name); /* Activate notification */
- }
- if ((callno = peer->callno) > 0) {
- ast_mutex_lock(&iaxsl[callno]);
- iax2_destroy(callno);
- ast_mutex_unlock(&iaxsl[callno]);
- }
- peer->callno = 0;
- peer->lastms = -1;
- /* Try again quickly */
- peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_peer_s, peer_ref(peer));
- if (peer->pokeexpire == -1)
- peer_unref(peer);
- }
- static int iax2_poke_noanswer(const void *data)
- {
- struct iax2_peer *peer = (struct iax2_peer *)data;
- peer->pokeexpire = -1;
- #ifdef SCHED_MULTITHREADED
- if (schedule_action(__iax2_poke_noanswer, data))
- #endif
- __iax2_poke_noanswer(data);
- peer_unref(peer);
- return 0;
- }
- static int iax2_poke_peer_cb(void *obj, void *arg, int flags)
- {
- struct iax2_peer *peer = obj;
- iax2_poke_peer(peer, 0);
- return 0;
- }
- static int iax2_poke_peer(struct iax2_peer *peer, int heldcall)
- {
- int callno;
- if (!peer->maxms || (!peer->addr.sin_addr.s_addr && !peer->dnsmgr)) {
- /* IF we have no IP without dnsmgr, or this isn't to be monitored, return
- immediately after clearing things out */
- peer->lastms = 0;
- peer->historicms = 0;
- peer->pokeexpire = -1;
- peer->callno = 0;
- return 0;
- }
- /* The peer could change the callno inside iax2_destroy, since we do deadlock avoidance */
- if ((callno = peer->callno) > 0) {
- ast_log(LOG_NOTICE, "Still have a callno...\n");
- ast_mutex_lock(&iaxsl[callno]);
- iax2_destroy(callno);
- ast_mutex_unlock(&iaxsl[callno]);
- }
- if (heldcall)
- ast_mutex_unlock(&iaxsl[heldcall]);
- callno = peer->callno = find_callno(0, 0, &peer->addr, NEW_FORCE, peer->sockfd, 0);
- if (heldcall)
- ast_mutex_lock(&iaxsl[heldcall]);
- if (peer->callno < 1) {
- ast_log(LOG_WARNING, "Unable to allocate call for poking peer '%s'\n", peer->name);
- return -1;
- }
- /* Speed up retransmission times for this qualify call */
- iaxs[peer->callno]->pingtime = peer->maxms / 4 + 1;
- iaxs[peer->callno]->peerpoke = peer;
- if (peer->pokeexpire > -1) {
- if (!ast_sched_del(sched, peer->pokeexpire)) {
- peer->pokeexpire = -1;
- peer_unref(peer);
- }
- }
-
- /* Queue up a new task to handle no reply */
- /* If the host is already unreachable then use the unreachable interval instead */
- if (peer->lastms < 0)
- peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_noanswer, peer_ref(peer));
- else
- peer->pokeexpire = iax2_sched_add(sched, DEFAULT_MAXMS * 2, iax2_poke_noanswer, peer_ref(peer));
- if (peer->pokeexpire == -1)
- peer_unref(peer);
- /* And send the poke */
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno]) {
- struct iax_ie_data ied = {
- .buf = { 0 },
- .pos = 0,
- };
- add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
- send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, ied.buf, ied.pos, -1);
- }
- ast_mutex_unlock(&iaxsl[callno]);
- return 0;
- }
- static void free_context(struct iax2_context *con)
- {
- struct iax2_context *conl;
- while(con) {
- conl = con;
- con = con->next;
- ast_free(conl);
- }
- }
- static struct ast_channel *iax2_request(const char *type, int format, void *data, int *cause)
- {
- int callno;
- int res;
- int fmt, native;
- struct sockaddr_in sin;
- struct ast_channel *c;
- struct parsed_dial_string pds;
- struct create_addr_info cai;
- char *tmpstr;
- memset(&pds, 0, sizeof(pds));
- tmpstr = ast_strdupa(data);
- parse_dial_string(tmpstr, &pds);
- if (ast_strlen_zero(pds.peer)) {
- ast_log(LOG_WARNING, "No peer provided in the IAX2 dial string '%s'\n", (char *) data);
- return NULL;
- }
-
- memset(&cai, 0, sizeof(cai));
- cai.capability = iax2_capability;
- ast_copy_flags(&cai, &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
-
- /* Populate our address from the given */
- if (create_addr(pds.peer, NULL, &sin, &cai)) {
- *cause = AST_CAUSE_UNREGISTERED;
- return NULL;
- }
- if (pds.port)
- sin.sin_port = htons(atoi(pds.port));
- callno = find_callno_locked(0, 0, &sin, NEW_FORCE, cai.sockfd, 0);
- if (callno < 1) {
- ast_log(LOG_WARNING, "Unable to create call\n");
- *cause = AST_CAUSE_CONGESTION;
- return NULL;
- }
- /* If this is a trunk, update it now */
- ast_copy_flags(iaxs[callno], &cai, IAX_TRUNK | IAX_SENDANI | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
- if (ast_test_flag(&cai, IAX_TRUNK)) {
- int new_callno;
- if ((new_callno = make_trunk(callno, 1)) != -1)
- callno = new_callno;
- }
- iaxs[callno]->maxtime = cai.maxtime;
- if (cai.found)
- ast_string_field_set(iaxs[callno], host, pds.peer);
- c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability);
- ast_mutex_unlock(&iaxsl[callno]);
- if (c) {
- /* Choose a format we can live with */
- if (c->nativeformats & format)
- c->nativeformats &= format;
- else {
- native = c->nativeformats;
- fmt = format;
- res = ast_translator_best_choice(&fmt, &native);
- if (res < 0) {
- ast_log(LOG_WARNING, "Unable to create translator path for %s to %s on %s\n",
- ast_getformatname(c->nativeformats), ast_getformatname(fmt), c->name);
- ast_hangup(c);
- return NULL;
- }
- c->nativeformats = native;
- }
- c->readformat = ast_best_codec(c->nativeformats);
- c->writeformat = c->readformat;
- }
- return c;
- }
- static void *sched_thread(void *ignore)
- {
- int count;
- int res;
- struct timeval tv;
- struct timespec ts;
- for (;;) {
- res = ast_sched_wait(sched);
- if ((res > 1000) || (res < 0))
- res = 1000;
- tv = ast_tvadd(ast_tvnow(), ast_samp2tv(res, 1000));
- ts.tv_sec = tv.tv_sec;
- ts.tv_nsec = tv.tv_usec * 1000;
- pthread_testcancel();
- ast_mutex_lock(&sched_lock);
- ast_cond_timedwait(&sched_cond, &sched_lock, &ts);
- ast_mutex_unlock(&sched_lock);
- pthread_testcancel();
- count = ast_sched_runq(sched);
- if (count >= 20)
- ast_debug(1, "chan_iax2: ast_sched_runq ran %d scheduled tasks all at once\n", count);
- }
- return NULL;
- }
- static void *network_thread(void *ignore)
- {
- /* Our job is simple: Send queued messages, retrying if necessary. Read frames
- from the network, and queue them for delivery to the channels */
- int res, count, wakeup;
- struct iax_frame *f;
- if (timingfd > -1)
- ast_io_add(io, timingfd, timing_read, AST_IO_IN | AST_IO_PRI, NULL);
-
- for(;;) {
- pthread_testcancel();
- /* Go through the queue, sending messages which have not yet been
- sent, and scheduling retransmissions if appropriate */
- AST_LIST_LOCK(&frame_queue);
- count = 0;
- wakeup = -1;
- AST_LIST_TRAVERSE_SAFE_BEGIN(&frame_queue, f, list) {
- if (f->sentyet)
- continue;
-
- /* Try to lock the pvt, if we can't... don't fret - defer it till later */
- if (ast_mutex_trylock(&iaxsl[f->callno])) {
- wakeup = 1;
- continue;
- }
- f->sentyet = 1;
- if (iaxs[f->callno]) {
- send_packet(f);
- count++;
- }
- ast_mutex_unlock(&iaxsl[f->callno]);
- if (f->retries < 0) {
- /* This is not supposed to be retransmitted */
- AST_LIST_REMOVE_CURRENT(list);
- /* Free the iax frame */
- iax_frame_free(f);
- } else {
- /* We need reliable delivery. Schedule a retransmission */
- f->retries++;
- f->retrans = iax2_sched_add(sched, f->retrytime, attempt_transmit, f);
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
- AST_LIST_UNLOCK(&frame_queue);
- pthread_testcancel();
- if (count >= 20)
- ast_debug(1, "chan_iax2: Sent %d queued outbound frames all at once\n", count);
- /* Now do the IO, and run scheduled tasks */
- res = ast_io_wait(io, wakeup);
- if (res >= 0) {
- if (res >= 20)
- ast_debug(1, "chan_iax2: ast_io_wait ran %d I/Os all at once\n", res);
- }
- }
- return NULL;
- }
- static int start_network_thread(void)
- {
- struct iax2_thread *thread;
- int threadcount = 0;
- int x;
- for (x = 0; x < iaxthreadcount; x++) {
- thread = ast_calloc(1, sizeof(*thread));
- if (thread) {
- thread->type = IAX_THREAD_TYPE_POOL;
- thread->threadnum = ++threadcount;
- ast_mutex_init(&thread->lock);
- ast_cond_init(&thread->cond, NULL);
- if (ast_pthread_create_detached(&thread->threadid, NULL, iax2_process_thread, thread)) {
- ast_log(LOG_WARNING, "Failed to create new thread!\n");
- ast_free(thread);
- thread = NULL;
- }
- AST_LIST_LOCK(&idle_list);
- AST_LIST_INSERT_TAIL(&idle_list, thread, list);
- AST_LIST_UNLOCK(&idle_list);
- }
- }
- ast_pthread_create_background(&schedthreadid, NULL, sched_thread, NULL);
- ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
- ast_verb(2, "%d helper threads started\n", threadcount);
- return 0;
- }
- static struct iax2_context *build_context(const char *context)
- {
- struct iax2_context *con;
- if ((con = ast_calloc(1, sizeof(*con))))
- ast_copy_string(con->context, context, sizeof(con->context));
-
- return con;
- }
- static int get_auth_methods(const char *value)
- {
- int methods = 0;
- if (strstr(value, "rsa"))
- methods |= IAX_AUTH_RSA;
- if (strstr(value, "md5"))
- methods |= IAX_AUTH_MD5;
- if (strstr(value, "plaintext"))
- methods |= IAX_AUTH_PLAINTEXT;
- return methods;
- }
- /*! \brief Check if address can be used as packet source.
- \return 0 address available, 1 address unavailable, -1 error
- */
- static int check_srcaddr(struct sockaddr *sa, socklen_t salen)
- {
- int sd;
- int res;
-
- sd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sd < 0) {
- ast_log(LOG_ERROR, "Socket: %s\n", strerror(errno));
- return -1;
- }
- res = bind(sd, sa, salen);
- if (res < 0) {
- ast_debug(1, "Can't bind: %s\n", strerror(errno));
- close(sd);
- return 1;
- }
- close(sd);
- return 0;
- }
- /*! \brief Parse the "sourceaddress" value,
- lookup in netsock list and set peer's sockfd. Defaults to defaultsockfd if
- not found. */
- static int peer_set_srcaddr(struct iax2_peer *peer, const char *srcaddr)
- {
- struct sockaddr_in sin;
- int nonlocal = 1;
- int port = IAX_DEFAULT_PORTNO;
- int sockfd = defaultsockfd;
- char *tmp;
- char *addr;
- char *portstr;
- if (!(tmp = ast_strdupa(srcaddr)))
- return -1;
- addr = strsep(&tmp, ":");
- portstr = tmp;
- if (portstr) {
- port = atoi(portstr);
- if (port < 1)
- port = IAX_DEFAULT_PORTNO;
- }
-
- if (!ast_get_ip(&sin, addr)) {
- struct ast_netsock *sock;
- int res;
- sin.sin_port = 0;
- sin.sin_family = AF_INET;
- res = check_srcaddr((struct sockaddr *) &sin, sizeof(sin));
- if (res == 0) {
- /* ip address valid. */
- sin.sin_port = htons(port);
- if (!(sock = ast_netsock_find(netsock, &sin)))
- sock = ast_netsock_find(outsock, &sin);
- if (sock) {
- sockfd = ast_netsock_sockfd(sock);
- nonlocal = 0;
- } else {
- unsigned int orig_saddr = sin.sin_addr.s_addr;
- /* INADDR_ANY matches anyway! */
- sin.sin_addr.s_addr = INADDR_ANY;
- if (ast_netsock_find(netsock, &sin)) {
- sin.sin_addr.s_addr = orig_saddr;
- sock = ast_netsock_bind(outsock, io, srcaddr, port, tos, cos, socket_read, NULL);
- if (sock) {
- sockfd = ast_netsock_sockfd(sock);
- ast_netsock_unref(sock);
- nonlocal = 0;
- } else {
- nonlocal = 2;
- }
- }
- }
- }
- }
-
- peer->sockfd = sockfd;
- if (nonlocal == 1) {
- ast_log(LOG_WARNING, "Non-local or unbound address specified (%s) in sourceaddress for '%s', reverting to default\n",
- srcaddr, peer->name);
- return -1;
- } else if (nonlocal == 2) {
- ast_log(LOG_WARNING, "Unable to bind to sourceaddress '%s' for '%s', reverting to default\n",
- srcaddr, peer->name);
- return -1;
- } else {
- ast_debug(1, "Using sourceaddress %s for '%s'\n", srcaddr, peer->name);
- return 0;
- }
- }
- static void peer_destructor(void *obj)
- {
- struct iax2_peer *peer = obj;
- int callno = peer->callno;
- ast_free_ha(peer->ha);
- if (callno > 0) {
- ast_mutex_lock(&iaxsl[callno]);
- iax2_destroy(callno);
- ast_mutex_unlock(&iaxsl[callno]);
- }
- register_peer_exten(peer, 0);
- if (peer->dnsmgr)
- ast_dnsmgr_release(peer->dnsmgr);
- if (peer->mwi_event_sub)
- ast_event_unsubscribe(peer->mwi_event_sub);
- ast_string_field_free_memory(peer);
- }
- /*! \brief Create peer structure based on configuration */
- static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly)
- {
- struct iax2_peer *peer = NULL;
- struct ast_ha *oldha = NULL;
- int maskfound = 0;
- int found = 0;
- int firstpass = 1;
- struct iax2_peer tmp_peer = {
- .name = name,
- };
- if (!temponly) {
- peer = ao2_find(peers, &tmp_peer, OBJ_POINTER);
- if (peer && !ast_test_flag(peer, IAX_DELME))
- firstpass = 0;
- }
- if (peer) {
- found++;
- if (firstpass) {
- oldha = peer->ha;
- peer->ha = NULL;
- }
- unlink_peer(peer);
- } else if ((peer = ao2_alloc(sizeof(*peer), peer_destructor))) {
- peer->expire = -1;
- peer->pokeexpire = -1;
- peer->sockfd = defaultsockfd;
- if (ast_string_field_init(peer, 32))
- peer = peer_unref(peer);
- }
- if (peer) {
- if (firstpass) {
- ast_copy_flags(peer, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
- peer->encmethods = iax2_encryption;
- peer->adsi = adsi;
- ast_string_field_set(peer,secret,"");
- if (!found) {
- ast_string_field_set(peer, name, name);
- peer->addr.sin_port = htons(IAX_DEFAULT_PORTNO);
- peer->expiry = min_reg_expire;
- }
- peer->prefs = prefs;
- peer->capability = iax2_capability;
- peer->smoothing = 0;
- peer->pokefreqok = DEFAULT_FREQ_OK;
- peer->pokefreqnotok = DEFAULT_FREQ_NOTOK;
- peer->maxcallno = 0;
- peercnt_modify(0, 0, &peer->addr);
- peer->calltoken_required = CALLTOKEN_DEFAULT;
- ast_string_field_set(peer,context,"");
- ast_string_field_set(peer,peercontext,"");
- ast_clear_flag(peer, IAX_HASCALLERID);
- ast_string_field_set(peer, cid_name, "");
- ast_string_field_set(peer, cid_num, "");
- ast_string_field_set(peer, mohinterpret, mohinterpret);
- ast_string_field_set(peer, mohsuggest, mohsuggest);
- }
- if (!v) {
- v = alt;
- alt = NULL;
- }
- while(v) {
- if (!strcasecmp(v->name, "secret")) {
- ast_string_field_set(peer, secret, v->value);
- } else if (!strcasecmp(v->name, "mailbox")) {
- ast_string_field_set(peer, mailbox, v->value);
- } else if (!strcasecmp(v->name, "hasvoicemail")) {
- if (ast_true(v->value) && ast_strlen_zero(peer->mailbox)) {
- ast_string_field_set(peer, mailbox, name);
- }
- } else if (!strcasecmp(v->name, "mohinterpret")) {
- ast_string_field_set(peer, mohinterpret, v->value);
- } else if (!strcasecmp(v->name, "mohsuggest")) {
- ast_string_field_set(peer, mohsuggest, v->value);
- } else if (!strcasecmp(v->name, "dbsecret")) {
- ast_string_field_set(peer, dbsecret, v->value);
- } else if (!strcasecmp(v->name, "trunk")) {
- ast_set2_flag(peer, ast_true(v->value), IAX_TRUNK);
- if (ast_test_flag(peer, IAX_TRUNK) && (timingfd < 0)) {
- ast_log(LOG_WARNING, "Unable to support trunking on peer '%s' without DAHDI timing\n", peer->name);
- ast_clear_flag(peer, IAX_TRUNK);
- }
- } else if (!strcasecmp(v->name, "auth")) {
- peer->authmethods = get_auth_methods(v->value);
- } else if (!strcasecmp(v->name, "encryption")) {
- peer->encmethods = get_encrypt_methods(v->value);
- } else if (!strcasecmp(v->name, "transfer")) {
- if (!strcasecmp(v->value, "mediaonly")) {
- ast_set_flags_to(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA);
- } else if (ast_true(v->value)) {
- ast_set_flags_to(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0);
- } else
- ast_set_flags_to(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER);
- } else if (!strcasecmp(v->name, "jitterbuffer")) {
- ast_set2_flag(peer, ast_true(v->value), IAX_USEJITTERBUF);
- } else if (!strcasecmp(v->name, "forcejitterbuffer")) {
- ast_set2_flag(peer, ast_true(v->value), IAX_FORCEJITTERBUF);
- } else if (!strcasecmp(v->name, "host")) {
- if (!strcasecmp(v->value, "dynamic")) {
- /* They'll register with us */
- ast_set_flag(peer, IAX_DYNAMIC);
- if (!found) {
- /* Initialize stuff iff we're not found, otherwise
- we keep going with what we had */
- memset(&peer->addr.sin_addr, 0, 4);
- if (peer->addr.sin_port) {
- /* If we've already got a port, make it the default rather than absolute */
- peer->defaddr.sin_port = peer->addr.sin_port;
- peer->addr.sin_port = 0;
- }
- }
- } else {
- /* Non-dynamic. Make sure we become that way if we're not */
- AST_SCHED_DEL(sched, peer->expire);
- ast_clear_flag(peer, IAX_DYNAMIC);
- if (ast_dnsmgr_lookup(v->value, &peer->addr.sin_addr, &peer->dnsmgr))
- return peer_unref(peer);
- if (!peer->addr.sin_port)
- peer->addr.sin_port = htons(IAX_DEFAULT_PORTNO);
- }
- if (!maskfound)
- inet_aton("255.255.255.255", &peer->mask);
- } else if (!strcasecmp(v->name, "defaultip")) {
- if (ast_get_ip(&peer->defaddr, v->value))
- return peer_unref(peer);
- } else if (!strcasecmp(v->name, "sourceaddress")) {
- peer_set_srcaddr(peer, v->value);
- } else if (!strcasecmp(v->name, "permit") ||
- !strcasecmp(v->name, "deny")) {
- peer->ha = ast_append_ha(v->name, v->value, peer->ha, NULL);
- } else if (!strcasecmp(v->name, "mask")) {
- maskfound++;
- inet_aton(v->value, &peer->mask);
- } else if (!strcasecmp(v->name, "context")) {
- ast_string_field_set(peer, context, v->value);
- } else if (!strcasecmp(v->name, "regexten")) {
- ast_string_field_set(peer, regexten, v->value);
- } else if (!strcasecmp(v->name, "peercontext")) {
- ast_string_field_set(peer, peercontext, v->value);
- } else if (!strcasecmp(v->name, "port")) {
- if (ast_test_flag(peer, IAX_DYNAMIC))
- peer->defaddr.sin_port = htons(atoi(v->value));
- else
- peer->addr.sin_port = htons(atoi(v->value));
- } else if (!strcasecmp(v->name, "username")) {
- ast_string_field_set(peer, username, v->value);
- } else if (!strcasecmp(v->name, "allow")) {
- ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 1);
- } else if (!strcasecmp(v->name, "disallow")) {
- ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 0);
- } else if (!strcasecmp(v->name, "callerid")) {
- if (!ast_strlen_zero(v->value)) {
- char name2[80];
- char num2[80];
- ast_callerid_split(v->value, name2, sizeof(name2), num2, sizeof(num2));
- ast_string_field_set(peer, cid_name, name2);
- ast_string_field_set(peer, cid_num, num2);
- } else {
- ast_string_field_set(peer, cid_name, "");
- ast_string_field_set(peer, cid_num, "");
- }
- ast_set_flag(peer, IAX_HASCALLERID);
- } else if (!strcasecmp(v->name, "fullname")) {
- ast_string_field_set(peer, cid_name, S_OR(v->value, ""));
- ast_set_flag(peer, IAX_HASCALLERID);
- } else if (!strcasecmp(v->name, "cid_number")) {
- ast_string_field_set(peer, cid_num, S_OR(v->value, ""));
- ast_set_flag(peer, IAX_HASCALLERID);
- } else if (!strcasecmp(v->name, "sendani")) {
- ast_set2_flag(peer, ast_true(v->value), IAX_SENDANI);
- } else if (!strcasecmp(v->name, "inkeys")) {
- ast_string_field_set(peer, inkeys, v->value);
- } else if (!strcasecmp(v->name, "outkey")) {
- ast_string_field_set(peer, outkey, v->value);
- } else if (!strcasecmp(v->name, "qualify")) {
- if (!strcasecmp(v->value, "no")) {
- peer->maxms = 0;
- } else if (!strcasecmp(v->value, "yes")) {
- peer->maxms = DEFAULT_MAXMS;
- } else if (sscanf(v->value, "%30d", &peer->maxms) != 1) {
- ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of iax.conf\n", peer->name, v->lineno);
- peer->maxms = 0;
- }
- } else if (!strcasecmp(v->name, "qualifysmoothing")) {
- peer->smoothing = ast_true(v->value);
- } else if (!strcasecmp(v->name, "qualifyfreqok")) {
- if (sscanf(v->value, "%30d", &peer->pokefreqok) != 1) {
- ast_log(LOG_WARNING, "Qualification testing frequency of peer '%s' when OK should a number of milliseconds at line %d of iax.conf\n", peer->name, v->lineno);
- }
- } else if (!strcasecmp(v->name, "qualifyfreqnotok")) {
- if (sscanf(v->value, "%30d", &peer->pokefreqnotok) != 1) {
- ast_log(LOG_WARNING, "Qualification testing frequency of peer '%s' when NOT OK should be a number of milliseconds at line %d of iax.conf\n", peer->name, v->lineno);
- } else ast_log(LOG_WARNING, "Set peer->pokefreqnotok to %d\n", peer->pokefreqnotok);
- } else if (!strcasecmp(v->name, "timezone")) {
- ast_string_field_set(peer, zonetag, v->value);
- } else if (!strcasecmp(v->name, "adsi")) {
- peer->adsi = ast_true(v->value);
- } else if (!strcasecmp(v->name, "maxcallnumbers")) {
- if (sscanf(v->value, "%10hu", &peer->maxcallno) != 1) {
- ast_log(LOG_WARNING, "maxcallnumbers must be set to a valid number. %s is not valid at line %d.\n", v->value, v->lineno);
- } else {
- peercnt_modify(1, peer->maxcallno, &peer->addr);
- }
- } else if (!strcasecmp(v->name, "requirecalltoken")) {
- /* default is required unless in optional ip list */
- if (ast_false(v->value)) {
- peer->calltoken_required = CALLTOKEN_NO;
- } else if (!strcasecmp(v->value, "auto")) {
- peer->calltoken_required = CALLTOKEN_AUTO;
- } else if (ast_true(v->value)) {
- peer->calltoken_required = CALLTOKEN_YES;
- } else {
- ast_log(LOG_WARNING, "requirecalltoken must be set to a valid value. at line %d\n", v->lineno);
- }
- } /* else if (strcasecmp(v->name,"type")) */
- /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
- v = v->next;
- if (!v) {
- v = alt;
- alt = NULL;
- }
- }
- if (!peer->authmethods)
- peer->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT;
- ast_clear_flag(peer, IAX_DELME);
- /* Make sure these are IPv4 addresses */
- peer->addr.sin_family = AF_INET;
- }
- if (oldha)
- ast_free_ha(oldha);
- if (!ast_strlen_zero(peer->mailbox)) {
- char *mailbox, *context;
- context = mailbox = ast_strdupa(peer->mailbox);
- strsep(&context, "@");
- if (ast_strlen_zero(context))
- context = "default";
- peer->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, NULL,
- AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
- AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
- AST_EVENT_IE_END);
- }
- return peer;
- }
- static void user_destructor(void *obj)
- {
- struct iax2_user *user = obj;
- ast_free_ha(user->ha);
- free_context(user->contexts);
- if(user->vars) {
- ast_variables_destroy(user->vars);
- user->vars = NULL;
- }
- ast_string_field_free_memory(user);
- }
- /*! \brief Create in-memory user structure from configuration */
- static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly)
- {
- struct iax2_user *user = NULL;
- struct iax2_context *con, *conl = NULL;
- struct ast_ha *oldha = NULL;
- struct iax2_context *oldcon = NULL;
- int format;
- int firstpass=1;
- int oldcurauthreq = 0;
- char *varname = NULL, *varval = NULL;
- struct ast_variable *tmpvar = NULL;
- struct iax2_user tmp_user = {
- .name = name,
- };
- if (!temponly) {
- user = ao2_find(users, &tmp_user, OBJ_POINTER);
- if (user && !ast_test_flag(user, IAX_DELME))
- firstpass = 0;
- }
- if (user) {
- if (firstpass) {
- oldcurauthreq = user->curauthreq;
- oldha = user->ha;
- oldcon = user->contexts;
- user->ha = NULL;
- user->contexts = NULL;
- }
- /* Already in the list, remove it and it will be added back (or FREE'd) */
- ao2_unlink(users, user);
- } else {
- user = ao2_alloc(sizeof(*user), user_destructor);
- }
-
- if (user) {
- if (firstpass) {
- ast_string_field_free_memory(user);
- memset(user, 0, sizeof(struct iax2_user));
- if (ast_string_field_init(user, 32)) {
- user = user_unref(user);
- goto cleanup;
- }
- user->maxauthreq = maxauthreq;
- user->curauthreq = oldcurauthreq;
- user->prefs = prefs;
- user->capability = iax2_capability;
- user->encmethods = iax2_encryption;
- user->adsi = adsi;
- user->calltoken_required = CALLTOKEN_DEFAULT;
- ast_string_field_set(user, name, name);
- ast_string_field_set(user, language, language);
- ast_copy_flags(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP);
- ast_clear_flag(user, IAX_HASCALLERID);
- ast_string_field_set(user, cid_name, "");
- ast_string_field_set(user, cid_num, "");
- ast_string_field_set(user, accountcode, accountcode);
- ast_string_field_set(user, mohinterpret, mohinterpret);
- ast_string_field_set(user, mohsuggest, mohsuggest);
- }
- if (!v) {
- v = alt;
- alt = NULL;
- }
- while(v) {
- if (!strcasecmp(v->name, "context")) {
- con = build_context(v->value);
- if (con) {
- if (conl)
- conl->next = con;
- else
- user->contexts = con;
- conl = con;
- }
- } else if (!strcasecmp(v->name, "permit") ||
- !strcasecmp(v->name, "deny")) {
- user->ha = ast_append_ha(v->name, v->value, user->ha, NULL);
- } else if (!strcasecmp(v->name, "setvar")) {
- varname = ast_strdupa(v->value);
- if (varname && (varval = strchr(varname,'='))) {
- *varval = '\0';
- varval++;
- if((tmpvar = ast_variable_new(varname, varval, ""))) {
- tmpvar->next = user->vars;
- user->vars = tmpvar;
- }
- }
- } else if (!strcasecmp(v->name, "allow")) {
- ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, 1);
- } else if (!strcasecmp(v->name, "disallow")) {
- ast_parse_allow_disallow(&user->prefs, &user->capability,v->value, 0);
- } else if (!strcasecmp(v->name, "trunk")) {
- ast_set2_flag(user, ast_true(v->value), IAX_TRUNK);
- if (ast_test_flag(user, IAX_TRUNK) && (timingfd < 0)) {
- ast_log(LOG_WARNING, "Unable to support trunking on user '%s' without DAHDI timing\n", user->name);
- ast_clear_flag(user, IAX_TRUNK);
- }
- } else if (!strcasecmp(v->name, "auth")) {
- user->authmethods = get_auth_methods(v->value);
- } else if (!strcasecmp(v->name, "encryption")) {
- user->encmethods = get_encrypt_methods(v->value);
- } else if (!strcasecmp(v->name, "transfer")) {
- if (!strcasecmp(v->value, "mediaonly")) {
- ast_set_flags_to(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA);
- } else if (ast_true(v->value)) {
- ast_set_flags_to(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0);
- } else
- ast_set_flags_to(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER);
- } else if (!strcasecmp(v->name, "codecpriority")) {
- if(!strcasecmp(v->value, "caller"))
- ast_set_flag(user, IAX_CODEC_USER_FIRST);
- else if(!strcasecmp(v->value, "disabled"))
- ast_set_flag(user, IAX_CODEC_NOPREFS);
- else if(!strcasecmp(v->value, "reqonly")) {
- ast_set_flag(user, IAX_CODEC_NOCAP);
- ast_set_flag(user, IAX_CODEC_NOPREFS);
- }
- } else if (!strcasecmp(v->name, "jitterbuffer")) {
- ast_set2_flag(user, ast_true(v->value), IAX_USEJITTERBUF);
- } else if (!strcasecmp(v->name, "forcejitterbuffer")) {
- ast_set2_flag(user, ast_true(v->value), IAX_FORCEJITTERBUF);
- } else if (!strcasecmp(v->name, "dbsecret")) {
- ast_string_field_set(user, dbsecret, v->value);
- } else if (!strcasecmp(v->name, "secret")) {
- if (!ast_strlen_zero(user->secret)) {
- char *old = ast_strdupa(user->secret);
- ast_string_field_build(user, secret, "%s;%s", old, v->value);
- } else
- ast_string_field_set(user, secret, v->value);
- } else if (!strcasecmp(v->name, "callerid")) {
- if (!ast_strlen_zero(v->value) && strcasecmp(v->value, "asreceived")) {
- char name2[80];
- char num2[80];
- ast_callerid_split(v->value, name2, sizeof(name2), num2, sizeof(num2));
- ast_string_field_set(user, cid_name, name2);
- ast_string_field_set(user, cid_num, num2);
- ast_set_flag(user, IAX_HASCALLERID);
- } else {
- ast_clear_flag(user, IAX_HASCALLERID);
- ast_string_field_set(user, cid_name, "");
- ast_string_field_set(user, cid_num, "");
- }
- } else if (!strcasecmp(v->name, "fullname")) {
- if (!ast_strlen_zero(v->value)) {
- ast_string_field_set(user, cid_name, v->value);
- ast_set_flag(user, IAX_HASCALLERID);
- } else {
- ast_string_field_set(user, cid_name, "");
- if (ast_strlen_zero(user->cid_num))
- ast_clear_flag(user, IAX_HASCALLERID);
- }
- } else if (!strcasecmp(v->name, "cid_number")) {
- if (!ast_strlen_zero(v->value)) {
- ast_string_field_set(user, cid_num, v->value);
- ast_set_flag(user, IAX_HASCALLERID);
- } else {
- ast_string_field_set(user, cid_num, "");
- if (ast_strlen_zero(user->cid_name))
- ast_clear_flag(user, IAX_HASCALLERID);
- }
- } else if (!strcasecmp(v->name, "accountcode")) {
- ast_string_field_set(user, accountcode, v->value);
- } else if (!strcasecmp(v->name, "mohinterpret")) {
- ast_string_field_set(user, mohinterpret, v->value);
- } else if (!strcasecmp(v->name, "mohsuggest")) {
- ast_string_field_set(user, mohsuggest, v->value);
- } else if (!strcasecmp(v->name, "language")) {
- ast_string_field_set(user, language, v->value);
- } else if (!strcasecmp(v->name, "amaflags")) {
- format = ast_cdr_amaflags2int(v->value);
- if (format < 0) {
- ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
- } else {
- user->amaflags = format;
- }
- } else if (!strcasecmp(v->name, "inkeys")) {
- ast_string_field_set(user, inkeys, v->value);
- } else if (!strcasecmp(v->name, "maxauthreq")) {
- user->maxauthreq = atoi(v->value);
- if (user->maxauthreq < 0)
- user->maxauthreq = 0;
- } else if (!strcasecmp(v->name, "adsi")) {
- user->adsi = ast_true(v->value);
- } else if (!strcasecmp(v->name, "requirecalltoken")) {
- /* default is required unless in optional ip list */
- if (ast_false(v->value)) {
- user->calltoken_required = CALLTOKEN_NO;
- } else if (!strcasecmp(v->value, "auto")) {
- user->calltoken_required = CALLTOKEN_AUTO;
- } else if (ast_true(v->value)) {
- user->calltoken_required = CALLTOKEN_YES;
- } else {
- ast_log(LOG_WARNING, "requirecalltoken must be set to a valid value. at line %d\n", v->lineno);
- }
- } /* else if (strcasecmp(v->name,"type")) */
- /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
- v = v->next;
- if (!v) {
- v = alt;
- alt = NULL;
- }
- }
- if (!user->authmethods) {
- if (!ast_strlen_zero(user->secret)) {
- user->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT;
- if (!ast_strlen_zero(user->inkeys))
- user->authmethods |= IAX_AUTH_RSA;
- } else if (!ast_strlen_zero(user->inkeys)) {
- user->authmethods = IAX_AUTH_RSA;
- } else {
- user->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT;
- }
- }
- ast_clear_flag(user, IAX_DELME);
- }
- cleanup:
- if (oldha)
- ast_free_ha(oldha);
- if (oldcon)
- free_context(oldcon);
- return user;
- }
- static int peer_delme_cb(void *obj, void *arg, int flags)
- {
- struct iax2_peer *peer = obj;
- ast_set_flag(peer, IAX_DELME);
- return 0;
- }
- static int user_delme_cb(void *obj, void *arg, int flags)
- {
- struct iax2_user *user = obj;
- ast_set_flag(user, IAX_DELME);
- return 0;
- }
- static void delete_users(void)
- {
- struct iax2_registry *reg;
- ao2_callback(users, 0, user_delme_cb, NULL);
- AST_LIST_LOCK(®istrations);
- while ((reg = AST_LIST_REMOVE_HEAD(®istrations, entry))) {
- AST_SCHED_DEL(sched, reg->expire);
- if (reg->callno) {
- int callno = reg->callno;
- ast_mutex_lock(&iaxsl[callno]);
- if (iaxs[callno]) {
- iaxs[callno]->reg = NULL;
- iax2_destroy(callno);
- }
- ast_mutex_unlock(&iaxsl[callno]);
- }
- if (reg->dnsmgr)
- ast_dnsmgr_release(reg->dnsmgr);
- ast_free(reg);
- }
- AST_LIST_UNLOCK(®istrations);
- ao2_callback(peers, 0, peer_delme_cb, NULL);
- }
- static void prune_users(void)
- {
- struct iax2_user *user;
- struct ao2_iterator i;
- i = ao2_iterator_init(users, 0);
- while ((user = ao2_iterator_next(&i))) {
- if (ast_test_flag(user, IAX_DELME) || ast_test_flag(user, IAX_RTCACHEFRIENDS)) {
- ao2_unlink(users, user);
- }
- user_unref(user);
- }
- ao2_iterator_destroy(&i);
- }
- /* Prune peers who still are supposed to be deleted */
- static void prune_peers(void)
- {
- struct iax2_peer *peer;
- struct ao2_iterator i;
- i = ao2_iterator_init(peers, 0);
- while ((peer = ao2_iterator_next(&i))) {
- if (ast_test_flag(peer, IAX_DELME) || ast_test_flag(peer, IAX_RTCACHEFRIENDS)) {
- unlink_peer(peer);
- }
- peer_unref(peer);
- }
- ao2_iterator_destroy(&i);
- }
- static void set_timing(void)
- {
- #ifdef HAVE_DAHDI
- int bs = trunkfreq * 8;
- if (timingfd > -1) {
- if (
- #ifdef DAHDI_TIMERACK
- ioctl(timingfd, DAHDI_TIMERCONFIG, &bs) &&
- #endif
- ioctl(timingfd, DAHDI_SET_BLOCKSIZE, &bs))
- ast_log(LOG_WARNING, "Unable to set blocksize on timing source\n");
- }
- #endif
- }
- static void set_config_destroy(void)
- {
- strcpy(accountcode, "");
- strcpy(language, "");
- strcpy(mohinterpret, "default");
- strcpy(mohsuggest, "");
- trunkmaxsize = MAX_TRUNKDATA;
- amaflags = 0;
- delayreject = 0;
- ast_clear_flag((&globalflags), IAX_NOTRANSFER);
- ast_clear_flag((&globalflags), IAX_TRANSFERMEDIA);
- ast_clear_flag((&globalflags), IAX_USEJITTERBUF);
- ast_clear_flag((&globalflags), IAX_FORCEJITTERBUF);
- delete_users();
- ao2_callback(callno_limits, OBJ_NODATA, addr_range_delme_cb, NULL);
- ao2_callback(calltoken_ignores, OBJ_NODATA, addr_range_delme_cb, NULL);
- }
- /*! \brief Load configuration */
- static int set_config(const char *config_file, int reload)
- {
- struct ast_config *cfg, *ucfg;
- int capability=iax2_capability;
- struct ast_variable *v;
- char *cat;
- const char *utype;
- const char *tosval;
- int format;
- int portno = IAX_DEFAULT_PORTNO;
- int x;
- int mtuv;
- struct iax2_user *user;
- struct iax2_peer *peer;
- struct ast_netsock *ns;
- struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
- #if 0
- static unsigned short int last_port=0;
- #endif
- cfg = ast_config_load(config_file, config_flags);
- if (!cfg) {
- ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
- return -1;
- } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
- ucfg = ast_config_load("users.conf", config_flags);
- if (ucfg == CONFIG_STATUS_FILEUNCHANGED)
- return 0;
- /* Otherwise we need to reread both files */
- ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
- cfg = ast_config_load(config_file, config_flags);
- } else { /* iax.conf changed, gotta reread users.conf, too */
- ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
- ucfg = ast_config_load("users.conf", config_flags);
- }
- if (reload) {
- set_config_destroy();
- }
- /* Reset global codec prefs */
- memset(&prefs, 0 , sizeof(struct ast_codec_pref));
- /* Reset Global Flags */
- memset(&globalflags, 0, sizeof(globalflags));
- ast_set_flag(&globalflags, IAX_RTUPDATE);
- ast_set_flag(&globalflags, IAX_SHRINKCALLERID);
- #ifdef SO_NO_CHECK
- nochecksums = 0;
- #endif
- min_reg_expire = IAX_DEFAULT_REG_EXPIRE;
- max_reg_expire = IAX_DEFAULT_REG_EXPIRE;
- global_max_trunk_mtu = MAX_TRUNK_MTU;
- global_maxcallno = DEFAULT_MAXCALLNO_LIMIT;
- global_maxcallno_nonval = DEFAULT_MAXCALLNO_LIMIT_NONVAL;
- maxauthreq = 3;
- srvlookup = 0;
- v = ast_variable_browse(cfg, "general");
- /* Seed initial tos value */
- tosval = ast_variable_retrieve(cfg, "general", "tos");
- if (tosval) {
- if (ast_str2tos(tosval, &tos))
- ast_log(LOG_WARNING, "Invalid tos value, refer to QoS documentation\n");
- }
- /* Seed initial cos value */
- tosval = ast_variable_retrieve(cfg, "general", "cos");
- if (tosval) {
- if (ast_str2cos(tosval, &cos))
- ast_log(LOG_WARNING, "Invalid cos value, refer to QoS documentation\n");
- }
- while(v) {
- if (!strcasecmp(v->name, "bindport")){
- if (reload)
- ast_log(LOG_NOTICE, "Ignoring bindport on reload\n");
- else
- portno = atoi(v->value);
- } else if (!strcasecmp(v->name, "pingtime"))
- ping_time = atoi(v->value);
- else if (!strcasecmp(v->name, "iaxthreadcount")) {
- if (reload) {
- if (atoi(v->value) != iaxthreadcount)
- ast_log(LOG_NOTICE, "Ignoring any changes to iaxthreadcount during reload\n");
- } else {
- iaxthreadcount = atoi(v->value);
- if (iaxthreadcount < 1) {
- ast_log(LOG_NOTICE, "iaxthreadcount must be at least 1.\n");
- iaxthreadcount = 1;
- } else if (iaxthreadcount > 256) {
- ast_log(LOG_NOTICE, "limiting iaxthreadcount to 256\n");
- iaxthreadcount = 256;
- }
- }
- } else if (!strcasecmp(v->name, "iaxmaxthreadcount")) {
- if (reload) {
- AST_LIST_LOCK(&dynamic_list);
- iaxmaxthreadcount = atoi(v->value);
- AST_LIST_UNLOCK(&dynamic_list);
- } else {
- iaxmaxthreadcount = atoi(v->value);
- if (iaxmaxthreadcount < 0) {
- ast_log(LOG_NOTICE, "iaxmaxthreadcount must be at least 0.\n");
- iaxmaxthreadcount = 0;
- } else if (iaxmaxthreadcount > 256) {
- ast_log(LOG_NOTICE, "Limiting iaxmaxthreadcount to 256\n");
- iaxmaxthreadcount = 256;
- }
- }
- } else if (!strcasecmp(v->name, "nochecksums")) {
- #ifdef SO_NO_CHECK
- if (ast_true(v->value))
- nochecksums = 1;
- else
- nochecksums = 0;
- #else
- if (ast_true(v->value))
- ast_log(LOG_WARNING, "Disabling RTP checksums is not supported on this operating system!\n");
- #endif
- }
- else if (!strcasecmp(v->name, "maxjitterbuffer"))
- maxjitterbuffer = atoi(v->value);
- else if (!strcasecmp(v->name, "resyncthreshold"))
- resyncthreshold = atoi(v->value);
- else if (!strcasecmp(v->name, "maxjitterinterps"))
- maxjitterinterps = atoi(v->value);
- else if (!strcasecmp(v->name, "jittertargetextra"))
- jittertargetextra = atoi(v->value);
- else if (!strcasecmp(v->name, "lagrqtime"))
- lagrq_time = atoi(v->value);
- else if (!strcasecmp(v->name, "maxregexpire"))
- max_reg_expire = atoi(v->value);
- else if (!strcasecmp(v->name, "minregexpire"))
- min_reg_expire = atoi(v->value);
- else if (!strcasecmp(v->name, "bindaddr")) {
- if (reload) {
- ast_log(LOG_NOTICE, "Ignoring bindaddr on reload\n");
- } else {
- if (!(ns = ast_netsock_bind(netsock, io, v->value, portno, tos, cos, socket_read, NULL))) {
- ast_log(LOG_WARNING, "Unable apply binding to '%s' at line %d\n", v->value, v->lineno);
- } else {
- if (strchr(v->value, ':'))
- ast_verb(2, "Binding IAX2 to '%s'\n", v->value);
- else
- ast_verb(2, "Binding IAX2 to '%s:%d'\n", v->value, portno);
- if (defaultsockfd < 0)
- defaultsockfd = ast_netsock_sockfd(ns);
- ast_netsock_unref(ns);
- }
- }
- } else if (!strcasecmp(v->name, "authdebug"))
- authdebug = ast_true(v->value);
- else if (!strcasecmp(v->name, "encryption"))
- iax2_encryption = get_encrypt_methods(v->value);
- else if (!strcasecmp(v->name, "transfer")) {
- if (!strcasecmp(v->value, "mediaonly")) {
- ast_set_flags_to((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA);
- } else if (ast_true(v->value)) {
- ast_set_flags_to((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0);
- } else
- ast_set_flags_to((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER);
- } else if (!strcasecmp(v->name, "codecpriority")) {
- if(!strcasecmp(v->value, "caller"))
- ast_set_flag((&globalflags), IAX_CODEC_USER_FIRST);
- else if(!strcasecmp(v->value, "disabled"))
- ast_set_flag((&globalflags), IAX_CODEC_NOPREFS);
- else if(!strcasecmp(v->value, "reqonly")) {
- ast_set_flag((&globalflags), IAX_CODEC_NOCAP);
- ast_set_flag((&globalflags), IAX_CODEC_NOPREFS);
- }
- } else if (!strcasecmp(v->name, "jitterbuffer"))
- ast_set2_flag((&globalflags), ast_true(v->value), IAX_USEJITTERBUF);
- else if (!strcasecmp(v->name, "forcejitterbuffer"))
- ast_set2_flag((&globalflags), ast_true(v->value), IAX_FORCEJITTERBUF);
- else if (!strcasecmp(v->name, "delayreject"))
- delayreject = ast_true(v->value);
- else if (!strcasecmp(v->name, "allowfwdownload"))
- ast_set2_flag((&globalflags), ast_true(v->value), IAX_ALLOWFWDOWNLOAD);
- else if (!strcasecmp(v->name, "rtcachefriends"))
- ast_set2_flag((&globalflags), ast_true(v->value), IAX_RTCACHEFRIENDS);
- else if (!strcasecmp(v->name, "rtignoreregexpire"))
- ast_set2_flag((&globalflags), ast_true(v->value), IAX_RTIGNOREREGEXPIRE);
- else if (!strcasecmp(v->name, "rtupdate"))
- ast_set2_flag((&globalflags), ast_true(v->value), IAX_RTUPDATE);
- else if (!strcasecmp(v->name, "trunktimestamps"))
- ast_set2_flag(&globalflags, ast_true(v->value), IAX_TRUNKTIMESTAMPS);
- else if (!strcasecmp(v->name, "rtautoclear")) {
- int i = atoi(v->value);
- if(i > 0)
- global_rtautoclear = i;
- else
- i = 0;
- ast_set2_flag((&globalflags), i || ast_true(v->value), IAX_RTAUTOCLEAR);
- } else if (!strcasecmp(v->name, "trunkfreq")) {
- trunkfreq = atoi(v->value);
- if (trunkfreq < 10)
- trunkfreq = 10;
- } else if (!strcasecmp(v->name, "trunkmtu")) {
- mtuv = atoi(v->value);
- if (mtuv == 0 )
- global_max_trunk_mtu = 0;
- else if (mtuv >= 172 && mtuv < 4000)
- global_max_trunk_mtu = mtuv;
- else
- ast_log(LOG_NOTICE, "trunkmtu value out of bounds (%d) at line %d\n",
- mtuv, v->lineno);
- } else if (!strcasecmp(v->name, "trunkmaxsize")) {
- trunkmaxsize = atoi(v->value);
- if (trunkmaxsize == 0)
- trunkmaxsize = MAX_TRUNKDATA;
- } else if (!strcasecmp(v->name, "autokill")) {
- if (sscanf(v->value, "%30d", &x) == 1) {
- if (x >= 0)
- autokill = x;
- else
- ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
- } else if (ast_true(v->value)) {
- autokill = DEFAULT_MAXMS;
- } else {
- autokill = 0;
- }
- } else if (!strcasecmp(v->name, "bandwidth")) {
- if (!strcasecmp(v->value, "low")) {
- capability = IAX_CAPABILITY_LOWBANDWIDTH;
- } else if (!strcasecmp(v->value, "medium")) {
- capability = IAX_CAPABILITY_MEDBANDWIDTH;
- } else if (!strcasecmp(v->value, "high")) {
- capability = IAX_CAPABILITY_FULLBANDWIDTH;
- } else
- ast_log(LOG_WARNING, "bandwidth must be either low, medium, or high\n");
- } else if (!strcasecmp(v->name, "allow")) {
- ast_parse_allow_disallow(&prefs, &capability, v->value, 1);
- } else if (!strcasecmp(v->name, "disallow")) {
- ast_parse_allow_disallow(&prefs, &capability, v->value, 0);
- } else if (!strcasecmp(v->name, "register")) {
- iax2_register(v->value, v->lineno);
- } else if (!strcasecmp(v->name, "iaxcompat")) {
- iaxcompat = ast_true(v->value);
- } else if (!strcasecmp(v->name, "regcontext")) {
- ast_copy_string(regcontext, v->value, sizeof(regcontext));
- /* Create context if it doesn't exist already */
- ast_context_find_or_create(NULL, NULL, regcontext, "IAX2");
- } else if (!strcasecmp(v->name, "tos")) {
- if (ast_str2tos(v->value, &tos))
- ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
- } else if (!strcasecmp(v->name, "cos")) {
- if (ast_str2cos(v->value, &cos))
- ast_log(LOG_WARNING, "Invalid cos value at line %d, refer to QoS documentation\n", v->lineno);
- } else if (!strcasecmp(v->name, "accountcode")) {
- ast_copy_string(accountcode, v->value, sizeof(accountcode));
- } else if (!strcasecmp(v->name, "mohinterpret")) {
- ast_copy_string(mohinterpret, v->value, sizeof(mohinterpret));
- } else if (!strcasecmp(v->name, "mohsuggest")) {
- ast_copy_string(mohsuggest, v->value, sizeof(mohsuggest));
- } else if (!strcasecmp(v->name, "amaflags")) {
- format = ast_cdr_amaflags2int(v->value);
- if (format < 0) {
- ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
- } else {
- amaflags = format;
- }
- } else if (!strcasecmp(v->name, "language")) {
- ast_copy_string(language, v->value, sizeof(language));
- } else if (!strcasecmp(v->name, "maxauthreq")) {
- maxauthreq = atoi(v->value);
- if (maxauthreq < 0)
- maxauthreq = 0;
- } else if (!strcasecmp(v->name, "adsi")) {
- adsi = ast_true(v->value);
- } else if (!strcasecmp(v->name, "srvlookup")) {
- srvlookup = ast_true(v->value);
- } else if (!strcasecmp(v->name, "maxcallnumbers")) {
- if (sscanf(v->value, "%10hu", &global_maxcallno) != 1) {
- ast_log(LOG_WARNING, "maxcallnumbers must be set to a valid number. %s is not valid at line %d\n", v->value, v->lineno);
- }
- } else if (!strcasecmp(v->name, "maxcallnumbers_nonvalidated")) {
- if (sscanf(v->value, "%10hu", &global_maxcallno_nonval) != 1) {
- ast_log(LOG_WARNING, "maxcallnumbers_nonvalidated must be set to a valid number. %s is not valid at line %d.\n", v->value, v->lineno);
- }
- } else if (!strcasecmp(v->name, "calltokenoptional")) {
- if (add_calltoken_ignore(v->value)) {
- ast_log(LOG_WARNING, "Invalid calltokenoptional address range - '%s' line %d\n", v->value, v->lineno);
- }
- } else if (!strcasecmp(v->name, "shrinkcallerid")) {
- if (ast_true(v->value)) {
- ast_set_flag((&globalflags), IAX_SHRINKCALLERID);
- } else if (ast_false(v->value)) {
- ast_clear_flag((&globalflags), IAX_SHRINKCALLERID);
- } else {
- ast_log(LOG_WARNING, "shrinkcallerid value %s is not valid at line %d.\n", v->value, v->lineno);
- }
- }/*else if (strcasecmp(v->name,"type")) */
- /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
- v = v->next;
- }
-
- if (defaultsockfd < 0) {
- if (!(ns = ast_netsock_bind(netsock, io, "0.0.0.0", portno, tos, cos, socket_read, NULL))) {
- ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
- } else {
- ast_verb(2, "Binding IAX2 to default address 0.0.0.0:%d\n", portno);
- defaultsockfd = ast_netsock_sockfd(ns);
- ast_netsock_unref(ns);
- }
- }
- if (reload) {
- ast_netsock_release(outsock);
- outsock = ast_netsock_list_alloc();
- if (!outsock) {
- ast_log(LOG_ERROR, "Could not allocate outsock list.\n");
- return -1;
- }
- ast_netsock_init(outsock);
- }
- if (min_reg_expire > max_reg_expire) {
- ast_log(LOG_WARNING, "Minimum registration interval of %d is more than maximum of %d, resetting minimum to %d\n",
- min_reg_expire, max_reg_expire, max_reg_expire);
- min_reg_expire = max_reg_expire;
- }
- iax2_capability = capability;
-
- if (ucfg) {
- struct ast_variable *gen;
- int genhasiax;
- int genregisteriax;
- const char *hasiax, *registeriax;
-
- genhasiax = ast_true(ast_variable_retrieve(ucfg, "general", "hasiax"));
- genregisteriax = ast_true(ast_variable_retrieve(ucfg, "general", "registeriax"));
- gen = ast_variable_browse(ucfg, "general");
- cat = ast_category_browse(ucfg, NULL);
- while (cat) {
- if (strcasecmp(cat, "general")) {
- hasiax = ast_variable_retrieve(ucfg, cat, "hasiax");
- registeriax = ast_variable_retrieve(ucfg, cat, "registeriax");
- if (ast_true(hasiax) || (!hasiax && genhasiax)) {
- /* Start with general parameters, then specific parameters, user and peer */
- user = build_user(cat, gen, ast_variable_browse(ucfg, cat), 0);
- if (user) {
- ao2_link(users, user);
- user = user_unref(user);
- }
- peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0);
- if (peer) {
- if (ast_test_flag(peer, IAX_DYNAMIC))
- reg_source_db(peer);
- ao2_link(peers, peer);
- peer = peer_unref(peer);
- }
- }
- if (ast_true(registeriax) || (!registeriax && genregisteriax)) {
- char tmp[256];
- const char *host = ast_variable_retrieve(ucfg, cat, "host");
- const char *username = ast_variable_retrieve(ucfg, cat, "username");
- const char *secret = ast_variable_retrieve(ucfg, cat, "secret");
- if (!host)
- host = ast_variable_retrieve(ucfg, "general", "host");
- if (!username)
- username = ast_variable_retrieve(ucfg, "general", "username");
- if (!secret)
- secret = ast_variable_retrieve(ucfg, "general", "secret");
- if (!ast_strlen_zero(username) && !ast_strlen_zero(host)) {
- if (!ast_strlen_zero(secret))
- snprintf(tmp, sizeof(tmp), "%s:%s@%s", username, secret, host);
- else
- snprintf(tmp, sizeof(tmp), "%s@%s", username, host);
- iax2_register(tmp, 0);
- }
- }
- }
- cat = ast_category_browse(ucfg, cat);
- }
- ast_config_destroy(ucfg);
- }
-
- cat = ast_category_browse(cfg, NULL);
- while(cat) {
- if (strcasecmp(cat, "general")) {
- utype = ast_variable_retrieve(cfg, cat, "type");
- if (!strcasecmp(cat, "callnumberlimits")) {
- build_callno_limits(ast_variable_browse(cfg, cat));
- } else if (utype) {
- if (!strcasecmp(utype, "user") || !strcasecmp(utype, "friend")) {
- user = build_user(cat, ast_variable_browse(cfg, cat), NULL, 0);
- if (user) {
- ao2_link(users, user);
- user = user_unref(user);
- }
- }
- if (!strcasecmp(utype, "peer") || !strcasecmp(utype, "friend")) {
- peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0);
- if (peer) {
- if (ast_test_flag(peer, IAX_DYNAMIC))
- reg_source_db(peer);
- ao2_link(peers, peer);
- peer = peer_unref(peer);
- }
- } else if (strcasecmp(utype, "user")) {
- ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, config_file);
- }
- } else
- ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
- }
- cat = ast_category_browse(cfg, cat);
- }
- ast_config_destroy(cfg);
- set_timing();
- return 1;
- }
- static void poke_all_peers(void)
- {
- struct ao2_iterator i;
- struct iax2_peer *peer;
- i = ao2_iterator_init(peers, 0);
- while ((peer = ao2_iterator_next(&i))) {
- iax2_poke_peer(peer, 0);
- peer_unref(peer);
- }
- ao2_iterator_destroy(&i);
- }
- static int reload_config(void)
- {
- static const char config[] = "iax.conf";
- struct iax2_registry *reg;
- if (set_config(config, 1) > 0) {
- prune_peers();
- prune_users();
- ao2_callback(callno_limits, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, prune_addr_range_cb, NULL);
- ao2_callback(calltoken_ignores, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, prune_addr_range_cb, NULL);
- ao2_callback(peercnts, OBJ_NODATA, set_peercnt_limit_all_cb, NULL);
- trunk_timed = trunk_untimed = 0;
- trunk_nmaxmtu = trunk_maxmtu = 0;
- AST_LIST_LOCK(®istrations);
- AST_LIST_TRAVERSE(®istrations, reg, entry)
- iax2_do_register(reg);
- AST_LIST_UNLOCK(®istrations);
- /* Qualify hosts, too */
- poke_all_peers();
- }
-
- reload_firmware(0);
- iax_provision_reload(1);
- return 0;
- }
- static char *handle_cli_iax2_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "iax2 reload";
- e->usage =
- "Usage: iax2 reload\n"
- " Reloads IAX configuration from iax.conf\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- reload_config();
- return CLI_SUCCESS;
- }
- static int reload(void)
- {
- return reload_config();
- }
- static int cache_get_callno_locked(const char *data)
- {
- struct sockaddr_in sin;
- int x;
- int callno;
- struct iax_ie_data ied;
- struct create_addr_info cai;
- struct parsed_dial_string pds;
- char *tmpstr;
- for (x = 0; x < ARRAY_LEN(iaxs); x++) {
- /* Look for an *exact match* call. Once a call is negotiated, it can only
- look up entries for a single context */
- if (!ast_mutex_trylock(&iaxsl[x])) {
- if (iaxs[x] && !strcasecmp(data, iaxs[x]->dproot))
- return x;
- ast_mutex_unlock(&iaxsl[x]);
- }
- }
- /* No match found, we need to create a new one */
- memset(&cai, 0, sizeof(cai));
- memset(&ied, 0, sizeof(ied));
- memset(&pds, 0, sizeof(pds));
- tmpstr = ast_strdupa(data);
- parse_dial_string(tmpstr, &pds);
- if (ast_strlen_zero(pds.peer)) {
- ast_log(LOG_WARNING, "No peer provided in the IAX2 dial string '%s'\n", data);
- return -1;
- }
- /* Populate our address from the given */
- if (create_addr(pds.peer, NULL, &sin, &cai))
- return -1;
- ast_debug(1, "peer: %s, username: %s, password: %s, context: %s\n",
- pds.peer, pds.username, pds.password, pds.context);
- callno = find_callno_locked(0, 0, &sin, NEW_FORCE, cai.sockfd, 0);
- if (callno < 1) {
- ast_log(LOG_WARNING, "Unable to create call\n");
- return -1;
- }
- ast_string_field_set(iaxs[callno], dproot, data);
- iaxs[callno]->capability = IAX_CAPABILITY_FULLBANDWIDTH;
- iax_ie_append_short(&ied, IAX_IE_VERSION, IAX_PROTO_VERSION);
- iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, "TBD");
- /* the string format is slightly different from a standard dial string,
- because the context appears in the 'exten' position
- */
- if (pds.exten)
- iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, pds.exten);
- if (pds.username)
- iax_ie_append_str(&ied, IAX_IE_USERNAME, pds.username);
- iax_ie_append_int(&ied, IAX_IE_FORMAT, IAX_CAPABILITY_FULLBANDWIDTH);
- iax_ie_append_int(&ied, IAX_IE_CAPABILITY, IAX_CAPABILITY_FULLBANDWIDTH);
- /* Keep password handy */
- if (pds.password)
- ast_string_field_set(iaxs[callno], secret, pds.password);
- if (pds.key)
- ast_string_field_set(iaxs[callno], outkey, pds.key);
- /* Start the call going */
- add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
- send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1);
- return callno;
- }
- static struct iax2_dpcache *find_cache(struct ast_channel *chan, const char *data, const char *context, const char *exten, int priority)
- {
- struct iax2_dpcache *dp = NULL;
- struct timeval tv = ast_tvnow();
- int x, com[2], timeout, old = 0, outfd, abort, callno;
- struct ast_channel *c = NULL;
- struct ast_frame *f = NULL;
- AST_LIST_TRAVERSE_SAFE_BEGIN(&dpcache, dp, cache_list) {
- if (ast_tvcmp(tv, dp->expiry) > 0) {
- AST_LIST_REMOVE_CURRENT(cache_list);
- if ((dp->flags & CACHE_FLAG_PENDING) || dp->callno)
- ast_log(LOG_WARNING, "DP still has peer field or pending or callno (flags = %d, peer = blah, callno = %d)\n", dp->flags, dp->callno);
- else
- ast_free(dp);
- continue;
- }
- if (!strcmp(dp->peercontext, data) && !strcmp(dp->exten, exten))
- break;
- }
- AST_LIST_TRAVERSE_SAFE_END;
- if (!dp) {
- /* No matching entry. Create a new one. */
- /* First, can we make a callno? */
- if ((callno = cache_get_callno_locked(data)) < 0) {
- ast_log(LOG_WARNING, "Unable to generate call for '%s'\n", data);
- return NULL;
- }
- if (!(dp = ast_calloc(1, sizeof(*dp)))) {
- ast_mutex_unlock(&iaxsl[callno]);
- return NULL;
- }
- ast_copy_string(dp->peercontext, data, sizeof(dp->peercontext));
- ast_copy_string(dp->exten, exten, sizeof(dp->exten));
- dp->expiry = ast_tvnow();
- dp->orig = dp->expiry;
- /* Expires in 30 mins by default */
- dp->expiry.tv_sec += iaxdefaultdpcache;
- dp->flags = CACHE_FLAG_PENDING;
- for (x = 0; x < ARRAY_LEN(dp->waiters); x++)
- dp->waiters[x] = -1;
- /* Insert into the lists */
- AST_LIST_INSERT_TAIL(&dpcache, dp, cache_list);
- AST_LIST_INSERT_TAIL(&iaxs[callno]->dpentries, dp, peer_list);
- /* Send the request if we're already up */
- if (ast_test_flag(&iaxs[callno]->state, IAX_STATE_STARTED))
- iax2_dprequest(dp, callno);
- ast_mutex_unlock(&iaxsl[callno]);
- }
- /* By here we must have a dp */
- if (dp->flags & CACHE_FLAG_PENDING) {
- /* Okay, here it starts to get nasty. We need a pipe now to wait
- for a reply to come back so long as it's pending */
- for (x = 0; x < ARRAY_LEN(dp->waiters); x++) {
- /* Find an empty slot */
- if (dp->waiters[x] < 0)
- break;
- }
- if (x >= ARRAY_LEN(dp->waiters)) {
- ast_log(LOG_WARNING, "No more waiter positions available\n");
- return NULL;
- }
- if (pipe(com)) {
- ast_log(LOG_WARNING, "Unable to create pipe for comm\n");
- return NULL;
- }
- dp->waiters[x] = com[1];
- /* Okay, now we wait */
- timeout = iaxdefaulttimeout * 1000;
- /* Temporarily unlock */
- AST_LIST_UNLOCK(&dpcache);
- /* Defer any dtmf */
- if (chan)
- old = ast_channel_defer_dtmf(chan);
- abort = 0;
- while(timeout) {
- c = ast_waitfor_nandfds(&chan, chan ? 1 : 0, &com[0], 1, NULL, &outfd, &timeout);
- if (outfd > -1)
- break;
- if (!c)
- continue;
- if (!(f = ast_read(c))) {
- abort = 1;
- break;
- }
- ast_frfree(f);
- }
- if (!timeout) {
- ast_log(LOG_WARNING, "Timeout waiting for %s exten %s\n", data, exten);
- }
- AST_LIST_LOCK(&dpcache);
- dp->waiters[x] = -1;
- close(com[1]);
- close(com[0]);
- if (abort) {
- /* Don't interpret anything, just abort. Not sure what th epoint
- of undeferring dtmf on a hung up channel is but hey whatever */
- if (!old && chan)
- ast_channel_undefer_dtmf(chan);
- return NULL;
- }
- if (!(dp->flags & CACHE_FLAG_TIMEOUT)) {
- /* Now to do non-independent analysis the results of our wait */
- if (dp->flags & CACHE_FLAG_PENDING) {
- /* Still pending... It's a timeout. Wake everybody up. Consider it no longer
- pending. Don't let it take as long to timeout. */
- dp->flags &= ~CACHE_FLAG_PENDING;
- dp->flags |= CACHE_FLAG_TIMEOUT;
- /* Expire after only 60 seconds now. This is designed to help reduce backlog in heavily loaded
- systems without leaving it unavailable once the server comes back online */
- dp->expiry.tv_sec = dp->orig.tv_sec + 60;
- for (x = 0; x < ARRAY_LEN(dp->waiters); x++) {
- if (dp->waiters[x] > -1) {
- if (write(dp->waiters[x], "asdf", 4) < 0) {
- ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
- }
- }
- }
- }
- }
- /* Our caller will obtain the rest */
- if (!old && chan)
- ast_channel_undefer_dtmf(chan);
- }
- return dp;
- }
- /*! \brief Part of the IAX2 switch interface */
- static int iax2_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
- {
- int res = 0;
- struct iax2_dpcache *dp = NULL;
- #if 0
- ast_log(LOG_NOTICE, "iax2_exists: con: %s, exten: %s, pri: %d, cid: %s, data: %s\n", context, exten, priority, callerid ? callerid : "<unknown>", data);
- #endif
- if ((priority != 1) && (priority != 2))
- return 0;
- AST_LIST_LOCK(&dpcache);
- if ((dp = find_cache(chan, data, context, exten, priority))) {
- if (dp->flags & CACHE_FLAG_EXISTS)
- res = 1;
- } else {
- ast_log(LOG_WARNING, "Unable to make DP cache\n");
- }
- AST_LIST_UNLOCK(&dpcache);
- return res;
- }
- /*! \brief part of the IAX2 dial plan switch interface */
- static int iax2_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
- {
- int res = 0;
- struct iax2_dpcache *dp = NULL;
- #if 0
- ast_log(LOG_NOTICE, "iax2_canmatch: con: %s, exten: %s, pri: %d, cid: %s, data: %s\n", context, exten, priority, callerid ? callerid : "<unknown>", data);
- #endif
- if ((priority != 1) && (priority != 2))
- return 0;
- AST_LIST_LOCK(&dpcache);
- if ((dp = find_cache(chan, data, context, exten, priority))) {
- if (dp->flags & CACHE_FLAG_CANEXIST)
- res = 1;
- } else {
- ast_log(LOG_WARNING, "Unable to make DP cache\n");
- }
- AST_LIST_UNLOCK(&dpcache);
- return res;
- }
- /*! \brief Part of the IAX2 Switch interface */
- static int iax2_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
- {
- int res = 0;
- struct iax2_dpcache *dp = NULL;
- #if 0
- ast_log(LOG_NOTICE, "iax2_matchmore: con: %s, exten: %s, pri: %d, cid: %s, data: %s\n", context, exten, priority, callerid ? callerid : "<unknown>", data);
- #endif
- if ((priority != 1) && (priority != 2))
- return 0;
- AST_LIST_LOCK(&dpcache);
- if ((dp = find_cache(chan, data, context, exten, priority))) {
- if (dp->flags & CACHE_FLAG_MATCHMORE)
- res = 1;
- } else {
- ast_log(LOG_WARNING, "Unable to make DP cache\n");
- }
- AST_LIST_UNLOCK(&dpcache);
- return res;
- }
- /*! \brief Execute IAX2 dialplan switch */
- static int iax2_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
- {
- char odata[256];
- char req[256];
- char *ncontext;
- struct iax2_dpcache *dp = NULL;
- struct ast_app *dial = NULL;
- #if 0
- ast_log(LOG_NOTICE, "iax2_exec: con: %s, exten: %s, pri: %d, cid: %s, data: %s, newstack: %d\n", context, exten, priority, callerid ? callerid : "<unknown>", data, newstack);
- #endif
- if (priority == 2) {
- /* Indicate status, can be overridden in dialplan */
- const char *dialstatus = pbx_builtin_getvar_helper(chan, "DIALSTATUS");
- if (dialstatus) {
- dial = pbx_findapp(dialstatus);
- if (dial)
- pbx_exec(chan, dial, "");
- }
- return -1;
- } else if (priority != 1)
- return -1;
- AST_LIST_LOCK(&dpcache);
- if ((dp = find_cache(chan, data, context, exten, priority))) {
- if (dp->flags & CACHE_FLAG_EXISTS) {
- ast_copy_string(odata, data, sizeof(odata));
- ncontext = strchr(odata, '/');
- if (ncontext) {
- *ncontext = '\0';
- ncontext++;
- snprintf(req, sizeof(req), "IAX2/%s/%s@%s", odata, exten, ncontext);
- } else {
- snprintf(req, sizeof(req), "IAX2/%s/%s", odata, exten);
- }
- ast_verb(3, "Executing Dial('%s')\n", req);
- } else {
- AST_LIST_UNLOCK(&dpcache);
- ast_log(LOG_WARNING, "Can't execute nonexistent extension '%s[@%s]' in data '%s'\n", exten, context, data);
- return -1;
- }
- }
- AST_LIST_UNLOCK(&dpcache);
- if ((dial = pbx_findapp("Dial")))
- return pbx_exec(chan, dial, req);
- else
- ast_log(LOG_WARNING, "No dial application registered\n");
- return -1;
- }
- static int function_iaxpeer(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
- {
- struct iax2_peer *peer;
- char *peername, *colname;
- peername = ast_strdupa(data);
- /* if our channel, return the IP address of the endpoint of current channel */
- if (!strcmp(peername,"CURRENTCHANNEL")) {
- unsigned short callno;
- if (chan->tech != &iax2_tech)
- return -1;
- callno = PTR_TO_CALLNO(chan->tech_pvt);
- ast_copy_string(buf, iaxs[callno]->addr.sin_addr.s_addr ? ast_inet_ntoa(iaxs[callno]->addr.sin_addr) : "", len);
- return 0;
- }
- if ((colname = strchr(peername, ',')))
- *colname++ = '\0';
- else
- colname = "ip";
- if (!(peer = find_peer(peername, 1)))
- return -1;
- if (!strcasecmp(colname, "ip")) {
- ast_copy_string(buf, peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "", len);
- } else if (!strcasecmp(colname, "status")) {
- peer_status(peer, buf, len);
- } else if (!strcasecmp(colname, "mailbox")) {
- ast_copy_string(buf, peer->mailbox, len);
- } else if (!strcasecmp(colname, "context")) {
- ast_copy_string(buf, peer->context, len);
- } else if (!strcasecmp(colname, "expire")) {
- snprintf(buf, len, "%d", peer->expire);
- } else if (!strcasecmp(colname, "dynamic")) {
- ast_copy_string(buf, (ast_test_flag(peer, IAX_DYNAMIC) ? "yes" : "no"), len);
- } else if (!strcasecmp(colname, "callerid_name")) {
- ast_copy_string(buf, peer->cid_name, len);
- } else if (!strcasecmp(colname, "callerid_num")) {
- ast_copy_string(buf, peer->cid_num, len);
- } else if (!strcasecmp(colname, "codecs")) {
- ast_getformatname_multiple(buf, len -1, peer->capability);
- } else if (!strncasecmp(colname, "codec[", 6)) {
- char *codecnum, *ptr;
- int index = 0, codec = 0;
-
- codecnum = strchr(colname, '[');
- *codecnum = '\0';
- codecnum++;
- if ((ptr = strchr(codecnum, ']'))) {
- *ptr = '\0';
- }
- index = atoi(codecnum);
- if((codec = ast_codec_pref_index(&peer->prefs, index))) {
- ast_copy_string(buf, ast_getformatname(codec), len);
- }
- }
- peer_unref(peer);
- return 0;
- }
- struct ast_custom_function iaxpeer_function = {
- .name = "IAXPEER",
- .synopsis = "Gets IAX peer information",
- .syntax = "IAXPEER(<peername|CURRENTCHANNEL>[,item])",
- .read = function_iaxpeer,
- .desc = "If peername specified, valid items are:\n"
- "- ip (default) The IP address.\n"
- "- status The peer's status (if qualify=yes)\n"
- "- mailbox The configured mailbox.\n"
- "- context The configured context.\n"
- "- expire The epoch time of the next expire.\n"
- "- dynamic Is it dynamic? (yes/no).\n"
- "- callerid_name The configured Caller ID name.\n"
- "- callerid_num The configured Caller ID number.\n"
- "- codecs The configured codecs.\n"
- "- codec[x] Preferred codec index number 'x' (beginning with zero).\n"
- "\n"
- "If CURRENTCHANNEL specified, returns IP address of current channel\n"
- "\n"
- };
- static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *args, char *buf, size_t buflen)
- {
- struct chan_iax2_pvt *pvt;
- unsigned int callno;
- int res = 0;
- if (!chan || chan->tech != &iax2_tech) {
- ast_log(LOG_ERROR, "This function requires a valid IAX2 channel\n");
- return -1;
- }
- callno = PTR_TO_CALLNO(chan->tech_pvt);
- ast_mutex_lock(&iaxsl[callno]);
- if (!(pvt = iaxs[callno])) {
- ast_mutex_unlock(&iaxsl[callno]);
- return -1;
- }
- if (!strcasecmp(args, "osptoken"))
- ast_copy_string(buf, pvt->osptoken, buflen);
- else
- res = -1;
- ast_mutex_unlock(&iaxsl[callno]);
- return res;
- }
- /*! \brief Part of the device state notification system ---*/
- static int iax2_devicestate(void *data)
- {
- struct parsed_dial_string pds;
- char *tmp = ast_strdupa(data);
- struct iax2_peer *p;
- int res = AST_DEVICE_INVALID;
- memset(&pds, 0, sizeof(pds));
- parse_dial_string(tmp, &pds);
- if (ast_strlen_zero(pds.peer)) {
- ast_log(LOG_WARNING, "No peer provided in the IAX2 dial string '%s'\n", (char *) data);
- return res;
- }
-
- ast_debug(3, "Checking device state for device %s\n", pds.peer);
- /* SLD: FIXME: second call to find_peer during registration */
- if (!(p = find_peer(pds.peer, 1)))
- return res;
- res = AST_DEVICE_UNAVAILABLE;
- ast_debug(3, "iax2_devicestate: Found peer. What's device state of %s? addr=%d, defaddr=%d maxms=%d, lastms=%d\n",
- pds.peer, p->addr.sin_addr.s_addr, p->defaddr.sin_addr.s_addr, p->maxms, p->lastms);
-
- if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) &&
- (!p->maxms || ((p->lastms > -1) && (p->historicms <= p->maxms)))) {
- /* Peer is registered, or have default IP address
- and a valid registration */
- if (p->historicms == 0 || p->historicms <= p->maxms)
- /* let the core figure out whether it is in use or not */
- res = AST_DEVICE_UNKNOWN;
- }
- peer_unref(p);
- return res;
- }
- static struct ast_switch iax2_switch =
- {
- name: "IAX2",
- description: "IAX Remote Dialplan Switch",
- exists: iax2_exists,
- canmatch: iax2_canmatch,
- exec: iax2_exec,
- matchmore: iax2_matchmore,
- };
- /*
- { { "iax2", "show", "cache", NULL },
- iax2_show_cache, "Display IAX cached dialplan",
- show_cache_usage },
- { { "iax2", "show", "channels", NULL },
- iax2_show_channels, "List active IAX channels",
- show_channels_usage },
- { { "iax2", "show", "firmware", NULL },
- iax2_show_firmware, "List available IAX firmwares",
- show_firmware_usage },
- { { "iax2", "show", "netstats", NULL },
- iax2_show_netstats, "List active IAX channel netstats",
- show_netstats_usage },
- { { "iax2", "show", "peers", NULL },
- iax2_show_peers, "List defined IAX peers",
- show_peers_usage },
- { { "iax2", "show", "registry", NULL },
- iax2_show_registry, "Display IAX registration status",
- show_reg_usage },
- { { "iax2", "show", "stats", NULL },
- iax2_show_stats, "Display IAX statistics",
- show_stats_usage },
- { { "iax2", "show", "threads", NULL },
- iax2_show_threads, "Display IAX helper thread info",
- show_threads_usage },
- { { "iax2", "unregister", NULL },
- iax2_unregister, "Unregister (force expiration) an IAX2 peer from the registry",
- unregister_usage, complete_iax2_unregister },
- { { "iax2", "set", "mtu", NULL },
- iax2_set_mtu, "Set the IAX systemwide trunking MTU",
- set_mtu_usage, NULL, NULL },
- { { "iax2", "show", "users", NULL },
- iax2_show_users, "List defined IAX users",
- show_users_usage },
- { { "iax2", "prune", "realtime", NULL },
- iax2_prune_realtime, "Prune a cached realtime lookup",
- prune_realtime_usage, complete_iax2_show_peer },
- { { "iax2", "reload", NULL },
- iax2_reload, "Reload IAX configuration",
- iax2_reload_usage },
- { { "iax2", "show", "peer", NULL },
- iax2_show_peer, "Show details on specific IAX peer",
- show_peer_usage, complete_iax2_show_peer },
- { { "iax2", "set", "debug", NULL },
- iax2_do_debug, "Enable IAX debugging",
- debug_usage },
- { { "iax2", "set", "debug", "trunk", NULL },
- iax2_do_trunk_debug, "Enable IAX trunk debugging",
- debug_trunk_usage },
- { { "iax2", "set", "debug", "jb", NULL },
- iax2_do_jb_debug, "Enable IAX jitterbuffer debugging",
- debug_jb_usage },
- { { "iax2", "set", "debug", "off", NULL },
- iax2_no_debug, "Disable IAX debugging",
- no_debug_usage },
- { { "iax2", "set", "debug", "trunk", "off", NULL },
- iax2_no_trunk_debug, "Disable IAX trunk debugging",
- no_debug_trunk_usage },
- { { "iax2", "set", "debug", "jb", "off", NULL },
- iax2_no_jb_debug, "Disable IAX jitterbuffer debugging",
- no_debug_jb_usage },
- { { "iax2", "test", "losspct", NULL },
- iax2_test_losspct, "Set IAX2 incoming frame loss percentage",
- iax2_test_losspct_usage },
- { { "iax2", "provision", NULL },
- iax2_prov_cmd, "Provision an IAX device",
- show_prov_usage, iax2_prov_complete_template_3rd },
- #ifdef IAXTESTS
- { { "iax2", "test", "late", NULL },
- iax2_test_late, "Test the receipt of a late frame",
- iax2_test_late_usage },
- { { "iax2", "test", "resync", NULL },
- iax2_test_resync, "Test a resync in received timestamps",
- iax2_test_resync_usage },
- { { "iax2", "test", "jitter", NULL },
- iax2_test_jitter, "Simulates jitter for testing",
- iax2_test_jitter_usage },
- #endif
- */
- static struct ast_cli_entry cli_iax2_set_debug_deprecated = AST_CLI_DEFINE(handle_cli_iax2_set_debug_deprecated, "Enable/Disable IAX debugging");
- static struct ast_cli_entry cli_iax2_set_debug_trunk_deprecated = AST_CLI_DEFINE(handle_cli_iax2_set_debug_trunk_deprecated, "Enable/Disable IAX debugging");
- static struct ast_cli_entry cli_iax2_set_debug_jb_deprecated = AST_CLI_DEFINE(handle_cli_iax2_set_debug_jb_deprecated, "Enable/Disable IAX debugging");
- static struct ast_cli_entry cli_iax2[] = {
- AST_CLI_DEFINE(handle_cli_iax2_provision, "Provision an IAX device"),
- AST_CLI_DEFINE(handle_cli_iax2_prune_realtime, "Prune a cached realtime lookup"),
- AST_CLI_DEFINE(handle_cli_iax2_reload, "Reload IAX configuration"),
- AST_CLI_DEFINE(handle_cli_iax2_set_mtu, "Set the IAX systemwide trunking MTU"),
- AST_CLI_DEFINE(handle_cli_iax2_set_debug, "Enable/Disable IAX debugging", .deprecate_cmd = &cli_iax2_set_debug_deprecated),
- AST_CLI_DEFINE(handle_cli_iax2_set_debug_trunk, "Enable/Disable IAX trunk debugging", .deprecate_cmd = &cli_iax2_set_debug_trunk_deprecated),
- AST_CLI_DEFINE(handle_cli_iax2_set_debug_jb, "Enable/Disable IAX jitterbuffer debugging", .deprecate_cmd = &cli_iax2_set_debug_jb_deprecated),
- AST_CLI_DEFINE(handle_cli_iax2_show_cache, "Display IAX cached dialplan"),
- AST_CLI_DEFINE(handle_cli_iax2_show_channels, "List active IAX channels"),
- AST_CLI_DEFINE(handle_cli_iax2_show_firmware, "List available IAX firmware"),
- AST_CLI_DEFINE(handle_cli_iax2_show_netstats, "List active IAX channel netstats"),
- AST_CLI_DEFINE(handle_cli_iax2_show_peer, "Show details on specific IAX peer"),
- AST_CLI_DEFINE(handle_cli_iax2_show_peers, "List defined IAX peers"),
- AST_CLI_DEFINE(handle_cli_iax2_show_registry, "Display IAX registration status"),
- AST_CLI_DEFINE(handle_cli_iax2_show_stats, "Display IAX statistics"),
- AST_CLI_DEFINE(handle_cli_iax2_show_threads, "Display IAX helper thread info"),
- AST_CLI_DEFINE(handle_cli_iax2_show_users, "List defined IAX users"),
- AST_CLI_DEFINE(handle_cli_iax2_test_losspct, "Set IAX2 incoming frame loss percentage"),
- AST_CLI_DEFINE(handle_cli_iax2_unregister, "Unregister (force expiration) an IAX2 peer from the registry"),
- AST_CLI_DEFINE(handle_cli_iax2_show_callno_limits, "Show current entries in IP call number limit table"),
- #ifdef IAXTESTS
- AST_CLI_DEFINE(handle_cli_iax2_test_jitter, "Simulates jitter for testing"),
- AST_CLI_DEFINE(handle_cli_iax2_test_late, "Test the receipt of a late frame"),
- AST_CLI_DEFINE(handle_cli_iax2_test_resync, "Test a resync in received timestamps"),
- #endif /* IAXTESTS */
- };
- static int __unload_module(void)
- {
- struct iax2_thread *thread = NULL;
- struct ast_context *con;
- int x;
- /* Make sure threads do not hold shared resources when they are canceled */
-
- /* Grab the sched lock resource to keep it away from threads about to die */
- /* Cancel the network thread, close the net socket */
- if (netthreadid != AST_PTHREADT_NULL) {
- AST_LIST_LOCK(&frame_queue);
- ast_mutex_lock(&sched_lock);
- pthread_cancel(netthreadid);
- ast_cond_signal(&sched_cond);
- ast_mutex_unlock(&sched_lock); /* Release the schedule lock resource */
- AST_LIST_UNLOCK(&frame_queue);
- pthread_join(netthreadid, NULL);
- }
- if (schedthreadid != AST_PTHREADT_NULL) {
- ast_mutex_lock(&sched_lock);
- pthread_cancel(schedthreadid);
- ast_cond_signal(&sched_cond);
- ast_mutex_unlock(&sched_lock);
- pthread_join(schedthreadid, NULL);
- }
- /* Call for all threads to halt */
- AST_LIST_LOCK(&idle_list);
- while ((thread = AST_LIST_REMOVE_HEAD(&idle_list, list)))
- pthread_cancel(thread->threadid);
- AST_LIST_UNLOCK(&idle_list);
- AST_LIST_LOCK(&active_list);
- while ((thread = AST_LIST_REMOVE_HEAD(&active_list, list)))
- pthread_cancel(thread->threadid);
- AST_LIST_UNLOCK(&active_list);
- AST_LIST_LOCK(&dynamic_list);
- while ((thread = AST_LIST_REMOVE_HEAD(&dynamic_list, list)))
- pthread_cancel(thread->threadid);
- AST_LIST_UNLOCK(&dynamic_list);
-
- /* Wait for threads to exit */
- while(0 < iaxactivethreadcount)
- usleep(10000);
-
- ast_netsock_release(netsock);
- ast_netsock_release(outsock);
- for (x = 0; x < ARRAY_LEN(iaxs); x++) {
- if (iaxs[x]) {
- iax2_destroy(x);
- }
- }
- ast_manager_unregister( "IAXpeers" );
- ast_manager_unregister( "IAXpeerlist" );
- ast_manager_unregister( "IAXnetstats" );
- ast_unregister_application(papp);
- ast_cli_unregister_multiple(cli_iax2, sizeof(cli_iax2) / sizeof(struct ast_cli_entry));
- ast_unregister_switch(&iax2_switch);
- ast_channel_unregister(&iax2_tech);
- delete_users();
- iax_provision_unload();
- sched_context_destroy(sched);
- reload_firmware(1);
- for (x = 0; x < ARRAY_LEN(iaxsl); x++) {
- ast_mutex_destroy(&iaxsl[x]);
- }
- ao2_ref(peers, -1);
- ao2_ref(users, -1);
- ao2_ref(iax_peercallno_pvts, -1);
- ao2_ref(iax_transfercallno_pvts, -1);
- ao2_ref(peercnts, -1);
- ao2_ref(callno_limits, -1);
- ao2_ref(calltoken_ignores, -1);
- ao2_ref(callno_pool, -1);
- ao2_ref(callno_pool_trunk, -1);
- con = ast_context_find(regcontext);
- if (con)
- ast_context_destroy(con, "IAX2");
-
- return 0;
- }
- static int unload_module(void)
- {
- ast_custom_function_unregister(&iaxpeer_function);
- ast_custom_function_unregister(&iaxvar_function);
- return __unload_module();
- }
- static int peer_set_sock_cb(void *obj, void *arg, int flags)
- {
- struct iax2_peer *peer = obj;
- if (peer->sockfd < 0)
- peer->sockfd = defaultsockfd;
- return 0;
- }
- static int pvt_hash_cb(const void *obj, const int flags)
- {
- const struct chan_iax2_pvt *pvt = obj;
- return pvt->peercallno;
- }
- static int pvt_cmp_cb(void *obj, void *arg, int flags)
- {
- struct chan_iax2_pvt *pvt = obj, *pvt2 = arg;
- /* The frames_received field is used to hold whether we're matching
- * against a full frame or not ... */
- return match(&pvt2->addr, pvt2->peercallno, pvt2->callno, pvt,
- pvt2->frames_received) ? CMP_MATCH : 0;
- }
- static int transfercallno_pvt_hash_cb(const void *obj, const int flags)
- {
- const struct chan_iax2_pvt *pvt = obj;
- return pvt->transfercallno;
- }
- static int transfercallno_pvt_cmp_cb(void *obj, void *arg, int flags)
- {
- struct chan_iax2_pvt *pvt = obj, *pvt2 = arg;
- /* The frames_received field is used to hold whether we're matching
- * against a full frame or not ... */
- return match(&pvt2->transfer, pvt2->transfercallno, pvt2->callno, pvt,
- pvt2->frames_received) ? CMP_MATCH | CMP_STOP : 0;
- }
- static int load_objects(void)
- {
- peers = users = iax_peercallno_pvts = iax_transfercallno_pvts = NULL;
- peercnts = callno_limits = calltoken_ignores = callno_pool = callno_pool_trunk = NULL;
- if (!(peers = ao2_container_alloc(MAX_PEER_BUCKETS, peer_hash_cb, peer_cmp_cb))) {
- goto container_fail;
- } else if (!(users = ao2_container_alloc(MAX_USER_BUCKETS, user_hash_cb, user_cmp_cb))) {
- goto container_fail;
- } else if (!(iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb))) {
- goto container_fail;
- } else if (!(iax_transfercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, transfercallno_pvt_hash_cb, transfercallno_pvt_cmp_cb))) {
- goto container_fail;
- } else if (!(peercnts = ao2_container_alloc(MAX_PEER_BUCKETS, peercnt_hash_cb, peercnt_cmp_cb))) {
- goto container_fail;
- } else if (!(callno_limits = ao2_container_alloc(MAX_PEER_BUCKETS, addr_range_hash_cb, addr_range_cmp_cb))) {
- goto container_fail;
- } else if (!(calltoken_ignores = ao2_container_alloc(MAX_PEER_BUCKETS, addr_range_hash_cb, addr_range_cmp_cb))) {
- goto container_fail;
- } else if (create_callno_pools()) {
- goto container_fail;
- }
- return 0;
- container_fail:
- if (peers) {
- ao2_ref(peers, -1);
- }
- if (users) {
- ao2_ref(users, -1);
- }
- if (iax_peercallno_pvts) {
- ao2_ref(iax_peercallno_pvts, -1);
- }
- if (iax_transfercallno_pvts) {
- ao2_ref(iax_transfercallno_pvts, -1);
- }
- if (peercnts) {
- ao2_ref(peercnts, -1);
- }
- if (callno_limits) {
- ao2_ref(callno_limits, -1);
- }
- if (calltoken_ignores) {
- ao2_ref(calltoken_ignores, -1);
- }
- if (callno_pool) {
- ao2_ref(callno_pool, -1);
- }
- if (callno_pool_trunk) {
- ao2_ref(callno_pool_trunk, -1);
- }
- return AST_MODULE_LOAD_FAILURE;
- }
- /*! \brief Load IAX2 module, load configuraiton ---*/
- static int load_module(void)
- {
- static const char config[] = "iax.conf";
- int x = 0;
- struct iax2_registry *reg = NULL;
- if (load_objects()) {
- return AST_MODULE_LOAD_FAILURE;
- }
- randomcalltokendata = ast_random();
- ast_custom_function_register(&iaxpeer_function);
- ast_custom_function_register(&iaxvar_function);
- iax_set_output(iax_debug_output);
- iax_set_error(iax_error_output);
- jb_setoutput(jb_error_output, jb_warning_output, NULL);
-
- #ifdef HAVE_DAHDI
- #ifdef DAHDI_TIMERACK
- timingfd = open("/dev/dahdi/timer", O_RDWR);
- if (timingfd < 0)
- #endif
- timingfd = open("/dev/dahdi/pseudo", O_RDWR);
- if (timingfd < 0)
- ast_log(LOG_WARNING, "Unable to open IAX timing interface: %s\n", strerror(errno));
- #endif
- memset(iaxs, 0, sizeof(iaxs));
- for (x = 0; x < ARRAY_LEN(iaxsl); x++) {
- ast_mutex_init(&iaxsl[x]);
- }
- ast_cond_init(&sched_cond, NULL);
- if (!(sched = sched_context_create())) {
- ast_log(LOG_ERROR, "Failed to create scheduler context\n");
- return AST_MODULE_LOAD_FAILURE;
- }
- if (!(io = io_context_create())) {
- ast_log(LOG_ERROR, "Failed to create I/O context\n");
- sched_context_destroy(sched);
- return AST_MODULE_LOAD_FAILURE;
- }
- if (!(netsock = ast_netsock_list_alloc())) {
- ast_log(LOG_ERROR, "Failed to create netsock list\n");
- io_context_destroy(io);
- sched_context_destroy(sched);
- return AST_MODULE_LOAD_FAILURE;
- }
- ast_netsock_init(netsock);
-
- outsock = ast_netsock_list_alloc();
- if (!outsock) {
- ast_log(LOG_ERROR, "Could not allocate outsock list.\n");
- io_context_destroy(io);
- sched_context_destroy(sched);
- return AST_MODULE_LOAD_FAILURE;
- }
- ast_netsock_init(outsock);
- ast_cli_register_multiple(cli_iax2, sizeof(cli_iax2) / sizeof(struct ast_cli_entry));
- ast_register_application(papp, iax2_prov_app, psyn, pdescrip);
-
- ast_manager_register( "IAXpeers", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_peers, "List IAX Peers" );
- ast_manager_register( "IAXpeerlist", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_peer_list, "List IAX Peers" );
- ast_manager_register( "IAXnetstats", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_iax2_show_netstats, "Show IAX Netstats" );
- if(set_config(config, 0) == -1)
- return AST_MODULE_LOAD_DECLINE;
- if (ast_channel_register(&iax2_tech)) {
- ast_log(LOG_ERROR, "Unable to register channel class %s\n", "IAX2");
- __unload_module();
- return AST_MODULE_LOAD_FAILURE;
- }
- if (ast_register_switch(&iax2_switch))
- ast_log(LOG_ERROR, "Unable to register IAX switch\n");
- if (start_network_thread()) {
- ast_log(LOG_ERROR, "Unable to start network thread\n");
- __unload_module();
- return AST_MODULE_LOAD_FAILURE;
- } else
- ast_verb(2, "IAX Ready and Listening\n");
- AST_LIST_LOCK(®istrations);
- AST_LIST_TRAVERSE(®istrations, reg, entry)
- iax2_do_register(reg);
- AST_LIST_UNLOCK(®istrations);
-
- ao2_callback(peers, 0, peer_set_sock_cb, NULL);
- ao2_callback(peers, 0, iax2_poke_peer_cb, NULL);
- reload_firmware(0);
- iax_provision_reload(0);
- return AST_MODULE_LOAD_SUCCESS;
- }
- AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Inter Asterisk eXchange (Ver 2)",
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- );
|