12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919159201592115922159231592415925159261592715928159291593015931159321593315934159351593615937159381593915940159411594215943159441594515946159471594815949159501595115952159531595415955159561595715958159591596015961159621596315964159651596615967159681596915970159711597215973159741597515976159771597815979159801598115982159831598415985159861598715988159891599015991159921599315994159951599615997159981599916000160011600216003160041600516006160071600816009160101601116012160131601416015160161601716018160191602016021160221602316024160251602616027160281602916030160311603216033160341603516036160371603816039160401604116042160431604416045160461604716048160491605016051160521605316054160551605616057160581605916060160611606216063160641606516066160671606816069160701607116072160731607416075160761607716078160791608016081160821608316084160851608616087160881608916090160911609216093160941609516096160971609816099161001610116102161031610416105161061610716108161091611016111161121611316114161151611616117161181611916120161211612216123161241612516126161271612816129161301613116132161331613416135161361613716138161391614016141161421614316144161451614616147161481614916150161511615216153161541615516156161571615816159161601616116162161631616416165161661616716168161691617016171161721617316174161751617616177161781617916180161811618216183161841618516186161871618816189161901619116192161931619416195161961619716198161991620016201162021620316204162051620616207162081620916210162111621216213162141621516216162171621816219162201622116222162231622416225162261622716228162291623016231162321623316234162351623616237162381623916240162411624216243162441624516246162471624816249162501625116252162531625416255162561625716258162591626016261162621626316264162651626616267162681626916270162711627216273162741627516276162771627816279162801628116282162831628416285162861628716288162891629016291162921629316294162951629616297162981629916300163011630216303163041630516306163071630816309163101631116312163131631416315163161631716318163191632016321163221632316324163251632616327163281632916330163311633216333163341633516336163371633816339163401634116342163431634416345163461634716348163491635016351163521635316354163551635616357163581635916360163611636216363163641636516366163671636816369163701637116372163731637416375163761637716378163791638016381163821638316384163851638616387163881638916390163911639216393163941639516396163971639816399164001640116402164031640416405164061640716408164091641016411164121641316414164151641616417164181641916420164211642216423164241642516426164271642816429164301643116432164331643416435164361643716438164391644016441164421644316444164451644616447164481644916450164511645216453164541645516456164571645816459164601646116462164631646416465164661646716468164691647016471164721647316474164751647616477164781647916480164811648216483164841648516486164871648816489164901649116492164931649416495164961649716498164991650016501165021650316504165051650616507165081650916510165111651216513165141651516516165171651816519165201652116522165231652416525165261652716528165291653016531165321653316534165351653616537165381653916540165411654216543165441654516546165471654816549165501655116552165531655416555165561655716558165591656016561165621656316564165651656616567165681656916570165711657216573165741657516576165771657816579165801658116582165831658416585165861658716588165891659016591165921659316594165951659616597165981659916600166011660216603166041660516606166071660816609166101661116612166131661416615166161661716618166191662016621166221662316624166251662616627166281662916630166311663216633166341663516636166371663816639166401664116642166431664416645166461664716648166491665016651166521665316654166551665616657166581665916660166611666216663166641666516666166671666816669166701667116672166731667416675166761667716678166791668016681166821668316684166851668616687166881668916690166911669216693166941669516696166971669816699167001670116702167031670416705167061670716708167091671016711167121671316714167151671616717167181671916720167211672216723167241672516726167271672816729167301673116732167331673416735167361673716738167391674016741167421674316744167451674616747167481674916750167511675216753167541675516756167571675816759167601676116762167631676416765167661676716768167691677016771167721677316774167751677616777167781677916780167811678216783167841678516786167871678816789167901679116792167931679416795167961679716798167991680016801168021680316804168051680616807168081680916810168111681216813168141681516816168171681816819168201682116822168231682416825168261682716828168291683016831168321683316834168351683616837168381683916840168411684216843168441684516846168471684816849168501685116852168531685416855168561685716858168591686016861168621686316864168651686616867168681686916870168711687216873168741687516876168771687816879168801688116882168831688416885168861688716888168891689016891168921689316894168951689616897168981689916900169011690216903169041690516906169071690816909169101691116912169131691416915169161691716918169191692016921169221692316924169251692616927169281692916930169311693216933169341693516936169371693816939169401694116942169431694416945169461694716948169491695016951169521695316954169551695616957169581695916960169611696216963169641696516966169671696816969169701697116972169731697416975169761697716978169791698016981169821698316984169851698616987169881698916990169911699216993169941699516996169971699816999170001700117002170031700417005170061700717008170091701017011170121701317014170151701617017170181701917020170211702217023170241702517026170271702817029170301703117032170331703417035170361703717038170391704017041170421704317044170451704617047170481704917050170511705217053170541705517056170571705817059170601706117062170631706417065170661706717068170691707017071170721707317074170751707617077170781707917080170811708217083170841708517086170871708817089170901709117092170931709417095170961709717098170991710017101171021710317104171051710617107171081710917110171111711217113171141711517116171171711817119171201712117122171231712417125171261712717128171291713017131171321713317134171351713617137171381713917140171411714217143171441714517146171471714817149171501715117152171531715417155171561715717158171591716017161171621716317164171651716617167171681716917170171711717217173171741717517176171771717817179171801718117182171831718417185171861718717188171891719017191171921719317194171951719617197171981719917200172011720217203172041720517206172071720817209172101721117212172131721417215172161721717218172191722017221172221722317224172251722617227172281722917230172311723217233172341723517236172371723817239172401724117242172431724417245172461724717248172491725017251172521725317254172551725617257172581725917260172611726217263172641726517266172671726817269172701727117272172731727417275172761727717278172791728017281172821728317284172851728617287172881728917290172911729217293172941729517296172971729817299173001730117302173031730417305173061730717308173091731017311173121731317314173151731617317173181731917320173211732217323173241732517326173271732817329173301733117332173331733417335173361733717338173391734017341173421734317344173451734617347173481734917350173511735217353173541735517356173571735817359173601736117362173631736417365173661736717368173691737017371173721737317374173751737617377173781737917380173811738217383173841738517386173871738817389173901739117392173931739417395173961739717398173991740017401174021740317404174051740617407174081740917410174111741217413174141741517416174171741817419174201742117422174231742417425174261742717428174291743017431174321743317434174351743617437174381743917440174411744217443174441744517446174471744817449174501745117452174531745417455174561745717458174591746017461174621746317464174651746617467174681746917470174711747217473174741747517476174771747817479174801748117482174831748417485174861748717488174891749017491174921749317494174951749617497174981749917500175011750217503175041750517506175071750817509175101751117512175131751417515175161751717518175191752017521175221752317524175251752617527175281752917530175311753217533175341753517536175371753817539175401754117542175431754417545175461754717548175491755017551175521755317554175551755617557175581755917560175611756217563175641756517566175671756817569175701757117572175731757417575175761757717578175791758017581175821758317584175851758617587175881758917590175911759217593175941759517596175971759817599176001760117602176031760417605176061760717608176091761017611176121761317614176151761617617176181761917620176211762217623176241762517626176271762817629176301763117632176331763417635176361763717638176391764017641176421764317644176451764617647176481764917650176511765217653176541765517656176571765817659176601766117662176631766417665176661766717668176691767017671176721767317674176751767617677176781767917680176811768217683176841768517686176871768817689176901769117692176931769417695176961769717698176991770017701177021770317704177051770617707177081770917710177111771217713177141771517716177171771817719177201772117722177231772417725177261772717728177291773017731177321773317734177351773617737177381773917740177411774217743177441774517746177471774817749177501775117752177531775417755177561775717758177591776017761177621776317764177651776617767177681776917770177711777217773177741777517776177771777817779177801778117782177831778417785177861778717788177891779017791177921779317794177951779617797177981779917800178011780217803178041780517806178071780817809178101781117812178131781417815178161781717818178191782017821178221782317824178251782617827178281782917830178311783217833178341783517836178371783817839178401784117842178431784417845178461784717848178491785017851178521785317854178551785617857178581785917860178611786217863178641786517866178671786817869178701787117872178731787417875178761787717878178791788017881178821788317884178851788617887178881788917890178911789217893178941789517896178971789817899179001790117902179031790417905179061790717908179091791017911179121791317914179151791617917179181791917920179211792217923179241792517926179271792817929179301793117932179331793417935179361793717938179391794017941179421794317944179451794617947179481794917950179511795217953179541795517956179571795817959179601796117962179631796417965179661796717968179691797017971179721797317974179751797617977179781797917980179811798217983179841798517986179871798817989179901799117992179931799417995179961799717998179991800018001180021800318004180051800618007180081800918010180111801218013180141801518016180171801818019180201802118022180231802418025180261802718028180291803018031180321803318034180351803618037180381803918040180411804218043180441804518046180471804818049180501805118052180531805418055180561805718058180591806018061180621806318064180651806618067180681806918070180711807218073180741807518076180771807818079180801808118082180831808418085180861808718088180891809018091180921809318094180951809618097180981809918100181011810218103181041810518106181071810818109181101811118112181131811418115181161811718118181191812018121181221812318124181251812618127181281812918130181311813218133181341813518136181371813818139181401814118142181431814418145181461814718148181491815018151181521815318154181551815618157181581815918160181611816218163181641816518166181671816818169181701817118172181731817418175181761817718178181791818018181181821818318184181851818618187181881818918190181911819218193181941819518196181971819818199182001820118202182031820418205182061820718208182091821018211182121821318214182151821618217182181821918220182211822218223182241822518226182271822818229182301823118232182331823418235182361823718238182391824018241182421824318244182451824618247182481824918250182511825218253182541825518256182571825818259182601826118262182631826418265182661826718268182691827018271182721827318274182751827618277182781827918280182811828218283182841828518286182871828818289182901829118292182931829418295182961829718298182991830018301183021830318304183051830618307183081830918310183111831218313183141831518316183171831818319183201832118322183231832418325183261832718328183291833018331183321833318334183351833618337183381833918340183411834218343183441834518346183471834818349183501835118352183531835418355183561835718358183591836018361183621836318364183651836618367183681836918370183711837218373183741837518376183771837818379183801838118382183831838418385183861838718388183891839018391183921839318394183951839618397183981839918400184011840218403184041840518406184071840818409184101841118412184131841418415184161841718418184191842018421184221842318424184251842618427184281842918430184311843218433184341843518436184371843818439184401844118442184431844418445184461844718448184491845018451184521845318454184551845618457184581845918460184611846218463184641846518466184671846818469184701847118472184731847418475184761847718478184791848018481184821848318484184851848618487184881848918490184911849218493184941849518496184971849818499185001850118502185031850418505185061850718508185091851018511185121851318514185151851618517185181851918520185211852218523185241852518526185271852818529185301853118532185331853418535185361853718538185391854018541185421854318544185451854618547185481854918550185511855218553185541855518556185571855818559185601856118562185631856418565185661856718568185691857018571185721857318574185751857618577185781857918580185811858218583185841858518586185871858818589185901859118592185931859418595185961859718598185991860018601186021860318604186051860618607186081860918610186111861218613186141861518616186171861818619186201862118622186231862418625186261862718628186291863018631186321863318634186351863618637186381863918640186411864218643186441864518646186471864818649186501865118652186531865418655186561865718658186591866018661186621866318664186651866618667186681866918670186711867218673186741867518676186771867818679186801868118682186831868418685186861868718688186891869018691186921869318694186951869618697186981869918700187011870218703187041870518706187071870818709187101871118712187131871418715187161871718718187191872018721187221872318724187251872618727187281872918730187311873218733187341873518736187371873818739187401874118742187431874418745187461874718748187491875018751187521875318754187551875618757187581875918760187611876218763187641876518766187671876818769187701877118772187731877418775187761877718778187791878018781187821878318784187851878618787187881878918790187911879218793187941879518796187971879818799188001880118802188031880418805188061880718808188091881018811188121881318814188151881618817188181881918820188211882218823188241882518826188271882818829188301883118832188331883418835188361883718838188391884018841188421884318844188451884618847188481884918850188511885218853188541885518856188571885818859188601886118862188631886418865188661886718868188691887018871188721887318874188751887618877188781887918880188811888218883188841888518886188871888818889188901889118892188931889418895188961889718898188991890018901189021890318904189051890618907189081890918910189111891218913189141891518916189171891818919189201892118922189231892418925189261892718928189291893018931189321893318934189351893618937189381893918940189411894218943189441894518946189471894818949189501895118952189531895418955189561895718958189591896018961189621896318964189651896618967189681896918970189711897218973189741897518976189771897818979189801898118982189831898418985189861898718988189891899018991189921899318994189951899618997189981899919000190011900219003190041900519006190071900819009190101901119012190131901419015190161901719018190191902019021190221902319024190251902619027190281902919030190311903219033190341903519036190371903819039190401904119042190431904419045190461904719048190491905019051190521905319054190551905619057190581905919060190611906219063190641906519066190671906819069190701907119072190731907419075190761907719078190791908019081190821908319084190851908619087190881908919090190911909219093190941909519096190971909819099191001910119102191031910419105191061910719108191091911019111191121911319114191151911619117191181911919120191211912219123191241912519126191271912819129191301913119132191331913419135191361913719138191391914019141191421914319144191451914619147191481914919150191511915219153191541915519156191571915819159191601916119162191631916419165191661916719168191691917019171191721917319174191751917619177191781917919180191811918219183191841918519186191871918819189191901919119192191931919419195191961919719198191991920019201192021920319204192051920619207192081920919210192111921219213192141921519216192171921819219192201922119222192231922419225192261922719228192291923019231192321923319234192351923619237192381923919240192411924219243192441924519246192471924819249192501925119252192531925419255192561925719258192591926019261192621926319264192651926619267192681926919270192711927219273192741927519276192771927819279192801928119282192831928419285192861928719288192891929019291192921929319294192951929619297192981929919300193011930219303193041930519306193071930819309193101931119312193131931419315193161931719318193191932019321193221932319324193251932619327193281932919330193311933219333193341933519336193371933819339193401934119342193431934419345193461934719348193491935019351193521935319354193551935619357193581935919360193611936219363193641936519366193671936819369193701937119372193731937419375193761937719378193791938019381193821938319384193851938619387193881938919390193911939219393193941939519396193971939819399194001940119402194031940419405194061940719408194091941019411194121941319414194151941619417194181941919420194211942219423194241942519426194271942819429194301943119432194331943419435194361943719438194391944019441194421944319444194451944619447194481944919450194511945219453194541945519456194571945819459194601946119462194631946419465194661946719468194691947019471194721947319474194751947619477194781947919480194811948219483194841948519486194871948819489194901949119492194931949419495194961949719498194991950019501195021950319504195051950619507195081950919510195111951219513195141951519516195171951819519195201952119522195231952419525195261952719528195291953019531195321953319534195351953619537195381953919540195411954219543195441954519546195471954819549195501955119552195531955419555195561955719558195591956019561195621956319564195651956619567195681956919570195711957219573195741957519576195771957819579195801958119582195831958419585195861958719588195891959019591195921959319594195951959619597195981959919600196011960219603196041960519606196071960819609196101961119612196131961419615196161961719618196191962019621196221962319624196251962619627196281962919630196311963219633196341963519636196371963819639196401964119642196431964419645196461964719648196491965019651196521965319654196551965619657196581965919660196611966219663196641966519666196671966819669196701967119672196731967419675196761967719678196791968019681196821968319684196851968619687196881968919690196911969219693196941969519696196971969819699197001970119702197031970419705197061970719708197091971019711197121971319714197151971619717197181971919720197211972219723197241972519726197271972819729197301973119732197331973419735197361973719738197391974019741197421974319744197451974619747197481974919750197511975219753197541975519756197571975819759197601976119762197631976419765197661976719768197691977019771197721977319774197751977619777197781977919780197811978219783197841978519786197871978819789197901979119792197931979419795197961979719798197991980019801198021980319804198051980619807198081980919810198111981219813198141981519816198171981819819198201982119822198231982419825198261982719828198291983019831198321983319834198351983619837198381983919840198411984219843198441984519846198471984819849198501985119852198531985419855198561985719858198591986019861198621986319864198651986619867198681986919870198711987219873198741987519876198771987819879198801988119882198831988419885198861988719888198891989019891198921989319894198951989619897198981989919900199011990219903199041990519906199071990819909199101991119912199131991419915199161991719918199191992019921199221992319924199251992619927199281992919930199311993219933199341993519936199371993819939199401994119942199431994419945199461994719948199491995019951199521995319954199551995619957199581995919960199611996219963199641996519966199671996819969199701997119972199731997419975199761997719978199791998019981199821998319984199851998619987199881998919990199911999219993199941999519996199971999819999200002000120002200032000420005200062000720008200092001020011200122001320014200152001620017200182001920020200212002220023200242002520026200272002820029200302003120032200332003420035200362003720038200392004020041200422004320044200452004620047200482004920050200512005220053200542005520056200572005820059200602006120062200632006420065200662006720068200692007020071200722007320074200752007620077200782007920080200812008220083200842008520086200872008820089200902009120092200932009420095200962009720098200992010020101201022010320104201052010620107201082010920110201112011220113201142011520116201172011820119201202012120122201232012420125201262012720128201292013020131201322013320134201352013620137201382013920140201412014220143201442014520146201472014820149201502015120152201532015420155201562015720158201592016020161201622016320164201652016620167201682016920170201712017220173201742017520176201772017820179201802018120182201832018420185201862018720188201892019020191201922019320194201952019620197201982019920200202012020220203202042020520206202072020820209202102021120212202132021420215202162021720218202192022020221202222022320224202252022620227202282022920230202312023220233202342023520236202372023820239202402024120242202432024420245202462024720248202492025020251202522025320254202552025620257202582025920260202612026220263202642026520266202672026820269202702027120272202732027420275202762027720278202792028020281202822028320284202852028620287202882028920290202912029220293202942029520296202972029820299203002030120302203032030420305203062030720308203092031020311203122031320314203152031620317203182031920320203212032220323203242032520326203272032820329203302033120332203332033420335203362033720338203392034020341203422034320344203452034620347203482034920350203512035220353203542035520356203572035820359203602036120362203632036420365203662036720368203692037020371203722037320374203752037620377203782037920380203812038220383203842038520386203872038820389203902039120392203932039420395203962039720398203992040020401204022040320404204052040620407204082040920410204112041220413204142041520416204172041820419204202042120422204232042420425204262042720428204292043020431204322043320434204352043620437204382043920440204412044220443204442044520446204472044820449204502045120452204532045420455204562045720458204592046020461204622046320464204652046620467204682046920470204712047220473204742047520476204772047820479204802048120482204832048420485204862048720488204892049020491204922049320494204952049620497204982049920500205012050220503205042050520506205072050820509205102051120512205132051420515205162051720518205192052020521205222052320524205252052620527205282052920530205312053220533205342053520536205372053820539205402054120542205432054420545205462054720548205492055020551205522055320554205552055620557205582055920560205612056220563205642056520566205672056820569205702057120572205732057420575205762057720578205792058020581205822058320584205852058620587205882058920590205912059220593205942059520596205972059820599206002060120602206032060420605206062060720608206092061020611206122061320614206152061620617206182061920620206212062220623206242062520626206272062820629206302063120632206332063420635206362063720638206392064020641206422064320644206452064620647206482064920650206512065220653206542065520656206572065820659206602066120662206632066420665206662066720668206692067020671206722067320674206752067620677206782067920680206812068220683206842068520686206872068820689206902069120692206932069420695206962069720698206992070020701207022070320704207052070620707207082070920710207112071220713207142071520716207172071820719207202072120722207232072420725207262072720728207292073020731207322073320734207352073620737207382073920740207412074220743207442074520746207472074820749207502075120752207532075420755207562075720758207592076020761207622076320764207652076620767207682076920770207712077220773207742077520776207772077820779207802078120782207832078420785207862078720788207892079020791207922079320794207952079620797207982079920800208012080220803208042080520806208072080820809208102081120812208132081420815208162081720818208192082020821208222082320824208252082620827208282082920830208312083220833208342083520836208372083820839208402084120842208432084420845208462084720848208492085020851208522085320854208552085620857208582085920860208612086220863208642086520866208672086820869208702087120872208732087420875208762087720878208792088020881208822088320884208852088620887208882088920890208912089220893208942089520896208972089820899209002090120902209032090420905209062090720908209092091020911209122091320914209152091620917209182091920920209212092220923209242092520926209272092820929209302093120932209332093420935209362093720938209392094020941209422094320944209452094620947209482094920950209512095220953209542095520956209572095820959209602096120962209632096420965209662096720968209692097020971209722097320974209752097620977209782097920980209812098220983209842098520986209872098820989209902099120992209932099420995209962099720998209992100021001210022100321004210052100621007210082100921010210112101221013210142101521016210172101821019210202102121022210232102421025210262102721028210292103021031210322103321034210352103621037210382103921040210412104221043210442104521046210472104821049210502105121052210532105421055210562105721058210592106021061210622106321064210652106621067210682106921070210712107221073210742107521076210772107821079210802108121082210832108421085210862108721088210892109021091210922109321094210952109621097210982109921100211012110221103211042110521106211072110821109211102111121112211132111421115211162111721118211192112021121211222112321124211252112621127211282112921130211312113221133211342113521136211372113821139211402114121142211432114421145211462114721148211492115021151211522115321154211552115621157211582115921160211612116221163211642116521166211672116821169211702117121172211732117421175211762117721178211792118021181211822118321184211852118621187211882118921190211912119221193211942119521196211972119821199212002120121202212032120421205212062120721208212092121021211212122121321214212152121621217212182121921220212212122221223212242122521226212272122821229212302123121232212332123421235212362123721238212392124021241212422124321244212452124621247212482124921250212512125221253212542125521256212572125821259212602126121262212632126421265212662126721268212692127021271212722127321274212752127621277212782127921280212812128221283212842128521286212872128821289212902129121292212932129421295212962129721298212992130021301213022130321304213052130621307213082130921310213112131221313213142131521316213172131821319213202132121322213232132421325213262132721328213292133021331213322133321334213352133621337213382133921340213412134221343213442134521346213472134821349213502135121352213532135421355213562135721358213592136021361213622136321364213652136621367213682136921370213712137221373213742137521376213772137821379213802138121382213832138421385213862138721388213892139021391213922139321394213952139621397213982139921400214012140221403214042140521406214072140821409214102141121412214132141421415214162141721418214192142021421214222142321424214252142621427214282142921430214312143221433214342143521436214372143821439214402144121442214432144421445214462144721448214492145021451214522145321454214552145621457214582145921460214612146221463214642146521466214672146821469214702147121472214732147421475214762147721478214792148021481214822148321484214852148621487214882148921490214912149221493214942149521496214972149821499215002150121502215032150421505215062150721508215092151021511215122151321514215152151621517215182151921520215212152221523215242152521526215272152821529215302153121532215332153421535215362153721538215392154021541215422154321544215452154621547215482154921550215512155221553215542155521556215572155821559215602156121562215632156421565215662156721568215692157021571215722157321574215752157621577215782157921580215812158221583215842158521586215872158821589215902159121592215932159421595215962159721598215992160021601216022160321604216052160621607216082160921610216112161221613216142161521616216172161821619216202162121622216232162421625216262162721628216292163021631216322163321634216352163621637216382163921640216412164221643216442164521646216472164821649216502165121652216532165421655216562165721658216592166021661216622166321664216652166621667216682166921670216712167221673216742167521676216772167821679216802168121682216832168421685216862168721688216892169021691216922169321694216952169621697216982169921700217012170221703217042170521706217072170821709217102171121712217132171421715217162171721718217192172021721217222172321724217252172621727217282172921730217312173221733217342173521736217372173821739217402174121742217432174421745217462174721748217492175021751217522175321754217552175621757217582175921760217612176221763217642176521766217672176821769217702177121772217732177421775217762177721778217792178021781217822178321784217852178621787217882178921790217912179221793217942179521796217972179821799218002180121802218032180421805218062180721808218092181021811218122181321814218152181621817218182181921820218212182221823218242182521826218272182821829218302183121832218332183421835218362183721838218392184021841218422184321844218452184621847218482184921850218512185221853218542185521856218572185821859218602186121862218632186421865218662186721868218692187021871218722187321874218752187621877218782187921880218812188221883218842188521886218872188821889218902189121892218932189421895218962189721898218992190021901219022190321904219052190621907219082190921910219112191221913219142191521916219172191821919219202192121922219232192421925219262192721928219292193021931219322193321934219352193621937219382193921940219412194221943219442194521946219472194821949219502195121952219532195421955219562195721958219592196021961219622196321964219652196621967219682196921970219712197221973219742197521976219772197821979219802198121982219832198421985219862198721988219892199021991219922199321994219952199621997219982199922000220012200222003220042200522006220072200822009220102201122012220132201422015220162201722018220192202022021220222202322024220252202622027220282202922030220312203222033220342203522036220372203822039220402204122042220432204422045220462204722048220492205022051220522205322054220552205622057220582205922060220612206222063220642206522066220672206822069220702207122072220732207422075220762207722078220792208022081220822208322084220852208622087220882208922090220912209222093220942209522096220972209822099221002210122102221032210422105221062210722108221092211022111221122211322114221152211622117221182211922120221212212222123221242212522126221272212822129221302213122132221332213422135221362213722138221392214022141221422214322144221452214622147221482214922150221512215222153221542215522156221572215822159221602216122162221632216422165221662216722168221692217022171221722217322174221752217622177221782217922180221812218222183221842218522186221872218822189221902219122192221932219422195221962219722198221992220022201222022220322204222052220622207222082220922210222112221222213222142221522216222172221822219222202222122222222232222422225222262222722228222292223022231222322223322234222352223622237222382223922240222412224222243222442224522246222472224822249222502225122252222532225422255222562225722258222592226022261222622226322264222652226622267222682226922270222712227222273222742227522276222772227822279222802228122282222832228422285222862228722288222892229022291222922229322294222952229622297222982229922300223012230222303223042230522306223072230822309223102231122312223132231422315223162231722318223192232022321223222232322324223252232622327223282232922330223312233222333223342233522336223372233822339223402234122342223432234422345223462234722348223492235022351223522235322354223552235622357223582235922360223612236222363223642236522366223672236822369223702237122372223732237422375223762237722378223792238022381223822238322384223852238622387223882238922390223912239222393223942239522396223972239822399224002240122402224032240422405224062240722408224092241022411224122241322414224152241622417224182241922420224212242222423224242242522426224272242822429224302243122432224332243422435224362243722438224392244022441224422244322444224452244622447224482244922450224512245222453224542245522456224572245822459224602246122462224632246422465224662246722468224692247022471224722247322474224752247622477224782247922480224812248222483224842248522486224872248822489224902249122492224932249422495224962249722498224992250022501225022250322504225052250622507225082250922510225112251222513225142251522516225172251822519225202252122522225232252422525225262252722528225292253022531225322253322534225352253622537225382253922540225412254222543225442254522546225472254822549225502255122552225532255422555225562255722558225592256022561225622256322564225652256622567225682256922570225712257222573225742257522576225772257822579225802258122582225832258422585225862258722588225892259022591225922259322594225952259622597225982259922600226012260222603226042260522606226072260822609226102261122612226132261422615226162261722618226192262022621226222262322624226252262622627226282262922630226312263222633226342263522636226372263822639226402264122642226432264422645226462264722648226492265022651226522265322654226552265622657226582265922660226612266222663226642266522666226672266822669226702267122672226732267422675226762267722678226792268022681226822268322684226852268622687226882268922690226912269222693226942269522696226972269822699227002270122702227032270422705227062270722708227092271022711227122271322714227152271622717227182271922720227212272222723227242272522726227272272822729227302273122732227332273422735227362273722738227392274022741227422274322744227452274622747227482274922750227512275222753227542275522756227572275822759227602276122762227632276422765227662276722768227692277022771227722277322774227752277622777227782277922780227812278222783227842278522786227872278822789227902279122792227932279422795227962279722798227992280022801228022280322804228052280622807228082280922810228112281222813228142281522816228172281822819228202282122822228232282422825228262282722828228292283022831228322283322834228352283622837228382283922840228412284222843228442284522846228472284822849228502285122852228532285422855228562285722858228592286022861228622286322864228652286622867228682286922870228712287222873228742287522876228772287822879228802288122882228832288422885228862288722888228892289022891228922289322894228952289622897228982289922900229012290222903229042290522906229072290822909229102291122912229132291422915229162291722918229192292022921229222292322924229252292622927229282292922930229312293222933229342293522936229372293822939229402294122942229432294422945229462294722948229492295022951229522295322954229552295622957229582295922960229612296222963229642296522966229672296822969229702297122972229732297422975229762297722978229792298022981229822298322984229852298622987229882298922990229912299222993229942299522996229972299822999230002300123002230032300423005230062300723008230092301023011230122301323014230152301623017230182301923020230212302223023230242302523026230272302823029230302303123032230332303423035230362303723038230392304023041230422304323044230452304623047230482304923050230512305223053230542305523056230572305823059230602306123062230632306423065230662306723068230692307023071230722307323074230752307623077230782307923080230812308223083230842308523086230872308823089230902309123092230932309423095230962309723098230992310023101231022310323104231052310623107231082310923110231112311223113231142311523116231172311823119231202312123122231232312423125231262312723128231292313023131231322313323134231352313623137231382313923140231412314223143231442314523146231472314823149231502315123152231532315423155231562315723158231592316023161231622316323164231652316623167231682316923170231712317223173231742317523176231772317823179231802318123182231832318423185231862318723188231892319023191231922319323194231952319623197231982319923200232012320223203232042320523206232072320823209232102321123212232132321423215232162321723218232192322023221232222322323224232252322623227232282322923230232312323223233232342323523236232372323823239232402324123242232432324423245232462324723248232492325023251232522325323254232552325623257232582325923260232612326223263232642326523266232672326823269232702327123272232732327423275232762327723278232792328023281232822328323284232852328623287232882328923290232912329223293232942329523296232972329823299233002330123302233032330423305233062330723308233092331023311233122331323314233152331623317233182331923320233212332223323233242332523326233272332823329233302333123332233332333423335233362333723338233392334023341233422334323344233452334623347233482334923350233512335223353233542335523356233572335823359233602336123362233632336423365233662336723368233692337023371233722337323374233752337623377233782337923380233812338223383233842338523386233872338823389233902339123392233932339423395233962339723398233992340023401234022340323404234052340623407234082340923410234112341223413234142341523416234172341823419234202342123422234232342423425234262342723428234292343023431234322343323434234352343623437234382343923440234412344223443234442344523446234472344823449234502345123452234532345423455234562345723458234592346023461234622346323464234652346623467234682346923470234712347223473234742347523476234772347823479234802348123482234832348423485234862348723488234892349023491234922349323494234952349623497234982349923500235012350223503235042350523506235072350823509235102351123512235132351423515235162351723518235192352023521235222352323524235252352623527235282352923530235312353223533235342353523536235372353823539235402354123542235432354423545235462354723548235492355023551235522355323554235552355623557235582355923560235612356223563235642356523566235672356823569235702357123572235732357423575235762357723578235792358023581235822358323584235852358623587235882358923590235912359223593235942359523596235972359823599236002360123602236032360423605236062360723608236092361023611236122361323614236152361623617236182361923620236212362223623236242362523626236272362823629236302363123632236332363423635236362363723638236392364023641236422364323644236452364623647236482364923650236512365223653236542365523656236572365823659236602366123662236632366423665236662366723668236692367023671236722367323674236752367623677236782367923680236812368223683236842368523686236872368823689236902369123692236932369423695236962369723698236992370023701237022370323704237052370623707237082370923710237112371223713237142371523716237172371823719237202372123722237232372423725237262372723728237292373023731237322373323734237352373623737237382373923740237412374223743237442374523746237472374823749237502375123752237532375423755237562375723758237592376023761237622376323764237652376623767237682376923770237712377223773237742377523776237772377823779237802378123782237832378423785237862378723788237892379023791237922379323794237952379623797237982379923800238012380223803238042380523806238072380823809238102381123812238132381423815238162381723818238192382023821238222382323824238252382623827238282382923830238312383223833238342383523836238372383823839238402384123842238432384423845238462384723848238492385023851238522385323854238552385623857238582385923860238612386223863238642386523866238672386823869238702387123872238732387423875238762387723878238792388023881238822388323884238852388623887238882388923890238912389223893238942389523896238972389823899239002390123902239032390423905239062390723908239092391023911239122391323914239152391623917239182391923920239212392223923239242392523926239272392823929239302393123932239332393423935239362393723938239392394023941239422394323944239452394623947239482394923950239512395223953239542395523956239572395823959239602396123962239632396423965239662396723968239692397023971239722397323974239752397623977239782397923980239812398223983239842398523986239872398823989239902399123992239932399423995239962399723998239992400024001240022400324004240052400624007240082400924010240112401224013240142401524016240172401824019240202402124022240232402424025240262402724028240292403024031240322403324034240352403624037240382403924040240412404224043240442404524046240472404824049240502405124052240532405424055240562405724058240592406024061240622406324064240652406624067240682406924070240712407224073240742407524076240772407824079240802408124082240832408424085240862408724088240892409024091240922409324094240952409624097240982409924100241012410224103241042410524106241072410824109241102411124112241132411424115241162411724118241192412024121241222412324124241252412624127241282412924130241312413224133241342413524136241372413824139241402414124142241432414424145241462414724148241492415024151241522415324154241552415624157241582415924160241612416224163241642416524166241672416824169241702417124172241732417424175241762417724178241792418024181241822418324184241852418624187241882418924190241912419224193241942419524196241972419824199242002420124202242032420424205242062420724208242092421024211242122421324214242152421624217242182421924220242212422224223242242422524226242272422824229242302423124232242332423424235242362423724238242392424024241242422424324244242452424624247242482424924250242512425224253242542425524256242572425824259242602426124262242632426424265242662426724268242692427024271242722427324274242752427624277242782427924280242812428224283242842428524286242872428824289242902429124292242932429424295242962429724298242992430024301243022430324304243052430624307243082430924310243112431224313243142431524316243172431824319243202432124322243232432424325243262432724328243292433024331243322433324334243352433624337243382433924340243412434224343243442434524346243472434824349243502435124352243532435424355243562435724358243592436024361243622436324364243652436624367243682436924370243712437224373243742437524376243772437824379243802438124382243832438424385243862438724388243892439024391243922439324394243952439624397243982439924400244012440224403244042440524406244072440824409244102441124412244132441424415244162441724418244192442024421244222442324424244252442624427244282442924430244312443224433244342443524436244372443824439244402444124442244432444424445244462444724448244492445024451244522445324454244552445624457244582445924460244612446224463244642446524466244672446824469244702447124472244732447424475244762447724478244792448024481244822448324484244852448624487244882448924490244912449224493244942449524496244972449824499245002450124502245032450424505245062450724508245092451024511245122451324514245152451624517245182451924520245212452224523245242452524526245272452824529245302453124532245332453424535245362453724538245392454024541245422454324544245452454624547245482454924550245512455224553245542455524556245572455824559245602456124562245632456424565245662456724568245692457024571245722457324574245752457624577245782457924580245812458224583245842458524586245872458824589245902459124592245932459424595245962459724598245992460024601246022460324604246052460624607246082460924610246112461224613246142461524616246172461824619246202462124622246232462424625246262462724628246292463024631246322463324634246352463624637246382463924640246412464224643246442464524646246472464824649246502465124652246532465424655246562465724658246592466024661246622466324664246652466624667246682466924670246712467224673246742467524676246772467824679246802468124682246832468424685246862468724688246892469024691246922469324694246952469624697246982469924700247012470224703247042470524706247072470824709247102471124712247132471424715247162471724718247192472024721247222472324724247252472624727247282472924730247312473224733247342473524736247372473824739247402474124742247432474424745247462474724748247492475024751247522475324754247552475624757247582475924760247612476224763247642476524766247672476824769247702477124772247732477424775247762477724778247792478024781247822478324784247852478624787247882478924790247912479224793247942479524796247972479824799248002480124802248032480424805248062480724808248092481024811248122481324814248152481624817248182481924820248212482224823248242482524826248272482824829248302483124832248332483424835248362483724838248392484024841248422484324844248452484624847248482484924850248512485224853248542485524856248572485824859248602486124862248632486424865248662486724868248692487024871248722487324874248752487624877248782487924880248812488224883248842488524886248872488824889248902489124892248932489424895248962489724898248992490024901249022490324904249052490624907249082490924910249112491224913249142491524916249172491824919249202492124922249232492424925249262492724928249292493024931249322493324934249352493624937249382493924940249412494224943249442494524946249472494824949249502495124952249532495424955249562495724958249592496024961249622496324964249652496624967249682496924970249712497224973249742497524976249772497824979249802498124982249832498424985249862498724988249892499024991249922499324994249952499624997249982499925000250012500225003250042500525006250072500825009250102501125012250132501425015250162501725018250192502025021250222502325024250252502625027250282502925030250312503225033250342503525036250372503825039250402504125042250432504425045250462504725048250492505025051250522505325054250552505625057250582505925060250612506225063250642506525066250672506825069250702507125072250732507425075250762507725078250792508025081250822508325084250852508625087250882508925090250912509225093250942509525096250972509825099251002510125102251032510425105251062510725108251092511025111251122511325114251152511625117251182511925120251212512225123251242512525126251272512825129251302513125132251332513425135251362513725138251392514025141251422514325144251452514625147251482514925150251512515225153251542515525156251572515825159251602516125162251632516425165251662516725168251692517025171251722517325174251752517625177251782517925180251812518225183251842518525186251872518825189251902519125192251932519425195251962519725198251992520025201252022520325204252052520625207252082520925210252112521225213252142521525216252172521825219252202522125222252232522425225252262522725228252292523025231252322523325234252352523625237252382523925240252412524225243252442524525246252472524825249252502525125252252532525425255252562525725258252592526025261252622526325264252652526625267252682526925270252712527225273252742527525276252772527825279252802528125282252832528425285252862528725288252892529025291252922529325294252952529625297252982529925300253012530225303253042530525306253072530825309253102531125312253132531425315253162531725318253192532025321253222532325324253252532625327253282532925330253312533225333253342533525336253372533825339253402534125342253432534425345253462534725348253492535025351253522535325354253552535625357253582535925360253612536225363253642536525366253672536825369253702537125372253732537425375253762537725378253792538025381253822538325384253852538625387253882538925390253912539225393253942539525396253972539825399254002540125402254032540425405254062540725408254092541025411254122541325414254152541625417254182541925420254212542225423254242542525426254272542825429254302543125432254332543425435254362543725438254392544025441254422544325444254452544625447254482544925450254512545225453254542545525456254572545825459254602546125462254632546425465254662546725468254692547025471254722547325474254752547625477254782547925480254812548225483254842548525486254872548825489254902549125492254932549425495254962549725498254992550025501255022550325504255052550625507255082550925510255112551225513255142551525516255172551825519255202552125522255232552425525255262552725528255292553025531255322553325534255352553625537255382553925540255412554225543255442554525546255472554825549255502555125552255532555425555255562555725558255592556025561255622556325564255652556625567255682556925570255712557225573255742557525576255772557825579255802558125582255832558425585255862558725588255892559025591255922559325594255952559625597255982559925600256012560225603256042560525606256072560825609256102561125612256132561425615256162561725618256192562025621256222562325624256252562625627256282562925630256312563225633256342563525636256372563825639256402564125642256432564425645256462564725648256492565025651256522565325654256552565625657256582565925660256612566225663256642566525666256672566825669256702567125672256732567425675256762567725678256792568025681256822568325684256852568625687256882568925690256912569225693256942569525696256972569825699257002570125702257032570425705257062570725708257092571025711257122571325714257152571625717257182571925720257212572225723257242572525726257272572825729257302573125732257332573425735257362573725738257392574025741257422574325744257452574625747257482574925750257512575225753257542575525756257572575825759257602576125762257632576425765257662576725768257692577025771257722577325774257752577625777257782577925780257812578225783257842578525786257872578825789257902579125792257932579425795257962579725798257992580025801258022580325804258052580625807258082580925810258112581225813258142581525816258172581825819258202582125822258232582425825258262582725828258292583025831258322583325834258352583625837258382583925840258412584225843258442584525846258472584825849258502585125852258532585425855258562585725858258592586025861258622586325864258652586625867258682586925870258712587225873258742587525876258772587825879258802588125882258832588425885258862588725888258892589025891258922589325894258952589625897258982589925900259012590225903259042590525906259072590825909259102591125912259132591425915259162591725918259192592025921259222592325924259252592625927259282592925930259312593225933259342593525936259372593825939259402594125942259432594425945259462594725948259492595025951259522595325954259552595625957259582595925960259612596225963259642596525966259672596825969259702597125972259732597425975259762597725978259792598025981259822598325984259852598625987259882598925990259912599225993259942599525996259972599825999260002600126002260032600426005260062600726008260092601026011260122601326014260152601626017260182601926020260212602226023260242602526026260272602826029260302603126032260332603426035260362603726038260392604026041260422604326044260452604626047260482604926050260512605226053260542605526056260572605826059260602606126062260632606426065260662606726068260692607026071260722607326074260752607626077260782607926080260812608226083260842608526086260872608826089260902609126092260932609426095260962609726098260992610026101261022610326104261052610626107261082610926110261112611226113261142611526116261172611826119261202612126122261232612426125261262612726128261292613026131261322613326134261352613626137261382613926140261412614226143261442614526146261472614826149261502615126152261532615426155261562615726158261592616026161261622616326164261652616626167261682616926170261712617226173261742617526176261772617826179261802618126182261832618426185261862618726188261892619026191261922619326194261952619626197261982619926200262012620226203262042620526206262072620826209262102621126212262132621426215262162621726218262192622026221262222622326224262252622626227262282622926230262312623226233262342623526236262372623826239262402624126242262432624426245262462624726248262492625026251262522625326254262552625626257262582625926260262612626226263262642626526266262672626826269262702627126272262732627426275262762627726278262792628026281262822628326284262852628626287262882628926290262912629226293262942629526296262972629826299263002630126302263032630426305263062630726308263092631026311263122631326314263152631626317263182631926320263212632226323263242632526326263272632826329263302633126332263332633426335263362633726338263392634026341263422634326344263452634626347263482634926350263512635226353263542635526356263572635826359263602636126362263632636426365263662636726368263692637026371263722637326374263752637626377263782637926380263812638226383263842638526386263872638826389263902639126392263932639426395263962639726398263992640026401264022640326404264052640626407264082640926410264112641226413264142641526416264172641826419264202642126422264232642426425264262642726428264292643026431264322643326434264352643626437264382643926440264412644226443264442644526446264472644826449264502645126452264532645426455264562645726458264592646026461264622646326464264652646626467264682646926470264712647226473264742647526476264772647826479264802648126482264832648426485264862648726488264892649026491264922649326494264952649626497264982649926500265012650226503265042650526506265072650826509265102651126512265132651426515265162651726518265192652026521265222652326524265252652626527265282652926530265312653226533265342653526536265372653826539265402654126542265432654426545265462654726548265492655026551265522655326554265552655626557265582655926560265612656226563265642656526566265672656826569265702657126572265732657426575265762657726578265792658026581265822658326584265852658626587265882658926590265912659226593265942659526596265972659826599266002660126602266032660426605266062660726608266092661026611266122661326614266152661626617266182661926620266212662226623266242662526626266272662826629266302663126632266332663426635266362663726638266392664026641266422664326644266452664626647266482664926650266512665226653266542665526656266572665826659266602666126662266632666426665266662666726668266692667026671266722667326674266752667626677266782667926680266812668226683266842668526686266872668826689266902669126692266932669426695266962669726698266992670026701267022670326704267052670626707267082670926710267112671226713267142671526716267172671826719267202672126722267232672426725267262672726728267292673026731267322673326734267352673626737267382673926740267412674226743267442674526746267472674826749267502675126752267532675426755267562675726758267592676026761267622676326764267652676626767267682676926770267712677226773267742677526776267772677826779267802678126782267832678426785267862678726788267892679026791267922679326794267952679626797267982679926800268012680226803268042680526806268072680826809268102681126812268132681426815268162681726818268192682026821268222682326824268252682626827268282682926830268312683226833268342683526836268372683826839268402684126842268432684426845268462684726848268492685026851268522685326854268552685626857268582685926860268612686226863268642686526866268672686826869268702687126872268732687426875268762687726878268792688026881268822688326884268852688626887268882688926890268912689226893268942689526896268972689826899269002690126902269032690426905269062690726908269092691026911269122691326914269152691626917269182691926920269212692226923269242692526926269272692826929269302693126932269332693426935269362693726938269392694026941269422694326944269452694626947269482694926950269512695226953269542695526956269572695826959269602696126962269632696426965269662696726968269692697026971269722697326974269752697626977269782697926980269812698226983269842698526986269872698826989269902699126992269932699426995269962699726998269992700027001270022700327004270052700627007270082700927010270112701227013270142701527016270172701827019270202702127022270232702427025270262702727028270292703027031270322703327034270352703627037270382703927040270412704227043270442704527046270472704827049270502705127052270532705427055270562705727058270592706027061270622706327064270652706627067270682706927070270712707227073270742707527076270772707827079270802708127082270832708427085270862708727088270892709027091270922709327094270952709627097270982709927100271012710227103271042710527106271072710827109271102711127112271132711427115271162711727118271192712027121271222712327124271252712627127271282712927130271312713227133271342713527136271372713827139271402714127142271432714427145271462714727148271492715027151271522715327154271552715627157271582715927160271612716227163271642716527166271672716827169271702717127172271732717427175271762717727178271792718027181271822718327184271852718627187271882718927190271912719227193271942719527196271972719827199272002720127202272032720427205272062720727208272092721027211272122721327214272152721627217272182721927220272212722227223272242722527226272272722827229272302723127232272332723427235272362723727238272392724027241272422724327244272452724627247272482724927250272512725227253272542725527256272572725827259272602726127262272632726427265272662726727268272692727027271272722727327274272752727627277272782727927280272812728227283272842728527286272872728827289272902729127292272932729427295272962729727298272992730027301273022730327304273052730627307273082730927310273112731227313273142731527316273172731827319273202732127322273232732427325273262732727328273292733027331273322733327334273352733627337273382733927340273412734227343273442734527346273472734827349273502735127352273532735427355273562735727358273592736027361273622736327364273652736627367273682736927370273712737227373273742737527376273772737827379273802738127382273832738427385273862738727388273892739027391273922739327394273952739627397273982739927400274012740227403274042740527406274072740827409274102741127412274132741427415274162741727418274192742027421274222742327424274252742627427274282742927430274312743227433274342743527436274372743827439274402744127442274432744427445274462744727448274492745027451274522745327454274552745627457274582745927460274612746227463274642746527466274672746827469274702747127472274732747427475274762747727478274792748027481274822748327484274852748627487274882748927490274912749227493274942749527496274972749827499275002750127502275032750427505275062750727508275092751027511275122751327514275152751627517275182751927520275212752227523275242752527526275272752827529275302753127532275332753427535275362753727538275392754027541275422754327544275452754627547275482754927550275512755227553275542755527556275572755827559275602756127562275632756427565275662756727568275692757027571275722757327574275752757627577275782757927580275812758227583275842758527586275872758827589275902759127592275932759427595275962759727598275992760027601276022760327604276052760627607276082760927610276112761227613276142761527616276172761827619276202762127622276232762427625276262762727628276292763027631276322763327634276352763627637276382763927640276412764227643276442764527646276472764827649276502765127652276532765427655276562765727658276592766027661276622766327664276652766627667276682766927670276712767227673276742767527676276772767827679276802768127682276832768427685276862768727688276892769027691276922769327694276952769627697276982769927700277012770227703277042770527706277072770827709277102771127712277132771427715277162771727718277192772027721277222772327724277252772627727277282772927730277312773227733277342773527736277372773827739277402774127742277432774427745277462774727748277492775027751277522775327754277552775627757277582775927760277612776227763277642776527766277672776827769277702777127772277732777427775277762777727778277792778027781277822778327784277852778627787277882778927790277912779227793277942779527796277972779827799278002780127802278032780427805278062780727808278092781027811278122781327814278152781627817278182781927820278212782227823278242782527826278272782827829278302783127832278332783427835278362783727838278392784027841278422784327844278452784627847278482784927850278512785227853278542785527856278572785827859278602786127862278632786427865278662786727868278692787027871278722787327874278752787627877278782787927880278812788227883278842788527886278872788827889278902789127892278932789427895278962789727898278992790027901279022790327904279052790627907279082790927910279112791227913279142791527916279172791827919279202792127922279232792427925279262792727928279292793027931279322793327934279352793627937279382793927940279412794227943279442794527946279472794827949279502795127952279532795427955279562795727958279592796027961279622796327964279652796627967279682796927970279712797227973279742797527976279772797827979279802798127982279832798427985279862798727988279892799027991279922799327994279952799627997279982799928000280012800228003280042800528006280072800828009280102801128012280132801428015280162801728018280192802028021280222802328024280252802628027280282802928030280312803228033280342803528036280372803828039280402804128042280432804428045280462804728048280492805028051280522805328054280552805628057280582805928060280612806228063280642806528066280672806828069280702807128072280732807428075280762807728078280792808028081280822808328084280852808628087280882808928090280912809228093280942809528096280972809828099281002810128102281032810428105281062810728108281092811028111281122811328114281152811628117281182811928120281212812228123281242812528126281272812828129281302813128132281332813428135281362813728138281392814028141281422814328144281452814628147281482814928150281512815228153281542815528156281572815828159281602816128162281632816428165281662816728168281692817028171281722817328174281752817628177281782817928180281812818228183281842818528186281872818828189281902819128192281932819428195281962819728198281992820028201282022820328204282052820628207282082820928210282112821228213282142821528216282172821828219282202822128222282232822428225282262822728228282292823028231282322823328234282352823628237282382823928240282412824228243282442824528246282472824828249282502825128252282532825428255282562825728258282592826028261282622826328264282652826628267282682826928270282712827228273282742827528276282772827828279282802828128282282832828428285282862828728288282892829028291282922829328294282952829628297282982829928300283012830228303283042830528306283072830828309283102831128312283132831428315283162831728318283192832028321283222832328324283252832628327283282832928330283312833228333283342833528336283372833828339283402834128342283432834428345283462834728348283492835028351283522835328354283552835628357283582835928360283612836228363283642836528366283672836828369283702837128372283732837428375283762837728378283792838028381283822838328384283852838628387283882838928390283912839228393283942839528396283972839828399284002840128402284032840428405284062840728408284092841028411284122841328414284152841628417284182841928420284212842228423284242842528426284272842828429284302843128432284332843428435284362843728438284392844028441284422844328444284452844628447284482844928450284512845228453284542845528456284572845828459284602846128462284632846428465284662846728468284692847028471284722847328474284752847628477284782847928480284812848228483284842848528486284872848828489284902849128492284932849428495284962849728498284992850028501285022850328504285052850628507285082850928510285112851228513285142851528516285172851828519285202852128522285232852428525285262852728528285292853028531285322853328534285352853628537285382853928540285412854228543285442854528546285472854828549285502855128552285532855428555285562855728558285592856028561285622856328564285652856628567285682856928570285712857228573285742857528576285772857828579285802858128582285832858428585285862858728588285892859028591285922859328594285952859628597285982859928600286012860228603286042860528606286072860828609286102861128612286132861428615286162861728618286192862028621286222862328624286252862628627286282862928630286312863228633286342863528636286372863828639286402864128642286432864428645286462864728648286492865028651286522865328654286552865628657286582865928660286612866228663286642866528666286672866828669286702867128672286732867428675286762867728678286792868028681286822868328684286852868628687286882868928690286912869228693286942869528696286972869828699287002870128702287032870428705287062870728708287092871028711287122871328714287152871628717287182871928720287212872228723287242872528726287272872828729287302873128732287332873428735287362873728738287392874028741287422874328744287452874628747287482874928750287512875228753287542875528756287572875828759287602876128762287632876428765287662876728768287692877028771287722877328774287752877628777287782877928780287812878228783287842878528786287872878828789287902879128792287932879428795287962879728798287992880028801288022880328804288052880628807288082880928810288112881228813288142881528816288172881828819288202882128822288232882428825288262882728828288292883028831288322883328834288352883628837288382883928840288412884228843288442884528846288472884828849288502885128852288532885428855288562885728858288592886028861288622886328864288652886628867288682886928870288712887228873288742887528876288772887828879288802888128882288832888428885288862888728888288892889028891288922889328894288952889628897288982889928900289012890228903289042890528906289072890828909289102891128912289132891428915289162891728918289192892028921289222892328924289252892628927289282892928930289312893228933289342893528936289372893828939289402894128942289432894428945289462894728948289492895028951289522895328954289552895628957289582895928960289612896228963289642896528966289672896828969289702897128972289732897428975289762897728978289792898028981289822898328984289852898628987289882898928990289912899228993289942899528996289972899828999290002900129002290032900429005290062900729008290092901029011290122901329014290152901629017290182901929020290212902229023290242902529026290272902829029290302903129032290332903429035290362903729038290392904029041290422904329044290452904629047290482904929050290512905229053290542905529056290572905829059290602906129062290632906429065290662906729068290692907029071290722907329074290752907629077290782907929080290812908229083290842908529086290872908829089290902909129092290932909429095290962909729098290992910029101291022910329104291052910629107291082910929110291112911229113291142911529116291172911829119291202912129122291232912429125291262912729128291292913029131291322913329134291352913629137291382913929140291412914229143291442914529146291472914829149291502915129152291532915429155291562915729158291592916029161291622916329164291652916629167291682916929170291712917229173291742917529176291772917829179291802918129182291832918429185291862918729188291892919029191291922919329194291952919629197291982919929200292012920229203292042920529206292072920829209292102921129212292132921429215292162921729218292192922029221292222922329224292252922629227292282922929230292312923229233292342923529236292372923829239292402924129242292432924429245292462924729248292492925029251292522925329254292552925629257292582925929260292612926229263292642926529266292672926829269292702927129272292732927429275292762927729278292792928029281292822928329284292852928629287292882928929290292912929229293292942929529296292972929829299293002930129302293032930429305293062930729308293092931029311293122931329314293152931629317293182931929320293212932229323293242932529326293272932829329293302933129332293332933429335293362933729338293392934029341293422934329344293452934629347293482934929350293512935229353293542935529356293572935829359293602936129362293632936429365293662936729368293692937029371293722937329374293752937629377293782937929380293812938229383293842938529386293872938829389293902939129392293932939429395293962939729398293992940029401294022940329404294052940629407294082940929410294112941229413294142941529416294172941829419294202942129422294232942429425294262942729428294292943029431294322943329434294352943629437294382943929440294412944229443294442944529446294472944829449294502945129452294532945429455294562945729458294592946029461294622946329464294652946629467294682946929470294712947229473294742947529476294772947829479294802948129482294832948429485294862948729488294892949029491294922949329494294952949629497294982949929500295012950229503295042950529506295072950829509295102951129512295132951429515295162951729518295192952029521295222952329524295252952629527295282952929530295312953229533295342953529536295372953829539295402954129542295432954429545295462954729548295492955029551295522955329554295552955629557295582955929560295612956229563295642956529566295672956829569295702957129572295732957429575295762957729578295792958029581295822958329584295852958629587295882958929590295912959229593295942959529596295972959829599296002960129602296032960429605296062960729608296092961029611296122961329614296152961629617296182961929620296212962229623296242962529626296272962829629296302963129632296332963429635296362963729638296392964029641296422964329644296452964629647296482964929650296512965229653296542965529656296572965829659296602966129662296632966429665296662966729668296692967029671296722967329674296752967629677296782967929680296812968229683296842968529686296872968829689296902969129692296932969429695296962969729698296992970029701297022970329704297052970629707297082970929710297112971229713297142971529716297172971829719297202972129722297232972429725297262972729728297292973029731297322973329734297352973629737297382973929740297412974229743297442974529746297472974829749297502975129752297532975429755297562975729758297592976029761297622976329764297652976629767297682976929770297712977229773297742977529776297772977829779297802978129782297832978429785297862978729788297892979029791297922979329794297952979629797297982979929800298012980229803298042980529806298072980829809298102981129812298132981429815298162981729818298192982029821298222982329824298252982629827298282982929830298312983229833298342983529836298372983829839298402984129842298432984429845298462984729848298492985029851298522985329854298552985629857298582985929860298612986229863298642986529866298672986829869298702987129872298732987429875298762987729878298792988029881298822988329884298852988629887298882988929890298912989229893298942989529896298972989829899299002990129902299032990429905299062990729908299092991029911299122991329914299152991629917299182991929920299212992229923299242992529926299272992829929299302993129932299332993429935299362993729938299392994029941299422994329944299452994629947299482994929950299512995229953299542995529956299572995829959299602996129962299632996429965299662996729968299692997029971299722997329974299752997629977299782997929980299812998229983299842998529986299872998829989299902999129992299932999429995299962999729998299993000030001300023000330004300053000630007300083000930010300113001230013300143001530016300173001830019300203002130022300233002430025300263002730028300293003030031300323003330034300353003630037300383003930040300413004230043300443004530046300473004830049300503005130052300533005430055300563005730058300593006030061300623006330064300653006630067300683006930070300713007230073300743007530076300773007830079300803008130082300833008430085300863008730088300893009030091300923009330094300953009630097300983009930100301013010230103301043010530106301073010830109301103011130112301133011430115301163011730118301193012030121301223012330124301253012630127301283012930130301313013230133301343013530136301373013830139301403014130142301433014430145301463014730148301493015030151301523015330154301553015630157301583015930160301613016230163301643016530166301673016830169301703017130172301733017430175301763017730178301793018030181301823018330184301853018630187301883018930190301913019230193301943019530196301973019830199302003020130202302033020430205302063020730208302093021030211302123021330214302153021630217302183021930220302213022230223302243022530226302273022830229302303023130232302333023430235302363023730238302393024030241302423024330244302453024630247302483024930250302513025230253302543025530256302573025830259302603026130262302633026430265302663026730268302693027030271302723027330274302753027630277302783027930280302813028230283302843028530286302873028830289302903029130292302933029430295302963029730298302993030030301303023030330304303053030630307303083030930310303113031230313303143031530316303173031830319303203032130322303233032430325303263032730328303293033030331303323033330334303353033630337303383033930340303413034230343303443034530346303473034830349303503035130352303533035430355303563035730358303593036030361303623036330364303653036630367303683036930370303713037230373303743037530376303773037830379303803038130382303833038430385303863038730388303893039030391303923039330394303953039630397303983039930400304013040230403304043040530406304073040830409304103041130412304133041430415304163041730418304193042030421304223042330424304253042630427304283042930430304313043230433304343043530436304373043830439304403044130442304433044430445304463044730448304493045030451304523045330454304553045630457304583045930460304613046230463304643046530466304673046830469304703047130472304733047430475304763047730478304793048030481304823048330484304853048630487304883048930490304913049230493304943049530496304973049830499305003050130502305033050430505305063050730508305093051030511305123051330514305153051630517305183051930520305213052230523305243052530526305273052830529305303053130532305333053430535305363053730538305393054030541305423054330544305453054630547305483054930550305513055230553305543055530556305573055830559305603056130562305633056430565305663056730568305693057030571305723057330574305753057630577305783057930580305813058230583305843058530586305873058830589305903059130592305933059430595305963059730598305993060030601306023060330604306053060630607306083060930610306113061230613306143061530616306173061830619306203062130622306233062430625306263062730628306293063030631306323063330634306353063630637306383063930640306413064230643306443064530646306473064830649306503065130652306533065430655306563065730658306593066030661306623066330664306653066630667306683066930670306713067230673306743067530676306773067830679306803068130682306833068430685306863068730688306893069030691306923069330694306953069630697306983069930700307013070230703307043070530706307073070830709307103071130712307133071430715307163071730718307193072030721307223072330724307253072630727307283072930730307313073230733307343073530736307373073830739307403074130742307433074430745307463074730748307493075030751307523075330754307553075630757307583075930760307613076230763307643076530766307673076830769307703077130772307733077430775307763077730778307793078030781307823078330784307853078630787307883078930790307913079230793307943079530796307973079830799308003080130802308033080430805308063080730808308093081030811308123081330814308153081630817308183081930820308213082230823308243082530826308273082830829308303083130832308333083430835308363083730838308393084030841308423084330844308453084630847308483084930850308513085230853308543085530856308573085830859308603086130862308633086430865308663086730868308693087030871308723087330874308753087630877308783087930880308813088230883308843088530886308873088830889308903089130892308933089430895308963089730898308993090030901309023090330904309053090630907309083090930910309113091230913309143091530916309173091830919309203092130922309233092430925309263092730928309293093030931309323093330934309353093630937309383093930940309413094230943309443094530946309473094830949309503095130952309533095430955309563095730958309593096030961309623096330964309653096630967309683096930970309713097230973309743097530976309773097830979309803098130982309833098430985309863098730988309893099030991309923099330994309953099630997309983099931000310013100231003310043100531006310073100831009310103101131012310133101431015310163101731018310193102031021310223102331024310253102631027310283102931030310313103231033310343103531036310373103831039310403104131042310433104431045310463104731048310493105031051310523105331054310553105631057310583105931060310613106231063310643106531066310673106831069310703107131072310733107431075310763107731078310793108031081310823108331084310853108631087310883108931090310913109231093310943109531096310973109831099311003110131102311033110431105311063110731108311093111031111311123111331114311153111631117311183111931120311213112231123311243112531126311273112831129311303113131132311333113431135311363113731138311393114031141311423114331144311453114631147311483114931150311513115231153311543115531156311573115831159311603116131162311633116431165311663116731168311693117031171311723117331174311753117631177311783117931180311813118231183311843118531186311873118831189311903119131192311933119431195311963119731198311993120031201312023120331204312053120631207312083120931210312113121231213312143121531216312173121831219312203122131222312233122431225312263122731228312293123031231312323123331234312353123631237312383123931240312413124231243312443124531246312473124831249312503125131252312533125431255312563125731258312593126031261312623126331264312653126631267312683126931270312713127231273312743127531276312773127831279312803128131282312833128431285312863128731288312893129031291312923129331294312953129631297312983129931300313013130231303313043130531306313073130831309313103131131312313133131431315313163131731318313193132031321313223132331324313253132631327313283132931330313313133231333313343133531336313373133831339313403134131342313433134431345313463134731348313493135031351313523135331354313553135631357313583135931360313613136231363313643136531366313673136831369313703137131372313733137431375313763137731378313793138031381313823138331384313853138631387313883138931390313913139231393313943139531396313973139831399314003140131402314033140431405314063140731408314093141031411314123141331414314153141631417314183141931420314213142231423314243142531426314273142831429314303143131432314333143431435314363143731438314393144031441314423144331444314453144631447314483144931450314513145231453314543145531456314573145831459314603146131462314633146431465314663146731468314693147031471314723147331474314753147631477314783147931480314813148231483314843148531486314873148831489314903149131492314933149431495314963149731498314993150031501315023150331504315053150631507315083150931510315113151231513315143151531516315173151831519315203152131522315233152431525315263152731528315293153031531315323153331534315353153631537315383153931540315413154231543315443154531546315473154831549315503155131552315533155431555315563155731558315593156031561315623156331564315653156631567315683156931570315713157231573315743157531576315773157831579315803158131582315833158431585315863158731588315893159031591315923159331594315953159631597315983159931600316013160231603316043160531606316073160831609316103161131612316133161431615316163161731618316193162031621316223162331624316253162631627316283162931630316313163231633316343163531636316373163831639316403164131642316433164431645316463164731648316493165031651316523165331654316553165631657316583165931660316613166231663316643166531666316673166831669316703167131672316733167431675316763167731678316793168031681316823168331684316853168631687316883168931690316913169231693316943169531696316973169831699317003170131702317033170431705317063170731708317093171031711317123171331714317153171631717317183171931720317213172231723317243172531726317273172831729317303173131732317333173431735317363173731738317393174031741317423174331744317453174631747317483174931750317513175231753317543175531756317573175831759317603176131762317633176431765317663176731768317693177031771317723177331774317753177631777317783177931780317813178231783317843178531786317873178831789317903179131792317933179431795317963179731798317993180031801318023180331804318053180631807318083180931810318113181231813318143181531816318173181831819318203182131822318233182431825318263182731828318293183031831318323183331834318353183631837318383183931840318413184231843318443184531846318473184831849318503185131852318533185431855318563185731858318593186031861318623186331864318653186631867318683186931870318713187231873318743187531876318773187831879318803188131882318833188431885318863188731888318893189031891318923189331894318953189631897318983189931900319013190231903319043190531906319073190831909319103191131912319133191431915319163191731918319193192031921319223192331924319253192631927319283192931930319313193231933319343193531936319373193831939319403194131942319433194431945319463194731948319493195031951319523195331954319553195631957319583195931960319613196231963319643196531966319673196831969319703197131972319733197431975319763197731978319793198031981319823198331984319853198631987319883198931990319913199231993319943199531996319973199831999320003200132002320033200432005320063200732008320093201032011320123201332014320153201632017320183201932020320213202232023320243202532026320273202832029320303203132032320333203432035320363203732038320393204032041320423204332044320453204632047320483204932050320513205232053320543205532056320573205832059320603206132062320633206432065320663206732068320693207032071320723207332074320753207632077320783207932080320813208232083320843208532086320873208832089320903209132092320933209432095320963209732098320993210032101321023210332104321053210632107321083210932110321113211232113321143211532116321173211832119321203212132122321233212432125321263212732128321293213032131321323213332134321353213632137321383213932140321413214232143321443214532146321473214832149321503215132152321533215432155321563215732158321593216032161321623216332164321653216632167321683216932170321713217232173321743217532176321773217832179321803218132182321833218432185321863218732188321893219032191321923219332194321953219632197321983219932200322013220232203322043220532206322073220832209322103221132212322133221432215322163221732218322193222032221322223222332224322253222632227322283222932230322313223232233322343223532236322373223832239322403224132242322433224432245322463224732248322493225032251322523225332254322553225632257322583225932260322613226232263322643226532266322673226832269322703227132272322733227432275322763227732278322793228032281322823228332284322853228632287322883228932290322913229232293322943229532296322973229832299323003230132302323033230432305323063230732308323093231032311323123231332314323153231632317323183231932320323213232232323323243232532326323273232832329323303233132332323333233432335323363233732338323393234032341323423234332344323453234632347323483234932350323513235232353323543235532356323573235832359323603236132362323633236432365323663236732368323693237032371323723237332374323753237632377323783237932380323813238232383323843238532386323873238832389323903239132392323933239432395323963239732398323993240032401324023240332404324053240632407324083240932410324113241232413324143241532416324173241832419324203242132422324233242432425324263242732428324293243032431324323243332434324353243632437324383243932440324413244232443324443244532446324473244832449324503245132452324533245432455324563245732458324593246032461324623246332464324653246632467324683246932470324713247232473324743247532476324773247832479324803248132482324833248432485324863248732488324893249032491324923249332494324953249632497324983249932500325013250232503325043250532506325073250832509325103251132512325133251432515325163251732518325193252032521325223252332524325253252632527325283252932530325313253232533325343253532536325373253832539325403254132542325433254432545325463254732548325493255032551325523255332554325553255632557325583255932560325613256232563325643256532566325673256832569325703257132572325733257432575325763257732578325793258032581325823258332584325853258632587325883258932590325913259232593325943259532596325973259832599326003260132602326033260432605326063260732608326093261032611326123261332614326153261632617326183261932620326213262232623326243262532626326273262832629326303263132632326333263432635326363263732638326393264032641326423264332644326453264632647326483264932650326513265232653326543265532656326573265832659326603266132662326633266432665326663266732668326693267032671326723267332674326753267632677326783267932680326813268232683326843268532686326873268832689326903269132692326933269432695326963269732698326993270032701327023270332704327053270632707327083270932710327113271232713327143271532716327173271832719327203272132722327233272432725327263272732728327293273032731327323273332734327353273632737327383273932740327413274232743327443274532746327473274832749327503275132752327533275432755327563275732758327593276032761327623276332764327653276632767327683276932770327713277232773327743277532776327773277832779327803278132782327833278432785327863278732788327893279032791327923279332794327953279632797327983279932800328013280232803328043280532806328073280832809328103281132812328133281432815328163281732818328193282032821328223282332824328253282632827328283282932830328313283232833328343283532836328373283832839328403284132842328433284432845328463284732848328493285032851328523285332854328553285632857328583285932860328613286232863328643286532866328673286832869328703287132872328733287432875328763287732878328793288032881328823288332884328853288632887328883288932890328913289232893328943289532896328973289832899329003290132902329033290432905329063290732908329093291032911329123291332914329153291632917329183291932920329213292232923329243292532926329273292832929329303293132932329333293432935329363293732938329393294032941329423294332944329453294632947329483294932950329513295232953329543295532956329573295832959329603296132962329633296432965329663296732968329693297032971329723297332974329753297632977329783297932980329813298232983329843298532986329873298832989329903299132992329933299432995329963299732998329993300033001330023300333004330053300633007330083300933010330113301233013330143301533016330173301833019330203302133022330233302433025330263302733028330293303033031330323303333034330353303633037330383303933040330413304233043330443304533046330473304833049330503305133052330533305433055330563305733058330593306033061330623306333064330653306633067330683306933070330713307233073330743307533076330773307833079330803308133082330833308433085330863308733088330893309033091330923309333094330953309633097330983309933100331013310233103331043310533106331073310833109331103311133112331133311433115331163311733118331193312033121331223312333124331253312633127331283312933130331313313233133331343313533136331373313833139331403314133142331433314433145331463314733148331493315033151331523315333154331553315633157331583315933160331613316233163331643316533166331673316833169331703317133172331733317433175331763317733178331793318033181331823318333184331853318633187331883318933190331913319233193331943319533196331973319833199332003320133202332033320433205332063320733208332093321033211332123321333214332153321633217332183321933220332213322233223332243322533226332273322833229332303323133232332333323433235332363323733238332393324033241332423324333244332453324633247332483324933250332513325233253332543325533256332573325833259332603326133262332633326433265332663326733268332693327033271332723327333274332753327633277332783327933280332813328233283332843328533286332873328833289332903329133292332933329433295332963329733298332993330033301333023330333304333053330633307333083330933310333113331233313333143331533316333173331833319333203332133322333233332433325333263332733328333293333033331333323333333334333353333633337333383333933340333413334233343333443334533346333473334833349333503335133352333533335433355333563335733358333593336033361333623336333364333653336633367333683336933370333713337233373333743337533376333773337833379333803338133382333833338433385333863338733388333893339033391333923339333394333953339633397333983339933400334013340233403334043340533406334073340833409334103341133412334133341433415334163341733418334193342033421334223342333424334253342633427334283342933430334313343233433334343343533436334373343833439334403344133442334433344433445334463344733448334493345033451334523345333454334553345633457334583345933460334613346233463334643346533466334673346833469334703347133472334733347433475334763347733478334793348033481334823348333484334853348633487334883348933490334913349233493334943349533496334973349833499335003350133502335033350433505335063350733508335093351033511335123351333514335153351633517335183351933520335213352233523335243352533526335273352833529335303353133532335333353433535335363353733538335393354033541335423354333544335453354633547335483354933550335513355233553335543355533556335573355833559335603356133562335633356433565335663356733568335693357033571335723357333574335753357633577335783357933580335813358233583335843358533586335873358833589335903359133592335933359433595335963359733598335993360033601336023360333604336053360633607336083360933610336113361233613336143361533616336173361833619336203362133622336233362433625336263362733628336293363033631336323363333634336353363633637336383363933640336413364233643336443364533646336473364833649336503365133652336533365433655336563365733658336593366033661336623366333664336653366633667336683366933670336713367233673336743367533676336773367833679336803368133682336833368433685336863368733688336893369033691336923369333694336953369633697336983369933700337013370233703337043370533706337073370833709337103371133712337133371433715337163371733718337193372033721337223372333724337253372633727337283372933730337313373233733337343373533736337373373833739337403374133742337433374433745337463374733748337493375033751337523375333754337553375633757337583375933760337613376233763337643376533766337673376833769337703377133772337733377433775337763377733778337793378033781337823378333784337853378633787337883378933790337913379233793337943379533796337973379833799338003380133802338033380433805338063380733808338093381033811338123381333814338153381633817338183381933820338213382233823338243382533826338273382833829338303383133832338333383433835338363383733838338393384033841338423384333844338453384633847338483384933850338513385233853338543385533856338573385833859338603386133862338633386433865338663386733868338693387033871338723387333874338753387633877338783387933880338813388233883338843388533886338873388833889338903389133892338933389433895338963389733898338993390033901339023390333904339053390633907339083390933910339113391233913339143391533916339173391833919339203392133922339233392433925339263392733928339293393033931339323393333934339353393633937339383393933940339413394233943339443394533946339473394833949339503395133952339533395433955339563395733958339593396033961339623396333964339653396633967339683396933970339713397233973339743397533976339773397833979339803398133982339833398433985339863398733988339893399033991339923399333994339953399633997339983399934000340013400234003340043400534006340073400834009340103401134012340133401434015340163401734018340193402034021340223402334024340253402634027340283402934030340313403234033340343403534036340373403834039340403404134042340433404434045340463404734048340493405034051340523405334054340553405634057340583405934060340613406234063340643406534066340673406834069340703407134072340733407434075340763407734078340793408034081340823408334084340853408634087340883408934090340913409234093340943409534096340973409834099341003410134102341033410434105341063410734108341093411034111341123411334114341153411634117341183411934120341213412234123341243412534126341273412834129341303413134132341333413434135341363413734138341393414034141341423414334144341453414634147341483414934150341513415234153341543415534156341573415834159341603416134162341633416434165341663416734168341693417034171341723417334174341753417634177341783417934180341813418234183341843418534186341873418834189341903419134192341933419434195341963419734198341993420034201342023420334204342053420634207342083420934210342113421234213342143421534216342173421834219342203422134222342233422434225342263422734228342293423034231342323423334234342353423634237342383423934240342413424234243342443424534246342473424834249342503425134252342533425434255342563425734258342593426034261342623426334264342653426634267342683426934270342713427234273342743427534276342773427834279342803428134282342833428434285342863428734288342893429034291342923429334294342953429634297342983429934300343013430234303343043430534306343073430834309343103431134312343133431434315343163431734318343193432034321343223432334324343253432634327343283432934330343313433234333343343433534336343373433834339343403434134342343433434434345343463434734348343493435034351343523435334354343553435634357343583435934360343613436234363343643436534366343673436834369343703437134372343733437434375343763437734378343793438034381343823438334384343853438634387343883438934390343913439234393343943439534396343973439834399344003440134402344033440434405344063440734408344093441034411344123441334414344153441634417344183441934420344213442234423344243442534426344273442834429344303443134432344333443434435344363443734438344393444034441344423444334444344453444634447344483444934450344513445234453344543445534456344573445834459344603446134462344633446434465344663446734468344693447034471344723447334474344753447634477344783447934480344813448234483344843448534486344873448834489344903449134492344933449434495344963449734498344993450034501345023450334504345053450634507345083450934510345113451234513345143451534516345173451834519345203452134522345233452434525345263452734528345293453034531345323453334534345353453634537345383453934540345413454234543345443454534546345473454834549345503455134552345533455434555345563455734558345593456034561345623456334564345653456634567345683456934570345713457234573345743457534576345773457834579345803458134582345833458434585345863458734588345893459034591345923459334594345953459634597345983459934600346013460234603346043460534606346073460834609346103461134612346133461434615346163461734618346193462034621346223462334624346253462634627346283462934630346313463234633346343463534636346373463834639346403464134642346433464434645346463464734648346493465034651346523465334654346553465634657346583465934660346613466234663346643466534666346673466834669346703467134672346733467434675346763467734678346793468034681346823468334684346853468634687346883468934690346913469234693346943469534696346973469834699347003470134702347033470434705347063470734708347093471034711347123471334714347153471634717347183471934720347213472234723347243472534726347273472834729347303473134732347333473434735347363473734738347393474034741347423474334744347453474634747347483474934750347513475234753347543475534756347573475834759347603476134762 |
- /*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2012, 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 Session Initiation Protocol
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * See Also:
- * \arg \ref AstCREDITS
- *
- * Implementation of RFC 3261 - without S/MIME, and experimental TCP and TLS support
- * Configuration file \link Config_sip sip.conf \endlink
- *
- * ********** IMPORTANT *
- * \note TCP/TLS support is EXPERIMENTAL and WILL CHANGE. This applies to configuration
- * settings, dialplan commands and dialplans apps/functions
- * See \ref sip_tcp_tls
- *
- *
- * ******** General TODO:s
- * \todo Better support of forking
- * \todo VIA branch tag transaction checking
- * \todo Transaction support
- *
- * ******** Wishlist: Improvements
- * - Support of SIP domains for devices, so that we match on username\@domain in the From: header
- * - Connect registrations with a specific device on the incoming call. It's not done
- * automatically in Asterisk
- *
- * \ingroup channel_drivers
- *
- * \par Overview of the handling of SIP sessions
- * The SIP channel handles several types of SIP sessions, or dialogs,
- * not all of them being "telephone calls".
- * - Incoming calls that will be sent to the PBX core
- * - Outgoing calls, generated by the PBX
- * - SIP subscriptions and notifications of states and voicemail messages
- * - SIP registrations, both inbound and outbound
- * - SIP peer management (peerpoke, OPTIONS)
- * - SIP text messages
- *
- * In the SIP channel, there's a list of active SIP dialogs, which includes
- * all of these when they are active. "sip show channels" in the CLI will
- * show most of these, excluding subscriptions which are shown by
- * "sip show subscriptions"
- *
- * \par incoming packets
- * Incoming packets are received in the monitoring thread, then handled by
- * sipsock_read() for udp only. In tcp, packets are read by the tcp_helper thread.
- * sipsock_read() function parses the packet and matches an existing
- * dialog or starts a new SIP dialog.
- *
- * sipsock_read sends the packet to handle_incoming(), that parses a bit more.
- * If it is a response to an outbound request, the packet is sent to handle_response().
- * If it is a request, handle_incoming() sends it to one of a list of functions
- * depending on the request type - INVITE, OPTIONS, REFER, BYE, CANCEL etc
- * sipsock_read locks the ast_channel if it exists (an active call) and
- * unlocks it after we have processed the SIP message.
- *
- * A new INVITE is sent to handle_request_invite(), that will end up
- * starting a new channel in the PBX, the new channel after that executing
- * in a separate channel thread. This is an incoming "call".
- * When the call is answered, either by a bridged channel or the PBX itself
- * the sip_answer() function is called.
- *
- * The actual media - Video or Audio - is mostly handled by the RTP subsystem
- * in rtp.c
- *
- * \par Outbound calls
- * Outbound calls are set up by the PBX through the sip_request_call()
- * function. After that, they are activated by sip_call().
- *
- * \par Hanging up
- * The PBX issues a hangup on both incoming and outgoing calls through
- * the sip_hangup() function
- */
- /*! \li \ref chan_sip.c uses configuration files \ref sip.conf and \ref sip_notify.conf
- * \addtogroup configuration_file
- */
- /*! \page sip.conf sip.conf
- * \verbinclude sip.conf.sample
- */
- /*! \page sip_notify.conf sip_notify.conf
- * \verbinclude sip_notify.conf.sample
- */
- /*!
- * \page sip_tcp_tls SIP TCP and TLS support
- *
- * \par tcpfixes TCP implementation changes needed
- * \todo Fix TCP/TLS handling in dialplan, SRV records, transfers and much more
- * \todo Save TCP/TLS sessions in registry
- * If someone registers a SIPS uri, this forces us to set up a TLS connection back.
- * \todo Add TCP/TLS information to function SIPPEER and CHANNEL function
- * \todo If tcpenable=yes, we must open a TCP socket on the same address as the IP for UDP.
- * The tcpbindaddr config option should only be used to open ADDITIONAL ports
- * So we should propably go back to
- * bindaddr= the default address to bind to. If tcpenable=yes, then bind this to both udp and TCP
- * if tlsenable=yes, open TLS port (provided we also have cert)
- * tcpbindaddr = extra address for additional TCP connections
- * tlsbindaddr = extra address for additional TCP/TLS connections
- * udpbindaddr = extra address for additional UDP connections
- * These three options should take multiple IP/port pairs
- * Note: Since opening additional listen sockets is a *new* feature we do not have today
- * the XXXbindaddr options needs to be disabled until we have support for it
- *
- * \todo re-evaluate the transport= setting in sip.conf. This is right now not well
- * thought of. If a device in sip.conf contacts us via TCP, we should not switch transport,
- * even if udp is the configured first transport.
- *
- * \todo Be prepared for one outbound and another incoming socket per pvt. This applies
- * specially to communication with other peers (proxies).
- * \todo We need to test TCP sessions with SIP proxies and in regards
- * to the SIP outbound specs.
- * \todo ;transport=tls was deprecated in RFC3261 and should not be used at all. See section 26.2.2.
- *
- * \todo If the message is smaller than the given Content-length, the request should get a 400 Bad request
- * message. If it's a response, it should be dropped. (RFC 3261, Section 18.3)
- * \todo Since we have had multidomain support in Asterisk for quite a while, we need to support
- * multiple domains in our TLS implementation, meaning one socket and one cert per domain
- * \todo Selection of transport for a request needs to be done after we've parsed all route headers,
- * also considering outbound proxy options.
- * First request: Outboundproxy, routes, (reg contact or URI. If URI doesn't have port: DNS naptr, srv, AAA)
- * Intermediate requests: Outboundproxy(only when forced), routes, contact/uri
- * DNS naptr support is crucial. A SIP uri might lead to a TLS connection.
- * Also note that due to outbound proxy settings, a SIPS uri might have to be sent on UDP (not to recommend though)
- * \todo Default transports are set to UDP, which cause the wrong behaviour when contacting remote
- * devices directly from the dialplan. UDP is only a fallback if no other method works,
- * in order to be compatible with RFC2543 (SIP/1.0) devices. For transactions that exceed the
- * MTU (like INIVTE with video, audio and RTT) TCP should be preferred.
- *
- * When dialling unconfigured peers (with no port number) or devices in external domains
- * NAPTR records MUST be consulted to find configured transport. If they are not found,
- * SRV records for both TCP and UDP should be checked. If there's a record for TCP, use that.
- * If there's no record for TCP, then use UDP as a last resort. If there's no SRV records,
- * \note this only applies if there's no outbound proxy configured for the session. If an outbound
- * proxy is configured, these procedures might apply for locating the proxy and determining
- * the transport to use for communication with the proxy.
- * \par Other bugs to fix ----
- * __set_address_from_contact(const char *fullcontact, struct sockaddr_in *sin, int tcp)
- * - sets TLS port as default for all TCP connections, unless other port is given in contact.
- * parse_register_contact(struct sip_pvt *pvt, struct sip_peer *peer, struct sip_request *req)
- * - assumes that the contact the UA registers is using the same transport as the REGISTER request, which is
- * a bad guess.
- * - Does not save any information about TCP/TLS connected devices, which is a severe BUG, as discussed on the mailing list.
- * get_destination(struct sip_pvt *p, struct sip_request *oreq)
- * - Doesn't store the information that we got an incoming SIPS request in the channel, so that
- * we can require a secure signalling path OUT of Asterisk (on SIP or IAX2). Possibly, the call should
- * fail on in-secure signalling paths if there's no override in our configuration. At least, provide a
- * channel variable in the dialplan.
- * get_refer_info(struct sip_pvt *transferer, struct sip_request *outgoing_req)
- * - As above, if we have a SIPS: uri in the refer-to header
- * - Does not check transport in refer_to uri.
- */
- /*** MODULEINFO
- <use type="module">res_crypto</use>
- <use type="module">res_http_websocket</use>
- <support_level>extended</support_level>
- ***/
- /*! \page sip_session_timers SIP Session Timers in Asterisk Chan_sip
- The SIP Session-Timers is an extension of the SIP protocol that allows end-points and proxies to
- refresh a session periodically. The sessions are kept alive by sending a RE-INVITE or UPDATE
- request at a negotiated interval. If a session refresh fails then all the entities that support Session-
- Timers clear their internal session state. In addition, UAs generate a BYE request in order to clear
- the state in the proxies and the remote UA (this is done for the benefit of SIP entities in the path
- that do not support Session-Timers).
- The Session-Timers can be configured on a system-wide, per-user, or per-peer basis. The peruser/
- per-peer settings override the global settings. The following new parameters have been
- added to the sip.conf file.
- session-timers=["accept", "originate", "refuse"]
- session-expires=[integer]
- session-minse=[integer]
- session-refresher=["uas", "uac"]
- The session-timers parameter in sip.conf defines the mode of operation of SIP session-timers feature in
- Asterisk. The Asterisk can be configured in one of the following three modes:
- 1. Accept :: In the "accept" mode, the Asterisk server honors session-timers requests
- made by remote end-points. A remote end-point can request Asterisk to engage
- session-timers by either sending it an INVITE request with a "Supported: timer"
- header in it or by responding to Asterisk's INVITE with a 200 OK that contains
- Session-Expires: header in it. In this mode, the Asterisk server does not
- request session-timers from remote end-points. This is the default mode.
- 2. Originate :: In the "originate" mode, the Asterisk server requests the remote
- end-points to activate session-timers in addition to honoring such requests
- made by the remote end-pints. In order to get as much protection as possible
- against hanging SIP channels due to network or end-point failures, Asterisk
- resends periodic re-INVITEs even if a remote end-point does not support
- the session-timers feature.
- 3. Refuse :: In the "refuse" mode, Asterisk acts as if it does not support session-
- timers for inbound or outbound requests. If a remote end-point requests
- session-timers in a dialog, then Asterisk ignores that request unless it's
- noted as a requirement (Require: header), in which case the INVITE is
- rejected with a 420 Bad Extension response.
- */
- #include "asterisk.h"
- ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
- #include <signal.h>
- #include <sys/signal.h>
- #include <regex.h>
- #include <inttypes.h>
- #include "asterisk/network.h"
- #include "asterisk/paths.h" /* need ast_config_AST_SYSTEM_NAME */
- #include "asterisk/lock.h"
- #include "asterisk/config.h"
- #include "asterisk/module.h"
- #include "asterisk/pbx.h"
- #include "asterisk/sched.h"
- #include "asterisk/io.h"
- #include "asterisk/rtp_engine.h"
- #include "asterisk/udptl.h"
- #include "asterisk/acl.h"
- #include "asterisk/manager.h"
- #include "asterisk/callerid.h"
- #include "asterisk/cli.h"
- #include "asterisk/musiconhold.h"
- #include "asterisk/dsp.h"
- #include "asterisk/pickup.h"
- #include "asterisk/parking.h"
- #include "asterisk/srv.h"
- #include "asterisk/astdb.h"
- #include "asterisk/causes.h"
- #include "asterisk/utils.h"
- #include "asterisk/file.h"
- #include "asterisk/astobj2.h"
- #include "asterisk/dnsmgr.h"
- #include "asterisk/devicestate.h"
- #include "asterisk/monitor.h"
- #include "asterisk/netsock2.h"
- #include "asterisk/localtime.h"
- #include "asterisk/abstract_jb.h"
- #include "asterisk/threadstorage.h"
- #include "asterisk/translate.h"
- #include "asterisk/ast_version.h"
- #include "asterisk/data.h"
- #include "asterisk/aoc.h"
- #include "asterisk/message.h"
- #include "sip/include/sip.h"
- #include "sip/include/globals.h"
- #include "sip/include/config_parser.h"
- #include "sip/include/reqresp_parser.h"
- #include "sip/include/sip_utils.h"
- #include "asterisk/sdp_srtp.h"
- #include "asterisk/ccss.h"
- #include "asterisk/xml.h"
- #include "sip/include/dialog.h"
- #include "sip/include/dialplan_functions.h"
- #include "sip/include/security_events.h"
- #include "sip/include/route.h"
- #include "asterisk/sip_api.h"
- #include "asterisk/app.h"
- #include "asterisk/bridge.h"
- #include "asterisk/stasis.h"
- #include "asterisk/stasis_endpoints.h"
- #include "asterisk/stasis_system.h"
- #include "asterisk/stasis_channels.h"
- #include "asterisk/features_config.h"
- #include "asterisk/http_websocket.h"
- #include "asterisk/format_cache.h"
- /*** DOCUMENTATION
- <application name="SIPDtmfMode" language="en_US">
- <synopsis>
- Change the dtmfmode for a SIP call.
- </synopsis>
- <syntax>
- <parameter name="mode" required="true">
- <enumlist>
- <enum name="inband" />
- <enum name="info" />
- <enum name="rfc2833" />
- </enumlist>
- </parameter>
- </syntax>
- <description>
- <para>Changes the dtmfmode for a SIP call.</para>
- </description>
- </application>
- <application name="SIPAddHeader" language="en_US">
- <synopsis>
- Add a SIP header to the outbound call.
- </synopsis>
- <syntax argsep=":">
- <parameter name="Header" required="true" />
- <parameter name="Content" required="true" />
- </syntax>
- <description>
- <para>Adds a header to a SIP call placed with DIAL.</para>
- <para>Remember to use the X-header if you are adding non-standard SIP
- headers, like <literal>X-Asterisk-Accountcode:</literal>. Use this with care.
- Adding the wrong headers may jeopardize the SIP dialog.</para>
- <para>Always returns <literal>0</literal>.</para>
- </description>
- </application>
- <application name="SIPRemoveHeader" language="en_US">
- <synopsis>
- Remove SIP headers previously added with SIPAddHeader
- </synopsis>
- <syntax>
- <parameter name="Header" required="false" />
- </syntax>
- <description>
- <para>SIPRemoveHeader() allows you to remove headers which were previously
- added with SIPAddHeader(). If no parameter is supplied, all previously added
- headers will be removed. If a parameter is supplied, only the matching headers
- will be removed.</para>
- <para>For example you have added these 2 headers:</para>
- <para>SIPAddHeader(P-Asserted-Identity: sip:foo@bar);</para>
- <para>SIPAddHeader(P-Preferred-Identity: sip:bar@foo);</para>
- <para></para>
- <para>// remove all headers</para>
- <para>SIPRemoveHeader();</para>
- <para>// remove all P- headers</para>
- <para>SIPRemoveHeader(P-);</para>
- <para>// remove only the PAI header (note the : at the end)</para>
- <para>SIPRemoveHeader(P-Asserted-Identity:);</para>
- <para></para>
- <para>Always returns <literal>0</literal>.</para>
- </description>
- </application>
- <application name="SIPSendCustomINFO" language="en_US">
- <synopsis>
- Send a custom INFO frame on specified channels.
- </synopsis>
- <syntax>
- <parameter name="Data" required="true" />
- <parameter name="UserAgent" required="false" />
- </syntax>
- <description>
- <para>SIPSendCustomINFO() allows you to send a custom INFO message on all
- active SIP channels or on channels with the specified User Agent. This
- application is only available if TEST_FRAMEWORK is defined.</para>
- </description>
- </application>
- <function name="SIP_HEADER" language="en_US">
- <synopsis>
- Gets the specified SIP header from an incoming INVITE message.
- </synopsis>
- <syntax>
- <parameter name="name" required="true" />
- <parameter name="number">
- <para>If not specified, defaults to <literal>1</literal>.</para>
- </parameter>
- </syntax>
- <description>
- <para>Since there are several headers (such as Via) which can occur multiple
- times, SIP_HEADER takes an optional second argument to specify which header with
- that name to retrieve. Headers start at offset <literal>1</literal>.</para>
- <para>Please observe that contents of the SDP (an attachment to the
- SIP request) can't be accessed with this function.</para>
- </description>
- </function>
- <function name="SIPPEER" language="en_US">
- <synopsis>
- Gets SIP peer information.
- </synopsis>
- <syntax>
- <parameter name="peername" required="true" />
- <parameter name="item">
- <enumlist>
- <enum name="ip">
- <para>(default) The IP address.</para>
- </enum>
- <enum name="port">
- <para>The port number.</para>
- </enum>
- <enum name="mailbox">
- <para>The configured mailbox.</para>
- </enum>
- <enum name="context">
- <para>The configured context.</para>
- </enum>
- <enum name="expire">
- <para>The epoch time of the next expire.</para>
- </enum>
- <enum name="dynamic">
- <para>Is it dynamic? (yes/no).</para>
- </enum>
- <enum name="callerid_name">
- <para>The configured Caller ID name.</para>
- </enum>
- <enum name="callerid_num">
- <para>The configured Caller ID number.</para>
- </enum>
- <enum name="callgroup">
- <para>The configured Callgroup.</para>
- </enum>
- <enum name="pickupgroup">
- <para>The configured Pickupgroup.</para>
- </enum>
- <enum name="namedcallgroup">
- <para>The configured Named Callgroup.</para>
- </enum>
- <enum name="namedpickupgroup">
- <para>The configured Named Pickupgroup.</para>
- </enum>
- <enum name="codecs">
- <para>The configured codecs.</para>
- </enum>
- <enum name="status">
- <para>Status (if qualify=yes).</para>
- </enum>
- <enum name="regexten">
- <para>Extension activated at registration.</para>
- </enum>
- <enum name="limit">
- <para>Call limit (call-limit).</para>
- </enum>
- <enum name="busylevel">
- <para>Configured call level for signalling busy.</para>
- </enum>
- <enum name="curcalls">
- <para>Current amount of calls. Only available if call-limit is set.</para>
- </enum>
- <enum name="language">
- <para>Default language for peer.</para>
- </enum>
- <enum name="accountcode">
- <para>Account code for this peer.</para>
- </enum>
- <enum name="useragent">
- <para>Current user agent header used by peer.</para>
- </enum>
- <enum name="maxforwards">
- <para>The value used for SIP loop prevention in outbound requests</para>
- </enum>
- <enum name="chanvar[name]">
- <para>A channel variable configured with setvar for this peer.</para>
- </enum>
- <enum name="codec[x]">
- <para>Preferred codec index number <replaceable>x</replaceable> (beginning with zero).</para>
- </enum>
- </enumlist>
- </parameter>
- </syntax>
- <description></description>
- </function>
- <function name="CHECKSIPDOMAIN" language="en_US">
- <synopsis>
- Checks if domain is a local domain.
- </synopsis>
- <syntax>
- <parameter name="domain" required="true" />
- </syntax>
- <description>
- <para>This function checks if the <replaceable>domain</replaceable> in the argument is configured
- as a local SIP domain that this Asterisk server is configured to handle.
- Returns the domain name if it is locally handled, otherwise an empty string.
- Check the <literal>domain=</literal> configuration in <filename>sip.conf</filename>.</para>
- </description>
- </function>
- <manager name="SIPpeers" language="en_US">
- <synopsis>
- List SIP peers (text format).
- </synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
- </syntax>
- <description>
- <para>Lists SIP peers in text format with details on current status.
- <literal>Peerlist</literal> will follow as separate events, followed by a final event called
- <literal>PeerlistComplete</literal>.</para>
- </description>
- </manager>
- <manager name="SIPshowpeer" language="en_US">
- <synopsis>
- show SIP peer (text format).
- </synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
- <parameter name="Peer" required="true">
- <para>The peer name you want to check.</para>
- </parameter>
- </syntax>
- <description>
- <para>Show one SIP peer with details on current status.</para>
- </description>
- </manager>
- <manager name="SIPqualifypeer" language="en_US">
- <synopsis>
- Qualify SIP peers.
- </synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
- <parameter name="Peer" required="true">
- <para>The peer name you want to qualify.</para>
- </parameter>
- </syntax>
- <description>
- <para>Qualify a SIP peer.</para>
- </description>
- <see-also>
- <ref type="managerEvent">SIPQualifyPeerDone</ref>
- </see-also>
- </manager>
- <manager name="SIPshowregistry" language="en_US">
- <synopsis>
- Show SIP registrations (text format).
- </synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
- </syntax>
- <description>
- <para>Lists all registration requests and status. Registrations will follow as separate
- events followed by a final event called <literal>RegistrationsComplete</literal>.</para>
- </description>
- </manager>
- <manager name="SIPnotify" language="en_US">
- <synopsis>
- Send a SIP notify.
- </synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
- <parameter name="Channel" required="true">
- <para>Peer to receive the notify.</para>
- </parameter>
- <parameter name="Variable" required="true">
- <para>At least one variable pair must be specified.
- <replaceable>name</replaceable>=<replaceable>value</replaceable></para>
- </parameter>
- </syntax>
- <description>
- <para>Sends a SIP Notify event.</para>
- <para>All parameters for this event must be specified in the body of this request
- via multiple <literal>Variable: name=value</literal> sequences.</para>
- </description>
- </manager>
- <manager name="SIPpeerstatus" language="en_US">
- <synopsis>
- Show the status of one or all of the sip peers.
- </synopsis>
- <syntax>
- <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
- <parameter name="Peer" required="false">
- <para>The peer name you want to check.</para>
- </parameter>
- </syntax>
- <description>
- <para>Retrieves the status of one or all of the sip peers. If no peer name is specified, status
- for all of the sip peers will be retrieved.</para>
- </description>
- </manager>
- <info name="SIPMessageFromInfo" language="en_US" tech="SIP">
- <para>The <literal>from</literal> parameter can be a configured peer name
- or in the form of "display-name" <URI>.</para>
- </info>
- <info name="SIPMessageToInfo" language="en_US" tech="SIP">
- <para>Specifying a prefix of <literal>sip:</literal> will send the
- message as a SIP MESSAGE request.</para>
- </info>
- <managerEvent language="en_US" name="SIPQualifyPeerDone">
- <managerEventInstance class="EVENT_FLAG_CALL">
- <synopsis>Raised when SIPQualifyPeer has finished qualifying the specified peer.</synopsis>
- <syntax>
- <parameter name="Peer">
- <para>The name of the peer.</para>
- </parameter>
- <parameter name="ActionID">
- <para>This is only included if an ActionID Header was sent with the action request, in which case it will be that ActionID.</para>
- </parameter>
- </syntax>
- <see-also>
- <ref type="manager">SIPqualifypeer</ref>
- </see-also>
- </managerEventInstance>
- </managerEvent>
- <managerEvent language="en_US" name="SessionTimeout">
- <managerEventInstance class="EVENT_FLAG_CALL">
- <synopsis>Raised when a SIP session times out.</synopsis>
- <syntax>
- <channel_snapshot/>
- <parameter name="Source">
- <para>The source of the session timeout.</para>
- <enumlist>
- <enum name="RTPTimeout" />
- <enum name="SIPSessionTimer" />
- </enumlist>
- </parameter>
- </syntax>
- </managerEventInstance>
- </managerEvent>
- ***/
- static int min_expiry = DEFAULT_MIN_EXPIRY; /*!< Minimum accepted registration time */
- static int max_expiry = DEFAULT_MAX_EXPIRY; /*!< Maximum accepted registration time */
- static int default_expiry = DEFAULT_DEFAULT_EXPIRY;
- static int min_subexpiry = DEFAULT_MIN_EXPIRY; /*!< Minimum accepted subscription time */
- static int max_subexpiry = DEFAULT_MAX_EXPIRY; /*!< Maximum accepted subscription time */
- static int mwi_expiry = DEFAULT_MWI_EXPIRY;
- static int unauth_sessions = 0;
- static int authlimit = DEFAULT_AUTHLIMIT;
- static int authtimeout = DEFAULT_AUTHTIMEOUT;
- /*! \brief Global jitterbuffer configuration - by default, jb is disabled
- * \note Values shown here match the defaults shown in sip.conf.sample */
- static struct ast_jb_conf default_jbconf =
- {
- .flags = 0,
- .max_size = 200,
- .resync_threshold = 1000,
- .impl = "fixed",
- .target_extra = 40,
- };
- static struct ast_jb_conf global_jbconf; /*!< Global jitterbuffer configuration */
- static const char config[] = "sip.conf"; /*!< Main configuration file */
- static const char notify_config[] = "sip_notify.conf"; /*!< Configuration file for sending Notify with CLI commands to reconfigure or reboot phones */
- /*! \brief Readable descriptions of device states.
- * \note Should be aligned to above table as index */
- static const struct invstate2stringtable {
- const enum invitestates state;
- const char *desc;
- } invitestate2string[] = {
- {INV_NONE, "None" },
- {INV_CALLING, "Calling (Trying)"},
- {INV_PROCEEDING, "Proceeding "},
- {INV_EARLY_MEDIA, "Early media"},
- {INV_COMPLETED, "Completed (done)"},
- {INV_CONFIRMED, "Confirmed (up)"},
- {INV_TERMINATED, "Done"},
- {INV_CANCELLED, "Cancelled"}
- };
- /*! \brief Subscription types that we support. We support
- * - dialoginfo updates (really device status, not dialog info as was the original intent of the standard)
- * - SIMPLE presence used for device status
- * - Voicemail notification subscriptions
- */
- static const struct cfsubscription_types {
- enum subscriptiontype type;
- const char * const event;
- const char * const mediatype;
- const char * const text;
- } subscription_types[] = {
- { NONE, "-", "unknown", "unknown" },
- /* RFC 4235: SIP Dialog event package */
- { DIALOG_INFO_XML, "dialog", "application/dialog-info+xml", "dialog-info+xml" },
- { CPIM_PIDF_XML, "presence", "application/cpim-pidf+xml", "cpim-pidf+xml" }, /* RFC 3863 */
- { PIDF_XML, "presence", "application/pidf+xml", "pidf+xml" }, /* RFC 3863 */
- { XPIDF_XML, "presence", "application/xpidf+xml", "xpidf+xml" }, /* Pre-RFC 3863 with MS additions */
- { MWI_NOTIFICATION, "message-summary", "application/simple-message-summary", "mwi" } /* RFC 3842: Mailbox notification */
- };
- /*! \brief The core structure to setup dialogs. We parse incoming messages by using
- * structure and then route the messages according to the type.
- *
- * \note Note that sip_methods[i].id == i must hold or the code breaks
- */
- static const struct cfsip_methods {
- enum sipmethod id;
- int need_rtp; /*!< when this is the 'primary' use for a pvt structure, does it need RTP? */
- char * const text;
- enum can_create_dialog can_create;
- } sip_methods[] = {
- { SIP_UNKNOWN, RTP, "-UNKNOWN-",CAN_CREATE_DIALOG },
- { SIP_RESPONSE, NO_RTP, "SIP/2.0", CAN_NOT_CREATE_DIALOG },
- { SIP_REGISTER, NO_RTP, "REGISTER", CAN_CREATE_DIALOG },
- { SIP_OPTIONS, NO_RTP, "OPTIONS", CAN_CREATE_DIALOG },
- { SIP_NOTIFY, NO_RTP, "NOTIFY", CAN_CREATE_DIALOG },
- { SIP_INVITE, RTP, "INVITE", CAN_CREATE_DIALOG },
- { SIP_ACK, NO_RTP, "ACK", CAN_NOT_CREATE_DIALOG },
- { SIP_PRACK, NO_RTP, "PRACK", CAN_NOT_CREATE_DIALOG },
- { SIP_BYE, NO_RTP, "BYE", CAN_NOT_CREATE_DIALOG },
- { SIP_REFER, NO_RTP, "REFER", CAN_CREATE_DIALOG },
- { SIP_SUBSCRIBE, NO_RTP, "SUBSCRIBE",CAN_CREATE_DIALOG },
- { SIP_MESSAGE, NO_RTP, "MESSAGE", CAN_CREATE_DIALOG },
- { SIP_UPDATE, NO_RTP, "UPDATE", CAN_NOT_CREATE_DIALOG },
- { SIP_INFO, NO_RTP, "INFO", CAN_NOT_CREATE_DIALOG },
- { SIP_CANCEL, NO_RTP, "CANCEL", CAN_NOT_CREATE_DIALOG },
- { SIP_PUBLISH, NO_RTP, "PUBLISH", CAN_CREATE_DIALOG },
- { SIP_PING, NO_RTP, "PING", CAN_CREATE_DIALOG_UNSUPPORTED_METHOD }
- };
- /*! \brief Diversion header reasons
- *
- * The core defines a bunch of constants used to define
- * redirecting reasons. This provides a translation table
- * between those and the strings which may be present in
- * a SIP Diversion header
- */
- static const struct sip_reasons {
- enum AST_REDIRECTING_REASON code;
- char * const text;
- } sip_reason_table[] = {
- { AST_REDIRECTING_REASON_UNKNOWN, "unknown" },
- { AST_REDIRECTING_REASON_USER_BUSY, "user-busy" },
- { AST_REDIRECTING_REASON_NO_ANSWER, "no-answer" },
- { AST_REDIRECTING_REASON_UNAVAILABLE, "unavailable" },
- { AST_REDIRECTING_REASON_UNCONDITIONAL, "unconditional" },
- { AST_REDIRECTING_REASON_TIME_OF_DAY, "time-of-day" },
- { AST_REDIRECTING_REASON_DO_NOT_DISTURB, "do-not-disturb" },
- { AST_REDIRECTING_REASON_DEFLECTION, "deflection" },
- { AST_REDIRECTING_REASON_FOLLOW_ME, "follow-me" },
- { AST_REDIRECTING_REASON_OUT_OF_ORDER, "out-of-service" },
- { AST_REDIRECTING_REASON_AWAY, "away" },
- { AST_REDIRECTING_REASON_CALL_FWD_DTE, "unknown"},
- { AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm"},
- };
- /*! \name DefaultSettings
- Default setttings are used as a channel setting and as a default when
- configuring devices
- */
- static char default_language[MAX_LANGUAGE]; /*!< Default language setting for new channels */
- static char default_callerid[AST_MAX_EXTENSION]; /*!< Default caller ID for sip messages */
- static char default_mwi_from[80]; /*!< Default caller ID for MWI updates */
- static char default_fromdomain[AST_MAX_EXTENSION]; /*!< Default domain on outound messages */
- static int default_fromdomainport; /*!< Default domain port on outbound messages */
- static char default_notifymime[AST_MAX_EXTENSION]; /*!< Default MIME media type for MWI notify messages */
- static char default_vmexten[AST_MAX_EXTENSION]; /*!< Default From Username on MWI updates */
- static int default_qualify; /*!< Default Qualify= setting */
- static int default_keepalive; /*!< Default keepalive= setting */
- static char default_mohinterpret[MAX_MUSICCLASS]; /*!< Global setting for moh class to use when put on hold */
- static char default_mohsuggest[MAX_MUSICCLASS]; /*!< Global setting for moh class to suggest when putting
- * a bridged channel on hold */
- static char default_parkinglot[AST_MAX_CONTEXT]; /*!< Parkinglot */
- static char default_engine[256]; /*!< Default RTP engine */
- static int default_maxcallbitrate; /*!< Maximum bitrate for call */
- static char default_zone[MAX_TONEZONE_COUNTRY]; /*!< Default tone zone for channels created from the SIP driver */
- static unsigned int default_transports; /*!< Default Transports (enum ast_transport) that are acceptable */
- static unsigned int default_primary_transport; /*!< Default primary Transport (enum ast_transport) for outbound connections to devices */
- static struct sip_settings sip_cfg; /*!< SIP configuration data.
- \note in the future we could have multiple of these (per domain, per device group etc) */
- /*!< use this macro when ast_uri_decode is dependent on pedantic checking to be on. */
- #define SIP_PEDANTIC_DECODE(str) \
- if (sip_cfg.pedanticsipchecking && !ast_strlen_zero(str)) { \
- ast_uri_decode(str, ast_uri_sip_user); \
- } \
- static unsigned int chan_idx; /*!< used in naming sip channel */
- static int global_match_auth_username; /*!< Match auth username if available instead of From: Default off. */
- static int global_relaxdtmf; /*!< Relax DTMF */
- static int global_prematuremediafilter; /*!< Enable/disable premature frames in a call (causing 183 early media) */
- static int global_rtptimeout; /*!< Time out call if no RTP */
- static int global_rtpholdtimeout; /*!< Time out call if no RTP during hold */
- static int global_rtpkeepalive; /*!< Send RTP keepalives */
- static int global_reg_timeout; /*!< Global time between attempts for outbound registrations */
- static int global_regattempts_max; /*!< Registration attempts before giving up */
- static int global_reg_retry_403; /*!< Treat 403 responses to registrations as 401 responses */
- static int global_shrinkcallerid; /*!< enable or disable shrinking of caller id */
- static int global_callcounter; /*!< Enable call counters for all devices. This is currently enabled by setting the peer
- * call-limit to INT_MAX. When we remove the call-limit from the code, we can make it
- * with just a boolean flag in the device structure */
- static unsigned int global_tos_sip; /*!< IP type of service for SIP packets */
- static unsigned int global_tos_audio; /*!< IP type of service for audio RTP packets */
- static unsigned int global_tos_video; /*!< IP type of service for video RTP packets */
- static unsigned int global_tos_text; /*!< IP type of service for text RTP packets */
- static unsigned int global_cos_sip; /*!< 802.1p class of service for SIP packets */
- static unsigned int global_cos_audio; /*!< 802.1p class of service for audio RTP packets */
- static unsigned int global_cos_video; /*!< 802.1p class of service for video RTP packets */
- static unsigned int global_cos_text; /*!< 802.1p class of service for text RTP packets */
- static unsigned int recordhistory; /*!< Record SIP history. Off by default */
- static unsigned int dumphistory; /*!< Dump history to verbose before destroying SIP dialog */
- static char global_useragent[AST_MAX_EXTENSION]; /*!< Useragent for the SIP channel */
- static char global_sdpsession[AST_MAX_EXTENSION]; /*!< SDP session name for the SIP channel */
- static char global_sdpowner[AST_MAX_EXTENSION]; /*!< SDP owner name for the SIP channel */
- static int global_authfailureevents; /*!< Whether we send authentication failure manager events or not. Default no. */
- static int global_t1; /*!< T1 time */
- static int global_t1min; /*!< T1 roundtrip time minimum */
- static int global_timer_b; /*!< Timer B - RFC 3261 Section 17.1.1.2 */
- static unsigned int global_autoframing; /*!< Turn autoframing on or off. */
- static int global_qualifyfreq; /*!< Qualify frequency */
- static int global_qualify_gap; /*!< Time between our group of peer pokes */
- static int global_qualify_peers; /*!< Number of peers to poke at a given time */
- static enum st_mode global_st_mode; /*!< Mode of operation for Session-Timers */
- static enum st_refresher_param global_st_refresher; /*!< Session-Timer refresher */
- static int global_min_se; /*!< Lowest threshold for session refresh interval */
- static int global_max_se; /*!< Highest threshold for session refresh interval */
- static int global_store_sip_cause; /*!< Whether the MASTER_CHANNEL(HASH(SIP_CAUSE,[chan_name])) var should be set */
- static int global_dynamic_exclude_static = 0; /*!< Exclude static peers from contact registrations */
- static unsigned char global_refer_addheaders; /*!< Add extra headers to outgoing REFER */
- /*@}*/
- /*!
- * We use libxml2 in order to parse XML that may appear in the body of a SIP message. Currently,
- * the only usage is for parsing PIDF bodies of incoming PUBLISH requests in the call-completion
- * event package. This variable is set at module load time and may be checked at runtime to determine
- * if XML parsing support was found.
- */
- static int can_parse_xml;
- /*! \name Object counters @{
- *
- * \bug These counters are not handled in a thread-safe way ast_atomic_fetchadd_int()
- * should be used to modify these values.
- */
- static int speerobjs = 0; /*!< Static peers */
- static int rpeerobjs = 0; /*!< Realtime peers */
- static int apeerobjs = 0; /*!< Autocreated peer objects */
- /*! @} */
- static struct ast_flags global_flags[3] = {{0}}; /*!< global SIP_ flags */
- static unsigned int global_t38_maxdatagram; /*!< global T.38 FaxMaxDatagram override */
- static struct stasis_subscription *network_change_sub; /*!< subscription id for network change events */
- static struct stasis_subscription *acl_change_sub; /*!< subscription id for named ACL system change events */
- static int network_change_sched_id = -1;
- static char used_context[AST_MAX_CONTEXT]; /*!< name of automatically created context for unloading */
- AST_MUTEX_DEFINE_STATIC(netlock);
- /*! \brief Protect the monitoring thread, so only one process can kill or start it, and not
- when it's doing something critical. */
- AST_MUTEX_DEFINE_STATIC(monlock);
- AST_MUTEX_DEFINE_STATIC(sip_reload_lock);
- /*! \brief This is the thread for the monitor which checks for input on the channels
- which are not currently in use. */
- static pthread_t monitor_thread = AST_PTHREADT_NULL;
- static int sip_reloading = FALSE; /*!< Flag for avoiding multiple reloads at the same time */
- static enum channelreloadreason sip_reloadreason; /*!< Reason for last reload/load of configuration */
- struct ast_sched_context *sched; /*!< The scheduling context */
- static struct io_context *io; /*!< The IO context */
- static int *sipsock_read_id; /*!< ID of IO entry for sipsock FD */
- struct sip_pkt;
- static AST_LIST_HEAD_STATIC(domain_list, domain); /*!< The SIP domain list */
- AST_LIST_HEAD_NOLOCK(sip_history_head, sip_history); /*!< history list, entry in sip_pvt */
- static enum sip_debug_e sipdebug;
- /*! \brief extra debugging for 'text' related events.
- * At the moment this is set together with sip_debug_console.
- * \note It should either go away or be implemented properly.
- */
- static int sipdebug_text;
- static const struct _map_x_s referstatusstrings[] = {
- { REFER_IDLE, "<none>" },
- { REFER_SENT, "Request sent" },
- { REFER_RECEIVED, "Request received" },
- { REFER_CONFIRMED, "Confirmed" },
- { REFER_ACCEPTED, "Accepted" },
- { REFER_RINGING, "Target ringing" },
- { REFER_200OK, "Done" },
- { REFER_FAILED, "Failed" },
- { REFER_NOAUTH, "Failed - auth failure" },
- { -1, NULL} /* terminator */
- };
- /* --- Hash tables of various objects --------*/
- #ifdef LOW_MEMORY
- static const int HASH_PEER_SIZE = 17;
- static const int HASH_DIALOG_SIZE = 17;
- static const int HASH_REGISTRY_SIZE = 17;
- #else
- static const int HASH_PEER_SIZE = 563; /*!< Size of peer hash table, prime number preferred! */
- static const int HASH_DIALOG_SIZE = 563;
- static const int HASH_REGISTRY_SIZE = 563;
- #endif
- static const struct {
- enum ast_cc_service_type service;
- const char *service_string;
- } sip_cc_service_map [] = {
- [AST_CC_NONE] = { AST_CC_NONE, "" },
- [AST_CC_CCBS] = { AST_CC_CCBS, "BS" },
- [AST_CC_CCNR] = { AST_CC_CCNR, "NR" },
- [AST_CC_CCNL] = { AST_CC_CCNL, "NL" },
- };
- static const struct {
- enum sip_cc_notify_state state;
- const char *state_string;
- } sip_cc_notify_state_map [] = {
- [CC_QUEUED] = {CC_QUEUED, "cc-state: queued"},
- [CC_READY] = {CC_READY, "cc-state: ready"},
- };
- AST_LIST_HEAD_STATIC(epa_static_data_list, epa_backend);
- /*!
- * Used to create new entity IDs by ESCs.
- */
- static int esc_etag_counter;
- static const int DEFAULT_PUBLISH_EXPIRES = 3600;
- #ifdef HAVE_LIBXML2
- static int cc_esc_publish_handler(struct sip_pvt *pvt, struct sip_request *req, struct event_state_compositor *esc, struct sip_esc_entry *esc_entry);
- static const struct sip_esc_publish_callbacks cc_esc_publish_callbacks = {
- .initial_handler = cc_esc_publish_handler,
- .modify_handler = cc_esc_publish_handler,
- };
- #endif
- /*!
- * \brief The Event State Compositors
- *
- * An Event State Compositor is an entity which
- * accepts PUBLISH requests and acts appropriately
- * based on these requests.
- *
- * The actual event_state_compositor structure is simply
- * an ao2_container of sip_esc_entrys. When an incoming
- * PUBLISH is received, we can match the appropriate sip_esc_entry
- * using the entity ID of the incoming PUBLISH.
- */
- static struct event_state_compositor {
- enum subscriptiontype event;
- const char * name;
- const struct sip_esc_publish_callbacks *callbacks;
- struct ao2_container *compositor;
- } event_state_compositors [] = {
- #ifdef HAVE_LIBXML2
- {CALL_COMPLETION, "call-completion", &cc_esc_publish_callbacks},
- #endif
- };
- struct state_notify_data {
- int state;
- struct ao2_container *device_state_info;
- int presence_state;
- const char *presence_subtype;
- const char *presence_message;
- };
- static const int ESC_MAX_BUCKETS = 37;
- /*!
- * \details
- * Here we implement the container for dialogs which are in the
- * dialog_needdestroy state to iterate only through the dialogs
- * unlink them instead of iterate through all dialogs
- */
- struct ao2_container *dialogs_needdestroy;
- /*!
- * \details
- * Here we implement the container for dialogs which have rtp
- * traffic and rtptimeout, rtpholdtimeout or rtpkeepalive
- * set. We use this container instead the whole dialog list.
- */
- struct ao2_container *dialogs_rtpcheck;
- /*!
- * \details
- * Here we implement the container for dialogs (sip_pvt), defining
- * generic wrapper functions to ease the transition from the current
- * implementation (a single linked list) to a different container.
- * In addition to a reference to the container, we need functions to lock/unlock
- * the container and individual items, and functions to add/remove
- * references to the individual items.
- */
- static struct ao2_container *dialogs;
- #define sip_pvt_lock(x) ao2_lock(x)
- #define sip_pvt_trylock(x) ao2_trylock(x)
- #define sip_pvt_unlock(x) ao2_unlock(x)
- /*! \brief The table of TCP threads */
- static struct ao2_container *threadt;
- /*! \brief The peer list: Users, Peers and Friends */
- static struct ao2_container *peers;
- static struct ao2_container *peers_by_ip;
- /*! \brief A bogus peer, to be used when authentication should fail */
- static struct sip_peer *bogus_peer;
- /*! \brief We can recognise the bogus peer by this invalid MD5 hash */
- #define BOGUS_PEER_MD5SECRET "intentionally_invalid_md5_string"
- /*! \brief The register list: Other SIP proxies we register with and receive calls from */
- static struct ao2_container *registry_list;
- /*! \brief The MWI subscription list */
- static struct ao2_container *subscription_mwi_list;
- static int temp_pvt_init(void *);
- static void temp_pvt_cleanup(void *);
- /*! \brief A per-thread temporary pvt structure */
- AST_THREADSTORAGE_CUSTOM(ts_temp_pvt, temp_pvt_init, temp_pvt_cleanup);
- /*! \brief A per-thread buffer for transport to string conversion */
- AST_THREADSTORAGE(sip_transport_str_buf);
- /*! \brief Size of the SIP transport buffer */
- #define SIP_TRANSPORT_STR_BUFSIZE 128
- /*! \brief Authentication container for realm authentication */
- static struct sip_auth_container *authl = NULL;
- /*! \brief Global authentication container protection while adjusting the references. */
- AST_MUTEX_DEFINE_STATIC(authl_lock);
- static struct ast_manager_event_blob *session_timeout_to_ami(struct stasis_message *msg);
- STASIS_MESSAGE_TYPE_DEFN_LOCAL(session_timeout_type,
- .to_ami = session_timeout_to_ami,
- );
- /* --- Sockets and networking --------------*/
- /*! \brief Main socket for UDP SIP communication.
- *
- * sipsock is shared between the SIP manager thread (which handles reload
- * requests), the udp io handler (sipsock_read()) and the user routines that
- * issue udp writes (using __sip_xmit()).
- * The socket is -1 only when opening fails (this is a permanent condition),
- * or when we are handling a reload() that changes its address (this is
- * a transient situation during which we might have a harmless race, see
- * below). Because the conditions for the race to be possible are extremely
- * rare, we don't want to pay the cost of locking on every I/O.
- * Rather, we remember that when the race may occur, communication is
- * bound to fail anyways, so we just live with this event and let
- * the protocol handle this above us.
- */
- static int sipsock = -1;
- struct ast_sockaddr bindaddr; /*!< UDP: The address we bind to */
- /*! \brief our (internal) default address/port to put in SIP/SDP messages
- * internip is initialized picking a suitable address from one of the
- * interfaces, and the same port number we bind to. It is used as the
- * default address/port in SIP messages, and as the default address
- * (but not port) in SDP messages.
- */
- static struct ast_sockaddr internip;
- /*! \brief our external IP address/port for SIP sessions.
- * externaddr.sin_addr is only set when we know we might be behind
- * a NAT, and this is done using a variety of (mutually exclusive)
- * ways from the config file:
- *
- * + with "externaddr = host[:port]" we specify the address/port explicitly.
- * The address is looked up only once when (re)loading the config file;
- *
- * + with "externhost = host[:port]" we do a similar thing, but the
- * hostname is stored in externhost, and the hostname->IP mapping
- * is refreshed every 'externrefresh' seconds;
- *
- * Other variables (externhost, externexpire, externrefresh) are used
- * to support the above functions.
- */
- static struct ast_sockaddr externaddr; /*!< External IP address if we are behind NAT */
- static struct ast_sockaddr media_address; /*!< External RTP IP address if we are behind NAT */
- static struct ast_sockaddr rtpbindaddr; /*!< RTP: The address we bind to */
- static char externhost[MAXHOSTNAMELEN]; /*!< External host name */
- static time_t externexpire; /*!< Expiration counter for re-resolving external host name in dynamic DNS */
- static int externrefresh = 10; /*!< Refresh timer for DNS-based external address (dyndns) */
- static uint16_t externtcpport; /*!< external tcp port */
- static uint16_t externtlsport; /*!< external tls port */
- /*! \brief List of local networks
- * We store "localnet" addresses from the config file into an access list,
- * marked as 'DENY', so the call to ast_apply_ha() will return
- * AST_SENSE_DENY for 'local' addresses, and AST_SENSE_ALLOW for 'non local'
- * (i.e. presumably public) addresses.
- */
- static struct ast_ha *localaddr; /*!< List of local networks, on the same side of NAT as this Asterisk */
- static int ourport_tcp; /*!< The port used for TCP connections */
- static int ourport_tls; /*!< The port used for TCP/TLS connections */
- static struct ast_sockaddr debugaddr;
- static struct ast_config *notify_types = NULL; /*!< The list of manual NOTIFY types we know how to send */
- /*! some list management macros. */
- #define UNLINK(element, head, prev) do { \
- if (prev) \
- (prev)->next = (element)->next; \
- else \
- (head) = (element)->next; \
- } while (0)
- struct ao2_container *sip_monitor_instances;
- struct show_peers_context;
- /*---------------------------- Forward declarations of functions in chan_sip.c */
- /* Note: This is added to help splitting up chan_sip.c into several files
- in coming releases. */
- /*--- PBX interface functions */
- static struct ast_channel *sip_request_call(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *dest, int *cause);
- static int sip_devicestate(const char *data);
- static int sip_sendtext(struct ast_channel *ast, const char *text);
- static int sip_call(struct ast_channel *ast, const char *dest, int timeout);
- static int sip_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen);
- static int sip_hangup(struct ast_channel *ast);
- static int sip_answer(struct ast_channel *ast);
- static struct ast_frame *sip_read(struct ast_channel *ast);
- static int sip_write(struct ast_channel *ast, struct ast_frame *frame);
- static int sip_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
- static int sip_transfer(struct ast_channel *ast, const char *dest);
- static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
- static int sip_senddigit_begin(struct ast_channel *ast, char digit);
- static int sip_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
- static int sip_setoption(struct ast_channel *chan, int option, void *data, int datalen);
- static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *datalen);
- static const char *sip_get_callid(struct ast_channel *chan);
- static int handle_request_do(struct sip_request *req, struct ast_sockaddr *addr);
- static int sip_standard_port(enum ast_transport type, int port);
- static int sip_prepare_socket(struct sip_pvt *p);
- static int get_address_family_filter(unsigned int transport);
- /*--- Transmitting responses and requests */
- static int sipsock_read(int *id, int fd, short events, void *ignore);
- static int __sip_xmit(struct sip_pvt *p, struct ast_str *data);
- static int __sip_reliable_xmit(struct sip_pvt *p, uint32_t seqno, int resp, struct ast_str *data, int fatal, int sipmethod);
- static void add_cc_call_info_to_response(struct sip_pvt *p, struct sip_request *resp);
- static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
- static int retrans_pkt(const void *data);
- static int transmit_response_using_temp(ast_string_field callid, struct ast_sockaddr *addr, int useglobal_nat, const int intended_method, const struct sip_request *req, const char *msg);
- static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req);
- static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req);
- static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req);
- static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp, int rpid);
- static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported);
- static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *rand, enum xmittype reliable, const char *header, int stale);
- static int transmit_provisional_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, int with_sdp);
- static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
- static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable);
- static int transmit_request(struct sip_pvt *p, int sipmethod, uint32_t seqno, enum xmittype reliable, int newbranch);
- static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, uint32_t seqno, enum xmittype reliable, int newbranch);
- static int transmit_publish(struct sip_epa_entry *epa_entry, enum sip_publish_type publish_type, const char * const explicit_uri);
- static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init, const char * const explicit_uri);
- static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int oldsdp);
- static int transmit_info_with_aoc(struct sip_pvt *p, struct ast_aoc_decoded *decoded);
- static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration);
- static int transmit_info_with_vidupdate(struct sip_pvt *p);
- static int transmit_message(struct sip_pvt *p, int init, int auth);
- static int transmit_refer(struct sip_pvt *p, const char *dest);
- static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, const char *vmexten);
- static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate);
- static int transmit_cc_notify(struct ast_cc_agent *agent, struct sip_pvt *subscription, enum sip_cc_notify_state state);
- static int transmit_register(struct sip_registry *r, int sipmethod, const char *auth, const char *authheader);
- static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, uint32_t seqno);
- static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, uint32_t seqno);
- static void copy_request(struct sip_request *dst, const struct sip_request *src);
- static void receive_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
- static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req, char **name, char **number, int set_call_forward);
- static int sip_send_mwi_to_peer(struct sip_peer *peer, int cache_only);
- /* Misc dialog routines */
- static int __sip_autodestruct(const void *data);
- static int update_call_counter(struct sip_pvt *fup, int event);
- static int auto_congest(const void *arg);
- static struct sip_pvt *__find_call(struct sip_request *req, struct ast_sockaddr *addr, const int intended_method,
- const char *file, int line, const char *func);
- #define find_call(req, addr, intended_method) \
- __find_call(req, addr, intended_method, __FILE__, __LINE__, __PRETTY_FUNCTION__)
- static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards, int resp);
- static int build_path(struct sip_pvt *p, struct sip_peer *peer, struct sip_request *req, const char *pathbuf);
- static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sockaddr *addr,
- struct sip_request *req, const char *uri);
- static int get_sip_pvt_from_replaces(const char *callid, const char *totag, const char *fromtag,
- struct sip_pvt **out_pvt, struct ast_channel **out_chan);
- static void check_pendings(struct sip_pvt *p);
- static void sip_set_owner(struct sip_pvt *p, struct ast_channel *chan);
- static void *sip_pickup_thread(void *stuff);
- static int sip_pickup(struct ast_channel *chan);
- static int sip_sipredirect(struct sip_pvt *p, const char *dest);
- static int is_method_allowed(unsigned int *allowed_methods, enum sipmethod method);
- /*--- Codec handling / SDP */
- static void try_suggested_sip_codec(struct sip_pvt *p);
- static const char *get_sdp_iterate(int* start, struct sip_request *req, const char *name);
- static char get_sdp_line(int *start, int stop, struct sip_request *req, const char **value);
- static int find_sdp(struct sip_request *req);
- static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action);
- static int process_sdp_o(const char *o, struct sip_pvt *p);
- static int process_sdp_c(const char *c, struct ast_sockaddr *addr);
- static int process_sdp_a_sendonly(const char *a, int *sendonly);
- static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance);
- static int process_sdp_a_dtls(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance);
- static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newaudiortp, int *last_rtpmap_codec);
- static int process_sdp_a_video(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newvideortp, int *last_rtpmap_codec);
- static int process_sdp_a_text(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newtextrtp, char *red_fmtp, int *red_num_gen, int *red_data_pt, int *last_rtpmap_codec);
- static int process_sdp_a_image(const char *a, struct sip_pvt *p);
- static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf);
- static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf);
- static void start_ice(struct ast_rtp_instance *instance, int offer);
- static void add_codec_to_sdp(const struct sip_pvt *p, struct ast_format *codec,
- struct ast_str **m_buf, struct ast_str **a_buf,
- int debug, int *min_packet_size, int *max_packet_size);
- static void add_noncodec_to_sdp(const struct sip_pvt *p, int format,
- struct ast_str **m_buf, struct ast_str **a_buf,
- int debug);
- static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp, int add_audio, int add_t38);
- static void do_setnat(struct sip_pvt *p);
- static void stop_media_flows(struct sip_pvt *p);
- /*--- Authentication stuff */
- static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, int sipmethod, char *digest, int digest_len);
- static int build_reply_digest(struct sip_pvt *p, int method, char *digest, int digest_len);
- static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username,
- const char *secret, const char *md5secret, int sipmethod,
- const char *uri, enum xmittype reliable);
- static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_request *req,
- int sipmethod, const char *uri, enum xmittype reliable,
- struct ast_sockaddr *addr, struct sip_peer **authpeer);
- static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod, const char *uri, enum xmittype reliable, struct ast_sockaddr *addr);
- /*--- Domain handling */
- static int check_sip_domain(const char *domain, char *context, size_t len); /* Check if domain is one of our local domains */
- static int add_sip_domain(const char *domain, const enum domain_mode mode, const char *context);
- static void clear_sip_domains(void);
- /*--- SIP realm authentication */
- static void add_realm_authentication(struct sip_auth_container **credentials, const char *configuration, int lineno);
- static struct sip_auth *find_realm_authentication(struct sip_auth_container *credentials, const char *realm);
- /*--- Misc functions */
- static int check_rtp_timeout(struct sip_pvt *dialog, time_t t);
- static int reload_config(enum channelreloadreason reason);
- static void add_diversion(struct sip_request *req, struct sip_pvt *pvt);
- static int expire_register(const void *data);
- static void *do_monitor(void *data);
- static int restart_monitor(void);
- static void peer_mailboxes_to_str(struct ast_str **mailbox_str, struct sip_peer *peer);
- static struct ast_variable *copy_vars(struct ast_variable *src);
- static int dialog_find_multiple(void *obj, void *arg, int flags);
- static struct ast_channel *sip_pvt_lock_full(struct sip_pvt *pvt);
- /* static int sip_addrcmp(char *name, struct sockaddr_in *sin); Support for peer matching */
- static int sip_refer_alloc(struct sip_pvt *p);
- static void sip_refer_destroy(struct sip_pvt *p);
- static int sip_notify_alloc(struct sip_pvt *p);
- static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context);
- static void set_peer_nat(const struct sip_pvt *p, struct sip_peer *peer);
- static void check_for_nat(const struct ast_sockaddr *them, struct sip_pvt *p);
- /*--- Device monitoring and Device/extension state/event handling */
- static int extensionstate_update(const char *context, const char *exten, struct state_notify_data *data, struct sip_pvt *p, int force);
- static int cb_extensionstate(char *context, char *exten, struct ast_state_cb_info *info, void *data);
- static int sip_poke_noanswer(const void *data);
- static int sip_poke_peer(struct sip_peer *peer, int force);
- static void sip_poke_all_peers(void);
- static void sip_peer_hold(struct sip_pvt *p, int hold);
- static void mwi_event_cb(void *, struct stasis_subscription *, struct stasis_message *);
- static void network_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message);
- static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message);
- static void sip_keepalive_all_peers(void);
- /*--- Applications, functions, CLI and manager command helpers */
- static const char *sip_nat_mode(const struct sip_pvt *p);
- static char *sip_show_inuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *transfermode2str(enum transfermodes mode) attribute_const;
- static int peer_status(struct sip_peer *peer, char *status, int statuslen);
- static char *sip_show_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char * _sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[]);
- static struct sip_peer *_sip_show_peers_one(int fd, struct mansession *s, struct show_peers_context *cont, struct sip_peer *peer);
- static char *sip_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *sip_show_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static void print_group(int fd, ast_group_t group, int crlf);
- static void print_named_groups(int fd, struct ast_namedgroups *groups, int crlf);
- static const char *dtmfmode2str(int mode) attribute_const;
- static int str2dtmfmode(const char *str) attribute_unused;
- static const char *insecure2str(int mode) attribute_const;
- static const char *allowoverlap2str(int mode) attribute_const;
- static void cleanup_stale_contexts(char *new, char *old);
- static const char *domain_mode_to_text(const enum domain_mode mode);
- static char *sip_show_domains(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[]);
- static char *sip_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *_sip_qualify_peer(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[]);
- static char *sip_qualify_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *sip_show_registry(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *sip_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *sip_show_mwi(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static const char *subscription_type2str(enum subscriptiontype subtype) attribute_pure;
- static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype);
- static char *complete_sip_peer(const char *word, int state, int flags2);
- static char *complete_sip_registered_peer(const char *word, int state, int flags2);
- static char *complete_sip_show_history(const char *line, const char *word, int pos, int state);
- static char *complete_sip_show_peer(const char *line, const char *word, int pos, int state);
- static char *complete_sip_unregister(const char *line, const char *word, int pos, int state);
- static char *complete_sip_notify(const char *line, const char *word, int pos, int state);
- static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *sip_show_channelstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *sip_show_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *sip_do_debug_ip(int fd, const char *arg);
- static char *sip_do_debug_peer(int fd, const char *arg);
- static char *sip_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *sip_cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static char *sip_set_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static int sip_dtmfmode(struct ast_channel *chan, const char *data);
- static int sip_addheader(struct ast_channel *chan, const char *data);
- static int sip_do_reload(enum channelreloadreason reason);
- static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- static int ast_sockaddr_resolve_first_af(struct ast_sockaddr *addr,
- const char *name, int flag, int family);
- static int ast_sockaddr_resolve_first(struct ast_sockaddr *addr,
- const char *name, int flag);
- static int ast_sockaddr_resolve_first_transport(struct ast_sockaddr *addr,
- const char *name, int flag, unsigned int transport);
- /*--- Debugging
- Functions for enabling debug per IP or fully, or enabling history logging for
- a SIP dialog
- */
- static void sip_dump_history(struct sip_pvt *dialog); /* Dump history to debuglog at end of dialog, before destroying data */
- static inline int sip_debug_test_addr(const struct ast_sockaddr *addr);
- static inline int sip_debug_test_pvt(struct sip_pvt *p);
- static void append_history_full(struct sip_pvt *p, const char *fmt, ...);
- static void sip_dump_history(struct sip_pvt *dialog);
- /*--- Device object handling */
- static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime, int devstate_only);
- static int update_call_counter(struct sip_pvt *fup, int event);
- static void sip_destroy_peer(struct sip_peer *peer);
- static void sip_destroy_peer_fn(void *peer);
- static void set_peer_defaults(struct sip_peer *peer);
- static struct sip_peer *temp_peer(const char *name);
- static void register_peer_exten(struct sip_peer *peer, int onoff);
- static int sip_poke_peer_s(const void *data);
- static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_request *req);
- static void reg_source_db(struct sip_peer *peer);
- static void destroy_association(struct sip_peer *peer);
- static void set_insecure_flags(struct ast_flags *flags, const char *value, int lineno);
- static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v);
- static void set_socket_transport(struct sip_socket *socket, int transport);
- static int peer_ipcmp_cb_full(void *obj, void *arg, void *data, int flags);
- /* Realtime device support */
- static void realtime_update_peer(const char *peername, struct ast_sockaddr *addr, const char *username, const char *fullcontact, const char *useragent, int expirey, unsigned short deprecated_username, int lastms, const char *path);
- static void update_peer(struct sip_peer *p, int expire);
- static struct ast_variable *get_insecure_variable_from_config(struct ast_config *config);
- static const char *get_name_from_variable(const struct ast_variable *var);
- static struct sip_peer *realtime_peer(const char *peername, struct ast_sockaddr *sin, char *callbackexten, int devstate_only, int which_objects);
- static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
- /*--- Internal UA client handling (outbound registrations) */
- static void ast_sip_ouraddrfor(const struct ast_sockaddr *them, struct ast_sockaddr *us, struct sip_pvt *p);
- static void sip_registry_destroy(void *reg);
- static int sip_register(const char *value, int lineno);
- static const char *regstate2str(enum sipregistrystate regstate) attribute_const;
- static int sip_reregister(const void *data);
- static int __sip_do_register(struct sip_registry *r);
- static int sip_reg_timeout(const void *data);
- static void sip_send_all_registers(void);
- static int sip_reinvite_retry(const void *data);
- /*--- Parsing SIP requests and responses */
- static int determine_firstline_parts(struct sip_request *req);
- static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype);
- static const char *gettag(const struct sip_request *req, const char *header, char *tagbuf, int tagbufsize);
- static int find_sip_method(const char *msg);
- static unsigned int parse_allowed_methods(struct sip_request *req);
- static unsigned int set_pvt_allowed_methods(struct sip_pvt *pvt, struct sip_request *req);
- static int parse_request(struct sip_request *req);
- static const char *referstatus2str(enum referstatus rstatus) attribute_pure;
- static int method_match(enum sipmethod id, const char *name);
- static void parse_copy(struct sip_request *dst, const struct sip_request *src);
- static void parse_oli(struct sip_request *req, struct ast_channel *chan);
- static const char *find_alias(const char *name, const char *_default);
- static const char *__get_header(const struct sip_request *req, const char *name, int *start);
- static void lws2sws(struct ast_str *msgbuf);
- static void extract_uri(struct sip_pvt *p, struct sip_request *req);
- static char *remove_uri_parameters(char *uri);
- static int get_refer_info(struct sip_pvt *transferer, struct sip_request *outgoing_req);
- static int get_also_info(struct sip_pvt *p, struct sip_request *oreq);
- static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req);
- static int set_address_from_contact(struct sip_pvt *pvt);
- static void check_via(struct sip_pvt *p, const struct sip_request *req);
- static int get_rpid(struct sip_pvt *p, struct sip_request *oreq);
- static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason, char **reason_str);
- static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id);
- static int transmit_state_notify(struct sip_pvt *p, struct state_notify_data *data, int full, int timeout);
- static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen);
- static void update_redirecting(struct sip_pvt *p, const void *data, size_t datalen);
- static int get_domain(const char *str, char *domain, int len);
- static void get_realm(struct sip_pvt *p, const struct sip_request *req);
- static char *get_content(struct sip_request *req);
- /*-- TCP connection handling ---*/
- static void *_sip_tcp_helper_thread(struct ast_tcptls_session_instance *tcptls_session);
- static void *sip_tcp_worker_fn(void *);
- /*--- Constructing requests and responses */
- static void initialize_initreq(struct sip_pvt *p, struct sip_request *req);
- static int init_req(struct sip_request *req, int sipmethod, const char *recip);
- static void deinit_req(struct sip_request *req);
- static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, uint32_t seqno, int newbranch);
- static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, const char * const explicit_uri);
- static int init_resp(struct sip_request *resp, const char *msg);
- static inline int resp_needs_contact(const char *msg, enum sipmethod method);
- static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, const struct sip_request *req);
- static const struct ast_sockaddr *sip_real_dst(const struct sip_pvt *p);
- static void build_via(struct sip_pvt *p);
- static int create_addr_from_peer(struct sip_pvt *r, struct sip_peer *peer);
- static int create_addr(struct sip_pvt *dialog, const char *opeer, struct ast_sockaddr *addr, int newdialog);
- static char *generate_random_string(char *buf, size_t size);
- static void build_callid_pvt(struct sip_pvt *pvt);
- static void change_callid_pvt(struct sip_pvt *pvt, const char *callid);
- static void build_callid_registry(struct sip_registry *reg, const struct ast_sockaddr *ourip, const char *fromdomain);
- static void build_localtag_registry(struct sip_registry *reg);
- static void make_our_tag(struct sip_pvt *pvt);
- static int add_header(struct sip_request *req, const char *var, const char *value);
- static int add_max_forwards(struct sip_pvt *dialog, struct sip_request *req);
- static int add_content(struct sip_request *req, const char *line);
- static int finalize_content(struct sip_request *req);
- static void destroy_msg_headers(struct sip_pvt *pvt);
- static int add_text(struct sip_request *req, struct sip_pvt *p);
- static int add_digit(struct sip_request *req, char digit, unsigned int duration, int mode);
- static int add_rpid(struct sip_request *req, struct sip_pvt *p);
- static int add_vidupdate(struct sip_request *req);
- static void add_route(struct sip_request *req, struct sip_route *route, int skip);
- static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field);
- static int copy_all_header(struct sip_request *req, const struct sip_request *orig, const char *field);
- static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const struct sip_request *orig, const char *field);
- static void set_destination(struct sip_pvt *p, const char *uri);
- static void add_date(struct sip_request *req);
- static void add_expires(struct sip_request *req, int expires);
- static void build_contact(struct sip_pvt *p, struct sip_request *req, int incoming);
- /*------Request handling functions */
- static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, int *recount, int *nounlock);
- static int handle_request_update(struct sip_pvt *p, struct sip_request *req);
- static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, int *recount, const char *e, int *nounlock);
- static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, uint32_t seqno, int *nounlock);
- static int handle_request_bye(struct sip_pvt *p, struct sip_request *req);
- static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *sin, const char *e);
- static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req);
- static int handle_request_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
- static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, const char *e);
- static void handle_request_info(struct sip_pvt *p, struct sip_request *req);
- static int handle_request_options(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
- static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req,
- int *nounlock, struct sip_pvt *replaces_pvt, struct ast_channel *replaces_chan);
- static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, const char *e);
- static int local_attended_transfer(struct sip_pvt *transferer, struct ast_channel *transferer_chan, uint32_t seqno, int *nounlock);
- /*------Response handling functions */
- static void handle_response_publish(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
- static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
- static void handle_response_notify(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
- static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
- static void handle_response_subscribe(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
- static int handle_response_register(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
- static void handle_response(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
- /*------ SRTP Support -------- */
- static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct ast_sdp_srtp **srtp, const char *a);
- /*------ T38 Support --------- */
- static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans);
- static void change_t38_state(struct sip_pvt *p, int state);
- /*------ Session-Timers functions --------- */
- static void proc_422_rsp(struct sip_pvt *p, struct sip_request *rsp);
- static int proc_session_timer(const void *vp);
- static void stop_session_timer(struct sip_pvt *p);
- static void start_session_timer(struct sip_pvt *p);
- static void restart_session_timer(struct sip_pvt *p);
- static const char *strefresherparam2str(enum st_refresher_param r);
- static int parse_session_expires(const char *p_hdrval, int *const p_interval, enum st_refresher_param *const p_ref);
- static int parse_minse(const char *p_hdrval, int *const p_interval);
- static int st_get_se(struct sip_pvt *, int max);
- static enum st_refresher st_get_refresher(struct sip_pvt *);
- static enum st_mode st_get_mode(struct sip_pvt *, int no_cached);
- static struct sip_st_dlg* sip_st_alloc(struct sip_pvt *const p);
- /*------- RTP Glue functions -------- */
- static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, const struct ast_format_cap *cap, int nat_active);
- /*!--- SIP MWI Subscription support */
- static int sip_subscribe_mwi(const char *value, int lineno);
- static void sip_subscribe_mwi_destroy(void *data);
- static void sip_send_all_mwi_subscriptions(void);
- static int sip_subscribe_mwi_do(const void *data);
- static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi);
- /*! \brief Definition of this channel for PBX channel registration */
- struct ast_channel_tech sip_tech = {
- .type = "SIP",
- .description = "Session Initiation Protocol (SIP)",
- .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
- .requester = sip_request_call, /* called with chan unlocked */
- .devicestate = sip_devicestate, /* called with chan unlocked (not chan-specific) */
- .call = sip_call, /* called with chan locked */
- .send_html = sip_sendhtml,
- .hangup = sip_hangup, /* called with chan locked */
- .answer = sip_answer, /* called with chan locked */
- .read = sip_read, /* called with chan locked */
- .write = sip_write, /* called with chan locked */
- .write_video = sip_write, /* called with chan locked */
- .write_text = sip_write,
- .indicate = sip_indicate, /* called with chan locked */
- .transfer = sip_transfer, /* called with chan locked */
- .fixup = sip_fixup, /* called with chan locked */
- .send_digit_begin = sip_senddigit_begin, /* called with chan unlocked */
- .send_digit_end = sip_senddigit_end,
- .early_bridge = ast_rtp_instance_early_bridge,
- .send_text = sip_sendtext, /* called with chan locked */
- .func_channel_read = sip_acf_channel_read,
- .setoption = sip_setoption,
- .queryoption = sip_queryoption,
- .get_pvt_uniqueid = sip_get_callid,
- };
- /*! \brief This version of the sip channel tech has no send_digit_begin
- * callback so that the core knows that the channel does not want
- * DTMF BEGIN frames.
- * The struct is initialized just before registering the channel driver,
- * and is for use with channels using SIP INFO DTMF.
- */
- struct ast_channel_tech sip_tech_info;
- /*------- CC Support -------- */
- static int sip_cc_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan);
- static int sip_cc_agent_start_offer_timer(struct ast_cc_agent *agent);
- static int sip_cc_agent_stop_offer_timer(struct ast_cc_agent *agent);
- static void sip_cc_agent_respond(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason);
- static int sip_cc_agent_status_request(struct ast_cc_agent *agent);
- static int sip_cc_agent_start_monitoring(struct ast_cc_agent *agent);
- static int sip_cc_agent_recall(struct ast_cc_agent *agent);
- static void sip_cc_agent_destructor(struct ast_cc_agent *agent);
- static struct ast_cc_agent_callbacks sip_cc_agent_callbacks = {
- .type = "SIP",
- .init = sip_cc_agent_init,
- .start_offer_timer = sip_cc_agent_start_offer_timer,
- .stop_offer_timer = sip_cc_agent_stop_offer_timer,
- .respond = sip_cc_agent_respond,
- .status_request = sip_cc_agent_status_request,
- .start_monitoring = sip_cc_agent_start_monitoring,
- .callee_available = sip_cc_agent_recall,
- .destructor = sip_cc_agent_destructor,
- };
- /* -------- End of declarations of structures, constants and forward declarations of functions
- Below starts actual code
- ------------------------
- */
- static int sip_epa_register(const struct epa_static_data *static_data)
- {
- struct epa_backend *backend = ast_calloc(1, sizeof(*backend));
- if (!backend) {
- return -1;
- }
- backend->static_data = static_data;
- AST_LIST_LOCK(&epa_static_data_list);
- AST_LIST_INSERT_TAIL(&epa_static_data_list, backend, next);
- AST_LIST_UNLOCK(&epa_static_data_list);
- return 0;
- }
- static void sip_epa_unregister_all(void)
- {
- struct epa_backend *backend;
- AST_LIST_LOCK(&epa_static_data_list);
- while ((backend = AST_LIST_REMOVE_HEAD(&epa_static_data_list, next))) {
- ast_free(backend);
- }
- AST_LIST_UNLOCK(&epa_static_data_list);
- }
- static void cc_handle_publish_error(struct sip_pvt *pvt, const int resp, struct sip_request *req, struct sip_epa_entry *epa_entry);
- static void cc_epa_destructor(void *data)
- {
- struct sip_epa_entry *epa_entry = data;
- struct cc_epa_entry *cc_entry = epa_entry->instance_data;
- ast_free(cc_entry);
- }
- static const struct epa_static_data cc_epa_static_data = {
- .event = CALL_COMPLETION,
- .name = "call-completion",
- .handle_error = cc_handle_publish_error,
- .destructor = cc_epa_destructor,
- };
- static const struct epa_static_data *find_static_data(const char * const event_package)
- {
- const struct epa_backend *backend = NULL;
- AST_LIST_LOCK(&epa_static_data_list);
- AST_LIST_TRAVERSE(&epa_static_data_list, backend, next) {
- if (!strcmp(backend->static_data->name, event_package)) {
- break;
- }
- }
- AST_LIST_UNLOCK(&epa_static_data_list);
- return backend ? backend->static_data : NULL;
- }
- static struct sip_epa_entry *create_epa_entry (const char * const event_package, const char * const destination)
- {
- struct sip_epa_entry *epa_entry;
- const struct epa_static_data *static_data;
- if (!(static_data = find_static_data(event_package))) {
- return NULL;
- }
- if (!(epa_entry = ao2_t_alloc(sizeof(*epa_entry), static_data->destructor, "Allocate new EPA entry"))) {
- return NULL;
- }
- epa_entry->static_data = static_data;
- ast_copy_string(epa_entry->destination, destination, sizeof(epa_entry->destination));
- return epa_entry;
- }
- static enum ast_cc_service_type service_string_to_service_type(const char * const service_string)
- {
- enum ast_cc_service_type service;
- for (service = AST_CC_CCBS; service <= AST_CC_CCNL; ++service) {
- if (!strcasecmp(service_string, sip_cc_service_map[service].service_string)) {
- return service;
- }
- }
- return AST_CC_NONE;
- }
- /* Even state compositors code */
- static void esc_entry_destructor(void *obj)
- {
- struct sip_esc_entry *esc_entry = obj;
- if (esc_entry->sched_id > -1) {
- AST_SCHED_DEL(sched, esc_entry->sched_id);
- }
- }
- static int esc_hash_fn(const void *obj, const int flags)
- {
- const struct sip_esc_entry *entry = obj;
- return ast_str_hash(entry->entity_tag);
- }
- static int esc_cmp_fn(void *obj, void *arg, int flags)
- {
- struct sip_esc_entry *entry1 = obj;
- struct sip_esc_entry *entry2 = arg;
- return (!strcmp(entry1->entity_tag, entry2->entity_tag)) ? (CMP_MATCH | CMP_STOP) : 0;
- }
- static struct event_state_compositor *get_esc(const char * const event_package) {
- int i;
- for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) {
- if (!strcasecmp(event_package, event_state_compositors[i].name)) {
- return &event_state_compositors[i];
- }
- }
- return NULL;
- }
- static struct sip_esc_entry *get_esc_entry(const char * entity_tag, struct event_state_compositor *esc) {
- struct sip_esc_entry *entry;
- struct sip_esc_entry finder;
- ast_copy_string(finder.entity_tag, entity_tag, sizeof(finder.entity_tag));
- entry = ao2_find(esc->compositor, &finder, OBJ_POINTER);
- return entry;
- }
- static int publish_expire(const void *data)
- {
- struct sip_esc_entry *esc_entry = (struct sip_esc_entry *) data;
- struct event_state_compositor *esc = get_esc(esc_entry->event);
- ast_assert(esc != NULL);
- ao2_unlink(esc->compositor, esc_entry);
- ao2_ref(esc_entry, -1);
- return 0;
- }
- static void create_new_sip_etag(struct sip_esc_entry *esc_entry, int is_linked)
- {
- int new_etag = ast_atomic_fetchadd_int(&esc_etag_counter, +1);
- struct event_state_compositor *esc = get_esc(esc_entry->event);
- ast_assert(esc != NULL);
- if (is_linked) {
- ao2_unlink(esc->compositor, esc_entry);
- }
- snprintf(esc_entry->entity_tag, sizeof(esc_entry->entity_tag), "%d", new_etag);
- ao2_link(esc->compositor, esc_entry);
- }
- static struct sip_esc_entry *create_esc_entry(struct event_state_compositor *esc, struct sip_request *req, const int expires)
- {
- struct sip_esc_entry *esc_entry;
- int expires_ms;
- if (!(esc_entry = ao2_alloc(sizeof(*esc_entry), esc_entry_destructor))) {
- return NULL;
- }
- esc_entry->event = esc->name;
- expires_ms = expires * 1000;
- /* Bump refcount for scheduler */
- ao2_ref(esc_entry, +1);
- esc_entry->sched_id = ast_sched_add(sched, expires_ms, publish_expire, esc_entry);
- /* Note: This links the esc_entry into the ESC properly */
- create_new_sip_etag(esc_entry, 0);
- return esc_entry;
- }
- static int initialize_escs(void)
- {
- int i, res = 0;
- for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) {
- if (!((event_state_compositors[i].compositor) =
- ao2_container_alloc(ESC_MAX_BUCKETS, esc_hash_fn, esc_cmp_fn))) {
- res = -1;
- }
- }
- return res;
- }
- static void destroy_escs(void)
- {
- int i;
- for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) {
- ao2_cleanup(event_state_compositors[i].compositor);
- }
- }
- static int find_by_notify_uri_helper(void *obj, void *arg, int flags)
- {
- struct ast_cc_agent *agent = obj;
- struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
- const char *uri = arg;
- return !sip_uri_cmp(agent_pvt->notify_uri, uri) ? CMP_MATCH | CMP_STOP : 0;
- }
- static struct ast_cc_agent *find_sip_cc_agent_by_notify_uri(const char * const uri)
- {
- struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_notify_uri_helper, (char *)uri, "SIP");
- return agent;
- }
- static int find_by_subscribe_uri_helper(void *obj, void *arg, int flags)
- {
- struct ast_cc_agent *agent = obj;
- struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
- const char *uri = arg;
- return !sip_uri_cmp(agent_pvt->subscribe_uri, uri) ? CMP_MATCH | CMP_STOP : 0;
- }
- static struct ast_cc_agent *find_sip_cc_agent_by_subscribe_uri(const char * const uri)
- {
- struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_subscribe_uri_helper, (char *)uri, "SIP");
- return agent;
- }
- static int find_by_callid_helper(void *obj, void *arg, int flags)
- {
- struct ast_cc_agent *agent = obj;
- struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
- struct sip_pvt *call_pvt = arg;
- return !strcmp(agent_pvt->original_callid, call_pvt->callid) ? CMP_MATCH | CMP_STOP : 0;
- }
- static struct ast_cc_agent *find_sip_cc_agent_by_original_callid(struct sip_pvt *pvt)
- {
- struct ast_cc_agent *agent = ast_cc_agent_callback(0, find_by_callid_helper, pvt, "SIP");
- return agent;
- }
- static int sip_cc_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan)
- {
- struct sip_cc_agent_pvt *agent_pvt = ast_calloc(1, sizeof(*agent_pvt));
- struct sip_pvt *call_pvt = ast_channel_tech_pvt(chan);
- if (!agent_pvt) {
- return -1;
- }
- ast_assert(!strcmp(ast_channel_tech(chan)->type, "SIP"));
- ast_copy_string(agent_pvt->original_callid, call_pvt->callid, sizeof(agent_pvt->original_callid));
- ast_copy_string(agent_pvt->original_exten, call_pvt->exten, sizeof(agent_pvt->original_exten));
- agent_pvt->offer_timer_id = -1;
- agent->private_data = agent_pvt;
- sip_pvt_lock(call_pvt);
- ast_set_flag(&call_pvt->flags[0], SIP_OFFER_CC);
- sip_pvt_unlock(call_pvt);
- return 0;
- }
- static int sip_offer_timer_expire(const void *data)
- {
- struct ast_cc_agent *agent = (struct ast_cc_agent *) data;
- struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
- agent_pvt->offer_timer_id = -1;
- return ast_cc_failed(agent->core_id, "SIP agent %s's offer timer expired", agent->device_name);
- }
- static int sip_cc_agent_start_offer_timer(struct ast_cc_agent *agent)
- {
- struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
- int when;
- when = ast_get_cc_offer_timer(agent->cc_params) * 1000;
- agent_pvt->offer_timer_id = ast_sched_add(sched, when, sip_offer_timer_expire, agent);
- return 0;
- }
- static int sip_cc_agent_stop_offer_timer(struct ast_cc_agent *agent)
- {
- struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
- AST_SCHED_DEL(sched, agent_pvt->offer_timer_id);
- return 0;
- }
- static void sip_cc_agent_respond(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason)
- {
- struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
- sip_pvt_lock(agent_pvt->subscribe_pvt);
- ast_set_flag(&agent_pvt->subscribe_pvt->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- if (reason == AST_CC_AGENT_RESPONSE_SUCCESS || !ast_strlen_zero(agent_pvt->notify_uri)) {
- /* The second half of this if statement may be a bit hard to grasp,
- * so here's an explanation. When a subscription comes into
- * chan_sip, as long as it is not malformed, it will be passed
- * to the CC core. If the core senses an out-of-order state transition,
- * then the core will call this callback with the "reason" set to a
- * failure condition.
- * However, an out-of-order state transition will occur during a resubscription
- * for CC. In such a case, we can see that we have already generated a notify_uri
- * and so we can detect that this isn't a *real* failure. Rather, it is just
- * something the core doesn't recognize as a legitimate SIP state transition.
- * Thus we respond with happiness and flowers.
- */
- transmit_response(agent_pvt->subscribe_pvt, "200 OK", &agent_pvt->subscribe_pvt->initreq);
- transmit_cc_notify(agent, agent_pvt->subscribe_pvt, CC_QUEUED);
- } else {
- transmit_response(agent_pvt->subscribe_pvt, "500 Internal Error", &agent_pvt->subscribe_pvt->initreq);
- }
- sip_pvt_unlock(agent_pvt->subscribe_pvt);
- agent_pvt->is_available = TRUE;
- }
- static int sip_cc_agent_status_request(struct ast_cc_agent *agent)
- {
- struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
- enum ast_device_state state = agent_pvt->is_available ? AST_DEVICE_NOT_INUSE : AST_DEVICE_INUSE;
- return ast_cc_agent_status_response(agent->core_id, state);
- }
- static int sip_cc_agent_start_monitoring(struct ast_cc_agent *agent)
- {
- /* To start monitoring just means to wait for an incoming PUBLISH
- * to tell us that the caller has become available again. No special
- * action is needed
- */
- return 0;
- }
- static int sip_cc_agent_recall(struct ast_cc_agent *agent)
- {
- struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
- /* If we have received a PUBLISH beforehand stating that the caller in question
- * is not available, we can save ourself a bit of effort here and just report
- * the caller as busy
- */
- if (!agent_pvt->is_available) {
- return ast_cc_agent_caller_busy(agent->core_id, "Caller %s is busy, reporting to the core",
- agent->device_name);
- }
- /* Otherwise, we transmit a NOTIFY to the caller and await either
- * a PUBLISH or an INVITE
- */
- sip_pvt_lock(agent_pvt->subscribe_pvt);
- transmit_cc_notify(agent, agent_pvt->subscribe_pvt, CC_READY);
- sip_pvt_unlock(agent_pvt->subscribe_pvt);
- return 0;
- }
- static void sip_cc_agent_destructor(struct ast_cc_agent *agent)
- {
- struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
- if (!agent_pvt) {
- /* The agent constructor probably failed. */
- return;
- }
- sip_cc_agent_stop_offer_timer(agent);
- if (agent_pvt->subscribe_pvt) {
- sip_pvt_lock(agent_pvt->subscribe_pvt);
- if (!ast_test_flag(&agent_pvt->subscribe_pvt->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) {
- /* If we haven't sent a 200 OK for the SUBSCRIBE dialog yet, then we need to send a response letting
- * the subscriber know something went wrong
- */
- transmit_response(agent_pvt->subscribe_pvt, "500 Internal Server Error", &agent_pvt->subscribe_pvt->initreq);
- }
- sip_pvt_unlock(agent_pvt->subscribe_pvt);
- agent_pvt->subscribe_pvt = dialog_unref(agent_pvt->subscribe_pvt, "SIP CC agent destructor: Remove ref to subscription");
- }
- ast_free(agent_pvt);
- }
- static int sip_monitor_instance_hash_fn(const void *obj, const int flags)
- {
- const struct sip_monitor_instance *monitor_instance = obj;
- return monitor_instance->core_id;
- }
- static int sip_monitor_instance_cmp_fn(void *obj, void *arg, int flags)
- {
- struct sip_monitor_instance *monitor_instance1 = obj;
- struct sip_monitor_instance *monitor_instance2 = arg;
- return monitor_instance1->core_id == monitor_instance2->core_id ? CMP_MATCH | CMP_STOP : 0;
- }
- static void sip_monitor_instance_destructor(void *data)
- {
- struct sip_monitor_instance *monitor_instance = data;
- if (monitor_instance->subscription_pvt) {
- sip_pvt_lock(monitor_instance->subscription_pvt);
- monitor_instance->subscription_pvt->expiry = 0;
- transmit_invite(monitor_instance->subscription_pvt, SIP_SUBSCRIBE, FALSE, 0, monitor_instance->subscribe_uri);
- sip_pvt_unlock(monitor_instance->subscription_pvt);
- dialog_unref(monitor_instance->subscription_pvt, "Unref monitor instance ref of subscription pvt");
- }
- if (monitor_instance->suspension_entry) {
- monitor_instance->suspension_entry->body[0] = '\0';
- transmit_publish(monitor_instance->suspension_entry, SIP_PUBLISH_REMOVE ,monitor_instance->notify_uri);
- ao2_t_ref(monitor_instance->suspension_entry, -1, "Decrementing suspension entry refcount in sip_monitor_instance_destructor");
- }
- ast_string_field_free_memory(monitor_instance);
- }
- static struct sip_monitor_instance *sip_monitor_instance_init(int core_id, const char * const subscribe_uri, const char * const peername, const char * const device_name)
- {
- struct sip_monitor_instance *monitor_instance = ao2_alloc(sizeof(*monitor_instance), sip_monitor_instance_destructor);
- if (!monitor_instance) {
- return NULL;
- }
- if (ast_string_field_init(monitor_instance, 256)) {
- ao2_ref(monitor_instance, -1);
- return NULL;
- }
- ast_string_field_set(monitor_instance, subscribe_uri, subscribe_uri);
- ast_string_field_set(monitor_instance, peername, peername);
- ast_string_field_set(monitor_instance, device_name, device_name);
- monitor_instance->core_id = core_id;
- ao2_link(sip_monitor_instances, monitor_instance);
- return monitor_instance;
- }
- static int find_sip_monitor_instance_by_subscription_pvt(void *obj, void *arg, int flags)
- {
- struct sip_monitor_instance *monitor_instance = obj;
- return monitor_instance->subscription_pvt == arg ? CMP_MATCH | CMP_STOP : 0;
- }
- static int find_sip_monitor_instance_by_suspension_entry(void *obj, void *arg, int flags)
- {
- struct sip_monitor_instance *monitor_instance = obj;
- return monitor_instance->suspension_entry == arg ? CMP_MATCH | CMP_STOP : 0;
- }
- static int sip_cc_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id);
- static int sip_cc_monitor_suspend(struct ast_cc_monitor *monitor);
- static int sip_cc_monitor_unsuspend(struct ast_cc_monitor *monitor);
- static int sip_cc_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id);
- static void sip_cc_monitor_destructor(void *private_data);
- static struct ast_cc_monitor_callbacks sip_cc_monitor_callbacks = {
- .type = "SIP",
- .request_cc = sip_cc_monitor_request_cc,
- .suspend = sip_cc_monitor_suspend,
- .unsuspend = sip_cc_monitor_unsuspend,
- .cancel_available_timer = sip_cc_monitor_cancel_available_timer,
- .destructor = sip_cc_monitor_destructor,
- };
- static int sip_cc_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id)
- {
- struct sip_monitor_instance *monitor_instance = monitor->private_data;
- enum ast_cc_service_type service = monitor->service_offered;
- int when;
- if (!monitor_instance) {
- return -1;
- }
- if (!(monitor_instance->subscription_pvt = sip_alloc(NULL, NULL, 0, SIP_SUBSCRIBE, NULL, 0))) {
- return -1;
- }
- when = service == AST_CC_CCBS ? ast_get_ccbs_available_timer(monitor->interface->config_params) :
- ast_get_ccnr_available_timer(monitor->interface->config_params);
- sip_pvt_lock(monitor_instance->subscription_pvt);
- ast_set_flag(&monitor_instance->subscription_pvt->flags[0], SIP_OUTGOING);
- create_addr(monitor_instance->subscription_pvt, monitor_instance->peername, 0, 1);
- ast_sip_ouraddrfor(&monitor_instance->subscription_pvt->sa, &monitor_instance->subscription_pvt->ourip, monitor_instance->subscription_pvt);
- monitor_instance->subscription_pvt->subscribed = CALL_COMPLETION;
- monitor_instance->subscription_pvt->expiry = when;
- transmit_invite(monitor_instance->subscription_pvt, SIP_SUBSCRIBE, FALSE, 2, monitor_instance->subscribe_uri);
- sip_pvt_unlock(monitor_instance->subscription_pvt);
- ao2_t_ref(monitor, +1, "Adding a ref to the monitor for the scheduler");
- *available_timer_id = ast_sched_add(sched, when * 1000, ast_cc_available_timer_expire, monitor);
- return 0;
- }
- static int construct_pidf_body(enum sip_cc_publish_state state, char *pidf_body, size_t size, const char *presentity)
- {
- struct ast_str *body = ast_str_alloca(size);
- char tuple_id[32];
- generate_random_string(tuple_id, sizeof(tuple_id));
- /* We'll make this a bare-bones pidf body. In state_notify_build_xml, the PIDF
- * body gets a lot more extra junk that isn't necessary, so we'll leave it out here.
- */
- ast_str_append(&body, 0, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
- /* XXX The entity attribute is currently set to the peer name associated with the
- * dialog. This is because we currently only call this function for call-completion
- * PUBLISH bodies. In such cases, the entity is completely disregarded. For other
- * event packages, it may be crucial to have a proper URI as the presentity so this
- * should be revisited as support is expanded.
- */
- ast_str_append(&body, 0, "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" entity=\"%s\">\n", presentity);
- ast_str_append(&body, 0, "<tuple id=\"%s\">\n", tuple_id);
- ast_str_append(&body, 0, "<status><basic>%s</basic></status>\n", state == CC_OPEN ? "open" : "closed");
- ast_str_append(&body, 0, "</tuple>\n");
- ast_str_append(&body, 0, "</presence>\n");
- ast_copy_string(pidf_body, ast_str_buffer(body), size);
- return 0;
- }
- static int sip_cc_monitor_suspend(struct ast_cc_monitor *monitor)
- {
- struct sip_monitor_instance *monitor_instance = monitor->private_data;
- enum sip_publish_type publish_type;
- struct cc_epa_entry *cc_entry;
- if (!monitor_instance) {
- return -1;
- }
- if (!monitor_instance->suspension_entry) {
- /* We haven't yet allocated the suspension entry, so let's give it a shot */
- if (!(monitor_instance->suspension_entry = create_epa_entry("call-completion", monitor_instance->peername))) {
- ast_log(LOG_WARNING, "Unable to allocate sip EPA entry for call-completion\n");
- ao2_ref(monitor_instance, -1);
- return -1;
- }
- if (!(cc_entry = ast_calloc(1, sizeof(*cc_entry)))) {
- ast_log(LOG_WARNING, "Unable to allocate space for instance data of EPA entry for call-completion\n");
- ao2_ref(monitor_instance, -1);
- return -1;
- }
- cc_entry->core_id = monitor->core_id;
- monitor_instance->suspension_entry->instance_data = cc_entry;
- publish_type = SIP_PUBLISH_INITIAL;
- } else {
- publish_type = SIP_PUBLISH_MODIFY;
- cc_entry = monitor_instance->suspension_entry->instance_data;
- }
- cc_entry->current_state = CC_CLOSED;
- if (ast_strlen_zero(monitor_instance->notify_uri)) {
- /* If we have no set notify_uri, then what this means is that we have
- * not received a NOTIFY from this destination stating that he is
- * currently available.
- *
- * This situation can arise when the core calls the suspend callbacks
- * of multiple destinations. If one of the other destinations aside
- * from this one notified Asterisk that he is available, then there
- * is no reason to take any suspension action on this device. Rather,
- * we should return now and if we receive a NOTIFY while monitoring
- * is still "suspended" then we can immediately respond with the
- * proper PUBLISH to let this endpoint know what is going on.
- */
- return 0;
- }
- construct_pidf_body(CC_CLOSED, monitor_instance->suspension_entry->body, sizeof(monitor_instance->suspension_entry->body), monitor_instance->peername);
- return transmit_publish(monitor_instance->suspension_entry, publish_type, monitor_instance->notify_uri);
- }
- static int sip_cc_monitor_unsuspend(struct ast_cc_monitor *monitor)
- {
- struct sip_monitor_instance *monitor_instance = monitor->private_data;
- struct cc_epa_entry *cc_entry;
- if (!monitor_instance) {
- return -1;
- }
- ast_assert(monitor_instance->suspension_entry != NULL);
- cc_entry = monitor_instance->suspension_entry->instance_data;
- cc_entry->current_state = CC_OPEN;
- if (ast_strlen_zero(monitor_instance->notify_uri)) {
- /* This means we are being asked to unsuspend a call leg we never
- * sent a PUBLISH on. As such, there is no reason to send another
- * PUBLISH at this point either. We can just return instead.
- */
- return 0;
- }
- construct_pidf_body(CC_OPEN, monitor_instance->suspension_entry->body, sizeof(monitor_instance->suspension_entry->body), monitor_instance->peername);
- return transmit_publish(monitor_instance->suspension_entry, SIP_PUBLISH_MODIFY, monitor_instance->notify_uri);
- }
- static int sip_cc_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id)
- {
- if (*sched_id != -1) {
- AST_SCHED_DEL(sched, *sched_id);
- ao2_t_ref(monitor, -1, "Removing scheduler's reference to the monitor");
- }
- return 0;
- }
- static void sip_cc_monitor_destructor(void *private_data)
- {
- struct sip_monitor_instance *monitor_instance = private_data;
- ao2_unlink(sip_monitor_instances, monitor_instance);
- ast_module_unref(ast_module_info->self);
- }
- static int sip_get_cc_information(struct sip_request *req, char *subscribe_uri, size_t size, enum ast_cc_service_type *service)
- {
- char *call_info = ast_strdupa(sip_get_header(req, "Call-Info"));
- char *uri;
- char *purpose;
- char *service_str;
- static const char cc_purpose[] = "purpose=call-completion";
- static const int cc_purpose_len = sizeof(cc_purpose) - 1;
- if (ast_strlen_zero(call_info)) {
- /* No Call-Info present. Definitely no CC offer */
- return -1;
- }
- uri = strsep(&call_info, ";");
- while ((purpose = strsep(&call_info, ";"))) {
- if (!strncmp(purpose, cc_purpose, cc_purpose_len)) {
- break;
- }
- }
- if (!purpose) {
- /* We didn't find the appropriate purpose= parameter. Oh well */
- return -1;
- }
- /* Okay, call-completion has been offered. Let's figure out what type of service this is */
- while ((service_str = strsep(&call_info, ";"))) {
- if (!strncmp(service_str, "m=", 2)) {
- break;
- }
- }
- if (!service_str) {
- /* So they didn't offer a particular service, We'll just go with CCBS since it really
- * doesn't matter anyway
- */
- service_str = "BS";
- } else {
- /* We already determined that there is an "m=" so no need to check
- * the result of this strsep
- */
- strsep(&service_str, "=");
- }
- if ((*service = service_string_to_service_type(service_str)) == AST_CC_NONE) {
- /* Invalid service offered */
- return -1;
- }
- ast_copy_string(subscribe_uri, get_in_brackets(uri), size);
- return 0;
- }
- /*
- * \brief Determine what, if any, CC has been offered and queue a CC frame if possible
- *
- * After taking care of some formalities to be sure that this call is eligible for CC,
- * we first try to see if we can make use of native CC. We grab the information from
- * the passed-in sip_request (which is always a response to an INVITE). If we can
- * use native CC monitoring for the call, then so be it.
- *
- * If native cc monitoring is not possible or not supported, then we will instead attempt
- * to use generic monitoring. Falling back to generic from a failed attempt at using native
- * monitoring will only work if the monitor policy of the endpoint is "always"
- *
- * \param pvt The current dialog. Contains CC parameters for the endpoint
- * \param req The response to the INVITE we want to inspect
- * \param service The service to use if generic monitoring is to be used. For native
- * monitoring, we get the service from the SIP response itself
- */
- static void sip_handle_cc(struct sip_pvt *pvt, struct sip_request *req, enum ast_cc_service_type service)
- {
- enum ast_cc_monitor_policies monitor_policy = ast_get_cc_monitor_policy(pvt->cc_params);
- int core_id;
- char interface_name[AST_CHANNEL_NAME];
- if (monitor_policy == AST_CC_MONITOR_NEVER) {
- /* Don't bother, just return */
- return;
- }
- if ((core_id = ast_cc_get_current_core_id(pvt->owner)) == -1) {
- /* For some reason, CC is invalid, so don't try it! */
- return;
- }
- ast_channel_get_device_name(pvt->owner, interface_name, sizeof(interface_name));
- if (monitor_policy == AST_CC_MONITOR_ALWAYS || monitor_policy == AST_CC_MONITOR_NATIVE) {
- char subscribe_uri[SIPBUFSIZE];
- char device_name[AST_CHANNEL_NAME];
- enum ast_cc_service_type offered_service;
- struct sip_monitor_instance *monitor_instance;
- if (sip_get_cc_information(req, subscribe_uri, sizeof(subscribe_uri), &offered_service)) {
- /* If CC isn't being offered to us, or for some reason the CC offer is
- * not formatted correctly, then it may still be possible to use generic
- * call completion since the monitor policy may be "always"
- */
- goto generic;
- }
- ast_channel_get_device_name(pvt->owner, device_name, sizeof(device_name));
- if (!(monitor_instance = sip_monitor_instance_init(core_id, subscribe_uri, pvt->peername, device_name))) {
- /* Same deal. We can try using generic still */
- goto generic;
- }
- /* We bump the refcount of chan_sip because once we queue this frame, the CC core
- * will have a reference to callbacks in this module. We decrement the module
- * refcount once the monitor destructor is called
- */
- ast_module_ref(ast_module_info->self);
- ast_queue_cc_frame(pvt->owner, "SIP", pvt->dialstring, offered_service, monitor_instance);
- ao2_ref(monitor_instance, -1);
- return;
- }
- generic:
- if (monitor_policy == AST_CC_MONITOR_GENERIC || monitor_policy == AST_CC_MONITOR_ALWAYS) {
- ast_queue_cc_frame(pvt->owner, AST_CC_GENERIC_MONITOR_TYPE, interface_name, service, NULL);
- }
- }
- /*! \brief Working TLS connection configuration */
- static struct ast_tls_config sip_tls_cfg;
- /*! \brief Default TLS connection configuration */
- static struct ast_tls_config default_tls_cfg;
- /*! \brief Default DTLS connection configuration */
- static struct ast_rtp_dtls_cfg default_dtls_cfg;
- /*! \brief The TCP server definition */
- static struct ast_tcptls_session_args sip_tcp_desc = {
- .accept_fd = -1,
- .master = AST_PTHREADT_NULL,
- .tls_cfg = NULL,
- .poll_timeout = -1,
- .name = "SIP TCP server",
- .accept_fn = ast_tcptls_server_root,
- .worker_fn = sip_tcp_worker_fn,
- };
- /*! \brief The TCP/TLS server definition */
- static struct ast_tcptls_session_args sip_tls_desc = {
- .accept_fd = -1,
- .master = AST_PTHREADT_NULL,
- .tls_cfg = &sip_tls_cfg,
- .poll_timeout = -1,
- .name = "SIP TLS server",
- .accept_fn = ast_tcptls_server_root,
- .worker_fn = sip_tcp_worker_fn,
- };
- /*! \brief Append to SIP dialog history
- \return Always returns 0 */
- #define append_history(p, event, fmt , args... ) append_history_full(p, "%-15s " fmt, event, ## args)
- /*! \brief map from an integer value to a string.
- * If no match is found, return errorstring
- */
- static const char *map_x_s(const struct _map_x_s *table, int x, const char *errorstring)
- {
- const struct _map_x_s *cur;
- for (cur = table; cur->s; cur++) {
- if (cur->x == x) {
- return cur->s;
- }
- }
- return errorstring;
- }
- /*! \brief map from a string to an integer value, case insensitive.
- * If no match is found, return errorvalue.
- */
- static int map_s_x(const struct _map_x_s *table, const char *s, int errorvalue)
- {
- const struct _map_x_s *cur;
- for (cur = table; cur->s; cur++) {
- if (!strcasecmp(cur->s, s)) {
- return cur->x;
- }
- }
- return errorvalue;
- }
- static enum AST_REDIRECTING_REASON sip_reason_str_to_code(const char *text)
- {
- enum AST_REDIRECTING_REASON ast = AST_REDIRECTING_REASON_UNKNOWN;
- int i;
- for (i = 0; i < ARRAY_LEN(sip_reason_table); ++i) {
- if (!strcasecmp(text, sip_reason_table[i].text)) {
- ast = sip_reason_table[i].code;
- break;
- }
- }
- return ast;
- }
- static const char *sip_reason_code_to_str(struct ast_party_redirecting_reason *reason, int *table_lookup)
- {
- int code = reason->code;
- /* If there's a specific string set, then we just
- * use it.
- */
- if (!ast_strlen_zero(reason->str)) {
- /* If we care about whether this can be found in
- * the table, then we need to check about that.
- */
- if (table_lookup) {
- /* If the string is literally "unknown" then don't bother with the lookup
- * because it can lead to a false negative.
- */
- if (!strcasecmp(reason->str, "unknown") ||
- sip_reason_str_to_code(reason->str) != AST_REDIRECTING_REASON_UNKNOWN) {
- *table_lookup = TRUE;
- } else {
- *table_lookup = FALSE;
- }
- }
- return reason->str;
- }
- if (table_lookup) {
- *table_lookup = TRUE;
- }
- if (code >= 0 && code < ARRAY_LEN(sip_reason_table)) {
- return sip_reason_table[code].text;
- }
- return "unknown";
- }
- /*!
- * \brief generic function for determining if a correct transport is being
- * used to contact a peer
- *
- * this is done as a macro so that the "tmpl" var can be passed either a
- * sip_request or a sip_peer
- */
- #define check_request_transport(peer, tmpl) ({ \
- int ret = 0; \
- if (peer->socket.type == tmpl->socket.type) \
- ; \
- else if (!(peer->transports & tmpl->socket.type)) {\
- ast_log(LOG_ERROR, \
- "'%s' is not a valid transport for '%s'. we only use '%s'! ending call.\n", \
- sip_get_transport(tmpl->socket.type), peer->name, get_transport_list(peer->transports) \
- ); \
- ret = 1; \
- } else if (peer->socket.type & AST_TRANSPORT_TLS) { \
- ast_log(LOG_WARNING, \
- "peer '%s' HAS NOT USED (OR SWITCHED TO) TLS in favor of '%s' (but this was allowed in sip.conf)!\n", \
- peer->name, sip_get_transport(tmpl->socket.type) \
- ); \
- } else { \
- ast_debug(1, \
- "peer '%s' has contacted us over %s even though we prefer %s.\n", \
- peer->name, sip_get_transport(tmpl->socket.type), sip_get_transport(peer->socket.type) \
- ); \
- }\
- (ret); \
- })
- /*! \brief
- * duplicate a list of channel variables, \return the copy.
- */
- static struct ast_variable *copy_vars(struct ast_variable *src)
- {
- struct ast_variable *res = NULL, *tmp, *v = NULL;
- for (v = src ; v ; v = v->next) {
- if ((tmp = ast_variable_new(v->name, v->value, v->file))) {
- tmp->next = res;
- res = tmp;
- }
- }
- return res;
- }
- static void tcptls_packet_destructor(void *obj)
- {
- struct tcptls_packet *packet = obj;
- ast_free(packet->data);
- }
- static void sip_tcptls_client_args_destructor(void *obj)
- {
- struct ast_tcptls_session_args *args = obj;
- if (args->tls_cfg) {
- ast_free(args->tls_cfg->certfile);
- ast_free(args->tls_cfg->pvtfile);
- ast_free(args->tls_cfg->cipher);
- ast_free(args->tls_cfg->cafile);
- ast_free(args->tls_cfg->capath);
- ast_ssl_teardown(args->tls_cfg);
- }
- ast_free(args->tls_cfg);
- ast_free((char *) args->name);
- }
- static void sip_threadinfo_destructor(void *obj)
- {
- struct sip_threadinfo *th = obj;
- struct tcptls_packet *packet;
- if (th->alert_pipe[1] > -1) {
- close(th->alert_pipe[0]);
- }
- if (th->alert_pipe[1] > -1) {
- close(th->alert_pipe[1]);
- }
- th->alert_pipe[0] = th->alert_pipe[1] = -1;
- while ((packet = AST_LIST_REMOVE_HEAD(&th->packet_q, entry))) {
- ao2_t_ref(packet, -1, "thread destruction, removing packet from frame queue");
- }
- if (th->tcptls_session) {
- ao2_t_ref(th->tcptls_session, -1, "remove tcptls_session for sip_threadinfo object");
- }
- }
- /*! \brief creates a sip_threadinfo object and links it into the threadt table. */
- static struct sip_threadinfo *sip_threadinfo_create(struct ast_tcptls_session_instance *tcptls_session, int transport)
- {
- struct sip_threadinfo *th;
- if (!tcptls_session || !(th = ao2_alloc(sizeof(*th), sip_threadinfo_destructor))) {
- return NULL;
- }
- th->alert_pipe[0] = th->alert_pipe[1] = -1;
- if (pipe(th->alert_pipe) == -1) {
- ao2_t_ref(th, -1, "Failed to open alert pipe on sip_threadinfo");
- ast_log(LOG_ERROR, "Could not create sip alert pipe in tcptls thread, error %s\n", strerror(errno));
- return NULL;
- }
- ao2_t_ref(tcptls_session, +1, "tcptls_session ref for sip_threadinfo object");
- th->tcptls_session = tcptls_session;
- th->type = transport ? transport : (tcptls_session->ssl ? AST_TRANSPORT_TLS: AST_TRANSPORT_TCP);
- ao2_t_link(threadt, th, "Adding new tcptls helper thread");
- ao2_t_ref(th, -1, "Decrementing threadinfo ref from alloc, only table ref remains");
- return th;
- }
- /*! \brief used to indicate to a tcptls thread that data is ready to be written */
- static int sip_tcptls_write(struct ast_tcptls_session_instance *tcptls_session, const void *buf, size_t len)
- {
- int res = len;
- struct sip_threadinfo *th = NULL;
- struct tcptls_packet *packet = NULL;
- struct sip_threadinfo tmp = {
- .tcptls_session = tcptls_session,
- };
- enum sip_tcptls_alert alert = TCPTLS_ALERT_DATA;
- if (!tcptls_session) {
- return XMIT_ERROR;
- }
- ao2_lock(tcptls_session);
- if ((tcptls_session->fd == -1) ||
- !(th = ao2_t_find(threadt, &tmp, OBJ_POINTER, "ao2_find, getting sip_threadinfo in tcp helper thread")) ||
- !(packet = ao2_alloc(sizeof(*packet), tcptls_packet_destructor)) ||
- !(packet->data = ast_str_create(len))) {
- goto tcptls_write_setup_error;
- }
- /* goto tcptls_write_error should _NOT_ be used beyond this point */
- ast_str_set(&packet->data, 0, "%s", (char *) buf);
- packet->len = len;
- /* alert tcptls thread handler that there is a packet to be sent.
- * must lock the thread info object to guarantee control of the
- * packet queue */
- ao2_lock(th);
- if (write(th->alert_pipe[1], &alert, sizeof(alert)) == -1) {
- ast_log(LOG_ERROR, "write() to alert pipe failed: %s\n", strerror(errno));
- ao2_t_ref(packet, -1, "could not write to alert pipe, remove packet");
- packet = NULL;
- res = XMIT_ERROR;
- } else { /* it is safe to queue the frame after issuing the alert when we hold the threadinfo lock */
- AST_LIST_INSERT_TAIL(&th->packet_q, packet, entry);
- }
- ao2_unlock(th);
- ao2_unlock(tcptls_session);
- ao2_t_ref(th, -1, "In sip_tcptls_write, unref threadinfo object after finding it");
- return res;
- tcptls_write_setup_error:
- if (th) {
- ao2_t_ref(th, -1, "In sip_tcptls_write, unref threadinfo obj, could not create packet");
- }
- if (packet) {
- ao2_t_ref(packet, -1, "could not allocate packet's data");
- }
- ao2_unlock(tcptls_session);
- return XMIT_ERROR;
- }
- /*! \brief SIP TCP connection handler */
- static void *sip_tcp_worker_fn(void *data)
- {
- struct ast_tcptls_session_instance *tcptls_session = data;
- return _sip_tcp_helper_thread(tcptls_session);
- }
- /*! \brief SIP WebSocket connection handler */
- static void sip_websocket_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
- {
- int res;
- if (ast_websocket_set_nonblock(session)) {
- goto end;
- }
- if (ast_websocket_set_timeout(session, sip_cfg.websocket_write_timeout)) {
- goto end;
- }
- while ((res = ast_wait_for_input(ast_websocket_fd(session), -1)) > 0) {
- char *payload;
- uint64_t payload_len;
- enum ast_websocket_opcode opcode;
- int fragmented;
- if (ast_websocket_read(session, &payload, &payload_len, &opcode, &fragmented)) {
- /* We err on the side of caution and terminate the session if any error occurs */
- break;
- }
- if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) {
- struct sip_request req = { 0, };
- char data[payload_len + 1];
- if (!(req.data = ast_str_create(payload_len + 1))) {
- goto end;
- }
- strncpy(data, payload, payload_len);
- data[payload_len] = '\0';
- if (ast_str_set(&req.data, -1, "%s", data) == AST_DYNSTR_BUILD_FAILED) {
- deinit_req(&req);
- goto end;
- }
- req.socket.fd = ast_websocket_fd(session);
- set_socket_transport(&req.socket, ast_websocket_is_secure(session) ? AST_TRANSPORT_WSS : AST_TRANSPORT_WS);
- req.socket.ws_session = session;
- handle_request_do(&req, ast_websocket_remote_address(session));
- deinit_req(&req);
- } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
- break;
- }
- }
- end:
- ast_websocket_unref(session);
- }
- /*! \brief Check if the authtimeout has expired.
- * \param start the time when the session started
- *
- * \retval 0 the timeout has expired
- * \retval -1 error
- * \return the number of milliseconds until the timeout will expire
- */
- static int sip_check_authtimeout(time_t start)
- {
- int timeout;
- time_t now;
- if(time(&now) == -1) {
- ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
- return -1;
- }
- timeout = (authtimeout - (now - start)) * 1000;
- if (timeout < 0) {
- /* we have timed out */
- return 0;
- }
- return timeout;
- }
- /*!
- * \brief Indication of a TCP message's integrity
- */
- enum message_integrity {
- /*!
- * The message has an error in it with
- * regards to its Content-Length header
- */
- MESSAGE_INVALID,
- /*!
- * The message is incomplete
- */
- MESSAGE_FRAGMENT,
- /*!
- * The data contains a complete message
- * plus a fragment of another.
- */
- MESSAGE_FRAGMENT_COMPLETE,
- /*!
- * The message is complete
- */
- MESSAGE_COMPLETE,
- };
- /*!
- * \brief
- * Get the content length from an unparsed SIP message
- *
- * \param message The unparsed SIP message headers
- * \return The value of the Content-Length header or -1 if message is invalid
- */
- static int read_raw_content_length(const char *message)
- {
- char *content_length_str;
- int content_length = -1;
- struct ast_str *msg_copy;
- char *msg;
- /* Using a ast_str because lws2sws takes one of those */
- if (!(msg_copy = ast_str_create(strlen(message) + 1))) {
- return -1;
- }
- ast_str_set(&msg_copy, 0, "%s", message);
- if (sip_cfg.pedanticsipchecking) {
- lws2sws(msg_copy);
- }
- msg = ast_str_buffer(msg_copy);
- /* Let's find a Content-Length header */
- if ((content_length_str = strcasestr(msg, "\nContent-Length:"))) {
- content_length_str += sizeof("\nContent-Length:") - 1;
- } else if ((content_length_str = strcasestr(msg, "\nl:"))) {
- content_length_str += sizeof("\nl:") - 1;
- } else {
- /* RFC 3261 18.3
- * "In the case of stream-oriented transports such as TCP, the Content-
- * Length header field indicates the size of the body. The Content-
- * Length header field MUST be used with stream oriented transports."
- */
- goto done;
- }
- /* Double-check that this is a complete header */
- if (!strchr(content_length_str, '\n')) {
- goto done;
- }
- if (sscanf(content_length_str, "%30d", &content_length) != 1) {
- content_length = -1;
- }
- done:
- ast_free(msg_copy);
- return content_length;
- }
- /*!
- * \brief Check that a message received over TCP is a full message
- *
- * This will take the information read in and then determine if
- * 1) The message is a full SIP request
- * 2) The message is a partial SIP request
- * 3) The message contains a full SIP request along with another partial request
- * \param data The unparsed incoming SIP message.
- * \param request The resulting request with extra fragments removed.
- * \param overflow If the message contains more than a full request, this is the remainder of the message
- * \return The resulting integrity of the message
- */
- static enum message_integrity check_message_integrity(struct ast_str **request, struct ast_str **overflow)
- {
- char *message = ast_str_buffer(*request);
- char *body;
- int content_length;
- int message_len = ast_str_strlen(*request);
- int body_len;
- /* Important pieces to search for in a SIP request are \r\n\r\n. This
- * marks either
- * 1) The division between the headers and body
- * 2) The end of the SIP request
- */
- body = strstr(message, "\r\n\r\n");
- if (!body) {
- /* This is clearly a partial message since we haven't reached an end
- * yet.
- */
- return MESSAGE_FRAGMENT;
- }
- body += sizeof("\r\n\r\n") - 1;
- body_len = message_len - (body - message);
- body[-1] = '\0';
- content_length = read_raw_content_length(message);
- body[-1] = '\n';
- if (content_length < 0) {
- return MESSAGE_INVALID;
- } else if (content_length == 0) {
- /* We've definitely received an entire message. We need
- * to check if there's also a fragment of another message
- * in addition.
- */
- if (body_len == 0) {
- return MESSAGE_COMPLETE;
- } else {
- ast_str_append(overflow, 0, "%s", body);
- ast_str_truncate(*request, message_len - body_len);
- return MESSAGE_FRAGMENT_COMPLETE;
- }
- }
- /* Positive content length. Let's see what sort of
- * message body we're dealing with.
- */
- if (body_len < content_length) {
- /* We don't have the full message body yet */
- return MESSAGE_FRAGMENT;
- } else if (body_len > content_length) {
- /* We have the full message plus a fragment of a further
- * message
- */
- ast_str_append(overflow, 0, "%s", body + content_length);
- ast_str_truncate(*request, message_len - (body_len - content_length));
- return MESSAGE_FRAGMENT_COMPLETE;
- } else {
- /* Yay! Full message with no extra content */
- return MESSAGE_COMPLETE;
- }
- }
- /*!
- * \brief Read SIP request or response from a TCP/TLS connection
- *
- * \param req The request structure to be filled in
- * \param tcptls_session The TCP/TLS connection from which to read
- * \retval -1 Failed to read data
- * \retval 0 Successfully read data
- */
- static int sip_tcptls_read(struct sip_request *req, struct ast_tcptls_session_instance *tcptls_session,
- int authenticated, time_t start)
- {
- enum message_integrity message_integrity = MESSAGE_FRAGMENT;
- while (message_integrity == MESSAGE_FRAGMENT) {
- size_t datalen;
- if (ast_str_strlen(tcptls_session->overflow_buf) == 0) {
- char readbuf[4097];
- int timeout;
- int res;
- if (!tcptls_session->client && !authenticated) {
- if ((timeout = sip_check_authtimeout(start)) < 0) {
- return -1;
- }
- if (timeout == 0) {
- ast_debug(2, "SIP TCP/TLS server timed out\n");
- return -1;
- }
- } else {
- timeout = -1;
- }
- res = ast_wait_for_input(tcptls_session->fd, timeout);
- if (res < 0) {
- ast_debug(2, "SIP TCP/TLS server :: ast_wait_for_input returned %d\n", res);
- return -1;
- } else if (res == 0) {
- ast_debug(2, "SIP TCP/TLS server timed out\n");
- return -1;
- }
- res = ast_tcptls_server_read(tcptls_session, readbuf, sizeof(readbuf) - 1);
- if (res < 0) {
- if (errno == EAGAIN || errno == EINTR) {
- continue;
- }
- ast_debug(2, "SIP TCP/TLS server error when receiving data\n");
- return -1;
- } else if (res == 0) {
- ast_debug(2, "SIP TCP/TLS server has shut down\n");
- return -1;
- }
- readbuf[res] = '\0';
- ast_str_append(&req->data, 0, "%s", readbuf);
- } else {
- ast_str_append(&req->data, 0, "%s", ast_str_buffer(tcptls_session->overflow_buf));
- ast_str_reset(tcptls_session->overflow_buf);
- }
- datalen = ast_str_strlen(req->data);
- if (datalen > SIP_MAX_PACKET_SIZE) {
- ast_log(LOG_WARNING, "Rejecting TCP/TLS packet from '%s' because way too large: %zu\n",
- ast_sockaddr_stringify(&tcptls_session->remote_address), datalen);
- return -1;
- }
- message_integrity = check_message_integrity(&req->data, &tcptls_session->overflow_buf);
- }
- return 0;
- }
- /*! \brief SIP TCP thread management function
- This function reads from the socket, parses the packet into a request
- */
- static void *_sip_tcp_helper_thread(struct ast_tcptls_session_instance *tcptls_session)
- {
- int res, timeout = -1, authenticated = 0, flags;
- time_t start;
- struct sip_request req = { 0, } , reqcpy = { 0, };
- struct sip_threadinfo *me = NULL;
- char buf[1024] = "";
- struct pollfd fds[2] = { { 0 }, { 0 }, };
- struct ast_tcptls_session_args *ca = NULL;
- /* If this is a server session, then the connection has already been
- * setup. Check if the authlimit has been reached and if not create the
- * threadinfo object so we can access this thread for writing.
- *
- * if this is a client connection more work must be done.
- * 1. We own the parent session args for a client connection. This pointer needs
- * to be held on to so we can decrement it's ref count on thread destruction.
- * 2. The threadinfo object was created before this thread was launched, however
- * it must be found within the threadt table.
- * 3. Last, the tcptls_session must be started.
- */
- if (!tcptls_session->client) {
- if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= authlimit) {
- /* unauth_sessions is decremented in the cleanup code */
- goto cleanup;
- }
- if ((flags = fcntl(tcptls_session->fd, F_GETFL)) == -1) {
- ast_log(LOG_ERROR, "error setting socket to non blocking mode, fcntl() failed: %s\n", strerror(errno));
- goto cleanup;
- }
- flags |= O_NONBLOCK;
- if (fcntl(tcptls_session->fd, F_SETFL, flags) == -1) {
- ast_log(LOG_ERROR, "error setting socket to non blocking mode, fcntl() failed: %s\n", strerror(errno));
- goto cleanup;
- }
- if (!(me = sip_threadinfo_create(tcptls_session, tcptls_session->ssl ? AST_TRANSPORT_TLS : AST_TRANSPORT_TCP))) {
- goto cleanup;
- }
- ao2_t_ref(me, +1, "Adding threadinfo ref for tcp_helper_thread");
- } else {
- struct sip_threadinfo tmp = {
- .tcptls_session = tcptls_session,
- };
- if ((!(ca = tcptls_session->parent)) ||
- (!(me = ao2_t_find(threadt, &tmp, OBJ_POINTER, "ao2_find, getting sip_threadinfo in tcp helper thread"))) ||
- (!(tcptls_session = ast_tcptls_client_start(tcptls_session)))) {
- goto cleanup;
- }
- }
- flags = 1;
- if (setsockopt(tcptls_session->fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
- ast_log(LOG_ERROR, "error enabling TCP keep-alives on sip socket: %s\n", strerror(errno));
- goto cleanup;
- }
- me->threadid = pthread_self();
- ast_debug(2, "Starting thread for %s server\n", tcptls_session->ssl ? "TLS" : "TCP");
- /* set up pollfd to watch for reads on both the socket and the alert_pipe */
- fds[0].fd = tcptls_session->fd;
- fds[1].fd = me->alert_pipe[0];
- fds[0].events = fds[1].events = POLLIN | POLLPRI;
- if (!(req.data = ast_str_create(SIP_MIN_PACKET))) {
- goto cleanup;
- }
- if (!(reqcpy.data = ast_str_create(SIP_MIN_PACKET))) {
- goto cleanup;
- }
- if(time(&start) == -1) {
- ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
- goto cleanup;
- }
- /*
- * We cannot let the stream exclusively wait for data to arrive.
- * We have to wake up the task to send outgoing messages.
- */
- ast_tcptls_stream_set_exclusive_input(tcptls_session->stream_cookie, 0);
- ast_tcptls_stream_set_timeout_sequence(tcptls_session->stream_cookie, ast_tvnow(),
- tcptls_session->client ? -1 : (authtimeout * 1000));
- for (;;) {
- struct ast_str *str_save;
- if (!tcptls_session->client && req.authenticated && !authenticated) {
- authenticated = 1;
- ast_tcptls_stream_set_timeout_disable(tcptls_session->stream_cookie);
- ast_atomic_fetchadd_int(&unauth_sessions, -1);
- }
- /* calculate the timeout for unauthenticated server sessions */
- if (!tcptls_session->client && !authenticated ) {
- if ((timeout = sip_check_authtimeout(start)) < 0) {
- goto cleanup;
- }
- if (timeout == 0) {
- ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "TLS": "TCP");
- goto cleanup;
- }
- } else {
- timeout = -1;
- }
- if (ast_str_strlen(tcptls_session->overflow_buf) == 0) {
- res = ast_poll(fds, 2, timeout); /* polls for both socket and alert_pipe */
- if (res < 0) {
- ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "TLS": "TCP", res);
- goto cleanup;
- } else if (res == 0) {
- /* timeout */
- ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "TLS": "TCP");
- goto cleanup;
- }
- }
- /*
- * handle the socket event, check for both reads from the socket fd or TCP overflow buffer,
- * and writes from alert_pipe fd.
- */
- if (fds[0].revents || (ast_str_strlen(tcptls_session->overflow_buf) > 0)) { /* there is data on the socket to be read */
- fds[0].revents = 0;
- /* clear request structure */
- str_save = req.data;
- memset(&req, 0, sizeof(req));
- req.data = str_save;
- ast_str_reset(req.data);
- str_save = reqcpy.data;
- memset(&reqcpy, 0, sizeof(reqcpy));
- reqcpy.data = str_save;
- ast_str_reset(reqcpy.data);
- memset(buf, 0, sizeof(buf));
- if (tcptls_session->ssl) {
- set_socket_transport(&req.socket, AST_TRANSPORT_TLS);
- req.socket.port = htons(ourport_tls);
- } else {
- set_socket_transport(&req.socket, AST_TRANSPORT_TCP);
- req.socket.port = htons(ourport_tcp);
- }
- req.socket.fd = tcptls_session->fd;
- res = sip_tcptls_read(&req, tcptls_session, authenticated, start);
- if (res < 0) {
- goto cleanup;
- }
- req.socket.tcptls_session = tcptls_session;
- req.socket.ws_session = NULL;
- handle_request_do(&req, &tcptls_session->remote_address);
- }
- if (fds[1].revents) { /* alert_pipe indicates there is data in the send queue to be sent */
- enum sip_tcptls_alert alert;
- struct tcptls_packet *packet;
- fds[1].revents = 0;
- if (read(me->alert_pipe[0], &alert, sizeof(alert)) == -1) {
- ast_log(LOG_ERROR, "read() failed: %s\n", strerror(errno));
- continue;
- }
- switch (alert) {
- case TCPTLS_ALERT_STOP:
- goto cleanup;
- case TCPTLS_ALERT_DATA:
- ao2_lock(me);
- if (!(packet = AST_LIST_REMOVE_HEAD(&me->packet_q, entry))) {
- ast_log(LOG_WARNING, "TCPTLS thread alert_pipe indicated packet should be sent, but frame_q is empty\n");
- }
- ao2_unlock(me);
- if (packet) {
- if (ast_tcptls_server_write(tcptls_session, ast_str_buffer(packet->data), packet->len) == -1) {
- ast_log(LOG_WARNING, "Failure to write to tcp/tls socket\n");
- }
- ao2_t_ref(packet, -1, "tcptls packet sent, this is no longer needed");
- }
- break;
- default:
- ast_log(LOG_ERROR, "Unknown tcptls thread alert '%u'\n", alert);
- }
- }
- }
- ast_debug(2, "Shutting down thread for %s server\n", tcptls_session->ssl ? "TLS" : "TCP");
- cleanup:
- if (tcptls_session && !tcptls_session->client && !authenticated) {
- ast_atomic_fetchadd_int(&unauth_sessions, -1);
- }
- if (me) {
- ao2_t_unlink(threadt, me, "Removing tcptls helper thread, thread is closing");
- ao2_t_ref(me, -1, "Removing tcp_helper_threads threadinfo ref");
- }
- deinit_req(&reqcpy);
- deinit_req(&req);
- /* if client, we own the parent session arguments and must decrement ref */
- if (ca) {
- ao2_t_ref(ca, -1, "closing tcptls thread, getting rid of client tcptls_session arguments");
- }
- if (tcptls_session) {
- ao2_lock(tcptls_session);
- ast_tcptls_close_session_file(tcptls_session);
- tcptls_session->parent = NULL;
- ao2_unlock(tcptls_session);
- ao2_ref(tcptls_session, -1);
- tcptls_session = NULL;
- }
- return NULL;
- }
- static void peer_sched_cleanup(struct sip_peer *peer)
- {
- if (peer->pokeexpire != -1) {
- AST_SCHED_DEL_UNREF(sched, peer->pokeexpire,
- sip_unref_peer(peer, "removing poke peer ref"));
- }
- if (peer->expire != -1) {
- AST_SCHED_DEL_UNREF(sched, peer->expire,
- sip_unref_peer(peer, "remove register expire ref"));
- }
- if (peer->keepalivesend != -1) {
- AST_SCHED_DEL_UNREF(sched, peer->keepalivesend,
- sip_unref_peer(peer, "remove keepalive peer ref"));
- }
- }
- typedef enum {
- SIP_PEERS_MARKED,
- SIP_PEERS_ALL,
- } peer_unlink_flag_t;
- /* this func is used with ao2_callback to unlink/delete all marked or linked
- peers, depending on arg */
- static int match_and_cleanup_peer_sched(void *peerobj, void *arg, int flags)
- {
- struct sip_peer *peer = peerobj;
- peer_unlink_flag_t which = *(peer_unlink_flag_t *)arg;
- if (which == SIP_PEERS_ALL || peer->the_mark) {
- peer_sched_cleanup(peer);
- if (peer->dnsmgr) {
- ast_dnsmgr_release(peer->dnsmgr);
- peer->dnsmgr = NULL;
- sip_unref_peer(peer, "Release peer from dnsmgr");
- }
- return CMP_MATCH;
- }
- return 0;
- }
- static void unlink_peers_from_tables(peer_unlink_flag_t flag)
- {
- ao2_t_callback(peers, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
- match_and_cleanup_peer_sched, &flag, "initiating callback to remove marked peers");
- ao2_t_callback(peers_by_ip, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
- match_and_cleanup_peer_sched, &flag, "initiating callback to remove marked peers_by_ip");
- }
- /* \brief Unlink all marked peers from ao2 containers */
- static void unlink_marked_peers_from_tables(void)
- {
- unlink_peers_from_tables(SIP_PEERS_MARKED);
- }
- static void unlink_all_peers_from_tables(void)
- {
- unlink_peers_from_tables(SIP_PEERS_ALL);
- }
- /*! \brief maintain proper refcounts for a sip_pvt's outboundproxy
- *
- * This function sets pvt's outboundproxy pointer to the one referenced
- * by the proxy parameter. Because proxy may be a refcounted object, and
- * because pvt's old outboundproxy may also be a refcounted object, we need
- * to maintain the proper refcounts.
- *
- * \param pvt The sip_pvt for which we wish to set the outboundproxy
- * \param proxy The sip_proxy which we will point pvt towards.
- * \return Returns void
- */
- static void ref_proxy(struct sip_pvt *pvt, struct sip_proxy *proxy)
- {
- struct sip_proxy *old_obproxy = pvt->outboundproxy;
- /* The sip_cfg.outboundproxy is statically allocated, and so
- * we don't ever need to adjust refcounts for it
- */
- if (proxy && proxy != &sip_cfg.outboundproxy) {
- ao2_ref(proxy, +1);
- }
- pvt->outboundproxy = proxy;
- if (old_obproxy && old_obproxy != &sip_cfg.outboundproxy) {
- ao2_ref(old_obproxy, -1);
- }
- }
- /*!
- * \brief Unlink a dialog from the dialogs container, as well as any other places
- * that it may be currently stored.
- *
- * \note A reference to the dialog must be held before calling this function, and this
- * function does not release that reference.
- */
- void dialog_unlink_all(struct sip_pvt *dialog)
- {
- struct sip_pkt *cp;
- struct ast_channel *owner;
- dialog_ref(dialog, "Let's bump the count in the unlink so it doesn't accidentally become dead before we are done");
- ao2_t_unlink(dialogs, dialog, "unlinking dialog via ao2_unlink");
- ao2_t_unlink(dialogs_needdestroy, dialog, "unlinking dialog_needdestroy via ao2_unlink");
- ao2_t_unlink(dialogs_rtpcheck, dialog, "unlinking dialog_rtpcheck via ao2_unlink");
- /* Unlink us from the owner (channel) if we have one */
- owner = sip_pvt_lock_full(dialog);
- if (owner) {
- ast_debug(1, "Detaching from channel %s\n", ast_channel_name(owner));
- ast_channel_tech_pvt_set(owner, dialog_unref(ast_channel_tech_pvt(owner), "resetting channel dialog ptr in unlink_all"));
- ast_channel_unlock(owner);
- ast_channel_unref(owner);
- sip_set_owner(dialog, NULL);
- }
- sip_pvt_unlock(dialog);
- if (dialog->registry) {
- if (dialog->registry->call == dialog) {
- dialog->registry->call = dialog_unref(dialog->registry->call, "nulling out the registry's call dialog field in unlink_all");
- }
- ao2_t_replace(dialog->registry, NULL, "delete dialog->registry");
- }
- if (dialog->stateid != -1) {
- ast_extension_state_del(dialog->stateid, cb_extensionstate);
- dialog->stateid = -1;
- }
- /* Remove link from peer to subscription of MWI */
- if (dialog->relatedpeer && dialog->relatedpeer->mwipvt == dialog) {
- dialog->relatedpeer->mwipvt = dialog_unref(dialog->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt");
- }
- if (dialog->relatedpeer && dialog->relatedpeer->call == dialog) {
- dialog->relatedpeer->call = dialog_unref(dialog->relatedpeer->call, "unset the relatedpeer->call field in tandem with relatedpeer field itself");
- }
- /* remove all current packets in this dialog */
- while((cp = dialog->packets)) {
- dialog->packets = dialog->packets->next;
- AST_SCHED_DEL(sched, cp->retransid);
- dialog_unref(cp->owner, "remove all current packets in this dialog, and the pointer to the dialog too as part of __sip_destroy");
- if (cp->data) {
- ast_free(cp->data);
- }
- ast_free(cp);
- }
- AST_SCHED_DEL_UNREF(sched, dialog->waitid, dialog_unref(dialog, "when you delete the waitid sched, you should dec the refcount for the stored dialog ptr"));
- AST_SCHED_DEL_UNREF(sched, dialog->initid, dialog_unref(dialog, "when you delete the initid sched, you should dec the refcount for the stored dialog ptr"));
- if (dialog->reinviteid > -1) {
- AST_SCHED_DEL_UNREF(sched, dialog->reinviteid, dialog_unref(dialog, "clear ref for reinvite_timeout"));
- }
- if (dialog->autokillid > -1) {
- AST_SCHED_DEL_UNREF(sched, dialog->autokillid, dialog_unref(dialog, "when you delete the autokillid sched, you should dec the refcount for the stored dialog ptr"));
- }
- if (dialog->request_queue_sched_id > -1) {
- AST_SCHED_DEL_UNREF(sched, dialog->request_queue_sched_id, dialog_unref(dialog, "when you delete the request_queue_sched_id sched, you should dec the refcount for the stored dialog ptr"));
- }
- AST_SCHED_DEL_UNREF(sched, dialog->provisional_keepalive_sched_id, dialog_unref(dialog, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
- if (dialog->t38id > -1) {
- AST_SCHED_DEL_UNREF(sched, dialog->t38id, dialog_unref(dialog, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
- }
- if (dialog->stimer) {
- stop_session_timer(dialog);
- }
- dialog_unref(dialog, "Let's unbump the count in the unlink so the poor pvt can disappear if it is time");
- }
- static void append_history_full(struct sip_pvt *p, const char *fmt, ...)
- __attribute__((format(printf, 2, 3)));
- /*! \brief Convert transfer status to string */
- static const char *referstatus2str(enum referstatus rstatus)
- {
- return map_x_s(referstatusstrings, rstatus, "");
- }
- static inline void pvt_set_needdestroy(struct sip_pvt *pvt, const char *reason)
- {
- if (pvt->final_destruction_scheduled) {
- return; /* This is already scheduled for final destruction, let the scheduler take care of it. */
- }
- append_history(pvt, "NeedDestroy", "Setting needdestroy because %s", reason);
- if (!pvt->needdestroy) {
- pvt->needdestroy = 1;
- ao2_t_link(dialogs_needdestroy, pvt, "link pvt into dialogs_needdestroy container");
- }
- }
- /*! \brief Initialize the initital request packet in the pvt structure.
- This packet is used for creating replies and future requests in
- a dialog */
- static void initialize_initreq(struct sip_pvt *p, struct sip_request *req)
- {
- if (p->initreq.headers) {
- ast_debug(1, "Initializing already initialized SIP dialog %s (presumably reinvite)\n", p->callid);
- } else {
- ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
- }
- /* Use this as the basis */
- copy_request(&p->initreq, req);
- parse_request(&p->initreq);
- if (req->debug) {
- ast_verbose("Initreq: %d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
- }
- }
- /*! \brief Encapsulate setting of SIP_ALREADYGONE to be able to trace it with debugging */
- static void sip_alreadygone(struct sip_pvt *dialog)
- {
- ast_debug(3, "Setting SIP_ALREADYGONE on dialog %s\n", dialog->callid);
- dialog->alreadygone = 1;
- }
- /*! Resolve DNS srv name or host name in a sip_proxy structure */
- static int proxy_update(struct sip_proxy *proxy)
- {
- /* if it's actually an IP address and not a name,
- there's no need for a managed lookup */
- if (!ast_sockaddr_parse(&proxy->ip, proxy->name, 0)) {
- /* Ok, not an IP address, then let's check if it's a domain or host */
- /* XXX Todo - if we have proxy port, don't do SRV */
- proxy->ip.ss.ss_family = get_address_family_filter(AST_TRANSPORT_UDP); /* Filter address family */
- if (ast_get_ip_or_srv(&proxy->ip, proxy->name, sip_cfg.srvlookup ? "_sip._udp" : NULL) < 0) {
- ast_log(LOG_WARNING, "Unable to locate host '%s'\n", proxy->name);
- return FALSE;
- }
- }
- ast_sockaddr_set_port(&proxy->ip, proxy->port);
- proxy->last_dnsupdate = time(NULL);
- return TRUE;
- }
- /*! \brief Parse proxy string and return an ao2_alloc'd proxy. If dest is
- * non-NULL, no allocation is performed and dest is used instead.
- * On error NULL is returned. */
- static struct sip_proxy *proxy_from_config(const char *proxy, int sipconf_lineno, struct sip_proxy *dest)
- {
- char *mutable_proxy, *sep, *name;
- int allocated = 0;
- if (!dest) {
- dest = ao2_alloc(sizeof(struct sip_proxy), NULL);
- if (!dest) {
- ast_log(LOG_WARNING, "Unable to allocate config storage for proxy\n");
- return NULL;
- }
- allocated = 1;
- }
- /* Format is: [transport://]name[:port][,force] */
- mutable_proxy = ast_skip_blanks(ast_strdupa(proxy));
- sep = strchr(mutable_proxy, ',');
- if (sep) {
- *sep++ = '\0';
- dest->force = !strncasecmp(ast_skip_blanks(sep), "force", 5);
- } else {
- dest->force = FALSE;
- }
- sip_parse_host(mutable_proxy, sipconf_lineno, &name, &dest->port, &dest->transport);
- /* Check that there is a name at all */
- if (ast_strlen_zero(name)) {
- if (allocated) {
- ao2_ref(dest, -1);
- } else {
- dest->name[0] = '\0';
- }
- return NULL;
- }
- ast_copy_string(dest->name, name, sizeof(dest->name));
- /* Resolve host immediately */
- proxy_update(dest);
- return dest;
- }
- /*! \brief converts ascii port to int representation. If no
- * pt buffer is provided or the pt has errors when being converted
- * to an int value, the port provided as the standard is used.
- */
- unsigned int port_str2int(const char *pt, unsigned int standard)
- {
- int port = standard;
- if (ast_strlen_zero(pt) || (sscanf(pt, "%30d", &port) != 1) || (port < 1) || (port > 65535)) {
- port = standard;
- }
- return port;
- }
- /*! \brief Get default outbound proxy or global proxy */
- static struct sip_proxy *obproxy_get(struct sip_pvt *dialog, struct sip_peer *peer)
- {
- if (dialog && dialog->options && dialog->options->outboundproxy) {
- if (sipdebug) {
- ast_debug(1, "OBPROXY: Applying dialplan set OBproxy to this call\n");
- }
- append_history(dialog, "OBproxy", "Using dialplan obproxy %s", dialog->options->outboundproxy->name);
- return dialog->options->outboundproxy;
- }
- if (peer && peer->outboundproxy) {
- if (sipdebug) {
- ast_debug(1, "OBPROXY: Applying peer OBproxy to this call\n");
- }
- append_history(dialog, "OBproxy", "Using peer obproxy %s", peer->outboundproxy->name);
- return peer->outboundproxy;
- }
- if (sip_cfg.outboundproxy.name[0]) {
- if (sipdebug) {
- ast_debug(1, "OBPROXY: Applying global OBproxy to this call\n");
- }
- append_history(dialog, "OBproxy", "Using global obproxy %s", sip_cfg.outboundproxy.name);
- return &sip_cfg.outboundproxy;
- }
- if (sipdebug) {
- ast_debug(1, "OBPROXY: Not applying OBproxy to this call\n");
- }
- return NULL;
- }
- /*! \brief returns true if 'name' (with optional trailing whitespace)
- * matches the sip method 'id'.
- * Strictly speaking, SIP methods are case SENSITIVE, but we do
- * a case-insensitive comparison to be more tolerant.
- * following Jon Postel's rule: Be gentle in what you accept, strict with what you send
- */
- static int method_match(enum sipmethod id, const char *name)
- {
- int len = strlen(sip_methods[id].text);
- int l_name = name ? strlen(name) : 0;
- /* true if the string is long enough, and ends with whitespace, and matches */
- return (l_name >= len && name && name[len] < 33 &&
- !strncasecmp(sip_methods[id].text, name, len));
- }
- /*! \brief find_sip_method: Find SIP method from header */
- static int find_sip_method(const char *msg)
- {
- int i, res = 0;
- if (ast_strlen_zero(msg)) {
- return 0;
- }
- for (i = 1; i < ARRAY_LEN(sip_methods) && !res; i++) {
- if (method_match(i, msg)) {
- res = sip_methods[i].id;
- }
- }
- return res;
- }
- /*! \brief See if we pass debug IP filter */
- static inline int sip_debug_test_addr(const struct ast_sockaddr *addr)
- {
- /* Can't debug if sipdebug is not enabled */
- if (!sipdebug) {
- return 0;
- }
- /* A null debug_addr means we'll debug any address */
- if (ast_sockaddr_isnull(&debugaddr)) {
- return 1;
- }
- /* If no port was specified for a debug address, just compare the
- * addresses, otherwise compare the address and port
- */
- if (ast_sockaddr_port(&debugaddr)) {
- return !ast_sockaddr_cmp(&debugaddr, addr);
- } else {
- return !ast_sockaddr_cmp_addr(&debugaddr, addr);
- }
- }
- /*! \brief The real destination address for a write */
- static const struct ast_sockaddr *sip_real_dst(const struct sip_pvt *p)
- {
- if (p->outboundproxy) {
- return &p->outboundproxy->ip;
- }
- return ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT) || ast_test_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT) ? &p->recv : &p->sa;
- }
- /*! \brief Display SIP nat mode */
- static const char *sip_nat_mode(const struct sip_pvt *p)
- {
- return ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT) ? "NAT" : "no NAT";
- }
- /*! \brief Test PVT for debugging output */
- static inline int sip_debug_test_pvt(struct sip_pvt *p)
- {
- if (!sipdebug) {
- return 0;
- }
- return sip_debug_test_addr(sip_real_dst(p));
- }
- /*! \brief Return int representing a bit field of transport types found in const char *transport */
- static int get_transport_str2enum(const char *transport)
- {
- int res = 0;
- if (ast_strlen_zero(transport)) {
- return res;
- }
- if (!strcasecmp(transport, "udp")) {
- res |= AST_TRANSPORT_UDP;
- }
- if (!strcasecmp(transport, "tcp")) {
- res |= AST_TRANSPORT_TCP;
- }
- if (!strcasecmp(transport, "tls")) {
- res |= AST_TRANSPORT_TLS;
- }
- if (!strcasecmp(transport, "ws")) {
- res |= AST_TRANSPORT_WS;
- }
- if (!strcasecmp(transport, "wss")) {
- res |= AST_TRANSPORT_WSS;
- }
- return res;
- }
- /*! \brief Return configuration of transports for a device */
- static inline const char *get_transport_list(unsigned int transports)
- {
- char *buf;
- if (!transports) {
- return "UNKNOWN";
- }
- if (!(buf = ast_threadstorage_get(&sip_transport_str_buf, SIP_TRANSPORT_STR_BUFSIZE))) {
- return "";
- }
- memset(buf, 0, SIP_TRANSPORT_STR_BUFSIZE);
- if (transports & AST_TRANSPORT_UDP) {
- strncat(buf, "UDP,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
- }
- if (transports & AST_TRANSPORT_TCP) {
- strncat(buf, "TCP,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
- }
- if (transports & AST_TRANSPORT_TLS) {
- strncat(buf, "TLS,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
- }
- if (transports & AST_TRANSPORT_WS) {
- strncat(buf, "WS,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
- }
- if (transports & AST_TRANSPORT_WSS) {
- strncat(buf, "WSS,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
- }
- /* Remove the trailing ',' if present */
- if (strlen(buf)) {
- buf[strlen(buf) - 1] = 0;
- }
- return buf;
- }
- /*! \brief Return transport as string */
- const char *sip_get_transport(enum ast_transport t)
- {
- switch (t) {
- case AST_TRANSPORT_UDP:
- return "UDP";
- case AST_TRANSPORT_TCP:
- return "TCP";
- case AST_TRANSPORT_TLS:
- return "TLS";
- case AST_TRANSPORT_WS:
- case AST_TRANSPORT_WSS:
- return "WS";
- }
- return "UNKNOWN";
- }
- /*! \brief Return protocol string for srv dns query */
- static inline const char *get_srv_protocol(enum ast_transport t)
- {
- switch (t) {
- case AST_TRANSPORT_UDP:
- return "udp";
- case AST_TRANSPORT_WS:
- return "ws";
- case AST_TRANSPORT_TLS:
- case AST_TRANSPORT_TCP:
- return "tcp";
- case AST_TRANSPORT_WSS:
- return "wss";
- }
- return "udp";
- }
- /*! \brief Return service string for srv dns query */
- static inline const char *get_srv_service(enum ast_transport t)
- {
- switch (t) {
- case AST_TRANSPORT_TCP:
- case AST_TRANSPORT_UDP:
- case AST_TRANSPORT_WS:
- return "sip";
- case AST_TRANSPORT_TLS:
- case AST_TRANSPORT_WSS:
- return "sips";
- }
- return "sip";
- }
- /*! \brief Return transport of dialog.
- \note this is based on a false assumption. We don't always use the
- outbound proxy for all requests in a dialog. It depends on the
- "force" parameter. The FIRST request is always sent to the ob proxy.
- \todo Fix this function to work correctly
- */
- static inline const char *get_transport_pvt(struct sip_pvt *p)
- {
- if (p->outboundproxy && p->outboundproxy->transport) {
- set_socket_transport(&p->socket, p->outboundproxy->transport);
- }
- return sip_get_transport(p->socket.type);
- }
- /*!
- * \internal
- * \brief Transmit SIP message
- *
- * \details
- * Sends a SIP request or response on a given socket (in the pvt)
- * \note
- * Called by retrans_pkt, send_request, send_response and __sip_reliable_xmit
- *
- * \return length of transmitted message, XMIT_ERROR on known network failures -1 on other failures.
- */
- static int __sip_xmit(struct sip_pvt *p, struct ast_str *data)
- {
- int res = 0;
- const struct ast_sockaddr *dst = sip_real_dst(p);
- ast_debug(2, "Trying to put '%.11s' onto %s socket destined for %s\n", ast_str_buffer(data), get_transport_pvt(p), ast_sockaddr_stringify(dst));
- if (sip_prepare_socket(p) < 0) {
- return XMIT_ERROR;
- }
- if (p->socket.type == AST_TRANSPORT_UDP) {
- res = ast_sendto(p->socket.fd, ast_str_buffer(data), ast_str_strlen(data), 0, dst);
- } else if (p->socket.tcptls_session) {
- res = sip_tcptls_write(p->socket.tcptls_session, ast_str_buffer(data), ast_str_strlen(data));
- } else if (p->socket.ws_session) {
- if (!(res = ast_websocket_write(p->socket.ws_session, AST_WEBSOCKET_OPCODE_TEXT, ast_str_buffer(data), ast_str_strlen(data)))) {
- /* The WebSocket API just returns 0 on success and -1 on failure, while this code expects the payload length to be returned */
- res = ast_str_strlen(data);
- }
- } else {
- ast_debug(2, "Socket type is TCP but no tcptls_session is present to write to\n");
- return XMIT_ERROR;
- }
- if (res == -1) {
- switch (errno) {
- case EBADF: /* Bad file descriptor - seems like this is generated when the host exist, but doesn't accept the UDP packet */
- case EHOSTUNREACH: /* Host can't be reached */
- case ENETDOWN: /* Interface down */
- case ENETUNREACH: /* Network failure */
- case ECONNREFUSED: /* ICMP port unreachable */
- res = XMIT_ERROR; /* Don't bother with trying to transmit again */
- }
- }
- if (res != ast_str_strlen(data)) {
- ast_log(LOG_WARNING, "sip_xmit of %p (len %zu) to %s returned %d: %s\n", data, ast_str_strlen(data), ast_sockaddr_stringify(dst), res, strerror(errno));
- }
- return res;
- }
- /*! \brief Build a Via header for a request */
- static void build_via(struct sip_pvt *p)
- {
- /* Work around buggy UNIDEN UIP200 firmware */
- const char *rport = (ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT) || ast_test_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT)) ? ";rport" : "";
- /* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */
- snprintf(p->via, sizeof(p->via), "SIP/2.0/%s %s;branch=z9hG4bK%08x%s",
- get_transport_pvt(p),
- ast_sockaddr_stringify_remote(&p->ourip),
- (unsigned)p->branch, rport);
- }
- /*! \brief NAT fix - decide which IP address to use for Asterisk server?
- *
- * Using the localaddr structure built up with localnet statements in sip.conf
- * apply it to their address to see if we need to substitute our
- * externaddr or can get away with our internal bindaddr
- * 'us' is always overwritten.
- */
- static void ast_sip_ouraddrfor(const struct ast_sockaddr *them, struct ast_sockaddr *us, struct sip_pvt *p)
- {
- struct ast_sockaddr theirs;
- /* Set want_remap to non-zero if we want to remap 'us' to an externally
- * reachable IP address and port. This is done if:
- * 1. we have a localaddr list (containing 'internal' addresses marked
- * as 'deny', so ast_apply_ha() will return AST_SENSE_DENY on them,
- * and AST_SENSE_ALLOW on 'external' ones);
- * 2. externaddr is set, so we know what to use as the
- * externally visible address;
- * 3. the remote address, 'them', is external;
- * 4. the address returned by ast_ouraddrfor() is 'internal' (AST_SENSE_DENY
- * when passed to ast_apply_ha() so it does need to be remapped.
- * This fourth condition is checked later.
- */
- int want_remap = 0;
- ast_sockaddr_copy(us, &internip); /* starting guess for the internal address */
- /* now ask the system what would it use to talk to 'them' */
- ast_ouraddrfor(them, us);
- ast_sockaddr_copy(&theirs, them);
- if (ast_sockaddr_is_ipv6(&theirs) && !ast_sockaddr_is_ipv4_mapped(&theirs)) {
- if (localaddr && !ast_sockaddr_isnull(&externaddr) && !ast_sockaddr_is_any(&bindaddr)) {
- ast_log(LOG_WARNING, "Address remapping activated in sip.conf "
- "but we're using IPv6, which doesn't need it. Please "
- "remove \"localnet\" and/or \"externaddr\" settings.\n");
- }
- } else {
- want_remap = localaddr &&
- !ast_sockaddr_isnull(&externaddr) &&
- ast_apply_ha(localaddr, &theirs) == AST_SENSE_ALLOW ;
- }
- if (want_remap &&
- (!sip_cfg.matchexternaddrlocally || !ast_apply_ha(localaddr, us)) ) {
- /* if we used externhost, see if it is time to refresh the info */
- if (externexpire && time(NULL) >= externexpire) {
- if (ast_sockaddr_resolve_first(&externaddr, externhost, 0)) {
- ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost);
- }
- externexpire = time(NULL) + externrefresh;
- }
- if (!ast_sockaddr_isnull(&externaddr)) {
- ast_sockaddr_copy(us, &externaddr);
- switch (p->socket.type) {
- case AST_TRANSPORT_TCP:
- if (!externtcpport && ast_sockaddr_port(&externaddr)) {
- /* for consistency, default to the externaddr port */
- externtcpport = ast_sockaddr_port(&externaddr);
- }
- ast_sockaddr_set_port(us, externtcpport);
- break;
- case AST_TRANSPORT_TLS:
- ast_sockaddr_set_port(us, externtlsport);
- break;
- case AST_TRANSPORT_UDP:
- if (!ast_sockaddr_port(&externaddr)) {
- ast_sockaddr_set_port(us, ast_sockaddr_port(&bindaddr));
- }
- break;
- default:
- break;
- }
- }
- ast_debug(1, "Target address %s is not local, substituting externaddr\n",
- ast_sockaddr_stringify(them));
- } else {
- /* no remapping, but we bind to a specific address, so use it. */
- switch (p->socket.type) {
- case AST_TRANSPORT_TCP:
- if (!ast_sockaddr_is_any(&sip_tcp_desc.local_address)) {
- ast_sockaddr_copy(us,
- &sip_tcp_desc.local_address);
- } else {
- ast_sockaddr_set_port(us,
- ast_sockaddr_port(&sip_tcp_desc.local_address));
- }
- break;
- case AST_TRANSPORT_TLS:
- if (!ast_sockaddr_is_any(&sip_tls_desc.local_address)) {
- ast_sockaddr_copy(us,
- &sip_tls_desc.local_address);
- } else {
- ast_sockaddr_set_port(us,
- ast_sockaddr_port(&sip_tls_desc.local_address));
- }
- break;
- case AST_TRANSPORT_UDP:
- /* fall through on purpose */
- default:
- if (!ast_sockaddr_is_any(&bindaddr)) {
- ast_sockaddr_copy(us, &bindaddr);
- }
- if (!ast_sockaddr_port(us)) {
- ast_sockaddr_set_port(us, ast_sockaddr_port(&bindaddr));
- }
- }
- }
- ast_debug(3, "Setting AST_TRANSPORT_%s with address %s\n", sip_get_transport(p->socket.type), ast_sockaddr_stringify(us));
- }
- /*! \brief Append to SIP dialog history with arg list */
- static __attribute__((format(printf, 2, 0))) void append_history_va(struct sip_pvt *p, const char *fmt, va_list ap)
- {
- char buf[80], *c = buf; /* max history length */
- struct sip_history *hist;
- int l;
- vsnprintf(buf, sizeof(buf), fmt, ap);
- strsep(&c, "\r\n"); /* Trim up everything after \r or \n */
- l = strlen(buf) + 1;
- if (!(hist = ast_calloc(1, sizeof(*hist) + l))) {
- return;
- }
- if (!p->history && !(p->history = ast_calloc(1, sizeof(*p->history)))) {
- ast_free(hist);
- return;
- }
- memcpy(hist->event, buf, l);
- if (p->history_entries == MAX_HISTORY_ENTRIES) {
- struct sip_history *oldest;
- oldest = AST_LIST_REMOVE_HEAD(p->history, list);
- p->history_entries--;
- ast_free(oldest);
- }
- AST_LIST_INSERT_TAIL(p->history, hist, list);
- p->history_entries++;
- }
- /*! \brief Append to SIP dialog history with arg list */
- static void append_history_full(struct sip_pvt *p, const char *fmt, ...)
- {
- va_list ap;
- if (!p) {
- return;
- }
- if (!p->do_history && !recordhistory && !dumphistory) {
- return;
- }
- va_start(ap, fmt);
- append_history_va(p, fmt, ap);
- va_end(ap);
- return;
- }
- /*! \brief Retransmit SIP message if no answer (Called from scheduler) */
- static int retrans_pkt(const void *data)
- {
- struct sip_pkt *pkt = (struct sip_pkt *)data, *prev, *cur = NULL;
- int reschedule = DEFAULT_RETRANS;
- int xmitres = 0;
- /* how many ms until retrans timeout is reached */
- int64_t diff = pkt->retrans_stop_time - ast_tvdiff_ms(ast_tvnow(), pkt->time_sent);
- /* Do not retransmit if time out is reached. This will be negative if the time between
- * the first transmission and now is larger than our timeout period. This is a fail safe
- * check in case the scheduler gets behind or the clock is changed. */
- if ((diff <= 0) || (diff > pkt->retrans_stop_time)) {
- pkt->retrans_stop = 1;
- }
- /* Lock channel PVT */
- sip_pvt_lock(pkt->owner);
- if (!pkt->retrans_stop) {
- pkt->retrans++;
- if (!pkt->timer_t1) { /* Re-schedule using timer_a and timer_t1 */
- if (sipdebug) {
- ast_debug(4, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n",
- pkt->retransid,
- sip_methods[pkt->method].text,
- pkt->method);
- }
- } else {
- int siptimer_a;
- if (sipdebug) {
- ast_debug(4, "SIP TIMER: Rescheduling retransmission #%d (%d) %s - %d\n",
- pkt->retransid,
- pkt->retrans,
- sip_methods[pkt->method].text,
- pkt->method);
- }
- if (!pkt->timer_a) {
- pkt->timer_a = 2 ;
- } else {
- pkt->timer_a = 2 * pkt->timer_a;
- }
- /* For non-invites, a maximum of 4 secs */
- siptimer_a = pkt->timer_t1 * pkt->timer_a; /* Double each time */
- if (pkt->method != SIP_INVITE && siptimer_a > 4000) {
- siptimer_a = 4000;
- }
- /* Reschedule re-transmit */
- reschedule = siptimer_a;
- ast_debug(4, "** SIP timers: Rescheduling retransmission %d to %d ms (t1 %d ms (Retrans id #%d)) \n",
- pkt->retrans + 1,
- siptimer_a,
- pkt->timer_t1,
- pkt->retransid);
- }
- if (sip_debug_test_pvt(pkt->owner)) {
- const struct ast_sockaddr *dst = sip_real_dst(pkt->owner);
- ast_verbose("Retransmitting #%d (%s) to %s:\n%s\n---\n",
- pkt->retrans, sip_nat_mode(pkt->owner),
- ast_sockaddr_stringify(dst),
- ast_str_buffer(pkt->data));
- }
- append_history(pkt->owner, "ReTx", "%d %s", reschedule, ast_str_buffer(pkt->data));
- xmitres = __sip_xmit(pkt->owner, pkt->data);
- /* If there was no error during the network transmission, schedule the next retransmission,
- * but if the next retransmission is going to be beyond our timeout period, mark the packet's
- * stop_retrans value and set the next retransmit to be the exact time of timeout. This will
- * allow any responses to the packet to be processed before the packet is destroyed on the next
- * call to this function by the scheduler. */
- if (xmitres != XMIT_ERROR) {
- if (reschedule >= diff) {
- pkt->retrans_stop = 1;
- reschedule = diff;
- }
- sip_pvt_unlock(pkt->owner);
- return reschedule;
- }
- }
- /* At this point, either the packet's retransmission timed out, or there was a
- * transmission error, either way destroy the scheduler item and this packet. */
- pkt->retransid = -1; /* Kill this scheduler item */
- if (pkt->method != SIP_OPTIONS && xmitres == 0) {
- if (pkt->is_fatal || sipdebug) { /* Tell us if it's critical or if we're debugging */
- ast_log(LOG_WARNING, "Retransmission timeout reached on transmission %s for seqno %u (%s %s) -- See https://wiki.asterisk.org/wiki/display/AST/SIP+Retransmissions\n"
- "Packet timed out after %dms with no response\n",
- pkt->owner->callid,
- pkt->seqno,
- pkt->is_fatal ? "Critical" : "Non-critical",
- pkt->is_resp ? "Response" : "Request",
- (int) ast_tvdiff_ms(ast_tvnow(), pkt->time_sent));
- }
- } else if (pkt->method == SIP_OPTIONS && sipdebug) {
- ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) -- See https://wiki.asterisk.org/wiki/display/AST/SIP+Retransmissions\n", pkt->owner->callid);
- }
- if (xmitres == XMIT_ERROR) {
- ast_log(LOG_WARNING, "Transmit error :: Cancelling transmission on Call ID %s\n", pkt->owner->callid);
- append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
- } else {
- append_history(pkt->owner, "MaxRetries", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
- }
- if (pkt->is_fatal) {
- while(pkt->owner->owner && ast_channel_trylock(pkt->owner->owner)) {
- sip_pvt_unlock(pkt->owner); /* SIP_PVT, not channel */
- usleep(1);
- sip_pvt_lock(pkt->owner);
- }
- if (pkt->owner->owner && !ast_channel_hangupcause(pkt->owner->owner)) {
- ast_channel_hangupcause_set(pkt->owner->owner, AST_CAUSE_NO_USER_RESPONSE);
- }
- if (pkt->owner->owner) {
- ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet (see https://wiki.asterisk.org/wiki/display/AST/SIP+Retransmissions).\n", pkt->owner->callid);
- if (pkt->is_resp &&
- (pkt->response_code >= 200) &&
- (pkt->response_code < 300) &&
- pkt->owner->pendinginvite &&
- ast_test_flag(&pkt->owner->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) {
- /* This is a timeout of the 2XX response to a pending INVITE. In this case terminate the INVITE
- * transaction just as if we received the ACK, but immediately hangup with a BYE (sip_hangup
- * will send the BYE as long as the dialog is not set as "alreadygone")
- * RFC 3261 section 13.3.1.4.
- * "If the server retransmits the 2xx response for 64*T1 seconds without receiving
- * an ACK, the dialog is confirmed, but the session SHOULD be terminated. This is
- * accomplished with a BYE, as described in Section 15." */
- pkt->owner->invitestate = INV_TERMINATED;
- pkt->owner->pendinginvite = 0;
- } else {
- /* there is nothing left to do, mark the dialog as gone */
- sip_alreadygone(pkt->owner);
- }
- ast_queue_hangup_with_cause(pkt->owner->owner, AST_CAUSE_NO_USER_RESPONSE);
- ast_channel_unlock(pkt->owner->owner);
- } else {
- /* If no channel owner, destroy now */
- /* Let the peerpoke system expire packets when the timer expires for poke_noanswer */
- if (pkt->method != SIP_OPTIONS && pkt->method != SIP_REGISTER) {
- pvt_set_needdestroy(pkt->owner, "no response to critical packet");
- sip_alreadygone(pkt->owner);
- append_history(pkt->owner, "DialogKill", "Killing this failed dialog immediately");
- }
- }
- } else if (pkt->owner->pendinginvite == pkt->seqno) {
- ast_log(LOG_WARNING, "Timeout on %s on non-critical invite transaction.\n", pkt->owner->callid);
- pkt->owner->invitestate = INV_TERMINATED;
- pkt->owner->pendinginvite = 0;
- check_pendings(pkt->owner);
- }
- if (pkt->method == SIP_BYE) {
- /* We're not getting answers on SIP BYE's. Tear down the call anyway. */
- sip_alreadygone(pkt->owner);
- if (pkt->owner->owner) {
- ast_channel_unlock(pkt->owner->owner);
- }
- append_history(pkt->owner, "ByeFailure", "Remote peer doesn't respond to bye. Destroying call anyway.");
- pvt_set_needdestroy(pkt->owner, "no response to BYE");
- }
- /* Remove the packet */
- for (prev = NULL, cur = pkt->owner->packets; cur; prev = cur, cur = cur->next) {
- if (cur == pkt) {
- UNLINK(cur, pkt->owner->packets, prev);
- sip_pvt_unlock(pkt->owner);
- if (pkt->owner) {
- pkt->owner = dialog_unref(pkt->owner,"pkt is being freed, its dialog ref is dead now");
- }
- if (pkt->data) {
- ast_free(pkt->data);
- }
- pkt->data = NULL;
- ast_free(pkt);
- return 0;
- }
- }
- /* error case */
- ast_log(LOG_WARNING, "Weird, couldn't find packet owner!\n");
- sip_pvt_unlock(pkt->owner);
- return 0;
- }
- /*!
- * \internal
- * \brief Transmit packet with retransmits
- * \return 0 on success, -1 on failure to allocate packet
- */
- static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, uint32_t seqno, int resp, struct ast_str *data, int fatal, int sipmethod)
- {
- struct sip_pkt *pkt = NULL;
- int siptimer_a = DEFAULT_RETRANS;
- int xmitres = 0;
- unsigned respid;
- if (sipmethod == SIP_INVITE) {
- /* Note this is a pending invite */
- p->pendinginvite = seqno;
- }
- /* If the transport is something reliable (TCP or TLS) then don't really send this reliably */
- /* I removed the code from retrans_pkt that does the same thing so it doesn't get loaded into the scheduler */
- /*! \todo According to the RFC some packets need to be retransmitted even if its TCP, so this needs to get revisited */
- if (!(p->socket.type & AST_TRANSPORT_UDP)) {
- xmitres = __sip_xmit(p, data); /* Send packet */
- if (xmitres == XMIT_ERROR) { /* Serious network trouble, no need to try again */
- append_history(p, "XmitErr", "%s", fatal ? "(Critical)" : "(Non-critical)");
- return AST_FAILURE;
- } else {
- return AST_SUCCESS;
- }
- }
- if (!(pkt = ast_calloc(1, sizeof(*pkt)))) {
- return AST_FAILURE;
- }
- /* copy data, add a terminator and save length */
- if (!(pkt->data = ast_str_create(ast_str_strlen(data)))) {
- ast_free(pkt);
- return AST_FAILURE;
- }
- ast_str_set(&pkt->data, 0, "%s%s", ast_str_buffer(data), "\0");
- /* copy other parameters from the caller */
- pkt->method = sipmethod;
- pkt->seqno = seqno;
- pkt->is_resp = resp;
- pkt->is_fatal = fatal;
- pkt->owner = dialog_ref(p, "__sip_reliable_xmit: setting pkt->owner");
- pkt->next = p->packets;
- p->packets = pkt; /* Add it to the queue */
- if (resp) {
- /* Parse out the response code */
- if (sscanf(ast_str_buffer(pkt->data), "SIP/2.0 %30u", &respid) == 1) {
- pkt->response_code = respid;
- }
- }
- pkt->timer_t1 = p->timer_t1; /* Set SIP timer T1 */
- pkt->retransid = -1;
- if (pkt->timer_t1) {
- siptimer_a = pkt->timer_t1;
- }
- pkt->time_sent = ast_tvnow(); /* time packet was sent */
- pkt->retrans_stop_time = 64 * (pkt->timer_t1 ? pkt->timer_t1 : DEFAULT_TIMER_T1); /* time in ms after pkt->time_sent to stop retransmission */
- /* Schedule retransmission */
- AST_SCHED_REPLACE_VARIABLE(pkt->retransid, sched, siptimer_a, retrans_pkt, pkt, 1);
- if (sipdebug) {
- ast_debug(4, "*** SIP TIMER: Initializing retransmit timer on packet: Id #%d\n", pkt->retransid);
- }
- xmitres = __sip_xmit(pkt->owner, pkt->data); /* Send packet */
- if (xmitres == XMIT_ERROR) { /* Serious network trouble, no need to try again */
- append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
- ast_log(LOG_ERROR, "Serious Network Trouble; __sip_xmit returns error for pkt data\n");
- AST_SCHED_DEL(sched, pkt->retransid);
- p->packets = pkt->next;
- pkt->owner = dialog_unref(pkt->owner,"pkt is being freed, its dialog ref is dead now");
- ast_free(pkt->data);
- ast_free(pkt);
- return AST_FAILURE;
- } else {
- /* This is odd, but since the retrans timer starts at 500ms and the do_monitor thread
- * only wakes up every 1000ms by default, we have to poke the thread here to make
- * sure it successfully detects this must be retransmitted in less time than
- * it usually sleeps for. Otherwise it might not retransmit this packet for 1000ms. */
- if (monitor_thread != AST_PTHREADT_NULL) {
- pthread_kill(monitor_thread, SIGURG);
- }
- return AST_SUCCESS;
- }
- }
- /*! \brief Kill a SIP dialog (called only by the scheduler)
- * The scheduler has a reference to this dialog when p->autokillid != -1,
- * and we are called using that reference. So if the event is not
- * rescheduled, we need to call dialog_unref().
- */
- static int __sip_autodestruct(const void *data)
- {
- struct sip_pvt *p = (struct sip_pvt *)data;
- struct ast_channel *owner;
- /* If this is a subscription, tell the phone that we got a timeout */
- if (p->subscribed && p->subscribed != MWI_NOTIFICATION && p->subscribed != CALL_COMPLETION) {
- struct state_notify_data data = { 0, };
- data.state = AST_EXTENSION_DEACTIVATED;
- transmit_state_notify(p, &data, 1, TRUE); /* Send last notification */
- p->subscribed = NONE;
- append_history(p, "Subscribestatus", "timeout");
- ast_debug(3, "Re-scheduled destruction of SIP subscription %s\n", p->callid ? p->callid : "<unknown>");
- return 10000; /* Reschedule this destruction so that we know that it's gone */
- }
- /* If there are packets still waiting for delivery, delay the destruction */
- if (p->packets) {
- if (!p->needdestroy) {
- char method_str[31];
- ast_debug(3, "Re-scheduled destruction of SIP call %s\n", p->callid ? p->callid : "<unknown>");
- append_history(p, "ReliableXmit", "timeout");
- if (sscanf(p->lastmsg, "Tx: %30s", method_str) == 1 || sscanf(p->lastmsg, "Rx: %30s", method_str) == 1) {
- if (p->ongoing_reinvite || method_match(SIP_CANCEL, method_str) || method_match(SIP_BYE, method_str)) {
- pvt_set_needdestroy(p, "autodestruct");
- }
- }
- return 10000;
- } else {
- /* They've had their chance to respond. Time to bail */
- __sip_pretend_ack(p);
- }
- }
- /* Reset schedule ID */
- p->autokillid = -1;
- /*
- * Lock both the pvt and the channel safely so that we can queue up a frame.
- */
- owner = sip_pvt_lock_full(p);
- if (owner) {
- ast_log(LOG_WARNING, "Autodestruct on dialog '%s' with owner %s in place (Method: %s). Rescheduling destruction for 10000 ms\n", p->callid, ast_channel_name(owner), sip_methods[p->method].text);
- ast_queue_hangup_with_cause(owner, AST_CAUSE_PROTOCOL_ERROR);
- ast_channel_unlock(owner);
- ast_channel_unref(owner);
- sip_pvt_unlock(p);
- return 10000;
- } else if (p->refer && !p->alreadygone) {
- ast_debug(3, "Finally hanging up channel after transfer: %s\n", p->callid);
- stop_media_flows(p);
- transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
- append_history(p, "ReferBYE", "Sending BYE on transferer call leg %s", p->callid);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- } else {
- append_history(p, "AutoDestroy", "%s", p->callid);
- ast_debug(3, "Auto destroying SIP dialog '%s'\n", p->callid);
- sip_pvt_unlock(p);
- dialog_unlink_all(p); /* once it's unlinked and unrefd everywhere, it'll be freed automagically */
- sip_pvt_lock(p);
- /* dialog_unref(p, "unref dialog-- no other matching conditions"); -- unlink all now should finish off the dialog's references and free it. */
- /* sip_destroy(p); */ /* Go ahead and destroy dialog. All attempts to recover is done */
- /* sip_destroy also absorbs the reference */
- }
- sip_pvt_unlock(p);
- dialog_unref(p, "The ref to a dialog passed to this sched callback is going out of scope; unref it.");
- return 0;
- }
- /*! \brief Schedule final destruction of SIP dialog. This can not be canceled.
- * This function is used to keep a dialog around for a period of time in order
- * to properly respond to any retransmits. */
- void sip_scheddestroy_final(struct sip_pvt *p, int ms)
- {
- if (p->final_destruction_scheduled) {
- return; /* already set final destruction */
- }
- sip_scheddestroy(p, ms);
- if (p->autokillid != -1) {
- p->final_destruction_scheduled = 1;
- }
- }
- /*! \brief Schedule destruction of SIP dialog */
- void sip_scheddestroy(struct sip_pvt *p, int ms)
- {
- if (p->final_destruction_scheduled) {
- return; /* already set final destruction */
- }
- if (ms < 0) {
- if (p->timer_t1 == 0) {
- p->timer_t1 = global_t1; /* Set timer T1 if not set (RFC 3261) */
- }
- if (p->timer_b == 0) {
- p->timer_b = global_timer_b; /* Set timer B if not set (RFC 3261) */
- }
- ms = p->timer_t1 * 64;
- }
- if (sip_debug_test_pvt(p)) {
- ast_verbose("Scheduling destruction of SIP dialog '%s' in %d ms (Method: %s)\n", p->callid, ms, sip_methods[p->method].text);
- }
- if (sip_cancel_destroy(p)) {
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- }
- if (p->do_history) {
- append_history(p, "SchedDestroy", "%d ms", ms);
- }
- p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, dialog_ref(p, "setting ref as passing into ast_sched_add for __sip_autodestruct"));
- if (p->stimer && p->stimer->st_active == TRUE && p->stimer->st_schedid > 0) {
- stop_session_timer(p);
- }
- }
- /*! \brief Cancel destruction of SIP dialog.
- * Be careful as this also absorbs the reference - if you call it
- * from within the scheduler, this might be the last reference.
- */
- int sip_cancel_destroy(struct sip_pvt *p)
- {
- if (p->final_destruction_scheduled) {
- return 0;
- }
- if (p->autokillid > -1) {
- append_history(p, "CancelDestroy", "");
- AST_SCHED_DEL_UNREF(sched, p->autokillid, dialog_unref(p, "remove ref for autokillid"));
- }
- return 0;
- }
- /*! \brief Acknowledges receipt of a packet and stops retransmission
- * called with p locked*/
- int __sip_ack(struct sip_pvt *p, uint32_t seqno, int resp, int sipmethod)
- {
- struct sip_pkt *cur, *prev = NULL;
- const char *msg = "Not Found"; /* used only for debugging */
- int res = FALSE;
- /* If we have an outbound proxy for this dialog, then delete it now since
- the rest of the requests in this dialog needs to follow the routing.
- If obforcing is set, we will keep the outbound proxy during the whole
- dialog, regardless of what the SIP rfc says
- */
- if (p->outboundproxy && !p->outboundproxy->force) {
- ref_proxy(p, NULL);
- }
- for (cur = p->packets; cur; prev = cur, cur = cur->next) {
- if (cur->seqno != seqno || cur->is_resp != resp) {
- continue;
- }
- if (cur->is_resp || cur->method == sipmethod) {
- res = TRUE;
- msg = "Found";
- if (!resp && (seqno == p->pendinginvite)) {
- ast_debug(1, "Acked pending invite %u\n", p->pendinginvite);
- p->pendinginvite = 0;
- }
- if (cur->retransid > -1) {
- if (sipdebug)
- ast_debug(4, "** SIP TIMER: Cancelling retransmit of packet (reply received) Retransid #%d\n", cur->retransid);
- }
- /* This odd section is designed to thwart a
- * race condition in the packet scheduler. There are
- * two conditions under which deleting the packet from the
- * scheduler can fail.
- *
- * 1. The packet has been removed from the scheduler because retransmission
- * is being attempted. The problem is that if the packet is currently attempting
- * retransmission and we are at this point in the code, then that MUST mean
- * that retrans_pkt is waiting on p's lock. Therefore we will relinquish the
- * lock temporarily to allow retransmission.
- *
- * 2. The packet has reached its maximum number of retransmissions and has
- * been permanently removed from the packet scheduler. If this is the case, then
- * the packet's retransid will be set to -1. The atomicity of the setting and checking
- * of the retransid to -1 is ensured since in both cases p's lock is held.
- */
- while (cur->retransid > -1 && ast_sched_del(sched, cur->retransid)) {
- sip_pvt_unlock(p);
- usleep(1);
- sip_pvt_lock(p);
- }
- UNLINK(cur, p->packets, prev);
- dialog_unref(cur->owner, "unref pkt cur->owner dialog from sip ack before freeing pkt");
- if (cur->data) {
- ast_free(cur->data);
- }
- ast_free(cur);
- break;
- }
- }
- ast_debug(1, "Stopping retransmission on '%s' of %s %u: Match %s\n",
- p->callid, resp ? "Response" : "Request", seqno, msg);
- return res;
- }
- /*! \brief Pretend to ack all packets
- * called with p locked */
- void __sip_pretend_ack(struct sip_pvt *p)
- {
- struct sip_pkt *cur = NULL;
- while (p->packets) {
- int method;
- if (cur == p->packets) {
- ast_log(LOG_WARNING, "Have a packet that doesn't want to give up! %s\n", sip_methods[cur->method].text);
- return;
- }
- cur = p->packets;
- method = (cur->method) ? cur->method : find_sip_method(ast_str_buffer(cur->data));
- __sip_ack(p, cur->seqno, cur->is_resp, method);
- }
- }
- /*! \brief Acks receipt of packet, keep it around (used for provisional responses) */
- int __sip_semi_ack(struct sip_pvt *p, uint32_t seqno, int resp, int sipmethod)
- {
- struct sip_pkt *cur;
- int res = FALSE;
- for (cur = p->packets; cur; cur = cur->next) {
- if (cur->seqno == seqno && cur->is_resp == resp &&
- (cur->is_resp || method_match(sipmethod, ast_str_buffer(cur->data)))) {
- /* this is our baby */
- if (cur->retransid > -1) {
- if (sipdebug)
- ast_debug(4, "*** SIP TIMER: Cancelling retransmission #%d - %s (got response)\n", cur->retransid, sip_methods[sipmethod].text);
- }
- AST_SCHED_DEL(sched, cur->retransid);
- res = TRUE;
- break;
- }
- }
- ast_debug(1, "(Provisional) Stopping retransmission (but retaining packet) on '%s' %s %u: %s\n", p->callid, resp ? "Response" : "Request", seqno, res == -1 ? "Not Found" : "Found");
- return res;
- }
- /*! \brief Copy SIP request, parse it */
- static void parse_copy(struct sip_request *dst, const struct sip_request *src)
- {
- copy_request(dst, src);
- parse_request(dst);
- }
- /*! \brief add a blank line if no body */
- static void add_blank(struct sip_request *req)
- {
- if (!req->lines) {
- /* Add extra empty return. add_header() reserves 4 bytes so cannot be truncated */
- ast_str_append(&req->data, 0, "\r\n");
- }
- }
- static int send_provisional_keepalive_full(struct sip_pvt *pvt, int with_sdp)
- {
- const char *msg = NULL;
- struct ast_channel *chan;
- int res = 0;
- int old_sched_id = pvt->provisional_keepalive_sched_id;
- chan = sip_pvt_lock_full(pvt);
- /* Check that nothing has changed while we were waiting for the lock */
- if (old_sched_id != pvt->provisional_keepalive_sched_id) {
- /* Keepalive has been cancelled or rescheduled, clean up and leave */
- if (chan) {
- ast_channel_unlock(chan);
- chan = ast_channel_unref(chan);
- }
- sip_pvt_unlock(pvt);
- dialog_unref(pvt, "dialog ref for provisional keepalive");
- return 0;
- }
- if (!pvt->last_provisional || !strncasecmp(pvt->last_provisional, "100", 3)) {
- msg = "183 Session Progress";
- }
- if (pvt->invitestate < INV_COMPLETED) {
- if (with_sdp) {
- transmit_response_with_sdp(pvt, S_OR(msg, pvt->last_provisional), &pvt->initreq, XMIT_UNRELIABLE, FALSE, FALSE);
- } else {
- transmit_response(pvt, S_OR(msg, pvt->last_provisional), &pvt->initreq);
- }
- res = PROVIS_KEEPALIVE_TIMEOUT;
- }
- if (chan) {
- ast_channel_unlock(chan);
- chan = ast_channel_unref(chan);
- }
- if (!res) {
- pvt->provisional_keepalive_sched_id = -1;
- }
- sip_pvt_unlock(pvt);
- if (!res) {
- dialog_unref(pvt, "dialog ref for provisional keepalive");
- }
- return res;
- }
- static int send_provisional_keepalive(const void *data)
- {
- struct sip_pvt *pvt = (struct sip_pvt *) data;
- return send_provisional_keepalive_full(pvt, 0);
- }
- static int send_provisional_keepalive_with_sdp(const void *data)
- {
- struct sip_pvt *pvt = (void *) data;
- return send_provisional_keepalive_full(pvt, 1);
- }
- static void update_provisional_keepalive(struct sip_pvt *pvt, int with_sdp)
- {
- AST_SCHED_DEL_UNREF(sched, pvt->provisional_keepalive_sched_id, dialog_unref(pvt, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
- pvt->provisional_keepalive_sched_id = ast_sched_add(sched, PROVIS_KEEPALIVE_TIMEOUT,
- with_sdp ? send_provisional_keepalive_with_sdp : send_provisional_keepalive, dialog_ref(pvt, "Increment refcount to pass dialog pointer to sched callback"));
- }
- static void add_required_respheader(struct sip_request *req)
- {
- struct ast_str *str;
- int i;
- if (!req->reqsipoptions) {
- return;
- }
- str = ast_str_create(32);
- for (i = 0; i < ARRAY_LEN(sip_options); ++i) {
- if (!(req->reqsipoptions & sip_options[i].id)) {
- continue;
- }
- if (ast_str_strlen(str) > 0) {
- ast_str_append(&str, 0, ", ");
- }
- ast_str_append(&str, 0, "%s", sip_options[i].text);
- }
- if (ast_str_strlen(str) > 0) {
- add_header(req, "Require", ast_str_buffer(str));
- }
- ast_free(str);
- }
- /*! \brief Transmit response on SIP request*/
- static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, uint32_t seqno)
- {
- int res;
- finalize_content(req);
- add_blank(req);
- if (sip_debug_test_pvt(p)) {
- const struct ast_sockaddr *dst = sip_real_dst(p);
- ast_verbose("\n<--- %sTransmitting (%s) to %s --->\n%s\n<------------>\n",
- reliable ? "Reliably " : "", sip_nat_mode(p),
- ast_sockaddr_stringify(dst),
- ast_str_buffer(req->data));
- }
- if (p->do_history) {
- struct sip_request tmp = { .rlpart1 = 0, };
- parse_copy(&tmp, req);
- append_history(p, reliable ? "TxRespRel" : "TxResp", "%s / %s - %s", ast_str_buffer(tmp.data), sip_get_header(&tmp, "CSeq"),
- (tmp.method == SIP_RESPONSE || tmp.method == SIP_UNKNOWN) ? REQ_OFFSET_TO_STR(&tmp, rlpart2) : sip_methods[tmp.method].text);
- deinit_req(&tmp);
- }
- /* If we are sending a final response to an INVITE, stop retransmitting provisional responses */
- if (p->initreq.method == SIP_INVITE && reliable == XMIT_CRITICAL) {
- AST_SCHED_DEL_UNREF(sched, p->provisional_keepalive_sched_id, dialog_unref(p, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
- }
- res = (reliable) ?
- __sip_reliable_xmit(p, seqno, 1, req->data, (reliable == XMIT_CRITICAL), req->method) :
- __sip_xmit(p, req->data);
- deinit_req(req);
- if (res > 0) {
- return 0;
- }
- return res;
- }
- /*!
- * \internal
- * \brief Send SIP Request to the other part of the dialogue
- * \return see \ref __sip_xmit
- */
- static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, uint32_t seqno)
- {
- int res;
- /* If we have an outbound proxy, reset peer address
- Only do this once.
- */
- if (p->outboundproxy) {
- p->sa = p->outboundproxy->ip;
- }
- finalize_content(req);
- add_blank(req);
- if (sip_debug_test_pvt(p)) {
- if (ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT)) {
- ast_verbose("%sTransmitting (NAT) to %s:\n%s\n---\n", reliable ? "Reliably " : "", ast_sockaddr_stringify(&p->recv), ast_str_buffer(req->data));
- } else {
- ast_verbose("%sTransmitting (no NAT) to %s:\n%s\n---\n", reliable ? "Reliably " : "", ast_sockaddr_stringify(&p->sa), ast_str_buffer(req->data));
- }
- }
- if (p->do_history) {
- struct sip_request tmp = { .rlpart1 = 0, };
- parse_copy(&tmp, req);
- append_history(p, reliable ? "TxReqRel" : "TxReq", "%s / %s - %s", ast_str_buffer(tmp.data), sip_get_header(&tmp, "CSeq"), sip_methods[tmp.method].text);
- deinit_req(&tmp);
- }
- res = (reliable) ?
- __sip_reliable_xmit(p, seqno, 0, req->data, (reliable == XMIT_CRITICAL), req->method) :
- __sip_xmit(p, req->data);
- deinit_req(req);
- return res;
- }
- static void enable_dsp_detect(struct sip_pvt *p)
- {
- int features = 0;
- if (p->dsp) {
- return;
- }
- if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
- (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
- if (p->rtp) {
- ast_rtp_instance_dtmf_mode_set(p->rtp, AST_RTP_DTMF_MODE_INBAND);
- }
- features |= DSP_FEATURE_DIGIT_DETECT;
- }
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_CNG)) {
- features |= DSP_FEATURE_FAX_DETECT;
- }
- if (!features) {
- return;
- }
- if (!(p->dsp = ast_dsp_new())) {
- return;
- }
- ast_dsp_set_features(p->dsp, features);
- if (global_relaxdtmf) {
- ast_dsp_set_digitmode(p->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
- }
- }
- static void disable_dsp_detect(struct sip_pvt *p)
- {
- if (p->dsp) {
- ast_dsp_free(p->dsp);
- p->dsp = NULL;
- }
- }
- /*! \brief Set an option on a SIP dialog */
- static int sip_setoption(struct ast_channel *chan, int option, void *data, int datalen)
- {
- int res = -1;
- struct sip_pvt *p = ast_channel_tech_pvt(chan);
- if (!p) {
- ast_log(LOG_ERROR, "Attempt to Ref a null pointer. sip private structure is gone!\n");
- return -1;
- }
- sip_pvt_lock(p);
- switch (option) {
- case AST_OPTION_FORMAT_READ:
- if (p->rtp) {
- res = ast_rtp_instance_set_read_format(p->rtp, *(struct ast_format **) data);
- }
- break;
- case AST_OPTION_FORMAT_WRITE:
- if (p->rtp) {
- res = ast_rtp_instance_set_write_format(p->rtp, *(struct ast_format **) data);
- }
- break;
- case AST_OPTION_DIGIT_DETECT:
- if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
- (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
- char *cp = (char *) data;
- ast_debug(1, "%sabling digit detection on %s\n", *cp ? "En" : "Dis", ast_channel_name(chan));
- if (*cp) {
- enable_dsp_detect(p);
- } else {
- disable_dsp_detect(p);
- }
- res = 0;
- }
- break;
- case AST_OPTION_SECURE_SIGNALING:
- p->req_secure_signaling = *(unsigned int *) data;
- res = 0;
- break;
- case AST_OPTION_SECURE_MEDIA:
- ast_set2_flag(&p->flags[1], *(unsigned int *) data, SIP_PAGE2_USE_SRTP);
- res = 0;
- break;
- default:
- break;
- }
- sip_pvt_unlock(p);
- return res;
- }
- /*! \brief Query an option on a SIP dialog */
- static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *datalen)
- {
- int res = -1;
- enum ast_t38_state state = T38_STATE_UNAVAILABLE;
- struct sip_pvt *p = (struct sip_pvt *) ast_channel_tech_pvt(chan);
- char *cp;
- sip_pvt_lock(p);
- switch (option) {
- case AST_OPTION_T38_STATE:
- /* Make sure we got an ast_t38_state enum passed in */
- if (*datalen != sizeof(enum ast_t38_state)) {
- ast_log(LOG_ERROR, "Invalid datalen for AST_OPTION_T38_STATE option. Expected %d, got %d\n", (int)sizeof(enum ast_t38_state), *datalen);
- break;
- }
- /* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) {
- switch (p->t38.state) {
- case T38_LOCAL_REINVITE:
- case T38_PEER_REINVITE:
- state = T38_STATE_NEGOTIATING;
- break;
- case T38_ENABLED:
- state = T38_STATE_NEGOTIATED;
- break;
- case T38_REJECTED:
- state = T38_STATE_REJECTED;
- break;
- default:
- state = T38_STATE_UNKNOWN;
- }
- }
- *((enum ast_t38_state *) data) = state;
- res = 0;
- break;
- case AST_OPTION_DIGIT_DETECT:
- cp = (char *) data;
- *cp = p->dsp ? 1 : 0;
- ast_debug(1, "Reporting digit detection %sabled on %s\n", *cp ? "en" : "dis", ast_channel_name(chan));
- break;
- case AST_OPTION_SECURE_SIGNALING:
- *((unsigned int *) data) = p->req_secure_signaling;
- res = 0;
- break;
- case AST_OPTION_SECURE_MEDIA:
- *((unsigned int *) data) = ast_test_flag(&p->flags[1], SIP_PAGE2_USE_SRTP) ? 1 : 0;
- res = 0;
- break;
- case AST_OPTION_DEVICE_NAME:
- if (p && p->outgoing_call) {
- cp = (char *) data;
- ast_copy_string(cp, p->dialstring, *datalen);
- res = 0;
- }
- /* We purposely break with a return of -1 in the
- * implied else case here
- */
- break;
- default:
- break;
- }
- sip_pvt_unlock(p);
- return res;
- }
- /*! \brief Locate closing quote in a string, skipping escaped quotes.
- * optionally with a limit on the search.
- * start must be past the first quote.
- */
- const char *find_closing_quote(const char *start, const char *lim)
- {
- char last_char = '\0';
- const char *s;
- for (s = start; *s && s != lim; last_char = *s++) {
- if (*s == '"' && last_char != '\\')
- break;
- }
- return s;
- }
- /*! \brief Send message with Access-URL header, if this is an HTML URL only! */
- static int sip_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen)
- {
- struct sip_pvt *p = ast_channel_tech_pvt(chan);
- if (subclass != AST_HTML_URL)
- return -1;
- ast_string_field_build(p, url, "<%s>;mode=active", data);
- if (sip_debug_test_pvt(p))
- ast_debug(1, "Send URL %s, state = %u!\n", data, ast_channel_state(chan));
- switch (ast_channel_state(chan)) {
- case AST_STATE_RING:
- transmit_response(p, "100 Trying", &p->initreq);
- break;
- case AST_STATE_RINGING:
- transmit_response(p, "180 Ringing", &p->initreq);
- break;
- case AST_STATE_UP:
- if (!p->pendinginvite) { /* We are up, and have no outstanding invite */
- transmit_reinvite_with_sdp(p, FALSE, FALSE);
- } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
- ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
- }
- break;
- default:
- ast_log(LOG_WARNING, "Don't know how to send URI when state is %u!\n", ast_channel_state(chan));
- }
- return 0;
- }
- /*! \brief Deliver SIP call ID for the call */
- static const char *sip_get_callid(struct ast_channel *chan)
- {
- return ast_channel_tech_pvt(chan) ? ((struct sip_pvt *) ast_channel_tech_pvt(chan))->callid : "";
- }
- /*!
- * \internal
- * \brief Send SIP MESSAGE text within a call
- * \note Called from PBX core sendtext() application
- */
- static int sip_sendtext(struct ast_channel *ast, const char *text)
- {
- struct sip_pvt *dialog = ast_channel_tech_pvt(ast);
- int debug;
- if (!dialog) {
- return -1;
- }
- /* NOT ast_strlen_zero, because a zero-length message is specifically
- * allowed by RFC 3428 (See section 10, Examples) */
- if (!text) {
- return 0;
- }
- if(!is_method_allowed(&dialog->allowed_methods, SIP_MESSAGE)) {
- ast_debug(2, "Trying to send MESSAGE to device that does not support it.\n");
- return 0;
- }
- debug = sip_debug_test_pvt(dialog);
- if (debug) {
- ast_verbose("Sending text %s on %s\n", text, ast_channel_name(ast));
- }
- /* Setup to send text message */
- sip_pvt_lock(dialog);
- destroy_msg_headers(dialog);
- ast_string_field_set(dialog, msg_body, text);
- transmit_message(dialog, 0, 0);
- sip_pvt_unlock(dialog);
- return 0;
- }
- /*! \brief Update peer object in realtime storage
- If the Asterisk system name is set in asterisk.conf, we will use
- that name and store that in the "regserver" field in the sippeers
- table to facilitate multi-server setups.
- */
- static void realtime_update_peer(const char *peername, struct ast_sockaddr *addr, const char *defaultuser, const char *fullcontact, const char *useragent, int expirey, unsigned short deprecated_username, int lastms, const char *path)
- {
- char port[10];
- char ipaddr[INET6_ADDRSTRLEN];
- char regseconds[20];
- char *tablename = NULL;
- char str_lastms[20];
- const char *sysname = ast_config_AST_SYSTEM_NAME;
- char *syslabel = NULL;
- time_t nowtime = time(NULL) + expirey;
- const char *fc = fullcontact ? "fullcontact" : NULL;
- int realtimeregs = ast_check_realtime("sipregs");
- tablename = realtimeregs ? "sipregs" : "sippeers";
- snprintf(str_lastms, sizeof(str_lastms), "%d", lastms);
- snprintf(regseconds, sizeof(regseconds), "%d", (int)nowtime); /* Expiration time */
- ast_copy_string(ipaddr, ast_sockaddr_isnull(addr) ? "" : ast_sockaddr_stringify_addr(addr), sizeof(ipaddr));
- ast_copy_string(port, ast_sockaddr_port(addr) ? ast_sockaddr_stringify_port(addr) : "", sizeof(port));
- if (ast_strlen_zero(sysname)) { /* No system name, disable this */
- sysname = NULL;
- } else if (sip_cfg.rtsave_sysname) {
- syslabel = "regserver";
- }
- /* XXX IMPORTANT: Anytime you add a new parameter to be updated, you
- * must also add it to contrib/scripts/asterisk.ldap-schema,
- * contrib/scripts/asterisk.ldif,
- * and to configs/res_ldap.conf.sample as described in
- * bugs 15156 and 15895
- */
- /* This is ugly, we need something better ;-) */
- if (sip_cfg.rtsave_path) {
- if (fc) {
- ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
- "port", port, "regseconds", regseconds,
- deprecated_username ? "username" : "defaultuser", defaultuser,
- "useragent", useragent, "lastms", str_lastms,
- "path", path, /* Path data can be NULL */
- fc, fullcontact, syslabel, sysname, SENTINEL); /* note fc and syslabel _can_ be NULL */
- } else {
- ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
- "port", port, "regseconds", regseconds,
- "useragent", useragent, "lastms", str_lastms,
- deprecated_username ? "username" : "defaultuser", defaultuser,
- "path", path, /* Path data can be NULL */
- syslabel, sysname, SENTINEL); /* note syslabel _can_ be NULL */
- }
- } else {
- if (fc) {
- ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
- "port", port, "regseconds", regseconds,
- deprecated_username ? "username" : "defaultuser", defaultuser,
- "useragent", useragent, "lastms", str_lastms,
- fc, fullcontact, syslabel, sysname, SENTINEL); /* note fc and syslabel _can_ be NULL */
- } else {
- ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
- "port", port, "regseconds", regseconds,
- "useragent", useragent, "lastms", str_lastms,
- deprecated_username ? "username" : "defaultuser", defaultuser,
- syslabel, sysname, SENTINEL); /* note syslabel _can_ be NULL */
- }
- }
- }
- /*! \brief Automatically add peer extension to dial plan */
- static void register_peer_exten(struct sip_peer *peer, int onoff)
- {
- char multi[256];
- char *stringp, *ext, *context;
- struct pbx_find_info q = { .stacklen = 0 };
- /* XXX note that sip_cfg.regcontext is both a global 'enable' flag and
- * the name of the global regexten context, if not specified
- * individually.
- */
- if (ast_strlen_zero(sip_cfg.regcontext))
- return;
- ast_copy_string(multi, S_OR(peer->regexten, peer->name), sizeof(multi));
- stringp = multi;
- while ((ext = strsep(&stringp, "&"))) {
- if ((context = strchr(ext, '@'))) {
- *context++ = '\0'; /* split ext@context */
- if (!ast_context_find(context)) {
- ast_log(LOG_WARNING, "Context %s must exist in regcontext= in sip.conf!\n", context);
- continue;
- }
- } else {
- context = sip_cfg.regcontext;
- }
- if (onoff) {
- if (!ast_exists_extension(NULL, context, ext, 1, NULL)) {
- ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop",
- ast_strdup(peer->name), ast_free_ptr, "SIP");
- }
- } else if (pbx_find_extension(NULL, NULL, &q, context, ext, 1, NULL, "", E_MATCH)) {
- ast_context_remove_extension(context, ext, 1, NULL);
- }
- }
- }
- /*! Destroy mailbox subscriptions */
- static void destroy_mailbox(struct sip_mailbox *mailbox)
- {
- if (mailbox->event_sub) {
- mailbox->event_sub = stasis_unsubscribe(mailbox->event_sub);
- }
- ast_free(mailbox);
- }
- /*! Destroy all peer-related mailbox subscriptions */
- static void clear_peer_mailboxes(struct sip_peer *peer)
- {
- struct sip_mailbox *mailbox;
- while ((mailbox = AST_LIST_REMOVE_HEAD(&peer->mailboxes, entry)))
- destroy_mailbox(mailbox);
- }
- static void sip_destroy_peer_fn(void *peer)
- {
- sip_destroy_peer(peer);
- }
- /*! \brief Destroy peer object from memory */
- static void sip_destroy_peer(struct sip_peer *peer)
- {
- ast_debug(3, "Destroying SIP peer %s\n", peer->name);
- /*
- * Remove any mailbox event subscriptions for this peer before
- * we destroy anything. An event subscription callback may be
- * happening right now.
- */
- clear_peer_mailboxes(peer);
- if (peer->outboundproxy) {
- ao2_ref(peer->outboundproxy, -1);
- peer->outboundproxy = NULL;
- }
- /* Delete it, it needs to disappear */
- if (peer->call) {
- dialog_unlink_all(peer->call);
- peer->call = dialog_unref(peer->call, "peer->call is being unset");
- }
- if (peer->mwipvt) { /* We have an active subscription, delete it */
- dialog_unlink_all(peer->mwipvt);
- peer->mwipvt = dialog_unref(peer->mwipvt, "unreffing peer->mwipvt");
- }
- if (peer->chanvars) {
- ast_variables_destroy(peer->chanvars);
- peer->chanvars = NULL;
- }
- sip_route_clear(&peer->path);
- register_peer_exten(peer, FALSE);
- ast_free_acl_list(peer->acl);
- ast_free_acl_list(peer->directmediaacl);
- if (peer->selfdestruct)
- ast_atomic_fetchadd_int(&apeerobjs, -1);
- else if (!ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) && peer->is_realtime) {
- ast_atomic_fetchadd_int(&rpeerobjs, -1);
- ast_debug(3, "-REALTIME- peer Destroyed. Name: %s. Realtime Peer objects: %d\n", peer->name, rpeerobjs);
- } else
- ast_atomic_fetchadd_int(&speerobjs, -1);
- if (peer->auth) {
- ao2_t_ref(peer->auth, -1, "Removing peer authentication");
- peer->auth = NULL;
- }
- if (peer->socket.tcptls_session) {
- ao2_ref(peer->socket.tcptls_session, -1);
- peer->socket.tcptls_session = NULL;
- } else if (peer->socket.ws_session) {
- ast_websocket_unref(peer->socket.ws_session);
- peer->socket.ws_session = NULL;
- }
- peer->named_callgroups = ast_unref_namedgroups(peer->named_callgroups);
- peer->named_pickupgroups = ast_unref_namedgroups(peer->named_pickupgroups);
- ast_cc_config_params_destroy(peer->cc_params);
- ast_string_field_free_memory(peer);
- ao2_cleanup(peer->caps);
- ast_rtp_dtls_cfg_free(&peer->dtls_cfg);
- ast_endpoint_shutdown(peer->endpoint);
- peer->endpoint = NULL;
- }
- /*! \brief Update peer data in database (if used) */
- static void update_peer(struct sip_peer *p, int expire)
- {
- int rtcachefriends = ast_test_flag(&p->flags[1], SIP_PAGE2_RTCACHEFRIENDS);
- if (sip_cfg.peer_rtupdate && (p->is_realtime || rtcachefriends)) {
- struct ast_str *r = sip_route_list(&p->path, 0, 0);
- if (r) {
- realtime_update_peer(p->name, &p->addr, p->username,
- p->fullcontact, p->useragent, expire, p->deprecated_username,
- p->lastms, ast_str_buffer(r));
- ast_free(r);
- }
- }
- }
- static struct ast_variable *get_insecure_variable_from_config(struct ast_config *cfg)
- {
- struct ast_variable *var = NULL;
- struct ast_flags flags = {0};
- char *cat = NULL;
- const char *insecure;
- while ((cat = ast_category_browse(cfg, cat))) {
- insecure = ast_variable_retrieve(cfg, cat, "insecure");
- set_insecure_flags(&flags, insecure, -1);
- if (ast_test_flag(&flags, SIP_INSECURE_PORT)) {
- var = ast_category_root(cfg, cat);
- break;
- }
- }
- return var;
- }
- static struct ast_variable *get_insecure_variable_from_sippeers(const char *column, const char *value)
- {
- struct ast_config *peerlist;
- struct ast_variable *var = NULL;
- if ((peerlist = ast_load_realtime_multientry("sippeers", column, value, "insecure LIKE", "%port%", SENTINEL))) {
- if ((var = get_insecure_variable_from_config(peerlist))) {
- /* Must clone, because var will get freed along with
- * peerlist. */
- var = ast_variables_dup(var);
- }
- ast_config_destroy(peerlist);
- }
- return var;
- }
- /* Yes.. the only column that makes sense to pass is "ipaddr", but for
- * consistency's sake, we require the column name to be passed. As extra
- * argument, we take a pointer to var. We already got the info, so we better
- * return it and save the caller a query. If return value is nonzero, then *var
- * is nonzero too (and the other way around). */
- static struct ast_variable *get_insecure_variable_from_sipregs(const char *column, const char *value, struct ast_variable **var)
- {
- struct ast_variable *varregs = NULL;
- struct ast_config *regs, *peers;
- char *regscat;
- const char *regname;
- if (!(regs = ast_load_realtime_multientry("sipregs", column, value, SENTINEL))) {
- return NULL;
- }
- /* Load *all* peers that are probably insecure=port */
- if (!(peers = ast_load_realtime_multientry("sippeers", "insecure LIKE", "%port%", SENTINEL))) {
- ast_config_destroy(regs);
- return NULL;
- }
- /* Loop over the sipregs that match IP address and attempt to find an
- * insecure=port match to it in sippeers. */
- regscat = NULL;
- while ((regscat = ast_category_browse(regs, regscat)) && (regname = ast_variable_retrieve(regs, regscat, "name"))) {
- char *peerscat;
- const char *peername;
- peerscat = NULL;
- while ((peerscat = ast_category_browse(peers, peerscat)) && (peername = ast_variable_retrieve(peers, peerscat, "name"))) {
- if (!strcasecmp(regname, peername)) {
- /* Ensure that it really is insecure=port and
- * not something else. */
- const char *insecure = ast_variable_retrieve(peers, peerscat, "insecure");
- struct ast_flags flags = {0};
- set_insecure_flags(&flags, insecure, -1);
- if (ast_test_flag(&flags, SIP_INSECURE_PORT)) {
- /* ENOMEM checks till the bitter end. */
- if ((varregs = ast_variables_dup(ast_category_root(regs, regscat)))) {
- if (!(*var = ast_variables_dup(ast_category_root(peers, peerscat)))) {
- ast_variables_destroy(varregs);
- varregs = NULL;
- }
- }
- goto done;
- }
- }
- }
- }
- done:
- ast_config_destroy(regs);
- ast_config_destroy(peers);
- return varregs;
- }
- static const char *get_name_from_variable(const struct ast_variable *var)
- {
- /* Don't expect this to return non-NULL. Both NULL and empty
- * values can cause the option to get removed from the variable
- * list. This is called on ast_variables gotten from both
- * ast_load_realtime and ast_load_realtime_multientry.
- * - ast_load_realtime removes options with empty values
- * - ast_load_realtime_multientry does not!
- * For consistent behaviour, we check for the empty name and
- * return NULL instead. */
- const struct ast_variable *tmp;
- for (tmp = var; tmp; tmp = tmp->next) {
- if (!strcasecmp(tmp->name, "name")) {
- if (!ast_strlen_zero(tmp->value)) {
- return tmp->value;
- }
- break;
- }
- }
- return NULL;
- }
- /* If varregs is NULL, we don't use sipregs.
- * Using empty if-bodies instead of goto's while avoiding unnecessary indents */
- static int realtime_peer_by_name(const char *const *name, struct ast_sockaddr *addr, const char *ipaddr, struct ast_variable **var, struct ast_variable **varregs)
- {
- /* Peer by name and host=dynamic */
- if ((*var = ast_load_realtime("sippeers", "name", *name, "host", "dynamic", SENTINEL))) {
- ;
- /* Peer by name and host=IP */
- } else if (addr && !(*var = ast_load_realtime("sippeers", "name", *name, "host", ipaddr, SENTINEL))) {
- ;
- /* Peer by name and host=HOSTNAME */
- } else if ((*var = ast_load_realtime("sippeers", "name", *name, SENTINEL))) {
- /*!\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 (addr) {
- struct ast_variable *tmp;
- for (tmp = *var; tmp; tmp = tmp->next) {
- if (!strcasecmp(tmp->name, "host")) {
- struct ast_sockaddr *addrs = NULL;
- if (ast_sockaddr_resolve(&addrs,
- tmp->value,
- PARSE_PORT_FORBID,
- get_address_family_filter(AST_TRANSPORT_UDP)) <= 0 ||
- ast_sockaddr_cmp(&addrs[0], addr)) {
- /* No match */
- ast_variables_destroy(*var);
- *var = NULL;
- }
- ast_free(addrs);
- break;
- }
- }
- }
- }
- /* Did we find anything? */
- if (*var) {
- if (varregs) {
- *varregs = ast_load_realtime("sipregs", "name", *name, SENTINEL);
- }
- return 1;
- }
- return 0;
- }
- /* Another little helper function for backwards compatibility: this
- * checks/fetches the sippeer that belongs to the sipreg. If none is
- * found, we free the sipreg and return false. This way we can do the
- * check inside the if-condition below. In the old code, not finding
- * the sippeer also had it continue look for another match, so we do
- * the same. */
- static struct ast_variable *realtime_peer_get_sippeer_helper(const char **name, struct ast_variable **varregs) {
- struct ast_variable *var = NULL;
- const char *old_name = *name;
- *name = get_name_from_variable(*varregs);
- if (!*name || !(var = ast_load_realtime("sippeers", "name", *name, SENTINEL))) {
- if (!*name) {
- ast_log(LOG_WARNING, "Found sipreg but it has no name\n");
- }
- ast_variables_destroy(*varregs);
- *varregs = NULL;
- *name = old_name;
- }
- return var;
- }
- /* If varregs is NULL, we don't use sipregs. If we return true, then *name is
- * set. Using empty if-bodies instead of goto's while avoiding unnecessary
- * indents. */
- static int realtime_peer_by_addr(const char **name, struct ast_sockaddr *addr, const char *ipaddr, const char *callbackexten, struct ast_variable **var, struct ast_variable **varregs)
- {
- char portstring[6]; /* up to 5 digits plus null terminator */
- ast_copy_string(portstring, ast_sockaddr_stringify_port(addr), sizeof(portstring));
- /* We're not finding this peer by this name anymore. Reset it. */
- *name = NULL;
- /* First check for fixed IP hosts with matching callbackextensions, if specified */
- if (!ast_strlen_zero(callbackexten) && (*var = ast_load_realtime("sippeers", "host", ipaddr, "port", portstring, "callbackextension", callbackexten, SENTINEL))) {
- ;
- /* Check for fixed IP hosts */
- } else if ((*var = ast_load_realtime("sippeers", "host", ipaddr, "port", portstring, SENTINEL))) {
- ;
- /* Check for registered hosts (in sipregs) */
- } else if (varregs && (*varregs = ast_load_realtime("sipregs", "ipaddr", ipaddr, "port", portstring, SENTINEL)) &&
- (*var = realtime_peer_get_sippeer_helper(name, varregs))) {
- ;
- /* Check for registered hosts (in sippeers) */
- } else if (!varregs && (*var = ast_load_realtime("sippeers", "ipaddr", ipaddr, "port", portstring, SENTINEL))) {
- ;
- /* We couldn't match on ipaddress and port, so we need to check if port is insecure */
- } else if ((*var = get_insecure_variable_from_sippeers("host", ipaddr))) {
- ;
- /* Same as above, but try the IP address field (in sipregs)
- * Observe that it fetches the name/var at the same time, without the
- * realtime_peer_get_sippeer_helper. Also note that it is quite inefficient.
- * Avoid sipregs if possible. */
- } else if (varregs && (*varregs = get_insecure_variable_from_sipregs("ipaddr", ipaddr, var))) {
- ;
- /* Same as above, but try the IP address field (in sippeers) */
- } else if (!varregs && (*var = get_insecure_variable_from_sippeers("ipaddr", ipaddr))) {
- ;
- }
- /* Nothing found? */
- if (!*var) {
- return 0;
- }
- /* Check peer name. It must not be empty. There may exist a
- * different match that does have a name, but it's too late for
- * that now. */
- if (!*name && !(*name = get_name_from_variable(*var))) {
- ast_log(LOG_WARNING, "Found peer for IP %s but it has no name\n", ipaddr);
- ast_variables_destroy(*var);
- *var = NULL;
- if (varregs && *varregs) {
- ast_variables_destroy(*varregs);
- *varregs = NULL;
- }
- return 0;
- }
- /* Make sure varregs is populated if var is. The inverse,
- * ensuring that var is set when varregs is, is taken
- * care of by realtime_peer_get_sippeer_helper(). */
- if (varregs && !*varregs) {
- *varregs = ast_load_realtime("sipregs", "name", *name, SENTINEL);
- }
- return 1;
- }
- static int register_realtime_peers_with_callbackextens(void)
- {
- struct ast_config *cfg;
- char *cat = NULL;
- if (!(ast_check_realtime("sippeers"))) {
- return 0;
- }
- /* This is hacky. We want name to be the cat, so it is the first property */
- if (!(cfg = ast_load_realtime_multientry("sippeers", "name LIKE", "%", "callbackextension LIKE", "%", SENTINEL))) {
- return -1;
- }
- while ((cat = ast_category_browse(cfg, cat))) {
- struct sip_peer *peer;
- struct ast_variable *var = ast_category_root(cfg, cat);
- if (!(peer = build_peer(cat, var, NULL, TRUE, FALSE))) {
- continue;
- }
- ast_log(LOG_NOTICE, "Created realtime peer '%s' for registration\n", peer->name);
- peer->is_realtime = 1;
- sip_unref_peer(peer, "register_realtime_peers: Done registering releasing");
- }
- ast_config_destroy(cfg);
- return 0;
- }
- /*! \brief realtime_peer: Get peer from realtime storage
- * Checks the "sippeers" realtime family from extconfig.conf
- * Checks the "sipregs" realtime family from extconfig.conf if it's configured.
- * This returns a pointer to a peer and because we use build_peer, we can rest
- * assured that the refcount is bumped.
- *
- * \note This is never called with both newpeername and addr at the same time.
- * If you do, be prepared to get a peer with a different name than newpeername.
- */
- static struct sip_peer *realtime_peer(const char *newpeername, struct ast_sockaddr *addr, char *callbackexten, int devstate_only, int which_objects)
- {
- struct sip_peer *peer = NULL;
- struct ast_variable *var = NULL;
- struct ast_variable *varregs = NULL;
- char ipaddr[INET6_ADDRSTRLEN];
- int realtimeregs = ast_check_realtime("sipregs");
- if (addr) {
- ast_copy_string(ipaddr, ast_sockaddr_stringify_addr(addr), sizeof(ipaddr));
- } else {
- ipaddr[0] = '\0';
- }
- if (newpeername && realtime_peer_by_name(&newpeername, addr, ipaddr, &var, realtimeregs ? &varregs : NULL)) {
- ;
- } else if (addr && realtime_peer_by_addr(&newpeername, addr, ipaddr, callbackexten, &var, realtimeregs ? &varregs : NULL)) {
- ;
- } else {
- return NULL;
- }
- /* If we're looking for users, don't return peers (although this check
- * should probably be done in realtime_peer_by_* instead...) */
- if (which_objects == FINDUSERS) {
- struct ast_variable *tmp;
- for (tmp = var; tmp; tmp = tmp->next) {
- if (!strcasecmp(tmp->name, "type") && (!strcasecmp(tmp->value, "peer"))) {
- goto cleanup;
- }
- }
- }
- /* Peer found in realtime, now build it in memory */
- peer = build_peer(newpeername, var, varregs, TRUE, devstate_only);
- if (!peer) {
- goto cleanup;
- }
- /* Previous versions of Asterisk did not require the type field to be
- * set for real time peers. This statement preserves that behavior. */
- if (peer->type == 0) {
- if (which_objects == FINDUSERS) {
- peer->type = SIP_TYPE_USER;
- } else if (which_objects == FINDPEERS) {
- peer->type = SIP_TYPE_PEER;
- } else {
- peer->type = SIP_TYPE_PEER | SIP_TYPE_USER;
- }
- }
- ast_debug(3, "-REALTIME- loading peer from database to memory. Name: %s. Peer objects: %d\n", peer->name, rpeerobjs);
- if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) && !devstate_only) {
- /* Cache peer */
- ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_RTAUTOCLEAR|SIP_PAGE2_RTCACHEFRIENDS);
- if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
- AST_SCHED_REPLACE_UNREF(peer->expire, sched, sip_cfg.rtautoclear * 1000, expire_register, peer,
- sip_unref_peer(_data, "remove registration ref"),
- sip_unref_peer(peer, "remove registration ref"),
- sip_ref_peer(peer, "add registration ref"));
- }
- ao2_t_link(peers, peer, "link peer into peers table");
- if (!ast_sockaddr_isnull(&peer->addr)) {
- ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
- }
- }
- peer->is_realtime = 1;
- cleanup:
- ast_variables_destroy(var);
- ast_variables_destroy(varregs);
- return peer;
- }
- /* Function to assist finding peers by name only */
- static int find_by_name(void *obj, void *arg, void *data, int flags)
- {
- struct sip_peer *search = obj, *match = arg;
- int *which_objects = data;
- /* Usernames in SIP uri's are case sensitive. Domains are not */
- if (strcmp(search->name, match->name)) {
- return 0;
- }
- switch (*which_objects) {
- case FINDUSERS:
- if (!(search->type & SIP_TYPE_USER)) {
- return 0;
- }
- break;
- case FINDPEERS:
- if (!(search->type & SIP_TYPE_PEER)) {
- return 0;
- }
- break;
- case FINDALLDEVICES:
- break;
- }
- return CMP_MATCH | CMP_STOP;
- }
- static struct sip_peer *sip_find_peer_full(const char *peer, struct ast_sockaddr *addr, char *callbackexten, int realtime, int which_objects, int devstate_only, int transport)
- {
- struct sip_peer *p = NULL;
- struct sip_peer tmp_peer;
- if (peer) {
- ast_copy_string(tmp_peer.name, peer, sizeof(tmp_peer.name));
- p = ao2_t_callback_data(peers, OBJ_POINTER, find_by_name, &tmp_peer, &which_objects, "ao2_find in peers table");
- } else if (addr) { /* search by addr? */
- ast_sockaddr_copy(&tmp_peer.addr, addr);
- tmp_peer.flags[0].flags = 0;
- tmp_peer.transports = transport;
- p = ao2_t_callback_data(peers_by_ip, OBJ_POINTER, peer_ipcmp_cb_full, &tmp_peer, callbackexten, "ao2_find in peers_by_ip table");
- if (!p) {
- ast_set_flag(&tmp_peer.flags[0], SIP_INSECURE_PORT);
- p = ao2_t_callback_data(peers_by_ip, OBJ_POINTER, peer_ipcmp_cb_full, &tmp_peer, callbackexten, "ao2_find in peers_by_ip table 2");
- if (p) {
- return p;
- }
- }
- }
- if (!p && (realtime || devstate_only)) {
- /* realtime_peer will return a peer with matching callbackexten if possible, otherwise one matching
- * without the callbackexten */
- p = realtime_peer(peer, addr, callbackexten, devstate_only, which_objects);
- if (p) {
- switch (which_objects) {
- case FINDUSERS:
- if (!(p->type & SIP_TYPE_USER)) {
- sip_unref_peer(p, "Wrong type of realtime SIP endpoint");
- return NULL;
- }
- break;
- case FINDPEERS:
- if (!(p->type & SIP_TYPE_PEER)) {
- sip_unref_peer(p, "Wrong type of realtime SIP endpoint");
- return NULL;
- }
- break;
- case FINDALLDEVICES:
- break;
- }
- }
- }
- return p;
- }
- /*!
- * \brief Locate device by name or ip address
- * \param peer, addr, realtime, devstate_only, transport
- * \param which_objects Define which objects should be matched when doing a lookup
- * by name. Valid options are FINDUSERS, FINDPEERS, or FINDALLDEVICES.
- * Note that this option is not used at all when doing a lookup by IP.
- *
- * This is used on find matching device on name or ip/port.
- * If the device was declared as type=peer, we don't match on peer name on incoming INVITEs.
- *
- * \note Avoid using this function in new functions if there is a way to avoid it,
- * since it might cause a database lookup.
- */
- struct sip_peer *sip_find_peer(const char *peer, struct ast_sockaddr *addr, int realtime, int which_objects, int devstate_only, int transport)
- {
- return sip_find_peer_full(peer, addr, NULL, realtime, which_objects, devstate_only, transport);
- }
- static struct sip_peer *sip_find_peer_by_ip_and_exten(struct ast_sockaddr *addr, char *callbackexten, int transport)
- {
- return sip_find_peer_full(NULL, addr, callbackexten, TRUE, FINDPEERS, FALSE, transport);
- }
- /*! \brief Set nat mode on the various data sockets */
- static void do_setnat(struct sip_pvt *p)
- {
- const char *mode;
- int natflags;
- natflags = ast_test_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP);
- mode = natflags ? "On" : "Off";
- if (p->rtp) {
- ast_debug(1, "Setting NAT on RTP to %s\n", mode);
- ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_NAT, natflags);
- }
- if (p->vrtp) {
- ast_debug(1, "Setting NAT on VRTP to %s\n", mode);
- ast_rtp_instance_set_prop(p->vrtp, AST_RTP_PROPERTY_NAT, natflags);
- }
- if (p->udptl) {
- ast_debug(1, "Setting NAT on UDPTL to %s\n", mode);
- ast_udptl_setnat(p->udptl, natflags);
- }
- if (p->trtp) {
- ast_debug(1, "Setting NAT on TRTP to %s\n", mode);
- ast_rtp_instance_set_prop(p->trtp, AST_RTP_PROPERTY_NAT, natflags);
- }
- }
- /*! \brief Change the T38 state on a SIP dialog */
- static void change_t38_state(struct sip_pvt *p, int state)
- {
- int old = p->t38.state;
- struct ast_channel *chan = p->owner;
- struct ast_control_t38_parameters parameters = { .request_response = 0 };
- /* Don't bother changing if we are already in the state wanted */
- if (old == state)
- return;
- p->t38.state = state;
- ast_debug(2, "T38 state changed to %u on channel %s\n", p->t38.state, chan ? ast_channel_name(chan) : "<none>");
- /* If no channel was provided we can't send off a control frame */
- if (!chan)
- return;
- /* Given the state requested and old state determine what control frame we want to queue up */
- switch (state) {
- case T38_PEER_REINVITE:
- parameters = p->t38.their_parms;
- parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
- parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
- ast_udptl_set_tag(p->udptl, "%s", ast_channel_name(chan));
- break;
- case T38_ENABLED:
- parameters = p->t38.their_parms;
- parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
- parameters.request_response = AST_T38_NEGOTIATED;
- ast_udptl_set_tag(p->udptl, "%s", ast_channel_name(chan));
- break;
- case T38_REJECTED:
- case T38_DISABLED:
- if (old == T38_ENABLED) {
- parameters.request_response = AST_T38_TERMINATED;
- } else if (old == T38_LOCAL_REINVITE) {
- parameters.request_response = AST_T38_REFUSED;
- }
- break;
- case T38_LOCAL_REINVITE:
- /* wait until we get a peer response before responding to local reinvite */
- break;
- }
- /* Woot we got a message, create a control frame and send it on! */
- if (parameters.request_response)
- ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, ¶meters, sizeof(parameters));
- }
- /*! \brief Set the global T38 capabilities on a SIP dialog structure */
- static void set_t38_capabilities(struct sip_pvt *p)
- {
- if (p->udptl) {
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY) {
- ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
- } else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_FEC) {
- ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC);
- } else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL) {
- ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
- }
- }
- }
- static void copy_socket_data(struct sip_socket *to_sock, const struct sip_socket *from_sock)
- {
- if (to_sock->tcptls_session) {
- ao2_ref(to_sock->tcptls_session, -1);
- to_sock->tcptls_session = NULL;
- } else if (to_sock->ws_session) {
- ast_websocket_unref(to_sock->ws_session);
- to_sock->ws_session = NULL;
- }
- if (from_sock->tcptls_session) {
- ao2_ref(from_sock->tcptls_session, +1);
- } else if (from_sock->ws_session) {
- ast_websocket_ref(from_sock->ws_session);
- }
- *to_sock = *from_sock;
- }
- /*! \brief Initialize DTLS-SRTP support on an RTP instance */
- static int dialog_initialize_dtls_srtp(const struct sip_pvt *dialog, struct ast_rtp_instance *rtp, struct ast_sdp_srtp **srtp)
- {
- struct ast_rtp_engine_dtls *dtls;
- if (!dialog->dtls_cfg.enabled) {
- return 0;
- }
- if (!ast_rtp_engine_srtp_is_registered()) {
- ast_log(LOG_ERROR, "No SRTP module loaded, can't setup SRTP session.\n");
- return -1;
- }
- if (!(dtls = ast_rtp_instance_get_dtls(rtp))) {
- ast_log(LOG_ERROR, "No DTLS-SRTP support present on engine for RTP instance '%p', was it compiled with support for it?\n",
- rtp);
- return -1;
- }
- if (dtls->set_configuration(rtp, &dialog->dtls_cfg)) {
- ast_log(LOG_ERROR, "Attempted to set an invalid DTLS-SRTP configuration on RTP instance '%p'\n",
- rtp);
- return -1;
- }
- if (!(*srtp = ast_sdp_srtp_alloc())) {
- ast_log(LOG_ERROR, "Failed to create required SRTP structure on RTP instance '%p'\n",
- rtp);
- return -1;
- }
- return 0;
- }
- /*! \brief Initialize RTP portion of a dialog
- * \return -1 on failure, 0 on success
- */
- static int dialog_initialize_rtp(struct sip_pvt *dialog)
- {
- struct ast_sockaddr bindaddr_tmp;
- struct ast_rtp_engine_ice *ice;
- if (!sip_methods[dialog->method].need_rtp) {
- return 0;
- }
- if (!ast_sockaddr_isnull(&rtpbindaddr)) {
- ast_sockaddr_copy(&bindaddr_tmp, &rtpbindaddr);
- } else {
- ast_sockaddr_copy(&bindaddr_tmp, &bindaddr);
- }
- if (!(dialog->rtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr_tmp, NULL))) {
- return -1;
- }
- if (!ast_test_flag(&dialog->flags[2], SIP_PAGE3_ICE_SUPPORT) && (ice = ast_rtp_instance_get_ice(dialog->rtp))) {
- ice->stop(dialog->rtp);
- }
- if (dialog_initialize_dtls_srtp(dialog, dialog->rtp, &dialog->srtp)) {
- return -1;
- }
- if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS) ||
- (ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) && (ast_format_cap_has_type(dialog->caps, AST_MEDIA_TYPE_VIDEO)))) {
- if (!(dialog->vrtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr_tmp, NULL))) {
- return -1;
- }
- if (!ast_test_flag(&dialog->flags[2], SIP_PAGE3_ICE_SUPPORT) && (ice = ast_rtp_instance_get_ice(dialog->vrtp))) {
- ice->stop(dialog->vrtp);
- }
- if (dialog_initialize_dtls_srtp(dialog, dialog->vrtp, &dialog->vsrtp)) {
- return -1;
- }
- ast_rtp_instance_set_timeout(dialog->vrtp, dialog->rtptimeout);
- ast_rtp_instance_set_hold_timeout(dialog->vrtp, dialog->rtpholdtimeout);
- ast_rtp_instance_set_keepalive(dialog->vrtp, dialog->rtpkeepalive);
- ast_rtp_instance_set_prop(dialog->vrtp, AST_RTP_PROPERTY_RTCP, 1);
- ast_rtp_instance_set_qos(dialog->vrtp, global_tos_video, global_cos_video, "SIP VIDEO");
- }
- if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_TEXTSUPPORT)) {
- if (!(dialog->trtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr_tmp, NULL))) {
- return -1;
- }
- if (!ast_test_flag(&dialog->flags[2], SIP_PAGE3_ICE_SUPPORT) && (ice = ast_rtp_instance_get_ice(dialog->trtp))) {
- ice->stop(dialog->trtp);
- }
- if (dialog_initialize_dtls_srtp(dialog, dialog->trtp, &dialog->tsrtp)) {
- return -1;
- }
- /* Do not timeout text as its not constant*/
- ast_rtp_instance_set_keepalive(dialog->trtp, dialog->rtpkeepalive);
- ast_rtp_instance_set_prop(dialog->trtp, AST_RTP_PROPERTY_RTCP, 1);
- }
- ast_rtp_instance_set_timeout(dialog->rtp, dialog->rtptimeout);
- ast_rtp_instance_set_hold_timeout(dialog->rtp, dialog->rtpholdtimeout);
- ast_rtp_instance_set_keepalive(dialog->rtp, dialog->rtpkeepalive);
- ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_RTCP, 1);
- ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
- ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
- ast_rtp_instance_set_qos(dialog->rtp, global_tos_audio, global_cos_audio, "SIP RTP");
- do_setnat(dialog);
- return 0;
- }
- static int __set_address_from_contact(const char *fullcontact, struct ast_sockaddr *addr, int tcp);
- /*! \brief Create address structure from peer reference.
- * This function copies data from peer to the dialog, so we don't have to look up the peer
- * again from memory or database during the life time of the dialog.
- *
- * \return -1 on error, 0 on success.
- *
- */
- static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
- {
- struct sip_auth_container *credentials;
- /* this checks that the dialog is contacting the peer on a valid
- * transport type based on the peers transport configuration,
- * otherwise, this function bails out */
- if (dialog->socket.type && check_request_transport(peer, dialog))
- return -1;
- copy_socket_data(&dialog->socket, &peer->socket);
- if (!(ast_sockaddr_isnull(&peer->addr) && ast_sockaddr_isnull(&peer->defaddr)) &&
- (!peer->maxms || ((peer->lastms >= 0) && (peer->lastms <= peer->maxms)))) {
- dialog->sa = ast_sockaddr_isnull(&peer->addr) ? peer->defaddr : peer->addr;
- dialog->recv = dialog->sa;
- } else
- return -1;
- /* XXX TODO: get flags directly from peer only as they are needed using dialog->relatedpeer */
- ast_copy_flags(&dialog->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
- ast_copy_flags(&dialog->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
- ast_copy_flags(&dialog->flags[2], &peer->flags[2], SIP_PAGE3_FLAGS_TO_COPY);
- /* Take the peer's caps */
- if (peer->caps) {
- ast_format_cap_remove_by_type(dialog->caps, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_append_from_cap(dialog->caps, peer->caps, AST_MEDIA_TYPE_UNKNOWN);
- }
- dialog->amaflags = peer->amaflags;
- ast_string_field_set(dialog, engine, peer->engine);
- ast_rtp_dtls_cfg_copy(&peer->dtls_cfg, &dialog->dtls_cfg);
- dialog->rtptimeout = peer->rtptimeout;
- dialog->rtpholdtimeout = peer->rtpholdtimeout;
- dialog->rtpkeepalive = peer->rtpkeepalive;
- sip_route_copy(&dialog->route, &peer->path);
- if (!sip_route_empty(&dialog->route)) {
- /* Parse SIP URI of first route-set hop and use it as target address */
- __set_address_from_contact(sip_route_first_uri(&dialog->route), &dialog->sa, dialog->socket.type == AST_TRANSPORT_TLS ? 1 : 0);
- }
- if (dialog_initialize_rtp(dialog)) {
- return -1;
- }
- if (dialog->rtp) { /* Audio */
- ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
- ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
- /* Set Frame packetization */
- dialog->autoframing = peer->autoframing;
- ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(dialog->rtp), ast_format_cap_get_framing(dialog->caps));
- }
- /* XXX TODO: get fields directly from peer only as they are needed using dialog->relatedpeer */
- ast_string_field_set(dialog, peername, peer->name);
- ast_string_field_set(dialog, authname, peer->username);
- ast_string_field_set(dialog, username, peer->username);
- ast_string_field_set(dialog, peersecret, peer->secret);
- ast_string_field_set(dialog, peermd5secret, peer->md5secret);
- ast_string_field_set(dialog, mohsuggest, peer->mohsuggest);
- ast_string_field_set(dialog, mohinterpret, peer->mohinterpret);
- ast_string_field_set(dialog, tohost, peer->tohost);
- ast_string_field_set(dialog, fullcontact, peer->fullcontact);
- ast_string_field_set(dialog, accountcode, peer->accountcode);
- ast_string_field_set(dialog, context, peer->context);
- ast_string_field_set(dialog, cid_num, peer->cid_num);
- ast_string_field_set(dialog, cid_name, peer->cid_name);
- ast_string_field_set(dialog, cid_tag, peer->cid_tag);
- ast_string_field_set(dialog, mwi_from, peer->mwi_from);
- if (!ast_strlen_zero(peer->parkinglot)) {
- ast_string_field_set(dialog, parkinglot, peer->parkinglot);
- }
- ast_string_field_set(dialog, engine, peer->engine);
- ref_proxy(dialog, obproxy_get(dialog, peer));
- dialog->callgroup = peer->callgroup;
- dialog->pickupgroup = peer->pickupgroup;
- ast_unref_namedgroups(dialog->named_callgroups);
- dialog->named_callgroups = ast_ref_namedgroups(peer->named_callgroups);
- ast_unref_namedgroups(dialog->named_pickupgroups);
- dialog->named_pickupgroups = ast_ref_namedgroups(peer->named_pickupgroups);
- ast_copy_string(dialog->zone, peer->zone, sizeof(dialog->zone));
- dialog->allowtransfer = peer->allowtransfer;
- dialog->jointnoncodeccapability = dialog->noncodeccapability;
- /* Update dialog authorization credentials */
- ao2_lock(peer);
- credentials = peer->auth;
- if (credentials) {
- ao2_t_ref(credentials, +1, "Ref peer auth for dialog");
- }
- ao2_unlock(peer);
- ao2_lock(dialog);
- if (dialog->peerauth) {
- ao2_t_ref(dialog->peerauth, -1, "Unref old dialog peer auth");
- }
- dialog->peerauth = credentials;
- ao2_unlock(dialog);
- dialog->maxcallbitrate = peer->maxcallbitrate;
- dialog->disallowed_methods = peer->disallowed_methods;
- ast_cc_copy_config_params(dialog->cc_params, peer->cc_params);
- if (ast_strlen_zero(dialog->tohost))
- ast_string_field_set(dialog, tohost, ast_sockaddr_stringify_host_remote(&dialog->sa));
- if (!ast_strlen_zero(peer->fromdomain)) {
- ast_string_field_set(dialog, fromdomain, peer->fromdomain);
- if (!dialog->initreq.headers) {
- char *new_callid;
- char *tmpcall = ast_strdupa(dialog->callid);
- /* this sure looks to me like we are going to change the callid on this dialog!! */
- new_callid = strchr(tmpcall, '@');
- if (new_callid) {
- int callid_size;
- *new_callid = '\0';
- /* Change the dialog callid. */
- callid_size = strlen(tmpcall) + strlen(peer->fromdomain) + 2;
- new_callid = ast_alloca(callid_size);
- snprintf(new_callid, callid_size, "%s@%s", tmpcall, peer->fromdomain);
- change_callid_pvt(dialog, new_callid);
- }
- }
- }
- if (!ast_strlen_zero(peer->fromuser)) {
- ast_string_field_set(dialog, fromuser, peer->fromuser);
- }
- if (!ast_strlen_zero(peer->language)) {
- ast_string_field_set(dialog, language, peer->language);
- }
- /* Set timer T1 to RTT for this peer (if known by qualify=) */
- /* Minimum is settable or default to 100 ms */
- /* If there is a maxms and lastms from a qualify use that over a manual T1
- value. Otherwise, use the peer's T1 value. */
- if (peer->maxms && peer->lastms) {
- dialog->timer_t1 = peer->lastms < global_t1min ? global_t1min : peer->lastms;
- } else {
- dialog->timer_t1 = peer->timer_t1;
- }
- /* Set timer B to control transaction timeouts, the peer setting is the default and overrides
- the known timer */
- if (peer->timer_b) {
- dialog->timer_b = peer->timer_b;
- } else {
- dialog->timer_b = 64 * dialog->timer_t1;
- }
- if ((ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
- (ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
- dialog->noncodeccapability |= AST_RTP_DTMF;
- } else {
- dialog->noncodeccapability &= ~AST_RTP_DTMF;
- }
- dialog->directmediaacl = ast_duplicate_acl_list(peer->directmediaacl);
- if (peer->call_limit) {
- ast_set_flag(&dialog->flags[0], SIP_CALL_LIMIT);
- }
- if (!dialog->portinuri) {
- dialog->portinuri = peer->portinuri;
- }
- dialog->chanvars = copy_vars(peer->chanvars);
- if (peer->fromdomainport) {
- dialog->fromdomainport = peer->fromdomainport;
- }
- dialog->callingpres = peer->callingpres;
- return 0;
- }
- /*! \brief The default sip port for the given transport */
- static inline int default_sip_port(enum ast_transport type)
- {
- return type == AST_TRANSPORT_TLS ? STANDARD_TLS_PORT : STANDARD_SIP_PORT;
- }
- /*! \brief create address structure from device name
- * Or, if peer not found, find it in the global DNS
- * returns TRUE (-1) on failure, FALSE on success */
- static int create_addr(struct sip_pvt *dialog, const char *opeer, struct ast_sockaddr *addr, int newdialog)
- {
- struct sip_peer *peer;
- char *peername, *peername2, *hostn;
- char host[MAXHOSTNAMELEN];
- char service[MAXHOSTNAMELEN];
- int srv_ret = 0;
- int tportno;
- AST_DECLARE_APP_ARGS(hostport,
- AST_APP_ARG(host);
- AST_APP_ARG(port);
- );
- peername = ast_strdupa(opeer);
- peername2 = ast_strdupa(opeer);
- AST_NONSTANDARD_RAW_ARGS(hostport, peername2, ':');
- if (hostport.port)
- dialog->portinuri = 1;
- dialog->timer_t1 = global_t1; /* Default SIP retransmission timer T1 (RFC 3261) */
- dialog->timer_b = global_timer_b; /* Default SIP transaction timer B (RFC 3261) */
- peer = sip_find_peer(peername, NULL, TRUE, FINDPEERS, FALSE, 0);
- if (peer) {
- int res;
- if (newdialog) {
- set_socket_transport(&dialog->socket, 0);
- }
- res = create_addr_from_peer(dialog, peer);
- dialog->relatedpeer = sip_ref_peer(peer, "create_addr: setting dialog's relatedpeer pointer");
- sip_unref_peer(peer, "create_addr: unref peer from sip_find_peer hashtab lookup");
- return res;
- } else if (ast_check_digits(peername)) {
- /* Although an IPv4 hostname *could* be represented as a 32-bit integer, it is uncommon and
- * it makes dialing SIP/${EXTEN} for a peer that isn't defined resolve to an IP that is
- * almost certainly not intended. It is much better to just reject purely numeric hostnames */
- ast_log(LOG_WARNING, "Purely numeric hostname (%s), and not a peer--rejecting!\n", peername);
- return -1;
- } else {
- dialog->rtptimeout = global_rtptimeout;
- dialog->rtpholdtimeout = global_rtpholdtimeout;
- dialog->rtpkeepalive = global_rtpkeepalive;
- if (dialog_initialize_rtp(dialog)) {
- return -1;
- }
- }
- ast_string_field_set(dialog, tohost, hostport.host);
- dialog->allowed_methods &= ~sip_cfg.disallowed_methods;
- /* Get the outbound proxy information */
- ref_proxy(dialog, obproxy_get(dialog, NULL));
- if (addr) {
- /* This address should be updated using dnsmgr */
- ast_sockaddr_copy(&dialog->sa, addr);
- } else {
- /* Let's see if we can find the host in DNS. First try DNS SRV records,
- then hostname lookup */
- /*! \todo Fix this function. When we ask for SRV, we should check all transports
- In the future, we should first check NAPTR to find out transport preference
- */
- hostn = peername;
- /* Section 4.2 of RFC 3263 specifies that if a port number is specified, then
- * an A record lookup should be used instead of SRV.
- */
- if (!hostport.port && sip_cfg.srvlookup) {
- snprintf(service, sizeof(service), "_%s._%s.%s",
- get_srv_service(dialog->socket.type),
- get_srv_protocol(dialog->socket.type), peername);
- if ((srv_ret = ast_get_srv(NULL, host, sizeof(host), &tportno,
- service)) > 0) {
- hostn = host;
- }
- }
- if (ast_sockaddr_resolve_first_transport(&dialog->sa, hostn, 0, dialog->socket.type ? dialog->socket.type : AST_TRANSPORT_UDP)) {
- ast_log(LOG_WARNING, "No such host: %s\n", peername);
- return -1;
- }
- if (srv_ret > 0) {
- ast_sockaddr_set_port(&dialog->sa, tportno);
- }
- }
- if (!dialog->socket.type)
- set_socket_transport(&dialog->socket, AST_TRANSPORT_UDP);
- if (!dialog->socket.port) {
- dialog->socket.port = htons(ast_sockaddr_port(&bindaddr));
- }
- if (!ast_sockaddr_port(&dialog->sa)) {
- ast_sockaddr_set_port(&dialog->sa, default_sip_port(dialog->socket.type));
- }
- ast_sockaddr_copy(&dialog->recv, &dialog->sa);
- return 0;
- }
- /*! \brief Scheduled congestion on a call.
- * Only called by the scheduler, must return the reference when done.
- */
- static int auto_congest(const void *arg)
- {
- struct sip_pvt *p = (struct sip_pvt *)arg;
- sip_pvt_lock(p);
- p->initid = -1; /* event gone, will not be rescheduled */
- if (p->owner) {
- /* XXX fails on possible deadlock */
- if (!ast_channel_trylock(p->owner)) {
- append_history(p, "Cong", "Auto-congesting (timer)");
- ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
- ast_channel_unlock(p->owner);
- }
- /* Give the channel a chance to act before we proceed with destruction */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- }
- sip_pvt_unlock(p);
- dialog_unref(p, "unreffing arg passed into auto_congest callback (p->initid)");
- return 0;
- }
- /*! \brief Initiate SIP call from PBX
- * used from the dial() application */
- static int sip_call(struct ast_channel *ast, const char *dest, int timeout)
- {
- int res;
- struct sip_pvt *p = ast_channel_tech_pvt(ast); /* chan is locked, so the reference cannot go away */
- struct varshead *headp;
- struct ast_var_t *current;
- const char *referer = NULL; /* SIP referrer */
- int cc_core_id;
- char uri[SIPBUFSIZE] = "";
- if ((ast_channel_state(ast) != AST_STATE_DOWN) && (ast_channel_state(ast) != AST_STATE_RESERVED)) {
- ast_log(LOG_WARNING, "sip_call called on %s, neither down nor reserved\n", ast_channel_name(ast));
- return -1;
- }
- if (ast_cc_is_recall(ast, &cc_core_id, "SIP")) {
- char device_name[AST_CHANNEL_NAME];
- struct ast_cc_monitor *recall_monitor;
- struct sip_monitor_instance *monitor_instance;
- ast_channel_get_device_name(ast, device_name, sizeof(device_name));
- if ((recall_monitor = ast_cc_get_monitor_by_recall_core_id(cc_core_id, device_name))) {
- monitor_instance = recall_monitor->private_data;
- ast_copy_string(uri, monitor_instance->notify_uri, sizeof(uri));
- ao2_t_ref(recall_monitor, -1, "Got the URI we need so unreffing monitor");
- }
- }
- /* Check whether there is vxml_url, distinctive ring variables */
- headp = ast_channel_varshead(ast);
- AST_LIST_TRAVERSE(headp, current, entries) {
- /* Check whether there is a VXML_URL variable */
- if (!p->options->vxml_url && !strcmp(ast_var_name(current), "VXML_URL")) {
- p->options->vxml_url = ast_var_value(current);
- } else if (!p->options->uri_options && !strcmp(ast_var_name(current), "SIP_URI_OPTIONS")) {
- p->options->uri_options = ast_var_value(current);
- } else if (!p->options->addsipheaders && !strncmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) {
- /* Check whether there is a variable with a name starting with SIPADDHEADER */
- p->options->addsipheaders = 1;
- } else if (!strcmp(ast_var_name(current), "SIPFROMDOMAIN")) {
- ast_string_field_set(p, fromdomain, ast_var_value(current));
- } else if (!strcmp(ast_var_name(current), "SIPTRANSFER")) {
- /* This is a transferred call */
- p->options->transfer = 1;
- } else if (!strcmp(ast_var_name(current), "SIPTRANSFER_REFERER")) {
- /* This is the referrer */
- referer = ast_var_value(current);
- } else if (!strcmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) {
- /* We're replacing a call. */
- p->options->replaces = ast_var_value(current);
- } else if (!strcmp(ast_var_name(current), "SIP_MAX_FORWARDS")) {
- if (sscanf(ast_var_value(current), "%30d", &(p->maxforwards)) != 1) {
- ast_log(LOG_WARNING, "The SIP_MAX_FORWARDS channel variable is not a valid integer.\n");
- }
- }
- }
- /* Check to see if we should try to force encryption */
- if (p->req_secure_signaling && p->socket.type != AST_TRANSPORT_TLS) {
- ast_log(LOG_WARNING, "Encrypted signaling is required\n");
- ast_channel_hangupcause_set(ast, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
- return -1;
- }
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_USE_SRTP)) {
- if (ast_test_flag(&p->flags[0], SIP_REINVITE)) {
- ast_debug(1, "Direct media not possible when using SRTP, ignoring canreinvite setting\n");
- ast_clear_flag(&p->flags[0], SIP_REINVITE);
- }
- if (p->rtp && !p->srtp && !(p->srtp = ast_sdp_srtp_alloc())) {
- ast_log(LOG_WARNING, "SRTP audio setup failed\n");
- return -1;
- }
- if (p->vrtp && !p->vsrtp && !(p->vsrtp = ast_sdp_srtp_alloc())) {
- ast_log(LOG_WARNING, "SRTP video setup failed\n");
- return -1;
- }
- if (p->trtp && !p->tsrtp && !(p->tsrtp = ast_sdp_srtp_alloc())) {
- ast_log(LOG_WARNING, "SRTP text setup failed\n");
- return -1;
- }
- }
- res = 0;
- ast_set_flag(&p->flags[0], SIP_OUTGOING);
- /* T.38 re-INVITE FAX detection should never be done for outgoing calls,
- * so ensure it is disabled.
- */
- ast_clear_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_T38);
- if (p->options->transfer) {
- char buf[SIPBUFSIZE / 2];
- if (referer) {
- if (sipdebug)
- ast_debug(3, "Call for %s transferred by %s\n", p->username, referer);
- snprintf(buf, sizeof(buf)-1, "-> %s (via %s)", p->cid_name, referer);
- } else
- snprintf(buf, sizeof(buf)-1, "-> %s", p->cid_name);
- ast_string_field_set(p, cid_name, buf);
- }
- ast_debug(1, "Outgoing Call for %s\n", p->username);
- res = update_call_counter(p, INC_CALL_RINGING);
- if (res == -1) {
- ast_channel_hangupcause_set(ast, AST_CAUSE_USER_BUSY);
- return res;
- }
- p->callingpres = ast_party_id_presentation(&ast_channel_caller(ast)->id);
- ast_rtp_instance_available_formats(p->rtp, p->caps, p->prefcaps, p->jointcaps);
- p->jointnoncodeccapability = p->noncodeccapability;
- /* If there are no audio formats left to offer, punt */
- if (!(ast_format_cap_has_type(p->jointcaps, AST_MEDIA_TYPE_AUDIO))) {
- ast_log(LOG_WARNING, "No audio format found to offer. Cancelling call to %s\n", p->username);
- res = -1;
- } else {
- int xmitres;
- struct ast_party_connected_line connected;
- struct ast_set_party_connected_line update_connected;
- sip_pvt_lock(p);
- /* Supply initial connected line information if available. */
- memset(&update_connected, 0, sizeof(update_connected));
- ast_party_connected_line_init(&connected);
- if (!ast_strlen_zero(p->cid_num)
- || (p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) {
- update_connected.id.number = 1;
- connected.id.number.valid = 1;
- connected.id.number.str = (char *) p->cid_num;
- connected.id.number.presentation = p->callingpres;
- }
- if (!ast_strlen_zero(p->cid_name)
- || (p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) {
- update_connected.id.name = 1;
- connected.id.name.valid = 1;
- connected.id.name.str = (char *) p->cid_name;
- connected.id.name.presentation = p->callingpres;
- }
- if (update_connected.id.number || update_connected.id.name) {
- /* Invalidate any earlier private connected id representation */
- ast_set_party_id_all(&update_connected.priv);
- connected.id.tag = (char *) p->cid_tag;
- connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
- ast_channel_queue_connected_line_update(ast, &connected, &update_connected);
- }
- xmitres = transmit_invite(p, SIP_INVITE, 1, 2, uri);
- if (xmitres == XMIT_ERROR) {
- sip_pvt_unlock(p);
- return -1;
- }
- p->invitestate = INV_CALLING;
- /* Initialize auto-congest time */
- AST_SCHED_REPLACE_UNREF(p->initid, sched, p->timer_b, auto_congest, p,
- dialog_unref(_data, "dialog ptr dec when SCHED_REPLACE del op succeeded"),
- dialog_unref(p, "dialog ptr dec when SCHED_REPLACE add failed"),
- dialog_ref(p, "dialog ptr inc when SCHED_REPLACE add succeeded") );
- sip_pvt_unlock(p);
- }
- return res;
- }
- /*! \brief Destroy registry object
- Objects created with the register= statement in static configuration */
- static void sip_registry_destroy(void *obj)
- {
- struct sip_registry *reg = obj;
- /* Really delete */
- ast_debug(3, "Destroying registry entry for %s@%s\n", reg->username, reg->hostname);
- if (reg->call) {
- /* Clear registry before destroying to ensure
- we don't get reentered trying to grab the registry lock */
- ao2_t_replace(reg->call->registry, NULL, "destroy reg->call->registry");
- ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", reg->username, reg->hostname);
- dialog_unlink_all(reg->call);
- reg->call = dialog_unref(reg->call, "unref reg->call");
- /* reg->call = sip_destroy(reg->call); */
- }
- AST_SCHED_DEL(sched, reg->expire);
- AST_SCHED_DEL(sched, reg->timeout);
- ast_string_field_free_memory(reg);
- }
- /*! \brief Destroy MWI subscription object */
- static void sip_subscribe_mwi_destroy(void *data)
- {
- struct sip_subscription_mwi *mwi = data;
- if (mwi->call) {
- mwi->call->mwi = NULL;
- mwi->call = dialog_unref(mwi->call, "sip_subscription_mwi destruction");
- }
- AST_SCHED_DEL(sched, mwi->resub);
- ast_string_field_free_memory(mwi);
- }
- /*! \brief Destroy SDP media offer list */
- static void offered_media_list_destroy(struct sip_pvt *p)
- {
- struct offered_media *offer;
- while ((offer = AST_LIST_REMOVE_HEAD(&p->offered_media, next))) {
- ast_free(offer->decline_m_line);
- ast_free(offer);
- }
- }
- /*! \brief Execute destruction of SIP dialog structure, release memory */
- void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
- {
- struct sip_request *req;
- /* Destroy Session-Timers if allocated */
- if (p->stimer) {
- p->stimer->quit_flag = 1;
- stop_session_timer(p);
- ast_free(p->stimer);
- p->stimer = NULL;
- }
- if (sip_debug_test_pvt(p))
- ast_verbose("Really destroying SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
- if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
- update_call_counter(p, DEC_CALL_LIMIT);
- ast_debug(2, "This call did not properly clean up call limits. Call ID %s\n", p->callid);
- }
- /* Unlink us from the owner if we have one */
- if (p->owner) {
- if (lockowner)
- ast_channel_lock(p->owner);
- ast_debug(1, "Detaching from %s\n", ast_channel_name(p->owner));
- ast_channel_tech_pvt_set(p->owner, NULL);
- /* Make sure that the channel knows its backend is going away */
- ast_channel_softhangup_internal_flag_add(p->owner, AST_SOFTHANGUP_DEV);
- if (lockowner)
- ast_channel_unlock(p->owner);
- /* Give the channel a chance to react before deallocation */
- usleep(1);
- }
- /* Remove link from peer to subscription of MWI */
- if (p->relatedpeer && p->relatedpeer->mwipvt == p)
- p->relatedpeer->mwipvt = dialog_unref(p->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt");
- if (p->relatedpeer && p->relatedpeer->call == p)
- p->relatedpeer->call = dialog_unref(p->relatedpeer->call, "unset the relatedpeer->call field in tandem with relatedpeer field itself");
- if (p->relatedpeer)
- p->relatedpeer = sip_unref_peer(p->relatedpeer,"unsetting a dialog relatedpeer field in sip_destroy");
- if (p->registry) {
- if (p->registry->call == p)
- p->registry->call = dialog_unref(p->registry->call, "nulling out the registry's call dialog field in unlink_all");
- ao2_t_replace(p->registry, NULL, "delete p->registry");
- }
- if (p->mwi) {
- p->mwi->call = NULL;
- p->mwi = NULL;
- }
- if (dumphistory)
- sip_dump_history(p);
- if (p->options) {
- if (p->options->outboundproxy) {
- ao2_ref(p->options->outboundproxy, -1);
- }
- ast_free(p->options);
- p->options = NULL;
- }
- if (p->outboundproxy) {
- ref_proxy(p, NULL);
- }
- if (p->notify) {
- ast_variables_destroy(p->notify->headers);
- ast_free(p->notify->content);
- ast_free(p->notify);
- p->notify = NULL;
- }
- if (p->rtp) {
- ast_rtp_instance_destroy(p->rtp);
- p->rtp = NULL;
- }
- if (p->vrtp) {
- ast_rtp_instance_destroy(p->vrtp);
- p->vrtp = NULL;
- }
- if (p->trtp) {
- ast_rtp_instance_destroy(p->trtp);
- p->trtp = NULL;
- }
- if (p->udptl) {
- ast_udptl_destroy(p->udptl);
- p->udptl = NULL;
- }
- sip_refer_destroy(p);
- sip_route_clear(&p->route);
- deinit_req(&p->initreq);
- /* Clear history */
- if (p->history) {
- struct sip_history *hist;
- while ( (hist = AST_LIST_REMOVE_HEAD(p->history, list)) ) {
- ast_free(hist);
- p->history_entries--;
- }
- ast_free(p->history);
- p->history = NULL;
- }
- while ((req = AST_LIST_REMOVE_HEAD(&p->request_queue, next))) {
- ast_free(req);
- }
- offered_media_list_destroy(p);
- if (p->chanvars) {
- ast_variables_destroy(p->chanvars);
- p->chanvars = NULL;
- }
- destroy_msg_headers(p);
- if (p->srtp) {
- ast_sdp_srtp_destroy(p->srtp);
- p->srtp = NULL;
- }
- if (p->vsrtp) {
- ast_sdp_srtp_destroy(p->vsrtp);
- p->vsrtp = NULL;
- }
- if (p->tsrtp) {
- ast_sdp_srtp_destroy(p->tsrtp);
- p->tsrtp = NULL;
- }
- if (p->directmediaacl) {
- p->directmediaacl = ast_free_acl_list(p->directmediaacl);
- }
- ast_string_field_free_memory(p);
- ast_cc_config_params_destroy(p->cc_params);
- p->cc_params = NULL;
- if (p->epa_entry) {
- ao2_ref(p->epa_entry, -1);
- p->epa_entry = NULL;
- }
- if (p->socket.tcptls_session) {
- ao2_ref(p->socket.tcptls_session, -1);
- p->socket.tcptls_session = NULL;
- } else if (p->socket.ws_session) {
- ast_websocket_unref(p->socket.ws_session);
- p->socket.ws_session = NULL;
- }
- if (p->peerauth) {
- ao2_t_ref(p->peerauth, -1, "Removing active peer authentication");
- p->peerauth = NULL;
- }
- p->named_callgroups = ast_unref_namedgroups(p->named_callgroups);
- p->named_pickupgroups = ast_unref_namedgroups(p->named_pickupgroups);
- ao2_cleanup(p->caps);
- ao2_cleanup(p->jointcaps);
- ao2_cleanup(p->peercaps);
- ao2_cleanup(p->redircaps);
- ao2_cleanup(p->prefcaps);
- ast_rtp_dtls_cfg_free(&p->dtls_cfg);
- if (p->last_device_state_info) {
- ao2_ref(p->last_device_state_info, -1);
- p->last_device_state_info = NULL;
- }
- }
- /*! \brief update_call_counter: Handle call_limit for SIP devices
- * Setting a call-limit will cause calls above the limit not to be accepted.
- *
- * Remember that for a type=friend, there's one limit for the user and
- * another for the peer, not a combined call limit.
- * This will cause unexpected behaviour in subscriptions, since a "friend"
- * is *two* devices in Asterisk, not one.
- *
- * Thought: For realtime, we should probably update storage with inuse counter...
- *
- * \return 0 if call is ok (no call limit, below threshold)
- * -1 on rejection of call
- *
- */
- static int update_call_counter(struct sip_pvt *fup, int event)
- {
- char name[256];
- int *inuse = NULL, *call_limit = NULL, *ringing = NULL;
- int outgoing = fup->outgoing_call;
- struct sip_peer *p = NULL;
- ast_debug(3, "Updating call counter for %s call\n", outgoing ? "outgoing" : "incoming");
- /* Test if we need to check call limits, in order to avoid
- realtime lookups if we do not need it */
- if (!ast_test_flag(&fup->flags[0], SIP_CALL_LIMIT) && !ast_test_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD))
- return 0;
- ast_copy_string(name, fup->username, sizeof(name));
- /* Check the list of devices */
- if (fup->relatedpeer) {
- p = sip_ref_peer(fup->relatedpeer, "ref related peer for update_call_counter");
- inuse = &p->inuse;
- call_limit = &p->call_limit;
- ringing = &p->ringing;
- ast_copy_string(name, fup->peername, sizeof(name));
- }
- if (!p) {
- ast_debug(2, "%s is not a local device, no call limit\n", name);
- return 0;
- }
- switch(event) {
- /* incoming and outgoing affects the inuse counter */
- case DEC_CALL_LIMIT:
- /* Decrement inuse count if applicable */
- if (inuse) {
- sip_pvt_lock(fup);
- ao2_lock(p);
- if (*inuse > 0) {
- if (ast_test_flag(&fup->flags[0], SIP_INC_COUNT)) {
- (*inuse)--;
- ast_clear_flag(&fup->flags[0], SIP_INC_COUNT);
- }
- } else {
- *inuse = 0;
- }
- ao2_unlock(p);
- sip_pvt_unlock(fup);
- }
- /* Decrement ringing count if applicable */
- if (ringing) {
- sip_pvt_lock(fup);
- ao2_lock(p);
- if (*ringing > 0) {
- if (ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
- (*ringing)--;
- ast_clear_flag(&fup->flags[0], SIP_INC_RINGING);
- }
- } else {
- *ringing = 0;
- }
- ao2_unlock(p);
- sip_pvt_unlock(fup);
- }
- /* Decrement onhold count if applicable */
- sip_pvt_lock(fup);
- ao2_lock(p);
- if (ast_test_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD) && sip_cfg.notifyhold) {
- ast_clear_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD);
- ao2_unlock(p);
- sip_pvt_unlock(fup);
- sip_peer_hold(fup, FALSE);
- } else {
- ao2_unlock(p);
- sip_pvt_unlock(fup);
- }
- if (sipdebug)
- ast_debug(2, "Call %s %s '%s' removed from call limit %d\n", outgoing ? "to" : "from", "peer", name, *call_limit);
- break;
- case INC_CALL_RINGING:
- case INC_CALL_LIMIT:
- /* If call limit is active and we have reached the limit, reject the call */
- if (*call_limit > 0 ) {
- if (*inuse >= *call_limit) {
- ast_log(LOG_NOTICE, "Call %s %s '%s' rejected due to usage limit of %d\n", outgoing ? "to" : "from", "peer", name, *call_limit);
- sip_unref_peer(p, "update_call_counter: unref peer p, call limit exceeded");
- return -1;
- }
- }
- if (ringing && (event == INC_CALL_RINGING)) {
- sip_pvt_lock(fup);
- ao2_lock(p);
- if (!ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
- (*ringing)++;
- ast_set_flag(&fup->flags[0], SIP_INC_RINGING);
- }
- ao2_unlock(p);
- sip_pvt_unlock(fup);
- }
- if (inuse) {
- sip_pvt_lock(fup);
- ao2_lock(p);
- if (!ast_test_flag(&fup->flags[0], SIP_INC_COUNT)) {
- (*inuse)++;
- ast_set_flag(&fup->flags[0], SIP_INC_COUNT);
- }
- ao2_unlock(p);
- sip_pvt_unlock(fup);
- }
- if (sipdebug) {
- ast_debug(2, "Call %s %s '%s' is %d out of %d\n", outgoing ? "to" : "from", "peer", name, *inuse, *call_limit);
- }
- break;
- case DEC_CALL_RINGING:
- if (ringing) {
- sip_pvt_lock(fup);
- ao2_lock(p);
- if (ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
- if (*ringing > 0) {
- (*ringing)--;
- }
- ast_clear_flag(&fup->flags[0], SIP_INC_RINGING);
- }
- ao2_unlock(p);
- sip_pvt_unlock(fup);
- }
- break;
- default:
- ast_log(LOG_ERROR, "update_call_counter(%s, %d) called with no event!\n", name, event);
- }
- if (p) {
- ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", p->name);
- sip_unref_peer(p, "update_call_counter: sip_unref_peer from call counter");
- }
- return 0;
- }
- static void sip_destroy_fn(void *p)
- {
- sip_destroy(p);
- }
- /*! \brief Destroy SIP call structure.
- * Make it return NULL so the caller can do things like
- * foo = sip_destroy(foo);
- * and reduce the chance of bugs due to dangling pointers.
- */
- struct sip_pvt *sip_destroy(struct sip_pvt *p)
- {
- ast_debug(3, "Destroying SIP dialog %s\n", p->callid);
- __sip_destroy(p, TRUE, TRUE);
- return NULL;
- }
- /*! \brief Convert SIP hangup causes to Asterisk hangup causes */
- int hangup_sip2cause(int cause)
- {
- /* Possible values taken from causes.h */
- switch(cause) {
- case 401: /* Unauthorized */
- return AST_CAUSE_CALL_REJECTED;
- case 403: /* Not found */
- return AST_CAUSE_CALL_REJECTED;
- case 404: /* Not found */
- return AST_CAUSE_UNALLOCATED;
- case 405: /* Method not allowed */
- return AST_CAUSE_INTERWORKING;
- case 407: /* Proxy authentication required */
- return AST_CAUSE_CALL_REJECTED;
- case 408: /* No reaction */
- return AST_CAUSE_NO_USER_RESPONSE;
- case 409: /* Conflict */
- return AST_CAUSE_NORMAL_TEMPORARY_FAILURE;
- case 410: /* Gone */
- return AST_CAUSE_NUMBER_CHANGED;
- case 411: /* Length required */
- return AST_CAUSE_INTERWORKING;
- case 413: /* Request entity too large */
- return AST_CAUSE_INTERWORKING;
- case 414: /* Request URI too large */
- return AST_CAUSE_INTERWORKING;
- case 415: /* Unsupported media type */
- return AST_CAUSE_INTERWORKING;
- case 420: /* Bad extension */
- return AST_CAUSE_NO_ROUTE_DESTINATION;
- case 480: /* No answer */
- return AST_CAUSE_NO_ANSWER;
- case 481: /* No answer */
- return AST_CAUSE_INTERWORKING;
- case 482: /* Loop detected */
- return AST_CAUSE_INTERWORKING;
- case 483: /* Too many hops */
- return AST_CAUSE_NO_ANSWER;
- case 484: /* Address incomplete */
- return AST_CAUSE_INVALID_NUMBER_FORMAT;
- case 485: /* Ambiguous */
- return AST_CAUSE_UNALLOCATED;
- case 486: /* Busy everywhere */
- return AST_CAUSE_BUSY;
- case 487: /* Request terminated */
- return AST_CAUSE_INTERWORKING;
- case 488: /* No codecs approved */
- return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
- case 491: /* Request pending */
- return AST_CAUSE_INTERWORKING;
- case 493: /* Undecipherable */
- return AST_CAUSE_INTERWORKING;
- case 500: /* Server internal failure */
- return AST_CAUSE_FAILURE;
- case 501: /* Call rejected */
- return AST_CAUSE_FACILITY_REJECTED;
- case 502:
- return AST_CAUSE_DESTINATION_OUT_OF_ORDER;
- case 503: /* Service unavailable */
- return AST_CAUSE_CONGESTION;
- case 504: /* Gateway timeout */
- return AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE;
- case 505: /* SIP version not supported */
- return AST_CAUSE_INTERWORKING;
- case 600: /* Busy everywhere */
- return AST_CAUSE_USER_BUSY;
- case 603: /* Decline */
- return AST_CAUSE_CALL_REJECTED;
- case 604: /* Does not exist anywhere */
- return AST_CAUSE_UNALLOCATED;
- case 606: /* Not acceptable */
- return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
- default:
- if (cause < 500 && cause >= 400) {
- /* 4xx class error that is unknown - someting wrong with our request */
- return AST_CAUSE_INTERWORKING;
- } else if (cause < 600 && cause >= 500) {
- /* 5xx class error - problem in the remote end */
- return AST_CAUSE_CONGESTION;
- } else if (cause < 700 && cause >= 600) {
- /* 6xx - global errors in the 4xx class */
- return AST_CAUSE_INTERWORKING;
- }
- return AST_CAUSE_NORMAL;
- }
- /* Never reached */
- return 0;
- }
- /*! \brief Convert Asterisk hangup causes to SIP codes
- \verbatim
- Possible values from causes.h
- AST_CAUSE_NOTDEFINED AST_CAUSE_NORMAL AST_CAUSE_BUSY
- AST_CAUSE_FAILURE AST_CAUSE_CONGESTION AST_CAUSE_UNALLOCATED
- In addition to these, a lot of PRI codes is defined in causes.h
- ...should we take care of them too ?
- Quote RFC 3398
- ISUP Cause value SIP response
- ---------------- ------------
- 1 unallocated number 404 Not Found
- 2 no route to network 404 Not found
- 3 no route to destination 404 Not found
- 16 normal call clearing --- (*)
- 17 user busy 486 Busy here
- 18 no user responding 408 Request Timeout
- 19 no answer from the user 480 Temporarily unavailable
- 20 subscriber absent 480 Temporarily unavailable
- 21 call rejected 403 Forbidden (+)
- 22 number changed (w/o diagnostic) 410 Gone
- 22 number changed (w/ diagnostic) 301 Moved Permanently
- 23 redirection to new destination 410 Gone
- 26 non-selected user clearing 404 Not Found (=)
- 27 destination out of order 502 Bad Gateway
- 28 address incomplete 484 Address incomplete
- 29 facility rejected 501 Not implemented
- 31 normal unspecified 480 Temporarily unavailable
- \endverbatim
- */
- const char *hangup_cause2sip(int cause)
- {
- switch (cause) {
- case AST_CAUSE_UNALLOCATED: /* 1 */
- case AST_CAUSE_NO_ROUTE_DESTINATION: /* 3 IAX2: Can't find extension in context */
- case AST_CAUSE_NO_ROUTE_TRANSIT_NET: /* 2 */
- return "404 Not Found";
- case AST_CAUSE_CONGESTION: /* 34 */
- case AST_CAUSE_SWITCH_CONGESTION: /* 42 */
- return "503 Service Unavailable";
- case AST_CAUSE_NO_USER_RESPONSE: /* 18 */
- return "408 Request Timeout";
- case AST_CAUSE_NO_ANSWER: /* 19 */
- case AST_CAUSE_UNREGISTERED: /* 20 */
- return "480 Temporarily unavailable";
- case AST_CAUSE_CALL_REJECTED: /* 21 */
- return "403 Forbidden";
- case AST_CAUSE_NUMBER_CHANGED: /* 22 */
- return "410 Gone";
- case AST_CAUSE_NORMAL_UNSPECIFIED: /* 31 */
- return "480 Temporarily unavailable";
- case AST_CAUSE_INVALID_NUMBER_FORMAT:
- return "484 Address incomplete";
- case AST_CAUSE_USER_BUSY:
- return "486 Busy here";
- case AST_CAUSE_FAILURE:
- return "500 Server internal failure";
- case AST_CAUSE_FACILITY_REJECTED: /* 29 */
- return "501 Not Implemented";
- case AST_CAUSE_CHAN_NOT_IMPLEMENTED:
- return "503 Service Unavailable";
- /* Used in chan_iax2 */
- case AST_CAUSE_DESTINATION_OUT_OF_ORDER:
- return "502 Bad Gateway";
- case AST_CAUSE_BEARERCAPABILITY_NOTAVAIL: /* Can't find codec to connect to host */
- return "488 Not Acceptable Here";
- case AST_CAUSE_INTERWORKING: /* Unspecified Interworking issues */
- return "500 Network error";
- case AST_CAUSE_NOTDEFINED:
- default:
- ast_debug(1, "AST hangup cause %d (no match found in SIP)\n", cause);
- return NULL;
- }
- /* Never reached */
- return 0;
- }
- static int reinvite_timeout(const void *data)
- {
- struct sip_pvt *dialog = (struct sip_pvt *) data;
- struct ast_channel *owner = sip_pvt_lock_full(dialog);
- dialog->reinviteid = -1;
- check_pendings(dialog);
- if (owner) {
- ast_channel_unlock(owner);
- ast_channel_unref(owner);
- }
- ao2_unlock(dialog);
- dialog_unref(dialog, "unref for reinvite timeout");
- return 0;
- }
- /*! \brief sip_hangup: Hangup SIP call
- * Part of PBX interface, called from ast_hangup */
- static int sip_hangup(struct ast_channel *ast)
- {
- struct sip_pvt *p = ast_channel_tech_pvt(ast);
- int needcancel = FALSE;
- int needdestroy = 0;
- struct ast_channel *oldowner = ast;
- if (!p) {
- ast_debug(1, "Asked to hangup channel that was not connected\n");
- return 0;
- }
- if (ast_channel_hangupcause(ast) == AST_CAUSE_ANSWERED_ELSEWHERE) {
- ast_debug(1, "This call was answered elsewhere\n");
- append_history(p, "Cancel", "Call answered elsewhere");
- p->answered_elsewhere = TRUE;
- }
- /* Store hangupcause locally in PVT so we still have it before disconnect */
- if (p->owner)
- p->hangupcause = ast_channel_hangupcause(p->owner);
- if (ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) {
- if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
- if (sipdebug)
- ast_debug(1, "update_call_counter(%s) - decrement call limit counter on hangup\n", p->username);
- update_call_counter(p, DEC_CALL_LIMIT);
- }
- ast_debug(4, "SIP Transfer: Not hanging up right now... Rescheduling hangup for %s.\n", p->callid);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Really hang up next time */
- if (p->owner) {
- sip_pvt_lock(p);
- oldowner = p->owner;
- sip_set_owner(p, NULL); /* Owner will be gone after we return, so take it away */
- sip_pvt_unlock(p);
- ast_channel_tech_pvt_set(oldowner, dialog_unref(ast_channel_tech_pvt(oldowner), "unref oldowner->tech_pvt"));
- }
- ast_module_unref(ast_module_info->self);
- return 0;
- }
- ast_debug(1, "Hangup call %s, SIP callid %s\n", ast_channel_name(ast), p->callid);
- sip_pvt_lock(p);
- if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
- if (sipdebug)
- ast_debug(1, "update_call_counter(%s) - decrement call limit counter on hangup\n", p->username);
- update_call_counter(p, DEC_CALL_LIMIT);
- }
- /* Determine how to disconnect */
- if (p->owner != ast) {
- ast_log(LOG_WARNING, "Huh? We aren't the owner? Can't hangup call.\n");
- sip_pvt_unlock(p);
- return 0;
- }
- /* If the call is not UP, we need to send CANCEL instead of BYE */
- /* In case of re-invites, the call might be UP even though we have an incomplete invite transaction */
- if (p->invitestate < INV_COMPLETED && ast_channel_state(p->owner) != AST_STATE_UP) {
- needcancel = TRUE;
- ast_debug(4, "Hanging up channel in state %s (not UP)\n", ast_state2str(ast_channel_state(ast)));
- }
- stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
- append_history(p, needcancel ? "Cancel" : "Hangup", "Cause %s", ast_cause2str(p->hangupcause));
- /* Disconnect */
- disable_dsp_detect(p);
- sip_set_owner(p, NULL);
- ast_channel_tech_pvt_set(ast, NULL);
- ast_module_unref(ast_module_info->self);
- /* Do not destroy this pvt until we have timeout or
- get an answer to the BYE or INVITE/CANCEL
- If we get no answer during retransmit period, drop the call anyway.
- (Sorry, mother-in-law, you can't deny a hangup by sending
- 603 declined to BYE...)
- */
- if (p->alreadygone)
- needdestroy = 1; /* Set destroy flag at end of this function */
- else if (p->invitestate != INV_CALLING)
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- /* Start the process if it's not already started */
- if (!p->alreadygone && p->initreq.data && ast_str_strlen(p->initreq.data)) {
- if (needcancel) { /* Outgoing call, not up */
- if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- /* if we can't send right now, mark it pending */
- if (p->invitestate == INV_CALLING) {
- /* We can't send anything in CALLING state */
- ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
- /* Do we need a timer here if we don't hear from them at all? Yes we do or else we will get hung dialogs and those are no fun. */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- append_history(p, "DELAY", "Not sending cancel, waiting for timeout");
- } else {
- struct sip_pkt *cur;
- for (cur = p->packets; cur; cur = cur->next) {
- __sip_semi_ack(p, cur->seqno, cur->is_resp, cur->method ? cur->method : find_sip_method(ast_str_buffer(cur->data)));
- }
- p->invitestate = INV_CANCELLED;
- /* Send a new request: CANCEL */
- transmit_request(p, SIP_CANCEL, p->lastinvite, XMIT_RELIABLE, FALSE);
- /* Actually don't destroy us yet, wait for the 487 on our original
- INVITE, but do set an autodestruct just in case we never get it. */
- needdestroy = 0;
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- }
- } else { /* Incoming call, not up */
- const char *res;
- AST_SCHED_DEL_UNREF(sched, p->provisional_keepalive_sched_id, dialog_unref(p, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
- if (p->hangupcause && (res = hangup_cause2sip(p->hangupcause)))
- transmit_response_reliable(p, res, &p->initreq);
- else
- transmit_response_reliable(p, "603 Declined", &p->initreq);
- p->invitestate = INV_TERMINATED;
- }
- } else { /* Call is in UP state, send BYE */
- if (p->stimer->st_active == TRUE) {
- stop_session_timer(p);
- }
- if (!p->pendinginvite) {
- char *quality;
- char quality_buf[AST_MAX_USER_FIELD];
- if (p->rtp) {
- struct ast_rtp_instance *p_rtp;
- p_rtp = p->rtp;
- ao2_ref(p_rtp, +1);
- ast_channel_unlock(oldowner);
- sip_pvt_unlock(p);
- ast_rtp_instance_set_stats_vars(oldowner, p_rtp);
- ao2_ref(p_rtp, -1);
- ast_channel_lock(oldowner);
- sip_pvt_lock(p);
- }
- /*
- * The channel variables are set below just to get the AMI
- * VarSet event because the channel is being hungup.
- */
- if (p->rtp || p->vrtp || p->trtp) {
- ast_channel_stage_snapshot(oldowner);
- }
- if (p->rtp && (quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
- if (p->do_history) {
- append_history(p, "RTCPaudio", "Quality:%s", quality);
- }
- pbx_builtin_setvar_helper(oldowner, "RTPAUDIOQOS", quality);
- }
- if (p->vrtp && (quality = ast_rtp_instance_get_quality(p->vrtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
- if (p->do_history) {
- append_history(p, "RTCPvideo", "Quality:%s", quality);
- }
- pbx_builtin_setvar_helper(oldowner, "RTPVIDEOQOS", quality);
- }
- if (p->trtp && (quality = ast_rtp_instance_get_quality(p->trtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
- if (p->do_history) {
- append_history(p, "RTCPtext", "Quality:%s", quality);
- }
- pbx_builtin_setvar_helper(oldowner, "RTPTEXTQOS", quality);
- }
- if (p->rtp || p->vrtp || p->trtp) {
- ast_channel_stage_snapshot_done(oldowner);
- }
- /* Send a hangup */
- if (ast_channel_state(oldowner) == AST_STATE_UP) {
- transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
- }
- } else {
- /* Note we will need a BYE when this all settles out
- but we can't send one while we have "INVITE" outstanding. */
- ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
- ast_clear_flag(&p->flags[0], SIP_NEEDREINVITE);
- AST_SCHED_DEL_UNREF(sched, p->waitid, dialog_unref(p, "when you delete the waitid sched, you should dec the refcount for the stored dialog ptr"));
- if (sip_cancel_destroy(p)) {
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- }
- /* If we have an ongoing reinvite, there is a chance that we have gotten a provisional
- * response, but something weird has happened and we will never receive a final response.
- * So, just in case, check for pending actions after a bit of time to trigger the pending
- * bye that we are setting above */
- if (p->ongoing_reinvite && p->reinviteid < 0) {
- p->reinviteid = ast_sched_add(sched, 32 * p->timer_t1, reinvite_timeout, dialog_ref(p, "ref for reinvite_timeout"));
- }
- }
- }
- }
- if (needdestroy) {
- pvt_set_needdestroy(p, "hangup");
- }
- sip_pvt_unlock(p);
- dialog_unref(p, "unref ast->tech_pvt");
- return 0;
- }
- /*! \brief Try setting the codecs suggested by the SIP_CODEC channel variable */
- static void try_suggested_sip_codec(struct sip_pvt *p)
- {
- const char *codec_list;
- char *codec_list_copy;
- struct ast_format_cap *original_jointcaps;
- char *codec;
- int first_codec = 1;
- char *strtok_ptr;
- if (p->outgoing_call) {
- codec_list = pbx_builtin_getvar_helper(p->owner, "SIP_CODEC_OUTBOUND");
- } else if (!(codec_list = pbx_builtin_getvar_helper(p->owner, "SIP_CODEC_INBOUND"))) {
- codec_list = pbx_builtin_getvar_helper(p->owner, "SIP_CODEC");
- }
- if (ast_strlen_zero(codec_list)) {
- return;
- }
- codec_list_copy = ast_strdupa(codec_list);
- original_jointcaps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- if (!original_jointcaps) {
- return;
- }
- ast_format_cap_append_from_cap(original_jointcaps, p->jointcaps, AST_MEDIA_TYPE_UNKNOWN);
- for (codec = strtok_r(codec_list_copy, ",", &strtok_ptr); codec; codec = strtok_r(NULL, ",", &strtok_ptr)) {
- struct ast_format *fmt;
- codec = ast_strip(codec);
- fmt = ast_format_cache_get(codec);
- if (!fmt) {
- ast_log(AST_LOG_NOTICE, "Ignoring ${SIP_CODEC*} variable because of unrecognized/not configured codec %s (check allow/disallow in sip.conf)\n", codec);
- continue;
- }
- if (ast_format_cap_iscompatible_format(original_jointcaps, fmt) != AST_FORMAT_CMP_NOT_EQUAL) {
- if (first_codec) {
- ast_verb(4, "Set codec to '%s' for this call because of ${SIP_CODEC*} variable\n", codec);
- ast_format_cap_remove_by_type(p->jointcaps, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_append(p->jointcaps, fmt, 0);
- ast_format_cap_remove_by_type(p->caps, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_append(p->caps, fmt, 0);
- first_codec = 0;
- } else {
- ast_verb(4, "Add codec to '%s' for this call because of ${SIP_CODEC*} variable\n", codec);
- /* Add the format to the capabilities structure */
- ast_format_cap_append(p->jointcaps, fmt, 0);
- ast_format_cap_append(p->caps, fmt, 0);
- }
- } else {
- ast_log(AST_LOG_NOTICE, "Ignoring ${SIP_CODEC*} variable because it is not shared by both ends: %s\n", codec);
- }
- ao2_ref(fmt, -1);
- }
- ao2_ref(original_jointcaps, -1);
- return;
- }
- /*! \brief sip_answer: Answer SIP call , send 200 OK on Invite
- * Part of PBX interface */
- static int sip_answer(struct ast_channel *ast)
- {
- int res = 0;
- struct sip_pvt *p = ast_channel_tech_pvt(ast);
- int oldsdp = FALSE;
- if (!p) {
- ast_debug(1, "Asked to answer channel %s without tech pvt; ignoring\n",
- ast_channel_name(ast));
- return res;
- }
- sip_pvt_lock(p);
- if (ast_channel_state(ast) != AST_STATE_UP) {
- try_suggested_sip_codec(p);
- if (ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT)) {
- oldsdp = TRUE;
- }
- ast_setstate(ast, AST_STATE_UP);
- ast_debug(1, "SIP answering channel: %s\n", ast_channel_name(ast));
- ast_rtp_instance_update_source(p->rtp);
- res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, oldsdp, TRUE);
- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- /* RFC says the session timer starts counting on 200,
- * not on INVITE. */
- if (p->stimer->st_active == TRUE) {
- start_session_timer(p);
- }
- }
- sip_pvt_unlock(p);
- return res;
- }
- /*! \brief Send frame to media channel (rtp) */
- static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
- {
- struct sip_pvt *p = ast_channel_tech_pvt(ast);
- int res = 0;
- switch (frame->frametype) {
- case AST_FRAME_VOICE:
- if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
- struct ast_str *codec_buf = ast_str_alloca(64);
- ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s read/write = %s/%s\n",
- ast_format_get_name(frame->subclass.format),
- ast_format_cap_get_names(ast_channel_nativeformats(ast), &codec_buf),
- ast_format_get_name(ast_channel_readformat(ast)),
- ast_format_get_name(ast_channel_writeformat(ast)));
- return 0;
- }
- if (p) {
- sip_pvt_lock(p);
- if (p->t38.state == T38_ENABLED) {
- /* drop frame, can't sent VOICE frames while in T.38 mode */
- sip_pvt_unlock(p);
- break;
- } else if (p->rtp) {
- /* If channel is not up, activate early media session */
- if ((ast_channel_state(ast) != AST_STATE_UP) &&
- !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
- !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- ast_rtp_instance_update_source(p->rtp);
- if (!global_prematuremediafilter) {
- p->invitestate = INV_EARLY_MEDIA;
- transmit_provisional_response(p, "183 Session Progress", &p->initreq, TRUE);
- ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
- }
- }
- if (p->invitestate > INV_EARLY_MEDIA || (p->invitestate == INV_EARLY_MEDIA &&
- ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT))) {
- p->lastrtptx = time(NULL);
- res = ast_rtp_instance_write(p->rtp, frame);
- }
- }
- sip_pvt_unlock(p);
- }
- break;
- case AST_FRAME_VIDEO:
- if (p) {
- sip_pvt_lock(p);
- if (p->vrtp) {
- /* Activate video early media */
- if ((ast_channel_state(ast) != AST_STATE_UP) &&
- !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
- !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- p->invitestate = INV_EARLY_MEDIA;
- transmit_provisional_response(p, "183 Session Progress", &p->initreq, TRUE);
- ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
- }
- if (p->invitestate > INV_EARLY_MEDIA || (p->invitestate == INV_EARLY_MEDIA &&
- ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT))) {
- p->lastrtptx = time(NULL);
- res = ast_rtp_instance_write(p->vrtp, frame);
- }
- }
- sip_pvt_unlock(p);
- }
- break;
- case AST_FRAME_TEXT:
- if (p) {
- sip_pvt_lock(p);
- if (p->red) {
- ast_rtp_red_buffer(p->trtp, frame);
- } else {
- if (p->trtp) {
- /* Activate text early media */
- if ((ast_channel_state(ast) != AST_STATE_UP) &&
- !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
- !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- p->invitestate = INV_EARLY_MEDIA;
- transmit_provisional_response(p, "183 Session Progress", &p->initreq, TRUE);
- ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
- }
- if (p->invitestate > INV_EARLY_MEDIA || (p->invitestate == INV_EARLY_MEDIA &&
- ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT))) {
- p->lastrtptx = time(NULL);
- res = ast_rtp_instance_write(p->trtp, frame);
- }
- }
- }
- sip_pvt_unlock(p);
- }
- break;
- case AST_FRAME_IMAGE:
- return 0;
- break;
- case AST_FRAME_MODEM:
- if (p) {
- sip_pvt_lock(p);
- /* UDPTL requires two-way communication, so early media is not needed here.
- we simply forget the frames if we get modem frames before the bridge is up.
- Fax will re-transmit.
- */
- if ((ast_channel_state(ast) == AST_STATE_UP) &&
- p->udptl &&
- (p->t38.state == T38_ENABLED)) {
- res = ast_udptl_write(p->udptl, frame);
- }
- sip_pvt_unlock(p);
- }
- break;
- default:
- ast_log(LOG_WARNING, "Can't send %u type frames with SIP write\n", frame->frametype);
- return 0;
- }
- return res;
- }
- /*! \brief sip_fixup: Fix up a channel: If a channel is consumed, this is called.
- Basically update any ->owner links */
- static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
- {
- int ret = -1;
- struct sip_pvt *p;
- if (newchan && ast_test_flag(ast_channel_flags(newchan), AST_FLAG_ZOMBIE))
- ast_debug(1, "New channel is zombie\n");
- if (oldchan && ast_test_flag(ast_channel_flags(oldchan), AST_FLAG_ZOMBIE))
- ast_debug(1, "Old channel is zombie\n");
- if (!newchan || !ast_channel_tech_pvt(newchan)) {
- if (!newchan)
- ast_log(LOG_WARNING, "No new channel! Fixup of %s failed.\n", ast_channel_name(oldchan));
- else
- ast_log(LOG_WARNING, "No SIP tech_pvt! Fixup of %s failed.\n", ast_channel_name(oldchan));
- return -1;
- }
- p = ast_channel_tech_pvt(newchan);
- sip_pvt_lock(p);
- append_history(p, "Masq", "Old channel: %s\n", ast_channel_name(oldchan));
- append_history(p, "Masq (cont)", "...new owner: %s\n", ast_channel_name(newchan));
- if (p->owner != oldchan)
- ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
- else {
- sip_set_owner(p, newchan);
- /* Re-invite RTP back to Asterisk. Needed if channel is masqueraded out of a native
- RTP bridge (i.e., RTP not going through Asterisk): RTP bridge code might not be
- able to do this if the masquerade happens before the bridge breaks (e.g., AMI
- redirect of both channels). Note that a channel can not be masqueraded *into*
- a native bridge. So there is no danger that this breaks a native bridge that
- should stay up. */
- sip_set_rtp_peer(newchan, NULL, NULL, 0, 0, 0);
- ret = 0;
- }
- ast_debug(3, "SIP Fixup: New owner for dialogue %s: %s (Old parent: %s)\n", p->callid, ast_channel_name(p->owner), ast_channel_name(oldchan));
- sip_pvt_unlock(p);
- return ret;
- }
- static int sip_senddigit_begin(struct ast_channel *ast, char digit)
- {
- struct sip_pvt *p = ast_channel_tech_pvt(ast);
- int res = 0;
- if (!p) {
- ast_debug(1, "Asked to begin DTMF digit on channel %s with no pvt; ignoring\n",
- ast_channel_name(ast));
- return res;
- }
- sip_pvt_lock(p);
- switch (ast_test_flag(&p->flags[0], SIP_DTMF)) {
- case SIP_DTMF_INBAND:
- res = -1; /* Tell Asterisk to generate inband indications */
- break;
- case SIP_DTMF_RFC2833:
- if (p->rtp)
- ast_rtp_instance_dtmf_begin(p->rtp, digit);
- break;
- default:
- break;
- }
- sip_pvt_unlock(p);
- return res;
- }
- /*! \brief Send DTMF character on SIP channel
- within one call, we're able to transmit in many methods simultaneously */
- static int sip_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration)
- {
- struct sip_pvt *p = ast_channel_tech_pvt(ast);
- int res = 0;
- if (!p) {
- ast_debug(1, "Asked to end DTMF digit on channel %s with no pvt; ignoring\n",
- ast_channel_name(ast));
- return res;
- }
- sip_pvt_lock(p);
- switch (ast_test_flag(&p->flags[0], SIP_DTMF)) {
- case SIP_DTMF_INFO:
- case SIP_DTMF_SHORTINFO:
- transmit_info_with_digit(p, digit, duration);
- break;
- case SIP_DTMF_RFC2833:
- if (p->rtp)
- ast_rtp_instance_dtmf_end_with_duration(p->rtp, digit, duration);
- break;
- case SIP_DTMF_INBAND:
- res = -1; /* Tell Asterisk to stop inband indications */
- break;
- }
- sip_pvt_unlock(p);
- return res;
- }
- /*! \brief Transfer SIP call */
- static int sip_transfer(struct ast_channel *ast, const char *dest)
- {
- struct sip_pvt *p = ast_channel_tech_pvt(ast);
- int res;
- if (!p) {
- ast_debug(1, "Asked to transfer channel %s with no pvt; ignoring\n",
- ast_channel_name(ast));
- return -1;
- }
- if (dest == NULL) /* functions below do not take a NULL */
- dest = "";
- sip_pvt_lock(p);
- if (ast_channel_state(ast) == AST_STATE_RING)
- res = sip_sipredirect(p, dest);
- else
- res = transmit_refer(p, dest);
- sip_pvt_unlock(p);
- return res;
- }
- /*! \brief Helper function which updates T.38 capability information and triggers a reinvite */
- static int interpret_t38_parameters(struct sip_pvt *p, const struct ast_control_t38_parameters *parameters)
- {
- int res = 0;
- if (!ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) || !p->udptl) {
- return -1;
- }
- switch (parameters->request_response) {
- case AST_T38_NEGOTIATED:
- case AST_T38_REQUEST_NEGOTIATE: /* Request T38 */
- /* Negotiation can not take place without a valid max_ifp value. */
- if (!parameters->max_ifp) {
- if (p->t38.state == T38_PEER_REINVITE) {
- AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
- transmit_response_reliable(p, "488 Not acceptable here", &p->initreq);
- }
- change_t38_state(p, T38_REJECTED);
- break;
- } else if (p->t38.state == T38_PEER_REINVITE) {
- AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
- p->t38.our_parms = *parameters;
- /* modify our parameters to conform to the peer's parameters,
- * based on the rules in the ITU T.38 recommendation
- */
- if (!p->t38.their_parms.fill_bit_removal) {
- p->t38.our_parms.fill_bit_removal = FALSE;
- }
- if (!p->t38.their_parms.transcoding_mmr) {
- p->t38.our_parms.transcoding_mmr = FALSE;
- }
- if (!p->t38.their_parms.transcoding_jbig) {
- p->t38.our_parms.transcoding_jbig = FALSE;
- }
- p->t38.our_parms.version = MIN(p->t38.our_parms.version, p->t38.their_parms.version);
- p->t38.our_parms.rate_management = p->t38.their_parms.rate_management;
- ast_udptl_set_local_max_ifp(p->udptl, p->t38.our_parms.max_ifp);
- change_t38_state(p, T38_ENABLED);
- transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
- } else if (p->t38.state != T38_ENABLED) {
- p->t38.our_parms = *parameters;
- ast_udptl_set_local_max_ifp(p->udptl, p->t38.our_parms.max_ifp);
- change_t38_state(p, T38_LOCAL_REINVITE);
- if (!p->pendinginvite) {
- transmit_reinvite_with_sdp(p, TRUE, FALSE);
- } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
- ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
- }
- }
- break;
- case AST_T38_TERMINATED:
- case AST_T38_REFUSED:
- case AST_T38_REQUEST_TERMINATE: /* Shutdown T38 */
- if (p->t38.state == T38_PEER_REINVITE) {
- AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
- change_t38_state(p, T38_REJECTED);
- transmit_response_reliable(p, "488 Not acceptable here", &p->initreq);
- } else if (p->t38.state == T38_ENABLED)
- transmit_reinvite_with_sdp(p, FALSE, FALSE);
- break;
- case AST_T38_REQUEST_PARMS: { /* Application wants remote's parameters re-sent */
- struct ast_control_t38_parameters parameters = p->t38.their_parms;
- if (p->t38.state == T38_PEER_REINVITE) {
- AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
- parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
- parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
- if (p->owner) {
- ast_queue_control_data(p->owner, AST_CONTROL_T38_PARAMETERS, ¶meters, sizeof(parameters));
- }
- /* we need to return a positive value here, so that applications that
- * send this request can determine conclusively whether it was accepted or not...
- * older versions of chan_sip would just silently accept it and return zero.
- */
- res = AST_T38_REQUEST_PARMS;
- }
- break;
- }
- default:
- res = -1;
- break;
- }
- return res;
- }
- /*!
- * \internal
- * \brief Create and initialize UDPTL for the specified dialog
- *
- * \param p SIP private structure to create UDPTL object for
- * \pre p is locked
- * \pre p->owner is locked
- *
- * \note In the case of failure, SIP_PAGE2_T38SUPPORT is cleared on p
- *
- * \return 0 on success, any other value on failure
- */
- static int initialize_udptl(struct sip_pvt *p)
- {
- int natflags = ast_test_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP);
- if (!ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) {
- return 1;
- }
- /* If we've already initialized T38, don't take any further action */
- if (p->udptl) {
- return 0;
- }
- /* T38 can be supported by this dialog, create it and set the derived properties */
- if ((p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, &bindaddr))) {
- if (p->owner) {
- ast_channel_set_fd(p->owner, 5, ast_udptl_fd(p->udptl));
- }
- ast_udptl_setqos(p->udptl, global_tos_audio, global_cos_audio);
- p->t38_maxdatagram = p->relatedpeer ? p->relatedpeer->t38_maxdatagram : global_t38_maxdatagram;
- set_t38_capabilities(p);
- ast_debug(1, "Setting NAT on UDPTL to %s\n", natflags ? "On" : "Off");
- ast_udptl_setnat(p->udptl, natflags);
- } else {
- ast_log(AST_LOG_WARNING, "UDPTL creation failed - disabling T38 for this dialog\n");
- ast_clear_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT);
- return 1;
- }
- return 0;
- }
- static int sipinfo_send(
- struct ast_channel *chan,
- struct ast_variable *headers,
- const char *content_type,
- const char *content,
- const char *useragent_filter)
- {
- struct sip_pvt *p;
- struct ast_variable *var;
- struct sip_request req;
- int res = -1;
- ast_channel_lock(chan);
- if (ast_channel_tech(chan) != &sip_tech) {
- ast_log(LOG_WARNING, "Attempted to send a custom INFO on a non-SIP channel %s\n", ast_channel_name(chan));
- ast_channel_unlock(chan);
- return res;
- }
- p = ast_channel_tech_pvt(chan);
- sip_pvt_lock(p);
- if (!(ast_strlen_zero(useragent_filter))) {
- int match = (strstr(p->useragent, useragent_filter)) ? 1 : 0;
- if (!match) {
- goto cleanup;
- }
- }
- reqprep(&req, p, SIP_INFO, 0, 1);
- for (var = headers; var; var = var->next) {
- add_header(&req, var->name, var->value);
- }
- if (!ast_strlen_zero(content) && !ast_strlen_zero(content_type)) {
- add_header(&req, "Content-Type", content_type);
- add_content(&req, content);
- }
- res = send_request(p, &req, XMIT_RELIABLE, p->ocseq);
- cleanup:
- sip_pvt_unlock(p);
- ast_channel_unlock(chan);
- return res;
- }
- /*! \brief Play indication to user
- * With SIP a lot of indications is sent as messages, letting the device play
- the indication - busy signal, congestion etc
- \return -1 to force ast_indicate to send indication in audio, 0 if SIP can handle the indication by sending a message
- */
- static int sip_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
- {
- struct sip_pvt *p = ast_channel_tech_pvt(ast);
- int res = 0;
- if (!p) {
- ast_debug(1, "Asked to indicate condition on channel %s with no pvt; ignoring\n",
- ast_channel_name(ast));
- return res;
- }
- sip_pvt_lock(p);
- switch(condition) {
- case AST_CONTROL_RINGING:
- if (ast_channel_state(ast) == AST_STATE_RING) {
- p->invitestate = INV_EARLY_MEDIA;
- if (!ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) ||
- (ast_test_flag(&p->flags[0], SIP_PROG_INBAND) == SIP_PROG_INBAND_NEVER)) {
- /* Send 180 ringing if out-of-band seems reasonable */
- transmit_provisional_response(p, "180 Ringing", &p->initreq, 0);
- ast_set_flag(&p->flags[0], SIP_RINGING);
- if (ast_test_flag(&p->flags[0], SIP_PROG_INBAND) != SIP_PROG_INBAND_YES)
- break;
- } else {
- /* Well, if it's not reasonable, just send in-band */
- }
- }
- res = -1;
- break;
- case AST_CONTROL_BUSY:
- if (ast_channel_state(ast) != AST_STATE_UP) {
- transmit_response_reliable(p, "486 Busy Here", &p->initreq);
- p->invitestate = INV_COMPLETED;
- sip_alreadygone(p);
- ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
- break;
- }
- res = -1;
- break;
- case AST_CONTROL_CONGESTION:
- if (ast_channel_state(ast) != AST_STATE_UP) {
- transmit_response_reliable(p, "503 Service Unavailable", &p->initreq);
- p->invitestate = INV_COMPLETED;
- sip_alreadygone(p);
- ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
- break;
- }
- res = -1;
- break;
- case AST_CONTROL_INCOMPLETE:
- if (ast_channel_state(ast) != AST_STATE_UP) {
- switch (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) {
- case SIP_PAGE2_ALLOWOVERLAP_YES:
- transmit_response_reliable(p, "484 Address Incomplete", &p->initreq);
- p->invitestate = INV_COMPLETED;
- sip_alreadygone(p);
- ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
- break;
- case SIP_PAGE2_ALLOWOVERLAP_DTMF:
- /* Just wait for inband DTMF digits */
- break;
- default:
- /* it actually means no support for overlap */
- transmit_response_reliable(p, "404 Not Found", &p->initreq);
- p->invitestate = INV_COMPLETED;
- sip_alreadygone(p);
- ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
- break;
- }
- }
- break;
- case AST_CONTROL_PROCEEDING:
- if ((ast_channel_state(ast) != AST_STATE_UP) &&
- !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
- !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- transmit_response(p, "100 Trying", &p->initreq);
- p->invitestate = INV_PROCEEDING;
- break;
- }
- res = -1;
- break;
- case AST_CONTROL_PROGRESS:
- if ((ast_channel_state(ast) != AST_STATE_UP) &&
- !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
- !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- p->invitestate = INV_EARLY_MEDIA;
- /* SIP_PROG_INBAND_NEVER means sending 180 ringing in place of a 183 */
- if (ast_test_flag(&p->flags[0], SIP_PROG_INBAND) != SIP_PROG_INBAND_NEVER) {
- transmit_provisional_response(p, "183 Session Progress", &p->initreq, TRUE);
- ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
- } else if (ast_channel_state(ast) == AST_STATE_RING && !ast_test_flag(&p->flags[0], SIP_RINGING)) {
- transmit_provisional_response(p, "180 Ringing", &p->initreq, 0);
- ast_set_flag(&p->flags[0], SIP_RINGING);
- }
- break;
- }
- res = -1;
- break;
- case AST_CONTROL_HOLD:
- ast_rtp_instance_update_source(p->rtp);
- ast_moh_start(ast, data, p->mohinterpret);
- break;
- case AST_CONTROL_UNHOLD:
- ast_rtp_instance_update_source(p->rtp);
- ast_moh_stop(ast);
- break;
- case AST_CONTROL_VIDUPDATE: /* Request a video frame update */
- if (p->vrtp && !p->novideo) {
- /* FIXME: Only use this for VP8. Additional work would have to be done to
- * fully support other video codecs */
- if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), ast_format_vp8) != AST_FORMAT_CMP_NOT_EQUAL) {
- /* FIXME Fake RTP write, this will be sent as an RTCP packet. Ideally the
- * RTP engine would provide a way to externally write/schedule RTCP
- * packets */
- struct ast_frame fr;
- fr.frametype = AST_FRAME_CONTROL;
- fr.subclass.integer = AST_CONTROL_VIDUPDATE;
- res = ast_rtp_instance_write(p->vrtp, &fr);
- } else {
- transmit_info_with_vidupdate(p);
- }
- } else {
- res = -1;
- }
- break;
- case AST_CONTROL_T38_PARAMETERS:
- res = -1;
- if (datalen != sizeof(struct ast_control_t38_parameters)) {
- ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38_PARAMETERS. Expected %d, got %d\n", (int) sizeof(struct ast_control_t38_parameters), (int) datalen);
- } else {
- const struct ast_control_t38_parameters *parameters = data;
- if (!initialize_udptl(p)) {
- res = interpret_t38_parameters(p, parameters);
- }
- }
- break;
- case AST_CONTROL_SRCUPDATE:
- ast_rtp_instance_update_source(p->rtp);
- break;
- case AST_CONTROL_SRCCHANGE:
- ast_rtp_instance_change_source(p->rtp);
- break;
- case AST_CONTROL_CONNECTED_LINE:
- update_connectedline(p, data, datalen);
- break;
- case AST_CONTROL_REDIRECTING:
- update_redirecting(p, data, datalen);
- break;
- case AST_CONTROL_AOC:
- {
- struct ast_aoc_decoded *decoded = ast_aoc_decode((struct ast_aoc_encoded *) data, datalen, ast);
- if (!decoded) {
- ast_log(LOG_ERROR, "Error decoding indicated AOC data\n");
- res = -1;
- break;
- }
- switch (ast_aoc_get_msg_type(decoded)) {
- case AST_AOC_REQUEST:
- if (ast_aoc_get_termination_request(decoded)) {
- /* TODO, once there is a way to get AOC-E on hangup, attempt that here
- * before hanging up the channel.*/
- /* The other side has already initiated the hangup. This frame
- * just says they are waiting to get AOC-E before completely tearing
- * the call down. Since SIP does not support this at the moment go
- * ahead and terminate the call here to avoid an unnecessary timeout. */
- ast_debug(1, "AOC-E termination request received on %s. This is not yet supported on sip. Continue with hangup \n", ast_channel_name(p->owner));
- ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
- }
- break;
- case AST_AOC_D:
- case AST_AOC_E:
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_SNOM_AOC)) {
- transmit_info_with_aoc(p, decoded);
- }
- break;
- case AST_AOC_S: /* S not supported yet */
- default:
- break;
- }
- ast_aoc_destroy_decoded(decoded);
- }
- break;
- case AST_CONTROL_UPDATE_RTP_PEER: /* Absorb this since it is handled by the bridge */
- break;
- case AST_CONTROL_FLASH: /* We don't currently handle AST_CONTROL_FLASH here, but it is expected, so we don't need to warn either. */
- res = -1;
- break;
- case AST_CONTROL_PVT_CAUSE_CODE: /* these should be handled by the code in channel.c */
- case AST_CONTROL_MASQUERADE_NOTIFY:
- case -1:
- res = -1;
- break;
- default:
- ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", condition);
- res = -1;
- break;
- }
- sip_pvt_unlock(p);
- return res;
- }
- /*!
- * \brief Initiate a call in the SIP channel
- *
- * \note called from sip_request_call (calls from the pbx ) for
- * outbound channels and from handle_request_invite for inbound
- * channels
- *
- * \pre i is locked
- *
- * \return New ast_channel locked.
- */
- static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *title, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, ast_callid callid)
- {
- struct ast_format_cap *caps;
- struct ast_channel *tmp;
- struct ast_variable *v = NULL;
- struct ast_format *fmt;
- struct ast_format_cap *what = NULL; /* SHALLOW COPY DO NOT DESTROY! */
- struct ast_str *codec_buf = ast_str_alloca(64);
- int needvideo = 0;
- int needtext = 0;
- char *exten;
- caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- if (!caps) {
- return NULL;
- }
- {
- const char *my_name; /* pick a good name */
- if (title) {
- my_name = title;
- } else {
- my_name = ast_strdupa(i->fromdomain);
- }
- /* Don't hold a sip pvt lock while we allocate a channel */
- sip_pvt_unlock(i);
- if (i->relatedpeer && i->relatedpeer->endpoint) {
- tmp = ast_channel_alloc_with_endpoint(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, assignedids, requestor, i->amaflags, i->relatedpeer->endpoint, "SIP/%s-%08x", my_name, (unsigned)ast_atomic_fetchadd_int((int *)&chan_idx, +1));
- } else {
- tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, assignedids, requestor, i->amaflags, "SIP/%s-%08x", my_name, (unsigned)ast_atomic_fetchadd_int((int *)&chan_idx, +1));
- }
- }
- if (!tmp) {
- ast_log(LOG_WARNING, "Unable to allocate AST channel structure for SIP channel\n");
- ao2_ref(caps, -1);
- sip_pvt_lock(i);
- return NULL;
- }
- ast_channel_stage_snapshot(tmp);
- /* If we sent in a callid, bind it to the channel. */
- if (callid) {
- ast_channel_callid_set(tmp, callid);
- }
- sip_pvt_lock(i);
- ast_channel_cc_params_init(tmp, i->cc_params);
- ast_channel_caller(tmp)->id.tag = ast_strdup(i->cid_tag);
- ast_channel_tech_set(tmp, (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INFO || ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_SHORTINFO) ? &sip_tech_info : &sip_tech);
- /* Select our native format based on codec preference until we receive
- something from another device to the contrary. */
- if (ast_format_cap_count(i->jointcaps)) { /* The joint capabilities of us and peer */
- what = i->jointcaps;
- } else if (ast_format_cap_count(i->caps)) { /* Our configured capability for this peer */
- what = i->caps;
- } else {
- what = sip_cfg.caps;
- }
- /* Set the native formats */
- ast_format_cap_append_from_cap(caps, what, AST_MEDIA_TYPE_UNKNOWN);
- /* Use only the preferred audio format, which is stored at the '0' index */
- fmt = ast_format_cap_get_best_by_type(what, AST_MEDIA_TYPE_AUDIO); /* get the best audio format */
- if (fmt) {
- ast_format_cap_remove_by_type(caps, AST_MEDIA_TYPE_AUDIO); /* remove only the other audio formats */
- ast_format_cap_append(caps, fmt, 0); /* add our best choice back */
- } else {
- /* If we don't have an audio format, try to get something */
- fmt = ast_format_cap_get_format(caps, 0);
- if (!fmt) {
- ast_log(LOG_WARNING, "No compatible formats could be found for %s\n", ast_channel_name(tmp));
- ao2_ref(caps, -1);
- tmp = ast_channel_unref(tmp);
- return NULL;
- }
- }
- ast_channel_nativeformats_set(tmp, caps);
- ao2_ref(caps, -1);
- ast_debug(3, "*** Our native formats are %s \n", ast_format_cap_get_names(ast_channel_nativeformats(tmp), &codec_buf));
- ast_debug(3, "*** Joint capabilities are %s \n", ast_format_cap_get_names(i->jointcaps, &codec_buf));
- ast_debug(3, "*** Our capabilities are %s \n", ast_format_cap_get_names(i->caps, &codec_buf));
- ast_debug(3, "*** AST_CODEC_CHOOSE formats are %s \n", ast_format_get_name(fmt));
- if (ast_format_cap_count(i->prefcaps)) {
- ast_debug(3, "*** Our preferred formats from the incoming channel are %s \n", ast_format_cap_get_names(i->prefcaps, &codec_buf));
- }
- /* If we have a prefcodec setting, we have an inbound channel that set a
- preferred format for this call. Otherwise, we check the jointcapability
- We also check for vrtp. If it's not there, we are not allowed do any video anyway.
- */
- if (i->vrtp) {
- if (ast_test_flag(&i->flags[1], SIP_PAGE2_VIDEOSUPPORT))
- needvideo = 1;
- else if (ast_format_cap_count(i->prefcaps))
- needvideo = ast_format_cap_has_type(i->prefcaps, AST_MEDIA_TYPE_VIDEO); /* Outbound call */
- else
- needvideo = ast_format_cap_has_type(i->jointcaps, AST_MEDIA_TYPE_VIDEO); /* Inbound call */
- }
- if (i->trtp) {
- if (ast_format_cap_count(i->prefcaps))
- needtext = ast_format_cap_has_type(i->prefcaps, AST_MEDIA_TYPE_TEXT); /* Outbound call */
- else
- needtext = ast_format_cap_has_type(i->jointcaps, AST_MEDIA_TYPE_TEXT); /* Inbound call */
- }
- if (needvideo) {
- ast_debug(3, "This channel can handle video! HOLLYWOOD next!\n");
- } else {
- ast_debug(3, "This channel will not be able to handle video.\n");
- }
- enable_dsp_detect(i);
- if ((ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
- (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
- if (i->rtp) {
- ast_rtp_instance_dtmf_mode_set(i->rtp, AST_RTP_DTMF_MODE_INBAND);
- }
- } else if (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) {
- if (i->rtp) {
- ast_rtp_instance_dtmf_mode_set(i->rtp, AST_RTP_DTMF_MODE_RFC2833);
- }
- }
- /* Set file descriptors for audio, video, and realtime text. Since
- * UDPTL is created as needed in the lifetime of a dialog, its file
- * descriptor is set in initialize_udptl */
- if (i->rtp) {
- ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(i->rtp, 0));
- ast_channel_set_fd(tmp, 1, ast_rtp_instance_fd(i->rtp, 1));
- ast_rtp_instance_set_write_format(i->rtp, fmt);
- ast_rtp_instance_set_read_format(i->rtp, fmt);
- }
- if (needvideo && i->vrtp) {
- ast_channel_set_fd(tmp, 2, ast_rtp_instance_fd(i->vrtp, 0));
- ast_channel_set_fd(tmp, 3, ast_rtp_instance_fd(i->vrtp, 1));
- }
- if (needtext && i->trtp) {
- ast_channel_set_fd(tmp, 4, ast_rtp_instance_fd(i->trtp, 0));
- }
- if (i->udptl) {
- ast_channel_set_fd(tmp, 5, ast_udptl_fd(i->udptl));
- }
- if (state == AST_STATE_RING) {
- ast_channel_rings_set(tmp, 1);
- }
- ast_channel_adsicpe_set(tmp, AST_ADSI_UNAVAILABLE);
- ast_channel_set_writeformat(tmp, fmt);
- ast_channel_set_rawwriteformat(tmp, fmt);
- ast_channel_set_readformat(tmp, fmt);
- ast_channel_set_rawreadformat(tmp, fmt);
- ao2_ref(fmt, -1);
- ast_channel_tech_pvt_set(tmp, dialog_ref(i, "sip_new: set chan->tech_pvt to i"));
- ast_channel_callgroup_set(tmp, i->callgroup);
- ast_channel_pickupgroup_set(tmp, i->pickupgroup);
- ast_channel_named_callgroups_set(tmp, i->named_callgroups);
- ast_channel_named_pickupgroups_set(tmp, i->named_pickupgroups);
- ast_channel_caller(tmp)->id.name.presentation = i->callingpres;
- ast_channel_caller(tmp)->id.number.presentation = i->callingpres;
- if (!ast_strlen_zero(i->parkinglot)) {
- ast_channel_parkinglot_set(tmp, i->parkinglot);
- }
- if (!ast_strlen_zero(i->accountcode)) {
- ast_channel_accountcode_set(tmp, i->accountcode);
- }
- if (i->amaflags) {
- ast_channel_amaflags_set(tmp, i->amaflags);
- }
- if (!ast_strlen_zero(i->language)) {
- ast_channel_language_set(tmp, i->language);
- }
- if (!ast_strlen_zero(i->zone)) {
- struct ast_tone_zone *zone;
- if (!(zone = ast_get_indication_zone(i->zone))) {
- ast_log(LOG_ERROR, "Unknown country code '%s' for tonezone. Check indications.conf for available country codes.\n", i->zone);
- }
- ast_channel_zone_set(tmp, zone);
- }
- sip_set_owner(i, tmp);
- ast_module_ref(ast_module_info->self);
- ast_channel_context_set(tmp, i->context);
- /*Since it is valid to have extensions in the dialplan that have unescaped characters in them
- * we should decode the uri before storing it in the channel, but leave it encoded in the sip_pvt
- * structure so that there aren't issues when forming URI's
- */
- exten = ast_strdupa(i->exten);
- sip_pvt_unlock(i);
- ast_channel_unlock(tmp);
- if (!ast_exists_extension(NULL, i->context, i->exten, 1, i->cid_num)) {
- ast_uri_decode(exten, ast_uri_sip_user);
- }
- ast_channel_lock(tmp);
- sip_pvt_lock(i);
- ast_channel_exten_set(tmp, exten);
- /* Don't use ast_set_callerid() here because it will
- * generate an unnecessary NewCallerID event */
- if (!ast_strlen_zero(i->cid_num)) {
- ast_channel_caller(tmp)->ani.number.valid = 1;
- ast_channel_caller(tmp)->ani.number.str = ast_strdup(i->cid_num);
- }
- if (!ast_strlen_zero(i->rdnis)) {
- ast_channel_redirecting(tmp)->from.number.valid = 1;
- ast_channel_redirecting(tmp)->from.number.str = ast_strdup(i->rdnis);
- }
- if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s")) {
- ast_channel_dialed(tmp)->number.str = ast_strdup(i->exten);
- }
- ast_channel_priority_set(tmp, 1);
- if (!ast_strlen_zero(i->uri)) {
- pbx_builtin_setvar_helper(tmp, "SIPURI", i->uri);
- }
- if (!ast_strlen_zero(i->domain)) {
- pbx_builtin_setvar_helper(tmp, "SIPDOMAIN", i->domain);
- }
- if (!ast_strlen_zero(i->tel_phone_context)) {
- pbx_builtin_setvar_helper(tmp, "SIPURIPHONECONTEXT", i->tel_phone_context);
- }
- if (!ast_strlen_zero(i->callid)) {
- pbx_builtin_setvar_helper(tmp, "SIPCALLID", i->callid);
- }
- if (i->rtp) {
- ast_jb_configure(tmp, &global_jbconf);
- }
- if (!i->relatedpeer) {
- ast_set_flag(ast_channel_flags(tmp), AST_FLAG_DISABLE_DEVSTATE_CACHE);
- }
- /* Set channel variables for this call from configuration */
- for (v = i->chanvars ; v ; v = v->next) {
- char valuebuf[1024];
- pbx_builtin_setvar_helper(tmp, v->name, ast_get_encoded_str(v->value, valuebuf, sizeof(valuebuf)));
- }
- if (i->do_history) {
- append_history(i, "NewChan", "Channel %s - from %s", ast_channel_name(tmp), i->callid);
- }
- ast_channel_stage_snapshot_done(tmp);
- return tmp;
- }
- /*! \brief Lookup 'name' in the SDP starting
- * at the 'start' line. Returns the matching line, and 'start'
- * is updated with the next line number.
- */
- static const char *get_sdp_iterate(int *start, struct sip_request *req, const char *name)
- {
- int len = strlen(name);
- const char *line;
- while (*start < (req->sdp_start + req->sdp_count)) {
- line = REQ_OFFSET_TO_STR(req, line[(*start)++]);
- if (!strncasecmp(line, name, len) && line[len] == '=') {
- return ast_skip_blanks(line + len + 1);
- }
- }
- /* if the line was not found, ensure that *start points past the SDP */
- (*start)++;
- return "";
- }
- /*! \brief Fetches the next valid SDP line between the 'start' line
- * (inclusive) and the 'stop' line (exclusive). Returns the type
- * ('a', 'c', ...) and matching line in reference 'start' is updated
- * with the next line number.
- */
- static char get_sdp_line(int *start, int stop, struct sip_request *req, const char **value)
- {
- char type = '\0';
- const char *line = NULL;
- if (stop > (req->sdp_start + req->sdp_count)) {
- stop = req->sdp_start + req->sdp_count;
- }
- while (*start < stop) {
- line = REQ_OFFSET_TO_STR(req, line[(*start)++]);
- if (line[1] == '=') {
- type = line[0];
- *value = ast_skip_blanks(line + 2);
- break;
- }
- }
- return type;
- }
- /*! \brief Get a specific line from the message content */
- static char *get_content_line(struct sip_request *req, char *name, char delimiter)
- {
- int i;
- int len = strlen(name);
- const char *line;
- for (i = 0; i < req->lines; i++) {
- line = REQ_OFFSET_TO_STR(req, line[i]);
- if (!strncasecmp(line, name, len) && line[len] == delimiter) {
- return ast_skip_blanks(line + len + 1);
- }
- }
- return "";
- }
- /*! \brief Structure for conversion between compressed SIP and "normal" SIP headers */
- struct cfalias {
- const char *fullname;
- const char *shortname;
- };
- static const struct cfalias aliases[] = {
- { "Content-Type", "c" },
- { "Content-Encoding", "e" },
- { "From", "f" },
- { "Call-ID", "i" },
- { "Contact", "m" },
- { "Content-Length", "l" },
- { "Subject", "s" },
- { "To", "t" },
- { "Supported", "k" },
- { "Refer-To", "r" },
- { "Referred-By", "b" },
- { "Allow-Events", "u" },
- { "Event", "o" },
- { "Via", "v" },
- { "Accept-Contact", "a" },
- { "Reject-Contact", "j" },
- { "Request-Disposition", "d" },
- { "Session-Expires", "x" },
- { "Identity", "y" },
- { "Identity-Info", "n" },
- };
- /*! \brief Find compressed SIP alias */
- static const char *find_alias(const char *name, const char *_default)
- {
- int x;
- for (x = 0; x < ARRAY_LEN(aliases); x++) {
- if (!strcasecmp(aliases[x].fullname, name))
- return aliases[x].shortname;
- }
- return _default;
- }
- /*! \brief Find full SIP alias */
- static const char *find_full_alias(const char *name, const char *_default)
- {
- int x;
- if (strlen(name) == 1) {
- /* We have a short header name to convert. */
- for (x = 0; x < ARRAY_LEN(aliases); ++x) {
- if (!strcasecmp(aliases[x].shortname, name))
- return aliases[x].fullname;
- }
- }
- return _default;
- }
- static const char *__get_header(const struct sip_request *req, const char *name, int *start)
- {
- /*
- * Technically you can place arbitrary whitespace both before and after the ':' in
- * a header, although RFC3261 clearly says you shouldn't before, and place just
- * one afterwards. If you shouldn't do it, what absolute idiot decided it was
- * a good idea to say you can do it, and if you can do it, why in the hell would.
- * you say you shouldn't.
- * Anyways, pedanticsipchecking controls whether we allow spaces before ':',
- * and we always allow spaces after that for compatibility.
- */
- const char *sname = find_alias(name, NULL);
- int x, len = strlen(name), slen = (sname ? 1 : 0);
- for (x = *start; x < req->headers; x++) {
- const char *header = REQ_OFFSET_TO_STR(req, header[x]);
- int smatch = 0, match = !strncasecmp(header, name, len);
- if (slen) {
- smatch = !strncasecmp(header, sname, slen);
- }
- if (match || smatch) {
- /* skip name */
- const char *r = header + (match ? len : slen );
- if (sip_cfg.pedanticsipchecking) {
- r = ast_skip_blanks(r);
- }
- if (*r == ':') {
- *start = x+1;
- return ast_skip_blanks(r+1);
- }
- }
- }
- /* Don't return NULL, so sip_get_header is always a valid pointer */
- return "";
- }
- /*! \brief Get header from SIP request
- \return Always return something, so don't check for NULL because it won't happen :-)
- */
- const char *sip_get_header(const struct sip_request *req, const char *name)
- {
- int start = 0;
- return __get_header(req, name, &start);
- }
- AST_THREADSTORAGE(sip_content_buf);
- /*! \brief Get message body content */
- static char *get_content(struct sip_request *req)
- {
- struct ast_str *str;
- int i;
- if (!(str = ast_str_thread_get(&sip_content_buf, 128))) {
- return NULL;
- }
- ast_str_reset(str);
- for (i = 0; i < req->lines; i++) {
- if (ast_str_append(&str, 0, "%s\n", REQ_OFFSET_TO_STR(req, line[i])) < 0) {
- return NULL;
- }
- }
- return ast_str_buffer(str);
- }
- /*! \brief Read RTP from network */
- static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect)
- {
- /* Retrieve audio/etc from channel. Assumes p->lock is already held. */
- struct ast_frame *f;
- if (!p->rtp) {
- /* We have no RTP allocated for this channel */
- return &ast_null_frame;
- }
- switch(ast_channel_fdno(ast)) {
- case 0:
- f = ast_rtp_instance_read(p->rtp, 0); /* RTP Audio */
- break;
- case 1:
- f = ast_rtp_instance_read(p->rtp, 1); /* RTCP Control Channel */
- break;
- case 2:
- f = ast_rtp_instance_read(p->vrtp, 0); /* RTP Video */
- break;
- case 3:
- f = ast_rtp_instance_read(p->vrtp, 1); /* RTCP Control Channel for video */
- break;
- case 4:
- f = ast_rtp_instance_read(p->trtp, 0); /* RTP Text */
- if (sipdebug_text) {
- struct ast_str *out = ast_str_create(f->datalen * 4 + 6);
- int i;
- unsigned char* arr = f->data.ptr;
- do {
- if (!out) {
- break;
- }
- for (i = 0; i < f->datalen; i++) {
- ast_str_append(&out, 0, "%c", (arr[i] > ' ' && arr[i] < '}') ? arr[i] : '.');
- }
- ast_str_append(&out, 0, " -> ");
- for (i = 0; i < f->datalen; i++) {
- ast_str_append(&out, 0, "%02hhX ", arr[i]);
- }
- ast_verb(0, "%s\n", ast_str_buffer(out));
- ast_free(out);
- } while (0);
- }
- break;
- case 5:
- f = ast_udptl_read(p->udptl); /* UDPTL for T.38 */
- break;
- default:
- f = &ast_null_frame;
- }
- /* Don't forward RFC2833 if we're not supposed to */
- if (f && (f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END) &&
- (ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_RFC2833)) {
- ast_debug(1, "Ignoring DTMF (%c) RTP frame because dtmfmode is not RFC2833\n", f->subclass.integer);
- ast_frfree(f);
- return &ast_null_frame;
- }
- /* We already hold the channel lock */
- if (!p->owner || (f && f->frametype != AST_FRAME_VOICE)) {
- return f;
- }
- if (f && ast_format_cap_iscompatible_format(ast_channel_nativeformats(p->owner), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
- struct ast_format_cap *caps;
- if (ast_format_cap_iscompatible_format(p->jointcaps, f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
- ast_debug(1, "Bogus frame of format '%s' received from '%s'!\n",
- ast_format_get_name(f->subclass.format), ast_channel_name(p->owner));
- ast_frfree(f);
- return &ast_null_frame;
- }
- ast_debug(1, "Oooh, format changed to %s\n",
- ast_format_get_name(f->subclass.format));
- caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- if (caps) {
- ast_format_cap_append_from_cap(caps, ast_channel_nativeformats(p->owner), AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_remove_by_type(caps, AST_MEDIA_TYPE_AUDIO);
- ast_format_cap_append(caps, f->subclass.format, 0);
- ast_channel_nativeformats_set(p->owner, caps);
- ao2_ref(caps, -1);
- }
- ast_set_read_format(p->owner, ast_channel_readformat(p->owner));
- ast_set_write_format(p->owner, ast_channel_writeformat(p->owner));
- }
- if (f && p->dsp) {
- f = ast_dsp_process(p->owner, p->dsp, f);
- if (f && f->frametype == AST_FRAME_DTMF) {
- if (f->subclass.integer == 'f') {
- ast_debug(1, "Fax CNG detected on %s\n", ast_channel_name(ast));
- *faxdetect = 1;
- /* If we only needed this DSP for fax detection purposes we can just drop it now */
- if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) {
- ast_dsp_set_features(p->dsp, DSP_FEATURE_DIGIT_DETECT);
- } else {
- ast_dsp_free(p->dsp);
- p->dsp = NULL;
- }
- } else {
- ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass.integer);
- }
- }
- }
- return f;
- }
- /*! \brief Read SIP RTP from channel */
- static struct ast_frame *sip_read(struct ast_channel *ast)
- {
- struct ast_frame *fr;
- struct sip_pvt *p = ast_channel_tech_pvt(ast);
- int faxdetected = FALSE;
- sip_pvt_lock(p);
- fr = sip_rtp_read(ast, p, &faxdetected);
- p->lastrtprx = time(NULL);
- /* If we detect a CNG tone and fax detection is enabled then send us off to the fax extension */
- if (faxdetected && ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_CNG)) {
- if (strcmp(ast_channel_exten(ast), "fax")) {
- const char *target_context = S_OR(ast_channel_macrocontext(ast), ast_channel_context(ast));
- /* We need to unlock 'ast' here because
- * ast_exists_extension has the potential to start and
- * stop an autoservice on the channel. Such action is
- * prone to deadlock if the channel is locked.
- */
- sip_pvt_unlock(p);
- ast_channel_unlock(ast);
- if (ast_exists_extension(ast, target_context, "fax", 1,
- S_COR(ast_channel_caller(ast)->id.number.valid, ast_channel_caller(ast)->id.number.str, NULL))) {
- ast_channel_lock(ast);
- sip_pvt_lock(p);
- ast_verb(2, "Redirecting '%s' to fax extension due to CNG detection\n", ast_channel_name(ast));
- pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast_channel_exten(ast));
- if (ast_async_goto(ast, target_context, "fax", 1)) {
- ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", ast_channel_name(ast), target_context);
- }
- ast_frfree(fr);
- fr = &ast_null_frame;
- } else {
- ast_channel_lock(ast);
- sip_pvt_lock(p);
- ast_log(LOG_NOTICE, "FAX CNG detected but no fax extension\n");
- }
- }
- }
- /* Only allow audio through if they sent progress with SDP, or if the channel is actually answered */
- if (fr && fr->frametype == AST_FRAME_VOICE && p->invitestate != INV_EARLY_MEDIA && ast_channel_state(ast) != AST_STATE_UP) {
- ast_frfree(fr);
- fr = &ast_null_frame;
- }
- sip_pvt_unlock(p);
- return fr;
- }
- /*! \brief Generate 32 byte random string for callid's etc */
- static char *generate_random_string(char *buf, size_t size)
- {
- long val[4];
- int x;
- for (x=0; x<4; x++)
- val[x] = ast_random();
- snprintf(buf, size, "%08lx%08lx%08lx%08lx", (unsigned long)val[0], (unsigned long)val[1], (unsigned long)val[2], (unsigned long)val[3]);
- return buf;
- }
- static char *generate_uri(struct sip_pvt *pvt, char *buf, size_t size)
- {
- struct ast_str *uri = ast_str_alloca(size);
- ast_str_set(&uri, 0, "%s", pvt->socket.type == AST_TRANSPORT_TLS ? "sips:" : "sip:");
- /* Here would be a great place to generate a UUID, but for now we'll
- * use the handy random string generation function we already have
- */
- ast_str_append(&uri, 0, "%s", generate_random_string(buf, size));
- ast_str_append(&uri, 0, "@%s", ast_sockaddr_stringify_remote(&pvt->ourip));
- ast_copy_string(buf, ast_str_buffer(uri), size);
- return buf;
- }
- /*!
- * \brief Build SIP Call-ID value for a non-REGISTER transaction
- *
- * \note The passed in pvt must not be in a dialogs container
- * since this function changes the hash key used by the
- * container.
- */
- static void build_callid_pvt(struct sip_pvt *pvt)
- {
- char buf[33];
- const char *host = S_OR(pvt->fromdomain, ast_sockaddr_stringify_remote(&pvt->ourip));
- ast_string_field_build(pvt, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host);
- }
- /*! \brief Unlink the given object from the container and return TRUE if it was in the container. */
- #define CONTAINER_UNLINK(container, obj, tag) \
- ({ \
- int found = 0; \
- typeof((obj)) __removed_obj; \
- __removed_obj = ao2_t_callback((container), \
- OBJ_UNLINK | OBJ_POINTER, ao2_match_by_addr, (obj), (tag)); \
- if (__removed_obj) { \
- ao2_ref(__removed_obj, -1); \
- found = 1; \
- } \
- found; \
- })
- /*!
- * \internal
- * \brief Safely change the callid of the given SIP dialog.
- *
- * \param pvt SIP private structure to change callid
- * \param callid Specified new callid to use. NULL if generate new callid.
- *
- * \return Nothing
- */
- static void change_callid_pvt(struct sip_pvt *pvt, const char *callid)
- {
- int in_dialog_container;
- int in_rtp_container;
- char *oldid = ast_strdupa(pvt->callid);
- ao2_lock(dialogs);
- ao2_lock(dialogs_rtpcheck);
- in_dialog_container = CONTAINER_UNLINK(dialogs, pvt,
- "About to change the callid -- remove the old name");
- in_rtp_container = CONTAINER_UNLINK(dialogs_rtpcheck, pvt,
- "About to change the callid -- remove the old name");
- if (callid) {
- ast_string_field_set(pvt, callid, callid);
- } else {
- build_callid_pvt(pvt);
- }
- if (in_dialog_container) {
- ao2_t_link(dialogs, pvt, "New dialog callid -- inserted back into table");
- }
- if (in_rtp_container) {
- ao2_t_link(dialogs_rtpcheck, pvt, "New dialog callid -- inserted back into table");
- }
- ao2_unlock(dialogs_rtpcheck);
- ao2_unlock(dialogs);
- if (strcmp(oldid, pvt->callid)) {
- ast_debug(1, "SIP call-id changed from '%s' to '%s'\n", oldid, pvt->callid);
- }
- }
- /*! \brief Build SIP Call-ID value for a REGISTER transaction */
- static void build_callid_registry(struct sip_registry *reg, const struct ast_sockaddr *ourip, const char *fromdomain)
- {
- char buf[33];
- const char *host = S_OR(fromdomain, ast_sockaddr_stringify_host_remote(ourip));
- ast_string_field_build(reg, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host);
- }
- /*! \brief Build SIP From tag value for REGISTER */
- static void build_localtag_registry(struct sip_registry *reg)
- {
- ast_string_field_build(reg, localtag, "as%08lx", (unsigned long)ast_random());
- }
- /*! \brief Make our SIP dialog tag */
- static void make_our_tag(struct sip_pvt *pvt)
- {
- ast_string_field_build(pvt, tag, "as%08lx", (unsigned long)ast_random());
- }
- /*! \brief Allocate Session-Timers struct w/in dialog */
- static struct sip_st_dlg* sip_st_alloc(struct sip_pvt *const p)
- {
- struct sip_st_dlg *stp;
- if (p->stimer) {
- ast_log(LOG_ERROR, "Session-Timer struct already allocated\n");
- return p->stimer;
- }
- if (!(stp = ast_calloc(1, sizeof(struct sip_st_dlg))))
- return NULL;
- p->stimer = stp;
- stp->st_schedid = -1; /* Session-Timers ast_sched scheduler id */
- return p->stimer;
- }
- static void sip_pvt_callid_set(struct sip_pvt *pvt, ast_callid callid)
- {
- pvt->logger_callid = callid;
- }
- /*! \brief Allocate sip_pvt structure, set defaults and link in the container.
- * Returns a reference to the object so whoever uses it later must
- * remember to release the reference.
- */
- struct sip_pvt *__sip_alloc(ast_string_field callid, struct ast_sockaddr *addr,
- int useglobal_nat, const int intended_method, struct sip_request *req, ast_callid logger_callid,
- const char *file, int line, const char *func)
- {
- struct sip_pvt *p;
- p = __ao2_alloc_debug(sizeof(*p), sip_destroy_fn,
- AO2_ALLOC_OPT_LOCK_MUTEX, "allocate a dialog(pvt) struct",
- file, line, func, 1);
- if (!p) {
- return NULL;
- }
- if (ast_string_field_init(p, 512)) {
- ao2_t_ref(p, -1, "failed to string_field_init, drop p");
- return NULL;
- }
- if (!(p->cc_params = ast_cc_config_params_init())) {
- ao2_t_ref(p, -1, "Yuck, couldn't allocate cc_params struct. Get rid o' p");
- return NULL;
- }
- if (logger_callid) {
- sip_pvt_callid_set(p, logger_callid);
- }
- p->caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- p->jointcaps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- p->peercaps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- p->redircaps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- p->prefcaps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- if (!p->caps|| !p->jointcaps || !p->peercaps || !p->redircaps || !p->prefcaps) {
- ao2_cleanup(p->caps);
- ao2_cleanup(p->jointcaps);
- ao2_cleanup(p->peercaps);
- ao2_cleanup(p->redircaps);
- ao2_cleanup(p->prefcaps);
- ao2_t_ref(p, -1, "Yuck, couldn't allocate format capabilities. Get rid o' p");
- return NULL;
- }
- /* If this dialog is created as a result of a request or response, lets store
- * some information about it in the dialog. */
- if (req) {
- struct sip_via *via;
- const char *cseq = sip_get_header(req, "Cseq");
- uint32_t seqno;
- /* get branch parameter from initial Request that started this dialog */
- via = parse_via(sip_get_header(req, "Via"));
- if (via) {
- /* only store the branch if it begins with the magic prefix "z9hG4bK", otherwise
- * it is not useful to us to have it */
- if (!ast_strlen_zero(via->branch) && !strncasecmp(via->branch, "z9hG4bK", 7)) {
- ast_string_field_set(p, initviabranch, via->branch);
- ast_string_field_set(p, initviasentby, via->sent_by);
- }
- free_via(via);
- }
- /* Store initial incoming cseq. An error in sscanf here is ignored. There is no approperiate
- * except not storing the number. CSeq validation must take place before dialog creation in find_call */
- if (!ast_strlen_zero(cseq) && (sscanf(cseq, "%30u", &seqno) == 1)) {
- p->init_icseq = seqno;
- }
- /* Later in ast_sip_ouraddrfor we need this to choose the right ip and port for the specific transport */
- set_socket_transport(&p->socket, req->socket.type);
- } else {
- set_socket_transport(&p->socket, AST_TRANSPORT_UDP);
- }
- p->socket.fd = -1;
- p->method = intended_method;
- p->initid = -1;
- p->waitid = -1;
- p->reinviteid = -1;
- p->autokillid = -1;
- p->request_queue_sched_id = -1;
- p->provisional_keepalive_sched_id = -1;
- p->t38id = -1;
- p->subscribed = NONE;
- p->stateid = -1;
- p->sessionversion_remote = -1;
- p->session_modify = TRUE;
- p->stimer = NULL;
- ast_copy_string(p->zone, default_zone, sizeof(p->zone));
- p->maxforwards = sip_cfg.default_max_forwards;
- if (intended_method != SIP_OPTIONS) { /* Peerpoke has it's own system */
- p->timer_t1 = global_t1; /* Default SIP retransmission timer T1 (RFC 3261) */
- p->timer_b = global_timer_b; /* Default SIP transaction timer B (RFC 3261) */
- }
- if (!addr) {
- p->ourip = internip;
- } else {
- ast_sockaddr_copy(&p->sa, addr);
- ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
- }
- /* Copy global flags to this PVT at setup. */
- ast_copy_flags(&p->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
- ast_copy_flags(&p->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
- ast_copy_flags(&p->flags[2], &global_flags[2], SIP_PAGE3_FLAGS_TO_COPY);
- p->do_history = recordhistory;
- p->branch = ast_random();
- make_our_tag(p);
- p->ocseq = INITIAL_CSEQ;
- p->allowed_methods = UINT_MAX;
- if (sip_methods[intended_method].need_rtp) {
- p->maxcallbitrate = default_maxcallbitrate;
- p->autoframing = global_autoframing;
- }
- if (useglobal_nat && addr) {
- /* Setup NAT structure according to global settings if we have an address */
- ast_sockaddr_copy(&p->recv, addr);
- check_via(p, req);
- do_setnat(p);
- }
- if (p->method != SIP_REGISTER) {
- ast_string_field_set(p, fromdomain, default_fromdomain);
- p->fromdomainport = default_fromdomainport;
- }
- build_via(p);
- if (!callid)
- build_callid_pvt(p);
- else
- ast_string_field_set(p, callid, callid);
- /* Assign default music on hold class */
- ast_string_field_set(p, mohinterpret, default_mohinterpret);
- ast_string_field_set(p, mohsuggest, default_mohsuggest);
- ast_format_cap_append_from_cap(p->caps, sip_cfg.caps, AST_MEDIA_TYPE_UNKNOWN);
- p->allowtransfer = sip_cfg.allowtransfer;
- if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
- (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
- p->noncodeccapability |= AST_RTP_DTMF;
- }
- ast_string_field_set(p, context, sip_cfg.default_context);
- ast_string_field_set(p, parkinglot, default_parkinglot);
- ast_string_field_set(p, engine, default_engine);
- AST_LIST_HEAD_INIT_NOLOCK(&p->request_queue);
- AST_LIST_HEAD_INIT_NOLOCK(&p->offered_media);
- /* Add to active dialog list */
- ao2_t_link(dialogs, p, "link pvt into dialogs table");
- ast_debug(1, "Allocating new SIP dialog for %s - %s (%s)\n", callid ? callid : p->callid, sip_methods[intended_method].text, p->rtp ? "With RTP" : "No RTP");
- return p;
- }
- /*!
- * \brief Process the Via header according to RFC 3261 section 18.2.2.
- * \param p a sip_pvt structure that will be modified according to the received
- * header
- * \param req a sip request with a Via header to process
- *
- * This function will update the destination of the response according to the
- * Via header in the request and RFC 3261 section 18.2.2. We do not have a
- * transport layer so we ignore certain values like the 'received' param (we
- * set the destination address to the address the request came from in the
- * respprep() function).
- *
- * \retval -1 error
- * \retval 0 success
- */
- static int process_via(struct sip_pvt *p, const struct sip_request *req)
- {
- struct sip_via *via = parse_via(sip_get_header(req, "Via"));
- if (!via) {
- ast_log(LOG_ERROR, "error processing via header\n");
- return -1;
- }
- if (via->maddr) {
- if (ast_sockaddr_resolve_first_transport(&p->sa, via->maddr, PARSE_PORT_FORBID, p->socket.type)) {
- ast_log(LOG_WARNING, "Can't find address for maddr '%s'\n", via->maddr);
- ast_log(LOG_ERROR, "error processing via header\n");
- free_via(via);
- return -1;
- }
- if (ast_sockaddr_is_ipv4_multicast(&p->sa)) {
- setsockopt(sipsock, IPPROTO_IP, IP_MULTICAST_TTL, &via->ttl, sizeof(via->ttl));
- }
- }
- ast_sockaddr_set_port(&p->sa, via->port ? via->port : STANDARD_SIP_PORT);
- free_via(via);
- return 0;
- }
- /* \brief arguments used for Request/Response to matching */
- struct match_req_args {
- int method;
- const char *callid;
- const char *totag;
- const char *fromtag;
- uint32_t seqno;
- /* Set if this method is a Response */
- int respid;
- /* Set if the method is a Request */
- const char *ruri;
- const char *viabranch;
- const char *viasentby;
- /* Set this if the Authentication header is present in the Request. */
- int authentication_present;
- };
- enum match_req_res {
- SIP_REQ_MATCH,
- SIP_REQ_NOT_MATCH,
- SIP_REQ_LOOP_DETECTED, /* multiple incoming requests with same call-id but different branch parameters have been detected */
- SIP_REQ_FORKED, /* An outgoing request has been forked as result of receiving two differing 200ok responses. */
- };
- /*
- * \brief Match a incoming Request/Response to a dialog
- *
- * \retval enum match_req_res indicating if the dialog matches the arg
- */
- static enum match_req_res match_req_to_dialog(struct sip_pvt *sip_pvt_ptr, struct match_req_args *arg)
- {
- const char *init_ruri = NULL;
- if (sip_pvt_ptr->initreq.headers) {
- init_ruri = REQ_OFFSET_TO_STR(&sip_pvt_ptr->initreq, rlpart2);
- }
- /*
- * Match Tags and call-id to Dialog
- */
- if (!ast_strlen_zero(arg->callid) && strcmp(sip_pvt_ptr->callid, arg->callid)) {
- /* call-id does not match. */
- return SIP_REQ_NOT_MATCH;
- }
- if (arg->method == SIP_RESPONSE) {
- /* Verify fromtag of response matches the tag we gave them. */
- if (strcmp(arg->fromtag, sip_pvt_ptr->tag)) {
- /* fromtag from response does not match our tag */
- return SIP_REQ_NOT_MATCH;
- }
- /* Verify totag if we have one stored for this dialog, but never be strict about this for
- * a response until the dialog is established */
- if (!ast_strlen_zero(sip_pvt_ptr->theirtag) && ast_test_flag(&sip_pvt_ptr->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) {
- if (ast_strlen_zero(arg->totag)) {
- /* missing totag when they already gave us one earlier */
- return SIP_REQ_NOT_MATCH;
- }
- /* compare the totag of response with the tag we have stored for them */
- if (strcmp(arg->totag, sip_pvt_ptr->theirtag)) {
- /* totag did not match what we had stored for them. */
- char invite_branch[32] = { 0, };
- if (sip_pvt_ptr->invite_branch) {
- snprintf(invite_branch, sizeof(invite_branch), "z9hG4bK%08x", (unsigned)sip_pvt_ptr->invite_branch);
- }
- /* Forked Request Detection
- *
- * If this is a 200ok response and the totags do not match, this
- * might be a forked response to an outgoing Request. Detection of
- * a forked response must meet the criteria below.
- *
- * 1. must be a 2xx Response
- * 2. call-d equal to call-id of Request. this is done earlier
- * 3. from-tag equal to from-tag of Request. this is done earlier
- * 4. branch parameter equal to branch of inital Request
- * 5. to-tag _NOT_ equal to previous 2xx response that already established the dialog.
- */
- if ((arg->respid == 200) &&
- !ast_strlen_zero(invite_branch) &&
- !ast_strlen_zero(arg->viabranch) &&
- !strcmp(invite_branch, arg->viabranch)) {
- return SIP_REQ_FORKED;
- }
- /* The totag did not match the one we had stored, and this is not a Forked Request. */
- return SIP_REQ_NOT_MATCH;
- }
- }
- } else {
- /* Verify the fromtag of Request matches the tag they provided earlier.
- * If this is a Request with authentication credentials, forget their old
- * tag as it is not valid after the 401 or 407 response. */
- if (!arg->authentication_present && strcmp(arg->fromtag, sip_pvt_ptr->theirtag)) {
- /* their tag does not match the one was have stored for them */
- return SIP_REQ_NOT_MATCH;
- }
- /* Verify if totag is present in Request, that it matches what we gave them as our tag earlier */
- if (!ast_strlen_zero(arg->totag) && (strcmp(arg->totag, sip_pvt_ptr->tag))) {
- /* totag from Request does not match our tag */
- return SIP_REQ_NOT_MATCH;
- }
- }
- /*
- * Compare incoming request against initial transaction.
- *
- * This is a best effort attempt at distinguishing forked requests from
- * our initial transaction. If all the elements are NOT in place to evaluate
- * this, this block is ignored and the dialog match is made regardless.
- * Once the totag is established after the dialog is confirmed, this is not necessary.
- *
- * CRITERIA required for initial transaction matching.
- *
- * 1. Is a Request
- * 2. Callid and theirtag match (this is done in the dialog matching block)
- * 3. totag is NOT present
- * 4. CSeq matchs our initial transaction's cseq number
- * 5. pvt has init via branch parameter stored
- */
- if ((arg->method != SIP_RESPONSE) && /* must be a Request */
- ast_strlen_zero(arg->totag) && /* must not have a totag */
- (sip_pvt_ptr->init_icseq == arg->seqno) && /* the cseq must be the same as this dialogs initial cseq */
- !ast_strlen_zero(sip_pvt_ptr->initviabranch) && /* The dialog must have started with a RFC3261 compliant branch tag */
- init_ruri) { /* the dialog must have an initial request uri associated with it */
- /* This Request matches all the criteria required for Loop/Merge detection.
- * Now we must go down the path of comparing VIA's and RURIs. */
- if (ast_strlen_zero(arg->viabranch) ||
- strcmp(arg->viabranch, sip_pvt_ptr->initviabranch) ||
- ast_strlen_zero(arg->viasentby) ||
- strcmp(arg->viasentby, sip_pvt_ptr->initviasentby)) {
- /* At this point, this request does not match this Dialog.*/
- /* if methods are different this is just a mismatch */
- if ((sip_pvt_ptr->method != arg->method)) {
- return SIP_REQ_NOT_MATCH;
- }
- /* If RUIs are different, this is a forked request to a separate URI.
- * Returning a mismatch allows this Request to be processed separately. */
- if (sip_uri_cmp(init_ruri, arg->ruri)) {
- /* not a match, request uris are different */
- return SIP_REQ_NOT_MATCH;
- }
- /* Loop/Merge Detected
- *
- * ---Current Matches to Initial Request---
- * request uri
- * Call-id
- * their-tag
- * no totag present
- * method
- * cseq
- *
- * --- Does not Match Initial Request ---
- * Top Via
- *
- * Without the same Via, this can not match our initial transaction for this dialog,
- * but given that this Request matches everything else associated with that initial
- * Request this is most certainly a Forked request in which we have already received
- * part of the fork.
- */
- return SIP_REQ_LOOP_DETECTED;
- }
- } /* end of Request Via check */
- /* Match Authentication Request.
- *
- * A Request with an Authentication header must come back with the
- * same Request URI. Otherwise it is not a match.
- */
- if ((arg->method != SIP_RESPONSE) && /* Must be a Request type to even begin checking this */
- ast_strlen_zero(arg->totag) && /* no totag is present to match */
- arg->authentication_present && /* Authentication header is present in Request */
- sip_uri_cmp(init_ruri, arg->ruri)) { /* Compare the Request URI of both the last Request and this new one */
- /* Authentication was provided, but the Request URI did not match the last one on this dialog. */
- return SIP_REQ_NOT_MATCH;
- }
- return SIP_REQ_MATCH;
- }
- /*! \brief This function creates a dialog to handle a forked request. This dialog
- * exists only to properly terminiate the the forked request immediately.
- */
- static void forked_invite_init(struct sip_request *req, const char *new_theirtag, struct sip_pvt *original, struct ast_sockaddr *addr)
- {
- struct sip_pvt *p;
- const char *callid;
- ast_callid logger_callid;
- sip_pvt_lock(original);
- callid = ast_strdupa(original->callid);
- logger_callid = original->logger_callid;
- sip_pvt_unlock(original);
- p = sip_alloc(callid, addr, 1, SIP_INVITE, req, logger_callid);
- if (!p) {
- return; /* alloc error */
- }
- /* Lock p and original private structures. */
- sip_pvt_lock(p);
- while (sip_pvt_trylock(original)) {
- /* Can't use DEADLOCK_AVOIDANCE since p is an ao2 object */
- sip_pvt_unlock(p);
- sched_yield();
- sip_pvt_lock(p);
- }
- p->invitestate = INV_TERMINATED;
- p->ocseq = original->ocseq;
- p->branch = original->branch;
- memcpy(&p->flags, &original->flags, sizeof(p->flags));
- copy_request(&p->initreq, &original->initreq);
- ast_string_field_set(p, theirtag, new_theirtag);
- ast_string_field_set(p, tag, original->tag);
- ast_string_field_set(p, uri, original->uri);
- ast_string_field_set(p, our_contact, original->our_contact);
- ast_string_field_set(p, fullcontact, original->fullcontact);
- sip_pvt_unlock(original);
- parse_ok_contact(p, req);
- build_route(p, req, 1, 0);
- transmit_request(p, SIP_ACK, p->ocseq, XMIT_UNRELIABLE, TRUE);
- transmit_request(p, SIP_BYE, 0, XMIT_RELIABLE, TRUE);
- pvt_set_needdestroy(p, "forked request"); /* this dialog will terminate once the BYE is responed to or times out. */
- sip_pvt_unlock(p);
- dialog_unref(p, "setup forked invite termination");
- }
- /*! \internal
- *
- * \brief Locks both pvt and pvt owner if owner is present.
- *
- * \note This function gives a ref to pvt->owner if it is present and locked.
- * This reference must be decremented after pvt->owner is unlocked.
- *
- * \note This function will never give you up,
- * \note This function will never let you down.
- * \note This function will run around and desert you.
- *
- * \pre pvt is not locked
- * \post pvt is locked
- * \post pvt->owner is locked and its reference count is increased (if pvt->owner is not NULL)
- *
- * \returns a pointer to the locked and reffed pvt->owner channel if it exists.
- */
- static struct ast_channel *sip_pvt_lock_full(struct sip_pvt *pvt)
- {
- struct ast_channel *chan;
- /* Locking is simple when it is done right. If you see a deadlock resulting
- * in this function, it is not this function's fault, Your problem exists elsewhere.
- * This function is perfect... seriously. */
- for (;;) {
- /* First, get the channel and grab a reference to it */
- sip_pvt_lock(pvt);
- chan = pvt->owner;
- if (chan) {
- /* The channel can not go away while we hold the pvt lock.
- * Give the channel a ref so it will not go away after we let
- * the pvt lock go. */
- ast_channel_ref(chan);
- } else {
- /* no channel, return pvt locked */
- return NULL;
- }
- /* We had to hold the pvt lock while getting a ref to the owner channel
- * but now we have to let this lock go in order to preserve proper
- * locking order when grabbing the channel lock */
- sip_pvt_unlock(pvt);
- /* Look, no deadlock avoidance, hooray! */
- ast_channel_lock(chan);
- sip_pvt_lock(pvt);
- if (pvt->owner == chan) {
- /* done */
- break;
- }
- /* If the owner changed while everything was unlocked, no problem,
- * just start over and everthing will work. This is rare, do not be
- * confused by this loop and think this it is an expensive operation.
- * The majority of the calls to this function will never involve multiple
- * executions of this loop. */
- ast_channel_unlock(chan);
- ast_channel_unref(chan);
- sip_pvt_unlock(pvt);
- }
- /* If owner exists, it is locked and reffed */
- return pvt->owner;
- }
- /*! \brief Set the owning channel on the \ref sip_pvt object */
- static void sip_set_owner(struct sip_pvt *p, struct ast_channel *chan)
- {
- p->owner = chan;
- if (p->rtp) {
- ast_rtp_instance_set_channel_id(p->rtp, p->owner ? ast_channel_uniqueid(p->owner) : "");
- }
- if (p->vrtp) {
- ast_rtp_instance_set_channel_id(p->vrtp, p->owner ? ast_channel_uniqueid(p->owner) : "");
- }
- if (p->trtp) {
- ast_rtp_instance_set_channel_id(p->trtp, p->owner ? ast_channel_uniqueid(p->owner) : "");
- }
- }
- /*! \brief find or create a dialog structure for an incoming SIP message.
- * Connect incoming SIP message to current dialog or create new dialog structure
- * Returns a reference to the sip_pvt object, remember to give it back once done.
- * Called by handle_request_do
- */
- static struct sip_pvt *__find_call(struct sip_request *req, struct ast_sockaddr *addr, const int intended_method,
- const char *file, int line, const char *func)
- {
- char totag[128];
- char fromtag[128];
- const char *callid = sip_get_header(req, "Call-ID");
- const char *from = sip_get_header(req, "From");
- const char *to = sip_get_header(req, "To");
- const char *cseq = sip_get_header(req, "Cseq");
- struct sip_pvt *sip_pvt_ptr;
- uint32_t seqno;
- /* Call-ID, to, from and Cseq are required by RFC 3261. (Max-forwards and via too - ignored now) */
- /* sip_get_header always returns non-NULL so we must use ast_strlen_zero() */
- if (ast_strlen_zero(callid) || ast_strlen_zero(to) ||
- ast_strlen_zero(from) || ast_strlen_zero(cseq) ||
- (sscanf(cseq, "%30u", &seqno) != 1)) {
- /* RFC 3261 section 24.4.1. Send a 400 Bad Request if the request is malformed. */
- if (intended_method != SIP_RESPONSE && intended_method != SIP_ACK) {
- transmit_response_using_temp(callid, addr, 1, intended_method,
- req, "400 Bad Request");
- }
- return NULL; /* Invalid packet */
- }
- if (sip_cfg.pedanticsipchecking) {
- /* In principle Call-ID's uniquely identify a call, but with a forking SIP proxy
- we need more to identify a branch - so we have to check branch, from
- and to tags to identify a call leg.
- For Asterisk to behave correctly, you need to turn on pedanticsipchecking
- in sip.conf
- */
- if (gettag(req, "To", totag, sizeof(totag)))
- req->has_to_tag = 1; /* Used in handle_request/response */
- gettag(req, "From", fromtag, sizeof(fromtag));
- ast_debug(5, "= Looking for Call ID: %s (Checking %s) --From tag %s --To-tag %s \n", callid, req->method==SIP_RESPONSE ? "To" : "From", fromtag, totag);
- /* All messages must always have From: tag */
- if (ast_strlen_zero(fromtag)) {
- ast_debug(5, "%s request has no from tag, dropping callid: %s from: %s\n", sip_methods[req->method].text , callid, from );
- return NULL;
- }
- /* reject requests that must always have a To: tag */
- if (ast_strlen_zero(totag) && (req->method == SIP_ACK || req->method == SIP_BYE || req->method == SIP_INFO )) {
- if (req->method != SIP_ACK) {
- transmit_response_using_temp(callid, addr, 1, intended_method, req, "481 Call leg/transaction does not exist");
- }
- ast_debug(5, "%s must have a to tag. dropping callid: %s from: %s\n", sip_methods[req->method].text , callid, from );
- return NULL;
- }
- }
- /* match on callid only for REGISTERs */
- if (!sip_cfg.pedanticsipchecking || req->method == SIP_REGISTER) {
- struct sip_pvt tmp_dialog = {
- .callid = callid,
- };
- sip_pvt_ptr = __ao2_find_debug(dialogs, &tmp_dialog, OBJ_POINTER,
- "find_call in dialogs", file, line, func);
- if (sip_pvt_ptr) { /* well, if we don't find it-- what IS in there? */
- /* Found the call */
- return sip_pvt_ptr;
- }
- } else { /* in pedantic mode! -- do the fancy search */
- struct sip_pvt tmp_dialog = {
- .callid = callid,
- };
- /* if a Outbound forked Request is detected, this pvt will point
- * to the dialog the Request is forking off of. */
- struct sip_pvt *fork_pvt = NULL;
- struct match_req_args args = { 0, };
- int found;
- struct ao2_iterator *iterator = __ao2_callback_debug(dialogs,
- OBJ_POINTER | OBJ_MULTIPLE,
- dialog_find_multiple,
- &tmp_dialog,
- "pedantic ao2_find in dialogs",
- file, line, func);
- struct sip_via *via = NULL;
- args.method = req->method;
- args.callid = NULL; /* we already matched this. */
- args.totag = totag;
- args.fromtag = fromtag;
- args.seqno = seqno;
- /* get via header information. */
- args.ruri = REQ_OFFSET_TO_STR(req, rlpart2);
- via = parse_via(sip_get_header(req, "Via"));
- if (via) {
- args.viasentby = via->sent_by;
- args.viabranch = via->branch;
- }
- /* determine if this is a Request with authentication credentials. */
- if (!ast_strlen_zero(sip_get_header(req, "Authorization")) ||
- !ast_strlen_zero(sip_get_header(req, "Proxy-Authorization"))) {
- args.authentication_present = 1;
- }
- /* if it is a response, get the response code */
- if (req->method == SIP_RESPONSE) {
- const char* e = ast_skip_blanks(REQ_OFFSET_TO_STR(req, rlpart2));
- int respid;
- if (!ast_strlen_zero(e) && (sscanf(e, "%30d", &respid) == 1)) {
- args.respid = respid;
- }
- }
- /* Iterate a list of dialogs already matched by Call-id */
- while (iterator && (sip_pvt_ptr = ao2_iterator_next(iterator))) {
- sip_pvt_lock(sip_pvt_ptr);
- found = match_req_to_dialog(sip_pvt_ptr, &args);
- sip_pvt_unlock(sip_pvt_ptr);
- switch (found) {
- case SIP_REQ_MATCH:
- ao2_iterator_destroy(iterator);
- dialog_unref(fork_pvt, "unref fork_pvt");
- free_via(via);
- return sip_pvt_ptr; /* return pvt with ref */
- case SIP_REQ_LOOP_DETECTED:
- /* This is likely a forked Request that somehow resulted in us receiving multiple parts of the fork.
- * RFC 3261 section 8.2.2.2, Indicate that we want to merge requests by sending a 482 response. */
- transmit_response_using_temp(callid, addr, 1, intended_method, req, "482 (Loop Detected)");
- __ao2_ref_debug(sip_pvt_ptr, -1, "pvt did not match incoming SIP msg, unref from search.",
- file, line, func);
- ao2_iterator_destroy(iterator);
- dialog_unref(fork_pvt, "unref fork_pvt");
- free_via(via);
- return NULL;
- case SIP_REQ_FORKED:
- dialog_unref(fork_pvt, "throwing way pvt to fork off of.");
- fork_pvt = dialog_ref(sip_pvt_ptr, "this pvt has a forked request, save this off to copy information into new dialog\n");
- /* fall through */
- case SIP_REQ_NOT_MATCH:
- default:
- __ao2_ref_debug(sip_pvt_ptr, -1, "pvt did not match incoming SIP msg, unref from search",
- file, line, func);
- break;
- }
- }
- if (iterator) {
- ao2_iterator_destroy(iterator);
- }
- /* Handle any possible forked requests. This must be done only after transaction matching is complete. */
- if (fork_pvt) {
- /* XXX right now we only support handling forked INVITE Requests. Any other
- * forked request type must be added here. */
- if (fork_pvt->method == SIP_INVITE) {
- forked_invite_init(req, args.totag, fork_pvt, addr);
- dialog_unref(fork_pvt, "throwing way old forked pvt");
- free_via(via);
- return NULL;
- }
- fork_pvt = dialog_unref(fork_pvt, "throwing way pvt to fork off of");
- }
- free_via(via);
- } /* end of pedantic mode Request/Reponse to Dialog matching */
- /* See if the method is capable of creating a dialog */
- if (sip_methods[intended_method].can_create == CAN_CREATE_DIALOG) {
- struct sip_pvt *p = NULL;
- ast_callid logger_callid = 0;
- if (intended_method == SIP_INVITE) {
- logger_callid = ast_create_callid();
- }
- /* Ok, time to create a new SIP dialog object, a pvt */
- if (!(p = sip_alloc(callid, addr, 1, intended_method, req, logger_callid))) {
- /* We have a memory or file/socket error (can't allocate RTP sockets or something) so we're not
- getting a dialog from sip_alloc.
- Without a dialog we can't retransmit and handle ACKs and all that, but at least
- send an error message.
- Sorry, we apologize for the inconvienience
- */
- transmit_response_using_temp(callid, addr, 1, intended_method, req, "500 Server internal error");
- ast_debug(4, "Failed allocating SIP dialog, sending 500 Server internal error and giving up\n");
- }
- return p; /* can be NULL */
- } else if( sip_methods[intended_method].can_create == CAN_CREATE_DIALOG_UNSUPPORTED_METHOD) {
- /* A method we do not support, let's take it on the volley */
- transmit_response_using_temp(callid, addr, 1, intended_method, req, "501 Method Not Implemented");
- ast_debug(2, "Got a request with unsupported SIP method.\n");
- } else if (intended_method != SIP_RESPONSE && intended_method != SIP_ACK) {
- /* This is a request outside of a dialog that we don't know about */
- transmit_response_using_temp(callid, addr, 1, intended_method, req, "481 Call leg/transaction does not exist");
- ast_debug(2, "That's odd... Got a request in unknown dialog. Callid %s\n", callid ? callid : "<unknown>");
- }
- /* We do not respond to responses for dialogs that we don't know about, we just drop
- the session quickly */
- if (intended_method == SIP_RESPONSE)
- ast_debug(2, "That's odd... Got a response on a call we don't know about. Callid %s\n", callid ? callid : "<unknown>");
- return NULL;
- }
- /*! \brief create sip_registry object from register=> line in sip.conf and link into reg container */
- static int sip_register(const char *value, int lineno)
- {
- struct sip_registry *reg;
- reg = ao2_t_find(registry_list, value, OBJ_SEARCH_KEY, "check for existing registry");
- if (reg) {
- ao2_t_ref(reg, -1, "throw away found registry");
- return 0;
- }
- if (!(reg = ao2_t_alloc(sizeof(*reg), sip_registry_destroy, "allocate a registry struct"))) {
- ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry entry\n");
- return -1;
- }
- if (ast_string_field_init(reg, 256)) {
- ao2_t_ref(reg, -1, "failed to string_field_init, drop reg");
- return -1;
- }
- ast_string_field_set(reg, configvalue, value);
- if (sip_parse_register_line(reg, default_expiry, value, lineno)) {
- ao2_t_ref(reg, -1, "failure to parse, unref the reg pointer");
- return -1;
- }
- /* set default expiry if necessary */
- if (reg->refresh && !reg->expiry && !reg->configured_expiry) {
- reg->refresh = reg->expiry = reg->configured_expiry = default_expiry;
- }
- ao2_t_link(registry_list, reg, "link reg to registry_list");
- ao2_t_ref(reg, -1, "unref the reg pointer");
- return 0;
- }
- /*! \brief Parse mwi=> line in sip.conf and add to list */
- static int sip_subscribe_mwi(const char *value, int lineno)
- {
- struct sip_subscription_mwi *mwi;
- int portnum = 0;
- enum ast_transport transport = AST_TRANSPORT_UDP;
- char buf[256] = "";
- char *username = NULL, *hostname = NULL, *secret = NULL, *authuser = NULL, *porta = NULL, *mailbox = NULL;
- if (!value) {
- return -1;
- }
- ast_copy_string(buf, value, sizeof(buf));
- username = buf;
- if ((hostname = strrchr(buf, '@'))) {
- *hostname++ = '\0';
- } else {
- return -1;
- }
- if ((secret = strchr(username, ':'))) {
- *secret++ = '\0';
- if ((authuser = strchr(secret, ':'))) {
- *authuser++ = '\0';
- }
- }
- if ((mailbox = strchr(hostname, '/'))) {
- *mailbox++ = '\0';
- }
- if (ast_strlen_zero(username) || ast_strlen_zero(hostname) || ast_strlen_zero(mailbox)) {
- ast_log(LOG_WARNING, "Format for MWI subscription is user[:secret[:authuser]]@host[:port]/mailbox at line %d\n", lineno);
- return -1;
- }
- if ((porta = strchr(hostname, ':'))) {
- *porta++ = '\0';
- if (!(portnum = atoi(porta))) {
- ast_log(LOG_WARNING, "%s is not a valid port number at line %d\n", porta, lineno);
- return -1;
- }
- }
- if (!(mwi = ao2_t_alloc(sizeof(*mwi), sip_subscribe_mwi_destroy, "allocate an mwi struct"))) {
- return -1;
- }
- if (ast_string_field_init(mwi, 256)) {
- ao2_t_ref(mwi, -1, "failed to string_field_init, drop mwi");
- return -1;
- }
- ast_string_field_set(mwi, username, username);
- if (secret) {
- ast_string_field_set(mwi, secret, secret);
- }
- if (authuser) {
- ast_string_field_set(mwi, authuser, authuser);
- }
- ast_string_field_set(mwi, hostname, hostname);
- ast_string_field_set(mwi, mailbox, mailbox);
- mwi->resub = -1;
- mwi->portno = portnum;
- mwi->transport = transport;
- ao2_t_link(subscription_mwi_list, mwi, "link new mwi object");
- ao2_t_ref(mwi, -1, "unref to match ao2_t_alloc");
- return 0;
- }
- static void mark_method_allowed(unsigned int *allowed_methods, enum sipmethod method)
- {
- (*allowed_methods) |= (1 << method);
- }
- static void mark_method_unallowed(unsigned int *allowed_methods, enum sipmethod method)
- {
- (*allowed_methods) &= ~(1 << method);
- }
- /*! \brief Check if method is allowed for a device or a dialog */
- static int is_method_allowed(unsigned int *allowed_methods, enum sipmethod method)
- {
- return ((*allowed_methods) >> method) & 1;
- }
- static void mark_parsed_methods(unsigned int *methods, char *methods_str)
- {
- char *method;
- for (method = strsep(&methods_str, ","); !ast_strlen_zero(method); method = strsep(&methods_str, ",")) {
- int id = find_sip_method(ast_skip_blanks(method));
- if (id == SIP_UNKNOWN) {
- continue;
- }
- mark_method_allowed(methods, id);
- }
- }
- /*!
- * \brief parse the Allow header to see what methods the endpoint we
- * are communicating with allows.
- *
- * We parse the allow header on incoming Registrations and save the
- * result to the SIP peer that is registering. When the registration
- * expires, we clear what we know about the peer's allowed methods.
- * When the peer re-registers, we once again parse to see if the
- * list of allowed methods has changed.
- *
- * For peers that do not register, we parse the first message we receive
- * during a call to see what is allowed, and save the information
- * for the duration of the call.
- * \param req The SIP request we are parsing
- * \retval The methods allowed
- */
- static unsigned int parse_allowed_methods(struct sip_request *req)
- {
- char *allow = ast_strdupa(sip_get_header(req, "Allow"));
- unsigned int allowed_methods = SIP_UNKNOWN;
- if (ast_strlen_zero(allow)) {
- /* I have witnessed that REGISTER requests from Polycom phones do not
- * place the phone's allowed methods in an Allow header. Instead, they place the
- * allowed methods in a methods= parameter in the Contact header.
- */
- char *contact = ast_strdupa(sip_get_header(req, "Contact"));
- char *methods = strstr(contact, ";methods=");
- if (ast_strlen_zero(methods)) {
- /* RFC 3261 states:
- *
- * "The absence of an Allow header field MUST NOT be
- * interpreted to mean that the UA sending the message supports no
- * methods. Rather, it implies that the UA is not providing any
- * information on what methods it supports."
- *
- * For simplicity, we'll assume that the peer allows all known
- * SIP methods if they have no Allow header. We can then clear out the necessary
- * bits if the peer lets us know that we have sent an unsupported method.
- */
- return UINT_MAX;
- }
- allow = ast_strip_quoted(methods + 9, "\"", "\"");
- }
- mark_parsed_methods(&allowed_methods, allow);
- return allowed_methods;
- }
- /*! A wrapper for parse_allowed_methods geared toward sip_pvts
- *
- * This function, in addition to setting the allowed methods for a sip_pvt
- * also will take into account the setting of the SIP_PAGE2_RPID_UPDATE flag.
- *
- * \param pvt The sip_pvt we are setting the allowed_methods for
- * \param req The request which we are parsing
- * \retval The methods alloweded by the sip_pvt
- */
- static unsigned int set_pvt_allowed_methods(struct sip_pvt *pvt, struct sip_request *req)
- {
- pvt->allowed_methods = parse_allowed_methods(req);
- if (ast_test_flag(&pvt->flags[1], SIP_PAGE2_RPID_UPDATE)) {
- mark_method_allowed(&pvt->allowed_methods, SIP_UPDATE);
- }
- pvt->allowed_methods &= ~(pvt->disallowed_methods);
- return pvt->allowed_methods;
- }
- /*! \brief Parse multiline SIP headers into one header
- This is enabled if pedanticsipchecking is enabled */
- static void lws2sws(struct ast_str *data)
- {
- char *msgbuf = ast_str_buffer(data);
- int len = ast_str_strlen(data);
- int h = 0, t = 0;
- int lws = 0;
- for (; h < len;) {
- /* Eliminate all CRs */
- if (msgbuf[h] == '\r') {
- h++;
- continue;
- }
- /* Check for end-of-line */
- if (msgbuf[h] == '\n') {
- /* Check for end-of-message */
- if (h + 1 == len)
- break;
- /* Check for a continuation line */
- if (msgbuf[h + 1] == ' ' || msgbuf[h + 1] == '\t') {
- /* Merge continuation line */
- h++;
- continue;
- }
- /* Propagate LF and start new line */
- msgbuf[t++] = msgbuf[h++];
- lws = 0;
- continue;
- }
- if (msgbuf[h] == ' ' || msgbuf[h] == '\t') {
- if (lws) {
- h++;
- continue;
- }
- msgbuf[t++] = msgbuf[h++];
- lws = 1;
- continue;
- }
- msgbuf[t++] = msgbuf[h++];
- if (lws)
- lws = 0;
- }
- msgbuf[t] = '\0';
- ast_str_update(data);
- }
- /*! \brief Parse a SIP message
- \note this function is used both on incoming and outgoing packets
- */
- static int parse_request(struct sip_request *req)
- {
- char *c = ast_str_buffer(req->data);
- ptrdiff_t *dst = req->header;
- int i = 0;
- unsigned int lim = SIP_MAX_HEADERS - 1;
- unsigned int skipping_headers = 0;
- ptrdiff_t current_header_offset = 0;
- char *previous_header = "";
- req->header[0] = 0;
- req->headers = -1; /* mark that we are working on the header */
- for (; *c; c++) {
- if (*c == '\r') { /* remove \r */
- *c = '\0';
- } else if (*c == '\n') { /* end of this line */
- *c = '\0';
- current_header_offset = (c + 1) - ast_str_buffer(req->data);
- previous_header = ast_str_buffer(req->data) + dst[i];
- if (skipping_headers) {
- /* check to see if this line is blank; if so, turn off
- the skipping flag, so the next line will be processed
- as a body line */
- if (ast_strlen_zero(previous_header)) {
- skipping_headers = 0;
- }
- dst[i] = current_header_offset; /* record start of next line */
- continue;
- }
- if (sipdebug) {
- ast_debug(4, "%7s %2d [%3d]: %s\n",
- req->headers < 0 ? "Header" : "Body",
- i, (int) strlen(previous_header), previous_header);
- }
- if (ast_strlen_zero(previous_header) && req->headers < 0) {
- req->headers = i; /* record number of header lines */
- dst = req->line; /* start working on the body */
- i = 0;
- lim = SIP_MAX_LINES - 1;
- } else { /* move to next line, check for overflows */
- if (i++ == lim) {
- /* if we're processing headers, then skip any remaining
- headers and move on to processing the body, otherwise
- we're done */
- if (req->headers != -1) {
- break;
- } else {
- req->headers = i;
- dst = req->line;
- i = 0;
- lim = SIP_MAX_LINES - 1;
- skipping_headers = 1;
- }
- }
- }
- dst[i] = current_header_offset; /* record start of next line */
- }
- }
- /* Check for last header or body line without CRLF. The RFC for SDP requires CRLF,
- but since some devices send without, we'll be generous in what we accept. However,
- if we've already reached the maximum number of lines for portion of the message
- we were parsing, we can't accept any more, so just ignore it.
- */
- previous_header = ast_str_buffer(req->data) + dst[i];
- if ((i < lim) && !ast_strlen_zero(previous_header)) {
- if (sipdebug) {
- ast_debug(4, "%7s %2d [%3d]: %s\n",
- req->headers < 0 ? "Header" : "Body",
- i, (int) strlen(previous_header), previous_header );
- }
- i++;
- }
- /* update count of header or body lines */
- if (req->headers >= 0) { /* we are in the body */
- req->lines = i;
- } else { /* no body */
- req->headers = i;
- req->lines = 0;
- /* req->data->used will be a NULL byte */
- req->line[0] = ast_str_strlen(req->data);
- }
- if (*c) {
- ast_log(LOG_WARNING, "Too many lines, skipping <%s>\n", c);
- }
- /* Split up the first line parts */
- return determine_firstline_parts(req);
- }
- /*!
- \brief Determine whether a SIP message contains an SDP in its body
- \param req the SIP request to process
- \return 1 if SDP found, 0 if not found
- Also updates req->sdp_start and req->sdp_count to indicate where the SDP
- lives in the message body.
- */
- static int find_sdp(struct sip_request *req)
- {
- const char *content_type;
- const char *content_length;
- const char *search;
- char *boundary;
- unsigned int x;
- int boundaryisquoted = FALSE;
- int found_application_sdp = FALSE;
- int found_end_of_headers = FALSE;
- content_length = sip_get_header(req, "Content-Length");
- if (!ast_strlen_zero(content_length)) {
- if (sscanf(content_length, "%30u", &x) != 1) {
- ast_log(LOG_WARNING, "Invalid Content-Length: %s\n", content_length);
- return 0;
- }
- /* Content-Length of zero means there can't possibly be an
- SDP here, even if the Content-Type says there is */
- if (x == 0)
- return 0;
- }
- content_type = sip_get_header(req, "Content-Type");
- /* if the body contains only SDP, this is easy */
- if (!strncasecmp(content_type, "application/sdp", 15)) {
- req->sdp_start = 0;
- req->sdp_count = req->lines;
- return req->lines ? 1 : 0;
- }
- /* if it's not multipart/mixed, there cannot be an SDP */
- if (strncasecmp(content_type, "multipart/mixed", 15))
- return 0;
- /* if there is no boundary marker, it's invalid */
- if ((search = strcasestr(content_type, ";boundary=")))
- search += 10;
- else if ((search = strcasestr(content_type, "; boundary=")))
- search += 11;
- else
- return 0;
- if (ast_strlen_zero(search))
- return 0;
- /* If the boundary is quoted with ", remove quote */
- if (*search == '\"') {
- search++;
- boundaryisquoted = TRUE;
- }
- /* make a duplicate of the string, with two extra characters
- at the beginning */
- boundary = ast_strdupa(search - 2);
- boundary[0] = boundary[1] = '-';
- /* Remove final quote */
- if (boundaryisquoted)
- boundary[strlen(boundary) - 1] = '\0';
- /* search for the boundary marker, the empty line delimiting headers from
- sdp part and the end boundry if it exists */
- for (x = 0; x < (req->lines); x++) {
- const char *line = REQ_OFFSET_TO_STR(req, line[x]);
- if (!strncasecmp(line, boundary, strlen(boundary))){
- if (found_application_sdp && found_end_of_headers) {
- req->sdp_count = (x - 1) - req->sdp_start;
- return 1;
- }
- found_application_sdp = FALSE;
- }
- if (!strcasecmp(line, "Content-Type: application/sdp"))
- found_application_sdp = TRUE;
- if (ast_strlen_zero(line)) {
- if (found_application_sdp && !found_end_of_headers){
- req->sdp_start = x;
- found_end_of_headers = TRUE;
- }
- }
- }
- if (found_application_sdp && found_end_of_headers) {
- req->sdp_count = x - req->sdp_start;
- return TRUE;
- }
- return FALSE;
- }
- /*! \brief Change hold state for a call */
- static void change_hold_state(struct sip_pvt *dialog, struct sip_request *req, int holdstate, int sendonly)
- {
- if (sip_cfg.notifyhold && (!holdstate || !ast_test_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD))) {
- sip_peer_hold(dialog, holdstate);
- }
- append_history(dialog, holdstate ? "Hold" : "Unhold", "%s", ast_str_buffer(req->data));
- if (!holdstate) { /* Put off remote hold */
- ast_clear_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD); /* Clear both flags */
- return;
- }
- /* No address for RTP, we're on hold */
- /* Ensure hold flags are cleared so that overlapping flags do not conflict */
- ast_clear_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD);
- if (sendonly == 1) /* One directional hold (sendonly/recvonly) */
- ast_set_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD_ONEDIR);
- else if (sendonly == 2) /* Inactive stream */
- ast_set_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD_INACTIVE);
- else
- ast_set_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD_ACTIVE);
- return;
- }
- /*! \internal
- * \brief Returns whether or not the address is null or ANY / unspecified (0.0.0.0 or ::)
- * \retval TRUE if the address is null or any
- * \retval FALSE if the address it not null or any
- * \note In some circumstances, calls should be placed on hold if either of these conditions exist.
- */
- static int sockaddr_is_null_or_any(const struct ast_sockaddr *addr)
- {
- return ast_sockaddr_isnull(addr) || ast_sockaddr_is_any(addr);
- }
- /*! \brief Check the media stream list to see if the given type already exists */
- static int has_media_stream(struct sip_pvt *p, enum media_type m)
- {
- struct offered_media *offer = NULL;
- AST_LIST_TRAVERSE(&p->offered_media, offer, next) {
- if (m == offer->type) {
- return 1;
- }
- }
- return 0;
- }
- /*! \brief Process SIP SDP offer, select formats and activate media channels
- If offer is rejected, we will not change any properties of the call
- Return 0 on success, a negative value on errors.
- Must be called after find_sdp().
- */
- static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action)
- {
- int res = 0;
- /* Iterators for SDP parsing */
- int start = req->sdp_start;
- int next = start;
- int iterator = start;
- /* Temporary vars for SDP parsing */
- char type = '\0';
- const char *value = NULL;
- const char *m = NULL; /* SDP media offer */
- const char *nextm = NULL;
- int len = -1;
- struct offered_media *offer;
- /* Host information */
- struct ast_sockaddr sessionsa;
- struct ast_sockaddr audiosa;
- struct ast_sockaddr videosa;
- struct ast_sockaddr textsa;
- struct ast_sockaddr imagesa;
- struct ast_sockaddr *sa = NULL; /*!< RTP audio destination IP address */
- struct ast_sockaddr *vsa = NULL; /*!< RTP video destination IP address */
- struct ast_sockaddr *tsa = NULL; /*!< RTP text destination IP address */
- struct ast_sockaddr *isa = NULL; /*!< UDPTL image destination IP address */
- int portno = -1; /*!< RTP audio destination port number */
- int vportno = -1; /*!< RTP video destination port number */
- int tportno = -1; /*!< RTP text destination port number */
- int udptlportno = -1; /*!< UDPTL image destination port number */
- /* Peer capability is the capability in the SDP, non codec is RFC2833 DTMF (101) */
- struct ast_format_cap *peercapability = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- struct ast_format_cap *vpeercapability = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- struct ast_format_cap *tpeercapability = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- int peernoncodeccapability = 0, vpeernoncodeccapability = 0, tpeernoncodeccapability = 0;
- struct ast_rtp_codecs newaudiortp = AST_RTP_CODECS_NULL_INIT;
- struct ast_rtp_codecs newvideortp = AST_RTP_CODECS_NULL_INIT;
- struct ast_rtp_codecs newtextrtp = AST_RTP_CODECS_NULL_INIT;
- struct ast_format_cap *newjointcapability = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); /* Negotiated capability */
- struct ast_format_cap *newpeercapability = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- int newnoncodeccapability;
- const char *codecs;
- unsigned int codec;
- /* SRTP */
- int secure_audio = FALSE;
- int secure_video = FALSE;
- /* Others */
- int sendonly = -1;
- unsigned int numberofports;
- int last_rtpmap_codec = 0;
- int red_data_pt[10]; /* For T.140 RED */
- int red_num_gen = 0; /* For T.140 RED */
- char red_fmtp[100] = "empty"; /* For T.140 RED */
- int debug = sip_debug_test_pvt(p);
- /* START UNKNOWN */
- struct ast_str *codec_buf = ast_str_alloca(64);
- struct ast_format *tmp_fmt;
- /* END UNKNOWN */
- /* Initial check */
- if (!p->rtp) {
- ast_log(LOG_ERROR, "Got SDP but have no RTP session allocated.\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if (!peercapability || !vpeercapability || !tpeercapability || !newpeercapability || !newjointcapability) {
- res = -1;
- goto process_sdp_cleanup;
- }
- if (ast_rtp_codecs_payloads_initialize(&newaudiortp) || ast_rtp_codecs_payloads_initialize(&newvideortp) ||
- ast_rtp_codecs_payloads_initialize(&newtextrtp)) {
- res = -1;
- goto process_sdp_cleanup;
- }
- /* Update our last rtprx when we receive an SDP, too */
- p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
- offered_media_list_destroy(p);
- /* Scan for the first media stream (m=) line to limit scanning of globals */
- nextm = get_sdp_iterate(&next, req, "m");
- if (ast_strlen_zero(nextm)) {
- ast_log(LOG_WARNING, "Insufficient information for SDP (m= not found)\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- /* Scan session level SDP parameters (lines before first media stream) */
- while ((type = get_sdp_line(&iterator, next - 1, req, &value)) != '\0') {
- int processed = FALSE;
- switch (type) {
- case 'o':
- /* If we end up receiving SDP that doesn't actually modify the session we don't want to treat this as a fatal
- * error. We just want to ignore the SDP and let the rest of the packet be handled as normal.
- */
- if (!process_sdp_o(value, p)) {
- res = (p->session_modify == FALSE) ? 0 : -1;
- goto process_sdp_cleanup;
- }
- processed = TRUE;
- break;
- case 'c':
- if (process_sdp_c(value, &sessionsa)) {
- processed = TRUE;
- sa = &sessionsa;
- vsa = sa;
- tsa = sa;
- isa = sa;
- }
- break;
- case 'a':
- if (process_sdp_a_sendonly(value, &sendonly)) {
- processed = TRUE;
- }
- else if (process_sdp_a_audio(value, p, &newaudiortp, &last_rtpmap_codec))
- processed = TRUE;
- else if (process_sdp_a_video(value, p, &newvideortp, &last_rtpmap_codec))
- processed = TRUE;
- else if (process_sdp_a_text(value, p, &newtextrtp, red_fmtp, &red_num_gen, red_data_pt, &last_rtpmap_codec))
- processed = TRUE;
- else if (process_sdp_a_image(value, p))
- processed = TRUE;
- if (process_sdp_a_ice(value, p, p->rtp)) {
- processed = TRUE;
- }
- if (process_sdp_a_ice(value, p, p->vrtp)) {
- processed = TRUE;
- }
- if (process_sdp_a_ice(value, p, p->trtp)) {
- processed = TRUE;
- }
- if (process_sdp_a_dtls(value, p, p->rtp)) {
- processed = TRUE;
- if (p->srtp) {
- ast_set_flag(p->srtp, AST_SRTP_CRYPTO_OFFER_OK);
- }
- }
- if (process_sdp_a_dtls(value, p, p->vrtp)) {
- processed = TRUE;
- if (p->vsrtp) {
- ast_set_flag(p->vsrtp, AST_SRTP_CRYPTO_OFFER_OK);
- }
- }
- if (process_sdp_a_dtls(value, p, p->trtp)) {
- processed = TRUE;
- if (p->tsrtp) {
- ast_set_flag(p->tsrtp, AST_SRTP_CRYPTO_OFFER_OK);
- }
- }
- break;
- }
- ast_debug(3, "Processing session-level SDP %c=%s... %s\n", type, value, (processed == TRUE)? "OK." : "UNSUPPORTED OR FAILED.");
- }
- /* default: novideo and notext set */
- p->novideo = TRUE;
- p->notext = TRUE;
- /* Scan media stream (m=) specific parameters loop */
- while (!ast_strlen_zero(nextm)) {
- int audio = FALSE;
- int video = FALSE;
- int image = FALSE;
- int text = FALSE;
- int processed_crypto = FALSE;
- char protocol[18] = {0,};
- unsigned int x;
- struct ast_rtp_engine_dtls *dtls;
- numberofports = 0;
- len = -1;
- start = next;
- m = nextm;
- iterator = next;
- nextm = get_sdp_iterate(&next, req, "m");
- if (!(offer = ast_calloc(1, sizeof(*offer)))) {
- ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer list\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- AST_LIST_INSERT_TAIL(&p->offered_media, offer, next);
- offer->type = SDP_UNKNOWN;
- /* Check for 'audio' media offer */
- if (strncmp(m, "audio ", 6) == 0) {
- if ((sscanf(m, "audio %30u/%30u %17s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
- (sscanf(m, "audio %30u %17s %n", &x, protocol, &len) == 2 && len > 0)) {
- codecs = m + len;
- /* produce zero-port m-line since it may be needed later
- * length is "m=audio 0 " + protocol + " " + codecs + "\r\n\0" */
- if (!(offer->decline_m_line = ast_malloc(10 + strlen(protocol) + 1 + strlen(codecs) + 3))) {
- ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- /* guaranteed to be exactly the right length */
- sprintf(offer->decline_m_line, "m=audio 0 %s %s\r\n", protocol, codecs);
- if (x == 0) {
- ast_debug(1, "Ignoring audio media offer because port number is zero\n");
- continue;
- }
- if (has_media_stream(p, SDP_AUDIO)) {
- ast_log(LOG_WARNING, "Declining non-primary audio stream: %s\n", m);
- continue;
- }
- /* Check number of ports offered for stream */
- if (numberofports > 1) {
- ast_log(LOG_WARNING, "%u ports offered for audio media, not supported by Asterisk. Will try anyway...\n", numberofports);
- }
- if ((!strcmp(protocol, "RTP/SAVPF") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
- if (req->method != SIP_RESPONSE) {
- ast_log(LOG_NOTICE, "Received SAVPF profle in audio offer but AVPF is not enabled, enabling: %s\n", m);
- secure_audio = 1;
- ast_set_flag(&p->flags[2], SIP_PAGE3_USE_AVPF);
- }
- else {
- ast_log(LOG_WARNING, "Received SAVPF profle in audio answer but AVPF is not enabled: %s\n", m);
- continue;
- }
- } else if ((!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVP")) && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
- if (req->method != SIP_RESPONSE) {
- ast_log(LOG_NOTICE, "Received SAVP profle in audio offer but AVPF is enabled, disabling: %s\n", m);
- secure_audio = 1;
- ast_clear_flag(&p->flags[2], SIP_PAGE3_USE_AVPF);
- }
- else {
- ast_log(LOG_WARNING, "Received SAVP profile in audio offer but AVPF is enabled: %s\n", m);
- continue;
- }
- } else if (!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) {
- secure_audio = 1;
- processed_crypto = 1;
- if (p->srtp) {
- ast_set_flag(p->srtp, AST_SRTP_CRYPTO_OFFER_OK);
- }
- } else if (!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "RTP/SAVPF")) {
- secure_audio = 1;
- } else if (!strcmp(protocol, "RTP/AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
- if (req->method != SIP_RESPONSE) {
- ast_log(LOG_NOTICE, "Received AVPF profile in audio offer but AVPF is not enabled, enabling: %s\n", m);
- ast_set_flag(&p->flags[2], SIP_PAGE3_USE_AVPF);
- }
- else {
- ast_log(LOG_WARNING, "Received AVP profile in audio answer but AVPF is enabled: %s\n", m);
- continue;
- }
- } else if (!strcmp(protocol, "RTP/AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
- if (req->method != SIP_RESPONSE) {
- ast_log(LOG_NOTICE, "Received AVP profile in audio answer but AVPF is enabled, disabling: %s\n", m);
- ast_clear_flag(&p->flags[2], SIP_PAGE3_USE_AVPF);
- }
- else {
- ast_log(LOG_WARNING, "Received AVP profile in audio answer but AVPF is enabled: %s\n", m);
- continue;
- }
- } else if ((!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) &&
- (!(dtls = ast_rtp_instance_get_dtls(p->rtp)) || !dtls->active(p->rtp))) {
- ast_log(LOG_WARNING, "Received UDP/TLS in audio offer but DTLS is not enabled: %s\n", m);
- continue;
- } else if (strcmp(protocol, "RTP/AVP") && strcmp(protocol, "RTP/AVPF")) {
- ast_log(LOG_WARNING, "Unknown RTP profile in audio offer: %s\n", m);
- continue;
- }
- audio = TRUE;
- offer->type = SDP_AUDIO;
- portno = x;
- /* Scan through the RTP payload types specified in a "m=" line: */
- for (; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
- if (sscanf(codecs, "%30u%n", &codec, &len) != 1) {
- ast_log(LOG_WARNING, "Invalid syntax in RTP audio format list: %s\n", codecs);
- res = -1;
- goto process_sdp_cleanup;
- }
- if (debug) {
- ast_verbose("Found RTP audio format %u\n", codec);
- }
- ast_rtp_codecs_payloads_set_m_type(&newaudiortp, NULL, codec);
- }
- } else {
- ast_log(LOG_WARNING, "Rejecting audio media offer due to invalid or unsupported syntax: %s\n", m);
- res = -1;
- goto process_sdp_cleanup;
- }
- }
- /* Check for 'video' media offer */
- else if (strncmp(m, "video ", 6) == 0) {
- if ((sscanf(m, "video %30u/%30u %17s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
- (sscanf(m, "video %30u %17s %n", &x, protocol, &len) == 2 && len > 0)) {
- codecs = m + len;
- /* produce zero-port m-line since it may be needed later
- * length is "m=video 0 " + protocol + " " + codecs + "\r\n\0" */
- if (!(offer->decline_m_line = ast_malloc(10 + strlen(protocol) + 1 + strlen(codecs) + 3))) {
- ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- /* guaranteed to be exactly the right length */
- sprintf(offer->decline_m_line, "m=video 0 %s %s\r\n", protocol, codecs);
- if (x == 0) {
- ast_debug(1, "Ignoring video stream offer because port number is zero\n");
- continue;
- }
- /* Check number of ports offered for stream */
- if (numberofports > 1) {
- ast_log(LOG_WARNING, "%u ports offered for video stream, not supported by Asterisk. Will try anyway...\n", numberofports);
- }
- if (has_media_stream(p, SDP_VIDEO)) {
- ast_log(LOG_WARNING, "Declining non-primary video stream: %s\n", m);
- continue;
- }
- if ((!strcmp(protocol, "RTP/SAVPF") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
- ast_log(LOG_WARNING, "Received SAVPF profle in video offer but AVPF is not enabled: %s\n", m);
- continue;
- } else if ((!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVP")) && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
- ast_log(LOG_WARNING, "Received SAVP profile in video offer but AVPF is enabled: %s\n", m);
- continue;
- } else if (!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) {
- secure_video = 1;
- processed_crypto = 1;
- if (p->vsrtp || (p->vsrtp = ast_sdp_srtp_alloc())) {
- ast_set_flag(p->vsrtp, AST_SRTP_CRYPTO_OFFER_OK);
- }
- } else if (!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "RTP/SAVPF")) {
- secure_video = 1;
- } else if (!strcmp(protocol, "RTP/AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
- ast_log(LOG_WARNING, "Received AVPF profile in video offer but AVPF is not enabled: %s\n", m);
- continue;
- } else if (!strcmp(protocol, "RTP/AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
- ast_log(LOG_WARNING, "Received AVP profile in video offer but AVPF is enabled: %s\n", m);
- continue;
- } else if (strcmp(protocol, "RTP/AVP") && strcmp(protocol, "RTP/AVPF")) {
- ast_log(LOG_WARNING, "Unknown RTP profile in video offer: %s\n", m);
- continue;
- }
- video = TRUE;
- p->novideo = FALSE;
- offer->type = SDP_VIDEO;
- vportno = x;
- /* Scan through the RTP payload types specified in a "m=" line: */
- for (; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
- if (sscanf(codecs, "%30u%n", &codec, &len) != 1) {
- ast_log(LOG_WARNING, "Invalid syntax in RTP video format list: %s\n", codecs);
- res = -1;
- goto process_sdp_cleanup;
- }
- if (debug) {
- ast_verbose("Found RTP video format %u\n", codec);
- }
- ast_rtp_codecs_payloads_set_m_type(&newvideortp, NULL, codec);
- }
- } else {
- ast_log(LOG_WARNING, "Rejecting video media offer due to invalid or unsupported syntax: %s\n", m);
- res = -1;
- goto process_sdp_cleanup;
- }
- }
- /* Check for 'text' media offer */
- else if (strncmp(m, "text ", 5) == 0) {
- if ((sscanf(m, "text %30u/%30u %17s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
- (sscanf(m, "text %30u %17s %n", &x, protocol, &len) == 2 && len > 0)) {
- codecs = m + len;
- /* produce zero-port m-line since it may be needed later
- * length is "m=text 0 " + protocol + " " + codecs + "\r\n\0" */
- if (!(offer->decline_m_line = ast_malloc(9 + strlen(protocol) + 1 + strlen(codecs) + 3))) {
- ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- /* guaranteed to be exactly the right length */
- sprintf(offer->decline_m_line, "m=text 0 %s %s\r\n", protocol, codecs);
- if (x == 0) {
- ast_debug(1, "Ignoring text stream offer because port number is zero\n");
- continue;
- }
- /* Check number of ports offered for stream */
- if (numberofports > 1) {
- ast_log(LOG_WARNING, "%u ports offered for text stream, not supported by Asterisk. Will try anyway...\n", numberofports);
- }
- if (!strcmp(protocol, "RTP/AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
- ast_log(LOG_WARNING, "Received AVPF profile in text offer but AVPF is not enabled: %s\n", m);
- continue;
- } else if (!strcmp(protocol, "RTP/AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
- ast_log(LOG_WARNING, "Received AVP profile in text offer but AVPF is enabled: %s\n", m);
- continue;
- } else if (strcmp(protocol, "RTP/AVP") && strcmp(protocol, "RTP/AVPF")) {
- ast_log(LOG_WARNING, "Unknown RTP profile in text offer: %s\n", m);
- continue;
- }
- if (has_media_stream(p, SDP_TEXT)) {
- ast_log(LOG_WARNING, "Declining non-primary text stream: %s\n", m);
- continue;
- }
- text = TRUE;
- p->notext = FALSE;
- offer->type = SDP_TEXT;
- tportno = x;
- /* Scan through the RTP payload types specified in a "m=" line: */
- for (; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
- if (sscanf(codecs, "%30u%n", &codec, &len) != 1) {
- ast_log(LOG_WARNING, "Invalid syntax in RTP video format list: %s\n", codecs);
- res = -1;
- goto process_sdp_cleanup;
- }
- if (debug) {
- ast_verbose("Found RTP text format %u\n", codec);
- }
- ast_rtp_codecs_payloads_set_m_type(&newtextrtp, NULL, codec);
- }
- } else {
- ast_log(LOG_WARNING, "Rejecting text stream offer due to invalid or unsupported syntax: %s\n", m);
- res = -1;
- goto process_sdp_cleanup;
- }
- }
- /* Check for 'image' media offer */
- else if (strncmp(m, "image ", 6) == 0) {
- if (((sscanf(m, "image %30u udptl t38%n", &x, &len) == 1 && len > 0) ||
- (sscanf(m, "image %30u UDPTL t38%n", &x, &len) == 1 && len > 0))) {
- /* produce zero-port m-line since it may be needed later
- * length is "m=image 0 udptl t38" + "\r\n\0" */
- if (!(offer->decline_m_line = ast_malloc(22))) {
- ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- /* guaranteed to be exactly the right length */
- strcpy(offer->decline_m_line, "m=image 0 udptl t38\r\n");
- if (x == 0) {
- ast_debug(1, "Ignoring image stream offer because port number is zero\n");
- continue;
- }
- if (initialize_udptl(p)) {
- ast_log(LOG_WARNING, "Failed to initialize UDPTL, declining image stream\n");
- continue;
- }
- if (has_media_stream(p, SDP_IMAGE)) {
- ast_log(LOG_WARNING, "Declining non-primary image stream: %s\n", m);
- continue;
- }
- image = TRUE;
- if (debug) {
- ast_verbose("Got T.38 offer in SDP in dialog %s\n", p->callid);
- }
- offer->type = SDP_IMAGE;
- udptlportno = x;
- if (p->t38.state != T38_ENABLED) {
- memset(&p->t38.their_parms, 0, sizeof(p->t38.their_parms));
- /* default EC to none, the remote end should
- * respond with the EC they want to use */
- ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
- }
- } else if (sscanf(m, "image %30u %17s t38%n", &x, protocol, &len) == 2 && len > 0) {
- ast_log(LOG_WARNING, "Declining image stream due to unsupported transport: %s\n", m);
- /* produce zero-port m-line since this is guaranteed to be declined
- * length is "m=image 0 strlen(protocol) t38" + "\r\n\0" */
- if (!(offer->decline_m_line = ast_malloc(10 + strlen(protocol) + 7))) {
- ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- /* guaranteed to be exactly the right length */
- sprintf(offer->decline_m_line, "m=image 0 %s t38\r\n", protocol);
- continue;
- } else {
- ast_log(LOG_WARNING, "Rejecting image media offer due to invalid or unsupported syntax: %s\n", m);
- res = -1;
- goto process_sdp_cleanup;
- }
- } else {
- char type[20] = {0,};
- if ((sscanf(m, "%19s %30u/%30u %n", type, &x, &numberofports, &len) == 3 && len > 0) ||
- (sscanf(m, "%19s %30u %n", type, &x, &len) == 2 && len > 0)) {
- /* produce zero-port m-line since it may be needed later
- * length is "m=" + type + " 0 " + remainder + "\r\n\0" */
- if (!(offer->decline_m_line = ast_malloc(2 + strlen(type) + 3 + strlen(m + len) + 3))) {
- ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- /* guaranteed to be long enough */
- sprintf(offer->decline_m_line, "m=%s 0 %s\r\n", type, m + len);
- continue;
- } else {
- ast_log(LOG_WARNING, "Unsupported top-level media type in offer: %s\n", m);
- res = -1;
- goto process_sdp_cleanup;
- }
- }
- /* Media stream specific parameters */
- while ((type = get_sdp_line(&iterator, next - 1, req, &value)) != '\0') {
- int processed = FALSE;
- switch (type) {
- case 'c':
- if (audio) {
- if (process_sdp_c(value, &audiosa)) {
- processed = TRUE;
- sa = &audiosa;
- }
- } else if (video) {
- if (process_sdp_c(value, &videosa)) {
- processed = TRUE;
- vsa = &videosa;
- }
- } else if (text) {
- if (process_sdp_c(value, &textsa)) {
- processed = TRUE;
- tsa = &textsa;
- }
- } else if (image) {
- if (process_sdp_c(value, &imagesa)) {
- processed = TRUE;
- isa = &imagesa;
- }
- }
- break;
- case 'a':
- /* Audio specific scanning */
- if (audio) {
- if (process_sdp_a_ice(value, p, p->rtp)) {
- processed = TRUE;
- } else if (process_sdp_a_dtls(value, p, p->rtp)) {
- processed_crypto = TRUE;
- processed = TRUE;
- if (p->srtp) {
- ast_set_flag(p->srtp, AST_SRTP_CRYPTO_OFFER_OK);
- }
- } else if (process_sdp_a_sendonly(value, &sendonly)) {
- processed = TRUE;
- } else if (!processed_crypto && process_crypto(p, p->rtp, &p->srtp, value)) {
- processed_crypto = TRUE;
- processed = TRUE;
- } else if (process_sdp_a_audio(value, p, &newaudiortp, &last_rtpmap_codec)) {
- processed = TRUE;
- }
- }
- /* Video specific scanning */
- else if (video) {
- if (process_sdp_a_ice(value, p, p->vrtp)) {
- processed = TRUE;
- } else if (process_sdp_a_dtls(value, p, p->vrtp)) {
- processed_crypto = TRUE;
- processed = TRUE;
- if (p->vsrtp) {
- ast_set_flag(p->vsrtp, AST_SRTP_CRYPTO_OFFER_OK);
- }
- } else if (!processed_crypto && process_crypto(p, p->vrtp, &p->vsrtp, value)) {
- processed_crypto = TRUE;
- processed = TRUE;
- } else if (process_sdp_a_video(value, p, &newvideortp, &last_rtpmap_codec)) {
- processed = TRUE;
- }
- }
- /* Text (T.140) specific scanning */
- else if (text) {
- if (process_sdp_a_ice(value, p, p->trtp)) {
- processed = TRUE;
- } else if (process_sdp_a_text(value, p, &newtextrtp, red_fmtp, &red_num_gen, red_data_pt, &last_rtpmap_codec)) {
- processed = TRUE;
- } else if (!processed_crypto && process_crypto(p, p->trtp, &p->tsrtp, value)) {
- processed_crypto = TRUE;
- processed = TRUE;
- }
- }
- /* Image (T.38 FAX) specific scanning */
- else if (image) {
- if (process_sdp_a_image(value, p))
- processed = TRUE;
- }
- break;
- }
- ast_debug(3, "Processing media-level (%s) SDP %c=%s... %s\n",
- (audio == TRUE)? "audio" : (video == TRUE)? "video" : (text == TRUE)? "text" : "image",
- type, value,
- (processed == TRUE)? "OK." : "UNSUPPORTED OR FAILED.");
- }
- /* Ensure crypto lines are provided where necessary */
- if (audio && secure_audio && !processed_crypto) {
- ast_log(LOG_WARNING, "Rejecting secure audio stream without encryption details: %s\n", m);
- res = -1;
- goto process_sdp_cleanup;
- } else if (video && secure_video && !processed_crypto) {
- ast_log(LOG_WARNING, "Rejecting secure video stream without encryption details: %s\n", m);
- res = -1;
- goto process_sdp_cleanup;
- }
- }
- /* Sanity checks */
- if (!sa && !vsa && !tsa && !isa) {
- ast_log(LOG_WARNING, "Insufficient information in SDP (c=)...\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if ((portno == -1) &&
- (vportno == -1) &&
- (tportno == -1) &&
- (udptlportno == -1)) {
- ast_log(LOG_WARNING, "Failing due to no acceptable offer found\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if (p->srtp && p->udptl && udptlportno != -1) {
- ast_debug(1, "Terminating SRTP due to T.38 UDPTL\n");
- ast_sdp_srtp_destroy(p->srtp);
- p->srtp = NULL;
- }
- if (secure_audio && !(p->srtp && (ast_test_flag(p->srtp, AST_SRTP_CRYPTO_OFFER_OK)))) {
- ast_log(LOG_WARNING, "Can't provide secure audio requested in SDP offer\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if (!secure_audio && p->srtp) {
- ast_log(LOG_WARNING, "Failed to receive SDP offer/answer with required SRTP crypto attributes for audio\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if (secure_video && !(p->vsrtp && (ast_test_flag(p->vsrtp, AST_SRTP_CRYPTO_OFFER_OK)))) {
- ast_log(LOG_WARNING, "Can't provide secure video requested in SDP offer\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if (!p->novideo && !secure_video && p->vsrtp) {
- ast_log(LOG_WARNING, "Failed to receive SDP offer/answer with required SRTP crypto attributes for video\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if (!(secure_audio || secure_video || (p->udptl && udptlportno != -1)) && ast_test_flag(&p->flags[1], SIP_PAGE2_USE_SRTP)) {
- ast_log(LOG_WARNING, "Matched device setup to use SRTP, but request was not!\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if (udptlportno == -1) {
- change_t38_state(p, T38_DISABLED);
- }
- /* Now gather all of the codecs that we are asked for: */
- ast_rtp_codecs_payload_formats(&newaudiortp, peercapability, &peernoncodeccapability);
- ast_rtp_codecs_payload_formats(&newvideortp, vpeercapability, &vpeernoncodeccapability);
- ast_rtp_codecs_payload_formats(&newtextrtp, tpeercapability, &tpeernoncodeccapability);
- ast_format_cap_append_from_cap(newpeercapability, peercapability, AST_MEDIA_TYPE_AUDIO);
- ast_format_cap_append_from_cap(newpeercapability, vpeercapability, AST_MEDIA_TYPE_VIDEO);
- ast_format_cap_append_from_cap(newpeercapability, tpeercapability, AST_MEDIA_TYPE_TEXT);
- ast_format_cap_get_compatible(p->caps, newpeercapability, newjointcapability);
- if (!ast_format_cap_count(newjointcapability) && udptlportno == -1) {
- ast_log(LOG_NOTICE, "No compatible codecs, not accepting this offer!\n");
- /* Do NOT Change current setting */
- res = -1;
- goto process_sdp_cleanup;
- }
- newnoncodeccapability = p->noncodeccapability & peernoncodeccapability;
- if (debug) {
- /* shame on whoever coded this.... */
- struct ast_str *cap_buf = ast_str_alloca(64);
- struct ast_str *peer_buf = ast_str_alloca(64);
- struct ast_str *vpeer_buf = ast_str_alloca(64);
- struct ast_str *tpeer_buf = ast_str_alloca(64);
- struct ast_str *joint_buf = ast_str_alloca(64);
- ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s/text=%s, combined - %s\n",
- ast_format_cap_get_names(p->caps, &cap_buf),
- ast_format_cap_get_names(peercapability, &peer_buf),
- ast_format_cap_get_names(vpeercapability, &vpeer_buf),
- ast_format_cap_get_names(tpeercapability, &tpeer_buf),
- ast_format_cap_get_names(newjointcapability, &joint_buf));
- }
- if (debug) {
- struct ast_str *s1 = ast_str_alloca(SIPBUFSIZE);
- struct ast_str *s2 = ast_str_alloca(SIPBUFSIZE);
- struct ast_str *s3 = ast_str_alloca(SIPBUFSIZE);
- ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n",
- ast_rtp_lookup_mime_multiple2(s1, NULL, p->noncodeccapability, 0, 0),
- ast_rtp_lookup_mime_multiple2(s2, NULL, peernoncodeccapability, 0, 0),
- ast_rtp_lookup_mime_multiple2(s3, NULL, newnoncodeccapability, 0, 0));
- }
- if (portno != -1 || vportno != -1 || tportno != -1) {
- /* We are now ready to change the sip session and RTP structures with the offered codecs, since
- they are acceptable */
- ast_format_cap_remove_by_type(p->jointcaps, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_append_from_cap(p->jointcaps, newjointcapability, AST_MEDIA_TYPE_UNKNOWN); /* Our joint codec profile for this call */
- ast_format_cap_remove_by_type(p->peercaps, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_append_from_cap(p->peercaps, newpeercapability, AST_MEDIA_TYPE_UNKNOWN); /* The other side's capability in latest offer */
- p->jointnoncodeccapability = newnoncodeccapability; /* DTMF capabilities */
- /* respond with single most preferred joint codec, limiting the other side's choice */
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_PREFERRED_CODEC)) {
- unsigned int framing;
- tmp_fmt = ast_format_cap_get_format(p->jointcaps, 0);
- framing = ast_format_cap_get_format_framing(p->jointcaps, tmp_fmt);
- ast_format_cap_remove_by_type(p->jointcaps, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_append(p->jointcaps, tmp_fmt, framing);
- ao2_ref(tmp_fmt, -1);
- }
- }
- /* Setup audio address and port */
- if (p->rtp) {
- if (sa && portno > 0) {
- start_ice(p->rtp, (req->method != SIP_RESPONSE) ? 0 : 1);
- ast_sockaddr_set_port(sa, portno);
- ast_rtp_instance_set_remote_address(p->rtp, sa);
- if (debug) {
- ast_verbose("Peer audio RTP is at port %s\n",
- ast_sockaddr_stringify(sa));
- }
- ast_rtp_codecs_payloads_copy(&newaudiortp, ast_rtp_instance_get_codecs(p->rtp), p->rtp);
- /* Ensure RTCP is enabled since it may be inactive
- if we're coming back from a T.38 session */
- ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_RTCP, 1);
- /* Ensure audio RTCP reads are enabled */
- if (p->owner) {
- ast_channel_set_fd(p->owner, 1, ast_rtp_instance_fd(p->rtp, 1));
- }
- if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) {
- ast_clear_flag(&p->flags[0], SIP_DTMF);
- if (newnoncodeccapability & AST_RTP_DTMF) {
- /* XXX Would it be reasonable to drop the DSP at this point? XXX */
- ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833);
- /* Since RFC2833 is now negotiated we need to change some properties of the RTP stream */
- ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, 1);
- ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
- } else {
- ast_set_flag(&p->flags[0], SIP_DTMF_INBAND);
- }
- }
- } else if (udptlportno > 0) {
- if (debug)
- ast_verbose("Got T.38 Re-invite without audio. Keeping RTP active during T.38 session.\n");
- /* Prevent audio RTCP reads */
- if (p->owner) {
- ast_channel_set_fd(p->owner, 1, -1);
- }
- /* Silence RTCP while audio RTP is inactive */
- ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_RTCP, 0);
- } else {
- ast_rtp_instance_stop(p->rtp);
- if (debug)
- ast_verbose("Peer doesn't provide audio\n");
- }
- }
- /* Setup video address and port */
- if (p->vrtp) {
- if (vsa && vportno > 0) {
- start_ice(p->vrtp, (req->method != SIP_RESPONSE) ? 0 : 1);
- ast_sockaddr_set_port(vsa, vportno);
- ast_rtp_instance_set_remote_address(p->vrtp, vsa);
- if (debug) {
- ast_verbose("Peer video RTP is at port %s\n",
- ast_sockaddr_stringify(vsa));
- }
- ast_rtp_codecs_payloads_copy(&newvideortp, ast_rtp_instance_get_codecs(p->vrtp), p->vrtp);
- } else {
- ast_rtp_instance_stop(p->vrtp);
- if (debug)
- ast_verbose("Peer doesn't provide video\n");
- }
- }
- /* Setup text address and port */
- if (p->trtp) {
- if (tsa && tportno > 0) {
- start_ice(p->trtp, (req->method != SIP_RESPONSE) ? 0 : 1);
- ast_sockaddr_set_port(tsa, tportno);
- ast_rtp_instance_set_remote_address(p->trtp, tsa);
- if (debug) {
- ast_verbose("Peer T.140 RTP is at port %s\n",
- ast_sockaddr_stringify(tsa));
- }
- if (ast_format_cap_iscompatible_format(p->jointcaps, ast_format_t140_red) != AST_FORMAT_CMP_NOT_EQUAL) {
- p->red = 1;
- ast_rtp_red_init(p->trtp, 300, red_data_pt, 2);
- } else {
- p->red = 0;
- }
- ast_rtp_codecs_payloads_copy(&newtextrtp, ast_rtp_instance_get_codecs(p->trtp), p->trtp);
- } else {
- ast_rtp_instance_stop(p->trtp);
- if (debug)
- ast_verbose("Peer doesn't provide T.140\n");
- }
- }
- /* Setup image address and port */
- if (p->udptl) {
- if (isa && udptlportno > 0) {
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP) && ast_test_flag(&p->flags[1], SIP_PAGE2_UDPTL_DESTINATION)) {
- ast_rtp_instance_get_remote_address(p->rtp, isa);
- if (!ast_sockaddr_isnull(isa) && debug) {
- ast_debug(1, "Peer T.38 UDPTL is set behind NAT and with destination, destination address now %s\n", ast_sockaddr_stringify(isa));
- }
- }
- ast_sockaddr_set_port(isa, udptlportno);
- ast_udptl_set_peer(p->udptl, isa);
- if (debug)
- ast_debug(1, "Peer T.38 UDPTL is at port %s\n", ast_sockaddr_stringify(isa));
- /* verify the far max ifp can be calculated. this requires far max datagram to be set. */
- if (!ast_udptl_get_far_max_datagram(p->udptl)) {
- /* setting to zero will force a default if none was provided by the SDP */
- ast_udptl_set_far_max_datagram(p->udptl, 0);
- }
- /* Remote party offers T38, we need to update state */
- if ((t38action == SDP_T38_ACCEPT) &&
- (p->t38.state == T38_LOCAL_REINVITE)) {
- change_t38_state(p, T38_ENABLED);
- } else if ((t38action == SDP_T38_INITIATE) &&
- p->owner && p->lastinvite) {
- change_t38_state(p, T38_PEER_REINVITE); /* T38 Offered in re-invite from remote party */
- /* If fax detection is enabled then send us off to the fax extension */
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_T38)) {
- ast_channel_lock(p->owner);
- if (strcmp(ast_channel_exten(p->owner), "fax")) {
- const char *target_context = S_OR(ast_channel_macrocontext(p->owner), ast_channel_context(p->owner));
- ast_channel_unlock(p->owner);
- if (ast_exists_extension(p->owner, target_context, "fax", 1,
- S_COR(ast_channel_caller(p->owner)->id.number.valid, ast_channel_caller(p->owner)->id.number.str, NULL))) {
- ast_verb(2, "Redirecting '%s' to fax extension due to peer T.38 re-INVITE\n", ast_channel_name(p->owner));
- pbx_builtin_setvar_helper(p->owner, "FAXEXTEN", ast_channel_exten(p->owner));
- if (ast_async_goto(p->owner, target_context, "fax", 1)) {
- ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", ast_channel_name(p->owner), target_context);
- }
- } else {
- ast_log(LOG_NOTICE, "T.38 re-INVITE detected but no fax extension\n");
- }
- } else {
- ast_channel_unlock(p->owner);
- }
- }
- }
- } else {
- change_t38_state(p, T38_DISABLED);
- ast_udptl_stop(p->udptl);
- if (debug)
- ast_debug(1, "Peer doesn't provide T.38 UDPTL\n");
- }
- }
- if ((portno == -1) && (p->t38.state != T38_DISABLED) && (p->t38.state != T38_REJECTED)) {
- ast_debug(3, "Have T.38 but no audio, accepting offer anyway\n");
- res = 0;
- goto process_sdp_cleanup;
- }
- /* Ok, we're going with this offer */
- ast_debug(2, "We're settling with these formats: %s\n", ast_format_cap_get_names(p->jointcaps, &codec_buf));
- if (!p->owner) { /* There's no open channel owning us so we can return here. For a re-invite or so, we proceed */
- res = 0;
- goto process_sdp_cleanup;
- }
- ast_debug(4, "We have an owner, now see if we need to change this call\n");
- if (ast_format_cap_has_type(p->jointcaps, AST_MEDIA_TYPE_AUDIO)) {
- struct ast_format_cap *caps;
- unsigned int framing;
- if (debug) {
- struct ast_str *cap_buf = ast_str_alloca(64);
- struct ast_str *joint_buf = ast_str_alloca(64);
- ast_debug(1, "Setting native formats after processing SDP. peer joint formats %s, old nativeformats %s\n",
- ast_format_cap_get_names(p->jointcaps, &joint_buf),
- ast_format_cap_get_names(ast_channel_nativeformats(p->owner), &cap_buf));
- }
- caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- if (caps) {
- tmp_fmt = ast_format_cap_get_format(p->jointcaps, 0);
- framing = ast_format_cap_get_format_framing(p->jointcaps, tmp_fmt);
- ast_format_cap_append(caps, tmp_fmt, framing);
- ast_format_cap_append_from_cap(caps, vpeercapability, AST_MEDIA_TYPE_VIDEO);
- ast_format_cap_append_from_cap(caps, tpeercapability, AST_MEDIA_TYPE_TEXT);
- ast_channel_nativeformats_set(p->owner, caps);
- ao2_ref(caps, -1);
- ao2_ref(tmp_fmt, -1);
- }
- ast_set_read_format(p->owner, ast_channel_readformat(p->owner));
- ast_set_write_format(p->owner, ast_channel_writeformat(p->owner));
- }
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) && (!ast_sockaddr_isnull(sa) || !ast_sockaddr_isnull(vsa) || !ast_sockaddr_isnull(tsa) || !ast_sockaddr_isnull(isa)) && (!sendonly || sendonly == -1)) {
- if (!ast_test_flag(&p->flags[2], SIP_PAGE3_DISCARD_REMOTE_HOLD_RETRIEVAL)) {
- ast_queue_unhold(p->owner);
- }
- /* Activate a re-invite */
- ast_queue_frame(p->owner, &ast_null_frame);
- change_hold_state(p, req, FALSE, sendonly);
- } else if ((sockaddr_is_null_or_any(sa) && sockaddr_is_null_or_any(vsa) && sockaddr_is_null_or_any(tsa) && sockaddr_is_null_or_any(isa)) || (sendonly && sendonly != -1)) {
- if (!ast_test_flag(&p->flags[2], SIP_PAGE3_DISCARD_REMOTE_HOLD_RETRIEVAL)) {
- ast_queue_hold(p->owner, p->mohsuggest);
- }
- if (sendonly)
- ast_rtp_instance_stop(p->rtp);
- /* RTCP needs to go ahead, even if we're on hold!!! */
- /* Activate a re-invite */
- ast_queue_frame(p->owner, &ast_null_frame);
- change_hold_state(p, req, TRUE, sendonly);
- }
- process_sdp_cleanup:
- if (res) {
- offered_media_list_destroy(p);
- }
- ast_rtp_codecs_payloads_destroy(&newtextrtp);
- ast_rtp_codecs_payloads_destroy(&newvideortp);
- ast_rtp_codecs_payloads_destroy(&newaudiortp);
- ao2_cleanup(peercapability);
- ao2_cleanup(vpeercapability);
- ao2_cleanup(tpeercapability);
- ao2_cleanup(newjointcapability);
- ao2_cleanup(newpeercapability);
- return res;
- }
- static int process_sdp_o(const char *o, struct sip_pvt *p)
- {
- char *o_copy;
- char *token;
- int64_t rua_version;
- /* Store the SDP version number of remote UA. This will allow us to
- distinguish between session modifications and session refreshes. If
- the remote UA does not send an incremented SDP version number in a
- subsequent RE-INVITE then that means its not changing media session.
- The RE-INVITE may have been sent to update connected party, remote
- target or to refresh the session (Session-Timers). Asterisk must not
- change media session and increment its own version number in answer
- SDP in this case. */
- p->session_modify = TRUE;
- if (ast_strlen_zero(o)) {
- ast_log(LOG_WARNING, "SDP syntax error. SDP without an o= line\n");
- return FALSE;
- }
- o_copy = ast_strdupa(o);
- token = strsep(&o_copy, " "); /* Skip username */
- if (!o_copy) {
- ast_log(LOG_WARNING, "SDP syntax error in o= line username\n");
- return FALSE;
- }
- token = strsep(&o_copy, " "); /* Skip session-id */
- if (!o_copy) {
- ast_log(LOG_WARNING, "SDP syntax error in o= line session-id\n");
- return FALSE;
- }
- token = strsep(&o_copy, " "); /* Version */
- if (!o_copy) {
- ast_log(LOG_WARNING, "SDP syntax error in o= line\n");
- return FALSE;
- }
- if (!sscanf(token, "%30" SCNd64, &rua_version)) {
- ast_log(LOG_WARNING, "SDP syntax error in o= line version\n");
- return FALSE;
- }
- /* we need to check the SDP version number the other end sent us;
- * our rules for deciding what to accept are a bit complex.
- *
- * 1) if 'ignoresdpversion' has been set for this dialog, then
- * we will just accept whatever they sent and assume it is
- * a modification of the session, even if it is not
- * 2) otherwise, if this is the first SDP we've seen from them
- * we accept it
- * 3) otherwise, if the new SDP version number is higher than the
- * old one, we accept it
- * 4) otherwise, if this SDP is in response to us requesting a switch
- * to T.38, we accept the SDP, but also generate a warning message
- * that this peer should have the 'ignoresdpversion' option set,
- * because it is not following the SDP offer/answer RFC; if we did
- * not request a switch to T.38, then we stop parsing the SDP, as it
- * has not changed from the previous version
- */
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_IGNORESDPVERSION) ||
- (p->sessionversion_remote < 0) ||
- (p->sessionversion_remote < rua_version)) {
- p->sessionversion_remote = rua_version;
- } else {
- if (p->t38.state == T38_LOCAL_REINVITE) {
- p->sessionversion_remote = rua_version;
- ast_log(LOG_WARNING, "Call %s responded to our T.38 reinvite without changing SDP version; 'ignoresdpversion' should be set for this peer.\n", p->callid);
- } else {
- p->session_modify = FALSE;
- ast_debug(2, "Call %s responded to our reinvite without changing SDP version; ignoring SDP.\n", p->callid);
- return FALSE;
- }
- }
- return TRUE;
- }
- static int process_sdp_c(const char *c, struct ast_sockaddr *addr)
- {
- char proto[4], host[258];
- int af;
- /* Check for Media-description-level-address */
- if (sscanf(c, "IN %3s %255s", proto, host) == 2) {
- if (!strcmp("IP4", proto)) {
- af = AF_INET;
- } else if (!strcmp("IP6", proto)) {
- af = AF_INET6;
- } else {
- ast_log(LOG_WARNING, "Unknown protocol '%s'.\n", proto);
- return FALSE;
- }
- if (ast_sockaddr_resolve_first_af(addr, host, 0, af)) {
- ast_log(LOG_WARNING, "Unable to lookup RTP Audio host in c= line, '%s'\n", c);
- return FALSE;
- }
- return TRUE;
- } else {
- ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c);
- return FALSE;
- }
- return FALSE;
- }
- static int process_sdp_a_sendonly(const char *a, int *sendonly)
- {
- int found = FALSE;
- if (!strcasecmp(a, "sendonly")) {
- if (*sendonly == -1)
- *sendonly = 1;
- found = TRUE;
- } else if (!strcasecmp(a, "inactive")) {
- if (*sendonly == -1)
- *sendonly = 2;
- found = TRUE;
- } else if (!strcasecmp(a, "sendrecv")) {
- if (*sendonly == -1)
- *sendonly = 0;
- found = TRUE;
- }
- return found;
- }
- static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance)
- {
- struct ast_rtp_engine_ice *ice;
- int found = FALSE;
- char ufrag[256], pwd[256], foundation[32], transport[4], address[46], cand_type[6], relay_address[46] = "";
- struct ast_rtp_engine_ice_candidate candidate = { 0, };
- unsigned int port, relay_port = 0;
- if (!instance || !(ice = ast_rtp_instance_get_ice(instance))) {
- return found;
- }
- if (sscanf(a, "ice-ufrag: %255s", ufrag) == 1) {
- ice->set_authentication(instance, ufrag, NULL);
- found = TRUE;
- } else if (sscanf(a, "ice-pwd: %255s", pwd) == 1) {
- ice->set_authentication(instance, NULL, pwd);
- found = TRUE;
- } else if (sscanf(a, "candidate: %31s %30u %3s %30u %23s %30u typ %5s %*s %23s %*s %30u", foundation, &candidate.id, transport, (unsigned *)&candidate.priority,
- address, &port, cand_type, relay_address, &relay_port) >= 7) {
- candidate.foundation = foundation;
- candidate.transport = transport;
- ast_sockaddr_parse(&candidate.address, address, PARSE_PORT_FORBID);
- ast_sockaddr_set_port(&candidate.address, port);
- if (!strcasecmp(cand_type, "host")) {
- candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_HOST;
- } else if (!strcasecmp(cand_type, "srflx")) {
- candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_SRFLX;
- } else if (!strcasecmp(cand_type, "relay")) {
- candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_RELAYED;
- } else {
- return found;
- }
- if (!ast_strlen_zero(relay_address)) {
- ast_sockaddr_parse(&candidate.relay_address, relay_address, PARSE_PORT_FORBID);
- }
- if (relay_port) {
- ast_sockaddr_set_port(&candidate.relay_address, relay_port);
- }
- ice->add_remote_candidate(instance, &candidate);
- found = TRUE;
- } else if (!strcasecmp(a, "ice-lite")) {
- ice->ice_lite(instance);
- found = TRUE;
- }
- return found;
- }
- static int process_sdp_a_dtls(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance)
- {
- struct ast_rtp_engine_dtls *dtls;
- int found = FALSE;
- char value[256], hash[32];
- if (!instance || !p->dtls_cfg.enabled || !(dtls = ast_rtp_instance_get_dtls(instance))) {
- return found;
- }
- if (sscanf(a, "setup: %255s", value) == 1) {
- found = TRUE;
- if (!strcasecmp(value, "active")) {
- dtls->set_setup(instance, AST_RTP_DTLS_SETUP_ACTIVE);
- } else if (!strcasecmp(value, "passive")) {
- dtls->set_setup(instance, AST_RTP_DTLS_SETUP_PASSIVE);
- } else if (!strcasecmp(value, "actpass")) {
- dtls->set_setup(instance, AST_RTP_DTLS_SETUP_ACTPASS);
- } else if (!strcasecmp(value, "holdconn")) {
- dtls->set_setup(instance, AST_RTP_DTLS_SETUP_HOLDCONN);
- } else {
- ast_log(LOG_WARNING, "Unsupported setup attribute value '%s' received on dialog '%s'\n",
- value, p->callid);
- }
- } else if (sscanf(a, "connection: %255s", value) == 1) {
- found = TRUE;
- if (!strcasecmp(value, "new")) {
- dtls->reset(instance);
- } else if (!strcasecmp(value, "existing")) {
- /* Since they want to just use what already exists we go on as if nothing happened */
- } else {
- ast_log(LOG_WARNING, "Unsupported connection attribute value '%s' received on dialog '%s'\n",
- value, p->callid);
- }
- } else if (sscanf(a, "fingerprint: %31s %255s", hash, value) == 2) {
- found = TRUE;
- if (!strcasecmp(hash, "sha-1")) {
- dtls->set_fingerprint(instance, AST_RTP_DTLS_HASH_SHA1, value);
- } else if (!strcasecmp(hash, "sha-256")) {
- dtls->set_fingerprint(instance, AST_RTP_DTLS_HASH_SHA256, value);
- } else {
- ast_log(LOG_WARNING, "Unsupported fingerprint hash type '%s' received on dialog '%s'\n",
- hash, p->callid);
- }
- }
- return found;
- }
- static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newaudiortp, int *last_rtpmap_codec)
- {
- int found = FALSE;
- unsigned int codec;
- char mimeSubtype[128];
- char fmtp_string[256];
- unsigned int sample_rate;
- int debug = sip_debug_test_pvt(p);
- if (!strncasecmp(a, "ptime", 5)) {
- char *tmp = strrchr(a, ':');
- long int framing = 0;
- if (tmp) {
- tmp++;
- framing = strtol(tmp, NULL, 10);
- if (framing == LONG_MIN || framing == LONG_MAX) {
- framing = 0;
- ast_debug(1, "Can't read framing from SDP: %s\n", a);
- }
- }
- if (framing && p->autoframing) {
- ast_debug(1, "Setting framing to %ld\n", framing);
- ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(p->rtp), framing);
- }
- found = TRUE;
- } else if (sscanf(a, "rtpmap: %30u %127[^/]/%30u", &codec, mimeSubtype, &sample_rate) == 3) {
- /* We have a rtpmap to handle */
- if (*last_rtpmap_codec < SDP_MAX_RTPMAP_CODECS) {
- if (!(ast_rtp_codecs_payloads_set_rtpmap_type_rate(newaudiortp, NULL, codec, "audio", mimeSubtype,
- ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0, sample_rate))) {
- if (debug)
- ast_verbose("Found audio description format %s for ID %u\n", mimeSubtype, codec);
- //found_rtpmap_codecs[last_rtpmap_codec] = codec;
- (*last_rtpmap_codec)++;
- found = TRUE;
- } else {
- ast_rtp_codecs_payloads_unset(newaudiortp, NULL, codec);
- if (debug)
- ast_verbose("Found unknown media description format %s for ID %u\n", mimeSubtype, codec);
- }
- } else {
- if (debug)
- ast_verbose("Discarded description format %s for ID %u\n", mimeSubtype, codec);
- }
- } else if (sscanf(a, "fmtp: %30u %255[^\t\n]", &codec, fmtp_string) == 2) {
- struct ast_format *format;
- if ((format = ast_rtp_codecs_get_payload_format(newaudiortp, codec))) {
- unsigned int bit_rate;
- struct ast_format *format_parsed;
- format_parsed = ast_format_parse_sdp_fmtp(format, fmtp_string);
- if (format_parsed) {
- ast_rtp_codecs_payload_replace_format(newaudiortp, codec, format_parsed);
- ao2_replace(format, format_parsed);
- ao2_ref(format_parsed, -1);
- found = TRUE;
- } else {
- ast_rtp_codecs_payloads_unset(newaudiortp, NULL, codec);
- }
- if (ast_format_cmp(format, ast_format_siren7) == AST_FORMAT_CMP_EQUAL) {
- if (sscanf(fmtp_string, "bitrate=%30u", &bit_rate) == 1) {
- if (bit_rate != 32000) {
- ast_log(LOG_WARNING, "Got Siren7 offer at %u bps, but only 32000 bps supported; ignoring.\n", bit_rate);
- ast_rtp_codecs_payloads_unset(newaudiortp, NULL, codec);
- } else {
- found = TRUE;
- }
- }
- } else if (ast_format_cmp(format, ast_format_siren14) == AST_FORMAT_CMP_EQUAL) {
- if (sscanf(fmtp_string, "bitrate=%30u", &bit_rate) == 1) {
- if (bit_rate != 48000) {
- ast_log(LOG_WARNING, "Got Siren14 offer at %u bps, but only 48000 bps supported; ignoring.\n", bit_rate);
- ast_rtp_codecs_payloads_unset(newaudiortp, NULL, codec);
- } else {
- found = TRUE;
- }
- }
- } else if (ast_format_cmp(format, ast_format_g719) == AST_FORMAT_CMP_EQUAL) {
- if (sscanf(fmtp_string, "bitrate=%30u", &bit_rate) == 1) {
- if (bit_rate != 64000) {
- ast_log(LOG_WARNING, "Got G.719 offer at %u bps, but only 64000 bps supported; ignoring.\n", bit_rate);
- ast_rtp_codecs_payloads_unset(newaudiortp, NULL, codec);
- } else {
- found = TRUE;
- }
- }
- }
- ao2_ref(format, -1);
- }
- }
- return found;
- }
- static int process_sdp_a_video(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newvideortp, int *last_rtpmap_codec)
- {
- int found = FALSE;
- unsigned int codec;
- char mimeSubtype[128];
- unsigned int sample_rate;
- int debug = sip_debug_test_pvt(p);
- char fmtp_string[256];
- if (sscanf(a, "rtpmap: %30u %127[^/]/%30u", &codec, mimeSubtype, &sample_rate) == 3) {
- /* We have a rtpmap to handle */
- if (*last_rtpmap_codec < SDP_MAX_RTPMAP_CODECS) {
- /* Note: should really look at the '#chans' params too */
- if (!strncasecmp(mimeSubtype, "H26", 3) || !strncasecmp(mimeSubtype, "MP4", 3)
- || !strncasecmp(mimeSubtype, "VP8", 3)) {
- if (!(ast_rtp_codecs_payloads_set_rtpmap_type_rate(newvideortp, NULL, codec, "video", mimeSubtype, 0, sample_rate))) {
- if (debug)
- ast_verbose("Found video description format %s for ID %u\n", mimeSubtype, codec);
- //found_rtpmap_codecs[last_rtpmap_codec] = codec;
- (*last_rtpmap_codec)++;
- found = TRUE;
- } else {
- ast_rtp_codecs_payloads_unset(newvideortp, NULL, codec);
- if (debug)
- ast_verbose("Found unknown media description format %s for ID %u\n", mimeSubtype, codec);
- }
- }
- } else {
- if (debug)
- ast_verbose("Discarded description format %s for ID %u\n", mimeSubtype, codec);
- }
- } else if (sscanf(a, "fmtp: %30u %255[^\t\n]", &codec, fmtp_string) == 2) {
- struct ast_format *format;
- if ((format = ast_rtp_codecs_get_payload_format(newvideortp, codec))) {
- struct ast_format *format_parsed;
- format_parsed = ast_format_parse_sdp_fmtp(format, fmtp_string);
- if (format_parsed) {
- ast_rtp_codecs_payload_replace_format(newvideortp, codec, format_parsed);
- ao2_replace(format, format_parsed);
- ao2_ref(format_parsed, -1);
- found = TRUE;
- } else {
- ast_rtp_codecs_payloads_unset(newvideortp, NULL, codec);
- }
- ao2_ref(format, -1);
- }
- }
- return found;
- }
- static int process_sdp_a_text(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newtextrtp, char *red_fmtp, int *red_num_gen, int *red_data_pt, int *last_rtpmap_codec)
- {
- int found = FALSE;
- unsigned int codec;
- char mimeSubtype[128];
- unsigned int sample_rate;
- char *red_cp;
- int debug = sip_debug_test_pvt(p);
- if (sscanf(a, "rtpmap: %30u %127[^/]/%30u", &codec, mimeSubtype, &sample_rate) == 3) {
- /* We have a rtpmap to handle */
- if (*last_rtpmap_codec < SDP_MAX_RTPMAP_CODECS) {
- if (!strncasecmp(mimeSubtype, "T140", 4)) { /* Text */
- if (p->trtp) {
- /* ast_verbose("Adding t140 mimeSubtype to textrtp struct\n"); */
- ast_rtp_codecs_payloads_set_rtpmap_type_rate(newtextrtp, NULL, codec, "text", mimeSubtype, 0, sample_rate);
- found = TRUE;
- }
- } else if (!strncasecmp(mimeSubtype, "RED", 3)) { /* Text with Redudancy */
- if (p->trtp) {
- ast_rtp_codecs_payloads_set_rtpmap_type_rate(newtextrtp, NULL, codec, "text", mimeSubtype, 0, sample_rate);
- sprintf(red_fmtp, "fmtp:%u ", codec);
- if (debug)
- ast_verbose("RED submimetype has payload type: %u\n", codec);
- found = TRUE;
- }
- }
- } else {
- if (debug)
- ast_verbose("Discarded description format %s for ID %u\n", mimeSubtype, codec);
- }
- } else if (!strncmp(a, red_fmtp, strlen(red_fmtp))) {
- /* count numbers of generations in fmtp */
- red_cp = &red_fmtp[strlen(red_fmtp)];
- strncpy(red_fmtp, a, 100);
- sscanf(red_cp, "%30u", (unsigned *)&red_data_pt[*red_num_gen]);
- red_cp = strtok(red_cp, "/");
- while (red_cp && (*red_num_gen)++ < AST_RED_MAX_GENERATION) {
- sscanf(red_cp, "%30u", (unsigned *)&red_data_pt[*red_num_gen]);
- red_cp = strtok(NULL, "/");
- }
- red_cp = red_fmtp;
- found = TRUE;
- }
- return found;
- }
- static int process_sdp_a_image(const char *a, struct sip_pvt *p)
- {
- int found = FALSE;
- char s[256];
- unsigned int x;
- char *attrib = ast_strdupa(a);
- char *pos;
- if (initialize_udptl(p)) {
- return found;
- }
- /* Due to a typo in an IANA registration of one of the T.38 attributes,
- * RFC5347 section 2.5.2 recommends that all T.38 attributes be parsed in
- * a case insensitive manner. Hence, the importance of proof reading (and
- * code reviews).
- */
- for (pos = attrib; *pos; ++pos) {
- *pos = tolower(*pos);
- }
- if ((sscanf(attrib, "t38faxmaxbuffer:%30u", &x) == 1)) {
- ast_debug(3, "MaxBufferSize:%u\n", x);
- found = TRUE;
- } else if ((sscanf(attrib, "t38maxbitrate:%30u", &x) == 1) || (sscanf(attrib, "t38faxmaxrate:%30u", &x) == 1)) {
- ast_debug(3, "T38MaxBitRate: %u\n", x);
- switch (x) {
- case 14400:
- p->t38.their_parms.rate = AST_T38_RATE_14400;
- break;
- case 12000:
- p->t38.their_parms.rate = AST_T38_RATE_12000;
- break;
- case 9600:
- p->t38.their_parms.rate = AST_T38_RATE_9600;
- break;
- case 7200:
- p->t38.their_parms.rate = AST_T38_RATE_7200;
- break;
- case 4800:
- p->t38.their_parms.rate = AST_T38_RATE_4800;
- break;
- case 2400:
- p->t38.their_parms.rate = AST_T38_RATE_2400;
- break;
- }
- found = TRUE;
- } else if ((sscanf(attrib, "t38faxversion:%30u", &x) == 1)) {
- ast_debug(3, "FaxVersion: %u\n", x);
- p->t38.their_parms.version = x;
- found = TRUE;
- } else if ((sscanf(attrib, "t38faxmaxdatagram:%30u", &x) == 1) || (sscanf(attrib, "t38maxdatagram:%30u", &x) == 1)) {
- /* override the supplied value if the configuration requests it */
- if (((signed int) p->t38_maxdatagram >= 0) && ((unsigned int) p->t38_maxdatagram > x)) {
- ast_debug(1, "Overriding T38FaxMaxDatagram '%u' with '%d'\n", x, p->t38_maxdatagram);
- x = p->t38_maxdatagram;
- }
- ast_debug(3, "FaxMaxDatagram: %u\n", x);
- ast_udptl_set_far_max_datagram(p->udptl, x);
- found = TRUE;
- } else if ((strncmp(attrib, "t38faxfillbitremoval", sizeof("t38faxfillbitremoval") - 1) == 0)) {
- if (sscanf(attrib, "t38faxfillbitremoval:%30u", &x) == 1) {
- ast_debug(3, "FillBitRemoval: %u\n", x);
- if (x == 1) {
- p->t38.their_parms.fill_bit_removal = TRUE;
- }
- } else {
- ast_debug(3, "FillBitRemoval\n");
- p->t38.their_parms.fill_bit_removal = TRUE;
- }
- found = TRUE;
- } else if ((strncmp(attrib, "t38faxtranscodingmmr", sizeof("t38faxtranscodingmmr") - 1) == 0)) {
- if (sscanf(attrib, "t38faxtranscodingmmr:%30u", &x) == 1) {
- ast_debug(3, "Transcoding MMR: %u\n", x);
- if (x == 1) {
- p->t38.their_parms.transcoding_mmr = TRUE;
- }
- } else {
- ast_debug(3, "Transcoding MMR\n");
- p->t38.their_parms.transcoding_mmr = TRUE;
- }
- found = TRUE;
- } else if ((strncmp(attrib, "t38faxtranscodingjbig", sizeof("t38faxtranscodingjbig") - 1) == 0)) {
- if (sscanf(attrib, "t38faxtranscodingjbig:%30u", &x) == 1) {
- ast_debug(3, "Transcoding JBIG: %u\n", x);
- if (x == 1) {
- p->t38.their_parms.transcoding_jbig = TRUE;
- }
- } else {
- ast_debug(3, "Transcoding JBIG\n");
- p->t38.their_parms.transcoding_jbig = TRUE;
- }
- found = TRUE;
- } else if ((sscanf(attrib, "t38faxratemanagement:%255s", s) == 1)) {
- ast_debug(3, "RateManagement: %s\n", s);
- if (!strcasecmp(s, "localTCF"))
- p->t38.their_parms.rate_management = AST_T38_RATE_MANAGEMENT_LOCAL_TCF;
- else if (!strcasecmp(s, "transferredTCF"))
- p->t38.their_parms.rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF;
- found = TRUE;
- } else if ((sscanf(attrib, "t38faxudpec:%255s", s) == 1)) {
- ast_debug(3, "UDP EC: %s\n", s);
- if (!strcasecmp(s, "t38UDPRedundancy")) {
- ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
- } else if (!strcasecmp(s, "t38UDPFEC")) {
- ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC);
- } else {
- ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
- }
- found = TRUE;
- }
- return found;
- }
- /*! \brief Add "Supported" header to sip message. Since some options may
- * be disabled in the config, the sip_pvt must be inspected to determine what
- * is supported for this dialog. */
- static int add_supported(struct sip_pvt *pvt, struct sip_request *req)
- {
- char supported_value[SIPBUFSIZE];
- int res;
- sprintf(supported_value, "replaces%s%s",
- (st_get_mode(pvt, 0) != SESSION_TIMER_MODE_REFUSE) ? ", timer" : "",
- ast_test_flag(&pvt->flags[0], SIP_USEPATH) ? ", path" : "");
- res = add_header(req, "Supported", supported_value);
- return res;
- }
- /*! \brief Add header to SIP message */
- static int add_header(struct sip_request *req, const char *var, const char *value)
- {
- if (req->headers == SIP_MAX_HEADERS) {
- ast_log(LOG_WARNING, "Out of SIP header space\n");
- return -1;
- }
- if (req->lines) {
- ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n");
- return -1;
- }
- if (sip_cfg.compactheaders) {
- var = find_alias(var, var);
- }
- ast_str_append(&req->data, 0, "%s: %s\r\n", var, value);
- req->header[req->headers] = ast_str_strlen(req->data);
- req->headers++;
- return 0;
- }
- /*!
- * \pre dialog is assumed to be locked while calling this function
- * \brief Add 'Max-Forwards' header to SIP message
- */
- static int add_max_forwards(struct sip_pvt *dialog, struct sip_request *req)
- {
- char clen[10];
- snprintf(clen, sizeof(clen), "%d", dialog->maxforwards);
- return add_header(req, "Max-Forwards", clen);
- }
- /*! \brief Add 'Content-Length' header and content to SIP message */
- static int finalize_content(struct sip_request *req)
- {
- char clen[10];
- if (req->lines) {
- ast_log(LOG_WARNING, "finalize_content() called on a message that has already been finalized\n");
- return -1;
- }
- snprintf(clen, sizeof(clen), "%zu", ast_str_strlen(req->content));
- add_header(req, "Content-Length", clen);
- if (ast_str_strlen(req->content)) {
- ast_str_append(&req->data, 0, "\r\n%s", ast_str_buffer(req->content));
- }
- req->lines = ast_str_strlen(req->content) ? 1 : 0;
- return 0;
- }
- /*! \brief Add content (not header) to SIP message */
- static int add_content(struct sip_request *req, const char *line)
- {
- if (req->lines) {
- ast_log(LOG_WARNING, "Can't add more content when the content has been finalized\n");
- return -1;
- }
- ast_str_append(&req->content, 0, "%s", line);
- return 0;
- }
- /*! \brief Copy one header field from one request to another */
- static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field)
- {
- const char *tmp = sip_get_header(orig, field);
- if (!ast_strlen_zero(tmp)) /* Add what we're responding to */
- return add_header(req, field, tmp);
- ast_log(LOG_NOTICE, "No field '%s' present to copy\n", field);
- return -1;
- }
- /*! \brief Copy all headers from one request to another */
- static int copy_all_header(struct sip_request *req, const struct sip_request *orig, const char *field)
- {
- int start = 0;
- int copied = 0;
- for (;;) {
- const char *tmp = __get_header(orig, field, &start);
- if (ast_strlen_zero(tmp))
- break;
- /* Add what we're responding to */
- add_header(req, field, tmp);
- copied++;
- }
- return copied ? 0 : -1;
- }
- /*! \brief Copy SIP VIA Headers from the request to the response
- \note If the client indicates that it wishes to know the port we received from,
- it adds ;rport without an argument to the topmost via header. We need to
- add the port number (from our point of view) to that parameter.
- \verbatim
- We always add ;received=<ip address> to the topmost via header.
- \endverbatim
- Received: RFC 3261, rport RFC 3581 */
- static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const struct sip_request *orig, const char *field)
- {
- int copied = 0;
- int start = 0;
- for (;;) {
- char new[512];
- const char *oh = __get_header(orig, field, &start);
- if (ast_strlen_zero(oh))
- break;
- if (!copied) { /* Only check for empty rport in topmost via header */
- char leftmost[512], *others, *rport;
- /* Only work on leftmost value */
- ast_copy_string(leftmost, oh, sizeof(leftmost));
- others = strchr(leftmost, ',');
- if (others)
- *others++ = '\0';
- /* Find ;rport; (empty request) */
- rport = strstr(leftmost, ";rport");
- if (rport && *(rport+6) == '=')
- rport = NULL; /* We already have a parameter to rport */
- if (((ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT)) || (rport && ast_test_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT)))) {
- /* We need to add received port - rport */
- char *end;
- rport = strstr(leftmost, ";rport");
- if (rport) {
- end = strchr(rport + 1, ';');
- if (end)
- memmove(rport, end, strlen(end) + 1);
- else
- *rport = '\0';
- }
- /* Add rport to first VIA header if requested */
- snprintf(new, sizeof(new), "%s;received=%s;rport=%d%s%s",
- leftmost, ast_sockaddr_stringify_addr_remote(&p->recv),
- ast_sockaddr_port(&p->recv),
- others ? "," : "", others ? others : "");
- } else {
- /* We should *always* add a received to the topmost via */
- snprintf(new, sizeof(new), "%s;received=%s%s%s",
- leftmost, ast_sockaddr_stringify_addr_remote(&p->recv),
- others ? "," : "", others ? others : "");
- }
- oh = new; /* the header to copy */
- } /* else add the following via headers untouched */
- add_header(req, field, oh);
- copied++;
- }
- if (!copied) {
- ast_log(LOG_NOTICE, "No header field '%s' present to copy\n", field);
- return -1;
- }
- return 0;
- }
- /*! \brief Add route header into request per learned route */
- static void add_route(struct sip_request *req, struct sip_route *route, int skip)
- {
- struct ast_str *r;
- if (sip_route_empty(route)) {
- return;
- }
- if ((r = sip_route_list(route, 0, skip))) {
- if (ast_str_strlen(r)) {
- add_header(req, "Route", ast_str_buffer(r));
- }
- ast_free(r);
- }
- }
- /*! \brief Set destination from SIP URI
- *
- * Parse uri to h (host) and port - uri is already just the part inside the <>
- * general form we are expecting is \verbatim sip[s]:username[:password][;parameter]@host[:port][;...] \endverbatim
- * If there's a port given, turn NAPTR/SRV off. NAPTR might indicate SIPS preference even
- * for SIP: uri's
- *
- * If there's a sips: uri scheme, TLS will be required.
- */
- static void set_destination(struct sip_pvt *p, const char *uri)
- {
- char *trans, *maddr, hostname[256];
- const char *h;
- int hn;
- int debug=sip_debug_test_pvt(p);
- int tls_on = FALSE;
- if (debug)
- ast_verbose("set_destination: Parsing <%s> for address/port to send to\n", uri);
- if ((trans = strcasestr(uri, ";transport="))) {
- trans += strlen(";transport=");
- if (!strncasecmp(trans, "ws", 2)) {
- if (debug)
- ast_verbose("set_destination: URI is for WebSocket, we can't set destination\n");
- return;
- }
- }
- /* Find and parse hostname */
- h = strchr(uri, '@');
- if (h)
- ++h;
- else {
- h = uri;
- if (!strncasecmp(h, "sip:", 4)) {
- h += 4;
- } else if (!strncasecmp(h, "sips:", 5)) {
- h += 5;
- tls_on = TRUE;
- }
- }
- hn = strcspn(h, ";>") + 1;
- if (hn > sizeof(hostname))
- hn = sizeof(hostname);
- ast_copy_string(hostname, h, hn);
- /* XXX bug here if string has been trimmed to sizeof(hostname) */
- h += hn - 1;
- /*! \todo XXX If we have sip_cfg.srvlookup on, then look for NAPTR/SRV,
- * otherwise, just look for A records */
- if (ast_sockaddr_resolve_first_transport(&p->sa, hostname, 0, p->socket.type)) {
- ast_log(LOG_WARNING, "Can't find address for host '%s'\n", hostname);
- return;
- }
- /* Got the hostname - but maybe there's a "maddr=" to override address? */
- maddr = strstr(h, "maddr=");
- if (maddr) {
- int port;
- maddr += 6;
- hn = strspn(maddr, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789-.:[]") + 1;
- if (hn > sizeof(hostname))
- hn = sizeof(hostname);
- ast_copy_string(hostname, maddr, hn);
- port = ast_sockaddr_port(&p->sa);
- /*! \todo XXX If we have sip_cfg.srvlookup on, then look for
- * NAPTR/SRV, otherwise, just look for A records */
- if (ast_sockaddr_resolve_first_transport(&p->sa, hostname, PARSE_PORT_FORBID, p->socket.type)) {
- ast_log(LOG_WARNING, "Can't find address for host '%s'\n", hostname);
- return;
- }
- ast_sockaddr_set_port(&p->sa, port);
- }
- if (!ast_sockaddr_port(&p->sa)) {
- ast_sockaddr_set_port(&p->sa, tls_on ?
- STANDARD_TLS_PORT : STANDARD_SIP_PORT);
- }
- if (debug) {
- ast_verbose("set_destination: set destination to %s\n",
- ast_sockaddr_stringify(&p->sa));
- }
- }
- /*! \brief Initialize SIP response, based on SIP request */
- static int init_resp(struct sip_request *resp, const char *msg)
- {
- /* Initialize a response */
- memset(resp, 0, sizeof(*resp));
- resp->method = SIP_RESPONSE;
- if (!(resp->data = ast_str_create(SIP_MIN_PACKET)))
- goto e_return;
- if (!(resp->content = ast_str_create(SIP_MIN_PACKET)))
- goto e_free_data;
- resp->header[0] = 0;
- ast_str_set(&resp->data, 0, "SIP/2.0 %s\r\n", msg);
- resp->headers++;
- return 0;
- e_free_data:
- ast_free(resp->data);
- resp->data = NULL;
- e_return:
- return -1;
- }
- /*! \brief Initialize SIP request */
- static int init_req(struct sip_request *req, int sipmethod, const char *recip)
- {
- /* Initialize a request */
- memset(req, 0, sizeof(*req));
- if (!(req->data = ast_str_create(SIP_MIN_PACKET)))
- goto e_return;
- if (!(req->content = ast_str_create(SIP_MIN_PACKET)))
- goto e_free_data;
- req->method = sipmethod;
- req->header[0] = 0;
- ast_str_set(&req->data, 0, "%s %s SIP/2.0\r\n", sip_methods[sipmethod].text, recip);
- req->headers++;
- return 0;
- e_free_data:
- ast_free(req->data);
- req->data = NULL;
- e_return:
- return -1;
- }
- /*! \brief Deinitialize SIP response/request */
- static void deinit_req(struct sip_request *req)
- {
- if (req->data) {
- ast_free(req->data);
- req->data = NULL;
- }
- if (req->content) {
- ast_free(req->content);
- req->content = NULL;
- }
- }
- /*! \brief Test if this response needs a contact header */
- static inline int resp_needs_contact(const char *msg, enum sipmethod method) {
- /* Requirements for Contact header inclusion in responses generated
- * from the header tables found in the following RFCs. Where the
- * Contact header was marked mandatory (m) or optional (o) this
- * function returns 1.
- *
- * - RFC 3261 (ACK, BYE, CANCEL, INVITE, OPTIONS, REGISTER)
- * - RFC 2976 (INFO)
- * - RFC 3262 (PRACK)
- * - RFC 3265 (SUBSCRIBE, NOTIFY)
- * - RFC 3311 (UPDATE)
- * - RFC 3428 (MESSAGE)
- * - RFC 3515 (REFER)
- * - RFC 3903 (PUBLISH)
- */
- switch (method) {
- /* 1xx, 2xx, 3xx, 485 */
- case SIP_INVITE:
- case SIP_UPDATE:
- case SIP_SUBSCRIBE:
- case SIP_NOTIFY:
- if ((msg[0] >= '1' && msg[0] <= '3') || !strncmp(msg, "485", 3))
- return 1;
- break;
- /* 2xx, 3xx, 485 */
- case SIP_REGISTER:
- case SIP_OPTIONS:
- if (msg[0] == '2' || msg[0] == '3' || !strncmp(msg, "485", 3))
- return 1;
- break;
- /* 3xx, 485 */
- case SIP_BYE:
- case SIP_PRACK:
- case SIP_MESSAGE:
- case SIP_PUBLISH:
- if (msg[0] == '3' || !strncmp(msg, "485", 3))
- return 1;
- break;
- /* 2xx, 3xx, 4xx, 5xx, 6xx */
- case SIP_REFER:
- if (msg[0] >= '2' && msg[0] <= '6')
- return 1;
- break;
- /* contact will not be included for everything else */
- case SIP_ACK:
- case SIP_CANCEL:
- case SIP_INFO:
- case SIP_PING:
- default:
- return 0;
- }
- return 0;
- }
- /*! \brief Prepare SIP response packet */
- static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, const struct sip_request *req)
- {
- char newto[256];
- const char *ot;
- init_resp(resp, msg);
- copy_via_headers(p, resp, req, "Via");
- if (msg[0] == '1' || msg[0] == '2')
- copy_all_header(resp, req, "Record-Route");
- copy_header(resp, req, "From");
- ot = sip_get_header(req, "To");
- if (!strcasestr(ot, "tag=") && strncmp(msg, "100", 3)) {
- /* Add the proper tag if we don't have it already. If they have specified
- their tag, use it. Otherwise, use our own tag */
- if (!ast_strlen_zero(p->theirtag) && ast_test_flag(&p->flags[0], SIP_OUTGOING))
- snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag);
- else if (p->tag && !ast_test_flag(&p->flags[0], SIP_OUTGOING))
- snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->tag);
- else
- ast_copy_string(newto, ot, sizeof(newto));
- ot = newto;
- }
- add_header(resp, "To", ot);
- copy_header(resp, req, "Call-ID");
- copy_header(resp, req, "CSeq");
- if (!ast_strlen_zero(global_useragent))
- add_header(resp, "Server", global_useragent);
- add_header(resp, "Allow", ALLOWED_METHODS);
- add_supported(p, resp);
- /* If this is an invite, add Session-Timers related headers if the feature is active for this session */
- if (p->method == SIP_INVITE && p->stimer && p->stimer->st_active == TRUE) {
- char se_hdr[256];
- snprintf(se_hdr, sizeof(se_hdr), "%d;refresher=%s", p->stimer->st_interval,
- p->stimer->st_ref == SESSION_TIMER_REFRESHER_US ? "uas" : "uac");
- add_header(resp, "Session-Expires", se_hdr);
- /* RFC 2048, Section 9
- * If the refresher parameter in the Session-Expires header field in the
- * 2xx response has a value of 'uac', the UAS MUST place a Require
- * header field into the response with the value 'timer'.
- * ...
- * If the refresher parameter in
- * the 2xx response has a value of 'uas' and the Supported header field
- * in the request contained the value 'timer', the UAS SHOULD place a
- * Require header field into the response with the value 'timer'
- */
- if (p->stimer->st_ref == SESSION_TIMER_REFRESHER_THEM ||
- (p->stimer->st_ref == SESSION_TIMER_REFRESHER_US &&
- p->stimer->st_active_peer_ua == TRUE)) {
- resp->reqsipoptions |= SIP_OPT_TIMER;
- }
- }
- if (msg[0] == '2' && (p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER || p->method == SIP_PUBLISH)) {
- /* For registration responses, we also need expiry and
- contact info */
- add_expires(resp, p->expiry);
- if (p->expiry) { /* Only add contact if we have an expiry time */
- char contact[SIPBUFSIZE];
- const char *contact_uri = p->method == SIP_SUBSCRIBE ? p->our_contact : p->fullcontact;
- char *brackets = strchr(contact_uri, '<');
- snprintf(contact, sizeof(contact), "%s%s%s;expires=%d", brackets ? "" : "<", contact_uri, brackets ? "" : ">", p->expiry);
- add_header(resp, "Contact", contact); /* Not when we unregister */
- }
- if (p->method == SIP_REGISTER && ast_test_flag(&p->flags[0], SIP_USEPATH)) {
- copy_header(resp, req, "Path");
- }
- } else if (!ast_strlen_zero(p->our_contact) && resp_needs_contact(msg, p->method)) {
- add_header(resp, "Contact", p->our_contact);
- }
- if (!ast_strlen_zero(p->url)) {
- add_header(resp, "Access-URL", p->url);
- ast_string_field_set(p, url, NULL);
- }
- /* default to routing the response to the address where the request
- * came from. Since we don't have a transport layer, we do this here.
- * The process_via() function will update the port to either the port
- * specified in the via header or the default port later on (per RFC
- * 3261 section 18.2.2).
- */
- p->sa = p->recv;
- if (process_via(p, req)) {
- ast_log(LOG_WARNING, "error processing via header, will send response to originating address\n");
- }
- return 0;
- }
- /*! \brief Initialize a SIP request message (not the initial one in a dialog) */
- static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, uint32_t seqno, int newbranch)
- {
- struct sip_request *orig = &p->initreq;
- char stripped[80];
- char tmp[80];
- char newto[256];
- const char *c;
- const char *ot, *of;
- int is_strict = FALSE; /*!< Strict routing flag */
- int is_outbound = ast_test_flag(&p->flags[0], SIP_OUTGOING); /* Session direction */
- snprintf(p->lastmsg, sizeof(p->lastmsg), "Tx: %s", sip_methods[sipmethod].text);
- if (!seqno) {
- p->ocseq++;
- seqno = p->ocseq;
- }
- /* A CANCEL must have the same branch as the INVITE that it is canceling. */
- if (sipmethod == SIP_CANCEL) {
- p->branch = p->invite_branch;
- build_via(p);
- } else if (newbranch && (sipmethod == SIP_INVITE)) {
- p->branch ^= ast_random();
- p->invite_branch = p->branch;
- build_via(p);
- } else if (newbranch) {
- p->branch ^= ast_random();
- build_via(p);
- }
- /* Check for strict or loose router */
- if (sip_route_is_strict(&p->route)) {
- is_strict = TRUE;
- if (sipdebug)
- ast_debug(1, "Strict routing enforced for session %s\n", p->callid);
- }
- if (sipmethod == SIP_CANCEL) {
- c = REQ_OFFSET_TO_STR(&p->initreq, rlpart2); /* Use original URI */
- } else if (sipmethod == SIP_ACK) {
- /* Use URI from Contact: in 200 OK (if INVITE)
- (we only have the contacturi on INVITEs) */
- if (!ast_strlen_zero(p->okcontacturi)) {
- c = is_strict ? sip_route_first_uri(&p->route) : p->okcontacturi;
- } else {
- c = REQ_OFFSET_TO_STR(&p->initreq, rlpart2);
- }
- } else if (!ast_strlen_zero(p->okcontacturi)) {
- /* Use for BYE or REINVITE */
- c = is_strict ? sip_route_first_uri(&p->route) : p->okcontacturi;
- } else if (!ast_strlen_zero(p->uri)) {
- c = p->uri;
- } else {
- char *n;
- /* We have no URI, use To: or From: header as URI (depending on direction) */
- ast_copy_string(stripped, sip_get_header(orig, is_outbound ? "To" : "From"),
- sizeof(stripped));
- n = get_in_brackets(stripped);
- c = remove_uri_parameters(n);
- }
- init_req(req, sipmethod, c);
- snprintf(tmp, sizeof(tmp), "%u %s", seqno, sip_methods[sipmethod].text);
- add_header(req, "Via", p->via);
- /*
- * Use the learned route set unless this is a CANCEL or an ACK for a non-2xx
- * final response. For a CANCEL or ACK, we have to send to the same destination
- * as the original INVITE.
- * Send UPDATE to the same destination as CANCEL, if call is not in final state.
- */
- if (!sip_route_empty(&p->route) &&
- !(sipmethod == SIP_CANCEL ||
- (sipmethod == SIP_ACK && (p->invitestate == INV_COMPLETED || p->invitestate == INV_CANCELLED)) ||
- (sipmethod == SIP_UPDATE && (p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA)))) {
- if (p->socket.type != AST_TRANSPORT_UDP && p->socket.tcptls_session) {
- /* For TCP/TLS sockets that are connected we won't need
- * to do any hostname/IP lookups */
- } else if (ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT)) {
- /* For NATed traffic, we ignore the contact/route and
- * simply send to the received-from address. No need
- * for lookups. */
- } else {
- set_destination(p, sip_route_first_uri(&p->route));
- }
- add_route(req, &p->route, is_strict ? 1 : 0);
- }
- add_max_forwards(p, req);
- ot = sip_get_header(orig, "To");
- of = sip_get_header(orig, "From");
- /* Add tag *unless* this is a CANCEL, in which case we need to send it exactly
- as our original request, including tag (or presumably lack thereof) */
- if (!strcasestr(ot, "tag=") && sipmethod != SIP_CANCEL) {
- /* Add the proper tag if we don't have it already. If they have specified
- their tag, use it. Otherwise, use our own tag */
- if (is_outbound && !ast_strlen_zero(p->theirtag))
- snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag);
- else if (!is_outbound)
- snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->tag);
- else
- snprintf(newto, sizeof(newto), "%s", ot);
- ot = newto;
- }
- if (is_outbound) {
- add_header(req, "From", of);
- add_header(req, "To", ot);
- } else {
- add_header(req, "From", ot);
- add_header(req, "To", of);
- }
- /* Do not add Contact for MESSAGE, BYE and Cancel requests */
- if (sipmethod != SIP_BYE && sipmethod != SIP_CANCEL && sipmethod != SIP_MESSAGE)
- add_header(req, "Contact", p->our_contact);
- copy_header(req, orig, "Call-ID");
- add_header(req, "CSeq", tmp);
- if (!ast_strlen_zero(global_useragent))
- add_header(req, "User-Agent", global_useragent);
- if (!ast_strlen_zero(p->url)) {
- add_header(req, "Access-URL", p->url);
- ast_string_field_set(p, url, NULL);
- }
- /* Add Session-Timers related headers if the feature is active for this session.
- An exception to this behavior is the ACK request. Since Asterisk never requires
- session-timers support from a remote end-point (UAS) in an INVITE, it must
- not send 'Require: timer' header in the ACK request.
- This should only be added in the INVITE transactions, not MESSAGE or REFER or other
- in-dialog messages.
- */
- if (p->stimer && p->stimer->st_active == TRUE && p->stimer->st_active_peer_ua == TRUE
- && sipmethod == SIP_INVITE) {
- char se_hdr[256];
- snprintf(se_hdr, sizeof(se_hdr), "%d;refresher=%s", p->stimer->st_interval,
- p->stimer->st_ref == SESSION_TIMER_REFRESHER_US ? "uac" : "uas");
- add_header(req, "Session-Expires", se_hdr);
- snprintf(se_hdr, sizeof(se_hdr), "%d", st_get_se(p, FALSE));
- add_header(req, "Min-SE", se_hdr);
- }
- return 0;
- }
- /*! \brief Base transmit response function */
- static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable)
- {
- struct sip_request resp;
- uint32_t seqno = 0;
- if (reliable && (sscanf(sip_get_header(req, "CSeq"), "%30u ", &seqno) != 1)) {
- ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", sip_get_header(req, "CSeq"));
- return -1;
- }
- respprep(&resp, p, msg, req);
- if (ast_test_flag(&p->flags[0], SIP_SENDRPID)
- && ast_test_flag(&p->flags[1], SIP_PAGE2_CONNECTLINEUPDATE_PEND)
- && (!strncmp(msg, "180", 3) || !strncmp(msg, "183", 3))) {
- ast_clear_flag(&p->flags[1], SIP_PAGE2_CONNECTLINEUPDATE_PEND);
- add_rpid(&resp, p);
- }
- if (ast_test_flag(&p->flags[0], SIP_OFFER_CC)) {
- add_cc_call_info_to_response(p, &resp);
- }
- /* If we are sending a 302 Redirect we can add a diversion header if the redirect information is set */
- if (!strncmp(msg, "302", 3)) {
- add_diversion(&resp, p);
- }
- /* If we are cancelling an incoming invite for some reason, add information
- about the reason why we are doing this in clear text */
- if (p->method == SIP_INVITE && msg[0] != '1') {
- char buf[20];
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_Q850_REASON)) {
- int hangupcause = 0;
- if (p->owner && ast_channel_hangupcause(p->owner)) {
- hangupcause = ast_channel_hangupcause(p->owner);
- } else if (p->hangupcause) {
- hangupcause = p->hangupcause;
- } else {
- int respcode;
- if (sscanf(msg, "%30d ", &respcode))
- hangupcause = hangup_sip2cause(respcode);
- }
- if (hangupcause) {
- sprintf(buf, "Q.850;cause=%i", hangupcause & 0x7f);
- add_header(&resp, "Reason", buf);
- }
- }
- if (p->owner && ast_channel_hangupcause(p->owner)) {
- add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(ast_channel_hangupcause(p->owner)));
- snprintf(buf, sizeof(buf), "%d", ast_channel_hangupcause(p->owner));
- add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
- }
- }
- return send_response(p, &resp, reliable, seqno);
- }
- static int transmit_response_with_sip_etag(struct sip_pvt *p, const char *msg, const struct sip_request *req, struct sip_esc_entry *esc_entry, int need_new_etag)
- {
- struct sip_request resp;
- if (need_new_etag) {
- create_new_sip_etag(esc_entry, 1);
- }
- respprep(&resp, p, msg, req);
- add_header(&resp, "SIP-ETag", esc_entry->entity_tag);
- return send_response(p, &resp, 0, 0);
- }
- static int temp_pvt_init(void *data)
- {
- struct sip_pvt *p = data;
- p->do_history = 0; /* XXX do we need it ? isn't already all 0 ? */
- return ast_string_field_init(p, 512);
- }
- static void temp_pvt_cleanup(void *data)
- {
- struct sip_pvt *p = data;
- ast_string_field_free_memory(p);
- ast_free(data);
- }
- /*! \brief Transmit response, no retransmits, using a temporary pvt structure */
- static int transmit_response_using_temp(ast_string_field callid, struct ast_sockaddr *addr, int useglobal_nat, const int intended_method, const struct sip_request *req, const char *msg)
- {
- struct sip_pvt *p = NULL;
- if (!(p = ast_threadstorage_get(&ts_temp_pvt, sizeof(*p)))) {
- ast_log(LOG_ERROR, "Failed to get temporary pvt\n");
- return -1;
- }
- /* XXX the structure may be dirty from previous usage.
- * Here we should state clearly how we should reinitialize it
- * before using it.
- * E.g. certainly the threadstorage should be left alone,
- * but other thihngs such as flags etc. maybe need cleanup ?
- */
- /* Initialize the bare minimum */
- p->method = intended_method;
- if (!addr) {
- ast_sockaddr_copy(&p->ourip, &internip);
- } else {
- ast_sockaddr_copy(&p->sa, addr);
- ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
- }
- p->branch = ast_random();
- make_our_tag(p);
- p->ocseq = INITIAL_CSEQ;
- if (useglobal_nat && addr) {
- ast_copy_flags(&p->flags[0], &global_flags[0], SIP_NAT_FORCE_RPORT);
- ast_copy_flags(&p->flags[2], &global_flags[2], SIP_PAGE3_NAT_AUTO_RPORT);
- ast_sockaddr_copy(&p->recv, addr);
- check_via(p, req);
- }
- ast_string_field_set(p, fromdomain, default_fromdomain);
- p->fromdomainport = default_fromdomainport;
- build_via(p);
- ast_string_field_set(p, callid, callid);
- copy_socket_data(&p->socket, &req->socket);
- /* Use this temporary pvt structure to send the message */
- __transmit_response(p, msg, req, XMIT_UNRELIABLE);
- /* Free the string fields, but not the pool space */
- ast_string_field_init(p, 0);
- return 0;
- }
- /*! \brief Transmit response, no retransmits */
- static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req)
- {
- return __transmit_response(p, msg, req, XMIT_UNRELIABLE);
- }
- /*! \brief Transmit response, no retransmits */
- static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported)
- {
- struct sip_request resp;
- respprep(&resp, p, msg, req);
- add_date(&resp);
- add_header(&resp, "Unsupported", unsupported);
- return send_response(p, &resp, XMIT_UNRELIABLE, 0);
- }
- /*! \brief Transmit 422 response with Min-SE header (Session-Timers) */
- static int transmit_response_with_minse(struct sip_pvt *p, const char *msg, const struct sip_request *req, int minse_int)
- {
- struct sip_request resp;
- char minse_str[20];
- respprep(&resp, p, msg, req);
- add_date(&resp);
- snprintf(minse_str, sizeof(minse_str), "%d", minse_int);
- add_header(&resp, "Min-SE", minse_str);
- return send_response(p, &resp, XMIT_UNRELIABLE, 0);
- }
- /*! \brief Transmit response, Make sure you get an ACK
- This is only used for responses to INVITEs, where we need to make sure we get an ACK
- */
- static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req)
- {
- return __transmit_response(p, msg, req, req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL);
- }
- /*! \brief Add date header to SIP message */
- static void add_date(struct sip_request *req)
- {
- char tmp[256];
- struct tm tm;
- time_t t = time(NULL);
- gmtime_r(&t, &tm);
- strftime(tmp, sizeof(tmp), "%a, %d %b %Y %T GMT", &tm);
- add_header(req, "Date", tmp);
- }
- /*! \brief Add Expires header to SIP message */
- static void add_expires(struct sip_request *req, int expires)
- {
- char tmp[32];
- snprintf(tmp, sizeof(tmp), "%d", expires);
- add_header(req, "Expires", tmp);
- }
- /*! \brief Append Retry-After header field when transmitting response */
- static int transmit_response_with_retry_after(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *seconds)
- {
- struct sip_request resp;
- respprep(&resp, p, msg, req);
- add_header(&resp, "Retry-After", seconds);
- return send_response(p, &resp, XMIT_UNRELIABLE, 0);
- }
- /*! \brief Add date before transmitting response */
- static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req)
- {
- struct sip_request resp;
- respprep(&resp, p, msg, req);
- add_date(&resp);
- return send_response(p, &resp, XMIT_UNRELIABLE, 0);
- }
- /*! \brief Append Accept header, content length before transmitting response */
- static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable)
- {
- struct sip_request resp;
- respprep(&resp, p, msg, req);
- add_header(&resp, "Accept", "application/sdp");
- return send_response(p, &resp, reliable, 0);
- }
- /*! \brief Append Min-Expires header, content length before transmitting response */
- static int transmit_response_with_minexpires(struct sip_pvt *p, const char *msg, const struct sip_request *req, int minexpires)
- {
- struct sip_request resp;
- char tmp[32];
- snprintf(tmp, sizeof(tmp), "%d", minexpires);
- respprep(&resp, p, msg, req);
- add_header(&resp, "Min-Expires", tmp);
- return send_response(p, &resp, XMIT_UNRELIABLE, 0);
- }
- /*! \brief Respond with authorization request */
- static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *nonce, enum xmittype reliable, const char *header, int stale)
- {
- struct sip_request resp;
- char tmp[512];
- uint32_t seqno = 0;
- if (reliable && (sscanf(sip_get_header(req, "CSeq"), "%30u ", &seqno) != 1)) {
- ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", sip_get_header(req, "CSeq"));
- return -1;
- }
- /* Choose Realm */
- get_realm(p, req);
- /* Stale means that they sent us correct authentication, but
- based it on an old challenge (nonce) */
- snprintf(tmp, sizeof(tmp), "Digest algorithm=MD5, realm=\"%s\", nonce=\"%s\"%s", p->realm, nonce, stale ? ", stale=true" : "");
- respprep(&resp, p, msg, req);
- add_header(&resp, header, tmp);
- append_history(p, "AuthChal", "Auth challenge sent for %s - nc %d", p->username, p->noncecount);
- return send_response(p, &resp, reliable, seqno);
- }
- /*!
- \brief Extract domain from SIP To/From header
- \return -1 on error, 1 if domain string is empty, 0 if domain was properly extracted
- \note TODO: Such code is all over SIP channel, there is a sense to organize
- this patern in one function
- */
- static int get_domain(const char *str, char *domain, int len)
- {
- char tmpf[256];
- char *a, *from;
- *domain = '\0';
- ast_copy_string(tmpf, str, sizeof(tmpf));
- from = get_in_brackets(tmpf);
- if (!ast_strlen_zero(from)) {
- if (strncasecmp(from, "sip:", 4)) {
- ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", from);
- return -1;
- }
- from += 4;
- } else
- from = NULL;
- if (from) {
- int bracket = 0;
- /* Strip any params or options from user */
- if ((a = strchr(from, ';')))
- *a = '\0';
- /* Strip port from domain if present */
- for (a = from; *a != '\0'; ++a) {
- if (*a == ':' && bracket == 0) {
- *a = '\0';
- break;
- } else if (*a == '[') {
- ++bracket;
- } else if (*a == ']') {
- --bracket;
- }
- }
- if ((a = strchr(from, '@'))) {
- *a = '\0';
- ast_copy_string(domain, a + 1, len);
- } else
- ast_copy_string(domain, from, len);
- }
- return ast_strlen_zero(domain);
- }
- /*!
- \brief Choose realm based on From header and then To header or use globaly configured realm.
- Realm from From/To header should be listed among served domains in config file: domain=...
- */
- static void get_realm(struct sip_pvt *p, const struct sip_request *req)
- {
- char domain[MAXHOSTNAMELEN];
- if (!ast_strlen_zero(p->realm))
- return;
- if (sip_cfg.domainsasrealm &&
- !AST_LIST_EMPTY(&domain_list))
- {
- /* Check From header first */
- if (!get_domain(sip_get_header(req, "From"), domain, sizeof(domain))) {
- if (check_sip_domain(domain, NULL, 0)) {
- ast_string_field_set(p, realm, domain);
- return;
- }
- }
- /* Check To header */
- if (!get_domain(sip_get_header(req, "To"), domain, sizeof(domain))) {
- if (check_sip_domain(domain, NULL, 0)) {
- ast_string_field_set(p, realm, domain);
- return;
- }
- }
- }
- /* Use default realm from config file */
- ast_string_field_set(p, realm, sip_cfg.realm);
- }
- /*!
- * \internal
- *
- * \arg msg Only use a string constant for the msg, here, it is shallow copied
- *
- * \note assumes the sip_pvt is locked.
- */
- static int transmit_provisional_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, int with_sdp)
- {
- int res;
- if (!(res = with_sdp ? transmit_response_with_sdp(p, msg, req, XMIT_UNRELIABLE, FALSE, FALSE) : transmit_response(p, msg, req))) {
- p->last_provisional = msg;
- update_provisional_keepalive(p, with_sdp);
- }
- return res;
- }
- /*!
- * \internal
- * \brief Destroy all additional MESSAGE headers.
- *
- * \param pvt SIP private dialog struct.
- *
- * \return Nothing
- */
- static void destroy_msg_headers(struct sip_pvt *pvt)
- {
- struct sip_msg_hdr *doomed;
- while ((doomed = AST_LIST_REMOVE_HEAD(&pvt->msg_headers, next))) {
- ast_free(doomed);
- }
- }
- /*!
- * \internal
- * \brief Add a MESSAGE header to the dialog.
- *
- * \param pvt SIP private dialog struct.
- * \param hdr_name Name of header for MESSAGE.
- * \param hdr_value Value of header for MESSAGE.
- *
- * \return Nothing
- */
- static void add_msg_header(struct sip_pvt *pvt, const char *hdr_name, const char *hdr_value)
- {
- size_t hdr_len_name;
- size_t hdr_len_value;
- struct sip_msg_hdr *node;
- char *pos;
- hdr_len_name = strlen(hdr_name) + 1;
- hdr_len_value = strlen(hdr_value) + 1;
- node = ast_calloc(1, sizeof(*node) + hdr_len_name + hdr_len_value);
- if (!node) {
- return;
- }
- pos = node->stuff;
- node->name = pos;
- strcpy(pos, hdr_name);
- pos += hdr_len_name;
- node->value = pos;
- strcpy(pos, hdr_value);
- AST_LIST_INSERT_TAIL(&pvt->msg_headers, node, next);
- }
- /*! \brief Add text body to SIP message */
- static int add_text(struct sip_request *req, struct sip_pvt *p)
- {
- const char *content_type = NULL;
- struct sip_msg_hdr *node;
- /* Add any additional MESSAGE headers. */
- AST_LIST_TRAVERSE(&p->msg_headers, node, next) {
- if (!strcasecmp(node->name, "Content-Type")) {
- /* Save content type */
- content_type = node->value;
- } else {
- add_header(req, node->name, node->value);
- }
- }
- if (ast_strlen_zero(content_type)) {
- /* "Content-Type" not set - use default value */
- content_type = "text/plain;charset=UTF-8";
- }
- add_header(req, "Content-Type", content_type);
- /* XXX Convert \n's to \r\n's XXX */
- add_content(req, p->msg_body);
- return 0;
- }
- /*! \brief Add DTMF INFO tone to sip message
- Mode = 0 for application/dtmf-relay (Cisco)
- 1 for application/dtmf
- */
- static int add_digit(struct sip_request *req, char digit, unsigned int duration, int mode)
- {
- char tmp[256];
- int event;
- if (mode) {
- /* Application/dtmf short version used by some implementations */
- if ('0' <= digit && digit <= '9') {
- event = digit - '0';
- } else if (digit == '*') {
- event = 10;
- } else if (digit == '#') {
- event = 11;
- } else if ('A' <= digit && digit <= 'D') {
- event = 12 + digit - 'A';
- } else if ('a' <= digit && digit <= 'd') {
- event = 12 + digit - 'a';
- } else {
- /* Unknown digit */
- event = 0;
- }
- snprintf(tmp, sizeof(tmp), "%d\r\n", event);
- add_header(req, "Content-Type", "application/dtmf");
- add_content(req, tmp);
- } else {
- /* Application/dtmf-relay as documented by Cisco */
- snprintf(tmp, sizeof(tmp), "Signal=%c\r\nDuration=%u\r\n", digit, duration);
- add_header(req, "Content-Type", "application/dtmf-relay");
- add_content(req, tmp);
- }
- return 0;
- }
- /*!
- * \pre if p->owner exists, it must be locked
- * \brief Add Remote-Party-ID header to SIP message
- */
- static int add_rpid(struct sip_request *req, struct sip_pvt *p)
- {
- struct ast_str *tmp = ast_str_alloca(256);
- char tmp2[256];
- char lid_name_buf[128];
- char *lid_num;
- char *lid_name;
- int lid_pres;
- const char *fromdomain;
- const char *privacy = NULL;
- const char *screen = NULL;
- struct ast_party_id connected_id;
- const char *anonymous_string = "\"Anonymous\" <sip:anonymous@anonymous.invalid>";
- if (!ast_test_flag(&p->flags[0], SIP_SENDRPID)) {
- return 0;
- }
- if (!p->owner) {
- return 0;
- }
- connected_id = ast_channel_connected_effective_id(p->owner);
- lid_num = S_COR(connected_id.number.valid, connected_id.number.str, NULL);
- if (!lid_num) {
- return 0;
- }
- lid_name = S_COR(connected_id.name.valid, connected_id.name.str, NULL);
- if (!lid_name) {
- lid_name = lid_num;
- }
- ast_escape_quoted(lid_name, lid_name_buf, sizeof(lid_name_buf));
- lid_pres = ast_party_id_presentation(&connected_id);
- if (((lid_pres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) &&
- (ast_test_flag(&p->flags[1], SIP_PAGE2_TRUST_ID_OUTBOUND) == SIP_PAGE2_TRUST_ID_OUTBOUND_NO)) {
- /* If pres is not allowed and we don't trust the peer, we don't apply an RPID header */
- return 0;
- }
- fromdomain = p->fromdomain;
- if (!fromdomain ||
- ((ast_test_flag(&p->flags[1], SIP_PAGE2_TRUST_ID_OUTBOUND) == SIP_PAGE2_TRUST_ID_OUTBOUND_YES) &&
- !strcmp("anonymous.invalid", fromdomain))) {
- /* If the fromdomain is NULL or if it was set to anonymous.invalid due to privacy settings and we trust the peer,
- * use the host IP address */
- fromdomain = ast_sockaddr_stringify_host_remote(&p->ourip);
- }
- lid_num = ast_uri_encode(lid_num, tmp2, sizeof(tmp2), ast_uri_sip_user);
- if (ast_test_flag(&p->flags[0], SIP_SENDRPID_PAI)) {
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_TRUST_ID_OUTBOUND) != SIP_PAGE2_TRUST_ID_OUTBOUND_LEGACY) {
- /* trust_id_outbound = yes - Always give full information even if it's private, but append a privacy header
- * When private data is included */
- ast_str_set(&tmp, -1, "\"%s\" <sip:%s@%s>", lid_name_buf, lid_num, fromdomain);
- if ((lid_pres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) {
- add_header(req, "Privacy", "id");
- }
- } else {
- /* trust_id_outbound = legacy - behave in a non RFC-3325 compliant manner and send anonymized data when
- * when handling private data. */
- if ((lid_pres & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
- ast_str_set(&tmp, -1, "\"%s\" <sip:%s@%s>", lid_name_buf, lid_num, fromdomain);
- } else {
- ast_str_set(&tmp, -1, "%s", anonymous_string);
- }
- }
- add_header(req, "P-Asserted-Identity", ast_str_buffer(tmp));
- } else {
- ast_str_set(&tmp, -1, "\"%s\" <sip:%s@%s>;party=%s", lid_name_buf, lid_num, fromdomain, p->outgoing_call ? "calling" : "called");
- switch (lid_pres) {
- case AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED:
- case AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN:
- privacy = "off";
- screen = "no";
- break;
- case AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN:
- case AST_PRES_ALLOWED_NETWORK_NUMBER:
- privacy = "off";
- screen = "yes";
- break;
- case AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED:
- case AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN:
- privacy = "full";
- screen = "no";
- break;
- case AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN:
- case AST_PRES_PROHIB_NETWORK_NUMBER:
- privacy = "full";
- screen = "yes";
- break;
- case AST_PRES_NUMBER_NOT_AVAILABLE:
- break;
- default:
- if ((lid_pres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) {
- privacy = "full";
- }
- else
- privacy = "off";
- screen = "no";
- break;
- }
- if (!ast_strlen_zero(privacy) && !ast_strlen_zero(screen)) {
- ast_str_append(&tmp, -1, ";privacy=%s;screen=%s", privacy, screen);
- }
- add_header(req, "Remote-Party-ID", ast_str_buffer(tmp));
- }
- return 0;
- }
- /*! \brief add XML encoded media control with update
- \note XML: The only way to turn 0 bits of information into a few hundred. (markster) */
- static int add_vidupdate(struct sip_request *req)
- {
- const char *xml_is_a_huge_waste_of_space =
- "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n"
- " <media_control>\r\n"
- " <vc_primitive>\r\n"
- " <to_encoder>\r\n"
- " <picture_fast_update>\r\n"
- " </picture_fast_update>\r\n"
- " </to_encoder>\r\n"
- " </vc_primitive>\r\n"
- " </media_control>\r\n";
- add_header(req, "Content-Type", "application/media_control+xml");
- add_content(req, xml_is_a_huge_waste_of_space);
- return 0;
- }
- /*! \brief Add ICE attributes to SDP */
- static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf)
- {
- struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(instance);
- const char *username, *password;
- struct ao2_container *candidates;
- struct ao2_iterator i;
- struct ast_rtp_engine_ice_candidate *candidate;
- /* If no ICE support is present we can't very well add the attributes */
- if (!ice || !(candidates = ice->get_local_candidates(instance))) {
- return;
- }
- if ((username = ice->get_ufrag(instance))) {
- ast_str_append(a_buf, 0, "a=ice-ufrag:%s\r\n", username);
- }
- if ((password = ice->get_password(instance))) {
- ast_str_append(a_buf, 0, "a=ice-pwd:%s\r\n", password);
- }
- i = ao2_iterator_init(candidates, 0);
- while ((candidate = ao2_iterator_next(&i))) {
- ast_str_append(a_buf, 0, "a=candidate:%s %u %s %d ", candidate->foundation, candidate->id, candidate->transport, candidate->priority);
- ast_str_append(a_buf, 0, "%s ", ast_sockaddr_stringify_host(&candidate->address));
- ast_str_append(a_buf, 0, "%s typ ", ast_sockaddr_stringify_port(&candidate->address));
- if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_HOST) {
- ast_str_append(a_buf, 0, "host");
- } else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_SRFLX) {
- ast_str_append(a_buf, 0, "srflx");
- } else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_RELAYED) {
- ast_str_append(a_buf, 0, "relay");
- }
- if (!ast_sockaddr_isnull(&candidate->relay_address)) {
- ast_str_append(a_buf, 0, " raddr %s ", ast_sockaddr_stringify_host(&candidate->relay_address));
- ast_str_append(a_buf, 0, "rport %s", ast_sockaddr_stringify_port(&candidate->relay_address));
- }
- ast_str_append(a_buf, 0, "\r\n");
- ao2_ref(candidate, -1);
- }
- ao2_iterator_destroy(&i);
- ao2_ref(candidates, -1);
- }
- /*! \brief Start ICE negotiation on an RTP instance */
- static void start_ice(struct ast_rtp_instance *instance, int offer)
- {
- struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(instance);
- if (!ice) {
- return;
- }
- /* If we are the offerer then we are the controlling agent, otherwise they are */
- ice->set_role(instance, offer ? AST_RTP_ICE_ROLE_CONTROLLING : AST_RTP_ICE_ROLE_CONTROLLED);
- ice->start(instance);
- }
- /*! \brief Add DTLS attributes to SDP */
- static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf)
- {
- struct ast_rtp_engine_dtls *dtls;
- enum ast_rtp_dtls_hash hash;
- const char *fingerprint;
- if (!instance || !(dtls = ast_rtp_instance_get_dtls(instance)) || !dtls->active(instance)) {
- return;
- }
- switch (dtls->get_connection(instance)) {
- case AST_RTP_DTLS_CONNECTION_NEW:
- ast_str_append(a_buf, 0, "a=connection:new\r\n");
- break;
- case AST_RTP_DTLS_CONNECTION_EXISTING:
- ast_str_append(a_buf, 0, "a=connection:existing\r\n");
- break;
- default:
- break;
- }
- switch (dtls->get_setup(instance)) {
- case AST_RTP_DTLS_SETUP_ACTIVE:
- ast_str_append(a_buf, 0, "a=setup:active\r\n");
- break;
- case AST_RTP_DTLS_SETUP_PASSIVE:
- ast_str_append(a_buf, 0, "a=setup:passive\r\n");
- break;
- case AST_RTP_DTLS_SETUP_ACTPASS:
- ast_str_append(a_buf, 0, "a=setup:actpass\r\n");
- break;
- case AST_RTP_DTLS_SETUP_HOLDCONN:
- ast_str_append(a_buf, 0, "a=setup:holdconn\r\n");
- break;
- default:
- break;
- }
- hash = dtls->get_fingerprint_hash(instance);
- fingerprint = dtls->get_fingerprint(instance);
- if (fingerprint && (hash == AST_RTP_DTLS_HASH_SHA1 || hash == AST_RTP_DTLS_HASH_SHA256)) {
- ast_str_append(a_buf, 0, "a=fingerprint:%s %s\r\n", hash == AST_RTP_DTLS_HASH_SHA1 ? "SHA-1" : "SHA-256",
- fingerprint);
- }
- }
- /*! \brief Add codec offer to SDP offer/answer body in INVITE or 200 OK */
- static void add_codec_to_sdp(const struct sip_pvt *p,
- struct ast_format *format,
- struct ast_str **m_buf,
- struct ast_str **a_buf,
- int debug,
- int *min_packet_size,
- int *max_packet_size)
- {
- int rtp_code;
- const char *mime;
- unsigned int rate, framing;
- if (debug)
- ast_verbose("Adding codec %s to SDP\n", ast_format_get_name(format));
- if (((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->rtp), 1, format, 0)) == -1) ||
- !(mime = ast_rtp_lookup_mime_subtype2(1, format, 0, ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0)) ||
- !(rate = ast_rtp_lookup_sample_rate2(1, format, 0))) {
- return;
- }
- ast_str_append(m_buf, 0, " %d", rtp_code);
- /* Opus mandates 2 channels in rtpmap */
- if (ast_format_cmp(format, ast_format_opus) == AST_FORMAT_CMP_EQUAL) {
- ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%u/2\r\n", rtp_code, mime, rate);
- } else {
- ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%u\r\n", rtp_code, mime, rate);
- }
- ast_format_generate_sdp_fmtp(format, rtp_code, a_buf);
- framing = ast_format_cap_get_format_framing(p->caps, format);
- if (ast_format_cmp(format, ast_format_g729) == AST_FORMAT_CMP_EQUAL) {
- /* Indicate that we don't support VAD (G.729 annex B) */
- ast_str_append(a_buf, 0, "a=fmtp:%d annexb=no\r\n", rtp_code);
- } else if (ast_format_cmp(format, ast_format_g723) == AST_FORMAT_CMP_EQUAL) {
- /* Indicate that we don't support VAD (G.723.1 annex A) */
- ast_str_append(a_buf, 0, "a=fmtp:%d annexa=no\r\n", rtp_code);
- } else if (ast_format_cmp(format, ast_format_ilbc) == AST_FORMAT_CMP_EQUAL) {
- /* Add information about us using only 20/30 ms packetization */
- ast_str_append(a_buf, 0, "a=fmtp:%d mode=%u\r\n", rtp_code, framing);
- } else if (ast_format_cmp(format, ast_format_siren7) == AST_FORMAT_CMP_EQUAL) {
- /* Indicate that we only expect 32Kbps */
- ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=32000\r\n", rtp_code);
- } else if (ast_format_cmp(format, ast_format_siren14) == AST_FORMAT_CMP_EQUAL) {
- /* Indicate that we only expect 48Kbps */
- ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=48000\r\n", rtp_code);
- } else if (ast_format_cmp(format, ast_format_g719) == AST_FORMAT_CMP_EQUAL) {
- /* Indicate that we only expect 64Kbps */
- ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=64000\r\n", rtp_code);
- }
- if (max_packet_size && ast_format_get_maximum_ms(format) &&
- (ast_format_get_maximum_ms(format) < *max_packet_size)) {
- *max_packet_size = ast_format_get_maximum_ms(format);
- }
- if (framing && (framing < *min_packet_size)) {
- *min_packet_size = framing;
- }
- /* Our first codec packetization processed cannot be zero */
- if ((*min_packet_size) == 0 && framing) {
- *min_packet_size = framing;
- }
- if ((*max_packet_size) == 0 && ast_format_get_maximum_ms(format)) {
- *max_packet_size = ast_format_get_maximum_ms(format);
- }
- }
- /*! \brief Add video codec offer to SDP offer/answer body in INVITE or 200 OK */
- /* This is different to the audio one now so we can add more caps later */
- static void add_vcodec_to_sdp(const struct sip_pvt *p, struct ast_format *format,
- struct ast_str **m_buf, struct ast_str **a_buf,
- int debug, int *min_packet_size)
- {
- int rtp_code;
- const char *subtype;
- unsigned int rate;
- if (!p->vrtp)
- return;
- if (debug)
- ast_verbose("Adding video codec %s to SDP\n", ast_format_get_name(format));
- if (((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->vrtp), 1, format, 0)) == -1) ||
- !(subtype = ast_rtp_lookup_mime_subtype2(1, format, 0, 0)) ||
- !(rate = ast_rtp_lookup_sample_rate2(1, format, 0))) {
- return;
- }
- ast_str_append(m_buf, 0, " %d", rtp_code);
- ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%u\r\n", rtp_code, subtype, rate);
- /* VP8: add RTCP FIR support */
- if (ast_format_cmp(format, ast_format_vp8) == AST_FORMAT_CMP_EQUAL) {
- ast_str_append(a_buf, 0, "a=rtcp-fb:* ccm fir\r\n");
- }
- ast_format_generate_sdp_fmtp(format, rtp_code, a_buf);
- }
- /*! \brief Add text codec offer to SDP offer/answer body in INVITE or 200 OK */
- static void add_tcodec_to_sdp(const struct sip_pvt *p, struct ast_format *format,
- struct ast_str **m_buf, struct ast_str **a_buf,
- int debug, int *min_packet_size)
- {
- int rtp_code;
- if (!p->trtp)
- return;
- if (debug)
- ast_verbose("Adding text codec %s to SDP\n", ast_format_get_name(format));
- if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->trtp), 1, format, 0)) == -1)
- return;
- ast_str_append(m_buf, 0, " %d", rtp_code);
- ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%u\r\n", rtp_code,
- ast_rtp_lookup_mime_subtype2(1, format, 0, 0),
- ast_rtp_lookup_sample_rate2(1, format, 0));
- /* Add fmtp code here */
- if (ast_format_cmp(format, ast_format_t140_red) == AST_FORMAT_CMP_EQUAL) {
- int t140code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->trtp), 1, ast_format_t140, 0);
- ast_str_append(a_buf, 0, "a=fmtp:%d %d/%d/%d\r\n", rtp_code,
- t140code,
- t140code,
- t140code);
- }
- }
- /*! \brief Get Max T.38 Transmission rate from T38 capabilities */
- static unsigned int t38_get_rate(enum ast_control_t38_rate rate)
- {
- switch (rate) {
- case AST_T38_RATE_2400:
- return 2400;
- case AST_T38_RATE_4800:
- return 4800;
- case AST_T38_RATE_7200:
- return 7200;
- case AST_T38_RATE_9600:
- return 9600;
- case AST_T38_RATE_12000:
- return 12000;
- case AST_T38_RATE_14400:
- return 14400;
- default:
- return 0;
- }
- }
- /*! \brief Add RFC 2833 DTMF offer to SDP */
- static void add_noncodec_to_sdp(const struct sip_pvt *p, int format,
- struct ast_str **m_buf, struct ast_str **a_buf,
- int debug)
- {
- int rtp_code;
- if (debug)
- ast_verbose("Adding non-codec 0x%x (%s) to SDP\n", (unsigned)format, ast_rtp_lookup_mime_subtype2(0, NULL, format, 0));
- if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->rtp), 0, NULL, format)) == -1)
- return;
- ast_str_append(m_buf, 0, " %d", rtp_code);
- ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%u\r\n", rtp_code,
- ast_rtp_lookup_mime_subtype2(0, NULL, format, 0),
- ast_rtp_lookup_sample_rate2(0, NULL, format));
- if (format == AST_RTP_DTMF) /* Indicate we support DTMF and FLASH... */
- ast_str_append(a_buf, 0, "a=fmtp:%d 0-16\r\n", rtp_code);
- }
- /*! \brief Set all IP media addresses for this call
- \note called from add_sdp()
- */
- static void get_our_media_address(struct sip_pvt *p, int needvideo, int needtext,
- struct ast_sockaddr *addr, struct ast_sockaddr *vaddr,
- struct ast_sockaddr *taddr, struct ast_sockaddr *dest,
- struct ast_sockaddr *vdest, struct ast_sockaddr *tdest)
- {
- int use_externip = 0;
- /* First, get our address */
- ast_rtp_instance_get_local_address(p->rtp, addr);
- if (p->vrtp) {
- ast_rtp_instance_get_local_address(p->vrtp, vaddr);
- }
- if (p->trtp) {
- ast_rtp_instance_get_local_address(p->trtp, taddr);
- }
- /* If our real IP differs from the local address returned by the RTP engine, use it. */
- /* The premise is that if we are already using that IP to communicate with the client, */
- /* we should be using it for RTP too. */
- use_externip = ast_sockaddr_cmp_addr(&p->ourip, addr);
- /* Now, try to figure out where we want them to send data */
- /* Is this a re-invite to move the media out, then use the original offer from caller */
- if (!ast_sockaddr_isnull(&p->redirip)) { /* If we have a redirection IP, use it */
- ast_sockaddr_copy(dest, &p->redirip);
- } else {
- /*
- * Audio Destination IP:
- *
- * 1. Specifically configured media address.
- * 2. Local address as specified by the RTP engine.
- * 3. The local IP as defined by chan_sip.
- *
- * Audio Destination Port:
- *
- * 1. Provided by the RTP engine.
- */
- ast_sockaddr_copy(dest,
- !ast_sockaddr_isnull(&media_address) ? &media_address :
- !ast_sockaddr_is_any(addr) && !use_externip ? addr :
- &p->ourip);
- ast_sockaddr_set_port(dest, ast_sockaddr_port(addr));
- }
- if (needvideo) {
- /* Determine video destination */
- if (!ast_sockaddr_isnull(&p->vredirip)) {
- ast_sockaddr_copy(vdest, &p->vredirip);
- } else {
- /*
- * Video Destination IP:
- *
- * 1. Specifically configured media address.
- * 2. Local address as specified by the RTP engine.
- * 3. The local IP as defined by chan_sip.
- *
- * Video Destination Port:
- *
- * 1. Provided by the RTP engine.
- */
- ast_sockaddr_copy(vdest,
- !ast_sockaddr_isnull(&media_address) ? &media_address :
- !ast_sockaddr_is_any(vaddr) && !use_externip ? vaddr :
- &p->ourip);
- ast_sockaddr_set_port(vdest, ast_sockaddr_port(vaddr));
- }
- }
- if (needtext) {
- /* Determine text destination */
- if (!ast_sockaddr_isnull(&p->tredirip)) {
- ast_sockaddr_copy(tdest, &p->tredirip);
- } else {
- /*
- * Text Destination IP:
- *
- * 1. Specifically configured media address.
- * 2. Local address as specified by the RTP engine.
- * 3. The local IP as defined by chan_sip.
- *
- * Text Destination Port:
- *
- * 1. Provided by the RTP engine.
- */
- ast_sockaddr_copy(tdest,
- !ast_sockaddr_isnull(&media_address) ? &media_address :
- !ast_sockaddr_is_any(taddr) && !use_externip ? taddr :
- &p->ourip);
- ast_sockaddr_set_port(tdest, ast_sockaddr_port(taddr));
- }
- }
- }
- static char *crypto_get_attrib(struct ast_sdp_srtp *srtp, int dtls_enabled, int default_taglen_32)
- {
- char *a_crypto;
- const char *orig_crypto;
- if (!srtp || dtls_enabled) {
- return NULL;
- }
- orig_crypto = ast_sdp_srtp_get_attrib(srtp, dtls_enabled, default_taglen_32);
- if (ast_strlen_zero(orig_crypto)) {
- return NULL;
- }
- if (ast_asprintf(&a_crypto, "a=crypto:%s\r\n", orig_crypto) == -1) {
- return NULL;
- }
- return a_crypto;
- }
- /*! \brief Add Session Description Protocol message
- If oldsdp is TRUE, then the SDP version number is not incremented. This mechanism
- is used in Session-Timers where RE-INVITEs are used for refreshing SIP sessions
- without modifying the media session in any way.
- */
- static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp, int add_audio, int add_t38)
- {
- struct ast_format_cap *alreadysent = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- struct ast_format_cap *tmpcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- int res = AST_SUCCESS;
- int doing_directmedia = FALSE;
- struct ast_sockaddr addr = { {0,} };
- struct ast_sockaddr vaddr = { {0,} };
- struct ast_sockaddr taddr = { {0,} };
- struct ast_sockaddr udptladdr = { {0,} };
- struct ast_sockaddr dest = { {0,} };
- struct ast_sockaddr vdest = { {0,} };
- struct ast_sockaddr tdest = { {0,} };
- struct ast_sockaddr udptldest = { {0,} };
- /* SDP fields */
- struct offered_media *offer;
- char *version = "v=0\r\n"; /* Protocol version */
- char subject[256]; /* Subject of the session */
- char owner[256]; /* Session owner/creator */
- char connection[256]; /* Connection data */
- char *session_time = "t=0 0\r\n"; /* Time the session is active */
- char bandwidth[256] = ""; /* Max bitrate */
- char *hold = "";
- struct ast_str *m_audio = ast_str_alloca(256); /* Media declaration line for audio */
- struct ast_str *m_video = ast_str_alloca(256); /* Media declaration line for video */
- struct ast_str *m_text = ast_str_alloca(256); /* Media declaration line for text */
- struct ast_str *m_modem = ast_str_alloca(256); /* Media declaration line for modem */
- struct ast_str *a_audio = ast_str_create(256); /* Attributes for audio */
- struct ast_str *a_video = ast_str_create(256); /* Attributes for video */
- struct ast_str *a_text = ast_str_create(256); /* Attributes for text */
- struct ast_str *a_modem = ast_str_alloca(1024); /* Attributes for modem */
- RAII_VAR(char *, a_crypto, NULL, ast_free);
- RAII_VAR(char *, v_a_crypto, NULL, ast_free);
- RAII_VAR(char *, t_a_crypto, NULL, ast_free);
- int x;
- struct ast_format *tmp_fmt;
- int needaudio = FALSE;
- int needvideo = FALSE;
- int needtext = FALSE;
- int debug = sip_debug_test_pvt(p);
- int min_audio_packet_size = 0;
- int max_audio_packet_size = 0;
- int min_video_packet_size = 0;
- int min_text_packet_size = 0;
- struct ast_str *codec_buf = ast_str_alloca(64);
- /* Set the SDP session name */
- snprintf(subject, sizeof(subject), "s=%s\r\n", ast_strlen_zero(global_sdpsession) ? "-" : global_sdpsession);
- if (!alreadysent || !tmpcap) {
- res = AST_FAILURE;
- goto add_sdp_cleanup;
- }
- if (!p->rtp) {
- ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n");
- res = AST_FAILURE;
- goto add_sdp_cleanup;
- }
- /* XXX We should not change properties in the SIP dialog until
- we have acceptance of the offer if this is a re-invite */
- /* Set RTP Session ID and version */
- if (!p->sessionid) {
- p->sessionid = (int)ast_random();
- p->sessionversion = p->sessionid;
- } else {
- if (oldsdp == FALSE)
- p->sessionversion++;
- }
- if (add_audio) {
- doing_directmedia = (!ast_sockaddr_isnull(&p->redirip) && (ast_format_cap_count(p->redircaps))) ? TRUE : FALSE;
- if (doing_directmedia) {
- ast_format_cap_get_compatible(p->jointcaps, p->redircaps, tmpcap);
- ast_debug(1, "** Our native-bridge filtered capablity: %s\n", ast_format_cap_get_names(tmpcap, &codec_buf));
- } else {
- ast_format_cap_append_from_cap(tmpcap, p->jointcaps, AST_MEDIA_TYPE_UNKNOWN);
- }
- /* Check if we need audio */
- if (ast_format_cap_has_type(tmpcap, AST_MEDIA_TYPE_AUDIO)
- || ast_format_cap_has_type(p->caps, AST_MEDIA_TYPE_AUDIO)) {
- needaudio = TRUE;
- }
- /* Check if we need video in this call */
- if ((ast_format_cap_has_type(tmpcap, AST_MEDIA_TYPE_VIDEO)) && !p->novideo) {
- if (doing_directmedia && !ast_format_cap_has_type(tmpcap, AST_MEDIA_TYPE_VIDEO)) {
- ast_debug(2, "This call needs video offers, but caller probably did not offer it!\n");
- } else if (p->vrtp) {
- needvideo = TRUE;
- ast_debug(2, "This call needs video offers!\n");
- } else {
- ast_debug(2, "This call needs video offers, but there's no video support enabled!\n");
- }
- }
- /* Check if we need text in this call */
- if ((ast_format_cap_has_type(p->jointcaps, AST_MEDIA_TYPE_TEXT)) && !p->notext) {
- if (sipdebug_text)
- ast_verbose("We think we can do text\n");
- if (p->trtp) {
- if (sipdebug_text) {
- ast_verbose("And we have a text rtp object\n");
- }
- needtext = TRUE;
- ast_debug(2, "This call needs text offers! \n");
- } else {
- ast_debug(2, "This call needs text offers, but there's no text support enabled ! \n");
- }
- }
- /* XXX note, Video and Text are negated - 'true' means 'no' */
- ast_debug(1, "** Our capability: %s Video flag: %s Text flag: %s\n",
- ast_format_cap_get_names(tmpcap, &codec_buf),
- p->novideo ? "True" : "False", p->notext ? "True" : "False");
- ast_debug(1, "** Our prefcodec: %s \n", ast_format_cap_get_names(p->prefcaps, &codec_buf));
- }
- get_our_media_address(p, needvideo, needtext, &addr, &vaddr, &taddr, &dest, &vdest, &tdest);
- snprintf(owner, sizeof(owner), "o=%s %d %d IN %s %s\r\n",
- ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner,
- p->sessionid, p->sessionversion,
- (ast_sockaddr_is_ipv6(&dest) && !ast_sockaddr_is_ipv4_mapped(&dest)) ?
- "IP6" : "IP4",
- ast_sockaddr_stringify_addr_remote(&dest));
- snprintf(connection, sizeof(connection), "c=IN %s %s\r\n",
- (ast_sockaddr_is_ipv6(&dest) && !ast_sockaddr_is_ipv4_mapped(&dest)) ?
- "IP6" : "IP4",
- ast_sockaddr_stringify_addr_remote(&dest));
- if (add_audio) {
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR) {
- hold = "a=recvonly\r\n";
- doing_directmedia = FALSE;
- } else if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_INACTIVE) {
- hold = "a=inactive\r\n";
- doing_directmedia = FALSE;
- } else {
- hold = "a=sendrecv\r\n";
- }
- if (debug) {
- ast_verbose("Audio is at %s\n", ast_sockaddr_stringify_port(&addr));
- }
- /* Ok, we need video. Let's add what we need for video and set codecs.
- Video is handled differently than audio since we can not transcode. */
- if (needvideo) {
- v_a_crypto = crypto_get_attrib(p->vsrtp, p->dtls_cfg.enabled,
- ast_test_flag(&p->flags[2], SIP_PAGE3_SRTP_TAG_32));
- ast_str_append(&m_video, 0, "m=video %d %s", ast_sockaddr_port(&vdest),
- ast_sdp_get_rtp_profile(v_a_crypto ? 1 : 0, p->vrtp,
- ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF),
- ast_test_flag(&p->flags[2], SIP_PAGE3_FORCE_AVP)));
- /* Build max bitrate string */
- if (p->maxcallbitrate)
- snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate);
- if (debug) {
- ast_verbose("Video is at %s\n", ast_sockaddr_stringify(&vdest));
- }
- if (!doing_directmedia) {
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
- add_ice_to_sdp(p->vrtp, &a_video);
- }
- add_dtls_to_sdp(p->vrtp, &a_video);
- }
- }
- /* Ok, we need text. Let's add what we need for text and set codecs.
- Text is handled differently than audio since we can not transcode. */
- if (needtext) {
- if (sipdebug_text)
- ast_verbose("Lets set up the text sdp\n");
- t_a_crypto = crypto_get_attrib(p->tsrtp, p->dtls_cfg.enabled,
- ast_test_flag(&p->flags[2], SIP_PAGE3_SRTP_TAG_32));
- ast_str_append(&m_text, 0, "m=text %d %s", ast_sockaddr_port(&tdest),
- ast_sdp_get_rtp_profile(t_a_crypto ? 1 : 0, p->trtp,
- ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF),
- ast_test_flag(&p->flags[2], SIP_PAGE3_FORCE_AVP)));
- if (debug) { /* XXX should I use tdest below ? */
- ast_verbose("Text is at %s\n", ast_sockaddr_stringify(&taddr));
- }
- if (!doing_directmedia) {
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
- add_ice_to_sdp(p->trtp, &a_text);
- }
- add_dtls_to_sdp(p->trtp, &a_text);
- }
- }
- /* Start building generic SDP headers */
- /* We break with the "recommendation" and send our IP, in order that our
- peer doesn't have to ast_gethostbyname() us */
- a_crypto = crypto_get_attrib(p->srtp, p->dtls_cfg.enabled,
- ast_test_flag(&p->flags[2], SIP_PAGE3_SRTP_TAG_32));
- ast_str_append(&m_audio, 0, "m=audio %d %s", ast_sockaddr_port(&dest),
- ast_sdp_get_rtp_profile(a_crypto ? 1 : 0, p->rtp,
- ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF),
- ast_test_flag(&p->flags[2], SIP_PAGE3_FORCE_AVP)));
- /* Now, start adding audio codecs. These are added in this order:
- - First what was requested by the calling channel
- - Then our mutually shared capabilities, determined previous in tmpcap
- - Then preferences in order from sip.conf device config for this peer/user
- */
- /* Unless otherwise configured, the prefcaps is added before the peer's
- * configured codecs.
- */
- if (!ast_test_flag(&p->flags[2], SIP_PAGE3_IGNORE_PREFCAPS)) {
- for (x = 0; x < ast_format_cap_count(p->prefcaps); x++) {
- tmp_fmt = ast_format_cap_get_format(p->prefcaps, x);
- if ((ast_format_get_type(tmp_fmt) != AST_MEDIA_TYPE_AUDIO) ||
- (ast_format_cap_iscompatible_format(tmpcap, tmp_fmt) == AST_FORMAT_CMP_NOT_EQUAL)) {
- ao2_ref(tmp_fmt, -1);
- continue;
- }
- add_codec_to_sdp(p, tmp_fmt, &m_audio, &a_audio, debug, &min_audio_packet_size, &max_audio_packet_size);
- ast_format_cap_append(alreadysent, tmp_fmt, 0);
- ao2_ref(tmp_fmt, -1);
- }
- }
- /* Now send any other common codecs */
- for (x = 0; x < ast_format_cap_count(tmpcap); x++) {
- tmp_fmt = ast_format_cap_get_format(tmpcap, x);
- if (ast_format_cap_iscompatible_format(alreadysent, tmp_fmt) != AST_FORMAT_CMP_NOT_EQUAL) {
- ao2_ref(tmp_fmt, -1);
- continue;
- }
- if (ast_format_get_type(tmp_fmt) == AST_MEDIA_TYPE_AUDIO) {
- add_codec_to_sdp(p, tmp_fmt, &m_audio, &a_audio, debug, &min_audio_packet_size, &max_audio_packet_size);
- } else if (needvideo && ast_format_get_type(tmp_fmt) == AST_MEDIA_TYPE_VIDEO) {
- add_vcodec_to_sdp(p, tmp_fmt, &m_video, &a_video, debug, &min_video_packet_size);
- } else if (needtext && ast_format_get_type(tmp_fmt) == AST_MEDIA_TYPE_TEXT) {
- add_tcodec_to_sdp(p, tmp_fmt, &m_text, &a_text, debug, &min_text_packet_size);
- }
- ast_format_cap_append(alreadysent, tmp_fmt, 0);
- ao2_ref(tmp_fmt, -1);
- }
- /* Finally our remaining audio/video codecs */
- for (x = 0; x < ast_format_cap_count(p->caps); x++) {
- tmp_fmt = ast_format_cap_get_format(p->caps, x);
- if (ast_format_cap_iscompatible_format(alreadysent, tmp_fmt) != AST_FORMAT_CMP_NOT_EQUAL) {
- ao2_ref(tmp_fmt, -1);
- continue;
- }
- if (ast_format_get_type(tmp_fmt) == AST_MEDIA_TYPE_AUDIO) {
- add_codec_to_sdp(p, tmp_fmt, &m_audio, &a_audio, debug, &min_audio_packet_size, &max_audio_packet_size);
- } else if (needvideo && ast_format_get_type(tmp_fmt) == AST_MEDIA_TYPE_VIDEO) {
- add_vcodec_to_sdp(p, tmp_fmt, &m_video, &a_video, debug, &min_video_packet_size);
- } else if (needtext && ast_format_get_type(tmp_fmt) == AST_MEDIA_TYPE_TEXT) {
- add_tcodec_to_sdp(p, tmp_fmt, &m_text, &a_text, debug, &min_text_packet_size);
- }
- ast_format_cap_append(alreadysent, tmp_fmt, 0);
- ao2_ref(tmp_fmt, -1);
- }
- /* Now add DTMF RFC2833 telephony-event as a codec */
- for (x = 1LL; x <= AST_RTP_MAX; x <<= 1) {
- if (!(p->jointnoncodeccapability & x))
- continue;
- add_noncodec_to_sdp(p, x, &m_audio, &a_audio, debug);
- }
- ast_debug(3, "-- Done with adding codecs to SDP\n");
- if (!p->owner || ast_channel_timingfd(p->owner) == -1) {
- ast_str_append(&a_audio, 0, "a=silenceSupp:off - - - -\r\n");
- }
- if (min_audio_packet_size) {
- ast_str_append(&a_audio, 0, "a=ptime:%d\r\n", min_audio_packet_size);
- }
- /* XXX don't think you can have ptime for video */
- if (min_video_packet_size) {
- ast_str_append(&a_video, 0, "a=ptime:%d\r\n", min_video_packet_size);
- }
- /* XXX don't think you can have ptime for text */
- if (min_text_packet_size) {
- ast_str_append(&a_text, 0, "a=ptime:%d\r\n", min_text_packet_size);
- }
- if (max_audio_packet_size) {
- ast_str_append(&a_audio, 0, "a=maxptime:%d\r\n", max_audio_packet_size);
- }
- if (!doing_directmedia) {
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
- add_ice_to_sdp(p->rtp, &a_audio);
- }
- add_dtls_to_sdp(p->rtp, &a_audio);
- }
- }
- if (add_t38) {
- /* Our T.38 end is */
- ast_udptl_get_us(p->udptl, &udptladdr);
- /* We don't use directmedia for T.38, so keep the destination the same as our IP address. */
- ast_sockaddr_copy(&udptldest, &p->ourip);
- ast_sockaddr_set_port(&udptldest, ast_sockaddr_port(&udptladdr));
- if (debug) {
- ast_debug(1, "T.38 UDPTL is at %s port %d\n", ast_sockaddr_stringify_addr(&p->ourip), ast_sockaddr_port(&udptladdr));
- }
- /* We break with the "recommendation" and send our IP, in order that our
- peer doesn't have to ast_gethostbyname() us */
- ast_str_append(&m_modem, 0, "m=image %d udptl t38\r\n", ast_sockaddr_port(&udptldest));
- if (ast_sockaddr_cmp(&udptldest, &dest)) {
- ast_str_append(&m_modem, 0, "c=IN %s %s\r\n",
- (ast_sockaddr_is_ipv6(&udptldest) && !ast_sockaddr_is_ipv4_mapped(&udptldest)) ?
- "IP6" : "IP4", ast_sockaddr_stringify_addr_remote(&udptldest));
- }
- ast_str_append(&a_modem, 0, "a=T38FaxVersion:%u\r\n", p->t38.our_parms.version);
- ast_str_append(&a_modem, 0, "a=T38MaxBitRate:%u\r\n", t38_get_rate(p->t38.our_parms.rate));
- if (p->t38.our_parms.fill_bit_removal) {
- ast_str_append(&a_modem, 0, "a=T38FaxFillBitRemoval\r\n");
- }
- if (p->t38.our_parms.transcoding_mmr) {
- ast_str_append(&a_modem, 0, "a=T38FaxTranscodingMMR\r\n");
- }
- if (p->t38.our_parms.transcoding_jbig) {
- ast_str_append(&a_modem, 0, "a=T38FaxTranscodingJBIG\r\n");
- }
- switch (p->t38.our_parms.rate_management) {
- case AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF:
- ast_str_append(&a_modem, 0, "a=T38FaxRateManagement:transferredTCF\r\n");
- break;
- case AST_T38_RATE_MANAGEMENT_LOCAL_TCF:
- ast_str_append(&a_modem, 0, "a=T38FaxRateManagement:localTCF\r\n");
- break;
- }
- ast_str_append(&a_modem, 0, "a=T38FaxMaxDatagram:%u\r\n", ast_udptl_get_local_max_datagram(p->udptl));
- switch (ast_udptl_get_error_correction_scheme(p->udptl)) {
- case UDPTL_ERROR_CORRECTION_NONE:
- break;
- case UDPTL_ERROR_CORRECTION_FEC:
- ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:t38UDPFEC\r\n");
- break;
- case UDPTL_ERROR_CORRECTION_REDUNDANCY:
- ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:t38UDPRedundancy\r\n");
- break;
- }
- }
- if (needaudio)
- ast_str_append(&m_audio, 0, "\r\n");
- if (needvideo)
- ast_str_append(&m_video, 0, "\r\n");
- if (needtext)
- ast_str_append(&m_text, 0, "\r\n");
- add_header(resp, "Content-Type", "application/sdp");
- add_content(resp, version);
- add_content(resp, owner);
- add_content(resp, subject);
- add_content(resp, connection);
- /* only if video response is appropriate */
- if (needvideo) {
- add_content(resp, bandwidth);
- }
- add_content(resp, session_time);
- /* if this is a response to an invite, order our offers properly */
- if (!AST_LIST_EMPTY(&p->offered_media)) {
- AST_LIST_TRAVERSE(&p->offered_media, offer, next) {
- switch (offer->type) {
- case SDP_AUDIO:
- if (needaudio) {
- add_content(resp, ast_str_buffer(m_audio));
- add_content(resp, ast_str_buffer(a_audio));
- add_content(resp, hold);
- if (a_crypto) {
- add_content(resp, a_crypto);
- }
- } else {
- add_content(resp, offer->decline_m_line);
- }
- break;
- case SDP_VIDEO:
- if (needvideo) { /* only if video response is appropriate */
- add_content(resp, ast_str_buffer(m_video));
- add_content(resp, ast_str_buffer(a_video));
- add_content(resp, hold); /* Repeat hold for the video stream */
- if (v_a_crypto) {
- add_content(resp, v_a_crypto);
- }
- } else {
- add_content(resp, offer->decline_m_line);
- }
- break;
- case SDP_TEXT:
- if (needtext) { /* only if text response is appropriate */
- add_content(resp, ast_str_buffer(m_text));
- add_content(resp, ast_str_buffer(a_text));
- add_content(resp, hold); /* Repeat hold for the text stream */
- if (t_a_crypto) {
- add_content(resp, t_a_crypto);
- }
- } else {
- add_content(resp, offer->decline_m_line);
- }
- break;
- case SDP_IMAGE:
- if (add_t38) {
- add_content(resp, ast_str_buffer(m_modem));
- add_content(resp, ast_str_buffer(a_modem));
- } else {
- add_content(resp, offer->decline_m_line);
- }
- break;
- case SDP_UNKNOWN:
- add_content(resp, offer->decline_m_line);
- break;
- }
- }
- } else {
- /* generate new SDP from scratch, no offers */
- if (needaudio) {
- add_content(resp, ast_str_buffer(m_audio));
- add_content(resp, ast_str_buffer(a_audio));
- add_content(resp, hold);
- if (a_crypto) {
- add_content(resp, a_crypto);
- }
- }
- if (needvideo) { /* only if video response is appropriate */
- add_content(resp, ast_str_buffer(m_video));
- add_content(resp, ast_str_buffer(a_video));
- add_content(resp, hold); /* Repeat hold for the video stream */
- if (v_a_crypto) {
- add_content(resp, v_a_crypto);
- }
- }
- if (needtext) { /* only if text response is appropriate */
- add_content(resp, ast_str_buffer(m_text));
- add_content(resp, ast_str_buffer(a_text));
- add_content(resp, hold); /* Repeat hold for the text stream */
- if (t_a_crypto) {
- add_content(resp, t_a_crypto);
- }
- }
- if (add_t38) {
- add_content(resp, ast_str_buffer(m_modem));
- add_content(resp, ast_str_buffer(a_modem));
- }
- }
- /* Update lastrtprx when we send our SDP */
- p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
- /*
- * We unlink this dialog and link again into the
- * dialogs_rtpcheck container so its not in there twice.
- */
- ao2_lock(dialogs_rtpcheck);
- ao2_t_unlink(dialogs_rtpcheck, p, "unlink pvt into dialogs_rtpcheck container");
- ao2_t_link(dialogs_rtpcheck, p, "link pvt into dialogs_rtpcheck container");
- ao2_unlock(dialogs_rtpcheck);
- ast_debug(3, "Done building SDP. Settling with this capability: %s\n",
- ast_format_cap_get_names(tmpcap, &codec_buf));
- add_sdp_cleanup:
- ast_free(a_text);
- ast_free(a_video);
- ast_free(a_audio);
- ao2_cleanup(alreadysent);
- ao2_cleanup(tmpcap);
- return res;
- }
- /*! \brief Used for 200 OK and 183 early media */
- static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans)
- {
- struct sip_request resp;
- uint32_t seqno;
- if (sscanf(sip_get_header(req, "CSeq"), "%30u ", &seqno) != 1) {
- ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", sip_get_header(req, "CSeq"));
- return -1;
- }
- respprep(&resp, p, msg, req);
- if (p->udptl) {
- add_sdp(&resp, p, 0, 0, 1);
- } else
- ast_log(LOG_ERROR, "Can't add SDP to response, since we have no UDPTL session allocated. Call-ID %s\n", p->callid);
- if (retrans && !p->pendinginvite)
- p->pendinginvite = seqno; /* Buggy clients sends ACK on RINGING too */
- return send_response(p, &resp, retrans, seqno);
- }
- /*! \brief copy SIP request (mostly used to save request for responses) */
- static void copy_request(struct sip_request *dst, const struct sip_request *src)
- {
- /* XXX this function can encounter memory allocation errors, perhaps it
- * should return a value */
- struct ast_str *duplicate = dst->data;
- struct ast_str *duplicate_content = dst->content;
- /* copy the entire request then restore the original data and content
- * members from the dst request */
- *dst = *src;
- dst->data = duplicate;
- dst->content = duplicate_content;
- /* copy the data into the dst request */
- if (!dst->data && !(dst->data = ast_str_create(ast_str_strlen(src->data) + 1))) {
- return;
- }
- ast_str_copy_string(&dst->data, src->data);
- /* copy the content into the dst request (if it exists) */
- if (src->content) {
- if (!dst->content && !(dst->content = ast_str_create(ast_str_strlen(src->content) + 1))) {
- return;
- }
- ast_str_copy_string(&dst->content, src->content);
- }
- }
- static void add_cc_call_info_to_response(struct sip_pvt *p, struct sip_request *resp)
- {
- char uri[SIPBUFSIZE];
- struct ast_str *header = ast_str_alloca(SIPBUFSIZE);
- struct ast_cc_agent *agent = find_sip_cc_agent_by_original_callid(p);
- struct sip_cc_agent_pvt *agent_pvt;
- if (!agent) {
- /* Um, what? How could the SIP_OFFER_CC flag be set but there not be an
- * agent? Oh well, we'll just warn and return without adding the header.
- */
- ast_log(LOG_WARNING, "Can't find SIP CC agent for call '%s' even though OFFER_CC flag was set?\n", p->callid);
- return;
- }
- agent_pvt = agent->private_data;
- if (!ast_strlen_zero(agent_pvt->subscribe_uri)) {
- ast_copy_string(uri, agent_pvt->subscribe_uri, sizeof(uri));
- } else {
- generate_uri(p, uri, sizeof(uri));
- ast_copy_string(agent_pvt->subscribe_uri, uri, sizeof(agent_pvt->subscribe_uri));
- }
- /* XXX Hardcode "NR" as the m reason for now. This should perhaps be changed
- * to be more accurate. This parameter has no bearing on the actual operation
- * of the feature; it's just there for informational purposes.
- */
- ast_str_set(&header, 0, "<%s>;purpose=call-completion;m=%s", uri, "NR");
- add_header(resp, "Call-Info", ast_str_buffer(header));
- ao2_ref(agent, -1);
- }
- /*! \brief Used for 200 OK and 183 early media
- \return Will return XMIT_ERROR for network errors.
- */
- static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp, int rpid)
- {
- struct sip_request resp;
- uint32_t seqno;
- if (sscanf(sip_get_header(req, "CSeq"), "%30u ", &seqno) != 1) {
- ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", sip_get_header(req, "CSeq"));
- return -1;
- }
- respprep(&resp, p, msg, req);
- if (rpid == TRUE) {
- add_rpid(&resp, p);
- }
- if (ast_test_flag(&p->flags[0], SIP_OFFER_CC)) {
- add_cc_call_info_to_response(p, &resp);
- }
- if (p->rtp) {
- if (!p->autoframing && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- ast_debug(1, "Setting framing from config on incoming call\n");
- ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(p->rtp), ast_format_cap_get_framing(p->caps));
- }
- ast_rtp_instance_activate(p->rtp);
- try_suggested_sip_codec(p);
- if (p->t38.state == T38_ENABLED) {
- add_sdp(&resp, p, oldsdp, TRUE, TRUE);
- } else {
- add_sdp(&resp, p, oldsdp, TRUE, FALSE);
- }
- } else
- ast_log(LOG_ERROR, "Can't add SDP to response, since we have no RTP session allocated. Call-ID %s\n", p->callid);
- if (reliable && !p->pendinginvite)
- p->pendinginvite = seqno; /* Buggy clients sends ACK on RINGING too */
- add_required_respheader(&resp);
- return send_response(p, &resp, reliable, seqno);
- }
- /*! \brief Parse first line of incoming SIP request */
- static int determine_firstline_parts(struct sip_request *req)
- {
- char *e = ast_skip_blanks(ast_str_buffer(req->data)); /* there shouldn't be any */
- char *local_rlpart1;
- if (!*e)
- return -1;
- req->rlpart1 = e - ast_str_buffer(req->data); /* method or protocol */
- local_rlpart1 = e;
- e = ast_skip_nonblanks(e);
- if (*e)
- *e++ = '\0';
- /* Get URI or status code */
- e = ast_skip_blanks(e);
- if ( !*e )
- return -1;
- ast_trim_blanks(e);
- if (!strcasecmp(local_rlpart1, "SIP/2.0") ) { /* We have a response */
- if (strlen(e) < 3) /* status code is 3 digits */
- return -1;
- req->rlpart2 = e - ast_str_buffer(req->data);
- } else { /* We have a request */
- if ( *e == '<' ) { /* XXX the spec says it must not be in <> ! */
- ast_debug(3, "Oops. Bogus uri in <> %s\n", e);
- e++;
- if (!*e)
- return -1;
- }
- req->rlpart2 = e - ast_str_buffer(req->data); /* URI */
- e = ast_skip_nonblanks(e);
- if (*e)
- *e++ = '\0';
- e = ast_skip_blanks(e);
- if (strcasecmp(e, "SIP/2.0") ) {
- ast_debug(3, "Skipping packet - Bad request protocol %s\n", e);
- return -1;
- }
- }
- return 1;
- }
- /*! \brief Transmit reinvite with SDP
- \note A re-invite is basically a new INVITE with the same CALL-ID and TAG as the
- INVITE that opened the SIP dialogue
- We reinvite so that the audio stream (RTP) go directly between
- the SIP UAs. SIP Signalling stays with * in the path.
- If t38version is TRUE, we send T38 SDP for re-invite from audio/video to
- T38 UDPTL transmission on the channel
- If oldsdp is TRUE then the SDP version number is not incremented. This
- is needed for Session-Timers so we can send a re-invite to refresh the
- SIP session without modifying the media session.
- */
- static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int oldsdp)
- {
- struct sip_request req;
- reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1);
- add_header(&req, "Allow", ALLOWED_METHODS);
- add_supported(p, &req);
- if (sipdebug) {
- if (oldsdp == TRUE)
- add_header(&req, "X-asterisk-Info", "SIP re-invite (Session-Timers)");
- else
- add_header(&req, "X-asterisk-Info", "SIP re-invite (External RTP bridge)");
- }
- if (ast_test_flag(&p->flags[0], SIP_SENDRPID))
- add_rpid(&req, p);
- if (p->do_history) {
- append_history(p, "ReInv", "Re-invite sent");
- }
- offered_media_list_destroy(p);
- try_suggested_sip_codec(p);
- if (t38version) {
- add_sdp(&req, p, oldsdp, FALSE, TRUE);
- } else {
- add_sdp(&req, p, oldsdp, TRUE, FALSE);
- }
- /* Use this as the basis */
- initialize_initreq(p, &req);
- p->lastinvite = p->ocseq;
- ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Change direction of this dialog */
- p->ongoing_reinvite = 1;
- return send_request(p, &req, XMIT_CRITICAL, p->ocseq);
- }
- /* \brief Remove URI parameters at end of URI, not in username part though */
- static char *remove_uri_parameters(char *uri)
- {
- char *atsign;
- atsign = strchr(uri, '@'); /* First, locate the at sign */
- if (!atsign) {
- atsign = uri; /* Ok hostname only, let's stick with the rest */
- }
- atsign = strchr(atsign, ';'); /* Locate semi colon */
- if (atsign)
- *atsign = '\0'; /* Kill at the semi colon */
- return uri;
- }
- /*! \brief Check Contact: URI of SIP message */
- static void extract_uri(struct sip_pvt *p, struct sip_request *req)
- {
- char stripped[SIPBUFSIZE];
- char *c;
- ast_copy_string(stripped, sip_get_header(req, "Contact"), sizeof(stripped));
- c = get_in_brackets(stripped);
- /* Cut the URI at the at sign after the @, not in the username part */
- c = remove_uri_parameters(c);
- if (!ast_strlen_zero(c)) {
- ast_string_field_set(p, uri, c);
- }
- }
- /*!
- * \brief Determine if, as a UAS, we need to use a SIPS Contact.
- *
- * This uses the rules defined in RFC 3261 section 12.1.1 to
- * determine if a SIPS URI should be used as the Contact header
- * when responding to incoming SIP requests.
- *
- * \param req The incoming SIP request
- * \retval 0 SIPS is not required
- * \retval 1 SIPS is required
- */
- static int uas_sips_contact(struct sip_request *req)
- {
- const char *record_route = sip_get_header(req, "Record-Route");
- if (!strncmp(REQ_OFFSET_TO_STR(req, rlpart2), "sips:", 5)) {
- return 1;
- }
- if (record_route) {
- char *record_route_uri = get_in_brackets(ast_strdupa(record_route));
- if (!strncmp(record_route_uri, "sips:", 5)) {
- return 1;
- }
- } else {
- const char *contact = sip_get_header(req, "Contact");
- char *contact_uri = get_in_brackets(ast_strdupa(contact));
- if (!strncmp(contact_uri, "sips:", 5)) {
- return 1;
- }
- }
- return 0;
- }
- /*!
- * \brief Determine if, as a UAC, we need to use a SIPS Contact.
- *
- * This uses the rules defined in RFC 3621 section 8.1.1.8 to
- * determine if a SIPS URI should be used as the Contact header
- * on our outgoing request.
- *
- * \param req The outgoing SIP request
- * \retval 0 SIPS is not required
- * \retval 1 SIPS is required
- */
- static int uac_sips_contact(struct sip_request *req)
- {
- const char *route = sip_get_header(req, "Route");
- if (!strncmp(REQ_OFFSET_TO_STR(req, rlpart2), "sips:", 5)) {
- return 1;
- }
- if (route) {
- char *route_uri = get_in_brackets(ast_strdupa(route));
- if (!strncmp(route_uri, "sips:", 5)) {
- return 1;
- }
- }
- return 0;
- }
- /*!
- * \brief Build contact header
- *
- * This is the Contact header that we send out in SIP requests and responses
- * involving this sip_pvt.
- *
- * The incoming parameter is used to tell if we are building the request parameter
- * is an incoming SIP request that we are building the Contact header in response to,
- * or if the req parameter is an outbound SIP request that we will later be adding
- * the Contact header to.
- *
- * \param p The sip_pvt where the built Contact will be saved.
- * \param req The request that triggered the creation of a Contact header.
- * \param incoming Indicates if the Contact header is being created for a response to an incoming request
- */
- static void build_contact(struct sip_pvt *p, struct sip_request *req, int incoming)
- {
- char tmp[SIPBUFSIZE];
- char *user = ast_uri_encode(p->exten, tmp, sizeof(tmp), ast_uri_sip_user);
- int use_sips;
- if (incoming) {
- use_sips = uas_sips_contact(req);
- } else {
- use_sips = uac_sips_contact(req);
- }
- if (p->socket.type == AST_TRANSPORT_UDP) {
- ast_string_field_build(p, our_contact, "<%s:%s%s%s>", use_sips ? "sips" : "sip",
- user, ast_strlen_zero(user) ? "" : "@",
- ast_sockaddr_stringify_remote(&p->ourip));
- } else {
- ast_string_field_build(p, our_contact, "<%s:%s%s%s;transport=%s>",
- use_sips ? "sips" : "sip", user, ast_strlen_zero(user) ? "" : "@",
- ast_sockaddr_stringify_remote(&p->ourip), sip_get_transport(p->socket.type));
- }
- }
- /*! \brief Initiate new SIP request to peer/user */
- static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, const char * const explicit_uri)
- {
- struct ast_str *invite = ast_str_alloca(256);
- char from[256];
- char to[256];
- char tmp_n[SIPBUFSIZE/2]; /* build a local copy of 'n' if needed */
- char tmp_l[SIPBUFSIZE/2]; /* build a local copy of 'l' if needed */
- const char *l = NULL; /* XXX what is this, exactly ? */
- const char *n = NULL; /* XXX what is this, exactly ? */
- const char *d = NULL; /* domain in from header */
- const char *urioptions = "";
- int ourport;
- int cid_has_name = 1;
- int cid_has_num = 1;
- struct ast_party_id connected_id;
- if (ast_test_flag(&p->flags[0], SIP_USEREQPHONE)) {
- const char *s = p->username; /* being a string field, cannot be NULL */
- /* Test p->username against allowed characters in AST_DIGIT_ANY
- If it matches the allowed characters list, then sipuser = ";user=phone"
- If not, then sipuser = ""
- */
- /* + is allowed in first position in a tel: uri */
- if (*s == '+')
- s++;
- for (; *s; s++) {
- if (!strchr(AST_DIGIT_ANYNUM, *s) )
- break;
- }
- /* If we have only digits, add ;user=phone to the uri */
- if (!*s)
- urioptions = ";user=phone";
- }
- snprintf(p->lastmsg, sizeof(p->lastmsg), "Init: %s", sip_methods[sipmethod].text);
- if (ast_strlen_zero(p->fromdomain)) {
- d = ast_sockaddr_stringify_host_remote(&p->ourip);
- }
- if (p->owner) {
- connected_id = ast_channel_connected_effective_id(p->owner);
- if ((ast_party_id_presentation(&connected_id) & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
- if (connected_id.number.valid) {
- l = connected_id.number.str;
- }
- if (connected_id.name.valid) {
- n = connected_id.name.str;
- }
- } else {
- /* Even if we are using RPID, we shouldn't leak information in the From if the user wants
- * their callerid restricted */
- l = "anonymous";
- n = CALLERID_UNKNOWN;
- d = FROMDOMAIN_INVALID;
- }
- }
- /* Hey, it's a NOTIFY! See if they've configured a mwi_from.
- * XXX Right now, this logic works because the only place that mwi_from
- * is set on the sip_pvt is in sip_send_mwi_to_peer. If things changed, then
- * we might end up putting the mwi_from setting into other types of NOTIFY
- * messages as well.
- */
- if (sipmethod == SIP_NOTIFY && !ast_strlen_zero(p->mwi_from)) {
- l = p->mwi_from;
- }
- if (ast_strlen_zero(l)) {
- cid_has_num = 0;
- l = default_callerid;
- }
- if (ast_strlen_zero(n)) {
- cid_has_name = 0;
- n = l;
- }
- /* Allow user to be overridden */
- if (!ast_strlen_zero(p->fromuser))
- l = p->fromuser;
- else /* Save for any further attempts */
- ast_string_field_set(p, fromuser, l);
- /* Allow user to be overridden */
- if (!ast_strlen_zero(p->fromname))
- n = p->fromname;
- else /* Save for any further attempts */
- ast_string_field_set(p, fromname, n);
- /* Allow domain to be overridden */
- if (!ast_strlen_zero(p->fromdomain))
- d = p->fromdomain;
- else /* Save for any further attempts */
- ast_string_field_set(p, fromdomain, d);
- ast_copy_string(tmp_l, l, sizeof(tmp_l));
- if (sip_cfg.pedanticsipchecking) {
- ast_escape_quoted(n, tmp_n, sizeof(tmp_n));
- n = tmp_n;
- ast_uri_encode(l, tmp_l, sizeof(tmp_l), ast_uri_sip_user);
- }
- ourport = (p->fromdomainport && (p->fromdomainport != STANDARD_SIP_PORT)) ? p->fromdomainport : ast_sockaddr_port(&p->ourip);
- /* If a caller id name was specified, add a display name. */
- if (cid_has_name || !cid_has_num) {
- snprintf(from, sizeof(from), "\"%s\" ", n);
- } else {
- from[0] = '\0';
- }
- if (!sip_standard_port(p->socket.type, ourport)) {
- size_t offset = strlen(from);
- snprintf(&from[offset], sizeof(from) - offset, "<sip:%s@%s:%d>;tag=%s", tmp_l, d, ourport, p->tag);
- } else {
- size_t offset = strlen(from);
- snprintf(&from[offset], sizeof(from) - offset, "<sip:%s@%s>;tag=%s", tmp_l, d, p->tag);
- }
- if (!ast_strlen_zero(explicit_uri)) {
- ast_str_set(&invite, 0, "%s", explicit_uri);
- } else {
- /* If we're calling a registered SIP peer, use the fullcontact to dial to the peer */
- if (!ast_strlen_zero(p->fullcontact)) {
- /* If we have full contact, trust it */
- ast_str_append(&invite, 0, "%s", p->fullcontact);
- } else {
- /* Otherwise, use the username while waiting for registration */
- ast_str_append(&invite, 0, "sip:");
- if (!ast_strlen_zero(p->username)) {
- n = p->username;
- if (sip_cfg.pedanticsipchecking) {
- ast_uri_encode(n, tmp_n, sizeof(tmp_n), ast_uri_sip_user);
- n = tmp_n;
- }
- ast_str_append(&invite, 0, "%s@", n);
- }
- ast_str_append(&invite, 0, "%s", p->tohost);
- if (p->portinuri) {
- ast_str_append(&invite, 0, ":%d", ast_sockaddr_port(&p->sa));
- }
- ast_str_append(&invite, 0, "%s", urioptions);
- }
- }
- /* If custom URI options have been provided, append them */
- if (p->options && !ast_strlen_zero(p->options->uri_options))
- ast_str_append(&invite, 0, ";%s", p->options->uri_options);
- /* This is the request URI, which is the next hop of the call
- which may or may not be the destination of the call
- */
- ast_string_field_set(p, uri, ast_str_buffer(invite));
- if (!ast_strlen_zero(p->todnid)) {
- /*! \todo Need to add back the VXML URL here at some point, possibly use build_string for all this junk */
- if (!strchr(p->todnid, '@')) {
- /* We have no domain in the dnid */
- snprintf(to, sizeof(to), "<sip:%s@%s>%s%s", p->todnid, p->tohost, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
- } else {
- snprintf(to, sizeof(to), "<sip:%s>%s%s", p->todnid, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
- }
- } else {
- if (sipmethod == SIP_NOTIFY && !ast_strlen_zero(p->theirtag)) {
- /* If this is a NOTIFY, use the From: tag in the subscribe (RFC 3265) */
- snprintf(to, sizeof(to), "<%s%s>;tag=%s", (strncasecmp(p->uri, "sip:", 4) ? "sip:" : ""), p->uri, p->theirtag);
- } else if (p->options && p->options->vxml_url) {
- /* If there is a VXML URL append it to the SIP URL */
- snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url);
- } else {
- snprintf(to, sizeof(to), "<%s>", p->uri);
- }
- }
- init_req(req, sipmethod, p->uri);
- /* now tmp_n is available so reuse it to build the CSeq */
- snprintf(tmp_n, sizeof(tmp_n), "%u %s", ++p->ocseq, sip_methods[sipmethod].text);
- add_header(req, "Via", p->via);
- add_max_forwards(p, req);
- /* This will be a no-op most of the time. However, under certain circumstances,
- * NOTIFY messages will use this function for preparing the request and should
- * have Route headers present.
- */
- add_route(req, &p->route, 0);
- add_header(req, "From", from);
- add_header(req, "To", to);
- ast_string_field_set(p, exten, l);
- build_contact(p, req, 0);
- add_header(req, "Contact", p->our_contact);
- add_header(req, "Call-ID", p->callid);
- add_header(req, "CSeq", tmp_n);
- if (!ast_strlen_zero(global_useragent)) {
- add_header(req, "User-Agent", global_useragent);
- }
- }
- /*! \brief Add "Diversion" header to outgoing message
- *
- * We need to add a Diversion header if the owner channel of
- * this dialog has redirecting information associated with it.
- *
- * \param req The request/response to which we will add the header
- * \param pvt The sip_pvt which represents the call-leg
- */
- static void add_diversion(struct sip_request *req, struct sip_pvt *pvt)
- {
- struct ast_party_id diverting_from;
- const char *reason;
- int found_in_table;
- char header_text[256];
- char encoded_number[SIPBUFSIZE/2];
- /* We skip this entirely if the configuration doesn't allow diversion headers */
- if (!sip_cfg.send_diversion) {
- return;
- }
- if (!pvt->owner) {
- return;
- }
- diverting_from = ast_channel_redirecting_effective_from(pvt->owner);
- if (!diverting_from.number.valid
- || ast_strlen_zero(diverting_from.number.str)) {
- return;
- }
- if (sip_cfg.pedanticsipchecking) {
- ast_uri_encode(diverting_from.number.str, encoded_number, sizeof(encoded_number), ast_uri_sip_user);
- } else {
- ast_copy_string(encoded_number, diverting_from.number.str, sizeof(encoded_number));
- }
- reason = sip_reason_code_to_str(&ast_channel_redirecting(pvt->owner)->reason, &found_in_table);
- /* We at least have a number to place in the Diversion header, which is enough */
- if (!diverting_from.name.valid
- || ast_strlen_zero(diverting_from.name.str)) {
- snprintf(header_text, sizeof(header_text), "<sip:%s@%s>;reason=%s%s%s",
- encoded_number,
- ast_sockaddr_stringify_host_remote(&pvt->ourip),
- found_in_table ? "" : "\"",
- reason,
- found_in_table ? "" : "\"");
- } else {
- char escaped_name[SIPBUFSIZE/2];
- if (sip_cfg.pedanticsipchecking) {
- ast_escape_quoted(diverting_from.name.str, escaped_name, sizeof(escaped_name));
- } else {
- ast_copy_string(escaped_name, diverting_from.name.str, sizeof(escaped_name));
- }
- snprintf(header_text, sizeof(header_text), "\"%s\" <sip:%s@%s>;reason=%s%s%s",
- escaped_name,
- encoded_number,
- ast_sockaddr_stringify_host_remote(&pvt->ourip),
- found_in_table ? "" : "\"",
- reason,
- found_in_table ? "" : "\"");
- }
- add_header(req, "Diversion", header_text);
- }
- static int transmit_publish(struct sip_epa_entry *epa_entry, enum sip_publish_type publish_type, const char * const explicit_uri)
- {
- struct sip_pvt *pvt;
- int expires;
- epa_entry->publish_type = publish_type;
- if (!(pvt = sip_alloc(NULL, NULL, 0, SIP_PUBLISH, NULL, 0))) {
- return -1;
- }
- sip_pvt_lock(pvt);
- if (create_addr(pvt, epa_entry->destination, NULL, TRUE)) {
- sip_pvt_unlock(pvt);
- dialog_unlink_all(pvt);
- dialog_unref(pvt, "create_addr failed in transmit_publish. Unref dialog");
- return -1;
- }
- ast_sip_ouraddrfor(&pvt->sa, &pvt->ourip, pvt);
- ast_set_flag(&pvt->flags[0], SIP_OUTGOING);
- expires = (publish_type == SIP_PUBLISH_REMOVE) ? 0 : DEFAULT_PUBLISH_EXPIRES;
- pvt->expiry = expires;
- /* Bump refcount for sip_pvt's reference */
- ao2_ref(epa_entry, +1);
- pvt->epa_entry = epa_entry;
- transmit_invite(pvt, SIP_PUBLISH, FALSE, 2, explicit_uri);
- sip_pvt_unlock(pvt);
- sip_scheddestroy(pvt, DEFAULT_TRANS_TIMEOUT);
- dialog_unref(pvt, "Done with the sip_pvt allocated for transmitting PUBLISH");
- return 0;
- }
- /*!
- * \brief Build REFER/INVITE/OPTIONS/SUBSCRIBE message and transmit it
- * \param p sip_pvt structure
- * \param sipmethod
- * \param sdp unknown
- * \param init 0 = Prepare request within dialog, 1= prepare request, new branch,
- * 2= prepare new request and new dialog. do_proxy_auth calls this with init!=2
- * \param explicit_uri
- */
- static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init, const char * const explicit_uri)
- {
- struct sip_request req;
- struct ast_variable *var;
- if (init) {/* Bump branch even on initial requests */
- p->branch ^= ast_random();
- p->invite_branch = p->branch;
- build_via(p);
- }
- if (init > 1) {
- initreqprep(&req, p, sipmethod, explicit_uri);
- } else {
- /* If init=1, we should not generate a new branch. If it's 0, we need a new branch. */
- reqprep(&req, p, sipmethod, 0, init ? 0 : 1);
- }
- if (p->options && p->options->auth) {
- add_header(&req, p->options->authheader, p->options->auth);
- }
- add_date(&req);
- if (sipmethod == SIP_REFER && p->refer) { /* Call transfer */
- if (!ast_strlen_zero(p->refer->refer_to)) {
- add_header(&req, "Refer-To", p->refer->refer_to);
- }
- if (!ast_strlen_zero(p->refer->referred_by)) {
- add_header(&req, "Referred-By", p->refer->referred_by);
- }
- } else if (sipmethod == SIP_SUBSCRIBE) {
- if (p->subscribed == MWI_NOTIFICATION) {
- add_header(&req, "Event", "message-summary");
- add_header(&req, "Accept", "application/simple-message-summary");
- } else if (p->subscribed == CALL_COMPLETION) {
- add_header(&req, "Event", "call-completion");
- add_header(&req, "Accept", "application/call-completion");
- }
- add_expires(&req, p->expiry);
- }
- /* This new INVITE is part of an attended transfer. Make sure that the
- other end knows and replace the current call with this new call */
- if (p->options && !ast_strlen_zero(p->options->replaces)) {
- add_header(&req, "Replaces", p->options->replaces);
- add_header(&req, "Require", "replaces");
- }
- /* Add Session-Timers related headers */
- if (st_get_mode(p, 0) == SESSION_TIMER_MODE_ORIGINATE
- || (st_get_mode(p, 0) == SESSION_TIMER_MODE_ACCEPT
- && st_get_se(p, FALSE) != DEFAULT_MIN_SE)) {
- char i2astr[10];
- if (!p->stimer->st_interval) {
- p->stimer->st_interval = st_get_se(p, TRUE);
- }
- p->stimer->st_active = TRUE;
- if (st_get_mode(p, 0) == SESSION_TIMER_MODE_ORIGINATE) {
- snprintf(i2astr, sizeof(i2astr), "%d", p->stimer->st_interval);
- add_header(&req, "Session-Expires", i2astr);
- }
- snprintf(i2astr, sizeof(i2astr), "%d", st_get_se(p, FALSE));
- add_header(&req, "Min-SE", i2astr);
- }
- add_header(&req, "Allow", ALLOWED_METHODS);
- add_supported(p, &req);
- if (p->owner && ((p->options && p->options->addsipheaders)
- || (p->refer && global_refer_addheaders))) {
- struct ast_channel *chan = p->owner; /* The owner channel */
- struct varshead *headp;
- ast_channel_lock(chan);
- headp = ast_channel_varshead(chan);
- if (!headp) {
- ast_log(LOG_WARNING, "No Headp for the channel...ooops!\n");
- } else {
- const struct ast_var_t *current;
- AST_LIST_TRAVERSE(headp, current, entries) {
- /* SIPADDHEADER: Add SIP header to outgoing call */
- if (!strncmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) {
- char *content, *end;
- const char *header = ast_var_value(current);
- char *headdup = ast_strdupa(header);
- /* Strip of the starting " (if it's there) */
- if (*headdup == '"') {
- headdup++;
- }
- if ((content = strchr(headdup, ':'))) {
- *content++ = '\0';
- content = ast_skip_blanks(content); /* Skip white space */
- /* Strip the ending " (if it's there) */
- end = content + strlen(content) -1;
- if (*end == '"') {
- *end = '\0';
- }
- add_header(&req, headdup, content);
- if (sipdebug) {
- ast_debug(1, "Adding SIP Header \"%s\" with content :%s: \n", headdup, content);
- }
- }
- }
- }
- }
- ast_channel_unlock(chan);
- }
- if ((sipmethod == SIP_INVITE || sipmethod == SIP_UPDATE) && ast_test_flag(&p->flags[0], SIP_SENDRPID))
- add_rpid(&req, p);
- if (sipmethod == SIP_INVITE) {
- add_diversion(&req, p);
- }
- if (sdp) {
- offered_media_list_destroy(p);
- if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) {
- ast_debug(1, "T38 is in state %u on channel %s\n", p->t38.state, p->owner ? ast_channel_name(p->owner) : "<none>");
- add_sdp(&req, p, FALSE, FALSE, TRUE);
- } else if (p->rtp) {
- try_suggested_sip_codec(p);
- add_sdp(&req, p, FALSE, TRUE, FALSE);
- }
- } else if (sipmethod == SIP_NOTIFY && p->notify) {
- for (var = p->notify->headers; var; var = var->next) {
- add_header(&req, var->name, var->value);
- }
- if (ast_str_strlen(p->notify->content)) {
- add_content(&req, ast_str_buffer(p->notify->content));
- }
- } else if (sipmethod == SIP_PUBLISH) {
- switch (p->epa_entry->static_data->event) {
- case CALL_COMPLETION:
- add_header(&req, "Event", "call-completion");
- add_expires(&req, p->expiry);
- if (p->epa_entry->publish_type != SIP_PUBLISH_INITIAL) {
- add_header(&req, "SIP-If-Match", p->epa_entry->entity_tag);
- }
- if (!ast_strlen_zero(p->epa_entry->body)) {
- add_header(&req, "Content-Type", "application/pidf+xml");
- add_content(&req, p->epa_entry->body);
- }
- default:
- break;
- }
- }
- if (!p->initreq.headers || init > 2) {
- initialize_initreq(p, &req);
- }
- if (sipmethod == SIP_INVITE || sipmethod == SIP_SUBSCRIBE) {
- p->lastinvite = p->ocseq;
- }
- return send_request(p, &req, init ? XMIT_CRITICAL : XMIT_RELIABLE, p->ocseq);
- }
- /*! \brief Send a subscription or resubscription for MWI */
- static int sip_subscribe_mwi_do(const void *data)
- {
- struct sip_subscription_mwi *mwi = (struct sip_subscription_mwi*)data;
- if (!mwi) {
- return -1;
- }
- mwi->resub = -1;
- __sip_subscribe_mwi_do(mwi);
- ao2_t_ref(mwi, -1, "unref mwi to balance ast_sched_add");
- return 0;
- }
- static void on_dns_update_registry(struct ast_sockaddr *old, struct ast_sockaddr *new, void *data)
- {
- struct sip_registry *reg = data;
- const char *old_str;
- /* This shouldn't happen, but just in case */
- if (ast_sockaddr_isnull(new)) {
- ast_debug(1, "Empty sockaddr change...ignoring!\n");
- return;
- }
- if (!ast_sockaddr_port(new)) {
- ast_sockaddr_set_port(new, reg->portno);
- }
- old_str = ast_strdupa(ast_sockaddr_stringify(old));
- ast_debug(1, "Changing registry %s from %s to %s\n", S_OR(reg->peername, reg->hostname), old_str, ast_sockaddr_stringify(new));
- ast_sockaddr_copy(®->us, new);
- }
- static void on_dns_update_peer(struct ast_sockaddr *old, struct ast_sockaddr *new, void *data)
- {
- struct sip_peer *peer = data;
- const char *old_str;
- /* This shouldn't happen, but just in case */
- if (ast_sockaddr_isnull(new)) {
- ast_debug(1, "Empty sockaddr change...ignoring!\n");
- return;
- }
- if (!ast_sockaddr_isnull(&peer->addr)) {
- ao2_unlink(peers_by_ip, peer);
- }
- if (!ast_sockaddr_port(new)) {
- ast_sockaddr_set_port(new, default_sip_port(peer->socket.type));
- }
- old_str = ast_strdupa(ast_sockaddr_stringify(old));
- ast_debug(1, "Changing peer %s address from %s to %s\n", peer->name, old_str, ast_sockaddr_stringify(new));
- ao2_lock(peer);
- ast_sockaddr_copy(&peer->addr, new);
- ao2_unlock(peer);
- ao2_link(peers_by_ip, peer);
- }
- static void on_dns_update_mwi(struct ast_sockaddr *old, struct ast_sockaddr *new, void *data)
- {
- struct sip_subscription_mwi *mwi = data;
- const char *old_str;
- /* This shouldn't happen, but just in case */
- if (ast_sockaddr_isnull(new)) {
- ast_debug(1, "Empty sockaddr change...ignoring!\n");
- return;
- }
- old_str = ast_strdupa(ast_sockaddr_stringify(old));
- ast_debug(1, "Changing mwi %s from %s to %s\n", mwi->hostname, old_str, ast_sockaddr_stringify(new));
- ast_sockaddr_copy(&mwi->us, new);
- }
- /*! \brief Actually setup an MWI subscription or resubscribe */
- static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi)
- {
- /* If we have no DNS manager let's do a lookup */
- if (!mwi->dnsmgr) {
- char transport[MAXHOSTNAMELEN];
- snprintf(transport, sizeof(transport), "_%s._%s", get_srv_service(mwi->transport), get_srv_protocol(mwi->transport));
- mwi->us.ss.ss_family = get_address_family_filter(mwi->transport); /* Filter address family */
- ao2_t_ref(mwi, +1, "dnsmgr reference to mwi");
- ast_dnsmgr_lookup_cb(mwi->hostname, &mwi->us, &mwi->dnsmgr, sip_cfg.srvlookup ? transport : NULL, on_dns_update_mwi, mwi);
- if (!mwi->dnsmgr) {
- ao2_t_ref(mwi, -1, "dnsmgr disabled, remove reference");
- }
- }
- /* If we already have a subscription up simply send a resubscription */
- if (mwi->call) {
- transmit_invite(mwi->call, SIP_SUBSCRIBE, 0, 0, NULL);
- return 0;
- }
- /* Create a dialog that we will use for the subscription */
- if (!(mwi->call = sip_alloc(NULL, NULL, 0, SIP_SUBSCRIBE, NULL, 0))) {
- return -1;
- }
- ref_proxy(mwi->call, obproxy_get(mwi->call, NULL));
- if (!ast_sockaddr_port(&mwi->us) && mwi->portno) {
- ast_sockaddr_set_port(&mwi->us, mwi->portno);
- }
- /* Setup the destination of our subscription */
- if (create_addr(mwi->call, mwi->hostname, &mwi->us, 0)) {
- dialog_unlink_all(mwi->call);
- mwi->call = dialog_unref(mwi->call, "unref dialog after unlink_all");
- return 0;
- }
- mwi->call->expiry = mwi_expiry;
- if (!mwi->dnsmgr && mwi->portno) {
- ast_sockaddr_set_port(&mwi->call->sa, mwi->portno);
- ast_sockaddr_set_port(&mwi->call->recv, mwi->portno);
- } else {
- mwi->portno = ast_sockaddr_port(&mwi->call->sa);
- }
- /* Set various other information */
- if (!ast_strlen_zero(mwi->authuser)) {
- ast_string_field_set(mwi->call, peername, mwi->authuser);
- ast_string_field_set(mwi->call, authname, mwi->authuser);
- ast_string_field_set(mwi->call, fromuser, mwi->authuser);
- } else {
- ast_string_field_set(mwi->call, peername, mwi->username);
- ast_string_field_set(mwi->call, authname, mwi->username);
- ast_string_field_set(mwi->call, fromuser, mwi->username);
- }
- ast_string_field_set(mwi->call, username, mwi->username);
- if (!ast_strlen_zero(mwi->secret)) {
- ast_string_field_set(mwi->call, peersecret, mwi->secret);
- }
- set_socket_transport(&mwi->call->socket, mwi->transport);
- mwi->call->socket.port = htons(mwi->portno);
- ast_sip_ouraddrfor(&mwi->call->sa, &mwi->call->ourip, mwi->call);
- build_via(mwi->call);
- /* Change the dialog callid. */
- change_callid_pvt(mwi->call, NULL);
- ast_set_flag(&mwi->call->flags[0], SIP_OUTGOING);
- /* Associate the call with us */
- mwi->call->mwi = ao2_t_bump(mwi, "Reference mwi from it's call");
- mwi->call->subscribed = MWI_NOTIFICATION;
- /* Actually send the packet */
- transmit_invite(mwi->call, SIP_SUBSCRIBE, 0, 2, NULL);
- return 0;
- }
- /*!
- * \internal
- * \brief Find the channel that is causing the RINGING update, ref'd
- */
- static struct ast_channel *find_ringing_channel(struct ao2_container *device_state_info, struct sip_pvt *p)
- {
- struct ao2_iterator citer;
- struct ast_device_state_info *device_state;
- struct ast_channel *c = NULL;
- struct timeval tv = {0,};
- /* iterate ringing devices and get the oldest of all causing channels */
- citer = ao2_iterator_init(device_state_info, 0);
- for (; (device_state = ao2_iterator_next(&citer)); ao2_ref(device_state, -1)) {
- if (!device_state->causing_channel || (device_state->device_state != AST_DEVICE_RINGING &&
- device_state->device_state != AST_DEVICE_RINGINUSE)) {
- continue;
- }
- ast_channel_lock(device_state->causing_channel);
- if (ast_tvzero(tv) || ast_tvcmp(ast_channel_creationtime(device_state->causing_channel), tv) < 0) {
- c = device_state->causing_channel;
- tv = ast_channel_creationtime(c);
- }
- ast_channel_unlock(device_state->causing_channel);
- }
- ao2_iterator_destroy(&citer);
- return c ? ast_channel_ref(c) : NULL;
- }
- /* XXX Candidate for moving into its own file */
- static int allow_notify_user_presence(struct sip_pvt *p)
- {
- return (strstr(p->useragent, "Digium")) ? 1 : 0;
- }
- /*! \brief Builds XML portion of NOTIFY messages for presence or dialog updates */
- static void state_notify_build_xml(struct state_notify_data *data, int full, const char *exten, const char *context, struct ast_str **tmp, struct sip_pvt *p, int subscribed, const char *mfrom, const char *mto)
- {
- enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED } local_state = NOTIFY_OPEN;
- const char *statestring = "terminated";
- const char *pidfstate = "--";
- const char *pidfnote ="Ready";
- char hint[AST_MAX_EXTENSION];
- switch (data->state) {
- case (AST_EXTENSION_RINGING | AST_EXTENSION_INUSE):
- statestring = (sip_cfg.notifyringing) ? "early" : "confirmed";
- local_state = NOTIFY_INUSE;
- pidfstate = "busy";
- pidfnote = "Ringing";
- break;
- case AST_EXTENSION_RINGING:
- statestring = "early";
- local_state = NOTIFY_INUSE;
- pidfstate = "busy";
- pidfnote = "Ringing";
- break;
- case AST_EXTENSION_INUSE:
- statestring = "confirmed";
- local_state = NOTIFY_INUSE;
- pidfstate = "busy";
- pidfnote = "On the phone";
- break;
- case AST_EXTENSION_BUSY:
- statestring = "confirmed";
- local_state = NOTIFY_CLOSED;
- pidfstate = "busy";
- pidfnote = "On the phone";
- break;
- case AST_EXTENSION_UNAVAILABLE:
- statestring = "terminated";
- local_state = NOTIFY_CLOSED;
- pidfstate = "away";
- pidfnote = "Unavailable";
- break;
- case AST_EXTENSION_ONHOLD:
- statestring = "confirmed";
- local_state = NOTIFY_CLOSED;
- pidfstate = "busy";
- pidfnote = "On hold";
- break;
- case AST_EXTENSION_NOT_INUSE:
- default:
- /* Default setting */
- break;
- }
- /* Check which device/devices we are watching and if they are registered */
- if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten)) {
- char *hint2;
- char *individual_hint = NULL;
- int hint_count = 0, unavailable_count = 0;
- /* strip off any possible PRESENCE providers from hint */
- if ((hint2 = strrchr(hint, ','))) {
- *hint2 = '\0';
- }
- hint2 = hint;
- while ((individual_hint = strsep(&hint2, "&"))) {
- hint_count++;
- if (ast_device_state(individual_hint) == AST_DEVICE_UNAVAILABLE)
- unavailable_count++;
- }
- /* If none of the hinted devices are registered, we will
- * override notification and show no availability.
- */
- if (hint_count > 0 && hint_count == unavailable_count) {
- local_state = NOTIFY_CLOSED;
- pidfstate = "away";
- pidfnote = "Not online";
- }
- }
- switch (subscribed) {
- case XPIDF_XML:
- case CPIM_PIDF_XML:
- ast_str_append(tmp, 0,
- "<?xml version=\"1.0\"?>\n"
- "<!DOCTYPE presence PUBLIC \"-//IETF//DTD RFCxxxx XPIDF 1.0//EN\" \"xpidf.dtd\">\n"
- "<presence>\n");
- ast_str_append(tmp, 0, "<presentity uri=\"%s;method=SUBSCRIBE\" />\n", mfrom);
- ast_str_append(tmp, 0, "<atom id=\"%s\">\n", exten);
- ast_str_append(tmp, 0, "<address uri=\"%s;user=ip\" priority=\"0.800000\">\n", mto);
- ast_str_append(tmp, 0, "<status status=\"%s\" />\n", (local_state == NOTIFY_OPEN) ? "open" : (local_state == NOTIFY_INUSE) ? "inuse" : "closed");
- ast_str_append(tmp, 0, "<msnsubstatus substatus=\"%s\" />\n", (local_state == NOTIFY_OPEN) ? "online" : (local_state == NOTIFY_INUSE) ? "onthephone" : "offline");
- ast_str_append(tmp, 0, "</address>\n</atom>\n</presence>\n");
- break;
- case PIDF_XML: /* Eyebeam supports this format */
- ast_str_append(tmp, 0,
- "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
- "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" \nxmlns:pp=\"urn:ietf:params:xml:ns:pidf:person\"\nxmlns:es=\"urn:ietf:params:xml:ns:pidf:rpid:status:rpid-status\"\nxmlns:ep=\"urn:ietf:params:xml:ns:pidf:rpid:rpid-person\"\nentity=\"%s\">\n", mfrom);
- ast_str_append(tmp, 0, "<pp:person><status>\n");
- if (pidfstate[0] != '-') {
- ast_str_append(tmp, 0, "<ep:activities><ep:%s/></ep:activities>\n", pidfstate);
- }
- ast_str_append(tmp, 0, "</status></pp:person>\n");
- ast_str_append(tmp, 0, "<note>%s</note>\n", pidfnote); /* Note */
- ast_str_append(tmp, 0, "<tuple id=\"%s\">\n", exten); /* Tuple start */
- ast_str_append(tmp, 0, "<contact priority=\"1\">%s</contact>\n", mto);
- if (pidfstate[0] == 'b') /* Busy? Still open ... */
- ast_str_append(tmp, 0, "<status><basic>open</basic></status>\n");
- else
- ast_str_append(tmp, 0, "<status><basic>%s</basic></status>\n", (local_state != NOTIFY_CLOSED) ? "open" : "closed");
- if (allow_notify_user_presence(p) && (data->presence_state != AST_PRESENCE_INVALID)
- && (data->presence_state != AST_PRESENCE_NOT_SET)) {
- ast_str_append(tmp, 0, "</tuple>\n");
- ast_str_append(tmp, 0, "<tuple id=\"digium-presence\">\n");
- ast_str_append(tmp, 0, "<status>\n");
- ast_str_append(tmp, 0, "<digium_presence type=\"%s\" subtype=\"%s\">%s</digium_presence>\n",
- ast_presence_state2str(data->presence_state),
- S_OR(data->presence_subtype, ""),
- S_OR(data->presence_message, ""));
- ast_str_append(tmp, 0, "</status>\n");
- ast_test_suite_event_notify("DIGIUM_PRESENCE_SENT",
- "PresenceState: %s\r\n"
- "Subtype: %s\r\n"
- "Message: %s",
- ast_presence_state2str(data->presence_state),
- S_OR(data->presence_subtype, ""),
- S_OR(data->presence_message, ""));
- }
- ast_str_append(tmp, 0, "</tuple>\n</presence>\n");
- break;
- case DIALOG_INFO_XML: /* SNOM subscribes in this format */
- ast_str_append(tmp, 0, "<?xml version=\"1.0\"?>\n");
- ast_str_append(tmp, 0, "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"%u\" state=\"%s\" entity=\"%s\">\n", p->dialogver, full ? "full" : "partial", mto);
- if (data->state > 0 && (data->state & AST_EXTENSION_RINGING) && sip_cfg.notifyringing) {
- /* Twice the extension length should be enough for XML encoding */
- char local_display[AST_MAX_EXTENSION * 2];
- char remote_display[AST_MAX_EXTENSION * 2];
- char *local_target = ast_strdupa(mto);
- /* It may seem odd to base the remote_target on the To header here,
- * but testing by reporters on issue ASTERISK-16735 found that basing
- * on the From header would cause ringing state hints to not work
- * properly on certain SNOM devices. If you are using notifycid properly
- * (i.e. in the same extension and context as the dialed call) then this
- * should not be an issue since the data will be overwritten shortly
- * with channel caller ID
- */
- char *remote_target = ast_strdupa(mto);
- ast_xml_escape(exten, local_display, sizeof(local_display));
- ast_xml_escape(exten, remote_display, sizeof(remote_display));
- /* There are some limitations to how this works. The primary one is that the
- callee must be dialing the same extension that is being monitored. Simply dialing
- the hint'd device is not sufficient. */
- if (sip_cfg.notifycid) {
- struct ast_channel *callee;
- callee = find_ringing_channel(data->device_state_info, p);
- if (callee) {
- static char *anonymous = "anonymous";
- static char *invalid = "anonymous.invalid";
- char *cid_num;
- char *connected_num;
- int need;
- int cid_num_restricted, connected_num_restricted;
- ast_channel_lock(callee);
- cid_num_restricted = (ast_channel_caller(callee)->id.number.presentation &
- AST_PRES_RESTRICTION) == AST_PRES_RESTRICTED;
- cid_num = S_COR(ast_channel_caller(callee)->id.number.valid,
- S_COR(cid_num_restricted, anonymous,
- ast_channel_caller(callee)->id.number.str), "");
- need = strlen(cid_num) + (cid_num_restricted ? strlen(invalid) :
- strlen(p->fromdomain)) + sizeof("sip:@");
- local_target = ast_alloca(need);
- snprintf(local_target, need, "sip:%s@%s", cid_num,
- cid_num_restricted ? invalid : p->fromdomain);
- ast_xml_escape(S_COR(ast_channel_caller(callee)->id.name.valid,
- S_COR((ast_channel_caller(callee)->id.name.presentation &
- AST_PRES_RESTRICTION) == AST_PRES_RESTRICTED, anonymous,
- ast_channel_caller(callee)->id.name.str), ""),
- local_display, sizeof(local_display));
- connected_num_restricted = (ast_channel_connected(callee)->id.number.presentation &
- AST_PRES_RESTRICTION) == AST_PRES_RESTRICTED;
- connected_num = S_COR(ast_channel_connected(callee)->id.number.valid,
- S_COR(connected_num_restricted, anonymous,
- ast_channel_connected(callee)->id.number.str), "");
- need = strlen(connected_num) + (connected_num_restricted ? strlen(invalid) :
- strlen(p->fromdomain)) + sizeof("sip:@");
- remote_target = ast_alloca(need);
- snprintf(remote_target, need, "sip:%s@%s", connected_num,
- connected_num_restricted ? invalid : p->fromdomain);
- ast_xml_escape(S_COR(ast_channel_connected(callee)->id.name.valid,
- S_COR((ast_channel_connected(callee)->id.name.presentation &
- AST_PRES_RESTRICTION) == AST_PRES_RESTRICTED, anonymous,
- ast_channel_connected(callee)->id.name.str), ""),
- remote_display, sizeof(remote_display));
- ast_channel_unlock(callee);
- callee = ast_channel_unref(callee);
- }
- /* We create a fake call-id which the phone will send back in an INVITE
- Replaces header which we can grab and do some magic with. */
- if (sip_cfg.pedanticsipchecking) {
- ast_str_append(tmp, 0, "<dialog id=\"%s\" call-id=\"pickup-%s\" local-tag=\"%s\" remote-tag=\"%s\" direction=\"recipient\">\n",
- exten, p->callid, p->theirtag, p->tag);
- } else {
- ast_str_append(tmp, 0, "<dialog id=\"%s\" call-id=\"pickup-%s\" direction=\"recipient\">\n",
- exten, p->callid);
- }
- ast_str_append(tmp, 0,
- "<remote>\n"
- /* See the limitations of this above. Luckily the phone seems to still be
- happy when these values are not correct. */
- "<identity display=\"%s\">%s</identity>\n"
- "<target uri=\"%s\"/>\n"
- "</remote>\n"
- "<local>\n"
- "<identity display=\"%s\">%s</identity>\n"
- "<target uri=\"%s\"/>\n"
- "</local>\n",
- remote_display, remote_target, remote_target, local_display, local_target, local_target);
- } else {
- ast_str_append(tmp, 0, "<dialog id=\"%s\" direction=\"recipient\">\n", exten);
- }
- } else {
- ast_str_append(tmp, 0, "<dialog id=\"%s\">\n", exten);
- }
- ast_str_append(tmp, 0, "<state>%s</state>\n", statestring);
- if (data->state == AST_EXTENSION_ONHOLD) {
- ast_str_append(tmp, 0, "<local>\n<target uri=\"%s\">\n"
- "<param pname=\"+sip.rendering\" pvalue=\"no\"/>\n"
- "</target>\n</local>\n", mto);
- }
- ast_str_append(tmp, 0, "</dialog>\n</dialog-info>\n");
- break;
- case NONE:
- default:
- break;
- }
- }
- static int transmit_cc_notify(struct ast_cc_agent *agent, struct sip_pvt *subscription, enum sip_cc_notify_state state)
- {
- struct sip_request req;
- struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
- char uri[SIPBUFSIZE];
- char state_str[64];
- char subscription_state_hdr[64];
- if (state < CC_QUEUED || state > CC_READY) {
- ast_log(LOG_WARNING, "Invalid state provided for transmit_cc_notify (%u)\n", state);
- return -1;
- }
- reqprep(&req, subscription, SIP_NOTIFY, 0, TRUE);
- snprintf(state_str, sizeof(state_str), "%s\r\n", sip_cc_notify_state_map[state].state_string);
- add_header(&req, "Event", "call-completion");
- add_header(&req, "Content-Type", "application/call-completion");
- snprintf(subscription_state_hdr, sizeof(subscription_state_hdr), "active;expires=%d", subscription->expiry);
- add_header(&req, "Subscription-State", subscription_state_hdr);
- if (state == CC_READY) {
- generate_uri(subscription, agent_pvt->notify_uri, sizeof(agent_pvt->notify_uri));
- snprintf(uri, sizeof(uri) - 1, "cc-URI: %s\r\n", agent_pvt->notify_uri);
- }
- add_content(&req, state_str);
- if (state == CC_READY) {
- add_content(&req, uri);
- }
- return send_request(subscription, &req, XMIT_RELIABLE, subscription->ocseq);
- }
- /*! \brief Used in the SUBSCRIBE notification subsystem (RFC3265) */
- static int transmit_state_notify(struct sip_pvt *p, struct state_notify_data *data, int full, int timeout)
- {
- struct ast_str *tmp = ast_str_alloca(4000);
- char from[256], to[256];
- char *c, *mfrom, *mto;
- struct sip_request req;
- const struct cfsubscription_types *subscriptiontype;
- /* If the subscription has not yet been accepted do not send a NOTIFY */
- if (!ast_test_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) {
- return 0;
- }
- memset(from, 0, sizeof(from));
- memset(to, 0, sizeof(to));
- subscriptiontype = find_subscription_type(p->subscribed);
- ast_copy_string(from, sip_get_header(&p->initreq, "From"), sizeof(from));
- c = get_in_brackets(from);
- if (strncasecmp(c, "sip:", 4) && strncasecmp(c, "sips:", 5)) {
- ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c);
- return -1;
- }
- mfrom = remove_uri_parameters(c);
- ast_copy_string(to, sip_get_header(&p->initreq, "To"), sizeof(to));
- c = get_in_brackets(to);
- if (strncasecmp(c, "sip:", 4) && strncasecmp(c, "sips:", 5)) {
- ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c);
- return -1;
- }
- mto = remove_uri_parameters(c);
- reqprep(&req, p, SIP_NOTIFY, 0, 1);
- switch(data->state) {
- case AST_EXTENSION_DEACTIVATED:
- if (timeout)
- add_header(&req, "Subscription-State", "terminated;reason=timeout");
- else {
- add_header(&req, "Subscription-State", "terminated;reason=probation");
- add_header(&req, "Retry-After", "60");
- }
- break;
- case AST_EXTENSION_REMOVED:
- add_header(&req, "Subscription-State", "terminated;reason=noresource");
- break;
- default:
- if (p->expiry)
- add_header(&req, "Subscription-State", "active");
- else /* Expired */
- add_header(&req, "Subscription-State", "terminated;reason=timeout");
- }
- switch (p->subscribed) {
- case XPIDF_XML:
- case CPIM_PIDF_XML:
- add_header(&req, "Event", subscriptiontype->event);
- state_notify_build_xml(data, full, p->exten, p->context, &tmp, p, p->subscribed, mfrom, mto);
- add_header(&req, "Content-Type", subscriptiontype->mediatype);
- p->dialogver++;
- break;
- case PIDF_XML: /* Eyebeam supports this format */
- add_header(&req, "Event", subscriptiontype->event);
- state_notify_build_xml(data, full, p->exten, p->context, &tmp, p, p->subscribed, mfrom, mto);
- add_header(&req, "Content-Type", subscriptiontype->mediatype);
- p->dialogver++;
- break;
- case DIALOG_INFO_XML: /* SNOM subscribes in this format */
- add_header(&req, "Event", subscriptiontype->event);
- state_notify_build_xml(data, full, p->exten, p->context, &tmp, p, p->subscribed, mfrom, mto);
- add_header(&req, "Content-Type", subscriptiontype->mediatype);
- p->dialogver++;
- break;
- case NONE:
- default:
- break;
- }
- add_content(&req, ast_str_buffer(tmp));
- p->pendinginvite = p->ocseq; /* Remember that we have a pending NOTIFY in order not to confuse the NOTIFY subsystem */
- /* Send as XMIT_CRITICAL as we may never receive a 200 OK Response which clears p->pendinginvite.
- *
- * extensionstate_update() uses p->pendinginvite for queuing control.
- * Updates stall if pendinginvite <> 0.
- *
- * The most appropriate solution is to remove the subscription when the NOTIFY transaction fails.
- * The client will re-subscribe after restarting or maxexpiry timeout.
- */
- /* RFC6665 4.2.2. Sending State Information to Subscribers
- * If the NOTIFY request fails due to expiration of SIP Timer F (transaction timeout),
- * the notifier SHOULD remove the subscription.
- */
- return send_request(p, &req, XMIT_CRITICAL, p->ocseq);
- }
- /*! \brief Notify user of messages waiting in voicemail (RFC3842)
- \note - Notification only works for registered peers with mailbox= definitions
- in sip.conf
- - We use the SIP Event package message-summary
- MIME type defaults to "application/simple-message-summary";
- */
- static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, const char *vmexten)
- {
- struct sip_request req;
- struct ast_str *out = ast_str_alloca(500);
- int ourport = (p->fromdomainport && (p->fromdomainport != STANDARD_SIP_PORT)) ? p->fromdomainport : ast_sockaddr_port(&p->ourip);
- const char *domain;
- const char *exten = S_OR(vmexten, default_vmexten);
- initreqprep(&req, p, SIP_NOTIFY, NULL);
- add_header(&req, "Event", "message-summary");
- add_header(&req, "Content-Type", default_notifymime);
- ast_str_append(&out, 0, "Messages-Waiting: %s\r\n", newmsgs ? "yes" : "no");
- /* domain initialization occurs here because initreqprep changes ast_sockaddr_stringify string. */
- domain = S_OR(p->fromdomain, ast_sockaddr_stringify_host_remote(&p->ourip));
- if (!sip_standard_port(p->socket.type, ourport)) {
- if (p->socket.type == AST_TRANSPORT_UDP) {
- ast_str_append(&out, 0, "Message-Account: sip:%s@%s:%d\r\n", exten, domain, ourport);
- } else {
- ast_str_append(&out, 0, "Message-Account: sip:%s@%s:%d;transport=%s\r\n", exten, domain, ourport, sip_get_transport(p->socket.type));
- }
- } else {
- if (p->socket.type == AST_TRANSPORT_UDP) {
- ast_str_append(&out, 0, "Message-Account: sip:%s@%s\r\n", exten, domain);
- } else {
- ast_str_append(&out, 0, "Message-Account: sip:%s@%s;transport=%s\r\n", exten, domain, sip_get_transport(p->socket.type));
- }
- }
- /* Cisco has a bug in the SIP stack where it can't accept the
- (0/0) notification. This can temporarily be disabled in
- sip.conf with the "buggymwi" option */
- ast_str_append(&out, 0, "Voice-Message: %d/%d%s\r\n",
- newmsgs, oldmsgs, (ast_test_flag(&p->flags[1], SIP_PAGE2_BUGGY_MWI) ? "" : " (0/0)"));
- if (p->subscribed) {
- if (p->expiry) {
- add_header(&req, "Subscription-State", "active");
- } else { /* Expired */
- add_header(&req, "Subscription-State", "terminated;reason=timeout");
- }
- }
- add_content(&req, ast_str_buffer(out));
- if (!p->initreq.headers) {
- initialize_initreq(p, &req);
- }
- return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
- }
- /*! \brief Notify a transferring party of the status of transfer (RFC3515) */
- static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate)
- {
- struct sip_request req;
- char tmp[SIPBUFSIZE/2];
- reqprep(&req, p, SIP_NOTIFY, 0, 1);
- snprintf(tmp, sizeof(tmp), "refer;id=%d", cseq);
- add_header(&req, "Event", tmp);
- add_header(&req, "Subscription-state", terminate ? "terminated;reason=noresource" : "active");
- add_header(&req, "Content-Type", "message/sipfrag;version=2.0");
- add_header(&req, "Allow", ALLOWED_METHODS);
- add_supported(p, &req);
- snprintf(tmp, sizeof(tmp), "SIP/2.0 %s\r\n", message);
- add_content(&req, tmp);
- if (!p->initreq.headers) {
- initialize_initreq(p, &req);
- }
- return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
- }
- static int manager_sipnotify(struct mansession *s, const struct message *m)
- {
- const char *channame = astman_get_header(m, "Channel");
- struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL);
- struct sip_pvt *p;
- struct ast_variable *header, *var;
- if (ast_strlen_zero(channame)) {
- astman_send_error(s, m, "SIPNotify requires a channel name");
- return 0;
- }
- if (!strncasecmp(channame, "sip/", 4)) {
- channame += 4;
- }
- if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY, NULL, 0))) {
- astman_send_error(s, m, "Unable to build sip pvt data for notify (memory/socket error)");
- return 0;
- }
- if (create_addr(p, channame, NULL, 0)) {
- /* Maybe they're not registered, etc. */
- dialog_unlink_all(p);
- dialog_unref(p, "unref dialog inside for loop" );
- /* sip_destroy(p); */
- astman_send_error(s, m, "Could not create address");
- return 0;
- }
- /* Notify is outgoing call */
- ast_set_flag(&p->flags[0], SIP_OUTGOING);
- sip_notify_alloc(p);
- p->notify->headers = header = ast_variable_new("Subscription-State", "terminated", "");
- for (var = vars; var; var = var->next) {
- if (!strcasecmp(var->name, "Content")) {
- if (ast_str_strlen(p->notify->content))
- ast_str_append(&p->notify->content, 0, "\r\n");
- ast_str_append(&p->notify->content, 0, "%s", var->value);
- } else if (!strcasecmp(var->name, "Content-Length")) {
- ast_log(LOG_WARNING, "it is not necessary to specify Content-Length, ignoring\n");
- } else {
- header->next = ast_variable_new(var->name, var->value, "");
- header = header->next;
- }
- }
- sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
- transmit_invite(p, SIP_NOTIFY, 0, 2, NULL);
- dialog_unref(p, "bump down the count of p since we're done with it.");
- astman_send_ack(s, m, "Notify Sent");
- ast_variables_destroy(vars);
- return 0;
- }
- /*! \brief Send a provisional response indicating that a call was redirected
- */
- static void update_redirecting(struct sip_pvt *p, const void *data, size_t datalen)
- {
- struct sip_request resp;
- if (ast_channel_state(p->owner) == AST_STATE_UP || ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- return;
- }
- respprep(&resp, p, "181 Call is being forwarded", &p->initreq);
- add_diversion(&resp, p);
- send_response(p, &resp, XMIT_UNRELIABLE, 0);
- }
- /*! \brief Notify peer that the connected line has changed */
- static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen)
- {
- struct ast_party_id connected_id = ast_channel_connected_effective_id(p->owner);
- if (!ast_test_flag(&p->flags[0], SIP_SENDRPID)) {
- return;
- }
- if (!connected_id.number.valid
- || ast_strlen_zero(connected_id.number.str)) {
- return;
- }
- append_history(p, "ConnectedLine", "%s party is now %s <%s>",
- ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "Calling" : "Called",
- S_COR(connected_id.name.valid, connected_id.name.str, ""),
- S_COR(connected_id.number.valid, connected_id.number.str, ""));
- if (ast_channel_state(p->owner) == AST_STATE_UP || ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- struct sip_request req;
- if (!p->pendinginvite && (p->invitestate == INV_CONFIRMED || p->invitestate == INV_TERMINATED)) {
- reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1);
- add_header(&req, "Allow", ALLOWED_METHODS);
- add_supported(p, &req);
- add_rpid(&req, p);
- add_sdp(&req, p, FALSE, TRUE, FALSE);
- initialize_initreq(p, &req);
- p->lastinvite = p->ocseq;
- ast_set_flag(&p->flags[0], SIP_OUTGOING);
- p->invitestate = INV_CALLING;
- send_request(p, &req, XMIT_CRITICAL, p->ocseq);
- } else if ((is_method_allowed(&p->allowed_methods, SIP_UPDATE)) && (!ast_strlen_zero(p->okcontacturi))) {
- reqprep(&req, p, SIP_UPDATE, 0, 1);
- add_rpid(&req, p);
- add_header(&req, "X-Asterisk-rpid-update", "Yes");
- send_request(p, &req, XMIT_CRITICAL, p->ocseq);
- } else {
- /* We cannot send the update yet, so we have to wait until we can */
- ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
- }
- } else {
- ast_set_flag(&p->flags[1], SIP_PAGE2_CONNECTLINEUPDATE_PEND);
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_RPID_IMMEDIATE)) {
- struct sip_request resp;
- if ((ast_channel_state(p->owner) == AST_STATE_RING) && !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT)) {
- ast_clear_flag(&p->flags[1], SIP_PAGE2_CONNECTLINEUPDATE_PEND);
- respprep(&resp, p, "180 Ringing", &p->initreq);
- add_rpid(&resp, p);
- send_response(p, &resp, XMIT_UNRELIABLE, 0);
- ast_set_flag(&p->flags[0], SIP_RINGING);
- } else if (ast_channel_state(p->owner) == AST_STATE_RINGING) {
- ast_clear_flag(&p->flags[1], SIP_PAGE2_CONNECTLINEUPDATE_PEND);
- respprep(&resp, p, "183 Session Progress", &p->initreq);
- add_rpid(&resp, p);
- send_response(p, &resp, XMIT_UNRELIABLE, 0);
- ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
- } else {
- ast_debug(1, "Unable able to send update to '%s' in state '%s'\n", ast_channel_name(p->owner), ast_state2str(ast_channel_state(p->owner)));
- }
- }
- }
- }
- static const struct _map_x_s regstatestrings[] = {
- { REG_STATE_FAILED, "Failed" },
- { REG_STATE_UNREGISTERED, "Unregistered"},
- { REG_STATE_REGSENT, "Request Sent"},
- { REG_STATE_AUTHSENT, "Auth. Sent"},
- { REG_STATE_REGISTERED, "Registered"},
- { REG_STATE_REJECTED, "Rejected"},
- { REG_STATE_TIMEOUT, "Timeout"},
- { REG_STATE_NOAUTH, "No Authentication"},
- { -1, NULL } /* terminator */
- };
- /*! \brief Convert registration state status to string */
- static const char *regstate2str(enum sipregistrystate regstate)
- {
- return map_x_s(regstatestrings, regstate, "Unknown");
- }
- static void sip_publish_registry(const char *username, const char *domain, const char *status)
- {
- ast_system_publish_registry("SIP", username, domain, status, NULL);
- }
- /*! \brief Update registration with SIP Proxy.
- * Called from the scheduler when the previous registration expires,
- * so we don't have to cancel the pending event.
- * We assume the reference so the sip_registry is valid, since it
- * is stored in the scheduled event anyways.
- */
- static int sip_reregister(const void *data)
- {
- /* if we are here, we know that we need to reregister. */
- struct sip_registry *r = (struct sip_registry *) data;
- /* if we couldn't get a reference to the registry object, punt */
- if (!r) {
- return 0;
- }
- if (r->call && r->call->do_history) {
- append_history(r->call, "RegistryRenew", "Account: %s@%s", r->username, r->hostname);
- }
- /* Since registry's are only added/removed by the the monitor thread, this
- may be overkill to reference/dereference at all here */
- if (sipdebug) {
- ast_log(LOG_NOTICE, " -- Re-registration for %s@%s\n", r->username, r->hostname);
- }
- r->expire = -1;
- r->expiry = r->configured_expiry;
- __sip_do_register(r);
- ao2_t_ref(r, -1, "unref the re-register scheduled event");
- return 0;
- }
- /*! \brief Register with SIP proxy
- \return see \ref __sip_xmit
- */
- static int __sip_do_register(struct sip_registry *r)
- {
- int res;
- res = transmit_register(r, SIP_REGISTER, NULL, NULL);
- return res;
- }
- /*! \brief Registration timeout, register again
- * Registered as a timeout handler during transmit_register(),
- * to retransmit the packet if a reply does not come back.
- * This is called by the scheduler so the event is not pending anymore when
- * we are called.
- */
- static int sip_reg_timeout(const void *data)
- {
- /* if we are here, our registration timed out, so we'll just do it over */
- struct sip_registry *r = (struct sip_registry *)data; /* the ref count should have been bumped when the sched item was added */
- struct sip_pvt *p;
- /* if we couldn't get a reference to the registry object, punt */
- if (!r) {
- return 0;
- }
- if (r->dnsmgr) {
- /* If the registration has timed out, maybe the IP changed. Force a refresh. */
- ast_dnsmgr_refresh(r->dnsmgr);
- }
- /* If the initial tranmission failed, we may not have an existing dialog,
- * so it is possible that r->call == NULL.
- * Otherwise destroy it, as we have a timeout so we don't want it.
- */
- if (r->call) {
- /* Unlink us, destroy old call. Locking is not relevant here because all this happens
- in the single SIP manager thread. */
- p = r->call;
- sip_pvt_lock(p);
- pvt_set_needdestroy(p, "registration timeout");
- /* Pretend to ACK anything just in case */
- __sip_pretend_ack(p);
- sip_pvt_unlock(p);
- /* decouple the two objects */
- /* p->registry == r, so r has 2 refs, and the unref won't take the object away */
- ao2_t_replace(p->registry, NULL, "p->registry unreffed");
- r->call = dialog_unref(r->call, "unrefing r->call");
- }
- /* If we have a limit, stop registration and give up */
- r->timeout = -1;
- if (global_regattempts_max && r->regattempts >= global_regattempts_max) {
- /* Ok, enough is enough. Don't try any more */
- /* We could add an external notification here...
- steal it from app_voicemail :-) */
- ast_log(LOG_NOTICE, " -- Last Registration Attempt #%d failed, Giving up forever trying to register '%s@%s'\n", r->regattempts, r->username, r->hostname);
- r->regstate = REG_STATE_FAILED;
- } else {
- r->regstate = REG_STATE_UNREGISTERED;
- transmit_register(r, SIP_REGISTER, NULL, NULL);
- ast_log(LOG_NOTICE, " -- Registration for '%s@%s' timed out, trying again (Attempt #%d)\n", r->username, r->hostname, r->regattempts);
- }
- sip_publish_registry(r->username, r->hostname, regstate2str(r->regstate));
- ao2_t_ref(r, -1, "unreffing registry_unref r");
- return 0;
- }
- static const char *sip_sanitized_host(const char *host)
- {
- struct ast_sockaddr addr = { { 0, 0, }, };
- /* peer/sip_pvt->tohost and sip_registry->hostname should never have a port
- * in them, so we use PARSE_PORT_FORBID here. If this lookup fails, we return
- * the original host which is most likely a host name and not an IP. */
- if (!ast_sockaddr_parse(&addr, host, PARSE_PORT_FORBID)) {
- return host;
- }
- return ast_sockaddr_stringify_host_remote(&addr);
- }
- /*! \brief Transmit register to SIP proxy or UA
- * auth = NULL on the initial registration (from sip_reregister())
- */
- static int transmit_register(struct sip_registry *r, int sipmethod, const char *auth, const char *authheader)
- {
- struct sip_request req;
- char from[256];
- char to[256];
- char tmp[80];
- char addr[80];
- struct sip_pvt *p;
- struct sip_peer *peer = NULL;
- int res;
- int portno = 0;
- /* exit if we are already in process with this registrar ?*/
- if (r == NULL || ((auth == NULL) && (r->regstate == REG_STATE_REGSENT || r->regstate == REG_STATE_AUTHSENT))) {
- if (r) {
- ast_log(LOG_NOTICE, "Strange, trying to register %s@%s when registration already pending\n", r->username, r->hostname);
- }
- return 0;
- }
- if (r->dnsmgr == NULL) {
- char transport[MAXHOSTNAMELEN];
- peer = sip_find_peer(r->hostname, NULL, TRUE, FINDPEERS, FALSE, 0);
- snprintf(transport, sizeof(transport), "_%s._%s",get_srv_service(r->transport), get_srv_protocol(r->transport)); /* have to use static sip_get_transport function */
- r->us.ss.ss_family = get_address_family_filter(r->transport); /* Filter address family */
- /* No point in doing a DNS lookup of the register hostname if we're just going to
- * end up using an outbound proxy. obproxy_get is safe to call with either of r->call
- * or peer NULL. Since we're only concerned with its existence, we're not going to
- * bother getting a ref to the proxy*/
- if (!obproxy_get(r->call, peer)) {
- ao2_t_ref(r, +1, "add reg ref for dnsmgr");
- ast_dnsmgr_lookup_cb(peer ? peer->tohost : r->hostname, &r->us, &r->dnsmgr, sip_cfg.srvlookup ? transport : NULL, on_dns_update_registry, r);
- if (!r->dnsmgr) {
- /*dnsmgr refresh disabled, no reference added! */
- ao2_t_ref(r, -1, "remove reg ref, dnsmgr disabled");
- }
- }
- if (peer) {
- peer = sip_unref_peer(peer, "removing peer ref for dnsmgr_lookup");
- }
- }
- if (r->call) { /* We have a registration */
- if (!auth) {
- ast_log(LOG_WARNING, "Already have a REGISTER going on to %s@%s?? \n", r->username, r->hostname);
- return 0;
- } else {
- p = dialog_ref(r->call, "getting a copy of the r->call dialog in transmit_register");
- ast_string_field_set(p, theirtag, NULL); /* forget their old tag, so we don't match tags when getting response */
- }
- } else {
- /* Build callid for registration if we haven't registered before */
- if (!r->callid_valid) {
- build_callid_registry(r, &internip, default_fromdomain);
- build_localtag_registry(r);
- r->callid_valid = TRUE;
- }
- /* Allocate SIP dialog for registration */
- if (!(p = sip_alloc( r->callid, NULL, 0, SIP_REGISTER, NULL, 0))) {
- ast_log(LOG_WARNING, "Unable to allocate registration transaction (memory or socket error)\n");
- return 0;
- }
- /* reset tag to consistent value from registry */
- ast_string_field_set(p, tag, r->localtag);
- if (p->do_history) {
- append_history(p, "RegistryInit", "Account: %s@%s", r->username, r->hostname);
- }
- p->socket.type = r->transport;
- /* Use port number specified if no SRV record was found */
- if (!ast_sockaddr_isnull(&r->us)) {
- if (!ast_sockaddr_port(&r->us) && r->portno) {
- ast_sockaddr_set_port(&r->us, r->portno);
- }
- /* It is possible that DNS was unavailable at the time the peer was created.
- * Here, if we've updated the address in the registry via manually calling
- * ast_dnsmgr_lookup_cb() above, then we call the same function that dnsmgr would
- * call if it was updating a peer's address */
- if ((peer = sip_find_peer(S_OR(r->peername, r->hostname), NULL, TRUE, FINDPEERS, FALSE, 0))) {
- if (ast_sockaddr_cmp(&peer->addr, &r->us)) {
- on_dns_update_peer(&peer->addr, &r->us, peer);
- }
- peer = sip_unref_peer(peer, "unref after sip_find_peer");
- }
- }
- /* Find address to hostname */
- if (create_addr(p, S_OR(r->peername, r->hostname), &r->us, 0)) {
- /* we have what we hope is a temporary network error,
- * probably DNS. We need to reschedule a registration try */
- dialog_unlink_all(p);
- p = dialog_unref(p, "unref dialog after unlink_all");
- if (r->timeout > -1) {
- AST_SCHED_REPLACE_UNREF(r->timeout, sched, global_reg_timeout * 1000, sip_reg_timeout, r,
- ao2_t_ref(_data, -1, "del for REPLACE of registry ptr"),
- ao2_t_ref(r, -1, "object ptr dec when SCHED_REPLACE add failed"),
- ao2_t_ref(r, +1, "add for REPLACE registry ptr"));
- ast_log(LOG_WARNING, "Still have a registration timeout for %s@%s (create_addr() error), %d\n", r->username, r->hostname, r->timeout);
- } else {
- r->timeout = ast_sched_add(sched, global_reg_timeout * 1000, sip_reg_timeout, ao2_t_bump(r, "add for REPLACE registry ptr"));
- ast_log(LOG_WARNING, "Probably a DNS error for registration to %s@%s, trying REGISTER again (after %d seconds)\n", r->username, r->hostname, global_reg_timeout);
- }
- r->regattempts++;
- return 0;
- }
- /* Copy back Call-ID in case create_addr changed it */
- ast_string_field_set(r, callid, p->callid);
- if (!r->dnsmgr && r->portno) {
- ast_sockaddr_set_port(&p->sa, r->portno);
- ast_sockaddr_set_port(&p->recv, r->portno);
- }
- if (!ast_strlen_zero(p->fromdomain)) {
- portno = (p->fromdomainport) ? p->fromdomainport : STANDARD_SIP_PORT;
- } else if (!ast_strlen_zero(r->regdomain)) {
- portno = (r->regdomainport) ? r->regdomainport : STANDARD_SIP_PORT;
- } else {
- portno = ast_sockaddr_port(&p->sa);
- }
- ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Registration is outgoing call */
- r->call = dialog_ref(p, "copying dialog into registry r->call"); /* Save pointer to SIP dialog */
- p->registry = ao2_t_bump(r, "transmit_register: addref to p->registry in transmit_register"); /* Add pointer to registry in packet */
- if (!ast_strlen_zero(r->secret)) { /* Secret (password) */
- ast_string_field_set(p, peersecret, r->secret);
- }
- if (!ast_strlen_zero(r->md5secret))
- ast_string_field_set(p, peermd5secret, r->md5secret);
- /* User name in this realm
- - if authuser is set, use that, otherwise use username */
- if (!ast_strlen_zero(r->authuser)) {
- ast_string_field_set(p, peername, r->authuser);
- ast_string_field_set(p, authname, r->authuser);
- } else if (!ast_strlen_zero(r->username)) {
- ast_string_field_set(p, peername, r->username);
- ast_string_field_set(p, authname, r->username);
- ast_string_field_set(p, fromuser, r->username);
- }
- if (!ast_strlen_zero(r->username)) {
- ast_string_field_set(p, username, r->username);
- }
- /* Save extension in packet */
- if (!ast_strlen_zero(r->callback)) {
- ast_string_field_set(p, exten, r->callback);
- }
- /* Set transport and port so the correct contact is built */
- set_socket_transport(&p->socket, r->transport);
- if (r->transport == AST_TRANSPORT_TLS || r->transport == AST_TRANSPORT_TCP) {
- p->socket.port =
- htons(ast_sockaddr_port(&sip_tcp_desc.local_address));
- }
- /*
- check which address we should use in our contact header
- based on whether the remote host is on the external or
- internal network so we can register through nat
- */
- ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
- }
- /* set up a timeout */
- if (auth == NULL) {
- if (r->timeout > -1) {
- ast_log(LOG_WARNING, "Still have a registration timeout, #%d - deleting it\n", r->timeout);
- }
- AST_SCHED_REPLACE_UNREF(r->timeout, sched, global_reg_timeout * 1000, sip_reg_timeout, r,
- ao2_t_ref(_data, -1, "reg ptr unrefed from del in SCHED_REPLACE"),
- ao2_t_ref(r, -1, "reg ptr unrefed from add failure in SCHED_REPLACE"),
- ao2_t_ref(r, +1, "reg ptr reffed from add in SCHED_REPLACE"));
- ast_debug(1, "Scheduled a registration timeout for %s id #%d \n", r->hostname, r->timeout);
- }
- snprintf(from, sizeof(from), "<sip:%s@%s>;tag=%s", r->username, S_OR(r->regdomain, sip_sanitized_host(p->tohost)), p->tag);
- if (!ast_strlen_zero(p->theirtag)) {
- snprintf(to, sizeof(to), "<sip:%s@%s>;tag=%s", r->username, S_OR(r->regdomain, sip_sanitized_host(p->tohost)), p->theirtag);
- } else {
- snprintf(to, sizeof(to), "<sip:%s@%s>", r->username, S_OR(r->regdomain, sip_sanitized_host(p->tohost)));
- }
- /* Fromdomain is what we are registering to, regardless of actual
- host name from SRV */
- if (portno && portno != STANDARD_SIP_PORT) {
- snprintf(addr, sizeof(addr), "sip:%s:%d", S_OR(p->fromdomain,S_OR(r->regdomain, sip_sanitized_host(r->hostname))), portno);
- } else {
- snprintf(addr, sizeof(addr), "sip:%s", S_OR(p->fromdomain,S_OR(r->regdomain, sip_sanitized_host(r->hostname))));
- }
- ast_string_field_set(p, uri, addr);
- p->branch ^= ast_random();
- init_req(&req, sipmethod, addr);
- /* Add to CSEQ */
- snprintf(tmp, sizeof(tmp), "%u %s", ++r->ocseq, sip_methods[sipmethod].text);
- p->ocseq = r->ocseq;
- build_via(p);
- add_header(&req, "Via", p->via);
- add_max_forwards(p, &req);
- add_header(&req, "From", from);
- add_header(&req, "To", to);
- add_header(&req, "Call-ID", p->callid);
- add_header(&req, "CSeq", tmp);
- add_supported(p, &req);
- if (!ast_strlen_zero(global_useragent))
- add_header(&req, "User-Agent", global_useragent);
- if (auth) { /* Add auth header */
- add_header(&req, authheader, auth);
- } else if (!ast_strlen_zero(r->nonce)) {
- char digest[1024];
- /* We have auth data to reuse, build a digest header.
- * Note, this is not always useful because some parties do not
- * like nonces to be reused (for good reasons!) so they will
- * challenge us anyways.
- */
- if (sipdebug) {
- ast_debug(1, " >>> Re-using Auth data for %s@%s\n", r->username, r->hostname);
- }
- ast_string_field_set(p, realm, r->realm);
- ast_string_field_set(p, nonce, r->nonce);
- ast_string_field_set(p, domain, r->authdomain);
- ast_string_field_set(p, opaque, r->opaque);
- ast_string_field_set(p, qop, r->qop);
- p->noncecount = ++r->noncecount;
- memset(digest, 0, sizeof(digest));
- if(!build_reply_digest(p, sipmethod, digest, sizeof(digest))) {
- add_header(&req, "Authorization", digest);
- } else {
- ast_log(LOG_NOTICE, "No authorization available for authentication of registration to %s@%s\n", r->username, r->hostname);
- }
- }
- add_expires(&req, r->expiry);
- build_contact(p, &req, 0);
- add_header(&req, "Contact", p->our_contact);
- initialize_initreq(p, &req);
- if (sip_debug_test_pvt(p)) {
- ast_verbose("REGISTER %d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
- }
- r->regstate = auth ? REG_STATE_AUTHSENT : REG_STATE_REGSENT;
- r->regattempts++; /* Another attempt */
- ast_debug(4, "REGISTER attempt %d to %s@%s\n", r->regattempts, r->username, r->hostname);
- res = send_request(p, &req, XMIT_CRITICAL, p->ocseq);
- dialog_unref(p, "p is finished here at the end of transmit_register");
- return res;
- }
- /*!
- * \brief Transmit with SIP MESSAGE method
- * \note The p->msg_headers and p->msg_body are already setup.
- */
- static int transmit_message(struct sip_pvt *p, int init, int auth)
- {
- struct sip_request req;
- if (init) {
- initreqprep(&req, p, SIP_MESSAGE, NULL);
- initialize_initreq(p, &req);
- } else {
- reqprep(&req, p, SIP_MESSAGE, 0, 1);
- }
- if (auth) {
- return transmit_request_with_auth(p, SIP_MESSAGE, p->ocseq, XMIT_RELIABLE, 0);
- } else {
- add_text(&req, p);
- return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
- }
- }
- /*! \brief Allocate SIP refer structure */
- static int sip_refer_alloc(struct sip_pvt *p)
- {
- sip_refer_destroy(p);
- p->refer = ast_calloc_with_stringfields(1, struct sip_refer, 512);
- return p->refer ? 1 : 0;
- }
- /*! \brief Destroy SIP refer structure */
- static void sip_refer_destroy(struct sip_pvt *p)
- {
- if (p->refer) {
- ast_string_field_free_memory(p->refer);
- ast_free(p->refer);
- p->refer = NULL;
- }
- }
- /*! \brief Allocate SIP refer structure */
- static int sip_notify_alloc(struct sip_pvt *p)
- {
- p->notify = ast_calloc(1, sizeof(struct sip_notify));
- if (p->notify) {
- p->notify->content = ast_str_create(128);
- }
- return p->notify ? 1 : 0;
- }
- /*! \brief Transmit SIP REFER message (initiated by the transfer() dialplan application
- \note this is currently broken as we have no way of telling the dialplan
- engine whether a transfer succeeds or fails.
- \todo Fix the transfer() dialplan function so that a transfer may fail
- */
- static int transmit_refer(struct sip_pvt *p, const char *dest)
- {
- char from[256];
- const char *of;
- char *c;
- char referto[256];
- int use_tls=FALSE;
- if (sipdebug) {
- ast_debug(1, "SIP transfer of %s to %s\n", p->callid, dest);
- }
- /* Are we transfering an inbound or outbound call ? */
- if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- of = sip_get_header(&p->initreq, "To");
- } else {
- of = sip_get_header(&p->initreq, "From");
- }
- ast_copy_string(from, of, sizeof(from));
- of = get_in_brackets(from);
- ast_string_field_set(p, from, of);
- if (!strncasecmp(of, "sip:", 4)) {
- of += 4;
- } else if (!strncasecmp(of, "sips:", 5)) {
- of += 5;
- use_tls = TRUE;
- } else {
- ast_log(LOG_NOTICE, "From address missing 'sip(s):', assuming sip:\n");
- }
- /* Get just the username part */
- if (strchr(dest, '@')) {
- c = NULL;
- } else if ((c = strchr(of, '@'))) {
- *c++ = '\0';
- }
- if (c) {
- snprintf(referto, sizeof(referto), "<sip%s:%s@%s>", use_tls ? "s" : "", dest, c);
- } else {
- snprintf(referto, sizeof(referto), "<sip%s:%s>", use_tls ? "s" : "", dest);
- }
- /* save in case we get 407 challenge */
- sip_refer_alloc(p);
- ast_string_field_set(p->refer, refer_to, referto);
- ast_string_field_set(p->refer, referred_by, p->our_contact);
- p->refer->status = REFER_SENT; /* Set refer status */
- return transmit_invite(p, SIP_REFER, FALSE, 0, NULL);
- /* We should propably wait for a NOTIFY here until we ack the transfer */
- /* Maybe fork a new thread and wait for a STATUS of REFER_200OK on the refer status before returning to app_transfer */
- /*! \todo In theory, we should hang around and wait for a reply, before
- returning to the dial plan here. Don't know really how that would
- affect the transfer() app or the pbx, but, well, to make this
- useful we should have a STATUS code on transfer().
- */
- }
- /*! \brief Send SIP INFO advice of charge message */
- static int transmit_info_with_aoc(struct sip_pvt *p, struct ast_aoc_decoded *decoded)
- {
- struct sip_request req;
- struct ast_str *str = ast_str_alloca(512);
- const struct ast_aoc_unit_entry *unit_entry = ast_aoc_get_unit_info(decoded, 0);
- enum ast_aoc_charge_type charging = ast_aoc_get_charge_type(decoded);
- reqprep(&req, p, SIP_INFO, 0, 1);
- if (ast_aoc_get_msg_type(decoded) == AST_AOC_D) {
- ast_str_append(&str, 0, "type=active;");
- } else if (ast_aoc_get_msg_type(decoded) == AST_AOC_E) {
- ast_str_append(&str, 0, "type=terminated;");
- } else {
- /* unsupported message type */
- return -1;
- }
- switch (charging) {
- case AST_AOC_CHARGE_FREE:
- ast_str_append(&str, 0, "free-of-charge;");
- break;
- case AST_AOC_CHARGE_CURRENCY:
- ast_str_append(&str, 0, "charging;");
- ast_str_append(&str, 0, "charging-info=currency;");
- ast_str_append(&str, 0, "amount=%u;", ast_aoc_get_currency_amount(decoded));
- ast_str_append(&str, 0, "multiplier=%s;", ast_aoc_get_currency_multiplier_decimal(decoded));
- if (!ast_strlen_zero(ast_aoc_get_currency_name(decoded))) {
- ast_str_append(&str, 0, "currency=%s;", ast_aoc_get_currency_name(decoded));
- }
- break;
- case AST_AOC_CHARGE_UNIT:
- ast_str_append(&str, 0, "charging;");
- ast_str_append(&str, 0, "charging-info=pulse;");
- if (unit_entry) {
- ast_str_append(&str, 0, "recorded-units=%u;", unit_entry->amount);
- }
- break;
- default:
- ast_str_append(&str, 0, "not-available;");
- };
- add_header(&req, "AOC", ast_str_buffer(str));
- return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
- }
- /*! \brief Send SIP INFO dtmf message, see Cisco documentation on cisco.com */
- static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration)
- {
- struct sip_request req;
- reqprep(&req, p, SIP_INFO, 0, 1);
- add_digit(&req, digit, duration, (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_SHORTINFO));
- return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
- }
- /*! \brief Send SIP INFO with video update request */
- static int transmit_info_with_vidupdate(struct sip_pvt *p)
- {
- struct sip_request req;
- reqprep(&req, p, SIP_INFO, 0, 1);
- add_vidupdate(&req);
- return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
- }
- /*! \brief Transmit generic SIP request
- returns XMIT_ERROR if transmit failed with a critical error (don't retry)
- */
- static int transmit_request(struct sip_pvt *p, int sipmethod, uint32_t seqno, enum xmittype reliable, int newbranch)
- {
- struct sip_request resp;
- reqprep(&resp, p, sipmethod, seqno, newbranch);
- if (sipmethod == SIP_CANCEL && p->answered_elsewhere) {
- add_header(&resp, "Reason", "SIP;cause=200;text=\"Call completed elsewhere\"");
- }
- if (sipmethod == SIP_ACK) {
- p->invitestate = INV_CONFIRMED;
- }
- return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);
- }
- /*! \brief return the request and response header for a 401 or 407 code */
- void sip_auth_headers(enum sip_auth_type code, char **header, char **respheader)
- {
- if (code == WWW_AUTH) { /* 401 */
- *header = "WWW-Authenticate";
- *respheader = "Authorization";
- } else if (code == PROXY_AUTH) { /* 407 */
- *header = "Proxy-Authenticate";
- *respheader = "Proxy-Authorization";
- } else {
- ast_verbose("-- wrong response code %u\n", code);
- *header = *respheader = "Invalid";
- }
- }
- /*! \brief Transmit SIP request, auth added */
- static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, uint32_t seqno, enum xmittype reliable, int newbranch)
- {
- struct sip_request resp;
- reqprep(&resp, p, sipmethod, seqno, newbranch);
- if (!ast_strlen_zero(p->realm)) {
- char digest[1024];
- memset(digest, 0, sizeof(digest));
- if(!build_reply_digest(p, sipmethod, digest, sizeof(digest))) {
- char *dummy, *response;
- enum sip_auth_type code = p->options ? p->options->auth_type : PROXY_AUTH; /* XXX force 407 if unknown */
- sip_auth_headers(code, &dummy, &response);
- add_header(&resp, response, digest);
- } else {
- ast_log(LOG_WARNING, "No authentication available for call %s\n", p->callid);
- }
- }
- switch (sipmethod) {
- case SIP_BYE:
- {
- char buf[20];
- /*
- * We are hanging up. If we know a cause for that, send it in
- * clear text to make debugging easier.
- */
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_Q850_REASON) && p->hangupcause) {
- snprintf(buf, sizeof(buf), "Q.850;cause=%d", p->hangupcause & 0x7f);
- add_header(&resp, "Reason", buf);
- }
- add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->hangupcause));
- snprintf(buf, sizeof(buf), "%d", p->hangupcause);
- add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
- break;
- }
- case SIP_MESSAGE:
- add_text(&resp, p);
- break;
- default:
- break;
- }
- return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);
- }
- /*! \brief Remove registration data from realtime database or AST/DB when registration expires */
- static void destroy_association(struct sip_peer *peer)
- {
- int realtimeregs = ast_check_realtime("sipregs");
- char *tablename = (realtimeregs) ? "sipregs" : "sippeers";
- if (!sip_cfg.ignore_regexpire) {
- if (peer->rt_fromcontact && sip_cfg.peer_rtupdate) {
- ast_update_realtime(tablename, "name", peer->name, "fullcontact", "", "ipaddr", "", "port", "0", "regseconds", "0", "regserver", "", "useragent", "", "lastms", "0", SENTINEL);
- } else {
- ast_db_del("SIP/Registry", peer->name);
- ast_db_del("SIP/RegistryPath", peer->name);
- ast_db_del("SIP/PeerMethods", peer->name);
- }
- }
- }
- static void set_socket_transport(struct sip_socket *socket, int transport)
- {
- /* if the transport type changes, clear all socket data */
- if (socket->type != transport) {
- socket->fd = -1;
- socket->type = transport;
- if (socket->tcptls_session) {
- ao2_ref(socket->tcptls_session, -1);
- socket->tcptls_session = NULL;
- } else if (socket->ws_session) {
- ast_websocket_unref(socket->ws_session);
- socket->ws_session = NULL;
- }
- }
- }
- /*! \brief Expire registration of SIP peer */
- static int expire_register(const void *data)
- {
- struct sip_peer *peer = (struct sip_peer *)data;
- if (!peer) { /* Hmmm. We have no peer. Weird. */
- return 0;
- }
- peer->expire = -1;
- peer->portinuri = 0;
- destroy_association(peer); /* remove registration data from storage */
- set_socket_transport(&peer->socket, peer->default_outbound_transport);
- if (peer->socket.tcptls_session) {
- ao2_ref(peer->socket.tcptls_session, -1);
- peer->socket.tcptls_session = NULL;
- } else if (peer->socket.ws_session) {
- ast_websocket_unref(peer->socket.ws_session);
- peer->socket.ws_session = NULL;
- }
- if (peer->endpoint) {
- RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
- ast_endpoint_set_state(peer->endpoint, AST_ENDPOINT_OFFLINE);
- blob = ast_json_pack("{s: s, s: s}",
- "peer_status", "Unregistered",
- "cause", "Expired");
- ast_endpoint_blob_publish(peer->endpoint, ast_endpoint_state_type(), blob);
- }
- register_peer_exten(peer, FALSE); /* Remove regexten */
- ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name);
- /* Do we need to release this peer from memory?
- Only for realtime peers and autocreated peers
- */
- if (peer->is_realtime) {
- ast_debug(3, "-REALTIME- peer expired registration. Name: %s. Realtime peer objects now %d\n", peer->name, rpeerobjs);
- }
- if (peer->selfdestruct ||
- ast_test_flag(&peer->flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
- ao2_t_unlink(peers, peer, "ao2_unlink of peer from peers table");
- }
- if (!ast_sockaddr_isnull(&peer->addr)) {
- /* We still need to unlink the peer from the peers_by_ip table,
- * otherwise we end up with multiple copies hanging around each
- * time a registration expires and the peer re-registers. */
- ao2_t_unlink(peers_by_ip, peer, "ao2_unlink of peer from peers_by_ip table");
- }
- /* Only clear the addr after we check for destruction. The addr must remain
- * in order to unlink from the peers_by_ip container correctly */
- memset(&peer->addr, 0, sizeof(peer->addr));
- sip_unref_peer(peer, "removing peer ref for expire_register");
- return 0;
- }
- /*! \brief Poke peer (send qualify to check if peer is alive and well) */
- static int sip_poke_peer_s(const void *data)
- {
- struct sip_peer *peer = (struct sip_peer *)data;
- struct sip_peer *foundpeer;
- peer->pokeexpire = -1;
- foundpeer = ao2_find(peers, peer, OBJ_POINTER);
- if (!foundpeer) {
- sip_unref_peer(peer, "removing poke peer ref");
- return 0;
- } else if (foundpeer->name != peer->name) {
- sip_unref_peer(foundpeer, "removing above peer ref");
- sip_unref_peer(peer, "removing poke peer ref");
- return 0;
- }
- sip_unref_peer(foundpeer, "removing above peer ref");
- sip_poke_peer(peer, 0);
- sip_unref_peer(peer, "removing poke peer ref");
- return 0;
- }
- static int sip_poke_peer_now(const void *data)
- {
- struct sip_peer *peer = (struct sip_peer *) data;
- peer->pokeexpire = -1;
- sip_poke_peer(peer, 0);
- sip_unref_peer(peer, "removing poke peer ref");
- return 0;
- }
- /*! \brief Get registration details from Asterisk DB */
- static void reg_source_db(struct sip_peer *peer)
- {
- char data[256];
- char path[SIPBUFSIZE * 2];
- struct ast_sockaddr sa;
- int expire;
- char full_addr[128];
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(addr);
- AST_APP_ARG(port);
- AST_APP_ARG(expiry_str);
- AST_APP_ARG(username);
- AST_APP_ARG(contact);
- );
- /* If read-only RT backend, then refresh from local DB cache */
- if (peer->rt_fromcontact && sip_cfg.peer_rtupdate) {
- return;
- }
- if (ast_db_get("SIP/Registry", peer->name, data, sizeof(data))) {
- return;
- }
- AST_NONSTANDARD_RAW_ARGS(args, data, ':');
- snprintf(full_addr, sizeof(full_addr), "%s:%s", args.addr, args.port);
- if (!ast_sockaddr_parse(&sa, full_addr, 0)) {
- return;
- }
- if (args.expiry_str) {
- expire = atoi(args.expiry_str);
- } else {
- return;
- }
- if (args.username) {
- ast_string_field_set(peer, username, args.username);
- }
- if (args.contact) {
- ast_string_field_set(peer, fullcontact, args.contact);
- }
- ast_debug(2, "SIP Seeding peer from astdb: '%s' at %s@%s for %d\n",
- peer->name, peer->username, ast_sockaddr_stringify_host(&sa), expire);
- ast_sockaddr_copy(&peer->addr, &sa);
- if (peer->maxms) {
- /* Don't poke peer immediately, just schedule it within qualifyfreq */
- AST_SCHED_REPLACE_UNREF(peer->pokeexpire, sched,
- ast_random() % ((peer->qualifyfreq) ? peer->qualifyfreq : global_qualifyfreq) + 1,
- sip_poke_peer_s, peer,
- sip_unref_peer(_data, "removing poke peer ref"),
- sip_unref_peer(peer, "removing poke peer ref"),
- sip_ref_peer(peer, "adding poke peer ref"));
- }
- AST_SCHED_REPLACE_UNREF(peer->expire, sched, (expire + 10) * 1000, expire_register, peer,
- sip_unref_peer(_data, "remove registration ref"),
- sip_unref_peer(peer, "remove registration ref"),
- sip_ref_peer(peer, "add registration ref"));
- register_peer_exten(peer, TRUE);
- if (!ast_db_get("SIP/RegistryPath", peer->name, path, sizeof(path))) {
- build_path(NULL, peer, NULL, path);
- }
- }
- /*! \brief Save contact header for 200 OK on INVITE */
- static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req)
- {
- char contact[SIPBUFSIZE];
- char *c;
- /* Look for brackets */
- ast_copy_string(contact, sip_get_header(req, "Contact"), sizeof(contact));
- c = get_in_brackets(contact);
- /* Save full contact to call pvt for later bye or re-invite */
- ast_string_field_set(pvt, fullcontact, c);
- /* Save URI for later ACKs, BYE or RE-invites */
- ast_string_field_set(pvt, okcontacturi, c);
- /* We should return false for URI:s we can't handle,
- like tel:, mailto:,ldap: etc */
- return TRUE;
- }
- /*! \brief parse uri in a way that allows semicolon stripping if legacy mode is enabled
- *
- * \note This calls parse_uri which has the unexpected property that passing more
- * arguments results in more splitting. Most common is to leave out the pass
- * argument, causing user to contain user:pass if available.
- */
- static int parse_uri_legacy_check(char *uri, const char *scheme, char **user, char **pass, char **hostport, char **transport)
- {
- int ret = parse_uri(uri, scheme, user, pass, hostport, transport);
- if (sip_cfg.legacy_useroption_parsing) { /* if legacy mode is active, strip semis from the user field */
- char *p;
- if ((p = strchr(uri, (int)';'))) {
- *p = '\0';
- }
- }
- return ret;
- }
- static int __set_address_from_contact(const char *fullcontact, struct ast_sockaddr *addr, int tcp)
- {
- char *hostport, *transport;
- char contact_buf[256];
- char *contact;
- /* Work on a copy */
- ast_copy_string(contact_buf, fullcontact, sizeof(contact_buf));
- contact = contact_buf;
- /*
- * We have only the part in <brackets> here so we just need to parse a SIP URI.
- *
- * Note: The outbound proxy could be using UDP between the proxy and Asterisk.
- * We still need to be able to send to the remote agent through the proxy.
- */
- if (parse_uri_legacy_check(contact, "sip:,sips:", &contact, NULL, &hostport,
- &transport)) {
- ast_log(LOG_WARNING, "Invalid contact uri %s (missing sip: or sips:), attempting to use anyway\n", fullcontact);
- }
- /* XXX This could block for a long time XXX */
- /* We should only do this if it's a name, not an IP */
- /* \todo - if there's no PORT number in contact - we are required to check NAPTR/SRV records
- to find transport, port address and hostname. If there's a port number, we have to
- assume that the hostport part is a host name and only look for an A/AAAA record in DNS.
- */
- /* If we took in an invalid URI, hostport may not have been initialized */
- /* ast_sockaddr_resolve requires an initialized hostport string. */
- if (ast_strlen_zero(hostport)) {
- ast_log(LOG_WARNING, "Invalid URI: parse_uri failed to acquire hostport\n");
- return -1;
- }
- if (ast_sockaddr_resolve_first_transport(addr, hostport, 0, get_transport_str2enum(transport))) {
- ast_log(LOG_WARNING, "Invalid host name in Contact: (can't "
- "resolve in DNS) : '%s'\n", hostport);
- return -1;
- }
- /* set port */
- if (!ast_sockaddr_port(addr)) {
- ast_sockaddr_set_port(addr,
- (get_transport_str2enum(transport) ==
- AST_TRANSPORT_TLS ||
- !strncasecmp(fullcontact, "sips", 4)) ?
- STANDARD_TLS_PORT : STANDARD_SIP_PORT);
- }
- return 0;
- }
- /*! \brief Change the other partys IP address based on given contact */
- static int set_address_from_contact(struct sip_pvt *pvt)
- {
- if (ast_test_flag(&pvt->flags[0], SIP_NAT_FORCE_RPORT)) {
- /* NAT: Don't trust the contact field. Just use what they came to us
- with. */
- /*! \todo We need to save the TRANSPORT here too */
- pvt->sa = pvt->recv;
- return 0;
- }
- return __set_address_from_contact(pvt->fullcontact, &pvt->sa, pvt->socket.type == AST_TRANSPORT_TLS ? 1 : 0);
- }
- /*! \brief Parse contact header and save registration (peer registration) */
- static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *peer, struct sip_request *req)
- {
- char contact[SIPBUFSIZE];
- char data[SIPBUFSIZE];
- const char *expires = sip_get_header(req, "Expires");
- int expire = atoi(expires);
- char *curi = NULL, *hostport = NULL, *transport = NULL;
- int transport_type;
- const char *useragent;
- struct ast_sockaddr oldsin, testsa;
- char *firstcuri = NULL;
- int start = 0;
- int wildcard_found = 0;
- int single_binding_found = 0;
- ast_copy_string(contact, __get_header(req, "Contact", &start), sizeof(contact));
- if (ast_strlen_zero(expires)) { /* No expires header, try look in Contact: */
- char *s = strcasestr(contact, ";expires=");
- if (s) {
- expires = strsep(&s, ";"); /* trim ; and beyond */
- if (sscanf(expires + 9, "%30d", &expire) != 1) {
- expire = default_expiry;
- }
- } else {
- /* Nothing has been specified */
- expire = default_expiry;
- }
- }
- if (expire > max_expiry) {
- expire = max_expiry;
- }
- if (expire < min_expiry && expire != 0) {
- expire = min_expiry;
- }
- pvt->expiry = expire;
- copy_socket_data(&pvt->socket, &req->socket);
- do {
- /* Look for brackets */
- curi = contact;
- if (strchr(contact, '<') == NULL) /* No <, check for ; and strip it */
- strsep(&curi, ";"); /* This is Header options, not URI options */
- curi = get_in_brackets(contact);
- if (!firstcuri) {
- firstcuri = ast_strdupa(curi);
- }
- if (!strcasecmp(curi, "*")) {
- wildcard_found = 1;
- } else {
- single_binding_found = 1;
- }
- if (wildcard_found && (ast_strlen_zero(expires) || expire != 0 || single_binding_found)) {
- /* Contact header parameter "*" detected, so punt if: Expires header is missing,
- * Expires value is not zero, or another Contact header is present. */
- return PARSE_REGISTER_FAILED;
- }
- ast_copy_string(contact, __get_header(req, "Contact", &start), sizeof(contact));
- } while (!ast_strlen_zero(contact));
- curi = firstcuri;
- /* if they did not specify Contact: or Expires:, they are querying
- what we currently have stored as their contact address, so return
- it
- */
- if (ast_strlen_zero(curi) && ast_strlen_zero(expires)) {
- /* If we have an active registration, tell them when the registration is going to expire */
- if (peer->expire > -1 && !ast_strlen_zero(peer->fullcontact)) {
- pvt->expiry = ast_sched_when(sched, peer->expire);
- }
- return PARSE_REGISTER_QUERY;
- } else if (!strcasecmp(curi, "*") || !expire) { /* Unregister this peer */
- /* This means remove all registrations and return OK */
- AST_SCHED_DEL_UNREF(sched, peer->expire,
- sip_unref_peer(peer, "remove register expire ref"));
- ast_verb(3, "Unregistered SIP '%s'\n", peer->name);
- expire_register(sip_ref_peer(peer,"add ref for explicit expire_register"));
- return PARSE_REGISTER_UPDATE;
- }
- /* Store whatever we got as a contact from the client */
- ast_string_field_set(peer, fullcontact, curi);
- /* For the 200 OK, we should use the received contact */
- ast_string_field_build(pvt, our_contact, "<%s>", curi);
- /* Make sure it's a SIP URL */
- if (ast_strlen_zero(curi) || parse_uri_legacy_check(curi, "sip:,sips:", &curi, NULL, &hostport, &transport)) {
- ast_log(LOG_NOTICE, "Not a valid SIP contact (missing sip:/sips:) trying to use anyway\n");
- }
- /* handle the transport type specified in Contact header. */
- if (!(transport_type = get_transport_str2enum(transport))) {
- transport_type = pvt->socket.type;
- }
- /* if the peer's socket type is different than the Registration
- * transport type, change it. If it got this far, it is a
- * supported type, but check just in case */
- if ((peer->socket.type != transport_type) && (peer->transports & transport_type)) {
- set_socket_transport(&peer->socket, transport_type);
- }
- oldsin = peer->addr;
- /* If we were already linked into the peers_by_ip container unlink ourselves so nobody can find us */
- if (!ast_sockaddr_isnull(&peer->addr) && (!peer->is_realtime || ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS))) {
- ao2_t_unlink(peers_by_ip, peer, "ao2_unlink of peer from peers_by_ip table");
- }
- if ((transport_type != AST_TRANSPORT_WS) && (transport_type != AST_TRANSPORT_WSS) &&
- (!ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) && !ast_test_flag(&peer->flags[0], SIP_NAT_RPORT_PRESENT))) {
- /* use the data provided in the Contact header for call routing */
- ast_debug(1, "Store REGISTER's Contact header for call routing.\n");
- /* XXX This could block for a long time XXX */
- /*! \todo Check NAPTR/SRV if we have not got a port in the URI */
- if (ast_sockaddr_resolve_first_transport(&testsa, hostport, 0, peer->socket.type)) {
- ast_log(LOG_WARNING, "Invalid hostport '%s'\n", hostport);
- ast_string_field_set(peer, fullcontact, "");
- ast_string_field_set(pvt, our_contact, "");
- return PARSE_REGISTER_FAILED;
- }
- /* If we have a port number in the given URI, make sure we do remember to not check for NAPTR/SRV records.
- The hostport part is actually a host. */
- peer->portinuri = ast_sockaddr_port(&testsa) ? TRUE : FALSE;
- if (!ast_sockaddr_port(&testsa)) {
- ast_sockaddr_set_port(&testsa, default_sip_port(transport_type));
- }
- ast_sockaddr_copy(&peer->addr, &testsa);
- } else {
- /* Don't trust the contact field. Just use what they came to us
- with */
- ast_debug(1, "Store REGISTER's src-IP:port for call routing.\n");
- peer->addr = pvt->recv;
- }
- /* Check that they're allowed to register at this IP */
- if (ast_apply_acl(sip_cfg.contact_acl, &peer->addr, "SIP contact ACL: ") != AST_SENSE_ALLOW ||
- ast_apply_acl(peer->contactacl, &peer->addr, "SIP contact ACL: ") != AST_SENSE_ALLOW) {
- ast_log(LOG_WARNING, "Domain '%s' disallowed by contact ACL (violating IP %s)\n", hostport,
- ast_sockaddr_stringify_addr(&peer->addr));
- ast_string_field_set(peer, fullcontact, "");
- ast_string_field_set(pvt, our_contact, "");
- return PARSE_REGISTER_DENIED;
- }
- /* if the Contact header information copied into peer->addr matches the
- * received address, and the transport types are the same, then copy socket
- * data into the peer struct */
- if ((peer->socket.type == pvt->socket.type) &&
- !ast_sockaddr_cmp(&peer->addr, &pvt->recv)) {
- copy_socket_data(&peer->socket, &pvt->socket);
- }
- /* Now that our address has been updated put ourselves back into the container for lookups */
- if (!peer->is_realtime || ast_test_flag(&peer->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
- ao2_t_link(peers_by_ip, peer, "ao2_link into peers_by_ip table");
- }
- /* Save SIP options profile */
- peer->sipoptions = pvt->sipoptions;
- if (!ast_strlen_zero(curi) && ast_strlen_zero(peer->username)) {
- ast_string_field_set(peer, username, curi);
- }
- AST_SCHED_DEL_UNREF(sched, peer->expire,
- sip_unref_peer(peer, "remove register expire ref"));
- if (peer->is_realtime && !ast_test_flag(&peer->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
- peer->expire = -1;
- } else {
- peer->expire = ast_sched_add(sched, (expire + 10) * 1000, expire_register,
- sip_ref_peer(peer, "add registration ref"));
- if (peer->expire == -1) {
- sip_unref_peer(peer, "remote registration ref");
- }
- }
- if (!build_path(pvt, peer, req, NULL)) {
- /* Tell the dialog to use the Path header in the response */
- ast_set2_flag(&pvt->flags[0], 1, SIP_USEPATH);
- }
- snprintf(data, sizeof(data), "%s:%d:%s:%s", ast_sockaddr_stringify(&peer->addr),
- expire, peer->username, peer->fullcontact);
- /* We might not immediately be able to reconnect via TCP, but try caching it anyhow */
- if (!peer->rt_fromcontact || !sip_cfg.peer_rtupdate) {
- if (!sip_route_empty(&peer->path)) {
- struct ast_str *r = sip_route_list(&peer->path, 0, 0);
- if (r) {
- ast_db_put("SIP/RegistryPath", peer->name, ast_str_buffer(r));
- ast_free(r);
- }
- }
- ast_db_put("SIP/Registry", peer->name, data);
- }
- if (peer->endpoint) {
- RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
- ast_endpoint_set_state(peer->endpoint, AST_ENDPOINT_ONLINE);
- blob = ast_json_pack("{s: s, s: s}",
- "peer_status", "Registered",
- "address", ast_sockaddr_stringify(&peer->addr));
- ast_endpoint_blob_publish(peer->endpoint, ast_endpoint_state_type(), blob);
- }
- /* Is this a new IP address for us? */
- if (ast_sockaddr_cmp(&peer->addr, &oldsin)) {
- ast_verb(3, "Registered SIP '%s' at %s\n", peer->name,
- ast_sockaddr_stringify(&peer->addr));
- }
- sip_pvt_unlock(pvt);
- sip_poke_peer(peer, 0);
- sip_pvt_lock(pvt);
- register_peer_exten(peer, 1);
- /* Save User agent */
- useragent = sip_get_header(req, "User-Agent");
- if (strcasecmp(useragent, peer->useragent)) {
- ast_string_field_set(peer, useragent, useragent);
- ast_verb(4, "Saved useragent \"%s\" for peer %s\n", peer->useragent, peer->name);
- }
- return PARSE_REGISTER_UPDATE;
- }
- /*! \brief Build route list from Record-Route header
- *
- * \param p
- * \param req
- * \param backwards
- * \param resp the SIP response code or 0 for a request
- *
- */
- static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards, int resp)
- {
- int start = 0;
- const char *header;
- /* Once a persistent route is set, don't fool with it */
- if (!sip_route_empty(&p->route) && p->route_persistent) {
- ast_debug(1, "build_route: Retaining previous route: <%s>\n", sip_route_first_uri(&p->route));
- return;
- }
- sip_route_clear(&p->route);
- /* We only want to create the route set the first time this is called except
- it is called from a provisional response.*/
- if ((resp < 100) || (resp > 199)) {
- p->route_persistent = 1;
- }
- /* Build a tailq, then assign it to p->route when done.
- * If backwards, we add entries from the head so they end up
- * in reverse order. However, we do need to maintain a correct
- * tail pointer because the contact is always at the end.
- */
- /* 1st we pass through all the hops in any Record-Route headers */
- for (;;) {
- header = __get_header(req, "Record-Route", &start);
- if (*header == '\0') {
- break;
- }
- sip_route_process_header(&p->route, header, backwards);
- }
- /* Only append the contact if we are dealing with a strict router or have no route */
- if (sip_route_empty(&p->route) || sip_route_is_strict(&p->route)) {
- /* 2nd append the Contact: if there is one */
- /* Can be multiple Contact headers, comma separated values - we just take the first */
- int len;
- header = sip_get_header(req, "Contact");
- if (strchr(header, '<')) {
- get_in_brackets_const(header, &header, &len);
- } else {
- len = strlen(header);
- }
- if (header && len) {
- sip_route_add(&p->route, header, len, 0);
- }
- }
- /* For debugging dump what we ended up with */
- if (sip_debug_test_pvt(p)) {
- sip_route_dump(&p->route);
- }
- }
- /*! \brief Build route list from Path header
- * RFC 3327 requires that the Path header contains SIP URIs with lr paramter.
- * Thus, we do not care about strict routing SIP routers
- */
- static int build_path(struct sip_pvt *p, struct sip_peer *peer, struct sip_request *req, const char *pathbuf)
- {
- sip_route_clear(&peer->path);
- if (!ast_test_flag(&peer->flags[0], SIP_USEPATH)) {
- ast_debug(2, "build_path: do not use Path headers\n");
- return -1;
- }
- ast_debug(2, "build_path: try to build pre-loaded route-set by parsing Path headers\n");
- if (req) {
- int start = 0;
- const char *header;
- for (;;) {
- header = __get_header(req, "Path", &start);
- if (*header == '\0') {
- break;
- }
- sip_route_process_header(&peer->path, header, 0);
- }
- } else if (pathbuf) {
- sip_route_process_header(&peer->path, pathbuf, 0);
- }
- /* Caches result for any dialog->route copied from peer->path */
- sip_route_is_strict(&peer->path);
- /* For debugging dump what we ended up with */
- if (p && sip_debug_test_pvt(p)) {
- sip_route_dump(&peer->path);
- }
- return 0;
- }
- /*! \brief builds the sip_pvt's nonce field which is used for the authentication
- * challenge. When forceupdate is not set, the nonce is only updated if
- * the current one is stale. In this case, a stalenonce is one which
- * has already received a response, if a nonce has not received a response
- * it is not always necessary or beneficial to create a new one. */
- static void build_nonce(struct sip_pvt *p, int forceupdate)
- {
- if (p->stalenonce || forceupdate || ast_strlen_zero(p->nonce)) {
- ast_string_field_build(p, nonce, "%08lx", (unsigned long)ast_random()); /* Create nonce for challenge */
- p->stalenonce = 0;
- }
- }
- /*! \brief Takes the digest response and parses it */
- void sip_digest_parser(char *c, struct digestkeys *keys)
- {
- struct digestkeys *i = i;
- while(c && *(c = ast_skip_blanks(c)) ) { /* lookup for keys */
- for (i = keys; i->key != NULL; i++) {
- const char *separator = ","; /* default */
- if (strncasecmp(c, i->key, strlen(i->key)) != 0) {
- continue;
- }
- /* Found. Skip keyword, take text in quotes or up to the separator. */
- c += strlen(i->key);
- if (*c == '"') { /* in quotes. Skip first and look for last */
- c++;
- separator = "\"";
- }
- i->s = c;
- strsep(&c, separator);
- break;
- }
- if (i->key == NULL) { /* not found, jump after space or comma */
- strsep(&c, " ,");
- }
- }
- }
- /*! \brief Check user authorization from peer definition
- Some actions, like REGISTER and INVITEs from peers require
- authentication (if peer have secret set)
- \return 0 on success, non-zero on error
- */
- static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username,
- const char *secret, const char *md5secret, int sipmethod,
- const char *uri, enum xmittype reliable)
- {
- const char *response;
- char *reqheader, *respheader;
- const char *authtoken;
- char a1_hash[256];
- char resp_hash[256]="";
- char *c;
- int is_bogus_peer = 0;
- int wrongnonce = FALSE;
- int good_response;
- const char *usednonce = p->nonce;
- struct ast_str *buf;
- int res;
- /* table of recognised keywords, and their value in the digest */
- struct digestkeys keys[] = {
- [K_RESP] = { "response=", "" },
- [K_URI] = { "uri=", "" },
- [K_USER] = { "username=", "" },
- [K_NONCE] = { "nonce=", "" },
- [K_LAST] = { NULL, NULL}
- };
- /* Always OK if no secret */
- if (ast_strlen_zero(secret) && ast_strlen_zero(md5secret)) {
- return AUTH_SUCCESSFUL;
- }
- /* Always auth with WWW-auth since we're NOT a proxy */
- /* Using proxy-auth in a B2BUA may block proxy authorization in the same transaction */
- response = "401 Unauthorized";
- /*
- * Note the apparent swap of arguments below, compared to other
- * usages of sip_auth_headers().
- */
- sip_auth_headers(WWW_AUTH, &respheader, &reqheader);
- authtoken = sip_get_header(req, reqheader);
- if (req->ignore && !ast_strlen_zero(p->nonce) && ast_strlen_zero(authtoken)) {
- /* This is a retransmitted invite/register/etc, don't reconstruct authentication
- information */
- if (!reliable) {
- /* Resend message if this was NOT a reliable delivery. Otherwise the
- retransmission should get it */
- transmit_response_with_auth(p, response, req, p->nonce, reliable, respheader, 0);
- /* Schedule auto destroy in 32 seconds (according to RFC 3261) */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- }
- return AUTH_CHALLENGE_SENT;
- } else if (ast_strlen_zero(p->nonce) || ast_strlen_zero(authtoken)) {
- /* We have no auth, so issue challenge and request authentication */
- build_nonce(p, 1); /* Create nonce for challenge */
- transmit_response_with_auth(p, response, req, p->nonce, reliable, respheader, 0);
- /* Schedule auto destroy in 32 seconds */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return AUTH_CHALLENGE_SENT;
- }
- /* --- We have auth, so check it */
- /* Whoever came up with the authentication section of SIP can suck my %&#$&* for not putting
- an example in the spec of just what it is you're doing a hash on. */
- if (!(buf = ast_str_thread_get(&check_auth_buf, CHECK_AUTH_BUF_INITLEN))) {
- return AUTH_SECRET_FAILED; /*! XXX \todo need a better return code here */
- }
- /* Make a copy of the response and parse it */
- res = ast_str_set(&buf, 0, "%s", authtoken);
- if (res == AST_DYNSTR_BUILD_FAILED) {
- return AUTH_SECRET_FAILED; /*! XXX \todo need a better return code here */
- }
- c = ast_str_buffer(buf);
- sip_digest_parser(c, keys);
- /* We cannot rely on the bogus_peer having a bad md5 value. Someone could
- * use it to construct valid auth. */
- if (md5secret && strcmp(md5secret, BOGUS_PEER_MD5SECRET) == 0) {
- is_bogus_peer = 1;
- }
- /* Verify that digest username matches the username we auth as */
- if (strcmp(username, keys[K_USER].s) && !is_bogus_peer) {
- ast_log(LOG_WARNING, "username mismatch, have <%s>, digest has <%s>\n",
- username, keys[K_USER].s);
- /* Oops, we're trying something here */
- return AUTH_USERNAME_MISMATCH;
- }
- /* Verify nonce from request matches our nonce, and the nonce has not already been responded to.
- * If this check fails, send 401 with new nonce */
- if (strcasecmp(p->nonce, keys[K_NONCE].s) || p->stalenonce) { /* XXX it was 'n'casecmp ? */
- wrongnonce = TRUE;
- usednonce = keys[K_NONCE].s;
- } else {
- p->stalenonce = 1; /* now, since the nonce has a response, mark it as stale so it can't be sent or responded to again */
- }
- if (!ast_strlen_zero(md5secret)) {
- ast_copy_string(a1_hash, md5secret, sizeof(a1_hash));
- } else {
- char a1[256];
- snprintf(a1, sizeof(a1), "%s:%s:%s", username, p->realm, secret);
- ast_md5_hash(a1_hash, a1);
- }
- /* compute the expected response to compare with what we received */
- {
- char a2[256];
- char a2_hash[256];
- char resp[256];
- snprintf(a2, sizeof(a2), "%s:%s", sip_methods[sipmethod].text,
- S_OR(keys[K_URI].s, uri));
- ast_md5_hash(a2_hash, a2);
- snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, usednonce, a2_hash);
- ast_md5_hash(resp_hash, resp);
- }
- good_response = keys[K_RESP].s &&
- !strncasecmp(keys[K_RESP].s, resp_hash, strlen(resp_hash)) &&
- !is_bogus_peer; /* lastly, check that the peer isn't the fake peer */
- if (wrongnonce) {
- if (good_response) {
- if (sipdebug)
- ast_log(LOG_NOTICE, "Correct auth, but based on stale nonce received from '%s'\n", sip_get_header(req, "From"));
- /* We got working auth token, based on stale nonce . */
- build_nonce(p, 0);
- transmit_response_with_auth(p, response, req, p->nonce, reliable, respheader, TRUE);
- } else {
- /* Everything was wrong, so give the device one more try with a new challenge */
- if (!req->ignore) {
- if (sipdebug) {
- ast_log(LOG_NOTICE, "Bad authentication received from '%s'\n", sip_get_header(req, "To"));
- }
- build_nonce(p, 1);
- } else {
- if (sipdebug) {
- ast_log(LOG_NOTICE, "Duplicate authentication received from '%s'\n", sip_get_header(req, "To"));
- }
- }
- transmit_response_with_auth(p, response, req, p->nonce, reliable, respheader, FALSE);
- }
- /* Schedule auto destroy in 32 seconds */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return AUTH_CHALLENGE_SENT;
- }
- if (good_response) {
- append_history(p, "AuthOK", "Auth challenge successful for %s", username);
- return AUTH_SUCCESSFUL;
- }
- /* Ok, we have a bad username/secret pair */
- /* Tell the UAS not to re-send this authentication data, because
- it will continue to fail
- */
- return AUTH_SECRET_FAILED;
- }
- /*! \brief Change onhold state of a peer using a pvt structure */
- static void sip_peer_hold(struct sip_pvt *p, int hold)
- {
- if (!p->relatedpeer) {
- return;
- }
- /* If they put someone on hold, increment the value... otherwise decrement it */
- ast_atomic_fetchadd_int(&p->relatedpeer->onhold, (hold ? +1 : -1));
- /* Request device state update */
- ast_devstate_changed(AST_DEVICE_UNKNOWN, (ast_test_flag(ast_channel_flags(p->owner), AST_FLAG_DISABLE_DEVSTATE_CACHE) ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE),
- "SIP/%s", p->relatedpeer->name);
- return;
- }
- /*! \brief Receive MWI events that we have subscribed to */
- static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
- {
- char *peer_name = userdata;
- struct sip_peer *peer = sip_find_peer(peer_name, NULL, TRUE, FINDALLDEVICES, FALSE, 0);
- if (stasis_subscription_final_message(sub, msg)) {
- if (peer) {
- /* configuration reloaded */
- return;
- }
- ast_free(peer_name);
- return;
- }
- if (peer && ast_mwi_state_type() == stasis_message_type(msg)) {
- sip_send_mwi_to_peer(peer, 0);
- }
- ao2_cleanup(peer);
- }
- static void network_change_stasis_subscribe(void)
- {
- if (!network_change_sub) {
- network_change_sub = stasis_subscribe(ast_system_topic(),
- network_change_stasis_cb, NULL);
- }
- }
- static void network_change_stasis_unsubscribe(void)
- {
- network_change_sub = stasis_unsubscribe_and_join(network_change_sub);
- }
- static void acl_change_stasis_subscribe(void)
- {
- if (!acl_change_sub) {
- acl_change_sub = stasis_subscribe(ast_security_topic(),
- acl_change_stasis_cb, NULL);
- }
- }
- static void acl_change_event_stasis_unsubscribe(void)
- {
- acl_change_sub = stasis_unsubscribe_and_join(acl_change_sub);
- }
- static int network_change_sched_cb(const void *data)
- {
- network_change_sched_id = -1;
- sip_send_all_registers();
- sip_send_all_mwi_subscriptions();
- return 0;
- }
- static void network_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
- {
- /* This callback is only concerned with network change messages from the system topic. */
- if (stasis_message_type(message) != ast_network_change_type()) {
- return;
- }
- ast_verb(1, "SIP, got a network change message, renewing all SIP registrations.\n");
- if (network_change_sched_id == -1) {
- network_change_sched_id = ast_sched_add(sched, 1000, network_change_sched_cb, NULL);
- }
- }
- static void cb_extensionstate_destroy(int id, void *data)
- {
- struct sip_pvt *p = data;
- dialog_unref(p, "the extensionstate containing this dialog ptr was destroyed");
- }
- /*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
- \note If you add an "hint" priority to the extension in the dial plan,
- you will get notifications on device state changes */
- static int extensionstate_update(const char *context, const char *exten, struct state_notify_data *data, struct sip_pvt *p, int force)
- {
- sip_pvt_lock(p);
- switch (data->state) {
- case AST_EXTENSION_DEACTIVATED: /* Retry after a while */
- case AST_EXTENSION_REMOVED: /* Extension is gone */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); /* Delete subscription in 32 secs */
- ast_verb(2, "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, data->state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username);
- p->subscribed = NONE;
- append_history(p, "Subscribestatus", "%s", data->state == AST_EXTENSION_REMOVED ? "HintRemoved" : "Deactivated");
- break;
- default: /* Tell user */
- if (force) {
- /* we must skip the next two checks for a queued state change or resubscribe */
- } else if ((p->laststate == data->state && (~data->state & AST_EXTENSION_RINGING)) &&
- (p->last_presence_state == data->presence_state &&
- !strcmp(p->last_presence_subtype, data->presence_subtype) &&
- !strcmp(p->last_presence_message, data->presence_message))) {
- /* don't notify unchanged state or unchanged early-state causing parties again */
- sip_pvt_unlock(p);
- return 0;
- } else if (data->state & AST_EXTENSION_RINGING) {
- /* check if another channel than last time is ringing now to be notified */
- struct ast_channel *ringing = find_ringing_channel(data->device_state_info, p);
- if (ringing) {
- if (!ast_tvcmp(ast_channel_creationtime(ringing), p->last_ringing_channel_time)) {
- /* we assume here that no two channels have the exact same creation time */
- ao2_ref(ringing, -1);
- sip_pvt_unlock(p);
- return 0;
- } else {
- p->last_ringing_channel_time = ast_channel_creationtime(ringing);
- ao2_ref(ringing, -1);
- }
- }
- /* If no ringing channel was found, it doesn't necessarily indicate anything bad.
- * Likely, a device state change occurred for a custom device state, which does not
- * correspond to any channel. In such a case, just go ahead and pass the notification
- * along.
- */
- }
- /* ref before unref because the new could be the same as the old one. Don't risk destruction! */
- if (data->device_state_info) {
- ao2_ref(data->device_state_info, 1);
- }
- ao2_cleanup(p->last_device_state_info);
- p->last_device_state_info = data->device_state_info;
- p->laststate = data->state;
- p->last_presence_state = data->presence_state;
- ast_string_field_set(p, last_presence_subtype, S_OR(data->presence_subtype, ""));
- ast_string_field_set(p, last_presence_message, S_OR(data->presence_message, ""));
- break;
- }
- if (p->subscribed != NONE) { /* Only send state NOTIFY if we know the format */
- if (!p->pendinginvite) {
- transmit_state_notify(p, data, 1, FALSE);
- if (p->last_device_state_info) {
- /* We don't need the saved ref anymore, don't keep channels ref'd. */
- ao2_ref(p->last_device_state_info, -1);
- p->last_device_state_info = NULL;
- }
- } else {
- /* We already have a NOTIFY sent that is not answered. Queue the state up.
- if many state changes happen meanwhile, we will only send a notification of the last one */
- ast_set_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE);
- }
- }
- if (!force) {
- ast_verb(2, "Extension Changed %s[%s] new state %s for Notify User %s %s\n", exten, context, ast_extension_state2str(data->state), p->username,
- ast_test_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE) ? "(queued)" : "");
- }
- sip_pvt_unlock(p);
- return 0;
- }
- /*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
- \note If you add an "hint" priority to the extension in the dial plan,
- you will get notifications on device state changes */
- static int cb_extensionstate(char *context, char *exten, struct ast_state_cb_info *info, void *data)
- {
- struct sip_pvt *p = data;
- struct state_notify_data notify_data = {
- .state = info->exten_state,
- .device_state_info = info->device_state_info,
- .presence_state = info->presence_state,
- .presence_subtype = info->presence_subtype,
- .presence_message = info->presence_message,
- };
- if ((info->reason == AST_HINT_UPDATE_PRESENCE) && !(allow_notify_user_presence(p))) {
- /* ignore a presence triggered update if we know the useragent doesn't care */
- return 0;
- }
- return extensionstate_update(context, exten, ¬ify_data, p, FALSE);
- }
- /*! \brief Send a fake 401 Unauthorized response when the administrator
- wants to hide the names of local devices from fishers
- */
- static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable)
- {
- /* We have to emulate EXACTLY what we'd get with a good peer
- * and a bad password, or else we leak information. */
- const char *response = "401 Unauthorized";
- const char *reqheader = "Authorization";
- const char *respheader = "WWW-Authenticate";
- const char *authtoken;
- struct ast_str *buf;
- char *c;
- /* table of recognised keywords, and their value in the digest */
- enum keys { K_NONCE, K_LAST };
- struct x {
- const char *key;
- const char *s;
- } *i, keys[] = {
- [K_NONCE] = { "nonce=", "" },
- [K_LAST] = { NULL, NULL}
- };
- authtoken = sip_get_header(req, reqheader);
- if (req->ignore && !ast_strlen_zero(p->nonce) && ast_strlen_zero(authtoken)) {
- /* This is a retransmitted invite/register/etc, don't reconstruct authentication
- * information */
- transmit_response_with_auth(p, response, req, p->nonce, reliable, respheader, 0);
- /* Schedule auto destroy in 32 seconds (according to RFC 3261) */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- } else if (ast_strlen_zero(p->nonce) || ast_strlen_zero(authtoken)) {
- /* We have no auth, so issue challenge and request authentication */
- build_nonce(p, 1);
- transmit_response_with_auth(p, response, req, p->nonce, reliable, respheader, 0);
- /* Schedule auto destroy in 32 seconds */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- }
- if (!(buf = ast_str_thread_get(&check_auth_buf, CHECK_AUTH_BUF_INITLEN))) {
- __transmit_response(p, "403 Forbidden", &p->initreq, reliable);
- return;
- }
- /* Make a copy of the response and parse it */
- if (ast_str_set(&buf, 0, "%s", authtoken) == AST_DYNSTR_BUILD_FAILED) {
- __transmit_response(p, "403 Forbidden", &p->initreq, reliable);
- return;
- }
- c = ast_str_buffer(buf);
- while (c && *(c = ast_skip_blanks(c))) { /* lookup for keys */
- for (i = keys; i->key != NULL; i++) {
- const char *separator = ","; /* default */
- if (strncasecmp(c, i->key, strlen(i->key)) != 0) {
- continue;
- }
- /* Found. Skip keyword, take text in quotes or up to the separator. */
- c += strlen(i->key);
- if (*c == '"') { /* in quotes. Skip first and look for last */
- c++;
- separator = "\"";
- }
- i->s = c;
- strsep(&c, separator);
- break;
- }
- if (i->key == NULL) { /* not found, jump after space or comma */
- strsep(&c, " ,");
- }
- }
- /* Verify nonce from request matches our nonce. If not, send 401 with new nonce */
- if (strcasecmp(p->nonce, keys[K_NONCE].s)) {
- if (!req->ignore) {
- build_nonce(p, 1);
- }
- transmit_response_with_auth(p, response, req, p->nonce, reliable, respheader, FALSE);
- /* Schedule auto destroy in 32 seconds */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- } else {
- __transmit_response(p, "403 Forbidden", &p->initreq, reliable);
- }
- }
- /*!
- * Terminate the uri at the first ';' or space.
- * Technically we should ignore escaped space per RFC3261 (19.1.1 etc)
- * but don't do it for the time being. Remember the uri format is:
- * (User-parameters was added after RFC 3261)
- *\verbatim
- *
- * sip:user:password;user-parameters@host:port;uri-parameters?headers
- * sips:user:password;user-parameters@host:port;uri-parameters?headers
- *
- *\endverbatim
- * \todo As this function does not support user-parameters, it's considered broken
- * and needs fixing.
- */
- static char *terminate_uri(char *uri)
- {
- char *t = uri;
- while (*t && *t > ' ' && *t != ';') {
- t++;
- }
- *t = '\0';
- return uri;
- }
- /*! \brief Terminate a host:port at the ':'
- * \param hostport The address of the hostport string
- *
- * \note In the case of a bracket-enclosed IPv6 address, the hostport variable
- * will contain the non-bracketed host as a result of calling this function.
- */
- static void extract_host_from_hostport(char **hostport)
- {
- char *dont_care;
- ast_sockaddr_split_hostport(*hostport, hostport, &dont_care, PARSE_PORT_IGNORE);
- }
- /*!
- * \internal
- * \brief Helper function to update a peer's lastmsgssent value
- */
- static void update_peer_lastmsgssent(struct sip_peer *peer, int value, int locked)
- {
- if (!locked) {
- ao2_lock(peer);
- }
- peer->lastmsgssent = value;
- if (!locked) {
- ao2_unlock(peer);
- }
- }
- /*!
- * \brief Verify registration of user
- *
- * \details
- * - Registration is done in several steps, first a REGISTER without auth
- * to get a challenge (nonce) then a second one with auth
- * - Registration requests are only matched with peers that are marked as "dynamic"
- */
- static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sockaddr *addr,
- struct sip_request *req, const char *uri)
- {
- enum check_auth_result res = AUTH_NOT_FOUND;
- struct sip_peer *peer;
- char tmp[256];
- char *c, *name, *unused_password, *domain;
- char *uri2 = ast_strdupa(uri);
- int send_mwi = 0;
- terminate_uri(uri2);
- ast_copy_string(tmp, sip_get_header(req, "To"), sizeof(tmp));
- c = get_in_brackets(tmp);
- c = remove_uri_parameters(c);
- if (parse_uri_legacy_check(c, "sip:,sips:", &name, &unused_password, &domain, NULL)) {
- ast_log(LOG_NOTICE, "Invalid to address: '%s' from %s (missing sip:) trying to use anyway...\n", c, ast_sockaddr_stringify_addr(addr));
- return -1;
- }
- SIP_PEDANTIC_DECODE(name);
- SIP_PEDANTIC_DECODE(domain);
- extract_host_from_hostport(&domain);
- if (ast_strlen_zero(domain)) {
- /* <sip:name@[EMPTY]>, never good */
- transmit_response(p, "404 Not found", &p->initreq);
- return AUTH_UNKNOWN_DOMAIN;
- }
- if (ast_strlen_zero(name)) {
- /* <sip:[EMPTY][@]hostport>, unsure whether valid for
- * registration. RFC 3261, 10.2 states:
- * "The To header field and the Request-URI field typically
- * differ, as the former contains a user name."
- * But, Asterisk has always treated the domain-only uri as a
- * username: we allow admins to create accounts described by
- * domain name. */
- name = domain;
- }
- /* This here differs from 1.4 and 1.6: the domain matching ACLs were
- * skipped if it was a domain-only URI (used as username). Here we treat
- * <sip:hostport> as <sip:host@hostport> and won't forget to test the
- * domain ACLs against host. */
- if (!AST_LIST_EMPTY(&domain_list)) {
- if (!check_sip_domain(domain, NULL, 0)) {
- if (sip_cfg.alwaysauthreject) {
- transmit_fake_auth_response(p, &p->initreq, XMIT_UNRELIABLE);
- } else {
- transmit_response(p, "404 Not found (unknown domain)", &p->initreq);
- }
- return AUTH_UNKNOWN_DOMAIN;
- }
- }
- ast_string_field_set(p, exten, name);
- build_contact(p, req, 1);
- if (req->ignore) {
- /* Expires is a special case, where we only want to load the peer if this isn't a deregistration attempt */
- const char *expires = sip_get_header(req, "Expires");
- int expire = atoi(expires);
- if (ast_strlen_zero(expires)) { /* No expires header; look in Contact */
- if ((expires = strcasestr(sip_get_header(req, "Contact"), ";expires="))) {
- expire = atoi(expires + 9);
- }
- }
- if (!ast_strlen_zero(expires) && expire == 0) {
- transmit_response_with_date(p, "200 OK", req);
- return 0;
- }
- }
- peer = sip_find_peer(name, NULL, TRUE, FINDPEERS, FALSE, 0);
- /* If we don't want username disclosure, use the bogus_peer when a user
- * is not found. */
- if (!peer && sip_cfg.alwaysauthreject && sip_cfg.autocreatepeer == AUTOPEERS_DISABLED) {
- peer = bogus_peer;
- sip_ref_peer(peer, "register_verify: ref the bogus_peer");
- }
- if (!(peer && ast_apply_acl(peer->acl, addr, "SIP Peer ACL: "))) {
- /* Peer fails ACL check */
- if (peer) {
- sip_unref_peer(peer, "register_verify: sip_unref_peer: from sip_find_peer operation");
- peer = NULL;
- res = AUTH_ACL_FAILED;
- } else {
- res = AUTH_NOT_FOUND;
- }
- }
- if (peer) {
- ao2_lock(peer);
- if (!peer->host_dynamic) {
- ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name);
- res = AUTH_PEER_NOT_DYNAMIC;
- } else {
- set_peer_nat(p, peer);
- ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_NAT_FORCE_RPORT);
- if (!(res = check_auth(p, req, peer->name, peer->secret, peer->md5secret, SIP_REGISTER, uri2, XMIT_UNRELIABLE))) {
- if (sip_cancel_destroy(p))
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- if (check_request_transport(peer, req)) {
- ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
- transmit_response_with_date(p, "403 Forbidden", req);
- res = AUTH_BAD_TRANSPORT;
- } else {
- /* We have a successful registration attempt with proper authentication,
- now, update the peer */
- switch (parse_register_contact(p, peer, req)) {
- case PARSE_REGISTER_DENIED:
- ast_log(LOG_WARNING, "Registration denied because of contact ACL\n");
- transmit_response_with_date(p, "603 Denied", req);
- res = 0;
- break;
- case PARSE_REGISTER_FAILED:
- ast_log(LOG_WARNING, "Failed to parse contact info\n");
- transmit_response_with_date(p, "400 Bad Request", req);
- res = 0;
- break;
- case PARSE_REGISTER_QUERY:
- ast_string_field_set(p, fullcontact, peer->fullcontact);
- transmit_response_with_date(p, "200 OK", req);
- res = 0;
- send_mwi = 1;
- break;
- case PARSE_REGISTER_UPDATE:
- ast_string_field_set(p, fullcontact, peer->fullcontact);
- /* If expiry is 0, peer has been unregistered already */
- if (p->expiry != 0) {
- update_peer(peer, p->expiry);
- }
- /* Say OK and ask subsystem to retransmit msg counter */
- transmit_response_with_date(p, "200 OK", req);
- send_mwi = 1;
- res = 0;
- break;
- }
- }
- }
- }
- ao2_unlock(peer);
- }
- if (!peer && sip_cfg.autocreatepeer != AUTOPEERS_DISABLED) {
- /* Create peer if we have autocreate mode enabled */
- peer = temp_peer(name);
- if (peer) {
- ao2_t_link(peers, peer, "link peer into peer table");
- if (!ast_sockaddr_isnull(&peer->addr)) {
- ao2_t_link(peers_by_ip, peer, "link peer into peers-by-ip table");
- }
- ao2_lock(peer);
- if (sip_cancel_destroy(p))
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- switch (parse_register_contact(p, peer, req)) {
- case PARSE_REGISTER_DENIED:
- ast_log(LOG_WARNING, "Registration denied because of contact ACL\n");
- transmit_response_with_date(p, "403 Forbidden", req);
- res = 0;
- break;
- case PARSE_REGISTER_FAILED:
- ast_log(LOG_WARNING, "Failed to parse contact info\n");
- transmit_response_with_date(p, "400 Bad Request", req);
- res = 0;
- break;
- case PARSE_REGISTER_QUERY:
- ast_string_field_set(p, fullcontact, peer->fullcontact);
- transmit_response_with_date(p, "200 OK", req);
- send_mwi = 1;
- res = 0;
- break;
- case PARSE_REGISTER_UPDATE:
- ast_string_field_set(p, fullcontact, peer->fullcontact);
- /* Say OK and ask subsystem to retransmit msg counter */
- transmit_response_with_date(p, "200 OK", req);
- if (peer->endpoint) {
- RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
- ast_endpoint_set_state(peer->endpoint, AST_ENDPOINT_ONLINE);
- blob = ast_json_pack("{s: s, s: s}",
- "peer_status", "Registered",
- "address", ast_sockaddr_stringify(addr));
- ast_endpoint_blob_publish(peer->endpoint, ast_endpoint_state_type(), blob);
- }
- send_mwi = 1;
- res = 0;
- break;
- }
- ao2_unlock(peer);
- }
- }
- if (!res) {
- if (send_mwi) {
- sip_pvt_unlock(p);
- sip_send_mwi_to_peer(peer, 0);
- sip_pvt_lock(p);
- } else {
- update_peer_lastmsgssent(peer, -1, 0);
- }
- ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name);
- }
- if (res < 0) {
- RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
- switch (res) {
- case AUTH_SECRET_FAILED:
- /* Wrong password in authentication. Go away, don't try again until you fixed it */
- transmit_response(p, "403 Forbidden", &p->initreq);
- if (global_authfailureevents) {
- const char *peer_addr = ast_strdupa(ast_sockaddr_stringify_addr(addr));
- const char *peer_port = ast_strdupa(ast_sockaddr_stringify_port(addr));
- blob = ast_json_pack("{s: s, s: s, s: s, s: s}",
- "peer_status", "Rejected",
- "cause", "AUTH_SECRET_FAILED",
- "address", peer_addr,
- "port", peer_port);
- }
- break;
- case AUTH_USERNAME_MISMATCH:
- /* Username and digest username does not match.
- Asterisk uses the From: username for authentication. We need the
- devices to use the same authentication user name until we support
- proper authentication by digest auth name */
- case AUTH_NOT_FOUND:
- case AUTH_PEER_NOT_DYNAMIC:
- case AUTH_ACL_FAILED:
- if (sip_cfg.alwaysauthreject) {
- transmit_fake_auth_response(p, &p->initreq, XMIT_UNRELIABLE);
- if (global_authfailureevents) {
- const char *peer_addr = ast_strdupa(ast_sockaddr_stringify_addr(addr));
- const char *peer_port = ast_strdupa(ast_sockaddr_stringify_port(addr));
- blob = ast_json_pack("{s: s, s: s, s: s, s: s}",
- "peer_status", "Rejected",
- "cause", res == AUTH_PEER_NOT_DYNAMIC ? "AUTH_PEER_NOT_DYNAMIC" : "URI_NOT_FOUND",
- "address", peer_addr,
- "port", peer_port);
- }
- } else {
- /* URI not found */
- if (res == AUTH_PEER_NOT_DYNAMIC) {
- transmit_response(p, "403 Forbidden", &p->initreq);
- if (global_authfailureevents) {
- const char *peer_addr = ast_strdupa(ast_sockaddr_stringify_addr(addr));
- const char *peer_port = ast_strdupa(ast_sockaddr_stringify_port(addr));
- blob = ast_json_pack("{s: s, s: s, s: s, s: s}",
- "peer_status", "Rejected",
- "cause", "AUTH_PEER_NOT_DYNAMIC",
- "address", peer_addr,
- "port", peer_port);
- }
- } else {
- transmit_response(p, "404 Not found", &p->initreq);
- if (global_authfailureevents) {
- const char *peer_addr = ast_strdupa(ast_sockaddr_stringify_addr(addr));
- const char *peer_port = ast_strdupa(ast_sockaddr_stringify_port(addr));
- blob = ast_json_pack("{s: s, s: s, s: s, s: s}",
- "peer_status", "Rejected",
- "cause", (res == AUTH_USERNAME_MISMATCH) ? "AUTH_USERNAME_MISMATCH" : "URI_NOT_FOUND",
- "address", peer_addr,
- "port", peer_port);
- }
- }
- }
- break;
- case AUTH_BAD_TRANSPORT:
- default:
- break;
- }
- if (peer && peer->endpoint) {
- ast_endpoint_blob_publish(peer->endpoint, ast_endpoint_state_type(), blob);
- }
- }
- if (peer) {
- sip_unref_peer(peer, "register_verify: sip_unref_peer: tossing stack peer pointer at end of func");
- }
- return res;
- }
- /*! \brief Translate referring cause */
- static void sip_set_redirstr(struct sip_pvt *p, char *reason) {
- if (!strcmp(reason, "unknown")) {
- ast_string_field_set(p, redircause, "UNKNOWN");
- } else if (!strcmp(reason, "user-busy")) {
- ast_string_field_set(p, redircause, "BUSY");
- } else if (!strcmp(reason, "no-answer")) {
- ast_string_field_set(p, redircause, "NOANSWER");
- } else if (!strcmp(reason, "unavailable")) {
- ast_string_field_set(p, redircause, "UNREACHABLE");
- } else if (!strcmp(reason, "unconditional")) {
- ast_string_field_set(p, redircause, "UNCONDITIONAL");
- } else if (!strcmp(reason, "time-of-day")) {
- ast_string_field_set(p, redircause, "UNKNOWN");
- } else if (!strcmp(reason, "do-not-disturb")) {
- ast_string_field_set(p, redircause, "UNKNOWN");
- } else if (!strcmp(reason, "deflection")) {
- ast_string_field_set(p, redircause, "UNKNOWN");
- } else if (!strcmp(reason, "follow-me")) {
- ast_string_field_set(p, redircause, "UNKNOWN");
- } else if (!strcmp(reason, "out-of-service")) {
- ast_string_field_set(p, redircause, "UNREACHABLE");
- } else if (!strcmp(reason, "away")) {
- ast_string_field_set(p, redircause, "UNREACHABLE");
- } else {
- ast_string_field_set(p, redircause, "UNKNOWN");
- }
- }
- /*! \brief Parse the parts of the P-Asserted-Identity header
- * on an incoming packet. Returns 1 if a valid header is found
- * and it is different from the current caller id.
- */
- static int get_pai(struct sip_pvt *p, struct sip_request *req)
- {
- char pai[256];
- char privacy[64];
- char *cid_num = NULL;
- char *cid_name = NULL;
- char emptyname[1] = "";
- int callingpres = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
- char *uri = NULL;
- int is_anonymous = 0, do_update = 1, no_name = 0;
- ast_copy_string(pai, sip_get_header(req, "P-Asserted-Identity"), sizeof(pai));
- if (ast_strlen_zero(pai)) {
- return 0;
- }
- /* use the reqresp_parser function get_name_and_number*/
- if (get_name_and_number(pai, &cid_name, &cid_num)) {
- return 0;
- }
- if (global_shrinkcallerid && ast_is_shrinkable_phonenumber(cid_num)) {
- ast_shrink_phone_number(cid_num);
- }
- uri = get_in_brackets(pai);
- if (!strncasecmp(uri, "sip:anonymous@anonymous.invalid", 31)) {
- callingpres = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
- /*XXX Assume no change in cid_num. Perhaps it should be
- * blanked?
- */
- ast_free(cid_num);
- is_anonymous = 1;
- cid_num = (char *)p->cid_num;
- }
- ast_copy_string(privacy, sip_get_header(req, "Privacy"), sizeof(privacy));
- if (!ast_strlen_zero(privacy) && !strncmp(privacy, "id", 2)) {
- callingpres = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
- }
- if (!cid_name) {
- no_name = 1;
- cid_name = (char *)emptyname;
- }
- /* Only return true if the supplied caller id is different */
- if (!strcasecmp(p->cid_num, cid_num) && !strcasecmp(p->cid_name, cid_name) && p->callingpres == callingpres) {
- do_update = 0;
- } else {
- ast_string_field_set(p, cid_num, cid_num);
- ast_string_field_set(p, cid_name, cid_name);
- p->callingpres = callingpres;
- if (p->owner) {
- ast_set_callerid(p->owner, cid_num, cid_name, NULL);
- ast_channel_caller(p->owner)->id.name.presentation = callingpres;
- ast_channel_caller(p->owner)->id.number.presentation = callingpres;
- }
- }
- /* get_name_and_number allocates memory for cid_num and cid_name so we have to free it */
- if (!is_anonymous) {
- ast_free(cid_num);
- }
- if (!no_name) {
- ast_free(cid_name);
- }
- return do_update;
- }
- /*! \brief Get name, number and presentation from remote party id header,
- * returns true if a valid header was found and it was different from the
- * current caller id.
- */
- static int get_rpid(struct sip_pvt *p, struct sip_request *oreq)
- {
- char tmp[256];
- struct sip_request *req;
- char *cid_num = "";
- char *cid_name = "";
- int callingpres = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
- char *privacy = "";
- char *screen = "";
- char *start, *end;
- if (!ast_test_flag(&p->flags[0], SIP_TRUSTRPID))
- return 0;
- req = oreq;
- if (!req)
- req = &p->initreq;
- ast_copy_string(tmp, sip_get_header(req, "Remote-Party-ID"), sizeof(tmp));
- if (ast_strlen_zero(tmp)) {
- return get_pai(p, req);
- }
- /*
- * RPID is not:
- * rpid = (name-addr / addr-spec) *(SEMI rpi-token)
- * But it is:
- * rpid = [display-name] LAQUOT addr-spec RAQUOT *(SEMI rpi-token)
- * Ergo, calling parse_name_andor_addr() on it wouldn't be
- * correct because that would allow addr-spec style too.
- */
- start = tmp;
- /* Quoted (note that we're not dealing with escapes properly) */
- if (*start == '"') {
- *start++ = '\0';
- end = strchr(start, '"');
- if (!end)
- return 0;
- *end++ = '\0';
- cid_name = start;
- start = ast_skip_blanks(end);
- /* Unquoted */
- } else {
- cid_name = start;
- start = end = strchr(start, '<');
- if (!start) {
- return 0;
- }
- /* trim blanks if there are any. the mandatory NUL is done below */
- while (--end >= cid_name && *end < 33) {
- *end = '\0';
- }
- }
- if (*start != '<')
- return 0;
- *start++ = '\0';
- end = strchr(start, '@');
- if (!end)
- return 0;
- *end++ = '\0';
- if (strncasecmp(start, "sip:", 4))
- return 0;
- cid_num = start + 4;
- if (global_shrinkcallerid && ast_is_shrinkable_phonenumber(cid_num))
- ast_shrink_phone_number(cid_num);
- start = end;
- end = strchr(start, '>');
- if (!end)
- return 0;
- *end++ = '\0';
- if (*end) {
- start = end;
- if (*start != ';')
- return 0;
- *start++ = '\0';
- while (!ast_strlen_zero(start)) {
- end = strchr(start, ';');
- if (end)
- *end++ = '\0';
- if (!strncasecmp(start, "privacy=", 8))
- privacy = start + 8;
- else if (!strncasecmp(start, "screen=", 7))
- screen = start + 7;
- start = end;
- }
- if (!strcasecmp(privacy, "full")) {
- if (!strcasecmp(screen, "yes"))
- callingpres = AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN;
- else if (!strcasecmp(screen, "no"))
- callingpres = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
- } else {
- if (!strcasecmp(screen, "yes"))
- callingpres = AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN;
- else if (!strcasecmp(screen, "no"))
- callingpres = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
- }
- }
- /* Only return true if the supplied caller id is different */
- if (!strcasecmp(p->cid_num, cid_num) && !strcasecmp(p->cid_name, cid_name) && p->callingpres == callingpres)
- return 0;
- ast_string_field_set(p, cid_num, cid_num);
- ast_string_field_set(p, cid_name, cid_name);
- p->callingpres = callingpres;
- if (p->owner) {
- ast_set_callerid(p->owner, cid_num, cid_name, NULL);
- ast_channel_caller(p->owner)->id.name.presentation = callingpres;
- ast_channel_caller(p->owner)->id.number.presentation = callingpres;
- }
- return 1;
- }
- /*! \brief Get referring dnis
- *
- * \param p dialog information
- * \param oreq The request to retrieve RDNIS from
- * \param[out] name The name of the party redirecting the call.
- * \param[out] number The number of the party redirecting the call.
- * \param[out] reason_code The numerical code corresponding to the reason for the redirection.
- * \param[out] reason_str A string describing the reason for redirection. Will never be zero-length
- *
- * \retval -1 Could not retrieve RDNIS information
- * \retval 0 RDNIS successfully retrieved
- */
- static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason_code, char **reason_str)
- {
- char tmp[256], *exten, *rexten, *rdomain, *rname = NULL;
- char *params, *reason_param = NULL;
- struct sip_request *req;
- req = oreq ? oreq : &p->initreq;
- ast_copy_string(tmp, sip_get_header(req, "Diversion"), sizeof(tmp));
- if (ast_strlen_zero(tmp))
- return -1;
- if ((params = strchr(tmp, '>'))) {
- params = strchr(params, ';');
- }
- exten = get_in_brackets(tmp);
- if (!strncasecmp(exten, "sip:", 4)) {
- exten += 4;
- } else if (!strncasecmp(exten, "sips:", 5)) {
- exten += 5;
- } else {
- ast_log(LOG_WARNING, "Huh? Not an RDNIS SIP header (%s)?\n", exten);
- return -1;
- }
- /* Get diversion-reason param if present */
- if (params) {
- *params = '\0'; /* Cut off parameters */
- params++;
- while (*params == ';' || *params == ' ')
- params++;
- /* Check if we have a reason parameter */
- if ((reason_param = strcasestr(params, "reason="))) {
- char *end;
- reason_param+=7;
- if ((end = strchr(reason_param, ';'))) {
- *end = '\0';
- }
- /* Remove enclosing double-quotes */
- if (*reason_param == '"')
- reason_param = ast_strip_quoted(reason_param, "\"", "\"");
- if (!ast_strlen_zero(reason_param)) {
- sip_set_redirstr(p, reason_param);
- if (p->owner) {
- pbx_builtin_setvar_helper(p->owner, "__PRIREDIRECTREASON", p->redircause);
- pbx_builtin_setvar_helper(p->owner, "__SIPREDIRECTREASON", reason_param);
- }
- }
- }
- }
- rdomain = exten;
- rexten = strsep(&rdomain, "@"); /* trim anything after @ */
- if (p->owner)
- pbx_builtin_setvar_helper(p->owner, "__SIPRDNISDOMAIN", rdomain);
- if (sip_debug_test_pvt(p)) {
- ast_verbose("RDNIS for this call is %s (reason %s)\n", exten, S_OR(reason_param, ""));
- }
- /*ast_string_field_set(p, rdnis, rexten);*/
- if (*tmp == '\"') {
- char *end_quote;
- rname = tmp + 1;
- end_quote = strchr(rname, '\"');
- if (end_quote) {
- *end_quote = '\0';
- }
- }
- if (number) {
- *number = ast_strdup(rexten);
- }
- if (name && rname) {
- *name = ast_strdup(rname);
- }
- if (!ast_strlen_zero(reason_param)) {
- if (reason_code) {
- *reason_code = sip_reason_str_to_code(reason_param);
- }
- if (reason_str) {
- *reason_str = ast_strdup(reason_param);
- }
- }
- return 0;
- }
- /*!
- * \brief Find out who the call is for.
- *
- * \details
- * We use the request uri as a destination.
- * This code assumes authentication has been done, so that the
- * device (peer/user) context is already set.
- *
- * \return 0 on success (found a matching extension), non-zero on failure
- *
- * \note If the incoming uri is a SIPS: uri, we are required to carry this across
- * the dialplan, so that the outbound call also is a sips: call or encrypted
- * IAX2 call. If that's not available, the call should FAIL.
- */
- static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id)
- {
- char tmp[256] = "", *uri, *unused_password, *domain;
- RAII_VAR(char *, tmpf, NULL, ast_free);
- char *from = NULL;
- struct sip_request *req;
- char *decoded_uri;
- RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, ast_get_chan_features_pickup_config(p->owner), ao2_cleanup);
- const char *pickupexten;
- if (!pickup_cfg) {
- ast_log(LOG_ERROR, "Unable to retrieve pickup configuration options. Unable to detect call pickup extension\n");
- pickupexten = "";
- } else {
- /* Don't need to duplicate since channel is locked for the duration of this function */
- pickupexten = pickup_cfg->pickupexten;
- }
- req = oreq;
- if (!req) {
- req = &p->initreq;
- }
- /* Find the request URI */
- if (req->rlpart2) {
- ast_copy_string(tmp, REQ_OFFSET_TO_STR(req, rlpart2), sizeof(tmp));
- }
- uri = ast_strdupa(get_in_brackets(tmp));
- if (parse_uri_legacy_check(uri, "sip:,sips:,tel:", &uri, &unused_password, &domain, NULL)) {
- ast_log(LOG_WARNING, "Not a SIP header (%s)?\n", uri);
- return SIP_GET_DEST_INVALID_URI;
- }
- SIP_PEDANTIC_DECODE(domain);
- SIP_PEDANTIC_DECODE(uri);
- extract_host_from_hostport(&domain);
- if (strncasecmp(get_in_brackets(tmp), "tel:", 4)) {
- ast_string_field_set(p, domain, domain);
- } else {
- ast_string_field_set(p, tel_phone_context, domain);
- }
- if (ast_strlen_zero(uri)) {
- /*
- * Either there really was no extension found or the request
- * URI had encoded nulls that made the string "empty". Use "s"
- * as the extension.
- */
- uri = "s";
- }
- /* Now find the From: caller ID and name */
- /* XXX Why is this done in get_destination? Isn't it already done?
- Needs to be checked
- */
- tmpf = ast_strdup(sip_get_header(req, "From"));
- if (!ast_strlen_zero(tmpf)) {
- from = get_in_brackets(tmpf);
- if (parse_uri_legacy_check(from, "sip:,sips:,tel:", &from, NULL, &domain, NULL)) {
- ast_log(LOG_WARNING, "Not a SIP header (%s)?\n", from);
- return SIP_GET_DEST_INVALID_URI;
- }
- SIP_PEDANTIC_DECODE(from);
- SIP_PEDANTIC_DECODE(domain);
- extract_host_from_hostport(&domain);
- ast_string_field_set(p, fromdomain, domain);
- }
- if (!AST_LIST_EMPTY(&domain_list)) {
- char domain_context[AST_MAX_EXTENSION];
- domain_context[0] = '\0';
- if (!check_sip_domain(p->domain, domain_context, sizeof(domain_context))) {
- if (!sip_cfg.allow_external_domains && (req->method == SIP_INVITE || req->method == SIP_REFER)) {
- ast_debug(1, "Got SIP %s to non-local domain '%s'; refusing request.\n", sip_methods[req->method].text, p->domain);
- return SIP_GET_DEST_REFUSED;
- }
- }
- /* If we don't have a peer (i.e. we're a guest call),
- * overwrite the original context */
- if (!ast_test_flag(&p->flags[1], SIP_PAGE2_HAVEPEERCONTEXT) && !ast_strlen_zero(domain_context)) {
- ast_string_field_set(p, context, domain_context);
- }
- }
- /* If the request coming in is a subscription and subscribecontext has been specified use it */
- if (req->method == SIP_SUBSCRIBE && !ast_strlen_zero(p->subscribecontext)) {
- ast_string_field_set(p, context, p->subscribecontext);
- }
- if (sip_debug_test_pvt(p)) {
- ast_verbose("Looking for %s in %s (domain %s)\n", uri, p->context, p->domain);
- }
- /* Since extensions.conf can have unescaped characters, try matching a
- * decoded uri in addition to the non-decoded uri. */
- decoded_uri = ast_strdupa(uri);
- ast_uri_decode(decoded_uri, ast_uri_sip_user);
- /* If this is a subscription we actually just need to see if a hint exists for the extension */
- if (req->method == SIP_SUBSCRIBE) {
- char hint[AST_MAX_EXTENSION];
- int which = 0;
- if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, uri) ||
- (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, decoded_uri) && (which = 1))) {
- if (!oreq) {
- ast_string_field_set(p, exten, which ? decoded_uri : uri);
- }
- return SIP_GET_DEST_EXTEN_FOUND;
- } else {
- return SIP_GET_DEST_EXTEN_NOT_FOUND;
- }
- } else {
- struct ast_cc_agent *agent;
- /* Check the dialplan for the username part of the request URI,
- the domain will be stored in the SIPDOMAIN variable
- Return 0 if we have a matching extension */
- if (ast_exists_extension(NULL, p->context, uri, 1, S_OR(p->cid_num, from))) {
- if (!oreq) {
- ast_string_field_set(p, exten, uri);
- }
- return SIP_GET_DEST_EXTEN_FOUND;
- }
- if (ast_exists_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from))
- || !strcmp(decoded_uri, pickupexten)) {
- if (!oreq) {
- ast_string_field_set(p, exten, decoded_uri);
- }
- return SIP_GET_DEST_EXTEN_FOUND;
- }
- if ((agent = find_sip_cc_agent_by_notify_uri(tmp))) {
- struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
- /* This is a CC recall. We can set p's extension to the exten from
- * the original INVITE
- */
- ast_string_field_set(p, exten, agent_pvt->original_exten);
- /* And we need to let the CC core know that the caller is attempting
- * his recall
- */
- ast_cc_agent_recalling(agent->core_id, "SIP caller %s is attempting recall",
- agent->device_name);
- if (cc_recall_core_id) {
- *cc_recall_core_id = agent->core_id;
- }
- ao2_ref(agent, -1);
- return SIP_GET_DEST_EXTEN_FOUND;
- }
- }
- if (ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP)
- && (ast_canmatch_extension(NULL, p->context, uri, 1, S_OR(p->cid_num, from))
- || ast_canmatch_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from))
- || !strncmp(decoded_uri, pickupexten, strlen(decoded_uri)))) {
- /* Overlap dialing is enabled and we need more digits to match an extension. */
- return SIP_GET_DEST_EXTEN_MATCHMORE;
- }
- return SIP_GET_DEST_EXTEN_NOT_FOUND;
- }
- /*! \brief Find a companion dialog based on Replaces information
- *
- * This information may come from a Refer-To header in a REFER or from
- * a Replaces header in an INVITE.
- *
- * This function will find the appropriate sip_pvt and increment the refcount
- * of both the sip_pvt and its owner channel. These two references are returned
- * in the out parameters
- *
- * \param callid Callid to search for
- * \param totag to-tag parameter from Replaces
- * \param fromtag from-tag parameter from Replaces
- * \param[out] out_pvt The found sip_pvt.
- * \param[out] out_chan The found sip_pvt's owner channel.
- * \retval 0 Success
- * \retval non-zero Failure
- */
- static int get_sip_pvt_from_replaces(const char *callid, const char *totag,
- const char *fromtag, struct sip_pvt **out_pvt, struct ast_channel **out_chan)
- {
- struct sip_pvt *sip_pvt_ptr;
- struct sip_pvt tmp_dialog = {
- .callid = callid,
- };
- if (totag) {
- ast_debug(4, "Looking for callid %s (fromtag %s totag %s)\n", callid, fromtag ? fromtag : "<no fromtag>", totag ? totag : "<no totag>");
- }
- /* Search dialogs and find the match */
- sip_pvt_ptr = ao2_t_find(dialogs, &tmp_dialog, OBJ_POINTER, "ao2_find of dialog in dialogs table");
- if (sip_pvt_ptr) {
- /* Go ahead and lock it (and its owner) before returning */
- SCOPED_LOCK(lock, sip_pvt_ptr, sip_pvt_lock, sip_pvt_unlock);
- if (sip_cfg.pedanticsipchecking) {
- unsigned char frommismatch = 0, tomismatch = 0;
- if (ast_strlen_zero(fromtag)) {
- ast_debug(4, "Matched %s call for callid=%s - no from tag specified, pedantic check fails\n",
- sip_pvt_ptr->outgoing_call == TRUE ? "OUTGOING": "INCOMING", sip_pvt_ptr->callid);
- return -1;
- }
- if (ast_strlen_zero(totag)) {
- ast_debug(4, "Matched %s call for callid=%s - no to tag specified, pedantic check fails\n",
- sip_pvt_ptr->outgoing_call == TRUE ? "OUTGOING": "INCOMING", sip_pvt_ptr->callid);
- return -1;
- }
- /* RFC 3891
- * > 3. User Agent Server Behavior: Receiving a Replaces Header
- * > The Replaces header contains information used to match an existing
- * > SIP dialog (call-id, to-tag, and from-tag). Upon receiving an INVITE
- * > with a Replaces header, the User Agent (UA) attempts to match this
- * > information with a confirmed or early dialog. The User Agent Server
- * > (UAS) matches the to-tag and from-tag parameters as if they were tags
- * > present in an incoming request. In other words, the to-tag parameter
- * > is compared to the local tag, and the from-tag parameter is compared
- * > to the remote tag.
- *
- * Thus, the totag is always compared to the local tag, regardless if
- * this our call is an incoming or outgoing call.
- */
- frommismatch = !!strcmp(fromtag, sip_pvt_ptr->theirtag);
- tomismatch = !!strcmp(totag, sip_pvt_ptr->tag);
- /* Don't check from if the dialog is not established, due to multi forking the from
- * can change when the call is not answered yet.
- */
- if ((frommismatch && ast_test_flag(&sip_pvt_ptr->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) || tomismatch) {
- if (frommismatch) {
- ast_debug(4, "Matched %s call for callid=%s - pedantic from tag check fails; their tag is %s our tag is %s\n",
- sip_pvt_ptr->outgoing_call == TRUE ? "OUTGOING": "INCOMING", sip_pvt_ptr->callid,
- fromtag, sip_pvt_ptr->theirtag);
- }
- if (tomismatch) {
- ast_debug(4, "Matched %s call for callid=%s - pedantic to tag check fails; their tag is %s our tag is %s\n",
- sip_pvt_ptr->outgoing_call == TRUE ? "OUTGOING": "INCOMING", sip_pvt_ptr->callid,
- totag, sip_pvt_ptr->tag);
- }
- return -1;
- }
- }
- if (totag)
- ast_debug(4, "Matched %s call - their tag is %s Our tag is %s\n",
- sip_pvt_ptr->outgoing_call == TRUE ? "OUTGOING": "INCOMING",
- sip_pvt_ptr->theirtag, sip_pvt_ptr->tag);
- *out_pvt = sip_pvt_ptr;
- if (out_chan) {
- *out_chan = sip_pvt_ptr->owner ? ast_channel_ref(sip_pvt_ptr->owner) : NULL;
- }
- }
- return 0;
- }
- /*! \brief Call transfer support (the REFER method)
- * Extracts Refer headers into pvt dialog structure
- *
- * \note If we get a SIPS uri in the refer-to header, we're required to set up a secure signalling path
- * to that extension. As a minimum, this needs to be added to a channel variable, if not a channel
- * flag.
- */
- static int get_refer_info(struct sip_pvt *transferer, struct sip_request *outgoing_req)
- {
- const char *p_referred_by = NULL;
- char *h_refer_to = NULL;
- char *h_referred_by = NULL;
- char *refer_to;
- const char *p_refer_to;
- char *referred_by_uri = NULL;
- char *ptr;
- struct sip_request *req = NULL;
- const char *transfer_context = NULL;
- struct sip_refer *refer;
- req = outgoing_req;
- refer = transferer->refer;
- if (!req) {
- req = &transferer->initreq;
- }
- p_refer_to = sip_get_header(req, "Refer-To");
- if (ast_strlen_zero(p_refer_to)) {
- ast_log(LOG_WARNING, "Refer-To Header missing. Skipping transfer.\n");
- return -2; /* Syntax error */
- }
- h_refer_to = ast_strdupa(p_refer_to);
- refer_to = get_in_brackets(h_refer_to);
- if (!strncasecmp(refer_to, "sip:", 4)) {
- refer_to += 4; /* Skip sip: */
- } else if (!strncasecmp(refer_to, "sips:", 5)) {
- refer_to += 5;
- } else {
- ast_log(LOG_WARNING, "Can't transfer to non-sip: URI. (Refer-to: %s)?\n", refer_to);
- return -3;
- }
- /* Get referred by header if it exists */
- p_referred_by = sip_get_header(req, "Referred-By");
- /* Give useful transfer information to the dialplan */
- if (transferer->owner) {
- RAII_VAR(struct ast_channel *, peer, NULL, ast_channel_cleanup);
- RAII_VAR(struct ast_channel *, owner_relock, NULL, ast_channel_cleanup);
- RAII_VAR(struct ast_channel *, owner_ref, NULL, ast_channel_cleanup);
- /* Grab a reference to transferer->owner to prevent it from going away */
- owner_ref = ast_channel_ref(transferer->owner);
- /* Established locking order here is bridge, channel, pvt
- * and the bridge will be locked during ast_channel_bridge_peer */
- ast_channel_unlock(owner_ref);
- sip_pvt_unlock(transferer);
- peer = ast_channel_bridge_peer(owner_ref);
- if (peer) {
- pbx_builtin_setvar_helper(peer, "SIPREFERRINGCONTEXT",
- S_OR(transferer->context, NULL));
- pbx_builtin_setvar_helper(peer, "__SIPREFERREDBYHDR",
- S_OR(p_referred_by, NULL));
- ast_channel_unlock(peer);
- }
- owner_relock = sip_pvt_lock_full(transferer);
- if (!owner_relock) {
- ast_debug(3, "Unable to reacquire owner channel lock, channel is gone\n");
- return -5;
- }
- }
- if (!ast_strlen_zero(p_referred_by)) {
- h_referred_by = ast_strdupa(p_referred_by);
- referred_by_uri = get_in_brackets(h_referred_by);
- if (!strncasecmp(referred_by_uri, "sip:", 4)) {
- referred_by_uri += 4; /* Skip sip: */
- } else if (!strncasecmp(referred_by_uri, "sips:", 5)) {
- referred_by_uri += 5; /* Skip sips: */
- } else {
- ast_log(LOG_WARNING, "Huh? Not a sip: header (Referred-by: %s). Skipping.\n", referred_by_uri);
- referred_by_uri = NULL;
- }
- }
- /* Check for arguments in the refer_to header */
- if ((ptr = strcasestr(refer_to, "replaces="))) {
- char *to = NULL, *from = NULL, *callid;
- /* This is an attended transfer */
- refer->attendedtransfer = 1;
- callid = ast_strdupa(ptr + 9);
- ast_uri_decode(callid, ast_uri_sip_user);
- if ((ptr = strchr(callid, ';'))) { /* Find options */
- *ptr++ = '\0';
- }
- ast_string_field_set(refer, replaces_callid, callid);
- if (ptr) {
- /* Find the different tags before we destroy the string */
- to = strcasestr(ptr, "to-tag=");
- from = strcasestr(ptr, "from-tag=");
- }
- /* Grab the to header */
- if (to) {
- ptr = to + 7;
- if ((to = strchr(ptr, '&'))) {
- *to = '\0';
- }
- if ((to = strchr(ptr, ';'))) {
- *to = '\0';
- }
- ast_string_field_set(refer, replaces_callid_totag, ptr);
- }
- if (from) {
- ptr = from + 9;
- if ((from = strchr(ptr, '&'))) {
- *from = '\0';
- }
- if ((from = strchr(ptr, ';'))) {
- *from = '\0';
- }
- ast_string_field_set(refer, replaces_callid_fromtag, ptr);
- }
- if (!strcmp(refer->replaces_callid, transferer->callid) &&
- (!sip_cfg.pedanticsipchecking ||
- (!strcmp(refer->replaces_callid_fromtag, transferer->theirtag) &&
- !strcmp(refer->replaces_callid_totag, transferer->tag)))) {
- ast_log(LOG_WARNING, "Got an attempt to replace own Call-ID on %s\n", transferer->callid);
- return -4;
- }
- if (!sip_cfg.pedanticsipchecking) {
- ast_debug(2, "Attended transfer: Will use Replace-Call-ID : %s (No check of from/to tags)\n", refer->replaces_callid);
- } else {
- ast_debug(2, "Attended transfer: Will use Replace-Call-ID : %s F-tag: %s T-tag: %s\n", refer->replaces_callid, refer->replaces_callid_fromtag ? refer->replaces_callid_fromtag : "<none>", refer->replaces_callid_totag ? refer->replaces_callid_totag : "<none>");
- }
- }
- if ((ptr = strchr(refer_to, '@'))) { /* Separate domain */
- char *urioption = NULL, *domain;
- int bracket = 0;
- *ptr++ = '\0';
- if ((urioption = strchr(ptr, ';'))) { /* Separate urioptions */
- *urioption++ = '\0';
- }
- domain = ptr;
- /* Remove :port */
- for (; *ptr != '\0'; ++ptr) {
- if (*ptr == ':' && bracket == 0) {
- *ptr = '\0';
- break;
- } else if (*ptr == '[') {
- ++bracket;
- } else if (*ptr == ']') {
- --bracket;
- }
- }
- SIP_PEDANTIC_DECODE(domain);
- SIP_PEDANTIC_DECODE(urioption);
- /* Save the domain for the dial plan */
- ast_string_field_set(refer, refer_to_domain, domain);
- if (urioption) {
- ast_string_field_set(refer, refer_to_urioption, urioption);
- }
- }
- if ((ptr = strchr(refer_to, ';'))) /* Remove options */
- *ptr = '\0';
- SIP_PEDANTIC_DECODE(refer_to);
- ast_string_field_set(refer, refer_to, refer_to);
- if (referred_by_uri) {
- if ((ptr = strchr(referred_by_uri, ';'))) /* Remove options */
- *ptr = '\0';
- SIP_PEDANTIC_DECODE(referred_by_uri);
- ast_string_field_build(refer, referred_by, "<sip:%s>", referred_by_uri);
- } else {
- ast_string_field_set(refer, referred_by, "");
- }
- /* Determine transfer context */
- if (transferer->owner) {
- /* By default, use the context in the channel sending the REFER */
- transfer_context = pbx_builtin_getvar_helper(transferer->owner, "TRANSFER_CONTEXT");
- if (ast_strlen_zero(transfer_context)) {
- transfer_context = ast_channel_macrocontext(transferer->owner);
- }
- }
- if (ast_strlen_zero(transfer_context)) {
- transfer_context = S_OR(transferer->context, sip_cfg.default_context);
- }
- ast_string_field_set(refer, refer_to_context, transfer_context);
- /* Either an existing extension or the parking extension */
- if (refer->attendedtransfer || ast_exists_extension(NULL, transfer_context, refer_to, 1, NULL)) {
- if (sip_debug_test_pvt(transferer)) {
- ast_verbose("SIP transfer to extension %s@%s by %s\n", refer_to, transfer_context, S_OR(referred_by_uri, "Unknown"));
- }
- /* We are ready to transfer to the extension */
- return 0;
- }
- if (sip_debug_test_pvt(transferer))
- ast_verbose("Failed SIP Transfer to non-existing extension %s in context %s\n n", refer_to, transfer_context);
- /* Failure, we can't find this extension */
- return -1;
- }
- /*! \brief Call transfer support (old way, deprecated by the IETF)
- * \note does not account for SIPS: uri requirements, nor check transport
- */
- static int get_also_info(struct sip_pvt *p, struct sip_request *oreq)
- {
- char tmp[256] = "", *c, *a;
- struct sip_request *req = oreq ? oreq : &p->initreq;
- struct sip_refer *refer = NULL;
- const char *transfer_context = NULL;
- if (!sip_refer_alloc(p)) {
- return -1;
- }
- refer = p->refer;
- ast_copy_string(tmp, sip_get_header(req, "Also"), sizeof(tmp));
- c = get_in_brackets(tmp);
- if (parse_uri_legacy_check(c, "sip:,sips:", &c, NULL, &a, NULL)) {
- ast_log(LOG_WARNING, "Huh? Not a SIP header in Also: transfer (%s)?\n", c);
- return -1;
- }
- SIP_PEDANTIC_DECODE(c);
- SIP_PEDANTIC_DECODE(a);
- if (!ast_strlen_zero(a)) {
- ast_string_field_set(refer, refer_to_domain, a);
- }
- if (sip_debug_test_pvt(p))
- ast_verbose("Looking for %s in %s\n", c, p->context);
- /* Determine transfer context */
- if (p->owner) {
- /* By default, use the context in the channel sending the REFER */
- transfer_context = pbx_builtin_getvar_helper(p->owner, "TRANSFER_CONTEXT");
- if (ast_strlen_zero(transfer_context)) {
- transfer_context = ast_channel_macrocontext(p->owner);
- }
- }
- if (ast_strlen_zero(transfer_context)) {
- transfer_context = S_OR(p->context, sip_cfg.default_context);
- }
- if (ast_exists_extension(NULL, transfer_context, c, 1, NULL)) {
- /* This is a blind transfer */
- ast_debug(1, "SIP Bye-also transfer to Extension %s@%s \n", c, transfer_context);
- ast_string_field_set(refer, refer_to, c);
- ast_string_field_set(refer, referred_by, "");
- ast_string_field_set(refer, refer_contact, "");
- /* Set new context */
- ast_string_field_set(p, context, transfer_context);
- return 0;
- } else if (ast_canmatch_extension(NULL, p->context, c, 1, NULL)) {
- return 1;
- }
- return -1;
- }
- /*! \brief Set the peers nat flags if they are using auto_* settings */
- static void set_peer_nat(const struct sip_pvt *p, struct sip_peer *peer)
- {
- if (!p || !peer) {
- return;
- }
- if (ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
- if (p->natdetected) {
- ast_set_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT);
- } else {
- ast_clear_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT);
- }
- }
- if (ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) {
- if (p->natdetected) {
- ast_set_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP);
- } else {
- ast_clear_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP);
- }
- }
- }
- /*! \brief Check and see if the requesting UA is likely to be behind a NAT.
- *
- * If the requesting NAT is behind NAT, set the * natdetected flag so that
- * later, peers with nat=auto_* can use the value. Also, set the flags so
- * that Asterisk responds identically whether or not a peer exists so as
- * not to leak peer name information.
- */
- static void check_for_nat(const struct ast_sockaddr *addr, struct sip_pvt *p)
- {
- if (!addr || !p) {
- return;
- }
- if (ast_sockaddr_cmp_addr(addr, &p->recv)) {
- char *tmp_str = ast_strdupa(ast_sockaddr_stringify_addr(addr));
- ast_debug(3, "NAT detected for %s / %s\n", tmp_str, ast_sockaddr_stringify_addr(&p->recv));
- p->natdetected = 1;
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
- ast_set_flag(&p->flags[0], SIP_NAT_FORCE_RPORT);
- }
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) {
- ast_set_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP);
- }
- } else {
- p->natdetected = 0;
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
- ast_clear_flag(&p->flags[0], SIP_NAT_FORCE_RPORT);
- }
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) {
- ast_clear_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP);
- }
- }
- }
- /*! \brief check Via: header for hostname, port and rport request/answer */
- static void check_via(struct sip_pvt *p, const struct sip_request *req)
- {
- char via[512];
- char *c, *maddr;
- struct ast_sockaddr tmp = { { 0, } };
- uint16_t port;
- ast_copy_string(via, sip_get_header(req, "Via"), sizeof(via));
- /* If this is via WebSocket we don't use the Via header contents at all */
- if (!strncasecmp(via, "SIP/2.0/WS", 10)) {
- return;
- }
- /* Work on the leftmost value of the topmost Via header */
- c = strchr(via, ',');
- if (c)
- *c = '\0';
- /* Check for rport */
- c = strstr(via, ";rport");
- if (c && (c[6] != '=')) { /* rport query, not answer */
- ast_set_flag(&p->flags[1], SIP_PAGE2_RPORT_PRESENT);
- ast_set_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT);
- }
- /* Check for maddr */
- maddr = strstr(via, "maddr=");
- if (maddr) {
- maddr += 6;
- c = maddr + strspn(maddr, "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.:[]");
- *c = '\0';
- }
- c = strchr(via, ';');
- if (c)
- *c = '\0';
- c = strchr(via, ' ');
- if (c) {
- *c = '\0';
- c = ast_skip_blanks(c+1);
- if (strcasecmp(via, "SIP/2.0/UDP") && strcasecmp(via, "SIP/2.0/TCP") && strcasecmp(via, "SIP/2.0/TLS")) {
- ast_log(LOG_WARNING, "Don't know how to respond via '%s'\n", via);
- return;
- }
- if (maddr && ast_sockaddr_resolve_first(&p->sa, maddr, 0)) {
- p->sa = p->recv;
- }
- if (ast_sockaddr_resolve_first(&tmp, c, 0)) {
- ast_log(LOG_WARNING, "Could not resolve socket address for '%s'\n", c);
- port = STANDARD_SIP_PORT;
- } else if (!(port = ast_sockaddr_port(&tmp))) {
- port = STANDARD_SIP_PORT;
- ast_sockaddr_set_port(&tmp, port);
- }
- ast_sockaddr_set_port(&p->sa, port);
- check_for_nat(&tmp, p);
- if (sip_debug_test_pvt(p)) {
- ast_verbose("Sending to %s (%s)\n",
- ast_sockaddr_stringify(sip_real_dst(p)),
- sip_nat_mode(p));
- }
- }
- }
- /*! \brief Validate device authentication */
- static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
- struct sip_request *req, int sipmethod, struct ast_sockaddr *addr,
- struct sip_peer **authpeer,
- enum xmittype reliable, char *calleridname, char *uri2)
- {
- enum check_auth_result res;
- int debug = sip_debug_test_addr(addr);
- struct sip_peer *peer;
- if (sipmethod == SIP_SUBSCRIBE) {
- /* For subscribes, match on device name only; for other methods,
- * match on IP address-port of the incoming request.
- */
- peer = sip_find_peer(of, NULL, TRUE, FINDALLDEVICES, FALSE, 0);
- } else {
- /* First find devices based on username (avoid all type=peer's) */
- peer = sip_find_peer(of, NULL, TRUE, FINDUSERS, FALSE, 0);
- /* Then find devices based on IP */
- if (!peer) {
- char *uri_tmp, *callback = NULL, *dummy;
- uri_tmp = ast_strdupa(uri2);
- parse_uri(uri_tmp, "sip:,sips:,tel:", &callback, &dummy, &dummy, &dummy);
- if (!ast_strlen_zero(callback) && (peer = sip_find_peer_by_ip_and_exten(&p->recv, callback, p->socket.type))) {
- ; /* found, fall through */
- } else {
- peer = sip_find_peer(NULL, &p->recv, TRUE, FINDPEERS, FALSE, p->socket.type);
- }
- }
- }
- if (!peer) {
- if (debug) {
- ast_verbose("No matching peer for '%s' from '%s'\n",
- of, ast_sockaddr_stringify(&p->recv));
- }
- /* If you don't mind, we can return 404s for devices that do
- * not exist: username disclosure. If we allow guests, there
- * is no way around that. */
- if (sip_cfg.allowguest || !sip_cfg.alwaysauthreject) {
- return AUTH_DONT_KNOW;
- }
- /* If you do mind, we use a peer that will never authenticate.
- * This ensures that we follow the same code path as regular
- * auth: less chance for username disclosure. */
- peer = bogus_peer;
- sip_ref_peer(peer, "sip_ref_peer: check_peer_ok: must ref bogus_peer so unreffing it does not fail");
- }
- /* build_peer, called through sip_find_peer, is not able to check the
- * sip_pvt->natdetected flag in order to determine if the peer is behind
- * NAT or not when SIP_PAGE3_NAT_AUTO_RPORT or SIP_PAGE3_NAT_AUTO_COMEDIA
- * are set on the peer. So we check for that here and set the peer's
- * address accordingly.
- */
- set_peer_nat(p, peer);
- if (p->natdetected && ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
- ast_sockaddr_copy(&peer->addr, &p->recv);
- }
- if (!ast_apply_acl(peer->acl, addr, "SIP Peer ACL: ")) {
- ast_debug(2, "Found peer '%s' for '%s', but fails host access\n", peer->name, of);
- sip_unref_peer(peer, "sip_unref_peer: check_peer_ok: from sip_find_peer call, early return of AUTH_ACL_FAILED");
- return AUTH_ACL_FAILED;
- }
- if (debug && peer != bogus_peer) {
- ast_verbose("Found peer '%s' for '%s' from %s\n",
- peer->name, of, ast_sockaddr_stringify(&p->recv));
- }
- /* Set Frame packetization */
- if (p->rtp) {
- ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(p->rtp), ast_format_cap_get_framing(peer->caps));
- p->autoframing = peer->autoframing;
- }
- /* Take the peer */
- ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
- ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
- ast_copy_flags(&p->flags[2], &peer->flags[2], SIP_PAGE3_FLAGS_TO_COPY);
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && p->udptl) {
- p->t38_maxdatagram = peer->t38_maxdatagram;
- set_t38_capabilities(p);
- }
- ast_rtp_dtls_cfg_copy(&peer->dtls_cfg, &p->dtls_cfg);
- /* Copy SIP extensions profile to peer */
- /* XXX is this correct before a successful auth ? */
- if (p->sipoptions)
- peer->sipoptions = p->sipoptions;
- do_setnat(p);
- ast_string_field_set(p, peersecret, peer->secret);
- ast_string_field_set(p, peermd5secret, peer->md5secret);
- ast_string_field_set(p, subscribecontext, peer->subscribecontext);
- ast_string_field_set(p, mohinterpret, peer->mohinterpret);
- ast_string_field_set(p, mohsuggest, peer->mohsuggest);
- if (!ast_strlen_zero(peer->parkinglot)) {
- ast_string_field_set(p, parkinglot, peer->parkinglot);
- }
- ast_string_field_set(p, engine, peer->engine);
- p->disallowed_methods = peer->disallowed_methods;
- set_pvt_allowed_methods(p, req);
- ast_cc_copy_config_params(p->cc_params, peer->cc_params);
- if (peer->callingpres) /* Peer calling pres setting will override RPID */
- p->callingpres = peer->callingpres;
- if (peer->maxms && peer->lastms)
- p->timer_t1 = peer->lastms < global_t1min ? global_t1min : peer->lastms;
- else
- p->timer_t1 = peer->timer_t1;
- /* Set timer B to control transaction timeouts */
- if (peer->timer_b)
- p->timer_b = peer->timer_b;
- else
- p->timer_b = 64 * p->timer_t1;
- p->allowtransfer = peer->allowtransfer;
- if (ast_test_flag(&peer->flags[0], SIP_INSECURE_INVITE)) {
- /* Pretend there is no required authentication */
- ast_string_field_set(p, peersecret, NULL);
- ast_string_field_set(p, peermd5secret, NULL);
- }
- if (!(res = check_auth(p, req, peer->name, p->peersecret, p->peermd5secret, sipmethod, uri2, reliable))) {
- /* If we have a call limit, set flag */
- if (peer->call_limit)
- ast_set_flag(&p->flags[0], SIP_CALL_LIMIT);
- ast_string_field_set(p, peername, peer->name);
- ast_string_field_set(p, authname, peer->name);
- ast_rtp_dtls_cfg_copy(&peer->dtls_cfg, &p->dtls_cfg);
- if (sipmethod == SIP_INVITE) {
- /* destroy old channel vars and copy in new ones. */
- ast_variables_destroy(p->chanvars);
- p->chanvars = copy_vars(peer->chanvars);
- }
- if (authpeer) {
- ao2_t_ref(peer, 1, "copy pointer into (*authpeer)");
- (*authpeer) = peer; /* Add a ref to the object here, to keep it in memory a bit longer if it is realtime */
- }
- if (!ast_strlen_zero(peer->username)) {
- ast_string_field_set(p, username, peer->username);
- /* Use the default username for authentication on outbound calls */
- /* XXX this takes the name from the caller... can we override ? */
- ast_string_field_set(p, authname, peer->username);
- }
- if (!get_rpid(p, req)) {
- if (!ast_strlen_zero(peer->cid_num)) {
- char *tmp = ast_strdupa(peer->cid_num);
- if (global_shrinkcallerid && ast_is_shrinkable_phonenumber(tmp))
- ast_shrink_phone_number(tmp);
- ast_string_field_set(p, cid_num, tmp);
- }
- if (!ast_strlen_zero(peer->cid_name))
- ast_string_field_set(p, cid_name, peer->cid_name);
- if (peer->callingpres)
- p->callingpres = peer->callingpres;
- }
- if (!ast_strlen_zero(peer->cid_tag)) {
- ast_string_field_set(p, cid_tag, peer->cid_tag);
- }
- ast_string_field_set(p, fullcontact, peer->fullcontact);
- if (!ast_strlen_zero(peer->context)) {
- ast_string_field_set(p, context, peer->context);
- }
- if (!ast_strlen_zero(peer->messagecontext)) {
- ast_string_field_set(p, messagecontext, peer->messagecontext);
- }
- if (!ast_strlen_zero(peer->mwi_from)) {
- ast_string_field_set(p, mwi_from, peer->mwi_from);
- }
- ast_string_field_set(p, peersecret, peer->secret);
- ast_string_field_set(p, peermd5secret, peer->md5secret);
- ast_string_field_set(p, language, peer->language);
- ast_string_field_set(p, accountcode, peer->accountcode);
- p->amaflags = peer->amaflags;
- p->callgroup = peer->callgroup;
- p->pickupgroup = peer->pickupgroup;
- ast_unref_namedgroups(p->named_callgroups);
- p->named_callgroups = ast_ref_namedgroups(peer->named_callgroups);
- ast_unref_namedgroups(p->named_pickupgroups);
- p->named_pickupgroups = ast_ref_namedgroups(peer->named_pickupgroups);
- ast_format_cap_remove_by_type(p->caps, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_append_from_cap(p->caps, peer->caps, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_remove_by_type(p->jointcaps, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_append_from_cap(p->jointcaps, peer->caps, AST_MEDIA_TYPE_UNKNOWN);
- ast_copy_string(p->zone, peer->zone, sizeof(p->zone));
- if (peer->maxforwards > 0) {
- p->maxforwards = peer->maxforwards;
- }
- if (ast_format_cap_count(p->peercaps)) {
- struct ast_format_cap *joint;
- joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- if (joint) {
- ast_format_cap_get_compatible(p->jointcaps, p->peercaps, joint);
- ao2_ref(p->jointcaps, -1);
- p->jointcaps = joint;
- }
- }
- p->maxcallbitrate = peer->maxcallbitrate;
- if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
- (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
- p->noncodeccapability |= AST_RTP_DTMF;
- else
- p->noncodeccapability &= ~AST_RTP_DTMF;
- p->jointnoncodeccapability = p->noncodeccapability;
- p->rtptimeout = peer->rtptimeout;
- p->rtpholdtimeout = peer->rtpholdtimeout;
- p->rtpkeepalive = peer->rtpkeepalive;
- if (!dialog_initialize_rtp(p)) {
- if (p->rtp) {
- ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(p->rtp), ast_format_cap_get_framing(peer->caps));
- p->autoframing = peer->autoframing;
- }
- } else {
- res = AUTH_RTP_FAILED;
- }
- }
- sip_unref_peer(peer, "check_peer_ok: sip_unref_peer: tossing temp ptr to peer from sip_find_peer");
- return res;
- }
- /*! \brief Check if matching user or peer is defined
- Match user on From: user name and peer on IP/port
- This is used on first invite (not re-invites) and subscribe requests
- \return 0 on success, non-zero on failure
- */
- static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_request *req,
- int sipmethod, const char *uri, enum xmittype reliable,
- struct ast_sockaddr *addr, struct sip_peer **authpeer)
- {
- char *of, *name, *unused_password, *domain;
- RAII_VAR(char *, ofbuf, NULL, ast_free); /* beware, everyone starts pointing to this */
- RAII_VAR(char *, namebuf, NULL, ast_free);
- enum check_auth_result res = AUTH_DONT_KNOW;
- char calleridname[256];
- char *uri2 = ast_strdupa(uri);
- terminate_uri(uri2); /* trim extra stuff */
- ofbuf = ast_strdup(sip_get_header(req, "From"));
- /* XXX here tries to map the username for invite things */
- /* strip the display-name portion off the beginning of the FROM header. */
- if (!(of = (char *) get_calleridname(ofbuf, calleridname, sizeof(calleridname)))) {
- ast_log(LOG_ERROR, "FROM header can not be parsed\n");
- return res;
- }
- if (calleridname[0]) {
- ast_string_field_set(p, cid_name, calleridname);
- }
- if (ast_strlen_zero(p->exten)) {
- char *t = uri2;
- if (!strncasecmp(t, "sip:", 4)) {
- t += 4;
- } else if (!strncasecmp(t, "sips:", 5)) {
- t += 5;
- } else if (!strncasecmp(t, "tel:", 4)) { /* TEL URI INVITE */
- t += 4;
- }
- ast_string_field_set(p, exten, t);
- t = strchr(p->exten, '@');
- if (t)
- *t = '\0';
- if (ast_strlen_zero(p->our_contact)) {
- build_contact(p, req, 1);
- }
- }
- of = get_in_brackets(of);
- /* save the URI part of the From header */
- ast_string_field_set(p, from, of);
- if (parse_uri_legacy_check(of, "sip:,sips:,tel:", &name, &unused_password, &domain, NULL)) {
- ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n");
- }
- SIP_PEDANTIC_DECODE(name);
- SIP_PEDANTIC_DECODE(domain);
- extract_host_from_hostport(&domain);
- if (ast_strlen_zero(domain)) {
- /* <sip:name@[EMPTY]>, never good */
- ast_log(LOG_ERROR, "Empty domain name in FROM header\n");
- return res;
- }
- if (ast_strlen_zero(name)) {
- /* <sip:[EMPTY][@]hostport>. Asterisk 1.4 and 1.6 have always
- * treated that as a username, so we continue the tradition:
- * uri is now <sip:host@hostport>. */
- name = domain;
- } else {
- /* Non-empty name, try to get caller id from it */
- char *tmp = ast_strdupa(name);
- /* We need to be able to handle from-headers looking like
- <sip:8164444422;phone-context=+1@1.2.3.4:5060;user=phone;tag=SDadkoa01-gK0c3bdb43>
- */
- tmp = strsep(&tmp, ";");
- if (global_shrinkcallerid && ast_is_shrinkable_phonenumber(tmp)) {
- ast_shrink_phone_number(tmp);
- }
- ast_string_field_set(p, cid_num, tmp);
- }
- if (global_match_auth_username) {
- /*
- * XXX This is experimental code to grab the search key from the
- * Auth header's username instead of the 'From' name, if available.
- * Do not enable this block unless you understand the side effects (if any!)
- * Note, the search for "username" should be done in a more robust way.
- * Note2, at the moment we check both fields, though maybe we should
- * pick one or another depending on the request ? XXX
- */
- const char *hdr = sip_get_header(req, "Authorization");
- if (ast_strlen_zero(hdr)) {
- hdr = sip_get_header(req, "Proxy-Authorization");
- }
- if (!ast_strlen_zero(hdr) && (hdr = strstr(hdr, "username=\""))) {
- namebuf = name = ast_strdup(hdr + strlen("username=\""));
- name = strsep(&name, "\"");
- }
- }
- res = check_peer_ok(p, name, req, sipmethod, addr,
- authpeer, reliable, calleridname, uri2);
- if (res != AUTH_DONT_KNOW) {
- return res;
- }
- /* Finally, apply the guest policy */
- if (sip_cfg.allowguest) {
- /* Ignore check_return warning from Coverity for get_rpid below. */
- get_rpid(p, req);
- p->rtptimeout = global_rtptimeout;
- p->rtpholdtimeout = global_rtpholdtimeout;
- p->rtpkeepalive = global_rtpkeepalive;
- if (!dialog_initialize_rtp(p)) {
- res = AUTH_SUCCESSFUL;
- } else {
- res = AUTH_RTP_FAILED;
- }
- } else {
- res = AUTH_SECRET_FAILED; /* we don't want any guests, authentication will fail */
- }
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_RPORT_PRESENT)) {
- ast_set_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT);
- }
- return res;
- }
- /*! \brief Find user
- If we get a match, this will add a reference pointer to the user object, that needs to be unreferenced
- */
- static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod, const char *uri, enum xmittype reliable, struct ast_sockaddr *addr)
- {
- return check_user_full(p, req, sipmethod, uri, reliable, addr, NULL);
- }
- static int set_message_vars_from_req(struct ast_msg *msg, struct sip_request *req)
- {
- size_t x;
- char name_buf[1024];
- char val_buf[1024];
- const char *name;
- char *c;
- int res = 0;
- for (x = 0; x < req->headers; x++) {
- const char *header = REQ_OFFSET_TO_STR(req, header[x]);
- if ((c = strchr(header, ':'))) {
- ast_copy_string(name_buf, header, MIN((c - header + 1), sizeof(name_buf)));
- ast_copy_string(val_buf, ast_skip_blanks(c + 1), sizeof(val_buf));
- ast_trim_blanks(name_buf);
- /* Convert header name to full name alias. */
- name = find_full_alias(name_buf, name_buf);
- res = ast_msg_set_var(msg, name, val_buf);
- if (res) {
- break;
- }
- }
- }
- return res;
- }
- /*! \brief Receive SIP MESSAGE method messages
- \note We only handle messages within current calls currently
- Reference: RFC 3428 */
- static void receive_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e)
- {
- char *buf;
- size_t len;
- struct ast_frame f;
- const char *content_type = sip_get_header(req, "Content-Type");
- struct ast_msg *msg;
- int res;
- char *from;
- char *to;
- char from_name[50];
- char stripped[SIPBUFSIZE];
- enum sip_get_dest_result dest_result;
- if (strncmp(content_type, "text/plain", strlen("text/plain"))) { /* No text/plain attachment */
- transmit_response(p, "415 Unsupported Media Type", req); /* Good enough, or? */
- if (!p->owner) {
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- }
- return;
- }
- if (!(buf = get_content(req))) {
- ast_log(LOG_WARNING, "Unable to retrieve text from %s\n", p->callid);
- transmit_response(p, "500 Internal Server Error", req);
- if (!p->owner) {
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- }
- return;
- }
- /* Strip trailing line feeds from message body. (get_content may add
- * a trailing linefeed and we don't need any at the end) */
- len = strlen(buf);
- while (len > 0) {
- if (buf[--len] != '\n') {
- ++len;
- break;
- }
- }
- buf[len] = '\0';
- if (p->owner) {
- if (sip_debug_test_pvt(p)) {
- ast_verbose("SIP Text message received: '%s'\n", buf);
- }
- memset(&f, 0, sizeof(f));
- f.frametype = AST_FRAME_TEXT;
- f.subclass.integer = 0;
- f.offset = 0;
- f.data.ptr = buf;
- f.datalen = strlen(buf) + 1;
- ast_queue_frame(p->owner, &f);
- transmit_response(p, "202 Accepted", req); /* We respond 202 accepted, since we relay the message */
- return;
- }
- /*
- * At this point MESSAGE is outside of a call.
- *
- * NOTE: p->owner is NULL so no additional check is needed after
- * this point.
- */
- if (!sip_cfg.accept_outofcall_message) {
- /* Message outside of a call, we do not support that */
- ast_debug(1, "MESSAGE outside of a call administratively disabled.\n");
- transmit_response(p, "405 Method Not Allowed", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- }
- copy_request(&p->initreq, req);
- if (sip_cfg.auth_message_requests) {
- int res;
- set_pvt_allowed_methods(p, req);
- res = check_user(p, req, SIP_MESSAGE, e, XMIT_UNRELIABLE, addr);
- if (res == AUTH_CHALLENGE_SENT) {
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- }
- if (res < 0) { /* Something failed in authentication */
- ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", sip_get_header(req, "From"));
- transmit_response(p, "403 Forbidden", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- }
- /* Auth was successful. Proceed. */
- } else {
- struct sip_peer *peer;
- /*
- * MESSAGE outside of a call, not authenticating it.
- * Check to see if we match a peer anyway so that we can direct
- * it to the right context.
- */
- peer = sip_find_peer(NULL, &p->recv, TRUE, FINDPEERS, 0, p->socket.type);
- if (peer) {
- /* Only if no auth is required. */
- if (ast_strlen_zero(peer->secret) && ast_strlen_zero(peer->md5secret)) {
- ast_string_field_set(p, context, peer->context);
- }
- if (!ast_strlen_zero(peer->messagecontext)) {
- ast_string_field_set(p, messagecontext, peer->messagecontext);
- }
- ast_string_field_set(p, peername, peer->name);
- peer = sip_unref_peer(peer, "from sip_find_peer() in receive_message");
- }
- }
- /* Override the context with the message context _BEFORE_
- * getting the destination. This way we can guarantee the correct
- * extension is used in the message context when it is present. */
- if (!ast_strlen_zero(p->messagecontext)) {
- ast_string_field_set(p, context, p->messagecontext);
- } else if (!ast_strlen_zero(sip_cfg.messagecontext)) {
- ast_string_field_set(p, context, sip_cfg.messagecontext);
- }
- dest_result = get_destination(p, NULL, NULL);
- switch (dest_result) {
- case SIP_GET_DEST_REFUSED:
- /* Okay to send 403 since this is after auth processing */
- transmit_response(p, "403 Forbidden", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- case SIP_GET_DEST_INVALID_URI:
- transmit_response(p, "416 Unsupported URI Scheme", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- default:
- /* We may have something other than dialplan who wants
- * the message, so defer further error handling for now */
- break;
- }
- if (!(msg = ast_msg_alloc())) {
- transmit_response(p, "500 Internal Server Error", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- }
- to = ast_strdupa(REQ_OFFSET_TO_STR(req, rlpart2));
- from = ast_strdupa(sip_get_header(req, "From"));
- res = ast_msg_set_to(msg, "%s", to);
- /* Build "display" <uri> for from string. */
- from = (char *) get_calleridname(from, from_name, sizeof(from_name));
- from = get_in_brackets(from);
- if (from_name[0]) {
- char from_buf[128];
- ast_escape_quoted(from_name, from_buf, sizeof(from_buf));
- res |= ast_msg_set_from(msg, "\"%s\" <%s>", from_buf, from);
- } else {
- res |= ast_msg_set_from(msg, "<%s>", from);
- }
- res |= ast_msg_set_body(msg, "%s", buf);
- res |= ast_msg_set_context(msg, "%s", p->context);
- res |= ast_msg_set_var(msg, "SIP_RECVADDR", ast_sockaddr_stringify(&p->recv));
- res |= ast_msg_set_tech(msg, "%s", "SIP");
- if (!ast_strlen_zero(p->peername)) {
- res |= ast_msg_set_endpoint(msg, "%s", p->peername);
- res |= ast_msg_set_var(msg, "SIP_PEERNAME", p->peername);
- }
- ast_copy_string(stripped, sip_get_header(req, "Contact"), sizeof(stripped));
- res |= ast_msg_set_var(msg, "SIP_FULLCONTACT", get_in_brackets(stripped));
- res |= ast_msg_set_exten(msg, "%s", p->exten);
- res |= set_message_vars_from_req(msg, req);
- if (res) {
- ast_msg_destroy(msg);
- transmit_response(p, "500 Internal Server Error", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- }
- if (ast_msg_has_destination(msg)) {
- ast_msg_queue(msg);
- transmit_response(p, "202 Accepted", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- }
- /* Find a specific error cause to send */
- switch (dest_result) {
- case SIP_GET_DEST_EXTEN_NOT_FOUND:
- case SIP_GET_DEST_EXTEN_MATCHMORE:
- transmit_response(p, "404 Not Found", req);
- break;
- case SIP_GET_DEST_EXTEN_FOUND:
- default:
- /* We should have sent the message already! */
- ast_assert(0);
- transmit_response(p, "500 Internal Server Error", req);
- break;
- }
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- ast_msg_destroy(msg);
- }
- /*! \brief CLI Command to show calls within limits set by call_limit */
- static char *sip_show_inuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- #define FORMAT "%-25.25s %-15.15s %-15.15s \n"
- #define FORMAT2 "%-25.25s %-15.15s %-15.15s \n"
- char ilimits[40];
- char iused[40];
- int showall = FALSE;
- struct ao2_iterator i;
- struct sip_peer *peer;
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show inuse";
- e->usage =
- "Usage: sip show inuse [all]\n"
- " List all SIP devices usage counters and limits.\n"
- " Add option \"all\" to show all devices, not only those with a limit.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc < 3)
- return CLI_SHOWUSAGE;
- if (a->argc == 4 && !strcmp(a->argv[3], "all"))
- showall = TRUE;
- ast_cli(a->fd, FORMAT, "* Peer name", "In use", "Limit");
- i = ao2_iterator_init(peers, 0);
- while ((peer = ao2_t_iterator_next(&i, "iterate thru peer table"))) {
- ao2_lock(peer);
- if (peer->call_limit)
- snprintf(ilimits, sizeof(ilimits), "%d", peer->call_limit);
- else
- ast_copy_string(ilimits, "N/A", sizeof(ilimits));
- snprintf(iused, sizeof(iused), "%d/%d/%d", peer->inuse, peer->ringing, peer->onhold);
- if (showall || peer->call_limit)
- ast_cli(a->fd, FORMAT2, peer->name, iused, ilimits);
- ao2_unlock(peer);
- sip_unref_peer(peer, "toss iterator pointer");
- }
- ao2_iterator_destroy(&i);
- return CLI_SUCCESS;
- #undef FORMAT
- #undef FORMAT2
- }
- /*! \brief Convert transfer mode to text string */
- static char *transfermode2str(enum transfermodes mode)
- {
- if (mode == TRANSFER_OPENFORALL)
- return "open";
- else if (mode == TRANSFER_CLOSED)
- return "closed";
- return "strict";
- }
- /*! \brief Report Peer status in character string
- * \return 0 if peer is unreachable, 1 if peer is online, -1 if unmonitored
- */
- /* Session-Timer Modes */
- static const struct _map_x_s stmodes[] = {
- { SESSION_TIMER_MODE_ACCEPT, "Accept"},
- { SESSION_TIMER_MODE_ORIGINATE, "Originate"},
- { SESSION_TIMER_MODE_REFUSE, "Refuse"},
- { -1, NULL},
- };
- static const char *stmode2str(enum st_mode m)
- {
- return map_x_s(stmodes, m, "Unknown");
- }
- static enum st_mode str2stmode(const char *s)
- {
- return map_s_x(stmodes, s, -1);
- }
- /* Session-Timer Refreshers */
- static const struct _map_x_s strefresher_params[] = {
- { SESSION_TIMER_REFRESHER_PARAM_UNKNOWN, "unknown" },
- { SESSION_TIMER_REFRESHER_PARAM_UAC, "uac" },
- { SESSION_TIMER_REFRESHER_PARAM_UAS, "uas" },
- { -1, NULL },
- };
- static const struct _map_x_s strefreshers[] = {
- { SESSION_TIMER_REFRESHER_AUTO, "auto" },
- { SESSION_TIMER_REFRESHER_US, "us" },
- { SESSION_TIMER_REFRESHER_THEM, "them" },
- { -1, NULL },
- };
- static const char *strefresherparam2str(enum st_refresher_param r)
- {
- return map_x_s(strefresher_params, r, "Unknown");
- }
- static enum st_refresher_param str2strefresherparam(const char *s)
- {
- return map_s_x(strefresher_params, s, -1);
- }
- /* Autocreatepeer modes */
- static struct _map_x_s autopeermodes[] = {
- { AUTOPEERS_DISABLED, "Off"},
- { AUTOPEERS_VOLATILE, "Volatile"},
- { AUTOPEERS_PERSIST, "Persisted"},
- { -1, NULL},
- };
- static const char *strefresher2str(enum st_refresher r)
- {
- return map_x_s(strefreshers, r, "Unknown");
- }
- static const char *autocreatepeer2str(enum autocreatepeer_mode r)
- {
- return map_x_s(autopeermodes, r, "Unknown");
- }
- static int peer_status(struct sip_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);
- /* Checking if port is 0 */
- res = -1;
- }
- return res;
- }
- /*! \brief Show active TCP connections */
- static char *sip_show_tcp(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct sip_threadinfo *th;
- struct ao2_iterator i;
- #define FORMAT2 "%-47.47s %9.9s %6.6s\n"
- #define FORMAT "%-47.47s %-9.9s %-6.6s\n"
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show tcp";
- e->usage =
- "Usage: sip show tcp\n"
- " Lists all active TCP/TLS sessions.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
- ast_cli(a->fd, FORMAT2, "Address", "Transport", "Type");
- i = ao2_iterator_init(threadt, 0);
- while ((th = ao2_t_iterator_next(&i, "iterate through tcp threads for 'sip show tcp'"))) {
- ast_cli(a->fd, FORMAT,
- ast_sockaddr_stringify(&th->tcptls_session->remote_address),
- sip_get_transport(th->type),
- (th->tcptls_session->client ? "Client" : "Server"));
- ao2_t_ref(th, -1, "decrement ref from iterator");
- }
- ao2_iterator_destroy(&i);
- return CLI_SUCCESS;
- #undef FORMAT
- #undef FORMAT2
- }
- /*! \brief CLI Command 'SIP Show Users' */
- static char *sip_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- regex_t regexbuf;
- int havepattern = FALSE;
- struct ao2_iterator user_iter;
- struct sip_peer *user;
- #define FORMAT "%-25.25s %-15.15s %-15.15s %-15.15s %-5.5s%-10.10s\n"
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show users";
- e->usage =
- "Usage: sip show users [like <pattern>]\n"
- " Lists all known SIP 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 = TRUE;
- } else
- return CLI_SHOWUSAGE;
- case 3:
- break;
- default:
- return CLI_SHOWUSAGE;
- }
- ast_cli(a->fd, FORMAT, "Username", "Secret", "Accountcode", "Def.Context", "ACL", "Forcerport");
- user_iter = ao2_iterator_init(peers, 0);
- while ((user = ao2_t_iterator_next(&user_iter, "iterate thru peers table"))) {
- ao2_lock(user);
- if (!(user->type & SIP_TYPE_USER)) {
- ao2_unlock(user);
- sip_unref_peer(user, "sip show users");
- continue;
- }
- if (havepattern && regexec(®exbuf, user->name, 0, NULL, 0)) {
- ao2_unlock(user);
- sip_unref_peer(user, "sip show users");
- continue;
- }
- ast_cli(a->fd, FORMAT, user->name,
- user->secret,
- user->accountcode,
- user->context,
- AST_CLI_YESNO(ast_acl_list_is_empty(user->acl) == 0),
- AST_CLI_YESNO(ast_test_flag(&user->flags[0], SIP_NAT_FORCE_RPORT)));
- ao2_unlock(user);
- sip_unref_peer(user, "sip show users");
- }
- ao2_iterator_destroy(&user_iter);
- if (havepattern)
- regfree(®exbuf);
- return CLI_SUCCESS;
- #undef FORMAT
- }
- /*! \brief Show SIP registrations in the manager API */
- static int manager_show_registry(struct mansession *s, const struct message *m)
- {
- const char *id = astman_get_header(m, "ActionID");
- char idtext[256] = "";
- int total = 0;
- struct ao2_iterator iter;
- struct sip_registry *iterator;
- if (!ast_strlen_zero(id))
- snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
- astman_send_listack(s, m, "Registrations will follow", "start");
- iter = ao2_iterator_init(registry_list, 0);
- while ((iterator = ao2_t_iterator_next(&iter, "manager_show_registry iter"))) {
- ao2_lock(iterator);
- astman_append(s,
- "Event: RegistryEntry\r\n"
- "%s"
- "Host: %s\r\n"
- "Port: %d\r\n"
- "Username: %s\r\n"
- "Domain: %s\r\n"
- "DomainPort: %d\r\n"
- "Refresh: %d\r\n"
- "State: %s\r\n"
- "RegistrationTime: %ld\r\n"
- "\r\n",
- idtext,
- iterator->hostname,
- iterator->portno ? iterator->portno : STANDARD_SIP_PORT,
- iterator->username,
- S_OR(iterator->regdomain,iterator->hostname),
- iterator->regdomainport ? iterator->regdomainport : STANDARD_SIP_PORT,
- iterator->refresh,
- regstate2str(iterator->regstate),
- (long) iterator->regtime.tv_sec);
- ao2_unlock(iterator);
- ao2_t_ref(iterator, -1, "manager_show_registry iter");
- total++;
- }
- ao2_iterator_destroy(&iter);
- astman_send_list_complete_start(s, m, "RegistrationsComplete", total);
- astman_send_list_complete_end(s);
- return 0;
- }
- /*! \brief Show SIP peers in the manager API */
- /* Inspired from chan_iax2 */
- static int manager_sip_show_peers(struct mansession *s, const struct message *m)
- {
- const char *id = astman_get_header(m, "ActionID");
- const char *a[] = {"sip", "show", "peers"};
- char idtext[256] = "";
- int total = 0;
- if (!ast_strlen_zero(id))
- snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
- astman_send_listack(s, m, "Peer status list will follow", "start");
- /* List the peers in separate manager events */
- _sip_show_peers(-1, &total, s, m, 3, a);
- /* Send final confirmation */
- astman_send_list_complete_start(s, m, "PeerlistComplete", total);
- astman_send_list_complete_end(s);
- return 0;
- }
- /*! \brief CLI Show Peers command */
- static char *sip_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show peers";
- e->usage =
- "Usage: sip show peers [like <pattern>]\n"
- " Lists all known SIP peers.\n"
- " Optional regular expression pattern is used to filter the peer list.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- return _sip_show_peers(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
- }
- int peercomparefunc(const void *a, const void *b);
- int peercomparefunc(const void *a, const void *b)
- {
- struct sip_peer **ap = (struct sip_peer **)a;
- struct sip_peer **bp = (struct sip_peer **)b;
- return strcmp((*ap)->name, (*bp)->name);
- }
- /* the last argument is left-aligned, so we don't need a size anyways */
- #define PEERS_FORMAT2 "%-25.25s %-39.39s %-3.3s %-10.10s %-10.10s %-3.3s %-8s %-11s %-32.32s %s\n"
- /*! \brief Used in the sip_show_peers functions to pass parameters */
- struct show_peers_context {
- regex_t regexbuf;
- int havepattern;
- char idtext[256];
- int realtimepeers;
- int peers_mon_online;
- int peers_mon_offline;
- int peers_unmon_offline;
- int peers_unmon_online;
- };
- /*! \brief Execute sip show peers command */
- static char *_sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
- {
- struct show_peers_context cont = {
- .havepattern = FALSE,
- .idtext = "",
- .peers_mon_online = 0,
- .peers_mon_offline = 0,
- .peers_unmon_online = 0,
- .peers_unmon_offline = 0,
- };
- struct sip_peer *peer;
- struct ao2_iterator* it_peers;
- int total_peers = 0;
- const char *id;
- struct sip_peer **peerarray;
- int k;
- cont.realtimepeers = ast_check_realtime("sippeers");
- if (s) { /* Manager - get ActionID */
- id = astman_get_header(m, "ActionID");
- if (!ast_strlen_zero(id)) {
- snprintf(cont.idtext, sizeof(cont.idtext), "ActionID: %s\r\n", id);
- }
- }
- switch (argc) {
- case 5:
- if (!strcasecmp(argv[3], "like")) {
- if (regcomp(&cont.regexbuf, argv[4], REG_EXTENDED | REG_NOSUB)) {
- return CLI_SHOWUSAGE;
- }
- cont.havepattern = TRUE;
- } else {
- return CLI_SHOWUSAGE;
- }
- case 3:
- break;
- default:
- return CLI_SHOWUSAGE;
- }
- if (!s) {
- /* Normal list */
- ast_cli(fd, PEERS_FORMAT2, "Name/username", "Host", "Dyn", "Forcerport", "Comedia", "ACL", "Port", "Status", "Description", (cont.realtimepeers ? "Realtime" : ""));
- }
- ao2_lock(peers);
- if (!(it_peers = ao2_callback(peers, OBJ_MULTIPLE, NULL, NULL))) {
- ast_log(AST_LOG_ERROR, "Unable to create iterator for peers container for sip show peers\n");
- ao2_unlock(peers);
- return CLI_FAILURE;
- }
- if (!(peerarray = ast_calloc(sizeof(struct sip_peer *), ao2_container_count(peers)))) {
- ast_log(AST_LOG_ERROR, "Unable to allocate peer array for sip show peers\n");
- ao2_iterator_destroy(it_peers);
- ao2_unlock(peers);
- return CLI_FAILURE;
- }
- ao2_unlock(peers);
- while ((peer = ao2_t_iterator_next(it_peers, "iterate thru peers table"))) {
- ao2_lock(peer);
- if (!(peer->type & SIP_TYPE_PEER)) {
- ao2_unlock(peer);
- sip_unref_peer(peer, "unref peer because it's actually a user");
- continue;
- }
- if (cont.havepattern && regexec(&cont.regexbuf, peer->name, 0, NULL, 0)) {
- ao2_unlock(peer);
- sip_unref_peer(peer, "toss iterator peer ptr before continue");
- continue;
- }
- peerarray[total_peers++] = peer;
- ao2_unlock(peer);
- }
- ao2_iterator_destroy(it_peers);
- qsort(peerarray, total_peers, sizeof(struct sip_peer *), peercomparefunc);
- for(k = 0; k < total_peers; k++) {
- peerarray[k] = _sip_show_peers_one(fd, s, &cont, peerarray[k]);
- }
- if (!s) {
- ast_cli(fd, "%d sip peers [Monitored: %d online, %d offline Unmonitored: %d online, %d offline]\n",
- total_peers, cont.peers_mon_online, cont.peers_mon_offline, cont.peers_unmon_online, cont.peers_unmon_offline);
- }
- if (cont.havepattern) {
- regfree(&cont.regexbuf);
- }
- if (total) {
- *total = total_peers;
- }
- ast_free(peerarray);
- return CLI_SUCCESS;
- }
- /*! \brief Emit informations for one peer during sip show peers command */
- static struct sip_peer *_sip_show_peers_one(int fd, struct mansession *s, struct show_peers_context *cont, struct sip_peer *peer)
- {
- /* _sip_show_peers_one() is separated from _sip_show_peers() to properly free the ast_strdupa
- * (this is executed in a loop in _sip_show_peers() )
- */
- char name[256];
- char status[20] = "";
- char pstatus;
- /*
- * tmp_port and tmp_host store copies of ast_sockaddr_stringify strings since the
- * string pointers for that function aren't valid between subsequent calls to
- * ast_sockaddr_stringify functions
- */
- char *tmp_port;
- char *tmp_host;
- tmp_port = ast_sockaddr_isnull(&peer->addr) ?
- "0" : ast_strdupa(ast_sockaddr_stringify_port(&peer->addr));
- tmp_host = ast_sockaddr_isnull(&peer->addr) ?
- "(Unspecified)" : ast_strdupa(ast_sockaddr_stringify_addr(&peer->addr));
- ao2_lock(peer);
- if (cont->havepattern && regexec(&cont->regexbuf, peer->name, 0, NULL, 0)) {
- ao2_unlock(peer);
- return sip_unref_peer(peer, "toss iterator peer ptr no match");
- }
- if (!ast_strlen_zero(peer->username) && !s) {
- snprintf(name, sizeof(name), "%s/%s", peer->name, peer->username);
- } else {
- ast_copy_string(name, peer->name, sizeof(name));
- }
- pstatus = peer_status(peer, status, sizeof(status));
- if (pstatus == 1) {
- cont->peers_mon_online++;
- } else if (pstatus == 0) {
- cont->peers_mon_offline++;
- } else {
- if (ast_sockaddr_isnull(&peer->addr) ||
- !ast_sockaddr_port(&peer->addr)) {
- cont->peers_unmon_offline++;
- } else {
- cont->peers_unmon_online++;
- }
- }
- if (!s) { /* Normal CLI list */
- ast_cli(fd, PEERS_FORMAT2, name,
- tmp_host,
- peer->host_dynamic ? " D " : " ", /* Dynamic or not? */
- force_rport_string(peer->flags),
- comedia_string(peer->flags),
- (!ast_acl_list_is_empty(peer->acl)) ? " A " : " ", /* permit/deny */
- tmp_port, status,
- peer->description ? peer->description : "",
- cont->realtimepeers ? (peer->is_realtime ? "Cached RT" : "") : "");
- } else { /* Manager format */
- /* The names here need to be the same as other channels */
- astman_append(s,
- "Event: PeerEntry\r\n%s"
- "Channeltype: SIP\r\n"
- "ObjectName: %s\r\n"
- "ChanObjectType: peer\r\n" /* "peer" or "user" */
- "IPaddress: %s\r\n"
- "IPport: %s\r\n"
- "Dynamic: %s\r\n"
- "AutoForcerport: %s\r\n"
- "Forcerport: %s\r\n"
- "AutoComedia: %s\r\n"
- "Comedia: %s\r\n"
- "VideoSupport: %s\r\n"
- "TextSupport: %s\r\n"
- "ACL: %s\r\n"
- "Status: %s\r\n"
- "RealtimeDevice: %s\r\n"
- "Description: %s\r\n\r\n",
- cont->idtext,
- peer->name,
- ast_sockaddr_isnull(&peer->addr) ? "-none-" : tmp_host,
- ast_sockaddr_isnull(&peer->addr) ? "0" : tmp_port,
- peer->host_dynamic ? "yes" : "no", /* Dynamic or not? */
- ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) ? "yes" : "no",
- ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? "yes" : "no",
- ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA) ? "yes" : "no",
- ast_test_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP) ? "yes" : "no",
- ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "yes" : "no", /* VIDEOSUPPORT=yes? */
- ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "yes" : "no", /* TEXTSUPPORT=yes? */
- ast_acl_list_is_empty(peer->acl) ? "no" : "yes", /* permit/deny/acl */
- status,
- cont->realtimepeers ? (peer->is_realtime ? "yes" : "no") : "no",
- peer->description);
- }
- ao2_unlock(peer);
- return sip_unref_peer(peer, "toss iterator peer ptr");
- }
- #undef PEERS_FORMAT2
- static int peer_dump_func(void *userobj, void *arg, int flags)
- {
- struct sip_peer *peer = userobj;
- int refc = ao2_t_ref(userobj, 0, "");
- struct ast_cli_args *a = (struct ast_cli_args *) arg;
- ast_cli(a->fd, "name: %s\ntype: peer\nobjflags: %d\nrefcount: %d\n\n",
- peer->name, 0, refc);
- return 0;
- }
- static int dialog_dump_func(void *userobj, void *arg, int flags)
- {
- struct sip_pvt *pvt = userobj;
- int refc = ao2_t_ref(userobj, 0, "");
- struct ast_cli_args *a = (struct ast_cli_args *) arg;
- ast_cli(a->fd, "name: %s\ntype: dialog\nobjflags: %d\nrefcount: %d\n\n",
- pvt->callid, 0, refc);
- return 0;
- }
- /*! \brief List all allocated SIP Objects (realtime or static) */
- static char *sip_show_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct sip_registry *reg;
- struct ao2_iterator iter;
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show objects";
- e->usage =
- "Usage: sip show objects\n"
- " Lists status of known SIP objects\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
- ast_cli(a->fd, "-= Peer objects: %d static, %d realtime, %d autocreate =-\n\n", speerobjs, rpeerobjs, apeerobjs);
- ao2_t_callback(peers, OBJ_NODATA, peer_dump_func, a, "initiate ao2_callback to dump peers");
- ast_cli(a->fd, "-= Peer objects by IP =-\n\n");
- ao2_t_callback(peers_by_ip, OBJ_NODATA, peer_dump_func, a, "initiate ao2_callback to dump peers_by_ip");
- iter = ao2_iterator_init(registry_list, 0);
- ast_cli(a->fd, "-= Registry objects: %d =-\n\n", ao2_container_count(registry_list));
- while ((reg = ao2_t_iterator_next(&iter, "sip_show_objects iter"))) {
- ao2_lock(reg);
- ast_cli(a->fd, "name: %s\n", reg->configvalue);
- ao2_unlock(reg);
- ao2_t_ref(reg, -1, "sip_show_objects iter");
- }
- ao2_iterator_destroy(&iter);
- ast_cli(a->fd, "-= Dialog objects:\n\n");
- ao2_t_callback(dialogs, OBJ_NODATA, dialog_dump_func, a, "initiate ao2_callback to dump dialogs");
- return CLI_SUCCESS;
- }
- /*! \brief Print call group and pickup group */
- static void print_group(int fd, ast_group_t group, int crlf)
- {
- char buf[256];
- ast_cli(fd, crlf ? "%s\r\n" : "%s\n", ast_print_group(buf, sizeof(buf), group) );
- }
- /*! \brief Print named call groups and pickup groups */
- static void print_named_groups(int fd, struct ast_namedgroups *group, int crlf)
- {
- struct ast_str *buf = ast_str_create(1024);
- if (buf) {
- ast_cli(fd, crlf ? "%s\r\n" : "%s\n", ast_print_namedgroups(&buf, group) );
- ast_free(buf);
- }
- }
- /*! \brief mapping between dtmf flags and strings */
- static const struct _map_x_s dtmfstr[] = {
- { SIP_DTMF_RFC2833, "rfc2833" },
- { SIP_DTMF_INFO, "info" },
- { SIP_DTMF_SHORTINFO, "shortinfo" },
- { SIP_DTMF_INBAND, "inband" },
- { SIP_DTMF_AUTO, "auto" },
- { -1, NULL }, /* terminator */
- };
- /*! \brief Convert DTMF mode to printable string */
- static const char *dtmfmode2str(int mode)
- {
- return map_x_s(dtmfstr, mode, "<error>");
- }
- /*! \brief maps a string to dtmfmode, returns -1 on error */
- static int str2dtmfmode(const char *str)
- {
- return map_s_x(dtmfstr, str, -1);
- }
- static const struct _map_x_s insecurestr[] = {
- { SIP_INSECURE_PORT, "port" },
- { SIP_INSECURE_INVITE, "invite" },
- { SIP_INSECURE_PORT | SIP_INSECURE_INVITE, "port,invite" },
- { 0, "no" },
- { -1, NULL }, /* terminator */
- };
- /*! \brief Convert Insecure setting to printable string */
- static const char *insecure2str(int mode)
- {
- return map_x_s(insecurestr, mode, "<error>");
- }
- static const struct _map_x_s allowoverlapstr[] = {
- { SIP_PAGE2_ALLOWOVERLAP_YES, "Yes" },
- { SIP_PAGE2_ALLOWOVERLAP_DTMF, "DTMF" },
- { SIP_PAGE2_ALLOWOVERLAP_NO, "No" },
- { -1, NULL }, /* terminator */
- };
- /*! \brief Convert AllowOverlap setting to printable string */
- static const char *allowoverlap2str(int mode)
- {
- return map_x_s(allowoverlapstr, mode, "<error>");
- }
- static const struct _map_x_s trust_id_outboundstr[] = {
- { SIP_PAGE2_TRUST_ID_OUTBOUND_LEGACY, "Legacy" },
- { SIP_PAGE2_TRUST_ID_OUTBOUND_NO, "No" },
- { SIP_PAGE2_TRUST_ID_OUTBOUND_YES, "Yes" },
- { -1, NULL }, /* terminator */
- };
- static const char *trust_id_outbound2str(int mode)
- {
- return map_x_s(trust_id_outboundstr, mode, "<error>");
- }
- /*! \brief Destroy disused contexts between reloads
- Only used in reload_config so the code for regcontext doesn't get ugly
- */
- static void cleanup_stale_contexts(char *new, char *old)
- {
- char *oldcontext, *newcontext, *stalecontext, *stringp, newlist[AST_MAX_CONTEXT];
- while ((oldcontext = strsep(&old, "&"))) {
- stalecontext = NULL;
- ast_copy_string(newlist, new, sizeof(newlist));
- stringp = newlist;
- while ((newcontext = strsep(&stringp, "&"))) {
- if (!strcmp(newcontext, oldcontext)) {
- /* This is not the context you're looking for */
- stalecontext = NULL;
- break;
- } else if (strcmp(newcontext, oldcontext)) {
- stalecontext = oldcontext;
- }
- }
- if (stalecontext)
- ast_context_destroy(ast_context_find(stalecontext), "SIP");
- }
- }
- /*!
- * \brief Check RTP Timeout on dialogs
- *
- * \details This is used with ao2_callback to check rtptimeout
- * rtponholdtimeout and send rtpkeepalive packets.
- *
- * \return CMP_MATCH for items to be unlinked from dialogs_rtpcheck.
- */
- static int dialog_checkrtp_cb(void *dialogobj, void *arg, int flags)
- {
- struct sip_pvt *dialog = dialogobj;
- time_t *t = arg;
- int match_status;
- if (sip_pvt_trylock(dialog)) {
- return 0;
- }
- if (dialog->rtp || dialog->vrtp) {
- match_status = check_rtp_timeout(dialog, *t);
- } else {
- /* Dialog has no active RTP or VRTP. unlink it from dialogs_rtpcheck. */
- match_status = CMP_MATCH;
- }
- sip_pvt_unlock(dialog);
- return match_status;
- }
- /*!
- * \brief Match dialogs that need to be destroyed
- *
- * \details This is used with ao2_callback to unlink/delete all dialogs that
- * are marked needdestroy.
- *
- * \todo Re-work this to improve efficiency. Currently, this function is called
- * on _every_ dialog after processing _every_ incoming SIP/UDP packet, or
- * potentially even more often when the scheduler has entries to run.
- */
- static int dialog_needdestroy(void *dialogobj, void *arg, int flags)
- {
- struct sip_pvt *dialog = dialogobj;
- if (sip_pvt_trylock(dialog)) {
- /* Don't block the monitor thread. This function is called often enough
- * that we can wait for the next time around. */
- return 0;
- }
- /* If we have sessions that needs to be destroyed, do it now */
- /* Check if we have outstanding requests not responsed to or an active call
- - if that's the case, wait with destruction */
- if (dialog->needdestroy && !dialog->packets && !dialog->owner) {
- /* We absolutely cannot destroy the rtp struct while a bridge is active or we WILL crash */
- if (dialog->rtp && ast_rtp_instance_get_bridged(dialog->rtp)) {
- ast_debug(2, "Bridge still active. Delaying destruction of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text);
- sip_pvt_unlock(dialog);
- return 0;
- }
- if (dialog->vrtp && ast_rtp_instance_get_bridged(dialog->vrtp)) {
- ast_debug(2, "Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text);
- sip_pvt_unlock(dialog);
- return 0;
- }
- sip_pvt_unlock(dialog);
- /* no, the unlink should handle this: dialog_unref(dialog, "needdestroy: one more refcount decrement to allow dialog to be destroyed"); */
- /* the CMP_MATCH will unlink this dialog from the dialog hash table */
- dialog_unlink_all(dialog);
- return 0; /* the unlink_all should unlink this from the table, so.... no need to return a match */
- }
- sip_pvt_unlock(dialog);
- return 0;
- }
- /*! \brief Remove temporary realtime objects from memory (CLI) */
- /*! \todo XXXX Propably needs an overhaul after removal of the devices */
- static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct sip_peer *peer, *pi;
- int prunepeer = FALSE;
- int multi = FALSE;
- const char *name = NULL;
- regex_t regexbuf;
- int havepattern = 0;
- struct ao2_iterator i;
- static const char * const choices[] = { "all", "like", NULL };
- char *cmplt;
- if (cmd == CLI_INIT) {
- e->command = "sip prune realtime [peer|all]";
- e->usage =
- "Usage: sip prune realtime [peer [<name>|all|like <pattern>]|all]\n"
- " Prunes object(s) from the cache.\n"
- " Optional regular expression pattern is used to filter the objects.\n";
- return NULL;
- } else if (cmd == CLI_GENERATE) {
- if (a->pos == 4 && !strcasecmp(a->argv[3], "peer")) {
- cmplt = ast_cli_complete(a->word, choices, a->n);
- if (!cmplt)
- cmplt = complete_sip_peer(a->word, a->n - sizeof(choices), SIP_PAGE2_RTCACHEFRIENDS);
- return cmplt;
- }
- if (a->pos == 5 && !strcasecmp(a->argv[4], "like"))
- return complete_sip_peer(a->word, a->n, SIP_PAGE2_RTCACHEFRIENDS);
- return NULL;
- }
- switch (a->argc) {
- case 4:
- name = a->argv[3];
- /* we accept a name in position 3, but keywords are not good. */
- if (!strcasecmp(name, "peer") || !strcasecmp(name, "like"))
- return CLI_SHOWUSAGE;
- prunepeer = TRUE;
- if (!strcasecmp(name, "all")) {
- multi = TRUE;
- name = NULL;
- }
- /* else a single name, already set */
- break;
- case 5:
- /* sip prune realtime {peer|like} name */
- name = a->argv[4];
- if (!strcasecmp(a->argv[3], "peer"))
- prunepeer = TRUE;
- else if (!strcasecmp(a->argv[3], "like")) {
- prunepeer = TRUE;
- multi = TRUE;
- } else
- return CLI_SHOWUSAGE;
- if (!strcasecmp(name, "like"))
- return CLI_SHOWUSAGE;
- if (!multi && !strcasecmp(name, "all")) {
- multi = TRUE;
- name = NULL;
- }
- break;
- case 6:
- name = a->argv[5];
- multi = TRUE;
- /* sip prune realtime {peer} like name */
- if (strcasecmp(a->argv[4], "like"))
- return CLI_SHOWUSAGE;
- if (!strcasecmp(a->argv[3], "peer")) {
- prunepeer = TRUE;
- } else
- return CLI_SHOWUSAGE;
- break;
- default:
- return CLI_SHOWUSAGE;
- }
- if (multi && name) {
- if (regcomp(®exbuf, name, REG_EXTENDED | REG_NOSUB)) {
- return CLI_SHOWUSAGE;
- }
- havepattern = 1;
- }
- if (multi) {
- if (prunepeer) {
- int pruned = 0;
- i = ao2_iterator_init(peers, 0);
- while ((pi = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
- ao2_lock(pi);
- if (name && regexec(®exbuf, pi->name, 0, NULL, 0)) {
- ao2_unlock(pi);
- sip_unref_peer(pi, "toss iterator peer ptr before continue");
- continue;
- };
- if (ast_test_flag(&pi->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
- pi->the_mark = 1;
- pruned++;
- }
- ao2_unlock(pi);
- sip_unref_peer(pi, "toss iterator peer ptr");
- }
- ao2_iterator_destroy(&i);
- if (pruned) {
- unlink_marked_peers_from_tables();
- ast_cli(a->fd, "%d peers pruned.\n", pruned);
- } else
- ast_cli(a->fd, "No peers found to prune.\n");
- }
- } else {
- if (prunepeer) {
- struct sip_peer tmp;
- ast_copy_string(tmp.name, name, sizeof(tmp.name));
- if ((peer = ao2_t_find(peers, &tmp, OBJ_POINTER | OBJ_UNLINK, "finding to unlink from peers"))) {
- if (!ast_sockaddr_isnull(&peer->addr)) {
- ao2_t_unlink(peers_by_ip, peer, "unlinking peer from peers_by_ip also");
- }
- if (!ast_test_flag(&peer->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
- ast_cli(a->fd, "Peer '%s' is not a Realtime peer, cannot be pruned.\n", name);
- /* put it back! */
- ao2_t_link(peers, peer, "link peer into peer table");
- if (!ast_sockaddr_isnull(&peer->addr)) {
- ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
- }
- } else
- ast_cli(a->fd, "Peer '%s' pruned.\n", name);
- sip_unref_peer(peer, "sip_prune_realtime: sip_unref_peer: tossing temp peer ptr");
- } else
- ast_cli(a->fd, "Peer '%s' not found.\n", name);
- }
- }
- if (havepattern) {
- regfree(®exbuf);
- }
- return CLI_SUCCESS;
- }
- /*! \brief Print domain mode to cli */
- static const char *domain_mode_to_text(const enum domain_mode mode)
- {
- switch (mode) {
- case SIP_DOMAIN_AUTO:
- return "[Automatic]";
- case SIP_DOMAIN_CONFIG:
- return "[Configured]";
- }
- return "";
- }
- /*! \brief CLI command to list local domains */
- static char *sip_show_domains(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct domain *d;
- #define FORMAT "%-40.40s %-20.20s %-16.16s\n"
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show domains";
- e->usage =
- "Usage: sip show domains\n"
- " Lists all configured SIP local domains.\n"
- " Asterisk only responds to SIP messages to local domains.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (AST_LIST_EMPTY(&domain_list)) {
- ast_cli(a->fd, "SIP Domain support not enabled.\n\n");
- return CLI_SUCCESS;
- } else {
- ast_cli(a->fd, FORMAT, "Our local SIP domains:", "Context", "Set by");
- AST_LIST_LOCK(&domain_list);
- AST_LIST_TRAVERSE(&domain_list, d, list)
- ast_cli(a->fd, FORMAT, d->domain, S_OR(d->context, "(default)"),
- domain_mode_to_text(d->mode));
- AST_LIST_UNLOCK(&domain_list);
- ast_cli(a->fd, "\n");
- return CLI_SUCCESS;
- }
- }
- #undef FORMAT
- /*! \brief Show SIP peers in the manager API */
- static int manager_sip_show_peer(struct mansession *s, const struct message *m)
- {
- const char *a[4];
- const char *peer;
- peer = astman_get_header(m, "Peer");
- if (ast_strlen_zero(peer)) {
- astman_send_error(s, m, "Peer: <name> missing.");
- return 0;
- }
- a[0] = "sip";
- a[1] = "show";
- a[2] = "peer";
- a[3] = peer;
- _sip_show_peer(1, -1, s, m, 4, a);
- astman_append(s, "\r\n" );
- return 0;
- }
- /*! \brief Show one peer in detail */
- static char *sip_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show peer";
- e->usage =
- "Usage: sip show peer <name> [load]\n"
- " Shows all details on one SIP peer and the current status.\n"
- " Option \"load\" forces lookup of peer in realtime storage.\n";
- return NULL;
- case CLI_GENERATE:
- return complete_sip_show_peer(a->line, a->word, a->pos, a->n);
- }
- return _sip_show_peer(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
- }
- static void send_manager_peer_status(struct mansession *s, struct sip_peer *peer, const char *idText)
- {
- char time[128] = "";
- char status[128] = "";
- if (peer->maxms) {
- if (peer->lastms < 0) {
- snprintf(status, sizeof(status), "PeerStatus: Unreachable\r\n");
- } else if (peer->lastms > peer->maxms) {
- snprintf(status, sizeof(status), "PeerStatus: Lagged\r\n");
- snprintf(time, sizeof(time), "Time: %d\r\n", peer->lastms);
- } else if (peer->lastms) {
- snprintf(status, sizeof(status), "PeerStatus: Reachable\r\n");
- snprintf(time, sizeof(time), "Time: %d\r\n", peer->lastms);
- } else {
- snprintf(status, sizeof(status), "PeerStatus: Unknown\r\n");
- }
- } else {
- snprintf(status, sizeof(status), "PeerStatus: Unmonitored\r\n");
- }
- astman_append(s,
- "Event: PeerStatus\r\n"
- "Privilege: System\r\n"
- "ChannelType: SIP\r\n"
- "Peer: SIP/%s\r\n"
- "%s"
- "%s"
- "%s"
- "\r\n",
- peer->name, status, time, idText);
- }
- /*! \brief Show SIP peers in the manager API */
- static int manager_sip_peer_status(struct mansession *s, const struct message *m)
- {
- const char *id = astman_get_header(m,"ActionID");
- const char *peer_name = astman_get_header(m,"Peer");
- char idText[256];
- struct sip_peer *peer = NULL;
- int num_peers = 0;
- idText[0] = '\0';
- if (!ast_strlen_zero(id)) {
- snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
- }
- if (!ast_strlen_zero(peer_name)) {
- /* strip SIP/ from the begining of the peer name */
- if (strlen(peer_name) >= 4 && !strncasecmp("SIP/", peer_name, 4)) {
- peer_name += 4;
- }
- peer = sip_find_peer(peer_name, NULL, TRUE, FINDPEERS, FALSE, 0);
- if (!peer) {
- astman_send_error(s, m, "No such peer");
- return 0;
- }
- }
- astman_send_listack(s, m, "Peer status will follow", "start");
- if (!peer) {
- struct ao2_iterator i = ao2_iterator_init(peers, 0);
- while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table for SIPpeerstatus"))) {
- ao2_lock(peer);
- send_manager_peer_status(s, peer, idText);
- ao2_unlock(peer);
- sip_unref_peer(peer, "unref peer for SIPpeerstatus");
- ++num_peers;
- }
- ao2_iterator_destroy(&i);
- } else {
- ao2_lock(peer);
- send_manager_peer_status(s, peer, idText);
- ao2_unlock(peer);
- sip_unref_peer(peer, "unref peer for SIPpeerstatus");
- ++num_peers;
- }
- astman_send_list_complete_start(s, m, "SIPpeerstatusComplete", num_peers);
- astman_send_list_complete_end(s);
- return 0;
- }
- static void publish_qualify_peer_done(const char *id, const char *peer)
- {
- RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
- if (ast_strlen_zero(id)) {
- body = ast_json_pack("{s: s}", "Peer", peer);
- } else {
- body = ast_json_pack("{s: s, s: s}", "Peer", peer, "ActionID", id);
- }
- if (!body) {
- return;
- }
- ast_manager_publish_event("SIPQualifyPeerDone", EVENT_FLAG_CALL, body);
- }
- /*! \brief Send qualify message to peer from cli or manager. Mostly for debugging. */
- static char *_sip_qualify_peer(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
- {
- struct sip_peer *peer;
- int load_realtime;
- if (argc < 4)
- return CLI_SHOWUSAGE;
- load_realtime = (argc == 5 && !strcmp(argv[4], "load")) ? TRUE : FALSE;
- if ((peer = sip_find_peer(argv[3], NULL, load_realtime, FINDPEERS, FALSE, 0))) {
- const char *id = astman_get_header(m,"ActionID");
- if (type != 0) {
- astman_send_ack(s, m, "SIP peer found - will qualify");
- }
- sip_poke_peer(peer, 1);
- publish_qualify_peer_done(id, argv[3]);
- sip_unref_peer(peer, "qualify: done with peer");
- } else if (type == 0) {
- ast_cli(fd, "Peer '%s' not found\n", argv[3]);
- } else {
- astman_send_error(s, m, "Peer not found");
- }
- return CLI_SUCCESS;
- }
- /*! \brief Qualify SIP peers in the manager API */
- static int manager_sip_qualify_peer(struct mansession *s, const struct message *m)
- {
- const char *a[4];
- const char *peer;
- peer = astman_get_header(m, "Peer");
- if (ast_strlen_zero(peer)) {
- astman_send_error(s, m, "Peer: <name> missing.");
- return 0;
- }
- a[0] = "sip";
- a[1] = "qualify";
- a[2] = "peer";
- a[3] = peer;
- _sip_qualify_peer(1, -1, s, m, 4, a);
- return 0;
- }
- /*! \brief Send an OPTIONS packet to a SIP peer */
- static char *sip_qualify_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip qualify peer";
- e->usage =
- "Usage: sip qualify peer <name> [load]\n"
- " Requests a response from one SIP peer and the current status.\n"
- " Option \"load\" forces lookup of peer in realtime storage.\n";
- return NULL;
- case CLI_GENERATE:
- return complete_sip_show_peer(a->line, a->word, a->pos, a->n);
- }
- return _sip_qualify_peer(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
- }
- /*! \brief list peer mailboxes to CLI */
- static void peer_mailboxes_to_str(struct ast_str **mailbox_str, struct sip_peer *peer)
- {
- struct sip_mailbox *mailbox;
- AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
- ast_str_append(mailbox_str, 0, "%s%s",
- mailbox->id,
- AST_LIST_NEXT(mailbox, entry) ? "," : "");
- }
- }
- static struct _map_x_s faxecmodes[] = {
- { SIP_PAGE2_T38SUPPORT_UDPTL, "None"},
- { SIP_PAGE2_T38SUPPORT_UDPTL_FEC, "FEC"},
- { SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY, "Redundancy"},
- { -1, NULL},
- };
- static const char *faxec2str(int faxec)
- {
- return map_x_s(faxecmodes, faxec, "Unknown");
- }
- /*! \brief Show one peer in detail (main function) */
- static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
- {
- char status[30] = "";
- char cbuf[256];
- struct sip_peer *peer;
- struct ast_str *codec_buf = ast_str_alloca(64);
- struct ast_variable *v;
- int x = 0, load_realtime;
- int realtimepeers;
- realtimepeers = ast_check_realtime("sippeers");
- if (argc < 4)
- return CLI_SHOWUSAGE;
- load_realtime = (argc == 5 && !strcmp(argv[4], "load")) ? TRUE : FALSE;
- peer = sip_find_peer(argv[3], NULL, load_realtime, FINDPEERS, FALSE, 0);
- if (s) { /* Manager */
- if (peer) {
- const char *id = astman_get_header(m, "ActionID");
- astman_append(s, "Response: Success\r\n");
- if (!ast_strlen_zero(id))
- astman_append(s, "ActionID: %s\r\n", id);
- } else {
- snprintf (cbuf, sizeof(cbuf), "Peer %s not found.", argv[3]);
- astman_send_error(s, m, cbuf);
- return CLI_SUCCESS;
- }
- }
- if (peer && type==0 ) { /* Normal listing */
- struct ast_str *mailbox_str = ast_str_alloca(512);
- struct ast_str *path;
- struct sip_auth_container *credentials;
- ao2_lock(peer);
- credentials = peer->auth;
- if (credentials) {
- ao2_t_ref(credentials, +1, "Ref peer auth for show");
- }
- ao2_unlock(peer);
- ast_cli(fd, "\n\n");
- ast_cli(fd, " * Name : %s\n", peer->name);
- ast_cli(fd, " Description : %s\n", peer->description);
- if (realtimepeers) { /* Realtime is enabled */
- ast_cli(fd, " Realtime peer: %s\n", peer->is_realtime ? "Yes, cached" : "No");
- }
- ast_cli(fd, " Secret : %s\n", ast_strlen_zero(peer->secret)?"<Not set>":"<Set>");
- ast_cli(fd, " MD5Secret : %s\n", ast_strlen_zero(peer->md5secret)?"<Not set>":"<Set>");
- ast_cli(fd, " Remote Secret: %s\n", ast_strlen_zero(peer->remotesecret)?"<Not set>":"<Set>");
- if (credentials) {
- struct sip_auth *auth;
- AST_LIST_TRAVERSE(&credentials->list, auth, node) {
- ast_cli(fd, " Realm-auth : Realm %-15.15s User %-10.20s %s\n",
- auth->realm,
- auth->username,
- !ast_strlen_zero(auth->secret)
- ? "<Secret set>"
- : (!ast_strlen_zero(auth->md5secret)
- ? "<MD5secret set>" : "<Not set>"));
- }
- ao2_t_ref(credentials, -1, "Unref peer auth for show");
- }
- ast_cli(fd, " Context : %s\n", peer->context);
- ast_cli(fd, " Record On feature : %s\n", peer->record_on_feature);
- ast_cli(fd, " Record Off feature : %s\n", peer->record_off_feature);
- ast_cli(fd, " Subscr.Cont. : %s\n", S_OR(peer->subscribecontext, "<Not set>") );
- ast_cli(fd, " Language : %s\n", peer->language);
- ast_cli(fd, " Tonezone : %s\n", peer->zone[0] != '\0' ? peer->zone : "<Not set>");
- if (!ast_strlen_zero(peer->accountcode))
- ast_cli(fd, " Accountcode : %s\n", peer->accountcode);
- ast_cli(fd, " AMA flags : %s\n", ast_channel_amaflags2string(peer->amaflags));
- ast_cli(fd, " Transfer mode: %s\n", transfermode2str(peer->allowtransfer));
- ast_cli(fd, " CallingPres : %s\n", ast_describe_caller_presentation(peer->callingpres));
- if (!ast_strlen_zero(peer->fromuser))
- ast_cli(fd, " FromUser : %s\n", peer->fromuser);
- if (!ast_strlen_zero(peer->fromdomain))
- ast_cli(fd, " FromDomain : %s Port %d\n", peer->fromdomain, (peer->fromdomainport) ? peer->fromdomainport : STANDARD_SIP_PORT);
- ast_cli(fd, " Callgroup : ");
- print_group(fd, peer->callgroup, 0);
- ast_cli(fd, " Pickupgroup : ");
- print_group(fd, peer->pickupgroup, 0);
- ast_cli(fd, " Named Callgr : ");
- print_named_groups(fd, peer->named_callgroups, 0);
- ast_cli(fd, " Nam. Pickupgr: ");
- print_named_groups(fd, peer->named_pickupgroups, 0);
- peer_mailboxes_to_str(&mailbox_str, peer);
- ast_cli(fd, " MOH Suggest : %s\n", peer->mohsuggest);
- ast_cli(fd, " Mailbox : %s\n", ast_str_buffer(mailbox_str));
- ast_cli(fd, " VM Extension : %s\n", peer->vmexten);
- ast_cli(fd, " LastMsgsSent : %d/%d\n", (peer->lastmsgssent & 0x7fff0000) >> 16, peer->lastmsgssent & 0xffff);
- ast_cli(fd, " Call limit : %d\n", peer->call_limit);
- ast_cli(fd, " Max forwards : %d\n", peer->maxforwards);
- if (peer->busy_level)
- ast_cli(fd, " Busy level : %d\n", peer->busy_level);
- ast_cli(fd, " Dynamic : %s\n", AST_CLI_YESNO(peer->host_dynamic));
- ast_cli(fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "<unspecified>"));
- ast_cli(fd, " MaxCallBR : %d kbps\n", peer->maxcallbitrate);
- ast_cli(fd, " Expire : %ld\n", ast_sched_when(sched, peer->expire));
- ast_cli(fd, " Insecure : %s\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE)));
- ast_cli(fd, " Force rport : %s\n", force_rport_string(peer->flags));
- ast_cli(fd, " Symmetric RTP: %s\n", comedia_string(peer->flags));
- ast_cli(fd, " ACL : %s\n", AST_CLI_YESNO(ast_acl_list_is_empty(peer->acl) == 0));
- ast_cli(fd, " DirectMedACL : %s\n", AST_CLI_YESNO(ast_acl_list_is_empty(peer->directmediaacl) == 0));
- ast_cli(fd, " T.38 support : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT)));
- ast_cli(fd, " T.38 EC mode : %s\n", faxec2str(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT)));
- ast_cli(fd, " T.38 MaxDtgrm: %u\n", peer->t38_maxdatagram);
- ast_cli(fd, " DirectMedia : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_DIRECT_MEDIA)));
- ast_cli(fd, " PromiscRedir : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR)));
- ast_cli(fd, " User=Phone : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_USEREQPHONE)));
- ast_cli(fd, " Video Support: %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT) || ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS)));
- ast_cli(fd, " Text Support : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT)));
- ast_cli(fd, " Ign SDP ver : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_IGNORESDPVERSION)));
- ast_cli(fd, " Trust RPID : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_TRUSTRPID)));
- ast_cli(fd, " Send RPID : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_SENDRPID)));
- ast_cli(fd, " Path support : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_USEPATH)));
- if ((path = sip_route_list(&peer->path, 1, 0))) {
- ast_cli(fd, " Path : %s\n", ast_str_buffer(path));
- ast_free(path);
- }
- ast_cli(fd, " TrustIDOutbnd: %s\n", trust_id_outbound2str(ast_test_flag(&peer->flags[1], SIP_PAGE2_TRUST_ID_OUTBOUND)));
- ast_cli(fd, " Subscriptions: %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)));
- ast_cli(fd, " Overlap dial : %s\n", allowoverlap2str(ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWOVERLAP)));
- if (peer->outboundproxy)
- ast_cli(fd, " Outb. proxy : %s %s\n", ast_strlen_zero(peer->outboundproxy->name) ? "<not set>" : peer->outboundproxy->name,
- peer->outboundproxy->force ? "(forced)" : "");
- /* - is enumerated */
- ast_cli(fd, " DTMFmode : %s\n", dtmfmode2str(ast_test_flag(&peer->flags[0], SIP_DTMF)));
- ast_cli(fd, " Timer T1 : %d\n", peer->timer_t1);
- ast_cli(fd, " Timer B : %d\n", peer->timer_b);
- ast_cli(fd, " ToHost : %s\n", peer->tohost);
- ast_cli(fd, " Addr->IP : %s\n", ast_sockaddr_stringify(&peer->addr));
- ast_cli(fd, " Defaddr->IP : %s\n", ast_sockaddr_stringify(&peer->defaddr));
- ast_cli(fd, " Prim.Transp. : %s\n", sip_get_transport(peer->socket.type));
- ast_cli(fd, " Allowed.Trsp : %s\n", get_transport_list(peer->transports));
- if (!ast_strlen_zero(sip_cfg.regcontext))
- ast_cli(fd, " Reg. exten : %s\n", peer->regexten);
- ast_cli(fd, " Def. Username: %s\n", peer->username);
- ast_cli(fd, " SIP Options : ");
- if (peer->sipoptions) {
- int lastoption = -1;
- for (x = 0 ; x < ARRAY_LEN(sip_options); x++) {
- if (sip_options[x].id != lastoption) {
- if (peer->sipoptions & sip_options[x].id)
- ast_cli(fd, "%s ", sip_options[x].text);
- lastoption = x;
- }
- }
- } else
- ast_cli(fd, "(none)");
- ast_cli(fd, "\n");
- ast_cli(fd, " Codecs : %s\n", ast_format_cap_get_names(peer->caps, &codec_buf));
- ast_cli(fd, " Auto-Framing : %s\n", AST_CLI_YESNO(peer->autoframing));
- ast_cli(fd, " Status : ");
- peer_status(peer, status, sizeof(status));
- ast_cli(fd, "%s\n", status);
- ast_cli(fd, " Useragent : %s\n", peer->useragent);
- ast_cli(fd, " Reg. Contact : %s\n", peer->fullcontact);
- ast_cli(fd, " Qualify Freq : %d ms\n", peer->qualifyfreq);
- ast_cli(fd, " Keepalive : %d ms\n", peer->keepalive * 1000);
- if (peer->chanvars) {
- ast_cli(fd, " Variables :\n");
- for (v = peer->chanvars ; v ; v = v->next)
- ast_cli(fd, " %s = %s\n", v->name, v->value);
- }
- ast_cli(fd, " Sess-Timers : %s\n", stmode2str(peer->stimer.st_mode_oper));
- ast_cli(fd, " Sess-Refresh : %s\n", strefresherparam2str(peer->stimer.st_ref));
- ast_cli(fd, " Sess-Expires : %d secs\n", peer->stimer.st_max_se);
- ast_cli(fd, " Min-Sess : %d secs\n", peer->stimer.st_min_se);
- ast_cli(fd, " RTP Engine : %s\n", peer->engine);
- ast_cli(fd, " Parkinglot : %s\n", peer->parkinglot);
- ast_cli(fd, " Use Reason : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_Q850_REASON)));
- ast_cli(fd, " Encryption : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_USE_SRTP)));
- ast_cli(fd, "\n");
- peer = sip_unref_peer(peer, "sip_show_peer: sip_unref_peer: done with peer ptr");
- } else if (peer && type == 1) { /* manager listing */
- char buffer[256];
- struct ast_str *tmp_str = ast_str_alloca(512);
- astman_append(s, "Channeltype: SIP\r\n");
- astman_append(s, "ObjectName: %s\r\n", peer->name);
- astman_append(s, "ChanObjectType: peer\r\n");
- astman_append(s, "SecretExist: %s\r\n", ast_strlen_zero(peer->secret)?"N":"Y");
- astman_append(s, "RemoteSecretExist: %s\r\n", ast_strlen_zero(peer->remotesecret)?"N":"Y");
- astman_append(s, "MD5SecretExist: %s\r\n", ast_strlen_zero(peer->md5secret)?"N":"Y");
- astman_append(s, "Context: %s\r\n", peer->context);
- if (!ast_strlen_zero(peer->subscribecontext)) {
- astman_append(s, "SubscribeContext: %s\r\n", peer->subscribecontext);
- }
- astman_append(s, "Language: %s\r\n", peer->language);
- astman_append(s, "ToneZone: %s\r\n", peer->zone[0] != '\0' ? peer->zone : "<Not set>");
- if (!ast_strlen_zero(peer->accountcode))
- astman_append(s, "Accountcode: %s\r\n", peer->accountcode);
- astman_append(s, "AMAflags: %s\r\n", ast_channel_amaflags2string(peer->amaflags));
- astman_append(s, "CID-CallingPres: %s\r\n", ast_describe_caller_presentation(peer->callingpres));
- if (!ast_strlen_zero(peer->fromuser))
- astman_append(s, "SIP-FromUser: %s\r\n", peer->fromuser);
- if (!ast_strlen_zero(peer->fromdomain))
- astman_append(s, "SIP-FromDomain: %s\r\nSip-FromDomain-Port: %d\r\n", peer->fromdomain, (peer->fromdomainport) ? peer->fromdomainport : STANDARD_SIP_PORT);
- astman_append(s, "Callgroup: ");
- astman_append(s, "%s\r\n", ast_print_group(buffer, sizeof(buffer), peer->callgroup));
- astman_append(s, "Pickupgroup: ");
- astman_append(s, "%s\r\n", ast_print_group(buffer, sizeof(buffer), peer->pickupgroup));
- astman_append(s, "Named Callgroup: ");
- astman_append(s, "%s\r\n", ast_print_namedgroups(&tmp_str, peer->named_callgroups));
- ast_str_reset(tmp_str);
- astman_append(s, "Named Pickupgroup: ");
- astman_append(s, "%s\r\n", ast_print_namedgroups(&tmp_str, peer->named_pickupgroups));
- ast_str_reset(tmp_str);
- astman_append(s, "MOHSuggest: %s\r\n", peer->mohsuggest);
- peer_mailboxes_to_str(&tmp_str, peer);
- astman_append(s, "VoiceMailbox: %s\r\n", ast_str_buffer(tmp_str));
- astman_append(s, "TransferMode: %s\r\n", transfermode2str(peer->allowtransfer));
- astman_append(s, "LastMsgsSent: %d\r\n", peer->lastmsgssent);
- astman_append(s, "Maxforwards: %d\r\n", peer->maxforwards);
- astman_append(s, "Call-limit: %d\r\n", peer->call_limit);
- astman_append(s, "Busy-level: %d\r\n", peer->busy_level);
- astman_append(s, "MaxCallBR: %d kbps\r\n", peer->maxcallbitrate);
- astman_append(s, "Dynamic: %s\r\n", peer->host_dynamic?"Y":"N");
- astman_append(s, "Callerid: %s\r\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, ""));
- astman_append(s, "RegExpire: %ld seconds\r\n", ast_sched_when(sched, peer->expire));
- astman_append(s, "SIP-AuthInsecure: %s\r\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE)));
- astman_append(s, "SIP-Forcerport: %s\r\n", ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) ?
- (ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? "A" : "a") :
- (ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? "Y" : "N"));
- astman_append(s, "SIP-Comedia: %s\r\n", ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA) ?
- (ast_test_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP) ? "A" : "a") :
- (ast_test_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP) ? "Y" : "N"));
- astman_append(s, "ACL: %s\r\n", (ast_acl_list_is_empty(peer->acl) ? "N" : "Y"));
- astman_append(s, "SIP-CanReinvite: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_DIRECT_MEDIA)?"Y":"N"));
- astman_append(s, "SIP-DirectMedia: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_DIRECT_MEDIA)?"Y":"N"));
- astman_append(s, "SIP-PromiscRedir: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR)?"Y":"N"));
- astman_append(s, "SIP-UserPhone: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_USEREQPHONE)?"Y":"N"));
- astman_append(s, "SIP-VideoSupport: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT)?"Y":"N"));
- astman_append(s, "SIP-TextSupport: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT)?"Y":"N"));
- astman_append(s, "SIP-T.38Support: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT)?"Y":"N"));
- astman_append(s, "SIP-T.38EC: %s\r\n", faxec2str(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT)));
- astman_append(s, "SIP-T.38MaxDtgrm: %u\r\n", peer->t38_maxdatagram);
- astman_append(s, "SIP-Sess-Timers: %s\r\n", stmode2str(peer->stimer.st_mode_oper));
- astman_append(s, "SIP-Sess-Refresh: %s\r\n", strefresherparam2str(peer->stimer.st_ref));
- astman_append(s, "SIP-Sess-Expires: %d\r\n", peer->stimer.st_max_se);
- astman_append(s, "SIP-Sess-Min: %d\r\n", peer->stimer.st_min_se);
- astman_append(s, "SIP-RTP-Engine: %s\r\n", peer->engine);
- astman_append(s, "SIP-Encryption: %s\r\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_USE_SRTP) ? "Y" : "N");
- /* - is enumerated */
- astman_append(s, "SIP-DTMFmode: %s\r\n", dtmfmode2str(ast_test_flag(&peer->flags[0], SIP_DTMF)));
- astman_append(s, "ToHost: %s\r\n", peer->tohost);
- astman_append(s, "Address-IP: %s\r\nAddress-Port: %d\r\n", ast_sockaddr_stringify_addr(&peer->addr), ast_sockaddr_port(&peer->addr));
- astman_append(s, "Default-addr-IP: %s\r\nDefault-addr-port: %d\r\n", ast_sockaddr_stringify_addr(&peer->defaddr), ast_sockaddr_port(&peer->defaddr));
- astman_append(s, "Default-Username: %s\r\n", peer->username);
- if (!ast_strlen_zero(sip_cfg.regcontext))
- astman_append(s, "RegExtension: %s\r\n", peer->regexten);
- astman_append(s, "Codecs: %s\r\n", ast_format_cap_get_names(peer->caps, &codec_buf));
- astman_append(s, "Status: ");
- peer_status(peer, status, sizeof(status));
- astman_append(s, "%s\r\n", status);
- astman_append(s, "SIP-Useragent: %s\r\n", peer->useragent);
- astman_append(s, "Reg-Contact: %s\r\n", peer->fullcontact);
- astman_append(s, "QualifyFreq: %d ms\r\n", peer->qualifyfreq);
- astman_append(s, "Parkinglot: %s\r\n", peer->parkinglot);
- if (peer->chanvars) {
- for (v = peer->chanvars ; v ; v = v->next) {
- astman_append(s, "ChanVariable: %s=%s\r\n", v->name, v->value);
- }
- }
- astman_append(s, "SIP-Use-Reason-Header: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_Q850_REASON)) ? "Y" : "N");
- astman_append(s, "Description: %s\r\n", peer->description);
- peer = sip_unref_peer(peer, "sip_show_peer: sip_unref_peer: done with peer");
- } else {
- ast_cli(fd, "Peer %s not found.\n", argv[3]);
- ast_cli(fd, "\n");
- }
- return CLI_SUCCESS;
- }
- /*! \brief Do completion on user name */
- static char *complete_sip_user(const char *word, int state)
- {
- char *result = NULL;
- int wordlen = strlen(word);
- int which = 0;
- struct ao2_iterator user_iter;
- struct sip_peer *user;
- user_iter = ao2_iterator_init(peers, 0);
- while ((user = ao2_t_iterator_next(&user_iter, "iterate thru peers table"))) {
- ao2_lock(user);
- if (!(user->type & SIP_TYPE_USER)) {
- ao2_unlock(user);
- sip_unref_peer(user, "complete sip user");
- continue;
- }
- /* locking of the object is not required because only the name and flags are being compared */
- if (!strncasecmp(word, user->name, wordlen) && ++which > state) {
- result = ast_strdup(user->name);
- }
- ao2_unlock(user);
- sip_unref_peer(user, "complete sip user");
- if (result) {
- break;
- }
- }
- ao2_iterator_destroy(&user_iter);
- return result;
- }
- /*! \brief Support routine for 'sip show user' CLI */
- static char *complete_sip_show_user(const char *line, const char *word, int pos, int state)
- {
- if (pos == 3)
- return complete_sip_user(word, state);
- return NULL;
- }
- /*! \brief Show one user in detail */
- static char *sip_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- char cbuf[256];
- struct sip_peer *user;
- struct ast_variable *v;
- int load_realtime;
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show user";
- e->usage =
- "Usage: sip show user <name> [load]\n"
- " Shows all details on one SIP user and the current status.\n"
- " Option \"load\" forces lookup of peer in realtime storage.\n";
- return NULL;
- case CLI_GENERATE:
- return complete_sip_show_user(a->line, a->word, a->pos, a->n);
- }
- if (a->argc < 4)
- return CLI_SHOWUSAGE;
- /* Load from realtime storage? */
- load_realtime = (a->argc == 5 && !strcmp(a->argv[4], "load")) ? TRUE : FALSE;
- if ((user = sip_find_peer(a->argv[3], NULL, load_realtime, FINDUSERS, FALSE, 0))) {
- ao2_lock(user);
- ast_cli(a->fd, "\n\n");
- ast_cli(a->fd, " * Name : %s\n", user->name);
- ast_cli(a->fd, " Secret : %s\n", ast_strlen_zero(user->secret)?"<Not set>":"<Set>");
- ast_cli(a->fd, " MD5Secret : %s\n", ast_strlen_zero(user->md5secret)?"<Not set>":"<Set>");
- ast_cli(a->fd, " Context : %s\n", user->context);
- ast_cli(a->fd, " Language : %s\n", user->language);
- if (!ast_strlen_zero(user->accountcode))
- ast_cli(a->fd, " Accountcode : %s\n", user->accountcode);
- ast_cli(a->fd, " AMA flags : %s\n", ast_channel_amaflags2string(user->amaflags));
- ast_cli(a->fd, " Tonezone : %s\n", user->zone[0] != '\0' ? user->zone : "<Not set>");
- ast_cli(a->fd, " Transfer mode: %s\n", transfermode2str(user->allowtransfer));
- ast_cli(a->fd, " MaxCallBR : %d kbps\n", user->maxcallbitrate);
- ast_cli(a->fd, " CallingPres : %s\n", ast_describe_caller_presentation(user->callingpres));
- ast_cli(a->fd, " Call limit : %d\n", user->call_limit);
- ast_cli(a->fd, " Callgroup : ");
- print_group(a->fd, user->callgroup, 0);
- ast_cli(a->fd, " Pickupgroup : ");
- print_group(a->fd, user->pickupgroup, 0);
- ast_cli(a->fd, " Named Callgr : ");
- print_named_groups(a->fd, user->named_callgroups, 0);
- ast_cli(a->fd, " Nam. Pickupgr: ");
- print_named_groups(a->fd, user->named_pickupgroups, 0);
- ast_cli(a->fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), user->cid_name, user->cid_num, "<unspecified>"));
- ast_cli(a->fd, " ACL : %s\n", AST_CLI_YESNO(ast_acl_list_is_empty(user->acl) == 0));
- ast_cli(a->fd, " Sess-Timers : %s\n", stmode2str(user->stimer.st_mode_oper));
- ast_cli(a->fd, " Sess-Refresh : %s\n", strefresherparam2str(user->stimer.st_ref));
- ast_cli(a->fd, " Sess-Expires : %d secs\n", user->stimer.st_max_se);
- ast_cli(a->fd, " Sess-Min-SE : %d secs\n", user->stimer.st_min_se);
- ast_cli(a->fd, " RTP Engine : %s\n", user->engine);
- ast_cli(a->fd, " Auto-Framing: %s \n", AST_CLI_YESNO(user->autoframing));
- if (user->chanvars) {
- ast_cli(a->fd, " Variables :\n");
- for (v = user->chanvars ; v ; v = v->next)
- ast_cli(a->fd, " %s = %s\n", v->name, v->value);
- }
- ast_cli(a->fd, "\n");
- ao2_unlock(user);
- sip_unref_peer(user, "sip show user");
- } else {
- ast_cli(a->fd, "User %s not found.\n", a->argv[3]);
- ast_cli(a->fd, "\n");
- }
- return CLI_SUCCESS;
- }
- static char *sip_show_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct ast_str *cbuf;
- struct ast_cb_names cbnames = {
- 10,
- {
- "retrans_pkt",
- "__sip_autodestruct",
- "expire_register",
- "auto_congest",
- "sip_reg_timeout",
- "sip_poke_peer_s",
- "sip_poke_peer_now",
- "sip_poke_noanswer",
- "sip_reregister",
- "sip_reinvite_retry"
- },
- {
- retrans_pkt,
- __sip_autodestruct,
- expire_register,
- auto_congest,
- sip_reg_timeout,
- sip_poke_peer_s,
- sip_poke_peer_now,
- sip_poke_noanswer,
- sip_reregister,
- sip_reinvite_retry
- }
- };
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show sched";
- e->usage =
- "Usage: sip show sched\n"
- " Shows stats on what's in the sched queue at the moment\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- cbuf = ast_str_alloca(2048);
- ast_cli(a->fd, "\n");
- ast_sched_report(sched, &cbuf, &cbnames);
- ast_cli(a->fd, "%s", ast_str_buffer(cbuf));
- return CLI_SUCCESS;
- }
- /*! \brief Show SIP Registry (registrations with other SIP proxies */
- static char *sip_show_registry(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- #define FORMAT2 "%-39.39s %-6.6s %-12.12s %8.8s %-20.20s %-25.25s\n"
- #define FORMAT "%-39.39s %-6.6s %-12.12s %8d %-20.20s %-25.25s\n"
- char host[80];
- char user[80];
- char tmpdat[256];
- struct ast_tm tm;
- int counter = 0;
- struct ao2_iterator iter;
- struct sip_registry *iterator;
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show registry";
- e->usage =
- "Usage: sip 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", "Refresh", "State", "Reg.Time");
- iter = ao2_iterator_init(registry_list, 0);
- while ((iterator = ao2_t_iterator_next(&iter, "sip_show_registry iter"))) {
- ao2_lock(iterator);
- snprintf(host, sizeof(host), "%s:%d", iterator->hostname, iterator->portno ? iterator->portno : STANDARD_SIP_PORT);
- snprintf(user, sizeof(user), "%s", iterator->username);
- if (!ast_strlen_zero(iterator->regdomain)) {
- snprintf(tmpdat, sizeof(tmpdat), "%s", user);
- snprintf(user, sizeof(user), "%s@%s", tmpdat, iterator->regdomain);}
- if (iterator->regdomainport) {
- snprintf(tmpdat, sizeof(tmpdat), "%s", user);
- snprintf(user, sizeof(user), "%s:%d", tmpdat, iterator->regdomainport);}
- if (iterator->regtime.tv_sec) {
- ast_localtime(&iterator->regtime, &tm, NULL);
- ast_strftime(tmpdat, sizeof(tmpdat), "%a, %d %b %Y %T", &tm);
- } else
- tmpdat[0] = '\0';
- ast_cli(a->fd, FORMAT, host, (iterator->dnsmgr) ? "Y" : "N", user, iterator->refresh, regstate2str(iterator->regstate), tmpdat);
- ao2_unlock(iterator);
- ao2_t_ref(iterator, -1, "sip_show_registry iter");
- counter++;
- }
- ao2_iterator_destroy(&iter);
- ast_cli(a->fd, "%d SIP registrations.\n", counter);
- return CLI_SUCCESS;
- #undef FORMAT
- #undef FORMAT2
- }
- /*! \brief Unregister (force expiration) a SIP peer in the registry via CLI
- \note This function does not tell the SIP device what's going on,
- so use it with great care.
- */
- static char *sip_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct sip_peer *peer;
- int load_realtime = 0;
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip unregister";
- e->usage =
- "Usage: sip unregister <peer>\n"
- " Unregister (force expiration) a SIP peer from the registry\n";
- return NULL;
- case CLI_GENERATE:
- return complete_sip_unregister(a->line, a->word, a->pos, a->n);
- }
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
- if ((peer = sip_find_peer(a->argv[2], NULL, load_realtime, FINDPEERS, TRUE, 0))) {
- if (peer->expire > 0) {
- AST_SCHED_DEL_UNREF(sched, peer->expire,
- sip_unref_peer(peer, "remove register expire ref"));
- expire_register(sip_ref_peer(peer, "ref for expire_register"));
- ast_cli(a->fd, "Unregistered peer \'%s\'\n\n", a->argv[2]);
- } else {
- ast_cli(a->fd, "Peer %s not registered\n", a->argv[2]);
- }
- sip_unref_peer(peer, "sip_unregister: sip_unref_peer via sip_unregister: done with peer from sip_find_peer call");
- } else {
- ast_cli(a->fd, "Peer unknown: \'%s\'. Not unregistered.\n", a->argv[2]);
- }
- return CLI_SUCCESS;
- }
- /*! \brief Callback for show_chanstats */
- static int show_chanstats_cb(void *__cur, void *__arg, int flags)
- {
- #define FORMAT2 "%-15.15s %-11.11s %-8.8s %-10.10s %-10.10s ( %%) %-6.6s %-10.10s %-10.10s ( %%) %-6.6s\n"
- #define FORMAT "%-15.15s %-11.11s %-8.8s %-10.10u%-1.1s %-10.10u (%5.2f%%) %-6.4lf %-10.10u%-1.1s %-10.10u (%5.2f%%) %-6.4lf\n"
- struct sip_pvt *cur = __cur;
- struct ast_rtp_instance_stats stats;
- char durbuf[10];
- struct ast_channel *c;
- struct __show_chan_arg *arg = __arg;
- int fd = arg->fd;
- sip_pvt_lock(cur);
- c = cur->owner;
- if (cur->subscribed != NONE) {
- /* Subscriptions */
- sip_pvt_unlock(cur);
- return 0; /* don't care, we scan all channels */
- }
- if (!cur->rtp) {
- if (sipdebug) {
- ast_cli(fd, "%-15.15s %-11.11s (inv state: %s) -- %s\n",
- ast_sockaddr_stringify_addr(&cur->sa), cur->callid,
- invitestate2string[cur->invitestate].desc,
- "-- No RTP active");
- }
- sip_pvt_unlock(cur);
- return 0; /* don't care, we scan all channels */
- }
- if (ast_rtp_instance_get_stats(cur->rtp, &stats, AST_RTP_INSTANCE_STAT_ALL)) {
- sip_pvt_unlock(cur);
- ast_log(LOG_WARNING, "Could not get RTP stats.\n");
- return 0;
- }
- if (c) {
- ast_format_duration_hh_mm_ss(ast_channel_get_duration(c), durbuf, sizeof(durbuf));
- } else {
- durbuf[0] = '\0';
- }
- ast_cli(fd, FORMAT,
- ast_sockaddr_stringify_addr(&cur->sa),
- cur->callid,
- durbuf,
- stats.rxcount > (unsigned int) 100000 ? (unsigned int) (stats.rxcount)/(unsigned int) 1000 : stats.rxcount,
- stats.rxcount > (unsigned int) 100000 ? "K":" ",
- stats.rxploss,
- (stats.rxcount + stats.rxploss) > 0 ? (double) stats.rxploss / (stats.rxcount + stats.rxploss) * 100 : 0,
- stats.rxjitter,
- stats.txcount > (unsigned int) 100000 ? (unsigned int) (stats.txcount)/(unsigned int) 1000 : stats.txcount,
- stats.txcount > (unsigned int) 100000 ? "K":" ",
- stats.txploss,
- stats.txcount > 0 ? (double) stats.txploss / stats.txcount * 100 : 0,
- stats.txjitter
- );
- arg->numchans++;
- sip_pvt_unlock(cur);
- return 0; /* don't care, we scan all channels */
- }
- /*! \brief SIP show channelstats CLI (main function) */
- static char *sip_show_channelstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct __show_chan_arg arg = { .fd = a->fd, .numchans = 0 };
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show channelstats";
- e->usage =
- "Usage: sip show channelstats\n"
- " Lists all currently active SIP channel's RTCP statistics.\n"
- " Note that calls in the much optimized RTP P2P bridge mode will not show any packets here.";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
- ast_cli(a->fd, FORMAT2, "Peer", "Call ID", "Duration", "Recv: Pack", "Lost", "Jitter", "Send: Pack", "Lost", "Jitter");
- /* iterate on the container and invoke the callback on each item */
- ao2_t_callback(dialogs, OBJ_NODATA, show_chanstats_cb, &arg, "callback to sip show chanstats");
- ast_cli(a->fd, "%d active SIP channel%s\n", arg.numchans, (arg.numchans != 1) ? "s" : "");
- return CLI_SUCCESS;
- }
- #undef FORMAT
- #undef FORMAT2
- /*! \brief List global settings for the SIP channel */
- static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- int realtimepeers;
- int realtimeregs;
- struct ast_str *codec_buf = ast_str_alloca(64);
- const char *msg; /* temporary msg pointer */
- struct sip_auth_container *credentials;
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show settings";
- e->usage =
- "Usage: sip show settings\n"
- " Provides detailed list of the configuration of the SIP channel.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
- realtimepeers = ast_check_realtime("sippeers");
- realtimeregs = ast_check_realtime("sipregs");
- ast_mutex_lock(&authl_lock);
- credentials = authl;
- if (credentials) {
- ao2_t_ref(credentials, +1, "Ref global auth for show");
- }
- ast_mutex_unlock(&authl_lock);
- ast_cli(a->fd, "\n\nGlobal Settings:\n");
- ast_cli(a->fd, "----------------\n");
- ast_cli(a->fd, " UDP Bindaddress: %s\n", ast_sockaddr_stringify(&bindaddr));
- if (ast_sockaddr_is_ipv6(&bindaddr) && ast_sockaddr_is_any(&bindaddr)) {
- ast_cli(a->fd, " ** Additional Info:\n");
- ast_cli(a->fd, " [::] may include IPv4 in addition to IPv6, if such a feature is enabled in the OS.\n");
- }
- ast_cli(a->fd, " TCP SIP Bindaddress: %s\n",
- sip_cfg.tcp_enabled != FALSE ?
- ast_sockaddr_stringify(&sip_tcp_desc.local_address) :
- "Disabled");
- ast_cli(a->fd, " TLS SIP Bindaddress: %s\n",
- default_tls_cfg.enabled != FALSE ?
- ast_sockaddr_stringify(&sip_tls_desc.local_address) :
- "Disabled");
- ast_cli(a->fd, " RTP Bindaddress: %s\n",
- !ast_sockaddr_isnull(&rtpbindaddr) ?
- ast_sockaddr_stringify_addr(&rtpbindaddr) :
- "Disabled");
- ast_cli(a->fd, " Videosupport: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT)));
- ast_cli(a->fd, " Textsupport: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_TEXTSUPPORT)));
- ast_cli(a->fd, " Ignore SDP sess. ver.: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_IGNORESDPVERSION)));
- ast_cli(a->fd, " AutoCreate Peer: %s\n", autocreatepeer2str(sip_cfg.autocreatepeer));
- ast_cli(a->fd, " Match Auth Username: %s\n", AST_CLI_YESNO(global_match_auth_username));
- ast_cli(a->fd, " Allow unknown access: %s\n", AST_CLI_YESNO(sip_cfg.allowguest));
- ast_cli(a->fd, " Allow subscriptions: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)));
- ast_cli(a->fd, " Allow overlap dialing: %s\n", allowoverlap2str(ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP)));
- ast_cli(a->fd, " Allow promisc. redir: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_PROMISCREDIR)));
- ast_cli(a->fd, " Enable call counters: %s\n", AST_CLI_YESNO(global_callcounter));
- ast_cli(a->fd, " SIP domain support: %s\n", AST_CLI_YESNO(!AST_LIST_EMPTY(&domain_list)));
- ast_cli(a->fd, " Path support : %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_USEPATH)));
- ast_cli(a->fd, " Realm. auth: %s\n", AST_CLI_YESNO(credentials != NULL));
- if (credentials) {
- struct sip_auth *auth;
- AST_LIST_TRAVERSE(&credentials->list, auth, node) {
- ast_cli(a->fd, " Realm. auth entry: Realm %-15.15s User %-10.20s %s\n",
- auth->realm,
- auth->username,
- !ast_strlen_zero(auth->secret)
- ? "<Secret set>"
- : (!ast_strlen_zero(auth->md5secret)
- ? "<MD5secret set>" : "<Not set>"));
- }
- ao2_t_ref(credentials, -1, "Unref global auth for show");
- }
- ast_cli(a->fd, " Our auth realm %s\n", sip_cfg.realm);
- ast_cli(a->fd, " Use domains as realms: %s\n", AST_CLI_YESNO(sip_cfg.domainsasrealm));
- ast_cli(a->fd, " Call to non-local dom.: %s\n", AST_CLI_YESNO(sip_cfg.allow_external_domains));
- ast_cli(a->fd, " URI user is phone no: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_USEREQPHONE)));
- ast_cli(a->fd, " Always auth rejects: %s\n", AST_CLI_YESNO(sip_cfg.alwaysauthreject));
- ast_cli(a->fd, " Direct RTP setup: %s\n", AST_CLI_YESNO(sip_cfg.directrtpsetup));
- ast_cli(a->fd, " User Agent: %s\n", global_useragent);
- ast_cli(a->fd, " SDP Session Name: %s\n", ast_strlen_zero(global_sdpsession) ? "-" : global_sdpsession);
- ast_cli(a->fd, " SDP Owner Name: %s\n", ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner);
- ast_cli(a->fd, " Reg. context: %s\n", S_OR(sip_cfg.regcontext, "(not set)"));
- ast_cli(a->fd, " Regexten on Qualify: %s\n", AST_CLI_YESNO(sip_cfg.regextenonqualify));
- ast_cli(a->fd, " Trust RPID: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_TRUSTRPID)));
- ast_cli(a->fd, " Send RPID: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_SENDRPID)));
- ast_cli(a->fd, " Legacy userfield parse: %s\n", AST_CLI_YESNO(sip_cfg.legacy_useroption_parsing));
- ast_cli(a->fd, " Send Diversion: %s\n", AST_CLI_YESNO(sip_cfg.send_diversion));
- ast_cli(a->fd, " Caller ID: %s\n", default_callerid);
- if ((default_fromdomainport) && (default_fromdomainport != STANDARD_SIP_PORT)) {
- ast_cli(a->fd, " From: Domain: %s:%d\n", default_fromdomain, default_fromdomainport);
- } else {
- ast_cli(a->fd, " From: Domain: %s\n", default_fromdomain);
- }
- ast_cli(a->fd, " Record SIP history: %s\n", AST_CLI_ONOFF(recordhistory));
- ast_cli(a->fd, " Auth. Failure Events: %s\n", AST_CLI_ONOFF(global_authfailureevents));
- ast_cli(a->fd, " T.38 support: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT)));
- ast_cli(a->fd, " T.38 EC mode: %s\n", faxec2str(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT)));
- ast_cli(a->fd, " T.38 MaxDtgrm: %u\n", global_t38_maxdatagram);
- if (!realtimepeers && !realtimeregs)
- ast_cli(a->fd, " SIP realtime: Disabled\n" );
- else
- ast_cli(a->fd, " SIP realtime: Enabled\n" );
- ast_cli(a->fd, " Qualify Freq : %d ms\n", global_qualifyfreq);
- ast_cli(a->fd, " Q.850 Reason header: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_Q850_REASON)));
- ast_cli(a->fd, " Store SIP_CAUSE: %s\n", AST_CLI_YESNO(global_store_sip_cause));
- ast_cli(a->fd, "\nNetwork QoS Settings:\n");
- ast_cli(a->fd, "---------------------------\n");
- ast_cli(a->fd, " IP ToS SIP: %s\n", ast_tos2str(global_tos_sip));
- ast_cli(a->fd, " IP ToS RTP audio: %s\n", ast_tos2str(global_tos_audio));
- ast_cli(a->fd, " IP ToS RTP video: %s\n", ast_tos2str(global_tos_video));
- ast_cli(a->fd, " IP ToS RTP text: %s\n", ast_tos2str(global_tos_text));
- ast_cli(a->fd, " 802.1p CoS SIP: %u\n", global_cos_sip);
- ast_cli(a->fd, " 802.1p CoS RTP audio: %u\n", global_cos_audio);
- ast_cli(a->fd, " 802.1p CoS RTP video: %u\n", global_cos_video);
- ast_cli(a->fd, " 802.1p CoS RTP text: %u\n", global_cos_text);
- ast_cli(a->fd, " Jitterbuffer enabled: %s\n", AST_CLI_YESNO(ast_test_flag(&global_jbconf, AST_JB_ENABLED)));
- if (ast_test_flag(&global_jbconf, AST_JB_ENABLED)) {
- ast_cli(a->fd, " Jitterbuffer forced: %s\n", AST_CLI_YESNO(ast_test_flag(&global_jbconf, AST_JB_FORCED)));
- ast_cli(a->fd, " Jitterbuffer max size: %ld\n", global_jbconf.max_size);
- ast_cli(a->fd, " Jitterbuffer resync: %ld\n", global_jbconf.resync_threshold);
- ast_cli(a->fd, " Jitterbuffer impl: %s\n", global_jbconf.impl);
- if (!strcasecmp(global_jbconf.impl, "adaptive")) {
- ast_cli(a->fd, " Jitterbuffer tgt extra: %ld\n", global_jbconf.target_extra);
- }
- ast_cli(a->fd, " Jitterbuffer log: %s\n", AST_CLI_YESNO(ast_test_flag(&global_jbconf, AST_JB_LOG)));
- }
- ast_cli(a->fd, "\nNetwork Settings:\n");
- ast_cli(a->fd, "---------------------------\n");
- /* determine if/how SIP address can be remapped */
- if (localaddr == NULL)
- msg = "Disabled, no localnet list";
- else if (ast_sockaddr_isnull(&externaddr))
- msg = "Disabled";
- else if (!ast_strlen_zero(externhost))
- msg = "Enabled using externhost";
- else
- msg = "Enabled using externaddr";
- ast_cli(a->fd, " SIP address remapping: %s\n", msg);
- ast_cli(a->fd, " Externhost: %s\n", S_OR(externhost, "<none>"));
- ast_cli(a->fd, " Externaddr: %s\n", ast_sockaddr_stringify(&externaddr));
- ast_cli(a->fd, " Externrefresh: %d\n", externrefresh);
- {
- struct ast_ha *d;
- const char *prefix = "Localnet:";
- for (d = localaddr; d ; prefix = "", d = d->next) {
- const char *addr = ast_strdupa(ast_sockaddr_stringify_addr(&d->addr));
- const char *mask = ast_strdupa(ast_sockaddr_stringify_addr(&d->netmask));
- ast_cli(a->fd, " %-24s%s/%s\n", prefix, addr, mask);
- }
- }
- ast_cli(a->fd, "\nGlobal Signalling Settings:\n");
- ast_cli(a->fd, "---------------------------\n");
- ast_cli(a->fd, " Codecs: %s\n", ast_format_cap_get_names(sip_cfg.caps, &codec_buf));
- ast_cli(a->fd, " Relax DTMF: %s\n", AST_CLI_YESNO(global_relaxdtmf));
- ast_cli(a->fd, " RFC2833 Compensation: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_RFC2833_COMPENSATE)));
- ast_cli(a->fd, " Symmetric RTP: %s\n", comedia_string(global_flags));
- ast_cli(a->fd, " Compact SIP headers: %s\n", AST_CLI_YESNO(sip_cfg.compactheaders));
- ast_cli(a->fd, " RTP Keepalive: %d %s\n", global_rtpkeepalive, global_rtpkeepalive ? "" : "(Disabled)" );
- ast_cli(a->fd, " RTP Timeout: %d %s\n", global_rtptimeout, global_rtptimeout ? "" : "(Disabled)" );
- ast_cli(a->fd, " RTP Hold Timeout: %d %s\n", global_rtpholdtimeout, global_rtpholdtimeout ? "" : "(Disabled)");
- ast_cli(a->fd, " MWI NOTIFY mime type: %s\n", default_notifymime);
- ast_cli(a->fd, " DNS SRV lookup: %s\n", AST_CLI_YESNO(sip_cfg.srvlookup));
- ast_cli(a->fd, " Pedantic SIP support: %s\n", AST_CLI_YESNO(sip_cfg.pedanticsipchecking));
- ast_cli(a->fd, " Reg. min duration %d secs\n", min_expiry);
- ast_cli(a->fd, " Reg. max duration: %d secs\n", max_expiry);
- ast_cli(a->fd, " Reg. default duration: %d secs\n", default_expiry);
- ast_cli(a->fd, " Sub. min duration %d secs\n", min_subexpiry);
- ast_cli(a->fd, " Sub. max duration: %d secs\n", max_subexpiry);
- ast_cli(a->fd, " Outbound reg. timeout: %d secs\n", global_reg_timeout);
- ast_cli(a->fd, " Outbound reg. attempts: %d\n", global_regattempts_max);
- ast_cli(a->fd, " Outbound reg. retry 403:%d\n", global_reg_retry_403);
- ast_cli(a->fd, " Notify ringing state: %s\n", AST_CLI_YESNO(sip_cfg.notifyringing));
- if (sip_cfg.notifyringing) {
- ast_cli(a->fd, " Include CID: %s%s\n",
- AST_CLI_YESNO(sip_cfg.notifycid),
- sip_cfg.notifycid == IGNORE_CONTEXT ? " (Ignoring context)" : "");
- }
- ast_cli(a->fd, " Notify hold state: %s\n", AST_CLI_YESNO(sip_cfg.notifyhold));
- ast_cli(a->fd, " SIP Transfer mode: %s\n", transfermode2str(sip_cfg.allowtransfer));
- ast_cli(a->fd, " Max Call Bitrate: %d kbps\n", default_maxcallbitrate);
- ast_cli(a->fd, " Auto-Framing: %s\n", AST_CLI_YESNO(global_autoframing));
- ast_cli(a->fd, " Outb. proxy: %s %s\n", ast_strlen_zero(sip_cfg.outboundproxy.name) ? "<not set>" : sip_cfg.outboundproxy.name,
- sip_cfg.outboundproxy.force ? "(forced)" : "");
- ast_cli(a->fd, " Session Timers: %s\n", stmode2str(global_st_mode));
- ast_cli(a->fd, " Session Refresher: %s\n", strefresherparam2str(global_st_refresher));
- ast_cli(a->fd, " Session Expires: %d secs\n", global_max_se);
- ast_cli(a->fd, " Session Min-SE: %d secs\n", global_min_se);
- ast_cli(a->fd, " Timer T1: %d\n", global_t1);
- ast_cli(a->fd, " Timer T1 minimum: %d\n", global_t1min);
- ast_cli(a->fd, " Timer B: %d\n", global_timer_b);
- ast_cli(a->fd, " No premature media: %s\n", AST_CLI_YESNO(global_prematuremediafilter));
- ast_cli(a->fd, " Max forwards: %d\n", sip_cfg.default_max_forwards);
- ast_cli(a->fd, "\nDefault Settings:\n");
- ast_cli(a->fd, "-----------------\n");
- ast_cli(a->fd, " Allowed transports: %s\n", get_transport_list(default_transports));
- ast_cli(a->fd, " Outbound transport: %s\n", sip_get_transport(default_primary_transport));
- ast_cli(a->fd, " Context: %s\n", sip_cfg.default_context);
- ast_cli(a->fd, " Record on feature: %s\n", sip_cfg.default_record_on_feature);
- ast_cli(a->fd, " Record off feature: %s\n", sip_cfg.default_record_off_feature);
- ast_cli(a->fd, " Force rport: %s\n", force_rport_string(global_flags));
- ast_cli(a->fd, " DTMF: %s\n", dtmfmode2str(ast_test_flag(&global_flags[0], SIP_DTMF)));
- ast_cli(a->fd, " Qualify: %d\n", default_qualify);
- ast_cli(a->fd, " Keepalive: %d\n", default_keepalive);
- ast_cli(a->fd, " Use ClientCode: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_USECLIENTCODE)));
- ast_cli(a->fd, " Progress inband: %s\n", (ast_test_flag(&global_flags[0], SIP_PROG_INBAND) == SIP_PROG_INBAND_NEVER) ? "Never" : (AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_PROG_INBAND) != SIP_PROG_INBAND_NO)));
- ast_cli(a->fd, " Language: %s\n", default_language);
- ast_cli(a->fd, " Tone zone: %s\n", default_zone[0] != '\0' ? default_zone : "<Not set>");
- ast_cli(a->fd, " MOH Interpret: %s\n", default_mohinterpret);
- ast_cli(a->fd, " MOH Suggest: %s\n", default_mohsuggest);
- ast_cli(a->fd, " Voice Mail Extension: %s\n", default_vmexten);
- if (realtimepeers || realtimeregs) {
- ast_cli(a->fd, "\nRealtime SIP Settings:\n");
- ast_cli(a->fd, "----------------------\n");
- ast_cli(a->fd, " Realtime Peers: %s\n", AST_CLI_YESNO(realtimepeers));
- ast_cli(a->fd, " Realtime Regs: %s\n", AST_CLI_YESNO(realtimeregs));
- ast_cli(a->fd, " Cache Friends: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)));
- ast_cli(a->fd, " Update: %s\n", AST_CLI_YESNO(sip_cfg.peer_rtupdate));
- ast_cli(a->fd, " Ignore Reg. Expire: %s\n", AST_CLI_YESNO(sip_cfg.ignore_regexpire));
- ast_cli(a->fd, " Save sys. name: %s\n", AST_CLI_YESNO(sip_cfg.rtsave_sysname));
- ast_cli(a->fd, " Save path header: %s\n", AST_CLI_YESNO(sip_cfg.rtsave_path));
- ast_cli(a->fd, " Auto Clear: %d (%s)\n", sip_cfg.rtautoclear, ast_test_flag(&global_flags[1], SIP_PAGE2_RTAUTOCLEAR) ? "Enabled" : "Disabled");
- }
- ast_cli(a->fd, "\n----\n");
- return CLI_SUCCESS;
- }
- static char *sip_show_mwi(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- #define FORMAT "%-30.30s %-12.12s %-10.10s %-10.10s\n"
- char host[80];
- struct ao2_iterator iter;
- struct sip_subscription_mwi *iterator;
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show mwi";
- e->usage =
- "Usage: sip show mwi\n"
- " Provides a list of MWI subscriptions and status.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- ast_cli(a->fd, FORMAT, "Host", "Username", "Mailbox", "Subscribed");
- iter = ao2_iterator_init(subscription_mwi_list, 0);
- while ((iterator = ao2_t_iterator_next(&iter, "sip_show_mwi iter"))) {
- ao2_lock(iterator);
- snprintf(host, sizeof(host), "%s:%d", iterator->hostname, iterator->portno ? iterator->portno : STANDARD_SIP_PORT);
- ast_cli(a->fd, FORMAT, host, iterator->username, iterator->mailbox, AST_CLI_YESNO(iterator->subscribed));
- ao2_unlock(iterator);
- ao2_t_ref(iterator, -1, "sip_show_mwi iter");
- }
- ao2_iterator_destroy(&iter);
- return CLI_SUCCESS;
- #undef FORMAT
- }
- /*! \brief Show subscription type in string format */
- static const char *subscription_type2str(enum subscriptiontype subtype)
- {
- int i;
- for (i = 1; i < ARRAY_LEN(subscription_types); i++) {
- if (subscription_types[i].type == subtype) {
- return subscription_types[i].text;
- }
- }
- return subscription_types[0].text;
- }
- /*! \brief Find subscription type in array */
- static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype)
- {
- int i;
- for (i = 1; i < ARRAY_LEN(subscription_types); i++) {
- if (subscription_types[i].type == subtype) {
- return &subscription_types[i];
- }
- }
- return &subscription_types[0];
- }
- /*
- * We try to structure all functions that loop on data structures as
- * a handler for individual entries, and a mainloop that iterates
- * on the main data structure. This way, moving the code to containers
- * that support iteration through callbacks will be a lot easier.
- */
- #define FORMAT4 "%-15.15s %-15.15s %-15.15s %-15.15s %-13.13s %-15.15s %-10.10s %-6.6d\n"
- #define FORMAT3 "%-15.15s %-15.15s %-15.15s %-15.15s %-13.13s %-15.15s %-10.10s %-6.6s\n"
- #define FORMAT2 "%-15.15s %-15.15s %-15.15s %-15.15s %-7.7s %-15.15s %-10.10s %-10.10s\n"
- #define FORMAT "%-15.15s %-15.15s %-15.15s %-15.15s %-3.3s %-3.3s %-15.15s %-10.10s %-10.10s\n"
- /*! \brief callback for show channel|subscription */
- static int show_channels_cb(void *__cur, void *__arg, int flags)
- {
- struct sip_pvt *cur = __cur;
- struct __show_chan_arg *arg = __arg;
- const struct ast_sockaddr *dst;
- sip_pvt_lock(cur);
- dst = sip_real_dst(cur);
- /* XXX indentation preserved to reduce diff. Will be fixed later */
- if (cur->subscribed == NONE && !arg->subscriptions) {
- /* set if SIP transfer in progress */
- const char *referstatus = cur->refer ? referstatus2str(cur->refer->status) : "";
- struct ast_str *codec_buf = ast_str_alloca(64);
- ast_cli(arg->fd, FORMAT, ast_sockaddr_stringify_addr(dst),
- S_OR(cur->username, S_OR(cur->cid_num, "(None)")),
- cur->callid,
- cur->owner ? ast_format_cap_get_names(ast_channel_nativeformats(cur->owner), &codec_buf) : "(nothing)",
- AST_CLI_YESNO(ast_test_flag(&cur->flags[1], SIP_PAGE2_CALL_ONHOLD)),
- cur->needdestroy ? "(d)" : "",
- cur->lastmsg ,
- referstatus,
- cur->relatedpeer ? cur->relatedpeer->name : "<guest>"
- );
- arg->numchans++;
- }
- if (cur->subscribed != NONE && arg->subscriptions) {
- struct ast_str *mailbox_str = ast_str_alloca(512);
- if (cur->subscribed == MWI_NOTIFICATION && cur->relatedpeer)
- peer_mailboxes_to_str(&mailbox_str, cur->relatedpeer);
- ast_cli(arg->fd, FORMAT4, ast_sockaddr_stringify_addr(dst),
- S_OR(cur->username, S_OR(cur->cid_num, "(None)")),
- cur->callid,
- /* the 'complete' exten/context is hidden in the refer_to field for subscriptions */
- cur->subscribed == MWI_NOTIFICATION ? "--" : cur->subscribeuri,
- cur->subscribed == MWI_NOTIFICATION ? "<none>" : ast_extension_state2str(cur->laststate),
- subscription_type2str(cur->subscribed),
- cur->subscribed == MWI_NOTIFICATION ? S_OR(ast_str_buffer(mailbox_str), "<none>") : "<none>",
- cur->expiry
- );
- arg->numchans++;
- }
- sip_pvt_unlock(cur);
- return 0; /* don't care, we scan all channels */
- }
- /*! \brief CLI for show channels or subscriptions.
- * This is a new-style CLI handler so a single function contains
- * the prototype for the function, the 'generator' to produce multiple
- * entries in case it is required, and the actual handler for the command.
- */
- static char *sip_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct __show_chan_arg arg = { .fd = a->fd, .numchans = 0 };
- if (cmd == CLI_INIT) {
- e->command = "sip show {channels|subscriptions}";
- e->usage =
- "Usage: sip show channels\n"
- " Lists all currently active SIP calls (dialogs).\n"
- "Usage: sip show subscriptions\n"
- " Lists active SIP subscriptions.\n";
- return NULL;
- } else if (cmd == CLI_GENERATE)
- return NULL;
- if (a->argc != e->args)
- return CLI_SHOWUSAGE;
- arg.subscriptions = !strcasecmp(a->argv[e->args - 1], "subscriptions");
- if (!arg.subscriptions)
- ast_cli(arg.fd, FORMAT2, "Peer", "User/ANR", "Call ID", "Format", "Hold", "Last Message", "Expiry", "Peer");
- else
- ast_cli(arg.fd, FORMAT3, "Peer", "User", "Call ID", "Extension", "Last state", "Type", "Mailbox", "Expiry");
- /* iterate on the container and invoke the callback on each item */
- ao2_t_callback(dialogs, OBJ_NODATA, show_channels_cb, &arg, "callback to show channels");
- /* print summary information */
- ast_cli(arg.fd, "%d active SIP %s%s\n", arg.numchans,
- (arg.subscriptions ? "subscription" : "dialog"),
- ESS(arg.numchans)); /* ESS(n) returns an "s" if n>1 */
- return CLI_SUCCESS;
- #undef FORMAT
- #undef FORMAT2
- #undef FORMAT3
- }
- /*! \brief Support routine for 'sip show channel' and 'sip show history' CLI
- * This is in charge of generating all strings that match a prefix in the
- * given position. As many functions of this kind, each invokation has
- * O(state) time complexity so be careful in using it.
- */
- static char *complete_sipch(const char *line, const char *word, int pos, int state)
- {
- int which=0;
- struct sip_pvt *cur;
- char *c = NULL;
- int wordlen = strlen(word);
- struct ao2_iterator i;
- if (pos != 3) {
- return NULL;
- }
- i = ao2_iterator_init(dialogs, 0);
- while ((cur = ao2_t_iterator_next(&i, "iterate thru dialogs"))) {
- sip_pvt_lock(cur);
- if (!strncasecmp(word, cur->callid, wordlen) && ++which > state) {
- c = ast_strdup(cur->callid);
- sip_pvt_unlock(cur);
- dialog_unref(cur, "drop ref in iterator loop break");
- break;
- }
- sip_pvt_unlock(cur);
- dialog_unref(cur, "drop ref in iterator loop");
- }
- ao2_iterator_destroy(&i);
- return c;
- }
- /*! \brief Do completion on peer name */
- static char *complete_sip_peer(const char *word, int state, int flags2)
- {
- char *result = NULL;
- int wordlen = strlen(word);
- int which = 0;
- struct ao2_iterator i = ao2_iterator_init(peers, 0);
- struct sip_peer *peer;
- while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
- /* locking of the object is not required because only the name and flags are being compared */
- if (!strncasecmp(word, peer->name, wordlen) &&
- (!flags2 || ast_test_flag(&peer->flags[1], flags2)) &&
- ++which > state)
- result = ast_strdup(peer->name);
- sip_unref_peer(peer, "toss iterator peer ptr before break");
- if (result) {
- break;
- }
- }
- ao2_iterator_destroy(&i);
- return result;
- }
- /*! \brief Do completion on registered peer name */
- static char *complete_sip_registered_peer(const char *word, int state, int flags2)
- {
- char *result = NULL;
- int wordlen = strlen(word);
- int which = 0;
- struct ao2_iterator i;
- struct sip_peer *peer;
- i = ao2_iterator_init(peers, 0);
- while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
- if (!strncasecmp(word, peer->name, wordlen) &&
- (!flags2 || ast_test_flag(&peer->flags[1], flags2)) &&
- ++which > state && peer->expire > 0)
- result = ast_strdup(peer->name);
- if (result) {
- sip_unref_peer(peer, "toss iterator peer ptr before break");
- break;
- }
- sip_unref_peer(peer, "toss iterator peer ptr");
- }
- ao2_iterator_destroy(&i);
- return result;
- }
- /*! \brief Support routine for 'sip show history' CLI */
- static char *complete_sip_show_history(const char *line, const char *word, int pos, int state)
- {
- if (pos == 3)
- return complete_sipch(line, word, pos, state);
- return NULL;
- }
- /*! \brief Support routine for 'sip show peer' CLI */
- static char *complete_sip_show_peer(const char *line, const char *word, int pos, int state)
- {
- if (pos == 3) {
- return complete_sip_peer(word, state, 0);
- }
- return NULL;
- }
- /*! \brief Support routine for 'sip unregister' CLI */
- static char *complete_sip_unregister(const char *line, const char *word, int pos, int state)
- {
- if (pos == 2)
- return complete_sip_registered_peer(word, state, 0);
- return NULL;
- }
- /*! \brief Support routine for 'sip notify' CLI */
- static char *complete_sip_notify(const char *line, const char *word, int pos, int state)
- {
- char *c = NULL;
- if (pos == 2) {
- int which = 0;
- char *cat = NULL;
- int wordlen = strlen(word);
- /* do completion for notify type */
- if (!notify_types)
- return NULL;
- while ( (cat = ast_category_browse(notify_types, cat)) ) {
- if (!strncasecmp(word, cat, wordlen) && ++which > state) {
- c = ast_strdup(cat);
- break;
- }
- }
- return c;
- }
- if (pos > 2)
- return complete_sip_peer(word, state, 0);
- return NULL;
- }
- /*! \brief Show details of one active dialog */
- static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct sip_pvt *cur;
- size_t len;
- int found = 0;
- struct ao2_iterator i;
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show channel";
- e->usage =
- "Usage: sip show channel <call-id>\n"
- " Provides detailed status on a given SIP dialog (identified by SIP call-id).\n";
- return NULL;
- case CLI_GENERATE:
- return complete_sipch(a->line, a->word, a->pos, a->n);
- }
- if (a->argc != 4)
- return CLI_SHOWUSAGE;
- len = strlen(a->argv[3]);
- i = ao2_iterator_init(dialogs, 0);
- while ((cur = ao2_t_iterator_next(&i, "iterate thru dialogs"))) {
- sip_pvt_lock(cur);
- if (!strncasecmp(cur->callid, a->argv[3], len)) {
- struct ast_str *strbuf;
- struct ast_str *codec_buf = ast_str_alloca(64);
- ast_cli(a->fd, "\n");
- if (cur->subscribed != NONE) {
- ast_cli(a->fd, " * Subscription (type: %s)\n", subscription_type2str(cur->subscribed));
- } else {
- ast_cli(a->fd, " * SIP Call\n");
- }
- ast_cli(a->fd, " Curr. trans. direction: %s\n", ast_test_flag(&cur->flags[0], SIP_OUTGOING) ? "Outgoing" : "Incoming");
- ast_cli(a->fd, " Call-ID: %s\n", cur->callid);
- ast_cli(a->fd, " Owner channel ID: %s\n", cur->owner ? ast_channel_name(cur->owner) : "<none>");
- ast_cli(a->fd, " Our Codec Capability: %s\n", ast_format_cap_get_names(cur->caps, &codec_buf));
- ast_cli(a->fd, " Non-Codec Capability (DTMF): %d\n", cur->noncodeccapability);
- ast_cli(a->fd, " Their Codec Capability: %s\n", ast_format_cap_get_names(cur->peercaps, &codec_buf));
- ast_cli(a->fd, " Joint Codec Capability: %s\n", ast_format_cap_get_names(cur->jointcaps, &codec_buf));
- ast_cli(a->fd, " Format: %s\n", cur->owner ? ast_format_cap_get_names(ast_channel_nativeformats(cur->owner), &codec_buf) : "(nothing)" );
- ast_cli(a->fd, " T.38 support %s\n", AST_CLI_YESNO(cur->udptl != NULL));
- ast_cli(a->fd, " Video support %s\n", AST_CLI_YESNO(cur->vrtp != NULL));
- ast_cli(a->fd, " MaxCallBR: %d kbps\n", cur->maxcallbitrate);
- ast_cli(a->fd, " Theoretical Address: %s\n", ast_sockaddr_stringify(&cur->sa));
- ast_cli(a->fd, " Received Address: %s\n", ast_sockaddr_stringify(&cur->recv));
- ast_cli(a->fd, " SIP Transfer mode: %s\n", transfermode2str(cur->allowtransfer));
- ast_cli(a->fd, " Force rport: %s\n", force_rport_string(cur->flags));
- if (ast_sockaddr_isnull(&cur->redirip)) {
- ast_cli(a->fd,
- " Audio IP: %s (local)\n",
- ast_sockaddr_stringify_addr(&cur->ourip));
- } else {
- ast_cli(a->fd,
- " Audio IP: %s (Outside bridge)\n",
- ast_sockaddr_stringify_addr(&cur->redirip));
- }
- ast_cli(a->fd, " Our Tag: %s\n", cur->tag);
- ast_cli(a->fd, " Their Tag: %s\n", cur->theirtag);
- ast_cli(a->fd, " SIP User agent: %s\n", cur->useragent);
- if (!ast_strlen_zero(cur->username)) {
- ast_cli(a->fd, " Username: %s\n", cur->username);
- }
- if (!ast_strlen_zero(cur->peername)) {
- ast_cli(a->fd, " Peername: %s\n", cur->peername);
- }
- if (!ast_strlen_zero(cur->uri)) {
- ast_cli(a->fd, " Original uri: %s\n", cur->uri);
- }
- if (!ast_strlen_zero(cur->cid_num)) {
- ast_cli(a->fd, " Caller-ID: %s\n", cur->cid_num);
- }
- ast_cli(a->fd, " Need Destroy: %s\n", AST_CLI_YESNO(cur->needdestroy));
- ast_cli(a->fd, " Last Message: %s\n", cur->lastmsg);
- ast_cli(a->fd, " Promiscuous Redir: %s\n", AST_CLI_YESNO(ast_test_flag(&cur->flags[0], SIP_PROMISCREDIR)));
- if ((strbuf = sip_route_list(&cur->route, 1, 0))) {
- ast_cli(a->fd, " Route: %s\n", ast_str_buffer(strbuf));
- ast_free(strbuf);
- }
- ast_cli(a->fd, " DTMF Mode: %s\n", dtmfmode2str(ast_test_flag(&cur->flags[0], SIP_DTMF)));
- ast_cli(a->fd, " SIP Options: ");
- if (cur->sipoptions) {
- int x;
- for (x = 0 ; x < ARRAY_LEN(sip_options); x++) {
- if (cur->sipoptions & sip_options[x].id)
- ast_cli(a->fd, "%s ", sip_options[x].text);
- }
- ast_cli(a->fd, "\n");
- } else {
- ast_cli(a->fd, "(none)\n");
- }
- if (!cur->stimer) {
- ast_cli(a->fd, " Session-Timer: Uninitiallized\n");
- } else {
- ast_cli(a->fd, " Session-Timer: %s\n", cur->stimer->st_active ? "Active" : "Inactive");
- if (cur->stimer->st_active == TRUE) {
- ast_cli(a->fd, " S-Timer Interval: %d\n", cur->stimer->st_interval);
- ast_cli(a->fd, " S-Timer Refresher: %s\n", strefresher2str(cur->stimer->st_ref));
- ast_cli(a->fd, " S-Timer Sched Id: %d\n", cur->stimer->st_schedid);
- ast_cli(a->fd, " S-Timer Peer Sts: %s\n", cur->stimer->st_active_peer_ua ? "Active" : "Inactive");
- ast_cli(a->fd, " S-Timer Cached Min-SE: %d\n", cur->stimer->st_cached_min_se);
- ast_cli(a->fd, " S-Timer Cached SE: %d\n", cur->stimer->st_cached_max_se);
- ast_cli(a->fd, " S-Timer Cached Ref: %s\n", strefresher2str(cur->stimer->st_cached_ref));
- ast_cli(a->fd, " S-Timer Cached Mode: %s\n", stmode2str(cur->stimer->st_cached_mode));
- }
- }
- /* add transport and media types */
- ast_cli(a->fd, " Transport: %s\n", ast_transport2str(cur->socket.type));
- ast_cli(a->fd, " Media: %s\n", cur->srtp ? "SRTP" : cur->rtp ? "RTP" : "None");
- ast_cli(a->fd, "\n\n");
- found++;
- }
- sip_pvt_unlock(cur);
- ao2_t_ref(cur, -1, "toss dialog ptr set by iterator_next");
- }
- ao2_iterator_destroy(&i);
- if (!found) {
- ast_cli(a->fd, "No such SIP Call ID starting with '%s'\n", a->argv[3]);
- }
- return CLI_SUCCESS;
- }
- /*! \brief Show history details of one dialog */
- static char *sip_show_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct sip_pvt *cur;
- size_t len;
- int found = 0;
- struct ao2_iterator i;
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show history";
- e->usage =
- "Usage: sip show history <call-id>\n"
- " Provides detailed dialog history on a given SIP call (specified by call-id).\n";
- return NULL;
- case CLI_GENERATE:
- return complete_sip_show_history(a->line, a->word, a->pos, a->n);
- }
- if (a->argc != 4) {
- return CLI_SHOWUSAGE;
- }
- if (!recordhistory) {
- ast_cli(a->fd, "\n***Note: History recording is currently DISABLED. Use 'sip set history on' to ENABLE.\n");
- }
- len = strlen(a->argv[3]);
- i = ao2_iterator_init(dialogs, 0);
- while ((cur = ao2_t_iterator_next(&i, "iterate thru dialogs"))) {
- sip_pvt_lock(cur);
- if (!strncasecmp(cur->callid, a->argv[3], len)) {
- struct sip_history *hist;
- int x = 0;
- ast_cli(a->fd, "\n");
- if (cur->subscribed != NONE) {
- ast_cli(a->fd, " * Subscription\n");
- } else {
- ast_cli(a->fd, " * SIP Call\n");
- }
- if (cur->history) {
- AST_LIST_TRAVERSE(cur->history, hist, list)
- ast_cli(a->fd, "%d. %s\n", ++x, hist->event);
- }
- if (x == 0) {
- ast_cli(a->fd, "Call '%s' has no history\n", cur->callid);
- }
- found++;
- }
- sip_pvt_unlock(cur);
- ao2_t_ref(cur, -1, "toss dialog ptr from iterator_next");
- }
- ao2_iterator_destroy(&i);
- if (!found) {
- ast_cli(a->fd, "No such SIP Call ID starting with '%s'\n", a->argv[3]);
- }
- return CLI_SUCCESS;
- }
- /*! \brief Dump SIP history to debug log file at end of lifespan for SIP dialog */
- static void sip_dump_history(struct sip_pvt *dialog)
- {
- int x = 0;
- struct sip_history *hist;
- static int errmsg = 0;
- if (!dialog) {
- return;
- }
- if (!option_debug && !sipdebug) {
- if (!errmsg) {
- ast_log(LOG_NOTICE, "You must have debugging enabled (SIP or Asterisk) in order to dump SIP history.\n");
- errmsg = 1;
- }
- return;
- }
- ast_debug(1, "\n---------- SIP HISTORY for '%s' \n", dialog->callid);
- if (dialog->subscribed) {
- ast_debug(1, " * Subscription\n");
- } else {
- ast_debug(1, " * SIP Call\n");
- }
- if (dialog->history) {
- AST_LIST_TRAVERSE(dialog->history, hist, list)
- ast_debug(1, " %-3.3d. %s\n", ++x, hist->event);
- }
- if (!x) {
- ast_debug(1, "Call '%s' has no history\n", dialog->callid);
- }
- ast_debug(1, "\n---------- END SIP HISTORY for '%s' \n", dialog->callid);
- }
- /*! \brief Receive SIP INFO Message */
- static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
- {
- const char *buf = "";
- unsigned int event;
- const char *c = sip_get_header(req, "Content-Type");
- /* Need to check the media/type */
- if (!strcasecmp(c, "application/dtmf-relay") ||
- !strcasecmp(c, "application/vnd.nortelnetworks.digits") ||
- !strcasecmp(c, "application/dtmf")) {
- unsigned int duration = 0;
- if (!p->owner) { /* not a PBX call */
- transmit_response(p, "481 Call leg/transaction does not exist", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- }
- /* If dtmf-relay or vnd.nortelnetworks.digits, parse the signal and duration;
- * otherwise use the body as the signal */
- if (strcasecmp(c, "application/dtmf")) {
- const char *tmp;
- if (ast_strlen_zero(buf = get_content_line(req, "Signal", '='))
- && ast_strlen_zero(buf = get_content_line(req, "d", '='))) {
- ast_log(LOG_WARNING, "Unable to retrieve DTMF signal for INFO message on "
- "call %s\n", p->callid);
- transmit_response(p, "200 OK", req);
- return;
- }
- if (!ast_strlen_zero((tmp = get_content_line(req, "Duration", '=')))) {
- sscanf(tmp, "%30u", &duration);
- }
- } else {
- /* Type is application/dtmf, simply use what's in the message body */
- buf = get_content(req);
- }
- /* An empty message body requires us to send a 200 OK */
- if (ast_strlen_zero(buf)) {
- transmit_response(p, "200 OK", req);
- return;
- }
- if (!duration) {
- duration = 100; /* 100 ms */
- }
- if (buf[0] == '*') {
- event = 10;
- } else if (buf[0] == '#') {
- event = 11;
- } else if (buf[0] == '!') {
- event = 16;
- } else if ('A' <= buf[0] && buf[0] <= 'D') {
- event = 12 + buf[0] - 'A';
- } else if ('a' <= buf[0] && buf[0] <= 'd') {
- event = 12 + buf[0] - 'a';
- } else if ((sscanf(buf, "%30u", &event) != 1) || event > 16) {
- ast_log(AST_LOG_WARNING, "Unable to convert DTMF event signal code to a valid "
- "value for INFO message on call %s\n", p->callid);
- transmit_response(p, "200 OK", req);
- return;
- }
- if (event == 16) {
- /* send a FLASH event */
- struct ast_frame f = { AST_FRAME_CONTROL, { AST_CONTROL_FLASH, } };
- ast_queue_frame(p->owner, &f);
- if (sipdebug) {
- ast_verbose("* DTMF-relay event received: FLASH\n");
- }
- } else {
- /* send a DTMF event */
- struct ast_frame f = { AST_FRAME_DTMF, };
- if (event < 10) {
- f.subclass.integer = '0' + event;
- } else if (event == 10) {
- f.subclass.integer = '*';
- } else if (event == 11) {
- f.subclass.integer = '#';
- } else {
- f.subclass.integer = 'A' + (event - 12);
- }
- f.len = duration;
- ast_queue_frame(p->owner, &f);
- if (sipdebug) {
- ast_verbose("* DTMF-relay event received: %c\n", (int) f.subclass.integer);
- }
- }
- transmit_response(p, "200 OK", req);
- return;
- } else if (!strcasecmp(c, "application/media_control+xml")) {
- /* Eh, we'll just assume it's a fast picture update for now */
- if (p->owner) {
- ast_queue_control(p->owner, AST_CONTROL_VIDUPDATE);
- }
- transmit_response(p, "200 OK", req);
- return;
- } else if (!ast_strlen_zero(c = sip_get_header(req, "X-ClientCode"))) {
- /* Client code (from SNOM phone) */
- if (ast_test_flag(&p->flags[0], SIP_USECLIENTCODE)) {
- if (p->owner) {
- ast_cdr_setuserfield(ast_channel_name(p->owner), c);
- }
- transmit_response(p, "200 OK", req);
- } else {
- transmit_response(p, "403 Forbidden", req);
- }
- return;
- } else if (!ast_strlen_zero(c = sip_get_header(req, "Record"))) {
- /* INFO messages generated by some phones to start/stop recording
- * on phone calls.
- */
- char feat[AST_FEATURE_MAX_LEN];
- int feat_res = -1;
- int j;
- struct ast_frame f = { AST_FRAME_DTMF, };
- int suppress_warning = 0; /* Supress warning if the feature is blank */
- if (!p->owner) { /* not a PBX call */
- transmit_response(p, "481 Call leg/transaction does not exist", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- }
- /* first, get the feature string, if it exists */
- if (p->relatedpeer) {
- if (!strcasecmp(c, "on")) {
- if (ast_strlen_zero(p->relatedpeer->record_on_feature)) {
- suppress_warning = 1;
- } else {
- feat_res = ast_get_feature(p->owner, p->relatedpeer->record_on_feature, feat, sizeof(feat));
- }
- } else if (!strcasecmp(c, "off")) {
- if (ast_strlen_zero(p->relatedpeer->record_off_feature)) {
- suppress_warning = 1;
- } else {
- feat_res = ast_get_feature(p->owner, p->relatedpeer->record_off_feature, feat, sizeof(feat));
- }
- } else {
- ast_log(LOG_ERROR, "Received INFO requesting to record with invalid value: %s\n", c);
- }
- }
- if (feat_res || ast_strlen_zero(feat)) {
- if (!suppress_warning) {
- ast_log(LOG_WARNING, "Recording requested, but no One Touch Monitor registered. (See features.conf)\n");
- }
- /* 403 means that we don't support this feature, so don't request it again */
- transmit_response(p, "403 Forbidden", req);
- return;
- }
- /* Send the feature code to the PBX as DTMF, just like the handset had sent it */
- f.len = 100;
- for (j = 0; j < strlen(feat); j++) {
- f.subclass.integer = feat[j];
- ast_queue_frame(p->owner, &f);
- if (sipdebug) {
- ast_verbose("* DTMF-relay event faked: %c\n", f.subclass.integer);
- }
- }
- ast_debug(1, "Got a Request to Record the channel, state %s\n", c);
- transmit_response(p, "200 OK", req);
- return;
- } else if (ast_strlen_zero(c = sip_get_header(req, "Content-Length")) || !strcasecmp(c, "0")) {
- /* This is probably just a packet making sure the signalling is still up, just send back a 200 OK */
- transmit_response(p, "200 OK", req);
- return;
- }
- /* Other type of INFO message, not really understood by Asterisk */
- ast_log(LOG_WARNING, "Unable to parse INFO message from %s. Content %s\n", p->callid, buf);
- transmit_response(p, "415 Unsupported media type", req);
- return;
- }
- /*! \brief Enable SIP Debugging for a single IP */
- static char *sip_do_debug_ip(int fd, const char *arg)
- {
- if (ast_sockaddr_resolve_first_af(&debugaddr, arg, 0, 0)) {
- return CLI_SHOWUSAGE;
- }
- ast_cli(fd, "SIP Debugging Enabled for IP: %s\n", ast_sockaddr_stringify_addr(&debugaddr));
- sipdebug |= sip_debug_console;
- return CLI_SUCCESS;
- }
- /*! \brief Turn on SIP debugging for a given peer */
- static char *sip_do_debug_peer(int fd, const char *arg)
- {
- struct sip_peer *peer = sip_find_peer(arg, NULL, TRUE, FINDPEERS, FALSE, 0);
- if (!peer) {
- ast_cli(fd, "No such peer '%s'\n", arg);
- } else if (ast_sockaddr_isnull(&peer->addr)) {
- ast_cli(fd, "Unable to get IP address of peer '%s'\n", arg);
- } else {
- ast_sockaddr_copy(&debugaddr, &peer->addr);
- ast_cli(fd, "SIP Debugging Enabled for IP: %s\n", ast_sockaddr_stringify_addr(&debugaddr));
- sipdebug |= sip_debug_console;
- }
- if (peer) {
- sip_unref_peer(peer, "sip_do_debug_peer: sip_unref_peer, from sip_find_peer call");
- }
- return CLI_SUCCESS;
- }
- /*! \brief Turn on SIP debugging (CLI command) */
- static char *sip_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- int oldsipdebug = sipdebug & sip_debug_console;
- const char *what;
- if (cmd == CLI_INIT) {
- e->command = "sip set debug {on|off|ip|peer}";
- e->usage =
- "Usage: sip set debug {off|on|ip addr[:port]|peer peername}\n"
- " Globally disables dumping of SIP packets,\n"
- " or enables it either globally or for a (single)\n"
- " IP address or registered peer.\n";
- return NULL;
- } else if (cmd == CLI_GENERATE) {
- if (a->pos == 4 && !strcasecmp(a->argv[3], "peer"))
- return complete_sip_peer(a->word, a->n, 0);
- return NULL;
- }
- what = a->argv[e->args-1]; /* guaranteed to exist */
- if (a->argc == e->args) { /* on/off */
- if (!strcasecmp(what, "on")) {
- sipdebug |= sip_debug_console;
- sipdebug_text = 1; /*! \note this can be a special debug command - "sip debug text" or something */
- memset(&debugaddr, 0, sizeof(debugaddr));
- ast_cli(a->fd, "SIP Debugging %senabled\n", oldsipdebug ? "re-" : "");
- return CLI_SUCCESS;
- } else if (!strcasecmp(what, "off")) {
- sipdebug &= ~sip_debug_console;
- sipdebug_text = 0;
- ast_cli(a->fd, "SIP Debugging Disabled\n");
- return CLI_SUCCESS;
- }
- } else if (a->argc == e->args + 1) { /* ip/peer */
- if (!strcasecmp(what, "ip"))
- return sip_do_debug_ip(a->fd, a->argv[e->args]);
- else if (!strcasecmp(what, "peer"))
- return sip_do_debug_peer(a->fd, a->argv[e->args]);
- }
- return CLI_SHOWUSAGE; /* default, failure */
- }
- /*! \brief Cli command to send SIP notify to peer */
- static char *sip_cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct ast_variable *varlist;
- int i;
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip notify";
- e->usage =
- "Usage: sip notify <type> <peer> [<peer>...]\n"
- " Send a NOTIFY message to a SIP peer or peers\n"
- " Message types are defined in sip_notify.conf\n";
- return NULL;
- case CLI_GENERATE:
- return complete_sip_notify(a->line, a->word, a->pos, a->n);
- }
- if (a->argc < 4)
- return CLI_SHOWUSAGE;
- if (!notify_types) {
- ast_cli(a->fd, "No %s file found, or no types listed there\n", notify_config);
- return CLI_FAILURE;
- }
- varlist = ast_variable_browse(notify_types, a->argv[2]);
- if (!varlist) {
- ast_cli(a->fd, "Unable to find notify type '%s'\n", a->argv[2]);
- return CLI_FAILURE;
- }
- for (i = 3; i < a->argc; i++) {
- struct sip_pvt *p;
- char buf[512];
- struct ast_variable *header, *var;
- if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY, NULL, 0))) {
- ast_log(LOG_WARNING, "Unable to build sip pvt data for notify (memory/socket error)\n");
- return CLI_FAILURE;
- }
- if (create_addr(p, a->argv[i], NULL, 1)) {
- /* Maybe they're not registered, etc. */
- dialog_unlink_all(p);
- dialog_unref(p, "unref dialog inside for loop" );
- /* sip_destroy(p); */
- ast_cli(a->fd, "Could not create address for '%s'\n", a->argv[i]);
- continue;
- }
- /* Notify is outgoing call */
- ast_set_flag(&p->flags[0], SIP_OUTGOING);
- sip_notify_alloc(p);
- p->notify->headers = header = ast_variable_new("Subscription-State", "terminated", "");
- for (var = varlist; var; var = var->next) {
- ast_copy_string(buf, var->value, sizeof(buf));
- ast_unescape_semicolon(buf);
- if (!strcasecmp(var->name, "Content")) {
- if (ast_str_strlen(p->notify->content))
- ast_str_append(&p->notify->content, 0, "\r\n");
- ast_str_append(&p->notify->content, 0, "%s", buf);
- } else if (!strcasecmp(var->name, "Content-Length")) {
- ast_log(LOG_WARNING, "it is not necessary to specify Content-Length in sip_notify.conf, ignoring\n");
- } else {
- header->next = ast_variable_new(var->name, buf, "");
- header = header->next;
- }
- }
- /* Now that we have the peer's address, set our ip and change callid */
- ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
- build_via(p);
- change_callid_pvt(p, NULL);
- ast_cli(a->fd, "Sending NOTIFY of type '%s' to '%s'\n", a->argv[2], a->argv[i]);
- sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
- transmit_invite(p, SIP_NOTIFY, 0, 2, NULL);
- dialog_unref(p, "bump down the count of p since we're done with it.");
- }
- return CLI_SUCCESS;
- }
- /*! \brief Enable/Disable SIP History logging (CLI) */
- static char *sip_set_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip set history {on|off}";
- e->usage =
- "Usage: sip set history {on|off}\n"
- " Enables/Disables recording of SIP dialog history for debugging purposes.\n"
- " Use 'sip show history' to view the history of a call number.\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)) {
- recordhistory = TRUE;
- ast_cli(a->fd, "SIP History Recording Enabled (use 'sip show history')\n");
- } else if (!strncasecmp(a->argv[e->args - 1], "off", 3)) {
- recordhistory = FALSE;
- ast_cli(a->fd, "SIP History Recording Disabled\n");
- } else {
- return CLI_SHOWUSAGE;
- }
- return CLI_SUCCESS;
- }
- /*! \brief Authenticate for outbound registration */
- static int do_register_auth(struct sip_pvt *p, struct sip_request *req, enum sip_auth_type code)
- {
- char *header, *respheader;
- char digest[1024];
- p->authtries++;
- sip_auth_headers(code, &header, &respheader);
- memset(digest, 0, sizeof(digest));
- if (reply_digest(p, req, header, SIP_REGISTER, digest, sizeof(digest))) {
- /* There's nothing to use for authentication */
- /* No digest challenge in request */
- if (sip_debug_test_pvt(p) && p->registry)
- ast_verbose("No authentication challenge, sending blank registration to domain/host name %s\n", p->registry->hostname);
- /* No old challenge */
- return -1;
- }
- if (p->do_history)
- append_history(p, "RegistryAuth", "Try: %d", p->authtries);
- if (sip_debug_test_pvt(p) && p->registry)
- ast_verbose("Responding to challenge, registration to domain/host name %s\n", p->registry->hostname);
- return transmit_register(p->registry, SIP_REGISTER, digest, respheader);
- }
- /*! \brief Add authentication on outbound SIP packet */
- static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req, enum sip_auth_type code, int sipmethod, int init)
- {
- char *header, *respheader;
- char digest[1024];
- if (!p->options && !(p->options = ast_calloc(1, sizeof(*p->options))))
- return -2;
- p->authtries++;
- sip_auth_headers(code, &header, &respheader);
- ast_debug(2, "Auth attempt %d on %s\n", p->authtries, sip_methods[sipmethod].text);
- memset(digest, 0, sizeof(digest));
- if (reply_digest(p, req, header, sipmethod, digest, sizeof(digest) )) {
- /* No way to authenticate */
- return -1;
- }
- /* Now we have a reply digest */
- p->options->auth = digest;
- p->options->authheader = respheader;
- return transmit_invite(p, sipmethod, sipmethod == SIP_INVITE, init, NULL);
- }
- /*! \brief reply to authentication for outbound registrations
- \return Returns -1 if we have no auth
- \note This is used for register= servers in sip.conf, SIP proxies we register
- with for receiving calls from. */
- static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, int sipmethod, char *digest, int digest_len)
- {
- char tmp[512];
- char *c;
- char oldnonce[256];
- /* table of recognised keywords, and places where they should be copied */
- const struct x {
- const char *key;
- const ast_string_field *field;
- } *i, keys[] = {
- { "realm=", &p->realm },
- { "nonce=", &p->nonce },
- { "opaque=", &p->opaque },
- { "qop=", &p->qop },
- { "domain=", &p->domain },
- { NULL, 0 },
- };
- ast_copy_string(tmp, sip_get_header(req, header), sizeof(tmp));
- if (ast_strlen_zero(tmp))
- return -1;
- if (strncasecmp(tmp, "Digest ", strlen("Digest "))) {
- ast_log(LOG_WARNING, "missing Digest.\n");
- return -1;
- }
- c = tmp + strlen("Digest ");
- ast_copy_string(oldnonce, p->nonce, sizeof(oldnonce));
- while (c && *(c = ast_skip_blanks(c))) { /* lookup for keys */
- for (i = keys; i->key != NULL; i++) {
- char *src, *separator;
- if (strncasecmp(c, i->key, strlen(i->key)) != 0)
- continue;
- /* Found. Skip keyword, take text in quotes or up to the separator. */
- c += strlen(i->key);
- if (*c == '"') {
- src = ++c;
- separator = "\"";
- } else {
- src = c;
- separator = ",";
- }
- strsep(&c, separator); /* clear separator and move ptr */
- ast_string_field_ptr_set(p, i->field, src);
- break;
- }
- if (i->key == NULL) /* not found, try ',' */
- strsep(&c, ",");
- }
- /* Reset nonce count */
- if (strcmp(p->nonce, oldnonce))
- p->noncecount = 0;
- /* Save auth data for following registrations */
- if (p->registry) {
- struct sip_registry *r = p->registry;
- if (strcmp(r->nonce, p->nonce)) {
- ast_string_field_set(r, realm, p->realm);
- ast_string_field_set(r, nonce, p->nonce);
- ast_string_field_set(r, authdomain, p->domain);
- ast_string_field_set(r, opaque, p->opaque);
- ast_string_field_set(r, qop, p->qop);
- r->noncecount = 0;
- }
- }
- return build_reply_digest(p, sipmethod, digest, digest_len);
- }
- /*! \brief Build reply digest
- \return Returns -1 if we have no auth
- \note Build digest challenge for authentication of registrations and calls
- Also used for authentication of BYE
- */
- static int build_reply_digest(struct sip_pvt *p, int method, char* digest, int digest_len)
- {
- char a1[256];
- char a2[256];
- char a1_hash[256];
- char a2_hash[256];
- char resp[256];
- char resp_hash[256];
- char uri[256];
- char opaque[256] = "";
- char cnonce[80];
- const char *username;
- const char *secret;
- const char *md5secret;
- struct sip_auth *auth; /* Realm authentication credential */
- struct sip_auth_container *credentials;
- if (!ast_strlen_zero(p->domain))
- snprintf(uri, sizeof(uri), "%s:%s", p->socket.type == AST_TRANSPORT_TLS ? "sips" : "sip", p->domain);
- else if (!ast_strlen_zero(p->uri))
- ast_copy_string(uri, p->uri, sizeof(uri));
- else
- snprintf(uri, sizeof(uri), "%s:%s@%s", p->socket.type == AST_TRANSPORT_TLS ? "sips" : "sip", p->username, ast_sockaddr_stringify_host_remote(&p->sa));
- snprintf(cnonce, sizeof(cnonce), "%08lx", (unsigned long)ast_random());
- /* Check if we have peer credentials */
- ao2_lock(p);
- credentials = p->peerauth;
- if (credentials) {
- ao2_t_ref(credentials, +1, "Ref peer auth for digest");
- }
- ao2_unlock(p);
- auth = find_realm_authentication(credentials, p->realm);
- if (!auth) {
- /* If not, check global credentials */
- if (credentials) {
- ao2_t_ref(credentials, -1, "Unref peer auth for digest");
- }
- ast_mutex_lock(&authl_lock);
- credentials = authl;
- if (credentials) {
- ao2_t_ref(credentials, +1, "Ref global auth for digest");
- }
- ast_mutex_unlock(&authl_lock);
- auth = find_realm_authentication(credentials, p->realm);
- }
- if (auth) {
- ast_debug(3, "use realm [%s] from peer [%s][%s]\n", auth->username, p->peername, p->username);
- username = auth->username;
- secret = auth->secret;
- md5secret = auth->md5secret;
- if (sipdebug)
- ast_debug(1, "Using realm %s authentication for call %s\n", p->realm, p->callid);
- } else {
- /* No authentication, use peer or register= config */
- username = p->authname;
- secret = p->relatedpeer
- && !ast_strlen_zero(p->relatedpeer->remotesecret)
- ? p->relatedpeer->remotesecret : p->peersecret;
- md5secret = p->peermd5secret;
- }
- if (ast_strlen_zero(username)) {
- /* We have no authentication */
- if (credentials) {
- ao2_t_ref(credentials, -1, "Unref auth for digest");
- }
- return -1;
- }
- /* Calculate SIP digest response */
- snprintf(a1, sizeof(a1), "%s:%s:%s", username, p->realm, secret);
- snprintf(a2, sizeof(a2), "%s:%s", sip_methods[method].text, uri);
- if (!ast_strlen_zero(md5secret))
- ast_copy_string(a1_hash, md5secret, sizeof(a1_hash));
- else
- ast_md5_hash(a1_hash, a1);
- ast_md5_hash(a2_hash, a2);
- p->noncecount++;
- if (!ast_strlen_zero(p->qop))
- snprintf(resp, sizeof(resp), "%s:%s:%08x:%s:%s:%s", a1_hash, p->nonce, (unsigned)p->noncecount, cnonce, "auth", a2_hash);
- else
- snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, p->nonce, a2_hash);
- ast_md5_hash(resp_hash, resp);
- /* only include the opaque string if it's set */
- if (!ast_strlen_zero(p->opaque)) {
- snprintf(opaque, sizeof(opaque), ", opaque=\"%s\"", p->opaque);
- }
- /* XXX We hard code our qop to "auth" for now. XXX */
- if (!ast_strlen_zero(p->qop))
- snprintf(digest, digest_len, "Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\"%s, qop=auth, cnonce=\"%s\", nc=%08x", username, p->realm, uri, p->nonce, resp_hash, opaque, cnonce, (unsigned)p->noncecount);
- else
- snprintf(digest, digest_len, "Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\"%s", username, p->realm, uri, p->nonce, resp_hash, opaque);
- append_history(p, "AuthResp", "Auth response sent for %s in realm %s - nc %d", username, p->realm, p->noncecount);
- if (credentials) {
- ao2_t_ref(credentials, -1, "Unref auth for digest");
- }
- return 0;
- }
- /*! \brief Read SIP header (dialplan function) */
- static int func_header_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len)
- {
- struct sip_pvt *p;
- const char *content = NULL;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(header);
- AST_APP_ARG(number);
- );
- int i, number, start = 0;
- if (!chan) {
- ast_log(LOG_WARNING, "No channel was provided to %s function.\n", function);
- return -1;
- }
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "This function requires a header name.\n");
- return -1;
- }
- ast_channel_lock(chan);
- if (!IS_SIP_TECH(ast_channel_tech(chan))) {
- ast_log(LOG_WARNING, "This function can only be used on SIP channels.\n");
- ast_channel_unlock(chan);
- return -1;
- }
- AST_STANDARD_APP_ARGS(args, data);
- if (!args.number) {
- number = 1;
- } else {
- sscanf(args.number, "%30d", &number);
- if (number < 1)
- number = 1;
- }
- p = ast_channel_tech_pvt(chan);
- /* If there is no private structure, this channel is no longer alive */
- if (!p) {
- ast_channel_unlock(chan);
- return -1;
- }
- for (i = 0; i < number; i++)
- content = __get_header(&p->initreq, args.header, &start);
- if (ast_strlen_zero(content)) {
- ast_channel_unlock(chan);
- return -1;
- }
- ast_copy_string(buf, content, len);
- ast_channel_unlock(chan);
- return 0;
- }
- static struct ast_custom_function sip_header_function = {
- .name = "SIP_HEADER",
- .read = func_header_read,
- };
- /*! \brief Dial plan function to check if domain is local */
- static int func_check_sipdomain(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
- {
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "CHECKSIPDOMAIN requires an argument - A domain name\n");
- return -1;
- }
- if (check_sip_domain(data, NULL, 0))
- ast_copy_string(buf, data, len);
- else
- buf[0] = '\0';
- return 0;
- }
- static struct ast_custom_function checksipdomain_function = {
- .name = "CHECKSIPDOMAIN",
- .read = func_check_sipdomain,
- };
- /*! \brief ${SIPPEER()} Dialplan function - reads peer data */
- static int function_sippeer(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
- {
- struct sip_peer *peer;
- char *colname;
- if ((colname = strchr(data, ','))) {
- *colname++ = '\0';
- } else {
- colname = "ip";
- }
- if (!(peer = sip_find_peer(data, NULL, TRUE, FINDPEERS, FALSE, 0)))
- return -1;
- if (!strcasecmp(colname, "ip")) {
- ast_copy_string(buf, ast_sockaddr_stringify_addr(&peer->addr), len);
- } else if (!strcasecmp(colname, "port")) {
- snprintf(buf, len, "%d", ast_sockaddr_port(&peer->addr));
- } else if (!strcasecmp(colname, "status")) {
- peer_status(peer, buf, len);
- } else if (!strcasecmp(colname, "language")) {
- ast_copy_string(buf, peer->language, len);
- } else if (!strcasecmp(colname, "regexten")) {
- ast_copy_string(buf, peer->regexten, len);
- } else if (!strcasecmp(colname, "limit")) {
- snprintf(buf, len, "%d", peer->call_limit);
- } else if (!strcasecmp(colname, "busylevel")) {
- snprintf(buf, len, "%d", peer->busy_level);
- } else if (!strcasecmp(colname, "curcalls")) {
- snprintf(buf, len, "%d", peer->inuse);
- } else if (!strcasecmp(colname, "maxforwards")) {
- snprintf(buf, len, "%d", peer->maxforwards);
- } else if (!strcasecmp(colname, "accountcode")) {
- ast_copy_string(buf, peer->accountcode, len);
- } else if (!strcasecmp(colname, "callgroup")) {
- ast_print_group(buf, len, peer->callgroup);
- } else if (!strcasecmp(colname, "pickupgroup")) {
- ast_print_group(buf, len, peer->pickupgroup);
- } else if (!strcasecmp(colname, "namedcallgroup")) {
- struct ast_str *tmp_str = ast_str_create(1024);
- if (tmp_str) {
- ast_copy_string(buf, ast_print_namedgroups(&tmp_str, peer->named_callgroups), len);
- ast_free(tmp_str);
- }
- } else if (!strcasecmp(colname, "namedpickupgroup")) {
- struct ast_str *tmp_str = ast_str_create(1024);
- if (tmp_str) {
- ast_copy_string(buf, ast_print_namedgroups(&tmp_str, peer->named_pickupgroups), len);
- ast_free(tmp_str);
- }
- } else if (!strcasecmp(colname, "useragent")) {
- ast_copy_string(buf, peer->useragent, len);
- } else if (!strcasecmp(colname, "mailbox")) {
- struct ast_str *mailbox_str = ast_str_alloca(512);
- peer_mailboxes_to_str(&mailbox_str, peer);
- ast_copy_string(buf, ast_str_buffer(mailbox_str), 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, peer->host_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")) {
- struct ast_str *codec_buf = ast_str_alloca(64);
- ast_format_cap_get_names(peer->caps, &codec_buf);
- ast_copy_string(buf, ast_str_buffer(codec_buf), len);
- } else if (!strcasecmp(colname, "encryption")) {
- snprintf(buf, len, "%u", ast_test_flag(&peer->flags[1], SIP_PAGE2_USE_SRTP));
- } else if (!strncasecmp(colname, "chanvar[", 8)) {
- char *chanvar=colname + 8;
- struct ast_variable *v;
- chanvar = strsep(&chanvar, "]");
- for (v = peer->chanvars ; v ; v = v->next) {
- if (!strcasecmp(v->name, chanvar)) {
- ast_copy_string(buf, v->value, len);
- }
- }
- } else if (!strncasecmp(colname, "codec[", 6)) {
- char *codecnum;
- struct ast_format *codec;
- codecnum = colname + 6; /* move past the '[' */
- codecnum = strsep(&codecnum, "]"); /* trim trailing ']' if any */
- codec = ast_format_cap_get_format(peer->caps, atoi(codecnum));
- if (codec) {
- ast_copy_string(buf, ast_format_get_name(codec), len);
- ao2_ref(codec, -1);
- } else {
- buf[0] = '\0';
- }
- } else {
- buf[0] = '\0';
- }
- sip_unref_peer(peer, "sip_unref_peer from function_sippeer, just before return");
- return 0;
- }
- /*! \brief Structure to declare a dialplan function: SIPPEER */
- static struct ast_custom_function sippeer_function = {
- .name = "SIPPEER",
- .read = function_sippeer,
- };
- /*! \brief update redirecting information for a channel based on headers
- *
- */
- static void change_redirecting_information(struct sip_pvt *p, struct sip_request *req,
- struct ast_party_redirecting *redirecting,
- struct ast_set_party_redirecting *update_redirecting, int set_call_forward)
- {
- char *redirecting_from_name = NULL;
- char *redirecting_from_number = NULL;
- char *redirecting_to_name = NULL;
- char *redirecting_to_number = NULL;
- char *reason_str = NULL;
- int reason = AST_REDIRECTING_REASON_UNCONDITIONAL;
- int is_response = req->method == SIP_RESPONSE;
- int res = 0;
- res = get_rdnis(p, req, &redirecting_from_name, &redirecting_from_number, &reason, &reason_str);
- if (res == -1) {
- if (is_response) {
- get_name_and_number(sip_get_header(req, "TO"), &redirecting_from_name, &redirecting_from_number);
- } else {
- return;
- }
- }
- /* At this point, all redirecting "from" info should be filled in appropriately
- * on to the "to" info
- */
- if (is_response) {
- parse_moved_contact(p, req, &redirecting_to_name, &redirecting_to_number, set_call_forward);
- } else {
- get_name_and_number(sip_get_header(req, "TO"), &redirecting_to_name, &redirecting_to_number);
- }
- if (!ast_strlen_zero(redirecting_from_number)) {
- ast_debug(3, "Got redirecting from number %s\n", redirecting_from_number);
- update_redirecting->from.number = 1;
- redirecting->from.number.valid = 1;
- ast_free(redirecting->from.number.str);
- redirecting->from.number.str = redirecting_from_number;
- }
- if (!ast_strlen_zero(redirecting_from_name)) {
- ast_debug(3, "Got redirecting from name %s\n", redirecting_from_name);
- update_redirecting->from.name = 1;
- redirecting->from.name.valid = 1;
- ast_free(redirecting->from.name.str);
- redirecting->from.name.str = redirecting_from_name;
- }
- if (!ast_strlen_zero(p->cid_tag)) {
- ast_free(redirecting->from.tag);
- redirecting->from.tag = ast_strdup(p->cid_tag);
- ast_free(redirecting->to.tag);
- redirecting->to.tag = ast_strdup(p->cid_tag);
- }
- if (!ast_strlen_zero(redirecting_to_number)) {
- ast_debug(3, "Got redirecting to number %s\n", redirecting_to_number);
- update_redirecting->to.number = 1;
- redirecting->to.number.valid = 1;
- ast_free(redirecting->to.number.str);
- redirecting->to.number.str = redirecting_to_number;
- }
- if (!ast_strlen_zero(redirecting_to_name)) {
- ast_debug(3, "Got redirecting to name %s\n", redirecting_from_number);
- update_redirecting->to.name = 1;
- redirecting->to.name.valid = 1;
- ast_free(redirecting->to.name.str);
- redirecting->to.name.str = redirecting_to_name;
- }
- redirecting->reason.code = reason;
- if (reason_str) {
- ast_debug(3, "Got redirecting reason %s\n", reason_str);
- ast_free(redirecting->reason.str);
- redirecting->reason.str = reason_str;
- }
- }
- /*! \brief Parse 302 Moved temporalily response
- \todo XXX Doesn't redirect over TLS on sips: uri's.
- If we get a redirect to a SIPS: uri, this needs to be going back to the
- dialplan (this is a request for a secure signalling path).
- Note that transport=tls is deprecated, but we need to support it on incoming requests.
- */
- static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req, char **name, char **number, int set_call_forward)
- {
- char contact[SIPBUFSIZE];
- char *contact_name = NULL;
- char *contact_number = NULL;
- char *separator, *trans;
- char *domain;
- enum ast_transport transport = AST_TRANSPORT_UDP;
- ast_copy_string(contact, sip_get_header(req, "Contact"), sizeof(contact));
- if ((separator = strchr(contact, ',')))
- *separator = '\0';
- contact_number = get_in_brackets(contact);
- if ((trans = strcasestr(contact_number, ";transport="))) {
- trans += 11;
- if ((separator = strchr(trans, ';')))
- *separator = '\0';
- if (!strncasecmp(trans, "tcp", 3))
- transport = AST_TRANSPORT_TCP;
- else if (!strncasecmp(trans, "tls", 3))
- transport = AST_TRANSPORT_TLS;
- else {
- if (strncasecmp(trans, "udp", 3))
- ast_debug(1, "received contact with an invalid transport, '%s'\n", contact_number);
- /* This will assume UDP for all unknown transports */
- transport = AST_TRANSPORT_UDP;
- }
- }
- contact_number = remove_uri_parameters(contact_number);
- if (p->socket.tcptls_session) {
- ao2_ref(p->socket.tcptls_session, -1);
- p->socket.tcptls_session = NULL;
- } else if (p->socket.ws_session) {
- ast_websocket_unref(p->socket.ws_session);
- p->socket.ws_session = NULL;
- }
- set_socket_transport(&p->socket, transport);
- if (set_call_forward && ast_test_flag(&p->flags[0], SIP_PROMISCREDIR)) {
- char *host = NULL;
- if (!strncasecmp(contact_number, "sip:", 4))
- contact_number += 4;
- else if (!strncasecmp(contact_number, "sips:", 5))
- contact_number += 5;
- separator = strchr(contact_number, '/');
- if (separator)
- *separator = '\0';
- if ((host = strchr(contact_number, '@'))) {
- *host++ = '\0';
- ast_debug(2, "Found promiscuous redirection to 'SIP/%s::::%s@%s'\n", contact_number, sip_get_transport(transport), host);
- if (p->owner)
- ast_channel_call_forward_build(p->owner, "SIP/%s::::%s@%s", contact_number, sip_get_transport(transport), host);
- } else {
- ast_debug(2, "Found promiscuous redirection to 'SIP/::::%s@%s'\n", sip_get_transport(transport), contact_number);
- if (p->owner)
- ast_channel_call_forward_build(p->owner, "SIP/::::%s@%s", sip_get_transport(transport), contact_number);
- }
- } else {
- separator = strchr(contact, '@');
- if (separator) {
- *separator++ = '\0';
- domain = separator;
- } else {
- /* No username part */
- domain = contact;
- }
- separator = strchr(contact, '/'); /* WHEN do we hae a forward slash in the URI? */
- if (separator)
- *separator = '\0';
- if (!strncasecmp(contact_number, "sip:", 4))
- contact_number += 4;
- else if (!strncasecmp(contact_number, "sips:", 5))
- contact_number += 5;
- separator = strchr(contact_number, ';'); /* And username ; parameters? */
- if (separator)
- *separator = '\0';
- ast_uri_decode(contact_number, ast_uri_sip_user);
- if (set_call_forward) {
- ast_debug(2, "Received 302 Redirect to extension '%s' (domain %s)\n", contact_number, domain);
- if (p->owner) {
- pbx_builtin_setvar_helper(p->owner, "SIPDOMAIN", domain);
- ast_channel_call_forward_set(p->owner, contact_number);
- }
- }
- }
- /* We've gotten the number for the contact, now get the name */
- if (*contact == '\"') {
- contact_name = contact + 1;
- if (!(separator = (char *)find_closing_quote(contact_name, NULL))) {
- ast_log(LOG_NOTICE, "No closing quote on name in Contact header? %s\n", contact);
- }
- *separator = '\0';
- }
- if (name && !ast_strlen_zero(contact_name)) {
- *name = ast_strdup(contact_name);
- }
- if (number) {
- *number = ast_strdup(contact_number);
- }
- }
- /*! \brief Check pending actions on SIP call
- *
- * \note both sip_pvt and sip_pvt's owner channel (if present)
- * must be locked for this function.
- */
- static void check_pendings(struct sip_pvt *p)
- {
- if (ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
- if (p->reinviteid > -1) {
- /* Outstanding p->reinviteid timeout, so wait... */
- return;
- } else if (p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA) {
- /* if we can't BYE, then this is really a pending CANCEL */
- p->invitestate = INV_CANCELLED;
- transmit_request(p, SIP_CANCEL, p->lastinvite, XMIT_RELIABLE, FALSE);
- /* If the cancel occurred on an initial invite, cancel the pending BYE */
- if (!ast_test_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) {
- ast_clear_flag(&p->flags[0], SIP_PENDINGBYE);
- }
- /* Actually don't destroy us yet, wait for the 487 on our original
- INVITE, but do set an autodestruct just in case we never get it. */
- } else {
- /* We have a pending outbound invite, don't send something
- * new in-transaction, unless it is a pending reinvite, then
- * by the time we are called here, we should probably just hang up. */
- if (p->pendinginvite && !p->ongoing_reinvite)
- return;
- if (p->owner) {
- ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
- }
- /* Perhaps there is an SD change INVITE outstanding */
- transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, TRUE);
- ast_clear_flag(&p->flags[0], SIP_PENDINGBYE);
- }
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- } else if (ast_test_flag(&p->flags[0], SIP_NEEDREINVITE)) {
- /* if we can't REINVITE, hold it for later */
- if (p->pendinginvite || p->invitestate == INV_CALLING || p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA || p->waitid > 0) {
- ast_debug(2, "NOT Sending pending reinvite (yet) on '%s'\n", p->callid);
- } else {
- ast_debug(2, "Sending pending reinvite on '%s'\n", p->callid);
- /* Didn't get to reinvite yet, so do it now */
- transmit_reinvite_with_sdp(p, (p->t38.state == T38_LOCAL_REINVITE ? TRUE : FALSE), FALSE);
- ast_clear_flag(&p->flags[0], SIP_NEEDREINVITE);
- }
- }
- }
- /*! \brief Reset the NEEDREINVITE flag after waiting when we get 491 on a Re-invite
- to avoid race conditions between asterisk servers.
- Called from the scheduler.
- */
- static int sip_reinvite_retry(const void *data)
- {
- struct sip_pvt *p = (struct sip_pvt *) data;
- struct ast_channel *owner;
- sip_pvt_lock(p); /* called from schedule thread which requires a lock */
- while ((owner = p->owner) && ast_channel_trylock(owner)) {
- sip_pvt_unlock(p);
- usleep(1);
- sip_pvt_lock(p);
- }
- ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
- p->waitid = -1;
- check_pendings(p);
- sip_pvt_unlock(p);
- if (owner) {
- ast_channel_unlock(owner);
- }
- dialog_unref(p, "unref the dialog ptr from sip_reinvite_retry, because it held a dialog ptr");
- return 0;
- }
- /*!
- * \brief Handle authentication challenge for SIP UPDATE
- *
- * This function is only called upon the receipt of a 401/407 response to an UPDATE.
- */
- static void handle_response_update(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
- {
- if (p->options) {
- p->options->auth_type = (resp == 401 ? WWW_AUTH : PROXY_AUTH);
- }
- if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, resp, SIP_UPDATE, 1)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on UPDATE to '%s'\n", sip_get_header(&p->initreq, "From"));
- }
- }
- static void cc_handle_publish_error(struct sip_pvt *pvt, const int resp, struct sip_request *req, struct sip_epa_entry *epa_entry)
- {
- struct cc_epa_entry *cc_entry = epa_entry->instance_data;
- struct sip_monitor_instance *monitor_instance = ao2_callback(sip_monitor_instances, 0,
- find_sip_monitor_instance_by_suspension_entry, epa_entry);
- const char *min_expires;
- if (!monitor_instance) {
- ast_log(LOG_WARNING, "Can't find monitor_instance corresponding to epa_entry %p.\n", epa_entry);
- return;
- }
- if (resp != 423) {
- ast_cc_monitor_failed(cc_entry->core_id, monitor_instance->device_name,
- "Received error response to our PUBLISH");
- ao2_ref(monitor_instance, -1);
- return;
- }
- /* Allrighty, the other end doesn't like our Expires value. They think it's
- * too small, so let's see if they've provided a more sensible value. If they
- * haven't, then we'll just double our Expires value and see if they like that
- * instead.
- *
- * XXX Ideally this logic could be placed into its own function so that SUBSCRIBE,
- * PUBLISH, and REGISTER could all benefit from the same shared code.
- */
- min_expires = sip_get_header(req, "Min-Expires");
- if (ast_strlen_zero(min_expires)) {
- pvt->expiry *= 2;
- if (pvt->expiry < 0) {
- /* You dork! You overflowed! */
- ast_cc_monitor_failed(cc_entry->core_id, monitor_instance->device_name,
- "PUBLISH expiry overflowed");
- ao2_ref(monitor_instance, -1);
- return;
- }
- } else if (sscanf(min_expires, "%30d", &pvt->expiry) != 1) {
- ast_cc_monitor_failed(cc_entry->core_id, monitor_instance->device_name,
- "Min-Expires has non-numeric value");
- ao2_ref(monitor_instance, -1);
- return;
- }
- /* At this point, we have most certainly changed pvt->expiry, so try transmitting
- * again
- */
- transmit_invite(pvt, SIP_PUBLISH, FALSE, 0, NULL);
- ao2_ref(monitor_instance, -1);
- }
- static void handle_response_publish(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
- {
- struct sip_epa_entry *epa_entry = p->epa_entry;
- const char *etag = sip_get_header(req, "Sip-ETag");
- ast_assert(epa_entry != NULL);
- if (resp == 401 || resp == 407) {
- ast_string_field_set(p, theirtag, NULL);
- if (p->options) {
- p->options->auth_type = (resp == 401 ? WWW_AUTH : PROXY_AUTH);
- }
- if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, resp, SIP_PUBLISH, 0)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on PUBLISH to '%s'\n", sip_get_header(&p->initreq, "From"));
- pvt_set_needdestroy(p, "Failed to authenticate on PUBLISH");
- sip_alreadygone(p);
- }
- return;
- }
- if (resp == 501 || resp == 405) {
- mark_method_unallowed(&p->allowed_methods, SIP_PUBLISH);
- }
- if (resp == 200) {
- p->authtries = 0;
- /* If I've read section 6, item 6 of RFC 3903 correctly,
- * an ESC will only generate a new etag when it sends a 200 OK
- */
- if (!ast_strlen_zero(etag)) {
- ast_copy_string(epa_entry->entity_tag, etag, sizeof(epa_entry->entity_tag));
- }
- /* The nominal case. Everything went well. Everybody is happy.
- * Each EPA will have a specific action to take as a result of this
- * development, so ... callbacks!
- */
- if (epa_entry->static_data->handle_ok) {
- epa_entry->static_data->handle_ok(p, req, epa_entry);
- }
- } else {
- /* Rather than try to make individual callbacks for each error
- * type, there is just a single error callback. The callback
- * can distinguish between error messages and do what it needs to
- */
- if (epa_entry->static_data->handle_error) {
- epa_entry->static_data->handle_error(p, resp, req, epa_entry);
- }
- }
- }
- /*!
- * \internal
- * \brief Set hangup source and cause.
- *
- * \param p SIP private.
- * \param cause Hangup cause to queue. Zero if no cause.
- *
- * \pre p and p->owner are locked.
- *
- * \return Nothing
- */
- static void sip_queue_hangup_cause(struct sip_pvt *p, int cause)
- {
- struct ast_channel *owner = p->owner;
- const char *name = ast_strdupa(ast_channel_name(owner));
- /* Cannot hold any channel/private locks when calling. */
- ast_channel_ref(owner);
- ast_channel_unlock(owner);
- sip_pvt_unlock(p);
- ast_set_hangupsource(owner, name, 0);
- if (cause) {
- ast_queue_hangup_with_cause(owner, cause);
- } else {
- ast_queue_hangup(owner);
- }
- ast_channel_unref(owner);
- /* Relock things. */
- owner = sip_pvt_lock_full(p);
- if (owner) {
- ast_channel_unref(owner);
- }
- }
- /*! \brief Handle SIP response to INVITE dialogue */
- static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
- {
- int outgoing = ast_test_flag(&p->flags[0], SIP_OUTGOING);
- int res = 0;
- int xmitres = 0;
- int reinvite = ast_test_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- char *p_hdrval;
- int rtn;
- struct ast_party_connected_line connected;
- struct ast_set_party_connected_line update_connected;
- if (reinvite) {
- ast_debug(4, "SIP response %d to RE-invite on %s call %s\n", resp, outgoing ? "outgoing" : "incoming", p->callid);
- } else {
- ast_debug(4, "SIP response %d to standard invite\n", resp);
- }
- if (p->alreadygone) { /* This call is already gone */
- ast_debug(1, "Got response on call that is already terminated: %s (ignoring)\n", p->callid);
- return;
- }
- /* Acknowledge sequence number - This only happens on INVITE from SIP-call */
- /* Don't auto congest anymore since we've gotten something useful back */
- AST_SCHED_DEL_UNREF(sched, p->initid, dialog_unref(p, "when you delete the initid sched, you should dec the refcount for the stored dialog ptr"));
- /* RFC3261 says we must treat every 1xx response (but not 100)
- that we don't recognize as if it was 183.
- */
- if (resp > 100 && resp < 200 && resp!=101 && resp != 180 && resp != 181 && resp != 182 && resp != 183) {
- resp = 183;
- }
- /* For INVITE, treat all 2XX responses as we would a 200 response */
- if ((resp >= 200) && (resp < 300)) {
- resp = 200;
- }
- /* Any response between 100 and 199 is PROCEEDING */
- if (resp >= 100 && resp < 200 && p->invitestate == INV_CALLING) {
- p->invitestate = INV_PROCEEDING;
- }
- /* Final response, not 200 ? */
- if (resp >= 300 && (p->invitestate == INV_CALLING || p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA )) {
- p->invitestate = INV_COMPLETED;
- }
- if ((resp >= 200 && reinvite)) {
- p->ongoing_reinvite = 0;
- if (p->reinviteid > -1) {
- AST_SCHED_DEL_UNREF(sched, p->reinviteid, dialog_unref(p, "unref dialog for reinvite timeout because of a final response"));
- }
- }
- /* Final response, clear out pending invite */
- if ((resp == 200 || resp >= 300) && p->pendinginvite && seqno == p->pendinginvite) {
- p->pendinginvite = 0;
- }
- /* If this is a response to our initial INVITE, we need to set what we can use
- * for this peer.
- */
- if (!reinvite) {
- set_pvt_allowed_methods(p, req);
- }
- switch (resp) {
- case 100: /* Trying */
- case 101: /* Dialog establishment */
- if (!req->ignore && p->invitestate != INV_CANCELLED && sip_cancel_destroy(p)) {
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- }
- check_pendings(p);
- break;
- case 180: /* 180 Ringing */
- case 182: /* 182 Queued */
- if (!req->ignore && p->invitestate != INV_CANCELLED && sip_cancel_destroy(p)) {
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- }
- /* Store Route-set from provisional SIP responses so
- * early-dialog request can be routed properly
- * */
- parse_ok_contact(p, req);
- if (!reinvite) {
- build_route(p, req, 1, resp);
- }
- if (!req->ignore && p->owner) {
- if (get_rpid(p, req)) {
- /* Queue a connected line update */
- ast_party_connected_line_init(&connected);
- memset(&update_connected, 0, sizeof(update_connected));
- update_connected.id.number = 1;
- connected.id.number.valid = 1;
- connected.id.number.str = (char *) p->cid_num;
- connected.id.number.presentation = p->callingpres;
- update_connected.id.name = 1;
- connected.id.name.valid = 1;
- connected.id.name.str = (char *) p->cid_name;
- connected.id.name.presentation = p->callingpres;
- /* Invalidate any earlier private connected id representation */
- ast_set_party_id_all(&update_connected.priv);
- connected.id.tag = (char *) p->cid_tag;
- connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
- ast_channel_queue_connected_line_update(p->owner, &connected,
- &update_connected);
- }
- sip_handle_cc(p, req, AST_CC_CCNR);
- ast_queue_control(p->owner, AST_CONTROL_RINGING);
- if (ast_channel_state(p->owner) != AST_STATE_UP) {
- ast_setstate(p->owner, AST_STATE_RINGING);
- }
- }
- if (find_sdp(req)) {
- if (p->invitestate != INV_CANCELLED) {
- p->invitestate = INV_EARLY_MEDIA;
- }
- res = process_sdp(p, req, SDP_T38_NONE);
- if (!req->ignore && p->owner) {
- /* Queue a progress frame only if we have SDP in 180 or 182 */
- ast_queue_control(p->owner, AST_CONTROL_PROGRESS);
- /* We have not sent progress, but we have been sent progress so enable early media */
- ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
- }
- ast_rtp_instance_activate(p->rtp);
- }
- check_pendings(p);
- break;
- case 181: /* Call Is Being Forwarded */
- if (!req->ignore && (p->invitestate != INV_CANCELLED) && sip_cancel_destroy(p))
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- /* Store Route-set from provisional SIP responses so
- * early-dialog request can be routed properly
- * */
- parse_ok_contact(p, req);
- if (!reinvite) {
- build_route(p, req, 1, resp);
- }
- if (!req->ignore && p->owner) {
- struct ast_party_redirecting redirecting;
- struct ast_set_party_redirecting update_redirecting;
- ast_party_redirecting_init(&redirecting);
- memset(&update_redirecting, 0, sizeof(update_redirecting));
- change_redirecting_information(p, req, &redirecting, &update_redirecting,
- FALSE);
- /* Invalidate any earlier private redirecting id representations */
- ast_set_party_id_all(&update_redirecting.priv_orig);
- ast_set_party_id_all(&update_redirecting.priv_from);
- ast_set_party_id_all(&update_redirecting.priv_to);
- ast_channel_queue_redirecting_update(p->owner, &redirecting,
- &update_redirecting);
- ast_party_redirecting_free(&redirecting);
- sip_handle_cc(p, req, AST_CC_CCNR);
- }
- check_pendings(p);
- break;
- case 183: /* Session progress */
- if (!req->ignore && (p->invitestate != INV_CANCELLED) && sip_cancel_destroy(p)) {
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- }
- /* Store Route-set from provisional SIP responses so
- * early-dialog request can be routed properly
- * */
- parse_ok_contact(p, req);
- if (!reinvite) {
- build_route(p, req, 1, resp);
- }
- if (!req->ignore && p->owner) {
- if (get_rpid(p, req)) {
- /* Queue a connected line update */
- ast_party_connected_line_init(&connected);
- memset(&update_connected, 0, sizeof(update_connected));
- update_connected.id.number = 1;
- connected.id.number.valid = 1;
- connected.id.number.str = (char *) p->cid_num;
- connected.id.number.presentation = p->callingpres;
- update_connected.id.name = 1;
- connected.id.name.valid = 1;
- connected.id.name.str = (char *) p->cid_name;
- connected.id.name.presentation = p->callingpres;
- /* Invalidate any earlier private connected id representation */
- ast_set_party_id_all(&update_connected.priv);
- connected.id.tag = (char *) p->cid_tag;
- connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
- ast_channel_queue_connected_line_update(p->owner, &connected,
- &update_connected);
- }
- sip_handle_cc(p, req, AST_CC_CCNR);
- }
- if (find_sdp(req)) {
- if (p->invitestate != INV_CANCELLED) {
- p->invitestate = INV_EARLY_MEDIA;
- }
- res = process_sdp(p, req, SDP_T38_NONE);
- if (!req->ignore && p->owner) {
- /* Queue a progress frame */
- ast_queue_control(p->owner, AST_CONTROL_PROGRESS);
- /* We have not sent progress, but we have been sent progress so enable early media */
- ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
- }
- ast_rtp_instance_activate(p->rtp);
- } else {
- /* Alcatel PBXs are known to send 183s with no SDP after sending
- * a 100 Trying response. We're just going to treat this sort of thing
- * the same as we would treat a 180 Ringing
- */
- if (!req->ignore && p->owner) {
- ast_queue_control(p->owner, AST_CONTROL_RINGING);
- }
- }
- check_pendings(p);
- break;
- case 200: /* 200 OK on invite - someone's answering our call */
- if (!req->ignore && (p->invitestate != INV_CANCELLED) && sip_cancel_destroy(p)) {
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- }
- p->authtries = 0;
- if (find_sdp(req)) {
- if ((res = process_sdp(p, req, SDP_T38_ACCEPT)) && !req->ignore) {
- if (!reinvite) {
- /* This 200 OK's SDP is not acceptable, so we need to ack, then hangup */
- /* For re-invites, we try to recover */
- ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
- p->hangupcause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
- if (p->owner) {
- ast_channel_hangupcause_set(p->owner, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
- sip_queue_hangup_cause(p, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
- }
- }
- }
- ast_rtp_instance_activate(p->rtp);
- } else if (!reinvite) {
- struct ast_sockaddr remote_address = {{0,}};
- ast_rtp_instance_get_requested_target_address(p->rtp, &remote_address);
- if (ast_sockaddr_isnull(&remote_address) || (!ast_strlen_zero(p->theirprovtag) && strcmp(p->theirtag, p->theirprovtag))) {
- ast_log(LOG_WARNING, "Received response: \"200 OK\" from '%s' without SDP\n", p->relatedpeer->name);
- ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
- ast_rtp_instance_activate(p->rtp);
- }
- }
- if (!req->ignore && p->owner) {
- int rpid_changed;
- rpid_changed = get_rpid(p, req);
- if (rpid_changed || !reinvite) {
- /* Queue a connected line update */
- ast_party_connected_line_init(&connected);
- memset(&update_connected, 0, sizeof(update_connected));
- if (rpid_changed
- || !ast_strlen_zero(p->cid_num)
- || (p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) {
- update_connected.id.number = 1;
- connected.id.number.valid = 1;
- connected.id.number.str = (char *) p->cid_num;
- connected.id.number.presentation = p->callingpres;
- }
- if (rpid_changed
- || !ast_strlen_zero(p->cid_name)
- || (p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) {
- update_connected.id.name = 1;
- connected.id.name.valid = 1;
- connected.id.name.str = (char *) p->cid_name;
- connected.id.name.presentation = p->callingpres;
- }
- if (update_connected.id.number || update_connected.id.name) {
- /* Invalidate any earlier private connected id representation */
- ast_set_party_id_all(&update_connected.priv);
- connected.id.tag = (char *) p->cid_tag;
- connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
- ast_channel_queue_connected_line_update(p->owner, &connected,
- &update_connected);
- }
- }
- }
- /* Parse contact header for continued conversation */
- /* When we get 200 OK, we know which device (and IP) to contact for this call */
- /* This is important when we have a SIP proxy between us and the phone */
- if (outgoing) {
- update_call_counter(p, DEC_CALL_RINGING);
- parse_ok_contact(p, req);
- /* Save Record-Route for any later requests we make on this dialogue */
- if (!reinvite) {
- build_route(p, req, 1, resp);
- }
- if(set_address_from_contact(p)) {
- /* Bad contact - we don't know how to reach this device */
- /* We need to ACK, but then send a bye */
- if (sip_route_empty(&p->route) && !req->ignore) {
- ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
- }
- }
- }
- if (!req->ignore && p->owner) {
- if (!reinvite && !res) {
- ast_queue_control(p->owner, AST_CONTROL_ANSWER);
- } else { /* RE-invite */
- if (p->t38.state == T38_DISABLED || p->t38.state == T38_REJECTED) {
- ast_queue_control(p->owner, AST_CONTROL_UPDATE_RTP_PEER);
- } else {
- ast_queue_frame(p->owner, &ast_null_frame);
- }
- }
- } else {
- /* It's possible we're getting an 200 OK after we've tried to disconnect
- by sending CANCEL */
- /* First send ACK, then send bye */
- if (!req->ignore) {
- ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
- }
- }
- /* Check for Session-Timers related headers */
- if (st_get_mode(p, 0) != SESSION_TIMER_MODE_REFUSE) {
- p_hdrval = (char*)sip_get_header(req, "Session-Expires");
- if (!ast_strlen_zero(p_hdrval)) {
- /* UAS supports Session-Timers */
- enum st_refresher_param st_ref_param;
- int tmp_st_interval = 0;
- rtn = parse_session_expires(p_hdrval, &tmp_st_interval, &st_ref_param);
- if (rtn != 0) {
- ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
- } else if (tmp_st_interval < st_get_se(p, FALSE)) {
- ast_log(LOG_WARNING, "Got Session-Expires less than local Min-SE in 200 OK, tearing down call\n");
- ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
- }
- if (st_ref_param == SESSION_TIMER_REFRESHER_PARAM_UAC) {
- p->stimer->st_ref = SESSION_TIMER_REFRESHER_US;
- } else if (st_ref_param == SESSION_TIMER_REFRESHER_PARAM_UAS) {
- p->stimer->st_ref = SESSION_TIMER_REFRESHER_THEM;
- } else {
- ast_log(LOG_WARNING, "Unknown refresher on %s\n", p->callid);
- }
- if (tmp_st_interval) {
- p->stimer->st_interval = tmp_st_interval;
- }
- p->stimer->st_active = TRUE;
- p->stimer->st_active_peer_ua = TRUE;
- start_session_timer(p);
- } else {
- /* UAS doesn't support Session-Timers */
- if (st_get_mode(p, 0) == SESSION_TIMER_MODE_ORIGINATE) {
- p->stimer->st_ref = SESSION_TIMER_REFRESHER_US;
- p->stimer->st_active_peer_ua = FALSE;
- start_session_timer(p);
- }
- }
- }
- /* If I understand this right, the branch is different for a non-200 ACK only */
- p->invitestate = INV_TERMINATED;
- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, TRUE);
- check_pendings(p);
- break;
- case 407: /* Proxy authentication */
- case 401: /* Www auth */
- /* First we ACK */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- if (p->options) {
- p->options->auth_type = resp;
- }
- /* Then we AUTH */
- ast_string_field_set(p, theirtag, NULL); /* forget their old tag, so we don't match tags when getting response */
- if (!req->ignore) {
- if (p->authtries < MAX_AUTHTRIES) {
- p->invitestate = INV_CALLING;
- }
- if (p->authtries == MAX_AUTHTRIES || do_proxy_auth(p, req, resp, SIP_INVITE, 1)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on INVITE to '%s'\n", sip_get_header(&p->initreq, "From"));
- pvt_set_needdestroy(p, "failed to authenticate on INVITE");
- sip_alreadygone(p);
- if (p->owner) {
- ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
- }
- }
- }
- break;
- case 403: /* Forbidden */
- /* First we ACK */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- ast_log(LOG_WARNING, "Received response: \"Forbidden\" from '%s'\n", sip_get_header(&p->initreq, "From"));
- if (!req->ignore && p->owner) {
- sip_queue_hangup_cause(p, hangup_sip2cause(resp));
- }
- break;
- case 414: /* Bad request URI */
- case 493: /* Undecipherable */
- case 404: /* Not found */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- if (p->owner && !req->ignore) {
- sip_queue_hangup_cause(p, hangup_sip2cause(resp));
- }
- break;
- case 481: /* Call leg does not exist */
- /* Could be REFER caused INVITE with replaces */
- ast_log(LOG_WARNING, "Re-invite to non-existing call leg on other UA. SIP dialog '%s'. Giving up.\n", p->callid);
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- if (p->owner) {
- ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(resp));
- }
- break;
- case 422: /* Session-Timers: Session interval too small */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- ast_string_field_set(p, theirtag, NULL);
- proc_422_rsp(p, req);
- break;
- case 428: /* Use identity header - rfc 4474 - not supported by Asterisk yet */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- append_history(p, "Identity", "SIP identity is required. Not supported by Asterisk.");
- ast_log(LOG_WARNING, "SIP identity required by proxy. SIP dialog '%s'. Giving up.\n", p->callid);
- if (p->owner && !req->ignore) {
- ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(resp));
- }
- break;
- case 480: /* Temporarily unavailable. */
- /* RFC 3261 encourages setting the reason phrase to something indicative
- * of why the endpoint is not available. We will make this readable via the
- * redirecting reason.
- */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- append_history(p, "TempUnavailable", "Endpoint is temporarily unavailable.");
- if (p->owner && !req->ignore) {
- struct ast_party_redirecting redirecting;
- struct ast_set_party_redirecting update_redirecting;
- ast_party_redirecting_set_init(&redirecting, ast_channel_redirecting(p->owner));
- memset(&update_redirecting, 0, sizeof(update_redirecting));
- redirecting.reason.code = sip_reason_str_to_code(rest);
- redirecting.reason.str = ast_strdup(rest);
- ast_channel_queue_redirecting_update(p->owner, &redirecting, &update_redirecting);
- ast_party_redirecting_free(&redirecting);
- ast_queue_control(p->owner, AST_CONTROL_BUSY);
- }
- break;
- case 487: /* Cancelled transaction */
- /* We have sent CANCEL on an outbound INVITE
- This transaction is already scheduled to be killed by sip_hangup().
- */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- if (p->owner && !req->ignore) {
- ast_queue_hangup_with_cause(p->owner, AST_CAUSE_NORMAL_CLEARING);
- append_history(p, "Hangup", "Got 487 on CANCEL request from us. Queued AST hangup request");
- } else if (!req->ignore) {
- update_call_counter(p, DEC_CALL_LIMIT);
- append_history(p, "Hangup", "Got 487 on CANCEL request from us on call without owner. Killing this dialog.");
- }
- check_pendings(p);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- break;
- case 415: /* Unsupported media type */
- case 488: /* Not acceptable here */
- case 606: /* Not Acceptable */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) {
- change_t38_state(p, T38_REJECTED);
- /* Try to reset RTP timers */
- /* XXX Why is this commented away??? */
- //ast_rtp_set_rtptimers_onhold(p->rtp);
- /* Trigger a reinvite back to audio */
- transmit_reinvite_with_sdp(p, FALSE, FALSE);
- } else {
- /* We can't set up this call, so give up */
- if (p->owner && !req->ignore) {
- ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(resp));
- }
- }
- break;
- case 491: /* Pending */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- if (p->owner && !req->ignore) {
- if (ast_channel_state(p->owner) != AST_STATE_UP) {
- ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(resp));
- } else {
- /* This is a re-invite that failed. */
- /* Reset the flag after a while
- */
- int wait;
- /* RFC 3261, if owner of call, wait between 2.1 to 4 seconds,
- * if not owner of call, wait 0 to 2 seconds */
- if (p->outgoing_call) {
- wait = 2100 + ast_random() % 2000;
- } else {
- wait = ast_random() % 2000;
- }
- p->waitid = ast_sched_add(sched, wait, sip_reinvite_retry, dialog_ref(p, "passing dialog ptr into sched structure based on waitid for sip_reinvite_retry."));
- ast_debug(2, "Reinvite race. Scheduled sip_reinvite_retry in %d secs in handle_response_invite (waitid %d, dialog '%s')\n",
- wait, p->waitid, p->callid);
- }
- }
- break;
- case 408: /* Request timeout */
- case 405: /* Not allowed */
- case 501: /* Not implemented */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- if (p->owner) {
- ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(resp));
- }
- break;
- }
- if (xmitres == XMIT_ERROR) {
- ast_log(LOG_WARNING, "Could not transmit message in dialog %s\n", p->callid);
- }
- }
- /* \brief Handle SIP response in NOTIFY transaction
- We've sent a NOTIFY, now handle responses to it
- */
- static void handle_response_notify(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
- {
- switch (resp) {
- case 200: /* Notify accepted */
- /* They got the notify, this is the end */
- if (p->owner) {
- if (p->refer) {
- ast_log(LOG_NOTICE, "Got OK on REFER Notify message\n");
- } else {
- ast_log(LOG_WARNING, "Notify answer on an owned channel? - %s\n", ast_channel_name(p->owner));
- }
- } else {
- if (p->subscribed == NONE && !p->refer) {
- ast_debug(4, "Got 200 accepted on NOTIFY %s\n", p->callid);
- pvt_set_needdestroy(p, "received 200 response");
- }
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE)) {
- struct state_notify_data data = {
- .state = p->laststate,
- .device_state_info = p->last_device_state_info,
- .presence_state = p->last_presence_state,
- .presence_subtype = p->last_presence_subtype,
- .presence_message = p->last_presence_message,
- };
- /* Ready to send the next state we have on queue */
- ast_clear_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE);
- extensionstate_update(p->context, p->exten, &data, p, TRUE);
- }
- }
- break;
- case 401: /* Not www-authorized on SIP method */
- case 407: /* Proxy auth */
- if (!p->notify) {
- break; /* Only device notify can use NOTIFY auth */
- }
- ast_string_field_set(p, theirtag, NULL);
- if (ast_strlen_zero(p->authname)) {
- ast_log(LOG_WARNING, "Asked to authenticate NOTIFY to %s but we have no matching peer or realm auth!\n", ast_sockaddr_stringify(&p->recv));
- pvt_set_needdestroy(p, "unable to authenticate NOTIFY");
- }
- if (p->authtries > 1 || do_proxy_auth(p, req, resp, SIP_NOTIFY, 0)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on NOTIFY to '%s'\n", sip_get_header(&p->initreq, "From"));
- pvt_set_needdestroy(p, "failed to authenticate NOTIFY");
- }
- break;
- case 481: /* Call leg does not exist */
- pvt_set_needdestroy(p, "Received 481 response for NOTIFY");
- break;
- }
- }
- /* \brief Handle SIP response in SUBSCRIBE transaction */
- static void handle_response_subscribe(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
- {
- if (p->subscribed == CALL_COMPLETION) {
- struct sip_monitor_instance *monitor_instance;
- if (resp < 300) {
- return;
- }
- /* Final failure response received. */
- monitor_instance = ao2_callback(sip_monitor_instances, 0,
- find_sip_monitor_instance_by_subscription_pvt, p);
- if (monitor_instance) {
- ast_cc_monitor_failed(monitor_instance->core_id,
- monitor_instance->device_name,
- "Received error response to our SUBSCRIBE");
- }
- return;
- }
- if (p->subscribed != MWI_NOTIFICATION) {
- return;
- }
- if (!p->mwi) {
- return;
- }
- switch (resp) {
- case 200: /* Subscription accepted */
- ast_debug(3, "Got 200 OK on subscription for MWI\n");
- set_pvt_allowed_methods(p, req);
- if (p->options) {
- if (p->options->outboundproxy) {
- ao2_ref(p->options->outboundproxy, -1);
- }
- ast_free(p->options);
- p->options = NULL;
- }
- p->mwi->subscribed = 1;
- if ((p->mwi->resub = ast_sched_add(sched, mwi_expiry * 1000, sip_subscribe_mwi_do, ao2_t_bump(p->mwi, "mwi ast_sched_add"))) < 0) {
- ao2_t_ref(p->mwi, -1, "mwi ast_sched_add < 0");
- }
- break;
- case 401:
- case 407:
- ast_string_field_set(p, theirtag, NULL);
- if (p->authtries > 1 || do_proxy_auth(p, req, resp, SIP_SUBSCRIBE, 0)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on SUBSCRIBE to '%s'\n", sip_get_header(&p->initreq, "From"));
- p->mwi->call = NULL;
- ao2_t_ref(p->mwi, -1, "failed to authenticate SUBSCRIBE");
- pvt_set_needdestroy(p, "failed to authenticate SUBSCRIBE");
- }
- break;
- case 403:
- transmit_response_with_date(p, "200 OK", req);
- ast_log(LOG_WARNING, "Authentication failed while trying to subscribe for MWI.\n");
- p->mwi->call = NULL;
- ao2_t_ref(p->mwi, -1, "received 403 response");
- pvt_set_needdestroy(p, "received 403 response");
- sip_alreadygone(p);
- break;
- case 404:
- ast_log(LOG_WARNING, "Subscription failed for MWI. The remote side said that a mailbox may not have been configured.\n");
- p->mwi->call = NULL;
- ao2_t_ref(p->mwi, -1, "received 404 response");
- pvt_set_needdestroy(p, "received 404 response");
- break;
- case 481:
- ast_log(LOG_WARNING, "Subscription failed for MWI. The remote side said that our dialog did not exist.\n");
- p->mwi->call = NULL;
- ao2_t_ref(p->mwi, -1, "received 481 response");
- pvt_set_needdestroy(p, "received 481 response");
- break;
- case 400: /* Bad Request */
- case 414: /* Request URI too long */
- case 493: /* Undecipherable */
- case 500:
- case 501:
- ast_log(LOG_WARNING, "Subscription failed for MWI. The remote side may have suffered a heart attack.\n");
- p->mwi->call = NULL;
- ao2_t_ref(p->mwi, -1, "received 500/501 response");
- pvt_set_needdestroy(p, "received serious error (500/501/493/414/400) response");
- break;
- }
- }
- /* \brief Handle SIP response in REFER transaction
- We've sent a REFER, now handle responses to it
- */
- static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
- {
- enum ast_control_transfer message = AST_TRANSFER_FAILED;
- /* If no refer structure exists, then do nothing */
- if (!p->refer)
- return;
- switch (resp) {
- case 202: /* Transfer accepted */
- /* We need to do something here */
- /* The transferee is now sending INVITE to target */
- p->refer->status = REFER_ACCEPTED;
- /* Now wait for next message */
- ast_debug(3, "Got 202 accepted on transfer\n");
- /* We should hang along, waiting for NOTIFY's here */
- break;
- case 401: /* Not www-authorized on SIP method */
- case 407: /* Proxy auth */
- if (ast_strlen_zero(p->authname)) {
- ast_log(LOG_WARNING, "Asked to authenticate REFER to %s but we have no matching peer or realm auth!\n",
- ast_sockaddr_stringify(&p->recv));
- if (p->owner) {
- ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
- }
- pvt_set_needdestroy(p, "unable to authenticate REFER");
- }
- if (p->authtries > 1 || do_proxy_auth(p, req, resp, SIP_REFER, 0)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on REFER to '%s'\n", sip_get_header(&p->initreq, "From"));
- p->refer->status = REFER_NOAUTH;
- if (p->owner) {
- ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
- }
- pvt_set_needdestroy(p, "failed to authenticate REFER");
- }
- break;
- case 405: /* Method not allowed */
- /* Return to the current call onhold */
- /* Status flag needed to be reset */
- ast_log(LOG_NOTICE, "SIP transfer to %s failed, REFER not allowed. \n", p->refer->refer_to);
- pvt_set_needdestroy(p, "received 405 response");
- p->refer->status = REFER_FAILED;
- if (p->owner) {
- ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
- }
- break;
- case 481: /* Call leg does not exist */
- /* A transfer with Replaces did not work */
- /* OEJ: We should Set flag, cancel the REFER, go back
- to original call - but right now we can't */
- ast_log(LOG_WARNING, "Remote host can't match REFER request to call '%s'. Giving up.\n", p->callid);
- if (p->owner)
- ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
- pvt_set_needdestroy(p, "received 481 response");
- break;
- case 500: /* Server error */
- case 501: /* Method not implemented */
- /* Return to the current call onhold */
- /* Status flag needed to be reset */
- ast_log(LOG_NOTICE, "SIP transfer to %s failed, call miserably fails. \n", p->refer->refer_to);
- pvt_set_needdestroy(p, "received 500/501 response");
- p->refer->status = REFER_FAILED;
- if (p->owner) {
- ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
- }
- break;
- case 603: /* Transfer declined */
- ast_log(LOG_NOTICE, "SIP transfer to %s declined, call miserably fails. \n", p->refer->refer_to);
- p->refer->status = REFER_FAILED;
- pvt_set_needdestroy(p, "received 603 response");
- if (p->owner) {
- ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
- }
- break;
- default:
- /* We should treat unrecognized 9xx as 900. 400 is actually
- specified as a possible response, but any 4-6xx is
- theoretically possible. */
- if (resp < 299) { /* 1xx cases don't get here */
- ast_log(LOG_WARNING, "SIP transfer to %s had unexpected 2xx response (%d), confusion is possible. \n", p->refer->refer_to, resp);
- } else {
- ast_log(LOG_WARNING, "SIP transfer to %s with response (%d). \n", p->refer->refer_to, resp);
- }
- p->refer->status = REFER_FAILED;
- pvt_set_needdestroy(p, "received failure response");
- if (p->owner) {
- ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
- }
- break;
- }
- }
- /*! \brief Handle responses on REGISTER to services */
- static int handle_response_register(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
- {
- int expires, expires_ms;
- struct sip_registry *r;
- r = p->registry;
- switch (resp) {
- case 401: /* Unauthorized */
- if (p->authtries == MAX_AUTHTRIES || do_register_auth(p, req, resp)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on REGISTER to '%s@%s' (Tries %d)\n", p->registry->username, p->registry->hostname, p->authtries);
- pvt_set_needdestroy(p, "failed to authenticate REGISTER");
- }
- break;
- case 403: /* Forbidden */
- if (global_reg_retry_403) {
- ast_log(LOG_NOTICE, "Treating 403 response to REGISTER as non-fatal for %s@%s\n",
- p->registry->username, p->registry->hostname);
- ast_string_field_set(r, nonce, "");
- ast_string_field_set(p, nonce, "");
- break;
- }
- ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for REGISTER for '%s' to '%s'\n", p->registry->username, p->registry->hostname);
- AST_SCHED_DEL_UNREF(sched, r->timeout, ao2_t_ref(r, -1, "reg ptr unref from handle_response_register 403"));
- r->regstate = REG_STATE_NOAUTH;
- sip_publish_registry(r->username, r->hostname, regstate2str(r->regstate));
- pvt_set_needdestroy(p, "received 403 response");
- break;
- case 404: /* Not found */
- ast_log(LOG_WARNING, "Got 404 Not found on SIP register to service %s@%s, giving up\n", p->registry->username, p->registry->hostname);
- pvt_set_needdestroy(p, "received 404 response");
- if (r->call)
- r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 404");
- r->regstate = REG_STATE_REJECTED;
- sip_publish_registry(r->username, r->hostname, regstate2str(r->regstate));
- AST_SCHED_DEL_UNREF(sched, r->timeout, ao2_t_ref(r, -1, "reg ptr unref from handle_response_register 404"));
- break;
- case 407: /* Proxy auth */
- if (p->authtries == MAX_AUTHTRIES || do_register_auth(p, req, resp)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on REGISTER to '%s' (tries '%d')\n", sip_get_header(&p->initreq, "From"), p->authtries);
- pvt_set_needdestroy(p, "failed to authenticate REGISTER");
- }
- break;
- case 408: /* Request timeout */
- /* Got a timeout response, so reset the counter of failed responses */
- if (r) {
- r->regattempts = 0;
- } else {
- ast_log(LOG_WARNING, "Got a 408 response to our REGISTER on call %s after we had destroyed the registry object\n", p->callid);
- }
- break;
- case 423: /* Interval too brief */
- r->expiry = atoi(sip_get_header(req, "Min-Expires"));
- ast_log(LOG_WARNING, "Got 423 Interval too brief for service %s@%s, minimum is %d seconds\n", p->registry->username, p->registry->hostname, r->expiry);
- AST_SCHED_DEL_UNREF(sched, r->timeout, ao2_t_ref(r, -1, "reg ptr unref from handle_response_register 423"));
- if (r->call) {
- r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 423");
- pvt_set_needdestroy(p, "received 423 response");
- }
- if (r->expiry > max_expiry) {
- ast_log(LOG_WARNING, "Required expiration time from %s@%s is too high, giving up\n", p->registry->username, p->registry->hostname);
- r->expiry = r->configured_expiry;
- r->regstate = REG_STATE_REJECTED;
- } else {
- r->regstate = REG_STATE_UNREGISTERED;
- transmit_register(r, SIP_REGISTER, NULL, NULL);
- }
- sip_publish_registry(r->username, r->hostname, regstate2str(r->regstate));
- break;
- case 400: /* Bad request */
- case 414: /* Request URI too long */
- case 493: /* Undecipherable */
- case 479: /* Kamailio/OpenSIPS: Not able to process the URI - address is wrong in register*/
- ast_log(LOG_WARNING, "Got error %d on register to %s@%s, giving up (check config)\n", resp, p->registry->username, p->registry->hostname);
- pvt_set_needdestroy(p, "received 4xx response");
- if (r->call)
- r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 4xx");
- r->regstate = REG_STATE_REJECTED;
- sip_publish_registry(r->username, r->hostname, regstate2str(r->regstate));
- AST_SCHED_DEL_UNREF(sched, r->timeout, ao2_t_ref(r, -1, "reg ptr unref from handle_response_register 479"));
- break;
- case 200: /* 200 OK */
- if (!r) {
- ast_log(LOG_WARNING, "Got 200 OK on REGISTER, but there isn't a registry entry for '%s' (we probably already got the OK)\n", S_OR(p->peername, p->username));
- pvt_set_needdestroy(p, "received erroneous 200 response");
- return 0;
- }
- r->regstate = REG_STATE_REGISTERED;
- r->regtime = ast_tvnow(); /* Reset time of last successful registration */
- sip_publish_registry(r->username, r->hostname, regstate2str(r->regstate));
- r->regattempts = 0;
- ast_debug(1, "Registration successful\n");
- if (r->timeout > -1) {
- ast_debug(1, "Cancelling timeout %d\n", r->timeout);
- }
- AST_SCHED_DEL_UNREF(sched, r->timeout, ao2_t_ref(r, -1, "reg ptr unref from handle_response_register 200"));
- if (r->call)
- r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 200");
- ao2_t_replace(p->registry, NULL, "unref registry entry p->registry");
- /* destroy dialog now to avoid interference with next register */
- pvt_set_needdestroy(p, "Registration successfull");
- /* set us up for re-registering
- * figure out how long we got registered for
- * according to section 6.13 of RFC, contact headers override
- * expires headers, so check those first */
- expires = 0;
- /* XXX todo: try to save the extra call */
- if (!ast_strlen_zero(sip_get_header(req, "Contact"))) {
- const char *contact = NULL;
- const char *tmptmp = NULL;
- int start = 0;
- for(;;) {
- contact = __get_header(req, "Contact", &start);
- /* this loop ensures we get a contact header about our register request */
- if(!ast_strlen_zero(contact)) {
- if( (tmptmp=strstr(contact, p->our_contact))) {
- contact=tmptmp;
- break;
- }
- } else
- break;
- }
- tmptmp = strcasestr(contact, "expires=");
- if (tmptmp) {
- if (sscanf(tmptmp + 8, "%30d", &expires) != 1) {
- expires = 0;
- }
- }
- }
- if (!expires)
- expires=atoi(sip_get_header(req, "expires"));
- if (!expires)
- expires=default_expiry;
- expires_ms = expires * 1000;
- if (expires <= EXPIRY_GUARD_LIMIT)
- expires_ms -= MAX((expires_ms * EXPIRY_GUARD_PCT), EXPIRY_GUARD_MIN);
- else
- expires_ms -= EXPIRY_GUARD_SECS * 1000;
- if (sipdebug)
- ast_log(LOG_NOTICE, "Outbound Registration: Expiry for %s is %d sec (Scheduling reregistration in %d s)\n", r->hostname, expires, expires_ms/1000);
- r->refresh= (int) expires_ms / 1000;
- /* Schedule re-registration before we expire */
- AST_SCHED_REPLACE_UNREF(r->expire, sched, expires_ms, sip_reregister, r,
- ao2_t_ref(_data, -1, "unref in REPLACE del fail"),
- ao2_t_ref(r, -1, "unref in REPLACE add fail"),
- ao2_t_ref(r, +1, "The Addition side of REPLACE"));
- }
- return 1;
- }
- /*! \brief Handle qualification responses (OPTIONS) */
- static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_request *req)
- {
- struct sip_peer *peer = /* sip_ref_peer( */ p->relatedpeer /* , "bump refcount on p, as it is being used in this function(handle_response_peerpoke)")*/ ; /* hope this is already refcounted! */
- int statechanged, is_reachable, was_reachable;
- int pingtime = ast_tvdiff_ms(ast_tvnow(), peer->ps);
- /*
- * Compute the response time to a ping (goes in peer->lastms.)
- * -1 means did not respond, 0 means unknown,
- * 1..maxms is a valid response, >maxms means late response.
- */
- if (pingtime < 1) { /* zero = unknown, so round up to 1 */
- pingtime = 1;
- }
- if (!peer->maxms) { /* this should never happens */
- pvt_set_needdestroy(p, "got OPTIONS response but qualify is not enabled");
- return;
- }
- /* Now determine new state and whether it has changed.
- * Use some helper variables to simplify the writing
- * of the expressions.
- */
- was_reachable = peer->lastms > 0 && peer->lastms <= peer->maxms;
- is_reachable = pingtime <= peer->maxms;
- statechanged = peer->lastms == 0 /* yes, unknown before */
- || was_reachable != is_reachable;
- peer->lastms = pingtime;
- peer->call = dialog_unref(peer->call, "unref dialog peer->call");
- if (statechanged) {
- const char *s = is_reachable ? "Reachable" : "Lagged";
- char str_lastms[20];
- snprintf(str_lastms, sizeof(str_lastms), "%d", pingtime);
- ast_log(LOG_NOTICE, "Peer '%s' is now %s. (%dms / %dms)\n",
- peer->name, s, pingtime, peer->maxms);
- ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name);
- if (sip_cfg.peer_rtupdate) {
- ast_update_realtime(ast_check_realtime("sipregs") ? "sipregs" : "sippeers", "name", peer->name, "lastms", str_lastms, SENTINEL);
- }
- if (peer->endpoint) {
- RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
- ast_endpoint_set_state(peer->endpoint, AST_ENDPOINT_ONLINE);
- blob = ast_json_pack("{s: s, s: i}",
- "peer_status", s,
- "time", pingtime);
- ast_endpoint_blob_publish(peer->endpoint, ast_endpoint_state_type(), blob);
- }
- if (is_reachable && sip_cfg.regextenonqualify) {
- register_peer_exten(peer, TRUE);
- }
- }
- pvt_set_needdestroy(p, "got OPTIONS response");
- /* Try again eventually */
- AST_SCHED_REPLACE_UNREF(peer->pokeexpire, sched,
- is_reachable ? peer->qualifyfreq : DEFAULT_FREQ_NOTOK,
- sip_poke_peer_s, peer,
- sip_unref_peer(_data, "removing poke peer ref"),
- sip_unref_peer(peer, "removing poke peer ref"),
- sip_ref_peer(peer, "adding poke peer ref"));
- }
- /*!
- * \internal
- * \brief Handle responses to INFO messages
- *
- * \note The INFO method MUST NOT change the state of calls or
- * related sessions (RFC 2976).
- */
- static void handle_response_info(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
- {
- int sipmethod = SIP_INFO;
- switch (resp) {
- case 401: /* Not www-authorized on SIP method */
- case 407: /* Proxy auth required */
- ast_log(LOG_WARNING, "Host '%s' requests authentication (%d) for '%s'\n",
- ast_sockaddr_stringify(&p->sa), resp, sip_methods[sipmethod].text);
- break;
- case 405: /* Method not allowed */
- case 501: /* Not Implemented */
- mark_method_unallowed(&p->allowed_methods, sipmethod);
- if (p->relatedpeer) {
- mark_method_allowed(&p->relatedpeer->disallowed_methods, sipmethod);
- }
- ast_log(LOG_WARNING, "Host '%s' does not implement '%s'\n",
- ast_sockaddr_stringify(&p->sa), sip_methods[sipmethod].text);
- break;
- default:
- if (300 <= resp && resp < 700) {
- ast_verb(3, "Got SIP %s response %d \"%s\" back from host '%s'\n",
- sip_methods[sipmethod].text, resp, rest, ast_sockaddr_stringify(&p->sa));
- }
- break;
- }
- }
- /*!
- * \internal
- * \brief Handle auth requests to a MESSAGE request
- * \return TRUE if authentication failed.
- */
- static int do_message_auth(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
- {
- char *header;
- char *respheader;
- char digest[1024];
- if (p->options) {
- p->options->auth_type = (resp == 401 ? WWW_AUTH : PROXY_AUTH);
- }
- if (p->authtries == MAX_AUTHTRIES) {
- ast_log(LOG_NOTICE, "Failed to authenticate MESSAGE with host '%s'\n",
- ast_sockaddr_stringify(&p->sa));
- return -1;
- }
- ++p->authtries;
- sip_auth_headers((resp == 401 ? WWW_AUTH : PROXY_AUTH), &header, &respheader);
- memset(digest, 0, sizeof(digest));
- if (reply_digest(p, req, header, SIP_MESSAGE, digest, sizeof(digest))) {
- /* There's nothing to use for authentication */
- ast_debug(1, "Nothing to use for MESSAGE authentication\n");
- return -1;
- }
- if (p->do_history) {
- append_history(p, "MessageAuth", "Try: %d", p->authtries);
- }
- transmit_message(p, 0, 1);
- return 0;
- }
- /*!
- * \internal
- * \brief Handle responses to MESSAGE messages
- *
- * \note The MESSAGE method should not change the state of calls
- * or related sessions if associated with a dialog. (Implied by
- * RFC 3428 Section 2).
- */
- static void handle_response_message(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
- {
- int sipmethod = SIP_MESSAGE;
- int in_dialog = ast_test_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- switch (resp) {
- case 401: /* Not www-authorized on SIP method */
- case 407: /* Proxy auth required */
- if (do_message_auth(p, resp, rest, req, seqno) && !in_dialog) {
- pvt_set_needdestroy(p, "MESSAGE authentication failed");
- }
- break;
- case 405: /* Method not allowed */
- case 501: /* Not Implemented */
- mark_method_unallowed(&p->allowed_methods, sipmethod);
- if (p->relatedpeer) {
- mark_method_allowed(&p->relatedpeer->disallowed_methods, sipmethod);
- }
- ast_log(LOG_WARNING, "Host '%s' does not implement '%s'\n",
- ast_sockaddr_stringify(&p->sa), sip_methods[sipmethod].text);
- if (!in_dialog) {
- pvt_set_needdestroy(p, "MESSAGE not implemented or allowed");
- }
- break;
- default:
- if (100 <= resp && resp < 200) {
- /* Must allow provisional responses for out-of-dialog requests. */
- } else if (200 <= resp && resp < 300) {
- p->authtries = 0; /* Reset authentication counter */
- if (!in_dialog) {
- pvt_set_needdestroy(p, "MESSAGE delivery accepted");
- }
- } else if (300 <= resp && resp < 700) {
- ast_verb(3, "Got SIP %s response %d \"%s\" back from host '%s'\n",
- sip_methods[sipmethod].text, resp, rest, ast_sockaddr_stringify(&p->sa));
- if (!in_dialog) {
- pvt_set_needdestroy(p, (300 <= resp && resp < 600)
- ? "MESSAGE delivery failed" : "MESSAGE delivery refused");
- }
- }
- break;
- }
- }
- /*! \brief Immediately stop RTP, VRTP and UDPTL as applicable */
- static void stop_media_flows(struct sip_pvt *p)
- {
- /* Immediately stop RTP, VRTP and UDPTL as applicable */
- if (p->rtp)
- ast_rtp_instance_stop(p->rtp);
- if (p->vrtp)
- ast_rtp_instance_stop(p->vrtp);
- if (p->trtp)
- ast_rtp_instance_stop(p->trtp);
- if (p->udptl)
- ast_udptl_stop(p->udptl);
- }
- /*! \brief Handle SIP response in dialogue
- \note only called by handle_incoming */
- static void handle_response(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
- {
- struct ast_channel *owner;
- int sipmethod;
- const char *c = sip_get_header(req, "Cseq");
- /* GCC 4.2 complains if I try to cast c as a char * when passing it to ast_skip_nonblanks, so make a copy of it */
- char *c_copy = ast_strdupa(c);
- /* Skip the Cseq and its subsequent spaces */
- const char *msg = ast_skip_blanks(ast_skip_nonblanks(c_copy));
- if (!msg)
- msg = "";
- sipmethod = find_sip_method(msg);
- owner = p->owner;
- if (owner) {
- const char *rp = NULL, *rh = NULL;
- ast_channel_hangupcause_set(owner, 0);
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_Q850_REASON) && (rh = sip_get_header(req, "Reason"))) {
- rh = ast_skip_blanks(rh);
- if (!strncasecmp(rh, "Q.850", 5)) {
- int cause = ast_channel_hangupcause(owner);
- rp = strstr(rh, "cause=");
- if (rp && sscanf(rp + 6, "%30d", &cause) == 1) {
- ast_channel_hangupcause_set(owner, cause & 0x7f);
- if (req->debug)
- ast_verbose("Using Reason header for cause code: %d\n", ast_channel_hangupcause(owner));
- }
- }
- }
- if (!ast_channel_hangupcause(owner))
- ast_channel_hangupcause_set(owner, hangup_sip2cause(resp));
- }
- if (p->socket.type == AST_TRANSPORT_UDP) {
- int ack_res = FALSE;
- /* Acknowledge whatever it is destined for */
- if ((resp >= 100) && (resp <= 199)) {
- /* NON-INVITE messages do not ack a 1XX response. RFC 3261 section 17.1.2.2 */
- if (sipmethod == SIP_INVITE) {
- ack_res = __sip_semi_ack(p, seqno, 0, sipmethod);
- }
- } else {
- ack_res = __sip_ack(p, seqno, 0, sipmethod);
- }
- if (ack_res == FALSE) {
- /* RFC 3261 13.2.2.4 and 17.1.1.2 - We must re-send ACKs to re-transmitted final responses */
- if (sipmethod == SIP_INVITE && resp >= 200) {
- transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, resp < 300 ? TRUE: FALSE);
- }
- append_history(p, "Ignore", "Ignoring this retransmit\n");
- return;
- }
- }
- /* If this is a NOTIFY for a subscription clear the flag that indicates that we have a NOTIFY pending */
- if (!p->owner && sipmethod == SIP_NOTIFY && p->pendinginvite) {
- p->pendinginvite = 0;
- }
- /* Get their tag if we haven't already */
- if (ast_strlen_zero(p->theirtag) || (resp >= 200)) {
- char tag[128];
- gettag(req, "To", tag, sizeof(tag));
- ast_string_field_set(p, theirtag, tag);
- } else {
- /* Store theirtag to track for changes when 200 responses to invites are received without SDP */
- ast_string_field_set(p, theirprovtag, p->theirtag);
- }
- /* This needs to be configurable on a channel/peer level,
- not mandatory for all communication. Sadly enough, NAT implementations
- are not so stable so we can always rely on these headers.
- Temporarily disabled, while waiting for fix.
- Fix assigned to Rizzo :-)
- */
- /* check_via_response(p, req); */
- /* RFC 3261 Section 15 specifies that if we receive a 408 or 481
- * in response to a BYE, then we should end the current dialog
- * and session. It is known that at least one phone manufacturer
- * potentially will send a 404 in response to a BYE, so we'll be
- * liberal in what we accept and end the dialog and session if we
- * receive any of those responses to a BYE.
- */
- if ((resp == 404 || resp == 408 || resp == 481) && sipmethod == SIP_BYE) {
- pvt_set_needdestroy(p, "received 4XX response to a BYE");
- return;
- }
- if (p->relatedpeer && sipmethod == SIP_OPTIONS) {
- /* We don't really care what the response is, just that it replied back.
- Well, as long as it's not a 100 response... since we might
- need to hang around for something more "definitive" */
- if (resp != 100)
- handle_response_peerpoke(p, resp, req);
- } else if (sipmethod == SIP_REFER && resp >= 200) {
- handle_response_refer(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_PUBLISH) {
- /* SIP PUBLISH transcends this morass of doodoo and instead
- * we just always call the response handler. Good gravy!
- */
- handle_response_publish(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_INFO) {
- /* More good gravy! */
- handle_response_info(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_MESSAGE) {
- /* More good gravy! */
- handle_response_message(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_NOTIFY) {
- /* The gravy train continues to roll */
- handle_response_notify(p, resp, rest, req, seqno);
- } else if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- switch(resp) {
- case 100: /* 100 Trying */
- case 101: /* 101 Dialog establishment */
- case 183: /* 183 Session Progress */
- case 180: /* 180 Ringing */
- case 182: /* 182 Queued */
- case 181: /* 181 Call Is Being Forwarded */
- if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- break;
- case 200: /* 200 OK */
- p->authtries = 0; /* Reset authentication counter */
- if (sipmethod == SIP_INVITE) {
- handle_response_invite(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_REGISTER) {
- handle_response_register(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_SUBSCRIBE) {
- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- handle_response_subscribe(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_BYE) { /* Ok, we're ready to go */
- pvt_set_needdestroy(p, "received 200 response");
- ast_clear_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- }
- break;
- case 401: /* Not www-authorized on SIP method */
- case 407: /* Proxy auth required */
- if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_SUBSCRIBE)
- handle_response_subscribe(p, resp, rest, req, seqno);
- else if (p->registry && sipmethod == SIP_REGISTER)
- handle_response_register(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_UPDATE) {
- handle_response_update(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_BYE) {
- if (p->options)
- p->options->auth_type = resp;
- if (ast_strlen_zero(p->authname)) {
- ast_log(LOG_WARNING, "Asked to authenticate %s, to %s but we have no matching peer!\n",
- msg, ast_sockaddr_stringify(&p->recv));
- pvt_set_needdestroy(p, "unable to authenticate BYE");
- } else if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, resp, sipmethod, 0)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on %s to '%s'\n", msg, sip_get_header(&p->initreq, "From"));
- pvt_set_needdestroy(p, "failed to authenticate BYE");
- }
- } else {
- ast_log(LOG_WARNING, "Got authentication request (%d) on %s to '%s'\n", resp, sip_methods[sipmethod].text, sip_get_header(req, "To"));
- pvt_set_needdestroy(p, "received 407 response");
- }
- break;
- case 403: /* Forbidden - we failed authentication */
- if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_SUBSCRIBE)
- handle_response_subscribe(p, resp, rest, req, seqno);
- else if (p->registry && sipmethod == SIP_REGISTER)
- handle_response_register(p, resp, rest, req, seqno);
- else {
- ast_log(LOG_WARNING, "Forbidden - maybe wrong password on authentication for %s\n", msg);
- pvt_set_needdestroy(p, "received 403 response");
- }
- break;
- case 400: /* Bad Request */
- case 414: /* Request URI too long */
- case 493: /* Undecipherable */
- case 404: /* Not found */
- if (p->registry && sipmethod == SIP_REGISTER)
- handle_response_register(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_SUBSCRIBE)
- handle_response_subscribe(p, resp, rest, req, seqno);
- else if (owner)
- ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
- break;
- case 423: /* Interval too brief */
- if (sipmethod == SIP_REGISTER)
- handle_response_register(p, resp, rest, req, seqno);
- break;
- case 408: /* Request timeout - terminate dialog */
- if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_REGISTER)
- handle_response_register(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_BYE) {
- pvt_set_needdestroy(p, "received 408 response");
- ast_debug(4, "Got timeout on bye. Thanks for the answer. Now, kill this call\n");
- } else {
- if (owner)
- ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
- pvt_set_needdestroy(p, "received 408 response");
- }
- break;
- case 428:
- case 422: /* Session-Timers: Session Interval Too Small */
- if (sipmethod == SIP_INVITE) {
- handle_response_invite(p, resp, rest, req, seqno);
- }
- break;
- case 480:
- if (sipmethod == SIP_INVITE) {
- handle_response_invite(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_SUBSCRIBE) {
- handle_response_subscribe(p, resp, rest, req, seqno);
- } else if (owner) {
- /* No specific handler. Default to congestion */
- ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
- }
- break;
- case 481: /* Call leg does not exist */
- if (sipmethod == SIP_INVITE) {
- handle_response_invite(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_SUBSCRIBE) {
- handle_response_subscribe(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_BYE) {
- /* The other side has no transaction to bye,
- just assume it's all right then */
- ast_log(LOG_WARNING, "Remote host can't match request %s to call '%s'. Giving up.\n", sip_methods[sipmethod].text, p->callid);
- } else if (sipmethod == SIP_CANCEL) {
- /* The other side has no transaction to cancel,
- just assume it's all right then */
- ast_log(LOG_WARNING, "Remote host can't match request %s to call '%s'. Giving up.\n", sip_methods[sipmethod].text, p->callid);
- } else {
- ast_log(LOG_WARNING, "Remote host can't match request %s to call '%s'. Giving up.\n", sip_methods[sipmethod].text, p->callid);
- /* Guessing that this is not an important request */
- }
- break;
- case 487:
- if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- break;
- case 415: /* Unsupported media type */
- case 488: /* Not acceptable here - codec error */
- case 606: /* Not Acceptable */
- if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- break;
- case 491: /* Pending */
- if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- else {
- ast_debug(1, "Got 491 on %s, unsupported. Call ID %s\n", sip_methods[sipmethod].text, p->callid);
- pvt_set_needdestroy(p, "received 491 response");
- }
- break;
- case 405: /* Method not allowed */
- case 501: /* Not Implemented */
- mark_method_unallowed(&p->allowed_methods, sipmethod);
- if (p->relatedpeer) {
- mark_method_allowed(&p->relatedpeer->disallowed_methods, sipmethod);
- }
- if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- else
- ast_log(LOG_WARNING, "Host '%s' does not implement '%s'\n", ast_sockaddr_stringify(&p->sa), msg);
- break;
- default:
- if ((resp >= 200) && (resp < 300)) { /* on any 2XX response do the following */
- if (sipmethod == SIP_INVITE) {
- handle_response_invite(p, resp, rest, req, seqno);
- }
- } else if ((resp >= 300) && (resp < 700)) {
- /* Fatal response */
- if ((resp != 487))
- ast_verb(3, "Got SIP response %d \"%s\" back from %s\n", resp, rest, ast_sockaddr_stringify(&p->sa));
- if (sipmethod == SIP_INVITE)
- stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
- /* XXX Locking issues?? XXX */
- switch(resp) {
- case 300: /* Multiple Choices */
- case 301: /* Moved permanently */
- case 302: /* Moved temporarily */
- case 305: /* Use Proxy */
- if (p->owner) {
- struct ast_party_redirecting redirecting;
- struct ast_set_party_redirecting update_redirecting;
- ast_party_redirecting_init(&redirecting);
- memset(&update_redirecting, 0, sizeof(update_redirecting));
- change_redirecting_information(p, req, &redirecting,
- &update_redirecting, TRUE);
- ast_channel_set_redirecting(p->owner, &redirecting,
- &update_redirecting);
- ast_party_redirecting_free(&redirecting);
- }
- /* Fall through */
- case 486: /* Busy here */
- case 600: /* Busy everywhere */
- case 603: /* Decline */
- if (p->owner) {
- sip_handle_cc(p, req, AST_CC_CCBS);
- ast_queue_control(p->owner, AST_CONTROL_BUSY);
- }
- break;
- case 482: /* Loop Detected */
- case 404: /* Not Found */
- case 410: /* Gone */
- case 400: /* Bad Request */
- case 500: /* Server error */
- if (sipmethod == SIP_SUBSCRIBE) {
- handle_response_subscribe(p, resp, rest, req, seqno);
- break;
- }
- /* Fall through */
- case 502: /* Bad gateway */
- case 503: /* Service Unavailable */
- case 504: /* Server Timeout */
- if (owner)
- ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
- break;
- case 484: /* Address Incomplete */
- if (owner && sipmethod != SIP_BYE) {
- switch (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) {
- case SIP_PAGE2_ALLOWOVERLAP_YES:
- ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(resp));
- break;
- default:
- ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(404));
- break;
- }
- }
- break;
- default:
- /* Send hangup */
- if (owner && sipmethod != SIP_BYE)
- ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(resp));
- break;
- }
- /* ACK on invite */
- if (sipmethod == SIP_INVITE)
- transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- sip_alreadygone(p);
- if (!p->owner) {
- pvt_set_needdestroy(p, "transaction completed");
- }
- } else if ((resp >= 100) && (resp < 200)) {
- if (sipmethod == SIP_INVITE) {
- if (!req->ignore && sip_cancel_destroy(p))
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- if (find_sdp(req))
- process_sdp(p, req, SDP_T38_NONE);
- if (p->owner) {
- /* Queue a progress frame */
- ast_queue_control(p->owner, AST_CONTROL_PROGRESS);
- }
- }
- } else
- ast_log(LOG_NOTICE, "Don't know how to handle a %d %s response from %s\n", resp, rest, p->owner ? ast_channel_name(p->owner) : ast_sockaddr_stringify(&p->sa));
- }
- } else {
- /* Responses to OUTGOING SIP requests on INCOMING calls
- get handled here. As well as out-of-call message responses */
- if (req->debug)
- ast_verbose("SIP Response message for INCOMING dialog %s arrived\n", msg);
- if (sipmethod == SIP_INVITE && resp == 200) {
- /* Tags in early session is replaced by the tag in 200 OK, which is
- the final reply to our INVITE */
- char tag[128];
- gettag(req, "To", tag, sizeof(tag));
- ast_string_field_set(p, theirtag, tag);
- }
- switch(resp) {
- case 200:
- if (sipmethod == SIP_INVITE) {
- handle_response_invite(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_CANCEL) {
- ast_debug(1, "Got 200 OK on CANCEL\n");
- /* Wait for 487, then destroy */
- } else if (sipmethod == SIP_BYE) {
- pvt_set_needdestroy(p, "transaction completed");
- }
- break;
- case 401: /* www-auth */
- case 407:
- if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_BYE) {
- if (p->authtries == MAX_AUTHTRIES || do_proxy_auth(p, req, resp, sipmethod, 0)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on %s to '%s'\n", msg, sip_get_header(&p->initreq, "From"));
- pvt_set_needdestroy(p, "failed to authenticate BYE");
- }
- }
- break;
- case 481: /* Call leg does not exist */
- if (sipmethod == SIP_INVITE) {
- /* Re-invite failed */
- handle_response_invite(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_BYE) {
- pvt_set_needdestroy(p, "received 481 response");
- } else if (sipdebug) {
- ast_debug(1, "Remote host can't match request %s to call '%s'. Giving up\n", sip_methods[sipmethod].text, p->callid);
- }
- break;
- case 501: /* Not Implemented */
- if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- break;
- default: /* Errors without handlers */
- if ((resp >= 100) && (resp < 200)) {
- if (sipmethod == SIP_INVITE) { /* re-invite */
- if (!req->ignore && sip_cancel_destroy(p))
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- }
- } else if ((resp >= 200) && (resp < 300)) { /* on any unrecognized 2XX response do the following */
- if (sipmethod == SIP_INVITE) {
- handle_response_invite(p, resp, rest, req, seqno);
- }
- } else if ((resp >= 300) && (resp < 700)) {
- if ((resp != 487))
- ast_verb(3, "Incoming call: Got SIP response %d \"%s\" back from %s\n", resp, rest, ast_sockaddr_stringify(&p->sa));
- switch(resp) {
- case 415: /* Unsupported media type */
- case 488: /* Not acceptable here - codec error */
- case 603: /* Decline */
- case 500: /* Server error */
- case 502: /* Bad gateway */
- case 503: /* Service Unavailable */
- case 504: /* Server timeout */
- /* re-invite failed */
- if (sipmethod == SIP_INVITE && sip_cancel_destroy(p))
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- break;
- }
- }
- break;
- }
- }
- }
- /*! \brief SIP pickup support function
- * Starts in a new thread, then pickup the call
- */
- static void *sip_pickup_thread(void *stuff)
- {
- struct ast_channel *chan;
- chan = stuff;
- if (ast_pickup_call(chan)) {
- ast_channel_hangupcause_set(chan, AST_CAUSE_CALL_REJECTED);
- } else {
- ast_channel_hangupcause_set(chan, AST_CAUSE_NORMAL_CLEARING);
- }
- ast_hangup(chan);
- ast_channel_unref(chan);
- chan = NULL;
- return NULL;
- }
- /*! \brief Pickup a call using the subsystem in features.c
- * This is executed in a separate thread
- */
- static int sip_pickup(struct ast_channel *chan)
- {
- pthread_t threadid;
- ast_channel_ref(chan);
- if (ast_pthread_create_detached_background(&threadid, NULL, sip_pickup_thread, chan)) {
- ast_debug(1, "Unable to start Group pickup thread on channel %s\n", ast_channel_name(chan));
- ast_channel_unref(chan);
- return -1;
- }
- ast_debug(1, "Started Group pickup thread on channel %s\n", ast_channel_name(chan));
- return 0;
- }
- /*! \brief Get tag from packet
- *
- * \return Returns the pointer to the provided tag buffer,
- * or NULL if the tag was not found.
- */
- static const char *gettag(const struct sip_request *req, const char *header, char *tagbuf, int tagbufsize)
- {
- const char *thetag;
- if (!tagbuf)
- return NULL;
- tagbuf[0] = '\0'; /* reset the buffer */
- thetag = sip_get_header(req, header);
- thetag = strcasestr(thetag, ";tag=");
- if (thetag) {
- thetag += 5;
- ast_copy_string(tagbuf, thetag, tagbufsize);
- return strsep(&tagbuf, ";");
- }
- return NULL;
- }
- static int handle_cc_notify(struct sip_pvt *pvt, struct sip_request *req)
- {
- struct sip_monitor_instance *monitor_instance = ao2_callback(sip_monitor_instances, 0,
- find_sip_monitor_instance_by_subscription_pvt, pvt);
- const char *status = get_content_line(req, "cc-state", ':');
- struct cc_epa_entry *cc_entry;
- char *uri;
- if (!monitor_instance) {
- transmit_response(pvt, "400 Bad Request", req);
- return -1;
- }
- if (ast_strlen_zero(status)) {
- ao2_ref(monitor_instance, -1);
- transmit_response(pvt, "400 Bad Request", req);
- return -1;
- }
- if (!strcmp(status, "queued")) {
- /* We've been told that we're queued. This is the endpoint's way of telling
- * us that it has accepted our CC request. We need to alert the core of this
- * development
- */
- ast_cc_monitor_request_acked(monitor_instance->core_id, "SIP endpoint %s accepted request", monitor_instance->device_name);
- transmit_response(pvt, "200 OK", req);
- ao2_ref(monitor_instance, -1);
- return 0;
- }
- /* It's open! Yay! */
- uri = get_content_line(req, "cc-URI", ':');
- if (ast_strlen_zero(uri)) {
- uri = get_in_brackets((char *)sip_get_header(req, "From"));
- }
- ast_string_field_set(monitor_instance, notify_uri, uri);
- if (monitor_instance->suspension_entry) {
- cc_entry = monitor_instance->suspension_entry->instance_data;
- if (cc_entry->current_state == CC_CLOSED) {
- /* If we've created a suspension entry and the current state is closed, then that means
- * we got a notice from the CC core earlier to suspend monitoring, but because this particular
- * call leg had not yet notified us that it was ready for recall, it meant that we
- * could not yet send a PUBLISH. Now, however, we can.
- */
- construct_pidf_body(CC_CLOSED, monitor_instance->suspension_entry->body,
- sizeof(monitor_instance->suspension_entry->body), monitor_instance->peername);
- transmit_publish(monitor_instance->suspension_entry, SIP_PUBLISH_INITIAL, monitor_instance->notify_uri);
- } else {
- ast_cc_monitor_callee_available(monitor_instance->core_id, "SIP monitored callee has become available");
- }
- } else {
- ast_cc_monitor_callee_available(monitor_instance->core_id, "SIP monitored callee has become available");
- }
- ao2_ref(monitor_instance, -1);
- transmit_response(pvt, "200 OK", req);
- return 0;
- }
- /*! \brief Handle incoming notifications */
- static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, const char *e)
- {
- /* This is mostly a skeleton for future improvements */
- /* Mostly created to return proper answers on notifications on outbound REFER's */
- int res = 0;
- const char *event = sip_get_header(req, "Event");
- char *sep;
- if( (sep = strchr(event, ';')) ) { /* XXX bug here - overwriting string ? */
- *sep++ = '\0';
- }
- if (sipdebug)
- ast_debug(2, "Got NOTIFY Event: %s\n", event);
- if (!strcmp(event, "refer")) {
- /* Save nesting depth for now, since there might be other events we will
- support in the future */
- /* Handle REFER notifications */
- char *buf, *cmd, *code;
- int respcode;
- int success = TRUE;
- /* EventID for each transfer... EventID is basically the REFER cseq
- We are getting notifications on a call that we transferred
- We should hangup when we are getting a 200 OK in a sipfrag
- Check if we have an owner of this event */
- /* Check the content type */
- if (strncasecmp(sip_get_header(req, "Content-Type"), "message/sipfrag", strlen("message/sipfrag"))) {
- /* We need a sipfrag */
- transmit_response(p, "400 Bad request", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return -1;
- }
- /* Get the text of the attachment */
- if (ast_strlen_zero(buf = get_content(req))) {
- ast_log(LOG_WARNING, "Unable to retrieve attachment from NOTIFY %s\n", p->callid);
- transmit_response(p, "400 Bad request", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return -1;
- }
- /*
- From the RFC...
- A minimal, but complete, implementation can respond with a single
- NOTIFY containing either the body:
- SIP/2.0 100 Trying
- if the subscription is pending, the body:
- SIP/2.0 200 OK
- if the reference was successful, the body:
- SIP/2.0 503 Service Unavailable
- if the reference failed, or the body:
- SIP/2.0 603 Declined
- if the REFER request was accepted before approval to follow the
- reference could be obtained and that approval was subsequently denied
- (see Section 2.4.7).
- If there are several REFERs in the same dialog, we need to
- match the ID of the event header...
- */
- ast_debug(3, "* SIP Transfer NOTIFY Attachment: \n---%s\n---\n", buf);
- cmd = ast_skip_blanks(buf);
- code = cmd;
- /* We are at SIP/2.0 */
- while(*code && (*code > 32)) { /* Search white space */
- code++;
- }
- *code++ = '\0';
- code = ast_skip_blanks(code);
- sep = code;
- sep++;
- while(*sep && (*sep > 32)) { /* Search white space */
- sep++;
- }
- *sep++ = '\0'; /* Response string */
- respcode = atoi(code);
- switch (respcode) {
- case 200: /* OK: The new call is up, hangup this call */
- /* Hangup the call that we are replacing */
- break;
- case 301: /* Moved permenantly */
- case 302: /* Moved temporarily */
- /* Do we get the header in the packet in this case? */
- success = FALSE;
- break;
- case 503: /* Service Unavailable: The new call failed */
- case 603: /* Declined: Not accepted */
- /* Cancel transfer, continue the current call */
- success = FALSE;
- break;
- case 0: /* Parse error */
- /* Cancel transfer, continue the current call */
- ast_log(LOG_NOTICE, "Error parsing sipfrag in NOTIFY in response to REFER.\n");
- success = FALSE;
- break;
- default:
- if (respcode < 200) {
- /* ignore provisional responses */
- success = -1;
- } else {
- ast_log(LOG_NOTICE, "Got unknown code '%d' in NOTIFY in response to REFER.\n", respcode);
- success = FALSE;
- }
- break;
- }
- if (success == FALSE) {
- ast_log(LOG_NOTICE, "Transfer failed. Sorry. Nothing further to do with this call\n");
- }
- if (p->owner && success != -1) {
- enum ast_control_transfer message = success ? AST_TRANSFER_SUCCESS : AST_TRANSFER_FAILED;
- ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
- }
- /* Confirm that we received this packet */
- transmit_response(p, "200 OK", req);
- } else if (!strcmp(event, "message-summary")) {
- const char *mailbox = NULL;
- char *c = ast_strdupa(get_content_line(req, "Voice-Message", ':'));
- if (!p->mwi) {
- struct sip_peer *peer = sip_find_peer(NULL, &p->recv, TRUE, FINDPEERS, FALSE, p->socket.type);
- if (peer) {
- mailbox = ast_strdupa(peer->unsolicited_mailbox);
- sip_unref_peer(peer, "removing unsolicited mwi ref");
- }
- } else {
- mailbox = p->mwi->mailbox;
- }
- if (!ast_strlen_zero(mailbox) && !ast_strlen_zero(c)) {
- char *old = strsep(&c, " ");
- char *new = strsep(&old, "/");
- ast_publish_mwi_state(mailbox, "SIP_Remote", atoi(new), atoi(old));
- transmit_response(p, "200 OK", req);
- } else {
- transmit_response(p, "489 Bad event", req);
- res = -1;
- }
- } else if (!strcmp(event, "keep-alive")) {
- /* Used by Sipura/Linksys for NAT pinhole,
- * just confirm that we received the packet. */
- transmit_response(p, "200 OK", req);
- } else if (!strcmp(event, "call-completion")) {
- res = handle_cc_notify(p, req);
- } else {
- /* We don't understand this event. */
- transmit_response(p, "489 Bad event", req);
- res = -1;
- }
- if (!p->lastinvite)
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return res;
- }
- /*! \brief Handle incoming OPTIONS request
- An OPTIONS request should be answered like an INVITE from the same UA, including SDP
- */
- static int handle_request_options(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e)
- {
- const char *msg;
- enum sip_get_dest_result gotdest;
- int res;
- if (p->lastinvite) {
- /* if this is a request in an active dialog, just confirm that the dialog exists. */
- transmit_response_with_allow(p, "200 OK", req, 0);
- return 0;
- }
- if (sip_cfg.auth_options_requests) {
- /* Do authentication if this OPTIONS request began the dialog */
- copy_request(&p->initreq, req);
- set_pvt_allowed_methods(p, req);
- res = check_user(p, req, SIP_OPTIONS, e, XMIT_UNRELIABLE, addr);
- if (res == AUTH_CHALLENGE_SENT) {
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return 0;
- }
- if (res < 0) { /* Something failed in authentication */
- ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", sip_get_header(req, "From"));
- transmit_response(p, "403 Forbidden", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return 0;
- }
- }
- /* must go through authentication before getting here */
- gotdest = get_destination(p, req, NULL);
- build_contact(p, req, 1);
- if (ast_strlen_zero(p->context))
- ast_string_field_set(p, context, sip_cfg.default_context);
- if (ast_shutting_down()) {
- /*
- * Not taking any new calls at this time.
- * Likely a server availability OPTIONS poll.
- */
- msg = "503 Unavailable";
- } else {
- msg = "404 Not Found";
- switch (gotdest) {
- case SIP_GET_DEST_INVALID_URI:
- msg = "416 Unsupported URI scheme";
- break;
- case SIP_GET_DEST_EXTEN_MATCHMORE:
- case SIP_GET_DEST_REFUSED:
- case SIP_GET_DEST_EXTEN_NOT_FOUND:
- //msg = "404 Not Found";
- break;
- case SIP_GET_DEST_EXTEN_FOUND:
- msg = "200 OK";
- break;
- }
- }
- transmit_response_with_allow(p, msg, req, 0);
- /* Destroy if this OPTIONS was the opening request, but not if
- it's in the middle of a normal call flow. */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return 0;
- }
- /*! \brief Handle the transfer part of INVITE with a replaces: header,
- *
- * This is used for call-pickup and for attended transfers initiated on
- * remote endpoints (i.e. a REFER received on a remote server).
- *
- * \note p and p->owner are locked upon entering this function. If the
- * call pickup or attended transfer is successful, then p->owner will
- * be unlocked upon exiting this function. This is communicated to the
- * caller through the nounlock parameter.
- *
- * \param p The sip_pvt where the INVITE with Replaces was received
- * \param req The incoming INVITE
- * \param[out] nounlock Indicator if p->owner should remained locked. On successful transfer, this will be set true.
- * \param replaces_pvt sip_pvt referenced by Replaces header
- * \param replaces_chan replaces_pvt's owner channel
- * \retval 0 Success
- * \retval non-zero Failure
- */
- static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req,
- int *nounlock, struct sip_pvt *replaces_pvt, struct ast_channel *replaces_chan)
- {
- struct ast_channel *c;
- RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
- if (req->ignore) {
- return 0;
- }
- if (!p->owner) {
- /* What to do if no channel ??? */
- ast_log(LOG_ERROR, "Unable to create new channel. Invite/replace failed.\n");
- transmit_response_reliable(p, "503 Service Unavailable", req);
- append_history(p, "Xfer", "INVITE/Replace Failed. No new channel.");
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return 1;
- }
- append_history(p, "Xfer", "INVITE/Replace received");
- c = ast_channel_ref(p->owner);
- /* Fake call progress */
- transmit_response(p, "100 Trying", req);
- ast_setstate(c, AST_STATE_RING);
- ast_debug(4, "Invite/Replaces: preparing to replace %s with %s\n", ast_channel_name(replaces_chan), ast_channel_name(c));
- *nounlock = 1;
- ast_channel_unlock(c);
- sip_pvt_unlock(p);
- ast_raw_answer(c);
- ast_channel_lock(replaces_chan);
- bridge = ast_channel_get_bridge(replaces_chan);
- ast_channel_unlock(replaces_chan);
- if (bridge) {
- if (ast_bridge_impart(bridge, c, replaces_chan, NULL,
- AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
- ast_hangup(c);
- }
- } else {
- ast_channel_move(replaces_chan, c);
- ast_hangup(c);
- }
- sip_pvt_lock(p);
- return 0;
- }
- /*! \note No channel or pvt locks should be held while calling this function. */
- static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context)
- {
- struct ast_str *str = ast_str_alloca(AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2);
- struct ast_app *pickup = pbx_findapp("Pickup");
- if (!pickup) {
- ast_log(LOG_ERROR, "Unable to perform pickup: Application 'Pickup' not loaded (app_directed_pickup.so).\n");
- return -1;
- }
- ast_str_set(&str, 0, "%s@%s", extension, sip_cfg.notifycid == IGNORE_CONTEXT ? "PICKUPMARK" : context);
- ast_debug(2, "About to call Pickup(%s)\n", ast_str_buffer(str));
- /* There is no point in capturing the return value since pickup_exec
- doesn't return anything meaningful unless the passed data is an empty
- string (which in our case it will not be) */
- pbx_exec(channel, pickup, ast_str_buffer(str));
- return 0;
- }
- /*! \brief Called to deny a T38 reinvite if the core does not respond to our request */
- static int sip_t38_abort(const void *data)
- {
- struct sip_pvt *p = (struct sip_pvt *) data;
- sip_pvt_lock(p);
- /* an application may have taken ownership of the T.38 negotiation on this
- * channel while we were waiting to grab the lock... if it did, the scheduler
- * id will have been reset to -1, which is our indication that we do *not*
- * want to abort the negotiation process
- */
- if (p->t38id != -1) {
- change_t38_state(p, T38_REJECTED);
- transmit_response_reliable(p, "488 Not acceptable here", &p->initreq);
- p->t38id = -1;
- dialog_unref(p, "unref the dialog ptr from sip_t38_abort, because it held a dialog ptr");
- }
- sip_pvt_unlock(p);
- return 0;
- }
- /*!
- * \brief bare-bones support for SIP UPDATE
- *
- * XXX This is not even close to being RFC 3311-compliant. We don't advertise
- * that we support the UPDATE method, so no one should ever try sending us
- * an UPDATE anyway. However, Asterisk can send an UPDATE to change connected
- * line information, so we need to be prepared to handle this. The way we distinguish
- * such an UPDATE is through the X-Asterisk-rpid-update header.
- *
- * Actually updating the media session may be some future work.
- */
- static int handle_request_update(struct sip_pvt *p, struct sip_request *req)
- {
- if (ast_strlen_zero(sip_get_header(req, "X-Asterisk-rpid-update"))) {
- transmit_response(p, "501 Method Not Implemented", req);
- return 0;
- }
- if (!p->owner) {
- transmit_response(p, "481 Call/Transaction Does Not Exist", req);
- return 0;
- }
- if (get_rpid(p, req)) {
- struct ast_party_connected_line connected;
- struct ast_set_party_connected_line update_connected;
- ast_party_connected_line_init(&connected);
- memset(&update_connected, 0, sizeof(update_connected));
- update_connected.id.number = 1;
- connected.id.number.valid = 1;
- connected.id.number.str = (char *) p->cid_num;
- connected.id.number.presentation = p->callingpres;
- update_connected.id.name = 1;
- connected.id.name.valid = 1;
- connected.id.name.str = (char *) p->cid_name;
- connected.id.name.presentation = p->callingpres;
- /* Invalidate any earlier private connected id representation */
- ast_set_party_id_all(&update_connected.priv);
- connected.id.tag = (char *) p->cid_tag;
- connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
- ast_channel_queue_connected_line_update(p->owner, &connected, &update_connected);
- }
- transmit_response(p, "200 OK", req);
- return 0;
- }
- /*
- * \internal \brief Check Session Timers for an INVITE request
- *
- * \retval 0 ok
- * \retval -1 failure
- */
- static int handle_request_invite_st(struct sip_pvt *p, struct sip_request *req,
- const char *required, int reinvite)
- {
- const char *p_uac_se_hdr; /* UAC's Session-Expires header string */
- const char *p_uac_min_se; /* UAC's requested Min-SE interval (char string) */
- int uac_max_se = -1; /* UAC's Session-Expires in integer format */
- int uac_min_se = -1; /* UAC's Min-SE in integer format */
- int st_active = FALSE; /* Session-Timer on/off boolean */
- int st_interval = 0; /* Session-Timer negotiated refresh interval */
- enum st_refresher tmp_st_ref = SESSION_TIMER_REFRESHER_AUTO; /* Session-Timer refresher */
- int dlg_min_se = -1;
- int dlg_max_se = global_max_se;
- int rtn;
- /* Session-Timers */
- if ((p->sipoptions & SIP_OPT_TIMER)) {
- enum st_refresher_param st_ref_param = SESSION_TIMER_REFRESHER_PARAM_UNKNOWN;
- /* The UAC has requested session-timers for this session. Negotiate
- the session refresh interval and who will be the refresher */
- ast_debug(2, "Incoming INVITE with 'timer' option supported\n");
- /* Allocate Session-Timers struct w/in the dialog */
- if (!p->stimer) {
- sip_st_alloc(p);
- }
- /* Parse the Session-Expires header */
- p_uac_se_hdr = sip_get_header(req, "Session-Expires");
- if (!ast_strlen_zero(p_uac_se_hdr)) {
- ast_debug(2, "INVITE also has \"Session-Expires\" header.\n");
- rtn = parse_session_expires(p_uac_se_hdr, &uac_max_se, &st_ref_param);
- tmp_st_ref = (st_ref_param == SESSION_TIMER_REFRESHER_PARAM_UAC) ? SESSION_TIMER_REFRESHER_THEM : SESSION_TIMER_REFRESHER_US;
- if (rtn != 0) {
- transmit_response_reliable(p, "400 Session-Expires Invalid Syntax", req);
- return -1;
- }
- }
- /* Parse the Min-SE header */
- p_uac_min_se = sip_get_header(req, "Min-SE");
- if (!ast_strlen_zero(p_uac_min_se)) {
- ast_debug(2, "INVITE also has \"Min-SE\" header.\n");
- rtn = parse_minse(p_uac_min_se, &uac_min_se);
- if (rtn != 0) {
- transmit_response_reliable(p, "400 Min-SE Invalid Syntax", req);
- return -1;
- }
- }
- dlg_min_se = st_get_se(p, FALSE);
- switch (st_get_mode(p, 1)) {
- case SESSION_TIMER_MODE_ACCEPT:
- case SESSION_TIMER_MODE_ORIGINATE:
- if (uac_max_se > 0 && uac_max_se < dlg_min_se) {
- transmit_response_with_minse(p, "422 Session Interval Too Small", req, dlg_min_se);
- return -1;
- }
- p->stimer->st_active_peer_ua = TRUE;
- st_active = TRUE;
- if (st_ref_param == SESSION_TIMER_REFRESHER_PARAM_UNKNOWN) {
- tmp_st_ref = st_get_refresher(p);
- }
- dlg_max_se = st_get_se(p, TRUE);
- if (uac_max_se > 0) {
- if (dlg_max_se >= uac_min_se) {
- st_interval = (uac_max_se < dlg_max_se) ? uac_max_se : dlg_max_se;
- } else {
- st_interval = uac_max_se;
- }
- } else if (uac_min_se > 0) {
- st_interval = MAX(dlg_max_se, uac_min_se);
- } else {
- st_interval = dlg_max_se;
- }
- break;
- case SESSION_TIMER_MODE_REFUSE:
- if (p->reqsipoptions & SIP_OPT_TIMER) {
- transmit_response_with_unsupported(p, "420 Option Disabled", req, required);
- ast_log(LOG_WARNING, "Received SIP INVITE with supported but disabled option: %s\n", required);
- return -1;
- }
- break;
- default:
- ast_log(LOG_ERROR, "Internal Error %u at %s:%d\n", st_get_mode(p, 1), __FILE__, __LINE__);
- break;
- }
- } else {
- /* The UAC did not request session-timers. Asterisk (UAS), will now decide
- (based on session-timer-mode in sip.conf) whether to run session-timers for
- this session or not. */
- switch (st_get_mode(p, 1)) {
- case SESSION_TIMER_MODE_ORIGINATE:
- st_active = TRUE;
- st_interval = st_get_se(p, TRUE);
- tmp_st_ref = SESSION_TIMER_REFRESHER_US;
- p->stimer->st_active_peer_ua = (p->sipoptions & SIP_OPT_TIMER) ? TRUE : FALSE;
- break;
- default:
- break;
- }
- }
- if (reinvite == 0) {
- /* Session-Timers: Start session refresh timer based on negotiation/config */
- if (st_active == TRUE) {
- p->stimer->st_active = TRUE;
- p->stimer->st_interval = st_interval;
- p->stimer->st_ref = tmp_st_ref;
- }
- } else {
- if (p->stimer->st_active == TRUE) {
- /* Session-Timers: A re-invite request sent within a dialog will serve as
- a refresh request, no matter whether the re-invite was sent for refreshing
- the session or modifying it.*/
- ast_debug (2, "Restarting session-timers on a refresh - %s\n", p->callid);
- /* The UAC may be adjusting the session-timers mid-session */
- if (st_interval > 0) {
- p->stimer->st_interval = st_interval;
- p->stimer->st_ref = tmp_st_ref;
- }
- }
- }
- return 0;
- }
- /*!
- * \brief Handle incoming INVITE request
- * \note If the INVITE has a Replaces header, it is part of an
- * attended transfer. If so, we do not go through the dial
- * plan but try to find the active call and masquerade
- * into it
- */
- static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, int *recount, const char *e, int *nounlock)
- {
- int res = INV_REQ_SUCCESS;
- int gotdest;
- const char *p_replaces;
- char *replace_id = NULL;
- const char *required;
- unsigned int required_profile = 0;
- struct ast_channel *c = NULL; /* New channel */
- struct sip_peer *authpeer = NULL; /* Matching Peer */
- int reinvite = 0;
- struct ast_party_redirecting redirecting;
- struct ast_set_party_redirecting update_redirecting;
- int supported_start = 0;
- int require_start = 0;
- char unsupported[256] = { 0, };
- struct {
- char exten[AST_MAX_EXTENSION];
- char context[AST_MAX_CONTEXT];
- } pickup = {
- .exten = "",
- };
- RAII_VAR(struct sip_pvt *, replaces_pvt, NULL, ao2_cleanup);
- RAII_VAR(struct ast_channel *, replaces_chan, NULL, ao2_cleanup);
- /* Find out what they support */
- if (!p->sipoptions) {
- const char *supported = NULL;
- do {
- supported = __get_header(req, "Supported", &supported_start);
- if (!ast_strlen_zero(supported)) {
- p->sipoptions |= parse_sip_options(supported, NULL, 0);
- }
- } while (!ast_strlen_zero(supported));
- }
- /* Find out what they require */
- do {
- required = __get_header(req, "Require", &require_start);
- if (!ast_strlen_zero(required)) {
- required_profile |= parse_sip_options(required, unsupported, ARRAY_LEN(unsupported));
- }
- } while (!ast_strlen_zero(required));
- /* If there are any options required that we do not support,
- * then send a 420 with only those unsupported options listed */
- if (!ast_strlen_zero(unsupported)) {
- transmit_response_with_unsupported(p, "420 Bad extension (unsupported)", req, unsupported);
- ast_log(LOG_WARNING, "Received SIP INVITE with unsupported required extension: required:%s unsupported:%s\n", required, unsupported);
- p->invitestate = INV_COMPLETED;
- if (!p->lastinvite) {
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- }
- res = -1;
- goto request_invite_cleanup;
- }
- /* The option tags may be present in Supported: or Require: headers.
- Include the Require: option tags for further processing as well */
- p->sipoptions |= required_profile;
- p->reqsipoptions = required_profile;
- /* Check if this is a loop */
- if (ast_test_flag(&p->flags[0], SIP_OUTGOING) && p->owner && (p->invitestate != INV_TERMINATED && p->invitestate != INV_CONFIRMED) && ast_channel_state(p->owner) != AST_STATE_UP) {
- /* This is a call to ourself. Send ourselves an error code and stop
- processing immediately, as SIP really has no good mechanism for
- being able to call yourself */
- /* If pedantic is on, we need to check the tags. If they're different, this is
- in fact a forked call through a SIP proxy somewhere. */
- int different;
- const char *initial_rlpart2 = REQ_OFFSET_TO_STR(&p->initreq, rlpart2);
- const char *this_rlpart2 = REQ_OFFSET_TO_STR(req, rlpart2);
- if (sip_cfg.pedanticsipchecking)
- different = sip_uri_cmp(initial_rlpart2, this_rlpart2);
- else
- different = strcmp(initial_rlpart2, this_rlpart2);
- if (!different) {
- transmit_response(p, "482 Loop Detected", req);
- p->invitestate = INV_COMPLETED;
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- res = INV_REQ_FAILED;
- goto request_invite_cleanup;
- } else {
- /*! This is a spiral. What we need to do is to just change the outgoing INVITE
- * so that it now routes to the new Request URI. Since we created the INVITE ourselves
- * that should be all we need to do.
- *
- * \todo XXX This needs to be reviewed. YOu don't change the request URI really, you route the packet
- * correctly instead...
- */
- char *uri = ast_strdupa(this_rlpart2);
- char *at = strchr(uri, '@');
- char *peerorhost;
- ast_debug(2, "Potential spiral detected. Original RURI was %s, new RURI is %s\n", initial_rlpart2, this_rlpart2);
- transmit_response(p, "100 Trying", req);
- if (at) {
- *at = '\0';
- }
- /* Parse out "sip:" */
- if ((peerorhost = strchr(uri, ':'))) {
- *peerorhost++ = '\0';
- }
- ast_string_field_set(p, theirtag, NULL);
- /* Treat this as if there were a call forward instead...
- */
- ast_channel_call_forward_set(p->owner, peerorhost);
- ast_queue_control(p->owner, AST_CONTROL_BUSY);
- res = INV_REQ_FAILED;
- goto request_invite_cleanup;
- }
- }
- if (!req->ignore && p->pendinginvite) {
- if (!ast_test_flag(&p->flags[0], SIP_OUTGOING) && (p->invitestate == INV_COMPLETED || p->invitestate == INV_TERMINATED)) {
- /* What do these circumstances mean? We have received an INVITE for an "incoming" dialog for which we
- * have sent a final response. We have not yet received an ACK, though (which is why p->pendinginvite is non-zero).
- * We also know that the INVITE is not a retransmission, because otherwise the "ignore" flag would be set.
- * This means that either we are receiving a reinvite for a terminated dialog, or we are receiving an INVITE with
- * credentials based on one we challenged earlier.
- *
- * The action to take in either case is to treat the INVITE as though it contains an implicit ACK for the previous
- * transaction. Calling __sip_ack will take care of this by clearing the p->pendinginvite and removing the response
- * from the previous transaction from the list of outstanding packets.
- */
- __sip_ack(p, p->pendinginvite, 1, 0);
- } else {
- /* We already have a pending invite. Sorry. You are on hold. */
- p->glareinvite = seqno;
- transmit_response_reliable(p, "491 Request Pending", req);
- check_via(p, req);
- ast_debug(1, "Got INVITE on call where we already have pending INVITE, deferring that - %s\n", p->callid);
- /* Don't destroy dialog here */
- res = INV_REQ_FAILED;
- goto request_invite_cleanup;
- }
- }
- p_replaces = sip_get_header(req, "Replaces");
- if (!ast_strlen_zero(p_replaces)) {
- /* We have a replaces header */
- char *ptr;
- char *fromtag = NULL;
- char *totag = NULL;
- char *start, *to;
- int error = 0;
- if (p->owner) {
- ast_debug(3, "INVITE w Replaces on existing call? Refusing action. [%s]\n", p->callid);
- transmit_response_reliable(p, "400 Bad request", req); /* The best way to not not accept the transfer */
- check_via(p, req);
- copy_request(&p->initreq, req);
- /* Do not destroy existing call */
- res = INV_REQ_ERROR;
- goto request_invite_cleanup;
- }
- if (sipdebug)
- ast_debug(3, "INVITE part of call transfer. Replaces [%s]\n", p_replaces);
- /* Create a buffer we can manipulate */
- replace_id = ast_strdupa(p_replaces);
- ast_uri_decode(replace_id, ast_uri_sip_user);
- if (!sip_refer_alloc(p)) {
- transmit_response_reliable(p, "500 Server Internal Error", req);
- append_history(p, "Xfer", "INVITE/Replace Failed. Out of memory.");
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- p->invitestate = INV_COMPLETED;
- res = INV_REQ_ERROR;
- check_via(p, req);
- copy_request(&p->initreq, req);
- goto request_invite_cleanup;
- }
- /* Todo: (When we find phones that support this)
- if the replaces header contains ";early-only"
- we can only replace the call in early
- stage, not after it's up.
- If it's not in early mode, 486 Busy.
- */
- /* Skip leading whitespace */
- replace_id = ast_skip_blanks(replace_id);
- start = replace_id;
- while ( (ptr = strsep(&start, ";")) ) {
- ptr = ast_skip_blanks(ptr); /* XXX maybe unnecessary ? */
- if ( (to = strcasestr(ptr, "to-tag=") ) )
- totag = to + 7; /* skip the keyword */
- else if ( (to = strcasestr(ptr, "from-tag=") ) ) {
- fromtag = to + 9; /* skip the keyword */
- fromtag = strsep(&fromtag, "&"); /* trim what ? */
- }
- }
- if (sipdebug)
- ast_debug(4, "Invite/replaces: Will use Replace-Call-ID : %s Fromtag: %s Totag: %s\n",
- replace_id,
- fromtag ? fromtag : "<no from tag>",
- totag ? totag : "<no to tag>");
- /* Try to find call that we are replacing.
- If we have a Replaces header, we need to cancel that call if we succeed with this call.
- First we cheat a little and look for a magic call-id from phones that support
- dialog-info+xml so we can do technology independent pickup... */
- if (strncmp(replace_id, "pickup-", 7) == 0) {
- RAII_VAR(struct sip_pvt *, subscription, NULL, ao2_cleanup);
- RAII_VAR(struct ast_channel *, subscription_chan, NULL, ao2_cleanup);
- replace_id += 7; /* Worst case we are looking at \0 */
- if (get_sip_pvt_from_replaces(replace_id, totag, fromtag, &subscription, &subscription_chan)) {
- ast_log(LOG_NOTICE, "Unable to find subscription with call-id: %s\n", replace_id);
- transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req);
- error = 1;
- } else {
- SCOPED_LOCK(lock, subscription, sip_pvt_lock, sip_pvt_unlock);
- ast_log(LOG_NOTICE, "Trying to pick up %s@%s\n", subscription->exten, subscription->context);
- ast_copy_string(pickup.exten, subscription->exten, sizeof(pickup.exten));
- ast_copy_string(pickup.context, subscription->context, sizeof(pickup.context));
- }
- }
- if (!error && ast_strlen_zero(pickup.exten) && get_sip_pvt_from_replaces(replace_id,
- totag, fromtag, &replaces_pvt, &replaces_chan)) {
- ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existent call id (%s)!\n", replace_id);
- transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req);
- error = 1;
- }
- /* The matched call is the call from the transferer to Asterisk .
- We want to bridge the bridged part of the call to the
- incoming invite, thus taking over the refered call */
- if (replaces_pvt == p) {
- ast_log(LOG_NOTICE, "INVITE with replaces into it's own call id (%s == %s)!\n", replace_id, p->callid);
- transmit_response_reliable(p, "400 Bad request", req); /* The best way to not not accept the transfer */
- error = 1;
- }
- if (!error && ast_strlen_zero(pickup.exten) && !replaces_chan) {
- /* Oops, someting wrong anyway, no owner, no call */
- ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existing call id (%s)!\n", replace_id);
- /* Check for better return code */
- transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replace)", req);
- error = 1;
- }
- if (!error && ast_strlen_zero(pickup.exten) &&
- ast_channel_state(replaces_chan) != AST_STATE_RINGING &&
- ast_channel_state(replaces_chan) != AST_STATE_RING &&
- ast_channel_state(replaces_chan) != AST_STATE_UP) {
- ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-ringing or active call id (%s)!\n", replace_id);
- transmit_response_reliable(p, "603 Declined (Replaces)", req);
- error = 1;
- }
- if (error) { /* Give up this dialog */
- append_history(p, "Xfer", "INVITE/Replace Failed.");
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- p->invitestate = INV_COMPLETED;
- res = INV_REQ_ERROR;
- check_via(p, req);
- copy_request(&p->initreq, req);
- goto request_invite_cleanup;
- }
- }
- /* Check if this is an INVITE that sets up a new dialog or
- a re-invite in an existing dialog */
- if (!req->ignore) {
- int newcall = (p->initreq.headers ? TRUE : FALSE);
- if (sip_cancel_destroy(p))
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- /* This also counts as a pending invite */
- p->pendinginvite = seqno;
- check_via(p, req);
- copy_request(&p->initreq, req); /* Save this INVITE as the transaction basis */
- if (sipdebug)
- ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
- if (!p->owner) { /* Not a re-invite */
- if (req->debug)
- ast_verbose("Using INVITE request as basis request - %s\n", p->callid);
- if (newcall)
- append_history(p, "Invite", "New call: %s", p->callid);
- parse_ok_contact(p, req);
- } else { /* Re-invite on existing call */
- ast_clear_flag(&p->flags[0], SIP_OUTGOING); /* This is now an inbound dialog */
- if (get_rpid(p, req)) {
- struct ast_party_connected_line connected;
- struct ast_set_party_connected_line update_connected;
- ast_party_connected_line_init(&connected);
- memset(&update_connected, 0, sizeof(update_connected));
- update_connected.id.number = 1;
- connected.id.number.valid = 1;
- connected.id.number.str = (char *) p->cid_num;
- connected.id.number.presentation = p->callingpres;
- update_connected.id.name = 1;
- connected.id.name.valid = 1;
- connected.id.name.str = (char *) p->cid_name;
- connected.id.name.presentation = p->callingpres;
- /* Invalidate any earlier private connected id representation */
- ast_set_party_id_all(&update_connected.priv);
- connected.id.tag = (char *) p->cid_tag;
- connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
- ast_channel_queue_connected_line_update(p->owner, &connected,
- &update_connected);
- }
- /* Handle SDP here if we already have an owner */
- if (find_sdp(req)) {
- if (process_sdp(p, req, SDP_T38_INITIATE)) {
- if (!ast_strlen_zero(sip_get_header(req, "Content-Encoding"))) {
- /* Asterisk does not yet support any Content-Encoding methods. Always
- * attempt to process the sdp, but return a 415 if a Content-Encoding header
- * was present after processing failed. */
- transmit_response_reliable(p, "415 Unsupported Media type", req);
- } else {
- transmit_response_reliable(p, "488 Not acceptable here", req);
- }
- if (!p->lastinvite)
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- res = INV_REQ_ERROR;
- goto request_invite_cleanup;
- }
- ast_queue_control(p->owner, AST_CONTROL_SRCUPDATE);
- } else {
- ast_format_cap_remove_by_type(p->jointcaps, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_append_from_cap(p->jointcaps, p->caps, AST_MEDIA_TYPE_UNKNOWN);
- ast_debug(1, "Hm.... No sdp for the moment\n");
- /* Some devices signal they want to be put off hold by sending a re-invite
- *without* an SDP, which is supposed to mean "Go back to your state"
- and since they put os on remote hold, we go back to off hold */
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
- ast_queue_unhold(p->owner);
- /* Activate a re-invite */
- ast_queue_frame(p->owner, &ast_null_frame);
- change_hold_state(p, req, FALSE, 0);
- }
- }
- if (p->do_history) /* This is a response, note what it was for */
- append_history(p, "ReInv", "Re-invite received");
- }
- } else if (req->debug)
- ast_verbose("Ignoring this INVITE request\n");
- if (!p->lastinvite && !req->ignore && !p->owner) {
- /* This is a new invite */
- /* Handle authentication if this is our first invite */
- int cc_recall_core_id = -1;
- set_pvt_allowed_methods(p, req);
- res = check_user_full(p, req, SIP_INVITE, e, XMIT_RELIABLE, addr, &authpeer);
- if (res == AUTH_CHALLENGE_SENT) {
- p->invitestate = INV_COMPLETED; /* Needs to restart in another INVITE transaction */
- goto request_invite_cleanup;
- }
- if (res < 0) { /* Something failed in authentication */
- ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", sip_get_header(req, "From"));
- transmit_response_reliable(p, "403 Forbidden", req);
- p->invitestate = INV_COMPLETED;
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- goto request_invite_cleanup;
- }
- /* Successful authentication and peer matching so record the peer related to this pvt (for easy access to peer settings) */
- if (p->relatedpeer) {
- p->relatedpeer = sip_unref_peer(p->relatedpeer,"unsetting the relatedpeer field in the dialog, before it is set to something else.");
- }
- if (authpeer) {
- p->relatedpeer = sip_ref_peer(authpeer, "setting dialog's relatedpeer pointer");
- }
- req->authenticated = 1;
- /* We have a successful authentication, process the SDP portion if there is one */
- if (find_sdp(req)) {
- if (process_sdp(p, req, SDP_T38_INITIATE)) {
- /* Asterisk does not yet support any Content-Encoding methods. Always
- * attempt to process the sdp, but return a 415 if a Content-Encoding header
- * was present after processing fails. */
- if (!ast_strlen_zero(sip_get_header(req, "Content-Encoding"))) {
- transmit_response_reliable(p, "415 Unsupported Media type", req);
- } else {
- /* Unacceptable codecs */
- transmit_response_reliable(p, "488 Not acceptable here", req);
- }
- p->invitestate = INV_COMPLETED;
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- ast_debug(1, "No compatible codecs for this SIP call.\n");
- res = INV_REQ_ERROR;
- goto request_invite_cleanup;
- }
- } else { /* No SDP in invite, call control session */
- ast_format_cap_remove_by_type(p->jointcaps, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_append_from_cap(p->jointcaps, p->caps, AST_MEDIA_TYPE_UNKNOWN);
- ast_debug(2, "No SDP in Invite, third party call control\n");
- }
- /* Initialize the context if it hasn't been already */
- if (ast_strlen_zero(p->context))
- ast_string_field_set(p, context, sip_cfg.default_context);
- /* Check number of concurrent calls -vs- incoming limit HERE */
- ast_debug(1, "Checking SIP call limits for device %s\n", p->username);
- if ((res = update_call_counter(p, INC_CALL_LIMIT))) {
- if (res < 0) {
- ast_log(LOG_NOTICE, "Failed to place call for device %s, too many calls\n", p->username);
- transmit_response_reliable(p, "480 Temporarily Unavailable (Call limit) ", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- p->invitestate = INV_COMPLETED;
- res = AUTH_SESSION_LIMIT;
- }
- goto request_invite_cleanup;
- }
- gotdest = get_destination(p, NULL, &cc_recall_core_id); /* Get destination right away */
- extract_uri(p, req); /* Get the Contact URI */
- build_contact(p, req, 1); /* Build our contact header */
- if (p->rtp) {
- ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
- ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
- }
- if (!replace_id && (gotdest != SIP_GET_DEST_EXTEN_FOUND)) { /* No matching extension found */
- switch(gotdest) {
- case SIP_GET_DEST_INVALID_URI:
- transmit_response_reliable(p, "416 Unsupported URI scheme", req);
- break;
- case SIP_GET_DEST_EXTEN_MATCHMORE:
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)
- == SIP_PAGE2_ALLOWOVERLAP_YES) {
- transmit_response_reliable(p, "484 Address Incomplete", req);
- break;
- }
- /*
- * XXX We would have to implement collecting more digits in
- * chan_sip for any other schemes of overlap dialing.
- *
- * For SIP_PAGE2_ALLOWOVERLAP_DTMF it is better to do this in
- * the dialplan using the Incomplete application rather than
- * having the channel driver do it.
- */
- /* Fall through */
- case SIP_GET_DEST_EXTEN_NOT_FOUND:
- {
- char *decoded_exten = ast_strdupa(p->exten);
- transmit_response_reliable(p, "404 Not Found", req);
- ast_uri_decode(decoded_exten, ast_uri_sip_user);
- ast_log(LOG_NOTICE, "Call from '%s' (%s) to extension"
- " '%s' rejected because extension not found in context '%s'.\n",
- S_OR(p->username, p->peername), ast_sockaddr_stringify(&p->recv), decoded_exten, p->context);
- }
- break;
- case SIP_GET_DEST_REFUSED:
- default:
- transmit_response_reliable(p, "403 Forbidden", req);
- } /* end switch */
- p->invitestate = INV_COMPLETED;
- update_call_counter(p, DEC_CALL_LIMIT);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- res = INV_REQ_FAILED;
- goto request_invite_cleanup;
- } else {
- /* If no extension was specified, use the s one */
- /* Basically for calling to IP/Host name only */
- if (ast_strlen_zero(p->exten))
- ast_string_field_set(p, exten, "s");
- /* Initialize our tag */
- make_our_tag(p);
- if (handle_request_invite_st(p, req, required, reinvite)) {
- p->invitestate = INV_COMPLETED;
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- res = INV_REQ_ERROR;
- goto request_invite_cleanup;
- }
- /* First invitation - create the channel. Allocation
- * failures are handled below. */
- c = sip_new(p, AST_STATE_DOWN, S_OR(p->peername, NULL), NULL, NULL, p->logger_callid);
- if (cc_recall_core_id != -1) {
- ast_setup_cc_recall_datastore(c, cc_recall_core_id);
- ast_cc_agent_set_interfaces_chanvar(c);
- }
- *recount = 1;
- /* Save Record-Route for any later requests we make on this dialogue */
- build_route(p, req, 0, 0);
- if (c) {
- ast_party_redirecting_init(&redirecting);
- memset(&update_redirecting, 0, sizeof(update_redirecting));
- change_redirecting_information(p, req, &redirecting, &update_redirecting,
- FALSE); /*Will return immediately if no Diversion header is present */
- ast_channel_set_redirecting(c, &redirecting, &update_redirecting);
- ast_party_redirecting_free(&redirecting);
- }
- }
- } else {
- ast_party_redirecting_init(&redirecting);
- memset(&update_redirecting, 0, sizeof(update_redirecting));
- if (sipdebug) {
- if (!req->ignore)
- ast_debug(2, "Got a SIP re-invite for call %s\n", p->callid);
- else
- ast_debug(2, "Got a SIP re-transmit of INVITE for call %s\n", p->callid);
- }
- if (!req->ignore)
- reinvite = 1;
- if (handle_request_invite_st(p, req, required, reinvite)) {
- p->invitestate = INV_COMPLETED;
- if (!p->lastinvite) {
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- }
- res = INV_REQ_ERROR;
- goto request_invite_cleanup;
- }
- c = p->owner;
- change_redirecting_information(p, req, &redirecting, &update_redirecting, FALSE); /*Will return immediately if no Diversion header is present */
- if (c) {
- ast_channel_set_redirecting(c, &redirecting, &update_redirecting);
- }
- ast_party_redirecting_free(&redirecting);
- }
- /* Check if OLI/ANI-II is present in From: */
- parse_oli(req, p->owner);
- if (reinvite && p->stimer->st_active == TRUE) {
- restart_session_timer(p);
- }
- if (!req->ignore && p)
- p->lastinvite = seqno;
- if (c && replace_id) { /* Attended transfer or call pickup - we're the target */
- if (!ast_strlen_zero(pickup.exten)) {
- append_history(p, "Xfer", "INVITE/Replace received");
- /* Let the caller know we're giving it a shot */
- transmit_response(p, "100 Trying", req);
- p->invitestate = INV_PROCEEDING;
- ast_setstate(c, AST_STATE_RING);
- /* Do the pickup itself */
- ast_channel_unlock(c);
- *nounlock = 1;
- /* since p->owner (c) is unlocked, we need to go ahead and unlock pvt for both
- * magic pickup and ast_hangup. Both of these functions will attempt to lock
- * p->owner again, which can cause a deadlock if we already hold a lock on p.
- * Locking order is, channel then pvt. Dead lock avoidance must be used if
- * called the other way around. */
- sip_pvt_unlock(p);
- do_magic_pickup(c, pickup.exten, pickup.context);
- /* Now we're either masqueraded or we failed to pickup, in either case we... */
- ast_hangup(c);
- sip_pvt_lock(p); /* pvt is expected to remain locked on return, so re-lock it */
- res = INV_REQ_FAILED;
- goto request_invite_cleanup;
- } else {
- /* Go and take over the target call */
- if (sipdebug)
- ast_debug(4, "Sending this call to the invite/replaces handler %s\n", p->callid);
- res = handle_invite_replaces(p, req, nounlock, replaces_pvt, replaces_chan);
- goto request_invite_cleanup;
- }
- }
- if (c) { /* We have a call -either a new call or an old one (RE-INVITE) */
- enum ast_channel_state c_state = ast_channel_state(c);
- RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, ast_get_chan_features_pickup_config(c), ao2_cleanup);
- const char *pickupexten;
- if (!pickup_cfg) {
- ast_log(LOG_ERROR, "Unable to retrieve pickup configuration options. Unable to detect call pickup extension\n");
- pickupexten = "";
- } else {
- pickupexten = ast_strdupa(pickup_cfg->pickupexten);
- }
- if (c_state != AST_STATE_UP && reinvite &&
- (p->invitestate == INV_TERMINATED || p->invitestate == INV_CONFIRMED)) {
- /* If these conditions are true, and the channel is still in the 'ringing'
- * state, then this likely means that we have a situation where the initial
- * INVITE transaction has completed *but* the channel's state has not yet been
- * changed to UP. The reason this could happen is if the reinvite is received
- * on the SIP socket prior to an application calling ast_read on this channel
- * to read the answer frame we earlier queued on it. In this case, the reinvite
- * is completely legitimate so we need to handle this the same as if the channel
- * were already UP. Thus we are purposely falling through to the AST_STATE_UP case.
- */
- c_state = AST_STATE_UP;
- }
- switch(c_state) {
- case AST_STATE_DOWN:
- ast_debug(2, "%s: New call is still down.... Trying... \n", ast_channel_name(c));
- transmit_provisional_response(p, "100 Trying", req, 0);
- p->invitestate = INV_PROCEEDING;
- ast_setstate(c, AST_STATE_RING);
- if (strcmp(p->exten, pickupexten)) { /* Call to extension -start pbx on this call */
- enum ast_pbx_result result;
- result = ast_pbx_start(c);
- switch(result) {
- case AST_PBX_FAILED:
- sip_alreadygone(p);
- ast_log(LOG_WARNING, "Failed to start PBX :(\n");
- p->invitestate = INV_COMPLETED;
- transmit_response_reliable(p, "503 Unavailable", req);
- break;
- case AST_PBX_CALL_LIMIT:
- sip_alreadygone(p);
- ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
- p->invitestate = INV_COMPLETED;
- transmit_response_reliable(p, "480 Temporarily Unavailable", req);
- res = AUTH_SESSION_LIMIT;
- break;
- case AST_PBX_SUCCESS:
- /* nothing to do */
- break;
- }
- if (result) {
- /* Unlock locks so ast_hangup can do its magic */
- ast_channel_unlock(c);
- *nounlock = 1;
- sip_pvt_unlock(p);
- ast_hangup(c);
- sip_pvt_lock(p);
- c = NULL;
- }
- } else { /* Pickup call in call group */
- if (sip_pickup(c)) {
- ast_log(LOG_WARNING, "Failed to start Group pickup by %s\n", ast_channel_name(c));
- transmit_response_reliable(p, "480 Temporarily Unavailable", req);
- sip_alreadygone(p);
- ast_channel_hangupcause_set(c, AST_CAUSE_FAILURE);
- /* Unlock locks so ast_hangup can do its magic */
- ast_channel_unlock(c);
- *nounlock = 1;
- p->invitestate = INV_COMPLETED;
- sip_pvt_unlock(p);
- ast_hangup(c);
- sip_pvt_lock(p);
- c = NULL;
- }
- }
- break;
- case AST_STATE_RING:
- transmit_provisional_response(p, "100 Trying", req, 0);
- p->invitestate = INV_PROCEEDING;
- break;
- case AST_STATE_RINGING:
- transmit_provisional_response(p, "180 Ringing", req, 0);
- p->invitestate = INV_PROCEEDING;
- break;
- case AST_STATE_UP:
- ast_debug(2, "%s: This call is UP.... \n", ast_channel_name(c));
- transmit_response(p, "100 Trying", req);
- if (p->t38.state == T38_PEER_REINVITE) {
- if (p->t38id > -1) {
- /* reset t38 abort timer */
- AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "remove ref for t38id"));
- }
- p->t38id = ast_sched_add(sched, 5000, sip_t38_abort, dialog_ref(p, "passing dialog ptr into sched structure based on t38id for sip_t38_abort."));
- } else if (p->t38.state == T38_ENABLED) {
- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- transmit_response_with_t38_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL)));
- } else if ((p->t38.state == T38_DISABLED) || (p->t38.state == T38_REJECTED)) {
- /* If this is not a re-invite or something to ignore - it's critical */
- if (p->srtp && !ast_test_flag(p->srtp, AST_SRTP_CRYPTO_OFFER_OK)) {
- ast_log(LOG_WARNING, "Target does not support required crypto\n");
- transmit_response_reliable(p, "488 Not Acceptable Here (crypto)", req);
- } else {
- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- transmit_response_with_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL)), p->session_modify == TRUE ? FALSE : TRUE, FALSE);
- ast_queue_control(p->owner, AST_CONTROL_UPDATE_RTP_PEER);
- }
- }
- p->invitestate = INV_TERMINATED;
- break;
- default:
- ast_log(LOG_WARNING, "Don't know how to handle INVITE in state %u\n", ast_channel_state(c));
- transmit_response(p, "100 Trying", req);
- break;
- }
- } else {
- if (!req->ignore && p && (p->autokillid == -1)) {
- const char *msg;
- if ((!ast_format_cap_count(p->jointcaps)))
- msg = "488 Not Acceptable Here (codec error)";
- else {
- ast_log(LOG_NOTICE, "Unable to create/find SIP channel for this INVITE\n");
- msg = "503 Unavailable";
- }
- transmit_response_reliable(p, msg, req);
- p->invitestate = INV_COMPLETED;
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- }
- }
- request_invite_cleanup:
- if (authpeer) {
- authpeer = sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_invite authpeer");
- }
- return res;
- }
- /*! \brief Check for the presence of OLI tag(s) in the From header and set on the channel
- */
- static void parse_oli(struct sip_request *req, struct ast_channel *chan)
- {
- const char *from = NULL;
- const char *s = NULL;
- int ani2 = 0;
- if (!chan || !req) {
- /* null pointers are not helpful */
- return;
- }
- from = sip_get_header(req, "From");
- if (ast_strlen_zero(from)) {
- /* no From header */
- return;
- }
- /* Look for the possible OLI tags. */
- if ((s = strcasestr(from, ";isup-oli="))) {
- s += 10;
- } else if ((s = strcasestr(from, ";ss7-oli="))) {
- s += 9;
- } else if ((s = strcasestr(from, ";oli="))) {
- s += 5;
- }
- if (ast_strlen_zero(s)) {
- /* OLI tag is missing, or present with nothing following the '=' sign */
- return;
- }
- /* just in case OLI is quoted */
- if (*s == '\"') {
- s++;
- }
- if (sscanf(s, "%d", &ani2)) {
- ast_channel_caller(chan)->ani2 = ani2;
- }
- return;
- }
- /*! \brief Find all call legs and bridge transferee with target
- * called from handle_request_refer
- *
- * \note this function assumes two locks to begin with, sip_pvt transferer and current.chan1 (the pvt's owner)...
- * 2 additional locks are held at the beginning of the function, targetcall_pvt, and targetcall_pvt's owner
- * channel (which is stored in target.chan1). These 2 locks _MUST_ be let go by the end of the function. Do
- * not be confused into thinking a pvt's owner is the same thing as the channels locked at the beginning of
- * this function, after the masquerade this may not be true. Be consistent and unlock only the exact same
- * pointers that were locked to begin with.
- *
- * If this function is successful, only the transferer pvt lock will remain on return. Setting nounlock indicates
- * to handle_request_do() that the pvt's owner it locked does not require an unlock.
- */
- static int local_attended_transfer(struct sip_pvt *transferer, struct ast_channel *transferer_chan, uint32_t seqno, int *nounlock)
- {
- RAII_VAR(struct sip_pvt *, targetcall_pvt, NULL, ao2_cleanup);
- RAII_VAR(struct ast_channel *, targetcall_chan, NULL, ao2_cleanup);
- enum ast_transfer_result transfer_res;
- /* Check if the call ID of the replaces header does exist locally */
- if (get_sip_pvt_from_replaces(transferer->refer->replaces_callid,
- transferer->refer->replaces_callid_totag,
- transferer->refer->replaces_callid_fromtag,
- &targetcall_pvt, &targetcall_chan)) {
- if (transferer->refer->localtransfer) {
- /* We did not find the refered call. Sorry, can't accept then */
- /* Let's fake a response from someone else in order
- to follow the standard */
- transmit_notify_with_sipfrag(transferer, seqno, "481 Call leg/transaction does not exist", TRUE);
- append_history(transferer, "Xfer", "Refer failed");
- ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
- transferer->refer->status = REFER_FAILED;
- return -1;
- }
- /* Fall through for remote transfers that we did not find locally */
- ast_debug(3, "SIP attended transfer: Not our call - generating INVITE with replaces\n");
- return 0;
- }
- if (!targetcall_chan) { /* No active channel */
- ast_debug(4, "SIP attended transfer: Error: No owner of target call\n");
- /* Cancel transfer */
- transmit_notify_with_sipfrag(transferer, seqno, "503 Service Unavailable", TRUE);
- append_history(transferer, "Xfer", "Refer failed");
- ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
- transferer->refer->status = REFER_FAILED;
- return -1;
- }
- ast_set_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */
- sip_pvt_unlock(transferer);
- ast_channel_unlock(transferer_chan);
- *nounlock = 1;
- transfer_res = ast_bridge_transfer_attended(transferer_chan, targetcall_chan);
- sip_pvt_lock(transferer);
- switch (transfer_res) {
- case AST_BRIDGE_TRANSFER_SUCCESS:
- transferer->refer->status = REFER_200OK;
- transmit_notify_with_sipfrag(transferer, seqno, "200 OK", TRUE);
- append_history(transferer, "Xfer", "Refer succeeded");
- return 1;
- case AST_BRIDGE_TRANSFER_FAIL:
- transferer->refer->status = REFER_FAILED;
- transmit_notify_with_sipfrag(transferer, seqno, "500 Internal Server Error", TRUE);
- append_history(transferer, "Xfer", "Refer failed (internal error)");
- ast_clear_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
- return -1;
- case AST_BRIDGE_TRANSFER_INVALID:
- transferer->refer->status = REFER_FAILED;
- transmit_notify_with_sipfrag(transferer, seqno, "503 Service Unavailable", TRUE);
- append_history(transferer, "Xfer", "Refer failed (invalid bridge state)");
- ast_clear_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
- return -1;
- case AST_BRIDGE_TRANSFER_NOT_PERMITTED:
- transferer->refer->status = REFER_FAILED;
- transmit_notify_with_sipfrag(transferer, seqno, "403 Forbidden", TRUE);
- append_history(transferer, "Xfer", "Refer failed (operation not permitted)");
- ast_clear_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
- return -1;
- default:
- break;
- }
- return 1;
- }
- /*!
- * Data to set on a channel that runs dialplan
- * at the completion of a blind transfer
- */
- struct blind_transfer_cb_data {
- /*! Contents of the REFER's Referred-by header */
- const char *referred_by;
- /*! Domain of the URI in the REFER's Refer-To header */
- const char *domain;
- /*! Contents of what to place in a Replaces header of an INVITE */
- const char *replaces;
- /*! Redirecting information to set on the channel */
- struct ast_party_redirecting redirecting;
- /*! Parts of the redirecting structure that are to be updated */
- struct ast_set_party_redirecting update_redirecting;
- };
- /*!
- * \internal
- * \brief Callback called on new outbound channel during blind transfer
- *
- * We use this opportunity to populate the channel with data from the REFER
- * so that, if necessary, we can include proper information on any new INVITE
- * we may send out.
- *
- * \param chan The new outbound channel
- * \param user_data A blind_transfer_cb_data struct
- * \param transfer_type Unused
- */
- static void blind_transfer_cb(struct ast_channel *chan, struct transfer_channel_data *user_data_wrapper,
- enum ast_transfer_type transfer_type)
- {
- struct blind_transfer_cb_data *cb_data = user_data_wrapper->data;
- pbx_builtin_setvar_helper(chan, "SIPTRANSFER", "yes");
- pbx_builtin_setvar_helper(chan, "SIPTRANSFER_REFERER", cb_data->referred_by);
- pbx_builtin_setvar_helper(chan, "SIPTRANSFER_REPLACES", cb_data->replaces);
- pbx_builtin_setvar_helper(chan, "SIPDOMAIN", cb_data->domain);
- ast_channel_update_redirecting(chan, &cb_data->redirecting, &cb_data->update_redirecting);
- }
- /*! \brief Handle incoming REFER request */
- /*! \page SIP_REFER SIP transfer Support (REFER)
- REFER is used for call transfer in SIP. We get a REFER
- to place a new call with an INVITE somwhere and then
- keep the transferor up-to-date of the transfer. If the
- transfer fails, get back on line with the orginal call.
- - REFER can be sent outside or inside of a dialog.
- Asterisk only accepts REFER inside of a dialog.
- - If we get a replaces header, it is an attended transfer
- \par Blind transfers
- The transferor provides the transferee
- with the transfer targets contact. The signalling between
- transferer or transferee should not be cancelled, so the
- call is recoverable if the transfer target can not be reached
- by the transferee.
- In this case, Asterisk receives a TRANSFER from
- the transferor, thus is the transferee. We should
- try to set up a call to the contact provided
- and if that fails, re-connect the current session.
- If the new call is set up, we issue a hangup.
- In this scenario, we are following section 5.2
- in the SIP CC Transfer draft. (Transfer without
- a GRUU)
- \par Transfer with consultation hold
- In this case, the transferor
- talks to the transfer target before the transfer takes place.
- This is implemented with SIP hold and transfer.
- Note: The invite From: string could indicate a transfer.
- (Section 6. Transfer with consultation hold)
- The transferor places the transferee on hold, starts a call
- with the transfer target to alert them to the impending
- transfer, terminates the connection with the target, then
- proceeds with the transfer (as in Blind transfer above)
- \par Attended transfer
- The transferor places the transferee
- on hold, calls the transfer target to alert them,
- places the target on hold, then proceeds with the transfer
- using a Replaces header field in the Refer-to header. This
- will force the transfee to send an Invite to the target,
- with a replaces header that instructs the target to
- hangup the call between the transferor and the target.
- In this case, the Refer/to: uses the AOR address. (The same
- URI that the transferee used to establish the session with
- the transfer target (To: ). The Require: replaces header should
- be in the INVITE to avoid the wrong UA in a forked SIP proxy
- scenario to answer and have no call to replace with.
- The referred-by header is *NOT* required, but if we get it,
- can be copied into the INVITE to the transfer target to
- inform the target about the transferor
- "Any REFER request has to be appropriately authenticated.".
- We can't destroy dialogs, since we want the call to continue.
- */
- static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, uint32_t seqno, int *nounlock)
- {
- char *refer_to = NULL;
- char *refer_to_context = NULL;
- int res = 0;
- struct blind_transfer_cb_data cb_data;
- enum ast_transfer_result transfer_res;
- RAII_VAR(struct ast_channel *, transferer, NULL, ast_channel_cleanup);
- RAII_VAR(struct ast_str *, replaces_str, NULL, ast_free_ptr);
- if (req->debug) {
- ast_verbose("Call %s got a SIP call transfer from %s: (REFER)!\n",
- p->callid,
- ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "callee" : "caller");
- }
- if (!p->owner) {
- /* This is a REFER outside of an existing SIP dialog */
- /* We can't handle that, so decline it */
- ast_debug(3, "Call %s: Declined REFER, outside of dialog...\n", p->callid);
- transmit_response(p, "603 Declined (No dialog)", req);
- if (!req->ignore) {
- append_history(p, "Xfer", "Refer failed. Outside of dialog.");
- sip_alreadygone(p);
- pvt_set_needdestroy(p, "outside of dialog");
- }
- return 0;
- }
- /* Check if transfer is allowed from this device */
- if (p->allowtransfer == TRANSFER_CLOSED ) {
- /* Transfer not allowed, decline */
- transmit_response(p, "603 Declined (policy)", req);
- append_history(p, "Xfer", "Refer failed. Allowtransfer == closed.");
- /* Do not destroy SIP session */
- return 0;
- }
- if (!req->ignore && ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
- /* Already have a pending REFER */
- transmit_response(p, "491 Request pending", req);
- append_history(p, "Xfer", "Refer failed. Request pending.");
- return 0;
- }
- /* Allocate memory for call transfer data */
- if (!sip_refer_alloc(p)) {
- transmit_response(p, "500 Internal Server Error", req);
- append_history(p, "Xfer", "Refer failed. Memory allocation error.");
- return -3;
- }
- res = get_refer_info(p, req); /* Extract headers */
- p->refer->status = REFER_SENT;
- if (res != 0) {
- switch (res) {
- case -2: /* Syntax error */
- transmit_response(p, "400 Bad Request (Refer-to missing)", req);
- append_history(p, "Xfer", "Refer failed. Refer-to missing.");
- if (req->debug) {
- ast_debug(1, "SIP transfer to black hole can't be handled (no refer-to: )\n");
- }
- break;
- case -3:
- transmit_response(p, "603 Declined (Non sip: uri)", req);
- append_history(p, "Xfer", "Refer failed. Non SIP uri");
- if (req->debug) {
- ast_debug(1, "SIP transfer to non-SIP uri denied\n");
- }
- break;
- default:
- /* Refer-to extension not found, fake a failed transfer */
- transmit_response(p, "202 Accepted", req);
- append_history(p, "Xfer", "Refer failed. Bad extension.");
- transmit_notify_with_sipfrag(p, seqno, "404 Not found", TRUE);
- ast_clear_flag(&p->flags[0], SIP_GOTREFER);
- if (req->debug) {
- ast_debug(1, "SIP transfer to bad extension: %s\n", p->refer->refer_to);
- }
- break;
- }
- return 0;
- }
- if (ast_strlen_zero(p->context)) {
- ast_string_field_set(p, context, sip_cfg.default_context);
- }
- /* If we do not support SIP domains, all transfers are local */
- if (sip_cfg.allow_external_domains && check_sip_domain(p->refer->refer_to_domain, NULL, 0)) {
- p->refer->localtransfer = 1;
- if (sipdebug) {
- ast_debug(3, "This SIP transfer is local : %s\n", p->refer->refer_to_domain);
- }
- } else if (AST_LIST_EMPTY(&domain_list) || check_sip_domain(p->refer->refer_to_domain, NULL, 0)) {
- /* This PBX doesn't bother with SIP domains or domain is local, so this transfer is local */
- p->refer->localtransfer = 1;
- } else if (sipdebug) {
- ast_debug(3, "This SIP transfer is to a remote SIP extension (remote domain %s)\n", p->refer->refer_to_domain);
- }
- /* Is this a repeat of a current request? Ignore it */
- /* Don't know what else to do right now. */
- if (req->ignore) {
- return 0;
- }
- /* Get the transferer's channel */
- transferer = ast_channel_ref(p->owner);
- if (sipdebug) {
- ast_debug(3, "SIP %s transfer: Transferer channel %s\n",
- p->refer->attendedtransfer ? "attended" : "blind",
- ast_channel_name(transferer));
- }
- ast_set_flag(&p->flags[0], SIP_GOTREFER);
- /* From here on failures will be indicated with NOTIFY requests */
- transmit_response(p, "202 Accepted", req);
- /* Attended transfer: Find all call legs and bridge transferee with target*/
- if (p->refer->attendedtransfer) {
- /* both p and p->owner _MUST_ be locked while calling local_attended_transfer */
- if ((res = local_attended_transfer(p, transferer, seqno, nounlock))) {
- ast_clear_flag(&p->flags[0], SIP_GOTREFER);
- return res;
- }
- /* Fall through for remote transfers that we did not find locally */
- if (sipdebug) {
- ast_debug(4, "SIP attended transfer: Still not our call - generating INVITE with replaces\n");
- }
- /* Fallthrough if we can't find the call leg internally */
- }
- /* Copy data we can not safely access after letting the pvt lock go. */
- refer_to = ast_strdupa(p->refer->refer_to);
- refer_to_context = ast_strdupa(p->refer->refer_to_context);
- ast_party_redirecting_init(&cb_data.redirecting);
- memset(&cb_data.update_redirecting, 0, sizeof(cb_data.update_redirecting));
- change_redirecting_information(p, req, &cb_data.redirecting, &cb_data.update_redirecting, 0);
- cb_data.domain = ast_strdupa(p->refer->refer_to_domain);
- cb_data.referred_by = ast_strdupa(p->refer->referred_by);
- if (!ast_strlen_zero(p->refer->replaces_callid)) {
- replaces_str = ast_str_create(128);
- if (!replaces_str) {
- ast_log(LOG_NOTICE, "Unable to create Replaces string for remote attended transfer. Transfer failed\n");
- ast_clear_flag(&p->flags[0], SIP_GOTREFER);
- ast_party_redirecting_free(&cb_data.redirecting);
- return -1;
- }
- ast_str_append(&replaces_str, 0, "%s%s%s%s%s", p->refer->replaces_callid,
- !ast_strlen_zero(p->refer->replaces_callid_totag) ? ";to-tag=" : "",
- S_OR(p->refer->replaces_callid_totag, ""),
- !ast_strlen_zero(p->refer->replaces_callid_fromtag) ? ";from-tag=" : "",
- S_OR(p->refer->replaces_callid_fromtag, ""));
- cb_data.replaces = ast_str_buffer(replaces_str);
- } else {
- cb_data.replaces = NULL;
- }
- if (!*nounlock) {
- ast_channel_unlock(p->owner);
- *nounlock = 1;
- }
- ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
- sip_pvt_unlock(p);
- transfer_res = ast_bridge_transfer_blind(1, transferer, refer_to, refer_to_context, blind_transfer_cb, &cb_data);
- sip_pvt_lock(p);
- switch (transfer_res) {
- case AST_BRIDGE_TRANSFER_INVALID:
- res = -1;
- p->refer->status = REFER_FAILED;
- transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable (can't handle one-legged xfers)", TRUE);
- append_history(p, "Xfer", "Refer failed (only bridged calls).");
- ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
- break;
- case AST_BRIDGE_TRANSFER_NOT_PERMITTED:
- res = -1;
- p->refer->status = REFER_FAILED;
- transmit_notify_with_sipfrag(p, seqno, "403 Forbidden", TRUE);
- append_history(p, "Xfer", "Refer failed (bridge does not permit transfers)");
- ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
- break;
- case AST_BRIDGE_TRANSFER_FAIL:
- res = -1;
- p->refer->status = REFER_FAILED;
- transmit_notify_with_sipfrag(p, seqno, "500 Internal Server Error", TRUE);
- append_history(p, "Xfer", "Refer failed (internal error)");
- ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
- break;
- case AST_BRIDGE_TRANSFER_SUCCESS:
- res = 0;
- p->refer->status = REFER_200OK;
- transmit_notify_with_sipfrag(p, seqno, "200 OK", TRUE);
- append_history(p, "Xfer", "Refer succeeded.");
- break;
- default:
- break;
- }
- ast_clear_flag(&p->flags[0], SIP_GOTREFER);
- ast_party_redirecting_free(&cb_data.redirecting);
- return res;
- }
- /*! \brief Handle incoming CANCEL request */
- static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req)
- {
- check_via(p, req);
- sip_alreadygone(p);
- if (p->owner && ast_channel_state(p->owner) == AST_STATE_UP) {
- /* This call is up, cancel is ignored, we need a bye */
- transmit_response(p, "200 OK", req);
- ast_debug(1, "Got CANCEL on an answered call. Ignoring... \n");
- return 0;
- }
- /* At this point, we could have cancelled the invite at the same time
- as the other side sends a CANCEL. Our final reply with error code
- might not have been received by the other side before the CANCEL
- was sent, so let's just give up retransmissions and waiting for
- ACK on our error code. The call is hanging up any way. */
- if (p->invitestate == INV_TERMINATED || p->invitestate == INV_COMPLETED) {
- __sip_pretend_ack(p);
- }
- if (p->invitestate != INV_TERMINATED)
- p->invitestate = INV_CANCELLED;
- if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD))
- update_call_counter(p, DEC_CALL_LIMIT);
- stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
- if (p->owner) {
- sip_queue_hangup_cause(p, 0);
- } else {
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- }
- if (ast_str_strlen(p->initreq.data) > 0) {
- struct sip_pkt *pkt, *prev_pkt;
- /* If the CANCEL we are receiving is a retransmission, and we already have scheduled
- * a reliable 487, then we don't want to schedule another one on top of the previous
- * one.
- *
- * As odd as this may sound, we can't rely on the previously-transmitted "reliable"
- * response in this situation. What if we've sent all of our reliable responses
- * already and now all of a sudden, we get this second CANCEL?
- *
- * The only way to do this correctly is to cancel our previously-scheduled reliably-
- * transmitted response and send a new one in its place.
- */
- for (pkt = p->packets, prev_pkt = NULL; pkt; prev_pkt = pkt, pkt = pkt->next) {
- if (pkt->seqno == p->lastinvite && pkt->response_code == 487) {
- AST_SCHED_DEL(sched, pkt->retransid);
- UNLINK(pkt, p->packets, prev_pkt);
- dialog_unref(pkt->owner, "unref packet->owner from dialog");
- if (pkt->data) {
- ast_free(pkt->data);
- }
- ast_free(pkt);
- break;
- }
- }
- transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
- transmit_response(p, "200 OK", req);
- return 1;
- } else {
- transmit_response(p, "481 Call Leg Does Not Exist", req);
- return 0;
- }
- }
- /*! \brief Handle incoming BYE request */
- static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
- {
- struct ast_channel *c=NULL;
- int res;
- const char *required;
- RAII_VAR(struct ast_channel *, peer_channel, NULL, ast_channel_cleanup);
- char quality_buf[AST_MAX_USER_FIELD], *quality;
- /* If we have an INCOMING invite that we haven't answered, terminate that transaction */
- if (p->pendinginvite && !ast_test_flag(&p->flags[0], SIP_OUTGOING) && !req->ignore) {
- transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
- }
- __sip_pretend_ack(p);
- p->invitestate = INV_TERMINATED;
- copy_request(&p->initreq, req);
- if (sipdebug)
- ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
- check_via(p, req);
- sip_alreadygone(p);
- if (p->owner) {
- RAII_VAR(struct ast_channel *, owner_relock, NULL, ast_channel_cleanup);
- RAII_VAR(struct ast_channel *, owner_ref, NULL, ast_channel_cleanup);
- /* Grab a reference to p->owner to prevent it from going away */
- owner_ref = ast_channel_ref(p->owner);
- /* Established locking order here is bridge, channel, pvt
- * and the bridge will be locked during ast_channel_bridge_peer */
- ast_channel_unlock(owner_ref);
- sip_pvt_unlock(p);
- peer_channel = ast_channel_bridge_peer(owner_ref);
- owner_relock = sip_pvt_lock_full(p);
- if (!owner_relock) {
- ast_debug(3, "Unable to reacquire owner channel lock, channel is gone\n");
- return 0;
- }
- }
- /* Get RTCP quality before end of call */
- if (p->rtp) {
- if (p->do_history) {
- if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
- append_history(p, "RTCPaudio", "Quality:%s", quality);
- }
- if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER, quality_buf, sizeof(quality_buf)))) {
- append_history(p, "RTCPaudioJitter", "Quality:%s", quality);
- }
- if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS, quality_buf, sizeof(quality_buf)))) {
- append_history(p, "RTCPaudioLoss", "Quality:%s", quality);
- }
- if ((quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT, quality_buf, sizeof(quality_buf)))) {
- append_history(p, "RTCPaudioRTT", "Quality:%s", quality);
- }
- }
- if (p->owner) {
- RAII_VAR(struct ast_channel *, owner_relock, NULL, ast_channel_cleanup);
- RAII_VAR(struct ast_channel *, owner_ref, NULL, ast_channel_cleanup);
- struct ast_rtp_instance *p_rtp;
- /* Grab a reference to p->owner to prevent it from going away */
- owner_ref = ast_channel_ref(p->owner);
- p_rtp = p->rtp;
- ao2_ref(p_rtp, +1);
- /* Established locking order here is bridge, channel, pvt
- * and the bridge and channel will be locked during
- * ast_rtp_instance_set_stats_vars */
- ast_channel_unlock(owner_ref);
- sip_pvt_unlock(p);
- ast_rtp_instance_set_stats_vars(owner_ref, p_rtp);
- ao2_ref(p_rtp, -1);
- if (peer_channel) {
- ast_channel_lock(peer_channel);
- if (IS_SIP_TECH(ast_channel_tech(peer_channel))) {
- struct sip_pvt *peer_pvt;
- peer_pvt = ast_channel_tech_pvt(peer_channel);
- if (peer_pvt) {
- ao2_ref(peer_pvt, +1);
- sip_pvt_lock(peer_pvt);
- if (peer_pvt->rtp) {
- struct ast_rtp_instance *peer_rtp;
- peer_rtp = peer_pvt->rtp;
- ao2_ref(peer_rtp, +1);
- ast_channel_unlock(peer_channel);
- sip_pvt_unlock(peer_pvt);
- ast_rtp_instance_set_stats_vars(peer_channel, peer_rtp);
- ao2_ref(peer_rtp, -1);
- ast_channel_lock(peer_channel);
- sip_pvt_lock(peer_pvt);
- }
- sip_pvt_unlock(peer_pvt);
- ao2_ref(peer_pvt, -1);
- }
- }
- ast_channel_unlock(peer_channel);
- }
- owner_relock = sip_pvt_lock_full(p);
- if (!owner_relock) {
- ast_debug(3, "Unable to reacquire owner channel lock, channel is gone\n");
- return 0;
- }
- }
- }
- if (p->vrtp && (quality = ast_rtp_instance_get_quality(p->vrtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
- if (p->do_history) {
- append_history(p, "RTCPvideo", "Quality:%s", quality);
- }
- if (p->owner) {
- pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", quality);
- }
- }
- if (p->trtp && (quality = ast_rtp_instance_get_quality(p->trtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
- if (p->do_history) {
- append_history(p, "RTCPtext", "Quality:%s", quality);
- }
- if (p->owner) {
- pbx_builtin_setvar_helper(p->owner, "RTPTEXTQOS", quality);
- }
- }
- stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
- if (p->stimer) {
- stop_session_timer(p); /* Stop Session-Timer */
- }
- if (!ast_strlen_zero(sip_get_header(req, "Also"))) {
- ast_log(LOG_NOTICE, "Client '%s' using deprecated BYE/Also transfer method. Ask vendor to support REFER instead\n",
- ast_sockaddr_stringify(&p->recv));
- if (ast_strlen_zero(p->context))
- ast_string_field_set(p, context, sip_cfg.default_context);
- res = get_also_info(p, req);
- if (!res) {
- c = p->owner;
- if (c) {
- if (peer_channel) {
- RAII_VAR(struct ast_channel *, owner_relock, NULL, ast_channel_cleanup);
- char *local_context = ast_strdupa(p->context);
- char *local_refer_to = ast_strdupa(p->refer->refer_to);
- /* Grab a reference to p->owner to prevent it from going away */
- ast_channel_ref(c);
- /* Don't actually hangup here... */
- ast_queue_unhold(c);
- ast_channel_unlock(c); /* async_goto can do a masquerade, no locks can be held during a masq */
- sip_pvt_unlock(p);
- ast_async_goto(peer_channel, local_context, local_refer_to, 1);
- owner_relock = sip_pvt_lock_full(p);
- ast_channel_cleanup(c);
- if (!owner_relock) {
- ast_debug(3, "Unable to reacquire owner channel lock, channel is gone\n");
- return 0;
- }
- } else {
- ast_queue_hangup(p->owner);
- }
- }
- } else {
- ast_log(LOG_WARNING, "Invalid transfer information from '%s'\n", ast_sockaddr_stringify(&p->recv));
- if (p->owner)
- ast_queue_hangup_with_cause(p->owner, AST_CAUSE_PROTOCOL_ERROR);
- }
- } else if (p->owner) {
- sip_queue_hangup_cause(p, 0);
- sip_scheddestroy_final(p, DEFAULT_TRANS_TIMEOUT);
- ast_debug(3, "Received bye, issuing owner hangup\n");
- } else {
- sip_scheddestroy_final(p, DEFAULT_TRANS_TIMEOUT);
- ast_debug(3, "Received bye, no owner, selfdestruct soon.\n");
- }
- ast_clear_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- /* Find out what they require */
- required = sip_get_header(req, "Require");
- if (!ast_strlen_zero(required)) {
- char unsupported[256] = { 0, };
- parse_sip_options(required, unsupported, ARRAY_LEN(unsupported));
- /* If there are any options required that we do not support,
- * then send a 420 with only those unsupported options listed */
- if (!ast_strlen_zero(unsupported)) {
- transmit_response_with_unsupported(p, "420 Bad extension (unsupported)", req, unsupported);
- ast_log(LOG_WARNING, "Received SIP BYE with unsupported required extension: required:%s unsupported:%s\n", required, unsupported);
- } else {
- transmit_response(p, "200 OK", req);
- }
- } else {
- transmit_response(p, "200 OK", req);
- }
- /* Destroy any pending invites so we won't try to do another
- * scheduled reINVITE. */
- AST_SCHED_DEL_UNREF(sched, p->waitid, dialog_unref(p, "decrement refcount from sip_destroy because waitid won't be scheduled"));
- return 1;
- }
- /*! \brief Handle incoming MESSAGE request */
- static int handle_request_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e)
- {
- if (!req->ignore) {
- if (req->debug)
- ast_verbose("Receiving message!\n");
- receive_message(p, req, addr, e);
- } else
- transmit_response(p, "202 Accepted", req);
- return 1;
- }
- static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *from);
- static const struct ast_msg_tech sip_msg_tech = {
- .name = "sip",
- .msg_send = sip_msg_send,
- };
- /*!
- * \internal
- * \brief Check if the given header name is blocked.
- *
- * \details Determine if the given header name from the user is
- * blocked for outgoing MESSAGE packets.
- *
- * \param header_name Name of header to see if it is blocked.
- *
- * \retval TRUE if the given header is blocked.
- */
- static int block_msg_header(const char *header_name)
- {
- int idx;
- /*
- * Don't block Content-Type or Max-Forwards headers because the
- * user can override them.
- */
- static const char *hdr[] = {
- "To",
- "From",
- "Via",
- "Route",
- "Contact",
- "Call-ID",
- "CSeq",
- "Allow",
- "Content-Length",
- "Request-URI",
- };
- for (idx = 0; idx < ARRAY_LEN(hdr); ++idx) {
- if (!strcasecmp(header_name, hdr[idx])) {
- /* Block addition of this header. */
- return 1;
- }
- }
- return 0;
- }
- static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *from)
- {
- struct sip_pvt *pvt;
- int res;
- char *to_uri;
- char *to_host;
- char *to_user;
- const char *var;
- const char *val;
- struct ast_msg_var_iterator *iter;
- struct sip_peer *peer_ptr;
- if (!(pvt = sip_alloc(NULL, NULL, 0, SIP_MESSAGE, NULL, 0))) {
- return -1;
- }
- for (iter = ast_msg_var_iterator_init(msg);
- ast_msg_var_iterator_next(msg, iter, &var, &val);
- ast_msg_var_unref_current(iter)) {
- if (!strcasecmp(var, "Request-URI")) {
- ast_string_field_set(pvt, fullcontact, val);
- break;
- }
- }
- ast_msg_var_iterator_destroy(iter);
- to_uri = ast_strdupa(to);
- to_uri = get_in_brackets(to_uri);
- parse_uri(to_uri, "sip:,sips:", &to_user, NULL, &to_host, NULL);
- if (ast_strlen_zero(to_host)) {
- ast_log(LOG_WARNING, "MESSAGE(to) is invalid for SIP - '%s'\n", to);
- dialog_unlink_all(pvt);
- dialog_unref(pvt, "MESSAGE(to) is invalid for SIP");
- return -1;
- }
- if (!ast_strlen_zero(from)) {
- if ((peer_ptr = sip_find_peer(from, NULL, 0, 1, 0, 0))) {
- ast_string_field_set(pvt, fromname, S_OR(peer_ptr->cid_name, peer_ptr->name));
- ast_string_field_set(pvt, fromuser, S_OR(peer_ptr->cid_num, peer_ptr->name));
- sip_unref_peer(peer_ptr, "sip_unref_peer, from sip_msg_send, sip_find_peer");
- } else if (strchr(from, '<')) { /* from is callerid-style */
- char *sender;
- char *name = NULL, *location = NULL, *user = NULL, *domain = NULL;
- sender = ast_strdupa(from);
- ast_callerid_parse(sender, &name, &location);
- if (ast_strlen_zero(location)) {
- /* This can occur if either
- * 1) A name-addr style From header does not close the angle brackets
- * properly.
- * 2) The From header is not in name-addr style and the content of the
- * From contains characters other than 0-9, *, #, or +.
- *
- * In both cases, ast_callerid_parse() should have parsed the From header
- * as a name rather than a number. So we just need to set the location
- * to what was parsed as a name, and set the name NULL since there was
- * no name present.
- */
- location = name;
- name = NULL;
- }
- ast_string_field_set(pvt, fromname, name);
- if (strchr(location, ':')) { /* Must be a URI */
- parse_uri(location, "sip:,sips:", &user, NULL, &domain, NULL);
- SIP_PEDANTIC_DECODE(user);
- SIP_PEDANTIC_DECODE(domain);
- extract_host_from_hostport(&domain);
- ast_string_field_set(pvt, fromuser, user);
- ast_string_field_set(pvt, fromdomain, domain);
- } else { /* Treat it as an exten/user */
- ast_string_field_set(pvt, fromuser, location);
- }
- } else { /* assume we just have the name, use defaults for the rest */
- ast_string_field_set(pvt, fromname, from);
- }
- }
- sip_pvt_lock(pvt);
- /* Look up the host to contact */
- if (create_addr(pvt, to_host, NULL, TRUE)) {
- sip_pvt_unlock(pvt);
- dialog_unlink_all(pvt);
- dialog_unref(pvt, "create_addr failed sending a MESSAGE");
- return -1;
- }
- if (!ast_strlen_zero(to_user)) {
- ast_string_field_set(pvt, username, to_user);
- }
- ast_sip_ouraddrfor(&pvt->sa, &pvt->ourip, pvt);
- build_via(pvt);
- ast_set_flag(&pvt->flags[0], SIP_OUTGOING);
- /* XXX Does pvt->expiry need to be set? */
- /* Save additional MESSAGE headers in case of authentication request. */
- for (iter = ast_msg_var_iterator_init(msg);
- ast_msg_var_iterator_next(msg, iter, &var, &val);
- ast_msg_var_unref_current(iter)) {
- if (!strcasecmp(var, "Max-Forwards")) {
- /* Decrement Max-Forwards for SIP loop prevention. */
- if (sscanf(val, "%30d", &pvt->maxforwards) != 1 || pvt->maxforwards < 1) {
- ast_msg_var_iterator_destroy(iter);
- sip_pvt_unlock(pvt);
- dialog_unlink_all(pvt);
- dialog_unref(pvt, "MESSAGE(Max-Forwards) reached zero.");
- ast_log(LOG_NOTICE,
- "MESSAGE(Max-Forwards) reached zero. MESSAGE not sent.\n");
- return -1;
- }
- --pvt->maxforwards;
- continue;
- }
- if (block_msg_header(var)) {
- /* Block addition of this header. */
- continue;
- }
- add_msg_header(pvt, var, val);
- }
- ast_msg_var_iterator_destroy(iter);
- ast_string_field_set(pvt, msg_body, ast_msg_get_body(msg));
- res = transmit_message(pvt, 1, 0);
- sip_pvt_unlock(pvt);
- sip_scheddestroy(pvt, DEFAULT_TRANS_TIMEOUT);
- dialog_unref(pvt, "sent a MESSAGE");
- return res;
- }
- static enum sip_publish_type determine_sip_publish_type(struct sip_request *req, const char * const event, const char * const etag, const char * const expires, int *expires_int)
- {
- int etag_present = !ast_strlen_zero(etag);
- int body_present = req->lines > 0;
- ast_assert(expires_int != NULL);
- if (ast_strlen_zero(expires)) {
- /* Section 6, item 4, second bullet point of RFC 3903 says to
- * use a locally-configured default expiration if none is provided
- * in the request
- */
- *expires_int = DEFAULT_PUBLISH_EXPIRES;
- } else if (sscanf(expires, "%30d", expires_int) != 1) {
- return SIP_PUBLISH_UNKNOWN;
- }
- if (*expires_int == 0) {
- return SIP_PUBLISH_REMOVE;
- } else if (!etag_present && body_present) {
- return SIP_PUBLISH_INITIAL;
- } else if (etag_present && !body_present) {
- return SIP_PUBLISH_REFRESH;
- } else if (etag_present && body_present) {
- return SIP_PUBLISH_MODIFY;
- }
- return SIP_PUBLISH_UNKNOWN;
- }
- #ifdef HAVE_LIBXML2
- static int pidf_validate_tuple(struct ast_xml_node *tuple_node)
- {
- const char *id;
- int status_found = FALSE;
- struct ast_xml_node *tuple_children;
- struct ast_xml_node *tuple_children_iterator;
- /* Tuples have to have an id attribute or they're invalid */
- if (!(id = ast_xml_get_attribute(tuple_node, "id"))) {
- ast_log(LOG_WARNING, "Tuple XML element has no attribute 'id'\n");
- return FALSE;
- }
- /* We don't care what it actually is, just that it's there */
- ast_xml_free_attr(id);
- /* This is a tuple. It must have a status element */
- if (!(tuple_children = ast_xml_node_get_children(tuple_node))) {
- /* The tuple has no children. It sucks */
- ast_log(LOG_WARNING, "Tuple XML element has no child elements\n");
- return FALSE;
- }
- for (tuple_children_iterator = tuple_children; tuple_children_iterator;
- tuple_children_iterator = ast_xml_node_get_next(tuple_children_iterator)) {
- /* Similar to the wording used regarding tuples, the status element should appear
- * first. However, we will once again relax things and accept the status at any
- * position. We will enforce that only a single status element can be present.
- */
- if (strcmp(ast_xml_node_get_name(tuple_children_iterator), "status")) {
- /* Not the status, we don't care */
- continue;
- }
- if (status_found == TRUE) {
- /* THERE CAN BE ONLY ONE!!! */
- ast_log(LOG_WARNING, "Multiple status elements found in tuple. Only one allowed\n");
- return FALSE;
- }
- status_found = TRUE;
- }
- return status_found;
- }
- static int pidf_validate_presence(struct ast_xml_doc *doc)
- {
- struct ast_xml_node *presence_node = ast_xml_get_root(doc);
- struct ast_xml_node *child_nodes;
- struct ast_xml_node *node_iterator;
- struct ast_xml_ns *ns;
- const char *entity;
- const char *namespace;
- const char presence_namespace[] = "urn:ietf:params:xml:ns:pidf";
- if (!presence_node) {
- ast_log(LOG_WARNING, "Unable to retrieve root node of the XML document\n");
- return FALSE;
- }
- /* Okay, we managed to open the document! YAY! Now, let's start making sure it's all PIDF-ified
- * correctly.
- */
- if (strcmp(ast_xml_node_get_name(presence_node), "presence")) {
- ast_log(LOG_WARNING, "Root node of PIDF document is not 'presence'. Invalid\n");
- return FALSE;
- }
- /* The presence element must have an entity attribute and an xmlns attribute. Furthermore
- * the xmlns attribute must be "urn:ietf:params:xml:ns:pidf"
- */
- if (!(entity = ast_xml_get_attribute(presence_node, "entity"))) {
- ast_log(LOG_WARNING, "Presence element of PIDF document has no 'entity' attribute\n");
- return FALSE;
- }
- /* We're not interested in what the entity is, just that it exists */
- ast_xml_free_attr(entity);
- if (!(ns = ast_xml_find_namespace(doc, presence_node, NULL))) {
- ast_log(LOG_WARNING, "Couldn't find default namespace...\n");
- return FALSE;
- }
- namespace = ast_xml_get_ns_href(ns);
- if (ast_strlen_zero(namespace) || strcmp(namespace, presence_namespace)) {
- ast_log(LOG_WARNING, "PIDF document has invalid namespace value %s\n", namespace);
- return FALSE;
- }
- if (!(child_nodes = ast_xml_node_get_children(presence_node))) {
- ast_log(LOG_WARNING, "PIDF document has no elements as children of 'presence'. Invalid\n");
- return FALSE;
- }
- /* Check for tuple elements. RFC 3863 says that PIDF documents can have any number of
- * tuples, including 0. The big thing here is that if there are tuple elements present,
- * they have to have a single status element within.
- *
- * The RFC is worded such that tuples should appear as the first elements as children of
- * the presence element. However, we'll be accepting of documents which may place other elements
- * before the tuple(s).
- */
- for (node_iterator = child_nodes; node_iterator;
- node_iterator = ast_xml_node_get_next(node_iterator)) {
- if (strcmp(ast_xml_node_get_name(node_iterator), "tuple")) {
- /* Not a tuple. We don't give a rat's hind quarters */
- continue;
- }
- if (pidf_validate_tuple(node_iterator) == FALSE) {
- ast_log(LOG_WARNING, "Unable to validate tuple\n");
- return FALSE;
- }
- }
- return TRUE;
- }
- /*!
- * \brief Makes sure that body is properly formatted PIDF
- *
- * Specifically, we check that the document has a "presence" element
- * at the root and that within that, there is at least one "tuple" element
- * that contains a "status" element.
- *
- * XXX This function currently assumes a default namespace is used. Of course
- * if you're not using a default namespace, you're probably a stupid jerk anyway.
- *
- * \param req The SIP request to check
- * \param[out] pidf_doc The validated PIDF doc.
- * \retval FALSE The XML was malformed or the basic PIDF structure was marred
- * \retval TRUE The PIDF document is of a valid format
- */
- static int sip_pidf_validate(struct sip_request *req, struct ast_xml_doc **pidf_doc)
- {
- struct ast_xml_doc *doc;
- const char *content_type = sip_get_header(req, "Content-Type");
- char *pidf_body;
- int res;
- if (ast_strlen_zero(content_type) || strcmp(content_type, "application/pidf+xml")) {
- ast_log(LOG_WARNING, "Content type is not PIDF\n");
- return FALSE;
- }
- if (!(pidf_body = get_content(req))) {
- ast_log(LOG_WARNING, "Unable to get PIDF body\n");
- return FALSE;
- }
- if (!(doc = ast_xml_read_memory(pidf_body, strlen(pidf_body)))) {
- ast_log(LOG_WARNING, "Unable to open XML PIDF document. Is it malformed?\n");
- return FALSE;
- }
- res = pidf_validate_presence(doc);
- if (res == TRUE) {
- *pidf_doc = doc;
- } else {
- ast_xml_close(doc);
- }
- return res;
- }
- static int cc_esc_publish_handler(struct sip_pvt *pvt, struct sip_request *req, struct event_state_compositor *esc, struct sip_esc_entry *esc_entry)
- {
- const char *uri = REQ_OFFSET_TO_STR(req, rlpart2);
- struct ast_cc_agent *agent;
- struct sip_cc_agent_pvt *agent_pvt;
- struct ast_xml_doc *pidf_doc = NULL;
- const char *basic_status = NULL;
- struct ast_xml_node *presence_node;
- struct ast_xml_node *presence_children;
- struct ast_xml_node *tuple_node;
- struct ast_xml_node *tuple_children;
- struct ast_xml_node *status_node;
- struct ast_xml_node *status_children;
- struct ast_xml_node *basic_node;
- int res = 0;
- if (!((agent = find_sip_cc_agent_by_notify_uri(uri)) || (agent = find_sip_cc_agent_by_subscribe_uri(uri)))) {
- ast_log(LOG_WARNING, "Could not find agent using uri '%s'\n", uri);
- transmit_response(pvt, "412 Conditional Request Failed", req);
- return -1;
- }
- agent_pvt = agent->private_data;
- if (sip_pidf_validate(req, &pidf_doc) == FALSE) {
- res = -1;
- goto cc_publish_cleanup;
- }
- /* It's important to note that the PIDF validation routine has no knowledge
- * of what we specifically want in this instance. A valid PIDF document could
- * have no tuples, or it could have tuples whose status element has no basic
- * element contained within. While not violating the PIDF spec, these are
- * insufficient for our needs in this situation
- */
- presence_node = ast_xml_get_root(pidf_doc);
- if (!(presence_children = ast_xml_node_get_children(presence_node))) {
- ast_log(LOG_WARNING, "No tuples within presence element.\n");
- res = -1;
- goto cc_publish_cleanup;
- }
- if (!(tuple_node = ast_xml_find_element(presence_children, "tuple", NULL, NULL))) {
- ast_log(LOG_NOTICE, "Couldn't find tuple node?\n");
- res = -1;
- goto cc_publish_cleanup;
- }
- /* We already made sure that the tuple has a status node when we validated the PIDF
- * document earlier. So there's no need to enclose this operation in an if statement.
- */
- tuple_children = ast_xml_node_get_children(tuple_node);
- /* coverity[null_returns: FALSE] */
- status_node = ast_xml_find_element(tuple_children, "status", NULL, NULL);
- if (!(status_children = ast_xml_node_get_children(status_node))) {
- ast_log(LOG_WARNING, "No basic elements within status element.\n");
- res = -1;
- goto cc_publish_cleanup;
- }
- if (!(basic_node = ast_xml_find_element(status_children, "basic", NULL, NULL))) {
- ast_log(LOG_WARNING, "Couldn't find basic node?\n");
- res = -1;
- goto cc_publish_cleanup;
- }
- basic_status = ast_xml_get_text(basic_node);
- if (ast_strlen_zero(basic_status)) {
- ast_log(LOG_NOTICE, "NOthing in basic node?\n");
- res = -1;
- goto cc_publish_cleanup;
- }
- if (!strcmp(basic_status, "open")) {
- agent_pvt->is_available = TRUE;
- ast_cc_agent_caller_available(agent->core_id, "Received PUBLISH stating SIP caller %s is available",
- agent->device_name);
- } else if (!strcmp(basic_status, "closed")) {
- agent_pvt->is_available = FALSE;
- ast_cc_agent_caller_busy(agent->core_id, "Received PUBLISH stating SIP caller %s is busy",
- agent->device_name);
- } else {
- ast_log(LOG_NOTICE, "Invalid content in basic element: %s\n", basic_status);
- }
- cc_publish_cleanup:
- if (basic_status) {
- ast_xml_free_text(basic_status);
- }
- if (pidf_doc) {
- ast_xml_close(pidf_doc);
- }
- ao2_ref(agent, -1);
- if (res) {
- transmit_response(pvt, "400 Bad Request", req);
- }
- return res;
- }
- #endif /* HAVE_LIBXML2 */
- static int handle_sip_publish_initial(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const int expires)
- {
- struct sip_esc_entry *esc_entry = create_esc_entry(esc, req, expires);
- int res = 0;
- if (!esc_entry) {
- transmit_response(p, "503 Internal Server Failure", req);
- return -1;
- }
- if (esc->callbacks->initial_handler) {
- res = esc->callbacks->initial_handler(p, req, esc, esc_entry);
- }
- if (!res) {
- transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 0);
- }
- ao2_ref(esc_entry, -1);
- return res;
- }
- static int handle_sip_publish_refresh(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const char * const etag, const int expires)
- {
- struct sip_esc_entry *esc_entry = get_esc_entry(etag, esc);
- int expires_ms = expires * 1000;
- int res = 0;
- if (!esc_entry) {
- transmit_response(p, "412 Conditional Request Failed", req);
- return -1;
- }
- AST_SCHED_REPLACE_UNREF(esc_entry->sched_id, sched, expires_ms, publish_expire, esc_entry,
- ao2_ref(_data, -1),
- ao2_ref(esc_entry, -1),
- ao2_ref(esc_entry, +1));
- if (esc->callbacks->refresh_handler) {
- res = esc->callbacks->refresh_handler(p, req, esc, esc_entry);
- }
- if (!res) {
- transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 1);
- }
- ao2_ref(esc_entry, -1);
- return res;
- }
- static int handle_sip_publish_modify(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const char * const etag, const int expires)
- {
- struct sip_esc_entry *esc_entry = get_esc_entry(etag, esc);
- int expires_ms = expires * 1000;
- int res = 0;
- if (!esc_entry) {
- transmit_response(p, "412 Conditional Request Failed", req);
- return -1;
- }
- AST_SCHED_REPLACE_UNREF(esc_entry->sched_id, sched, expires_ms, publish_expire, esc_entry,
- ao2_ref(_data, -1),
- ao2_ref(esc_entry, -1),
- ao2_ref(esc_entry, +1));
- if (esc->callbacks->modify_handler) {
- res = esc->callbacks->modify_handler(p, req, esc, esc_entry);
- }
- if (!res) {
- transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 1);
- }
- ao2_ref(esc_entry, -1);
- return res;
- }
- static int handle_sip_publish_remove(struct sip_pvt *p, struct sip_request *req, struct event_state_compositor *esc, const char * const etag)
- {
- struct sip_esc_entry *esc_entry = get_esc_entry(etag, esc);
- int res = 0;
- if (!esc_entry) {
- transmit_response(p, "412 Conditional Request Failed", req);
- return -1;
- }
- AST_SCHED_DEL(sched, esc_entry->sched_id);
- /* Scheduler's ref of the esc_entry */
- ao2_ref(esc_entry, -1);
- if (esc->callbacks->remove_handler) {
- res = esc->callbacks->remove_handler(p, req, esc, esc_entry);
- }
- if (!res) {
- transmit_response_with_sip_etag(p, "200 OK", req, esc_entry, 1);
- }
- /* Ref from finding the esc_entry earlier in function */
- ao2_unlink(esc->compositor, esc_entry);
- ao2_ref(esc_entry, -1);
- return res;
- }
- static int handle_request_publish(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const uint32_t seqno, const char *uri)
- {
- const char *etag = sip_get_header(req, "SIP-If-Match");
- const char *event = sip_get_header(req, "Event");
- struct event_state_compositor *esc;
- enum sip_publish_type publish_type;
- const char *expires_str = sip_get_header(req, "Expires");
- int expires_int;
- int auth_result;
- int handler_result = -1;
- if (ast_strlen_zero(event)) {
- transmit_response(p, "489 Bad Event", req);
- pvt_set_needdestroy(p, "missing Event: header");
- return -1;
- }
- if (!(esc = get_esc(event))) {
- transmit_response(p, "489 Bad Event", req);
- pvt_set_needdestroy(p, "unknown event package in publish");
- return -1;
- }
- auth_result = check_user(p, req, SIP_PUBLISH, uri, XMIT_UNRELIABLE, addr);
- if (auth_result == AUTH_CHALLENGE_SENT) {
- p->lastinvite = seqno;
- return 0;
- } else if (auth_result < 0) {
- ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", sip_get_header(req, "From"));
- transmit_response(p, "403 Forbidden", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- ast_string_field_set(p, theirtag, NULL);
- return 0;
- } else if (auth_result == AUTH_SUCCESSFUL && p->lastinvite) {
- /* We need to stop retransmitting the 401 */
- __sip_ack(p, p->lastinvite, 1, 0);
- }
- publish_type = determine_sip_publish_type(req, event, etag, expires_str, &expires_int);
- if (expires_int > max_expiry) {
- expires_int = max_expiry;
- } else if (expires_int < min_expiry && expires_int > 0) {
- transmit_response_with_minexpires(p, "423 Interval too small", req, min_expiry);
- pvt_set_needdestroy(p, "Expires is less that the min expires allowed.");
- return 0;
- }
- p->expiry = expires_int;
- /* It is the responsibility of these handlers to formulate any response
- * sent for a PUBLISH
- */
- switch (publish_type) {
- case SIP_PUBLISH_UNKNOWN:
- transmit_response(p, "400 Bad Request", req);
- break;
- case SIP_PUBLISH_INITIAL:
- handler_result = handle_sip_publish_initial(p, req, esc, expires_int);
- break;
- case SIP_PUBLISH_REFRESH:
- handler_result = handle_sip_publish_refresh(p, req, esc, etag, expires_int);
- break;
- case SIP_PUBLISH_MODIFY:
- handler_result = handle_sip_publish_modify(p, req, esc, etag, expires_int);
- break;
- case SIP_PUBLISH_REMOVE:
- handler_result = handle_sip_publish_remove(p, req, esc, etag);
- break;
- default:
- transmit_response(p, "400 Impossible Condition", req);
- break;
- }
- if (!handler_result && p->expiry > 0) {
- sip_scheddestroy(p, (p->expiry + 10) * 1000);
- } else {
- pvt_set_needdestroy(p, "forcing expiration");
- }
- return handler_result;
- }
- /*!
- * \internal
- * \brief Subscribe to MWI events for the specified peer
- *
- * \note The peer cannot be locked during this method. sip_send_mwi_peer will
- * attempt to lock the peer after the event subscription lock is held; if the peer is locked during
- * this method then we will attempt to lock the event subscription lock but after the peer, creating
- * a locking inversion.
- */
- static void add_peer_mwi_subs(struct sip_peer *peer)
- {
- struct sip_mailbox *mailbox;
- AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
- struct stasis_topic *mailbox_specific_topic;
- mailbox->event_sub = stasis_unsubscribe(mailbox->event_sub);
- mailbox_specific_topic = ast_mwi_topic(mailbox->id);
- if (mailbox_specific_topic) {
- char *peer_name = ast_strdup(peer->name);
- if (!peer_name) {
- return;
- }
- mailbox->event_sub = stasis_subscribe_pool(mailbox_specific_topic, mwi_event_cb, peer_name);
- }
- }
- }
- static int handle_cc_subscribe(struct sip_pvt *p, struct sip_request *req)
- {
- const char *uri = REQ_OFFSET_TO_STR(req, rlpart2);
- char *param_separator;
- struct ast_cc_agent *agent;
- struct sip_cc_agent_pvt *agent_pvt;
- const char *expires_str = sip_get_header(req, "Expires");
- int expires = -1; /* Just need it to be non-zero */
- if (!ast_strlen_zero(expires_str)) {
- sscanf(expires_str, "%30d", &expires);
- }
- if ((param_separator = strchr(uri, ';'))) {
- *param_separator = '\0';
- }
- p->subscribed = CALL_COMPLETION;
- if (!(agent = find_sip_cc_agent_by_subscribe_uri(uri))) {
- if (!expires) {
- /* Typically, if a 0 Expires reaches us and we can't find
- * the corresponding agent, it means that the CC transaction
- * has completed and so the calling side is just trying to
- * clean up its subscription. We'll just respond with a
- * 200 OK and be done with it
- */
- transmit_response(p, "200 OK", req);
- return 0;
- }
- ast_log(LOG_WARNING, "Invalid URI '%s' in CC subscribe\n", uri);
- transmit_response(p, "404 Not Found", req);
- return -1;
- }
- agent_pvt = agent->private_data;
- if (!expires) {
- /* We got sent a SUBSCRIBE and found an agent. This means that CC
- * is being canceled.
- */
- ast_cc_failed(agent->core_id, "CC is being canceled by %s", agent->device_name);
- transmit_response(p, "200 OK", req);
- ao2_ref(agent, -1);
- return 0;
- }
- agent_pvt->subscribe_pvt = dialog_ref(p, "SIP CC agent gains reference to subscription dialog");
- ast_cc_agent_accept_request(agent->core_id, "SIP caller %s has requested CC via SUBSCRIBE",
- agent->device_name);
- /* We don't send a response here. That is done in the agent's ack callback or in the
- * agent destructor, should a failure occur before we have responded
- */
- ao2_ref(agent, -1);
- return 0;
- }
- /*! \brief Handle incoming SUBSCRIBE request */
- static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, const char *e)
- {
- int res = 0;
- struct sip_peer *authpeer = NULL;
- char *event = ast_strdupa(sip_get_header(req, "Event")); /* Get Event package name */
- int resubscribe = (p->subscribed != NONE) && !req->ignore;
- char *options;
- if (p->initreq.headers) {
- /* We already have a dialog */
- if (p->initreq.method != SIP_SUBSCRIBE) {
- /* This is a SUBSCRIBE within another SIP dialog, which we do not support */
- /* For transfers, this could happen, but since we haven't seen it happening, let us just refuse this */
- transmit_response(p, "403 Forbidden (within dialog)", req);
- /* Do not destroy session, since we will break the call if we do */
- ast_debug(1, "Got a subscription within the context of another call, can't handle that - %s (Method %s)\n", p->callid, sip_methods[p->initreq.method].text);
- return 0;
- } else if (req->debug) {
- if (resubscribe)
- ast_debug(1, "Got a re-subscribe on existing subscription %s\n", p->callid);
- else
- ast_debug(1, "Got a new subscription %s (possibly with auth) or retransmission\n", p->callid);
- }
- }
- /* Check if we have a global disallow setting on subscriptions.
- if so, we don't have to check peer settings after auth, which saves a lot of processing
- */
- if (!sip_cfg.allowsubscribe) {
- transmit_response(p, "403 Forbidden (policy)", req);
- pvt_set_needdestroy(p, "forbidden");
- return 0;
- }
- if (!req->ignore && !resubscribe) { /* Set up dialog, new subscription */
- const char *to = sip_get_header(req, "To");
- char totag[128];
- set_pvt_allowed_methods(p, req);
- /* Check to see if a tag was provided, if so this is actually a resubscription of a dialog we no longer know about */
- if (!ast_strlen_zero(to) && gettag(req, "To", totag, sizeof(totag))) {
- if (req->debug)
- ast_verbose("Received resubscription for a dialog we no longer know about. Telling remote side to subscribe again.\n");
- transmit_response(p, "481 Subscription does not exist", req);
- pvt_set_needdestroy(p, "subscription does not exist");
- return 0;
- }
- /* Use this as the basis */
- if (req->debug)
- ast_verbose("Creating new subscription\n");
- copy_request(&p->initreq, req);
- if (sipdebug)
- ast_debug(4, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
- check_via(p, req);
- build_route(p, req, 0, 0);
- } else if (req->debug && req->ignore)
- ast_verbose("Ignoring this SUBSCRIBE request\n");
- /* Find parameters to Event: header value and remove them for now */
- if (ast_strlen_zero(event)) {
- transmit_response(p, "489 Bad Event", req);
- ast_debug(2, "Received SIP subscribe for unknown event package: <none>\n");
- pvt_set_needdestroy(p, "unknown event package in subscribe");
- return 0;
- }
- if ((options = strchr(event, ';')) != NULL) {
- *options++ = '\0';
- }
- /* Handle authentication if we're new and not a retransmission. We can't just
- * use if !req->ignore, because then we'll end up sending
- * a 200 OK if someone retransmits without sending auth */
- if (p->subscribed == NONE || resubscribe) {
- res = check_user_full(p, req, SIP_SUBSCRIBE, e, XMIT_UNRELIABLE, addr, &authpeer);
- /* if an authentication response was sent, we are done here */
- if (res == AUTH_CHALLENGE_SENT) /* authpeer = NULL here */
- return 0;
- if (res != AUTH_SUCCESSFUL) {
- ast_log(LOG_NOTICE, "Failed to authenticate device %s for SUBSCRIBE\n", sip_get_header(req, "From"));
- transmit_response(p, "403 Forbidden", req);
- pvt_set_needdestroy(p, "authentication failed");
- return 0;
- }
- }
- /* At this point, we hold a reference to authpeer (if not NULL). It
- * must be released when done.
- */
- /* Check if this device is allowed to subscribe at all */
- if (!ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) {
- transmit_response(p, "403 Forbidden (policy)", req);
- pvt_set_needdestroy(p, "subscription not allowed");
- if (authpeer) {
- sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 1)");
- }
- return 0;
- }
- /* Get full contact header - this needs to be used as a request URI in NOTIFY's */
- parse_ok_contact(p, req);
- build_contact(p, req, 1);
- /* Initialize tag for new subscriptions */
- if (ast_strlen_zero(p->tag)) {
- make_our_tag(p);
- }
- if (!strcmp(event, "presence") || !strcmp(event, "dialog")) { /* Presence, RFC 3842 */
- int gotdest;
- const char *accept;
- int start = 0;
- enum subscriptiontype subscribed = NONE;
- const char *unknown_accept = NULL;
- /* Get destination right away */
- gotdest = get_destination(p, NULL, NULL);
- if (gotdest != SIP_GET_DEST_EXTEN_FOUND) {
- if (gotdest == SIP_GET_DEST_INVALID_URI) {
- transmit_response(p, "416 Unsupported URI scheme", req);
- } else {
- transmit_response(p, "404 Not Found", req);
- }
- pvt_set_needdestroy(p, "subscription target not found");
- if (authpeer) {
- sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 2)");
- }
- return 0;
- }
- /* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */
- accept = __get_header(req, "Accept", &start);
- while ((subscribed == NONE) && !ast_strlen_zero(accept)) {
- if (strstr(accept, "application/pidf+xml")) {
- if (strstr(p->useragent, "Polycom")) {
- subscribed = XPIDF_XML; /* Older versions of Polycom firmware will claim pidf+xml, but really they only support xpidf+xml */
- } else {
- subscribed = PIDF_XML; /* RFC 3863 format */
- }
- } else if (strstr(accept, "application/dialog-info+xml")) {
- subscribed = DIALOG_INFO_XML;
- /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */
- } else if (strstr(accept, "application/cpim-pidf+xml")) {
- subscribed = CPIM_PIDF_XML; /* RFC 3863 format */
- } else if (strstr(accept, "application/xpidf+xml")) {
- subscribed = XPIDF_XML; /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */
- } else {
- unknown_accept = accept;
- }
- /* check to see if there is another Accept header present */
- accept = __get_header(req, "Accept", &start);
- }
- if (!start) {
- if (p->subscribed == NONE) { /* if the subscribed field is not already set, and there is no accept header... */
- transmit_response(p, "489 Bad Event", req);
- ast_log(LOG_WARNING,"SUBSCRIBE failure: no Accept header: pvt: "
- "stateid: %d, laststate: %d, dialogver: %u, subscribecont: "
- "'%s', subscribeuri: '%s'\n",
- p->stateid,
- p->laststate,
- p->dialogver,
- p->subscribecontext,
- p->subscribeuri);
- pvt_set_needdestroy(p, "no Accept header");
- if (authpeer) {
- sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 2)");
- }
- return 0;
- }
- /* if p->subscribed is non-zero, then accept is not obligatory; according to rfc 3265 section 3.1.3, at least.
- so, we'll just let it ride, keeping the value from a previous subscription, and not abort the subscription */
- } else if (subscribed == NONE) {
- /* Can't find a format for events that we know about */
- char buf[200];
- if (!ast_strlen_zero(unknown_accept)) {
- snprintf(buf, sizeof(buf), "489 Bad Event (format %s)", unknown_accept);
- } else {
- snprintf(buf, sizeof(buf), "489 Bad Event");
- }
- transmit_response(p, buf, req);
- ast_log(LOG_WARNING,"SUBSCRIBE failure: unrecognized format:"
- "'%s' pvt: subscribed: %d, stateid: %d, laststate: %d,"
- "dialogver: %u, subscribecont: '%s', subscribeuri: '%s'\n",
- unknown_accept,
- (int)p->subscribed,
- p->stateid,
- p->laststate,
- p->dialogver,
- p->subscribecontext,
- p->subscribeuri);
- pvt_set_needdestroy(p, "unrecognized format");
- if (authpeer) {
- sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 2)");
- }
- return 0;
- } else {
- p->subscribed = subscribed;
- }
- } else if (!strcmp(event, "message-summary")) {
- int start = 0;
- int found_supported = 0;
- const char *accept;
- accept = __get_header(req, "Accept", &start);
- while (!found_supported && !ast_strlen_zero(accept)) {
- found_supported = strcmp(accept, "application/simple-message-summary") ? 0 : 1;
- if (!found_supported) {
- ast_debug(3, "Received SIP mailbox subscription for unknown format: %s\n", accept);
- }
- accept = __get_header(req, "Accept", &start);
- }
- /* If !start, there is no Accept header at all */
- if (start && !found_supported) {
- /* Format requested that we do not support */
- transmit_response(p, "406 Not Acceptable", req);
- ast_debug(2, "Received SIP mailbox subscription for unknown format: %s\n", accept);
- pvt_set_needdestroy(p, "unknown format");
- if (authpeer) {
- sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 3)");
- }
- return 0;
- }
- /* Looks like they actually want a mailbox status
- This version of Asterisk supports mailbox subscriptions
- The subscribed URI needs to exist in the dial plan
- In most devices, this is configurable to the voicemailmain extension you use
- */
- if (!authpeer || AST_LIST_EMPTY(&authpeer->mailboxes)) {
- if (!authpeer) {
- transmit_response(p, "404 Not found", req);
- } else {
- transmit_response(p, "404 Not found (no mailbox)", req);
- ast_log(LOG_NOTICE, "Received SIP subscribe for peer without mailbox: %s\n", S_OR(authpeer->name, ""));
- }
- pvt_set_needdestroy(p, "received 404 response");
- if (authpeer) {
- sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 3)");
- }
- return 0;
- }
- p->subscribed = MWI_NOTIFICATION;
- if (ast_test_flag(&authpeer->flags[1], SIP_PAGE2_SUBSCRIBEMWIONLY)) {
- ao2_unlock(p);
- add_peer_mwi_subs(authpeer);
- ao2_lock(p);
- }
- if (authpeer->mwipvt != p) { /* Destroy old PVT if this is a new one */
- /* We only allow one subscription per peer */
- if (authpeer->mwipvt) {
- dialog_unlink_all(authpeer->mwipvt);
- authpeer->mwipvt = dialog_unref(authpeer->mwipvt, "unref dialog authpeer->mwipvt");
- }
- authpeer->mwipvt = dialog_ref(p, "setting peers' mwipvt to p");
- }
- if (p->relatedpeer != authpeer) {
- if (p->relatedpeer) {
- sip_unref_peer(p->relatedpeer, "Unref previously stored relatedpeer ptr");
- }
- p->relatedpeer = sip_ref_peer(authpeer, "setting dialog's relatedpeer pointer");
- }
- /* Do not release authpeer here */
- } else if (!strcmp(event, "call-completion")) {
- handle_cc_subscribe(p, req);
- } else { /* At this point, Asterisk does not understand the specified event */
- transmit_response(p, "489 Bad Event", req);
- ast_debug(2, "Received SIP subscribe for unknown event package: %s\n", event);
- pvt_set_needdestroy(p, "unknown event package");
- if (authpeer) {
- sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 5)");
- }
- return 0;
- }
- if (!req->ignore) {
- p->lastinvite = seqno;
- }
- if (!p->needdestroy) {
- p->expiry = atoi(sip_get_header(req, "Expires"));
- /* check if the requested expiry-time is within the approved limits from sip.conf */
- if (p->expiry > max_subexpiry) {
- p->expiry = max_subexpiry;
- } else if (p->expiry < min_subexpiry && p->expiry > 0) {
- transmit_response_with_minexpires(p, "423 Interval too small", req, min_subexpiry);
- ast_log(LOG_WARNING, "Received subscription for extension \"%s\" context \"%s\" "
- "with Expire header less than 'subminexpire' limit. Received \"Expire: %d\" min is %d\n",
- p->exten, p->context, p->expiry, min_subexpiry);
- pvt_set_needdestroy(p, "Expires is less that the min expires allowed.");
- if (authpeer) {
- sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 6)");
- }
- return 0;
- }
- if (sipdebug) {
- const char *action = p->expiry > 0 ? "Adding" : "Removing";
- if (p->subscribed == MWI_NOTIFICATION && p->relatedpeer) {
- ast_debug(2, "%s subscription for mailbox notification - peer %s\n",
- action, p->relatedpeer->name);
- } else if (p->subscribed == CALL_COMPLETION) {
- ast_debug(2, "%s CC subscription for peer %s\n", action, p->username);
- } else {
- ast_debug(2, "%s subscription for extension %s context %s for peer %s\n",
- action, p->exten, p->context, p->username);
- }
- }
- if (p->autokillid > -1 && sip_cancel_destroy(p)) /* Remove subscription expiry for renewals */
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- if (p->expiry > 0)
- sip_scheddestroy(p, (p->expiry + 10) * 1000); /* Set timer for destruction of call at expiration */
- if (p->subscribed == MWI_NOTIFICATION) {
- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- transmit_response(p, "200 OK", req);
- if (p->relatedpeer) { /* Send first notification */
- struct sip_peer *peer = p->relatedpeer;
- sip_ref_peer(peer, "ensure a peer ref is held during MWI sending");
- ao2_unlock(p);
- sip_send_mwi_to_peer(peer, 0);
- ao2_lock(p);
- sip_unref_peer(peer, "release a peer ref now that MWI is sent");
- }
- } else if (p->subscribed != CALL_COMPLETION) {
- struct state_notify_data data = { 0, };
- char *subtype = NULL;
- char *message = NULL;
- struct ao2_container *device_state_info = NULL;
- if (p->expiry > 0 && !resubscribe) {
- /* Add subscription for extension state from the PBX core */
- if (p->stateid != -1) {
- ast_extension_state_del(p->stateid, cb_extensionstate);
- }
- dialog_ref(p, "copying dialog ptr into extension state struct");
- p->stateid = ast_extension_state_add_destroy_extended(p->context, p->exten, cb_extensionstate, cb_extensionstate_destroy, p);
- if (p->stateid == -1) {
- dialog_unref(p, "copying dialog ptr into extension state struct failed");
- }
- }
- sip_pvt_unlock(p);
- data.state = ast_extension_state_extended(NULL, p->context, p->exten, &device_state_info);
- sip_pvt_lock(p);
- if (data.state < 0) {
- ao2_cleanup(device_state_info);
- if (p->expiry > 0) {
- ast_log(LOG_NOTICE, "Got SUBSCRIBE for extension %s@%s from %s, but there is no hint for that extension.\n", p->exten, p->context, ast_sockaddr_stringify(&p->sa));
- }
- transmit_response(p, "404 Not found", req);
- pvt_set_needdestroy(p, "no extension for SUBSCRIBE");
- if (authpeer) {
- sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 6)");
- }
- return 0;
- }
- if (allow_notify_user_presence(p)) {
- data.presence_state = ast_hint_presence_state(NULL, p->context, p->exten, &subtype, &message);
- data.presence_subtype = subtype;
- data.presence_message = message;
- }
- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- transmit_response(p, "200 OK", req);
- /* RFC 3265: A notification must be sent on every subscribe, so force it */
- data.device_state_info = device_state_info;
- if (data.state & AST_EXTENSION_RINGING) {
- /* save last_ringing_channel_time if this state really contains a ringing channel
- * because extensionstate_update() doesn't do it if forced
- */
- struct ast_channel *ringing = find_ringing_channel(data.device_state_info, p);
- if (ringing) {
- p->last_ringing_channel_time = ast_channel_creationtime(ringing);
- ao2_ref(ringing, -1);
- }
- /* If there is no channel, this likely indicates that the ringing indication
- * is due to a custom device state. These do not have associated channels.
- */
- }
- extensionstate_update(p->context, p->exten, &data, p, TRUE);
- append_history(p, "Subscribestatus", "%s", ast_extension_state2str(data.state));
- /* hide the 'complete' exten/context in the refer_to field for later display */
- ast_string_field_build(p, subscribeuri, "%s@%s", p->exten, p->context);
- /* Deleted the slow iteration of all sip dialogs to find old subscribes from this peer for exten@context */
- ao2_cleanup(device_state_info);
- ast_free(subtype);
- ast_free(message);
- }
- if (!p->expiry) {
- pvt_set_needdestroy(p, "forcing expiration");
- }
- }
- if (authpeer) {
- sip_unref_peer(authpeer, "unref pointer into (*authpeer)");
- }
- return 1;
- }
- /*! \brief Handle incoming REGISTER request */
- static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e)
- {
- enum check_auth_result res;
- /* If this is not the intial request, and the initial request isn't
- * a register, something screwy happened, so bail */
- if (p->initreq.headers && p->initreq.method != SIP_REGISTER) {
- ast_log(LOG_WARNING, "Ignoring spurious REGISTER with Call-ID: %s\n", p->callid);
- return -1;
- }
- /* Use this as the basis */
- copy_request(&p->initreq, req);
- if (sipdebug)
- ast_debug(4, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
- check_via(p, req);
- if ((res = register_verify(p, addr, req, e)) < 0) {
- const char *reason;
- switch (res) {
- case AUTH_SECRET_FAILED:
- reason = "Wrong password";
- break;
- case AUTH_USERNAME_MISMATCH:
- reason = "Username/auth name mismatch";
- break;
- case AUTH_NOT_FOUND:
- reason = "No matching peer found";
- break;
- case AUTH_UNKNOWN_DOMAIN:
- reason = "Not a local domain";
- break;
- case AUTH_PEER_NOT_DYNAMIC:
- reason = "Peer is not supposed to register";
- break;
- case AUTH_ACL_FAILED:
- reason = "Device does not match ACL";
- break;
- case AUTH_BAD_TRANSPORT:
- reason = "Device not configured to use this transport type";
- break;
- case AUTH_RTP_FAILED:
- reason = "RTP initialization failed";
- break;
- default:
- reason = "Unknown failure";
- break;
- }
- ast_log(LOG_NOTICE, "Registration from '%s' failed for '%s' - %s\n",
- sip_get_header(req, "To"), ast_sockaddr_stringify(addr),
- reason);
- append_history(p, "RegRequest", "Failed : Account %s : %s", sip_get_header(req, "To"), reason);
- } else {
- req->authenticated = 1;
- append_history(p, "RegRequest", "Succeeded : Account %s", sip_get_header(req, "To"));
- }
- if (res != AUTH_CHALLENGE_SENT) {
- /* Destroy the session, but keep us around for just a bit in case they don't
- get our 200 OK */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- }
- return res;
- }
- /*!
- * \brief Handle incoming SIP requests (methods)
- * \note
- * This is where all incoming requests go first.
- * \note
- * called with p and p->owner locked
- */
- static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, int *recount, int *nounlock)
- {
- /* Called with p->lock held, as well as p->owner->lock if appropriate, keeping things
- relatively static */
- const char *cmd;
- const char *cseq;
- const char *useragent;
- const char *via;
- const char *callid;
- int via_pos = 0;
- uint32_t seqno;
- int len;
- int respid;
- int res = 0;
- const char *e;
- int error = 0;
- int oldmethod = p->method;
- int acked = 0;
- /* RFC 3261 - 8.1.1 A valid SIP request must contain To, From, CSeq, Call-ID and Via.
- * 8.2.6.2 Response must have To, From, Call-ID CSeq, and Via related to the request,
- * so we can check to make sure these fields exist for all requests and responses */
- cseq = sip_get_header(req, "Cseq");
- cmd = REQ_OFFSET_TO_STR(req, header[0]);
- /* Save the via_pos so we can check later that responses only have 1 Via header */
- via = __get_header(req, "Via", &via_pos);
- /* This must exist already because we've called find_call by now */
- callid = sip_get_header(req, "Call-ID");
- /* Must have Cseq */
- if (ast_strlen_zero(cmd) || ast_strlen_zero(cseq) || ast_strlen_zero(via)) {
- ast_log(LOG_ERROR, "Dropping this SIP message with Call-ID '%s', it's incomplete.\n", callid);
- error = 1;
- }
- if (!error && sscanf(cseq, "%30u%n", &seqno, &len) != 1) {
- ast_log(LOG_ERROR, "No seqno in '%s'. Dropping incomplete message.\n", cmd);
- error = 1;
- }
- if (error) {
- if (!p->initreq.headers) { /* New call */
- pvt_set_needdestroy(p, "no headers");
- }
- return -1;
- }
- /* Get the command XXX */
- cmd = REQ_OFFSET_TO_STR(req, rlpart1);
- e = ast_skip_blanks(REQ_OFFSET_TO_STR(req, rlpart2));
- /* Save useragent of the client */
- useragent = sip_get_header(req, "User-Agent");
- if (!ast_strlen_zero(useragent))
- ast_string_field_set(p, useragent, useragent);
- /* Find out SIP method for incoming request */
- if (req->method == SIP_RESPONSE) { /* Response to our request */
- /* ignore means "don't do anything with it" but still have to
- * respond appropriately.
- * But in this case this is a response already, so we really
- * have nothing to do with this message, and even setting the
- * ignore flag is pointless.
- */
- if (ast_strlen_zero(e)) {
- return 0;
- }
- if (sscanf(e, "%30d %n", &respid, &len) != 1) {
- ast_log(LOG_WARNING, "Invalid response: '%s'\n", e);
- return 0;
- }
- if (respid <= 0) {
- ast_log(LOG_WARNING, "Invalid SIP response code: '%d'\n", respid);
- return 0;
- }
- /* RFC 3261 - 8.1.3.3 If more than one Via header field value is present in a reponse
- * the UAC SHOULD discard the message. This is not perfect, as it will not catch multiple
- * headers joined with a comma. Fixing that would pretty much involve writing a new parser */
- if (!ast_strlen_zero(__get_header(req, "via", &via_pos))) {
- ast_log(LOG_WARNING, "Misrouted SIP response '%s' with Call-ID '%s', too many vias\n", e, callid);
- return 0;
- }
- if (p->ocseq && (p->ocseq < seqno)) {
- ast_debug(1, "Ignoring out of order response %u (expecting %u)\n", seqno, p->ocseq);
- return -1;
- } else {
- if ((respid == 200) || ((respid >= 300) && (respid <= 399))) {
- extract_uri(p, req);
- }
- if (p->owner) {
- struct ast_control_pvt_cause_code *cause_code;
- int data_size = sizeof(*cause_code);
- /* size of the string making up the cause code is "SIP " + cause length */
- data_size += 4 + strlen(REQ_OFFSET_TO_STR(req, rlpart2));
- cause_code = ast_alloca(data_size);
- memset(cause_code, 0, data_size);
- ast_copy_string(cause_code->chan_name, ast_channel_name(p->owner), AST_CHANNEL_NAME);
- snprintf(cause_code->code, data_size - sizeof(*cause_code) + 1, "SIP %s", REQ_OFFSET_TO_STR(req, rlpart2));
- cause_code->ast_cause = hangup_sip2cause(respid);
- if (global_store_sip_cause) {
- cause_code->emulate_sip_cause = 1;
- }
- ast_queue_control_data(p->owner, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size);
- ast_channel_hangupcause_hash_set(p->owner, cause_code, data_size);
- }
- handle_response(p, respid, e + len, req, seqno);
- }
- return 0;
- }
- /* New SIP request coming in
- (could be new request in existing SIP dialog as well...)
- */
- p->method = req->method; /* Find out which SIP method they are using */
- ast_debug(4, "**** Received %s (%u) - Command in SIP %s\n", sip_methods[p->method].text, sip_methods[p->method].id, cmd);
- if (p->icseq && (p->icseq > seqno) ) {
- if (p->pendinginvite && seqno == p->pendinginvite && (req->method == SIP_ACK || req->method == SIP_CANCEL)) {
- ast_debug(2, "Got CANCEL or ACK on INVITE with transactions in between.\n");
- } else {
- ast_debug(1, "Ignoring too old SIP packet packet %u (expecting >= %u)\n", seqno, p->icseq);
- if (req->method == SIP_INVITE) {
- unsigned int ran = (ast_random() % 10) + 1;
- char seconds[4];
- snprintf(seconds, sizeof(seconds), "%u", ran);
- transmit_response_with_retry_after(p, "500 Server error", req, seconds); /* respond according to RFC 3261 14.2 with Retry-After betwewn 0 and 10 */
- } else if (req->method != SIP_ACK) {
- transmit_response(p, "500 Server error", req); /* We must respond according to RFC 3261 sec 12.2 */
- }
- return -1;
- }
- } else if (p->icseq &&
- p->icseq == seqno &&
- req->method != SIP_ACK &&
- (p->method != SIP_CANCEL || p->alreadygone)) {
- /* ignore means "don't do anything with it" but still have to
- respond appropriately. We do this if we receive a repeat of
- the last sequence number */
- req->ignore = 1;
- ast_debug(3, "Ignoring SIP message because of retransmit (%s Seqno %u, ours %u)\n", sip_methods[p->method].text, p->icseq, seqno);
- }
- /* RFC 3261 section 9. "CANCEL has no effect on a request to which a UAS has
- * already given a final response." */
- if (!p->pendinginvite && (req->method == SIP_CANCEL)) {
- transmit_response(p, "481 Call/Transaction Does Not Exist", req);
- return res;
- }
- if (seqno >= p->icseq)
- /* Next should follow monotonically (but not necessarily
- incrementally -- thanks again to the genius authors of SIP --
- increasing */
- p->icseq = seqno;
- /* Find their tag if we haven't got it */
- if (ast_strlen_zero(p->theirtag)) {
- char tag[128];
- gettag(req, "From", tag, sizeof(tag));
- ast_string_field_set(p, theirtag, tag);
- }
- snprintf(p->lastmsg, sizeof(p->lastmsg), "Rx: %s", cmd);
- if (sip_cfg.pedanticsipchecking) {
- /* If this is a request packet without a from tag, it's not
- correct according to RFC 3261 */
- /* Check if this a new request in a new dialog with a totag already attached to it,
- RFC 3261 - section 12.2 - and we don't want to mess with recovery */
- if (!p->initreq.headers && req->has_to_tag) {
- /* If this is a first request and it got a to-tag, it is not for us */
- if (!req->ignore && req->method == SIP_INVITE) {
- /* Just because we think this is a dialog-starting INVITE with a to-tag
- * doesn't mean it actually is. It could be a reinvite for an established, but
- * unknown dialog. In such a case, we need to change our tag to the
- * incoming INVITE's to-tag so that they will recognize the 481 we send and
- * so that we will properly match their incoming ACK.
- */
- char totag[128];
- gettag(req, "To", totag, sizeof(totag));
- ast_string_field_set(p, tag, totag);
- p->pendinginvite = p->icseq;
- transmit_response_reliable(p, "481 Call/Transaction Does Not Exist", req);
- /* Will cease to exist after ACK */
- return res;
- } else if (req->method != SIP_ACK) {
- transmit_response(p, "481 Call/Transaction Does Not Exist", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return res;
- }
- /* Otherwise, this is an ACK. It will always have a to-tag */
- }
- }
- if (!e && (p->method == SIP_INVITE || p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER || p->method == SIP_NOTIFY || p->method == SIP_PUBLISH)) {
- transmit_response(p, "400 Bad request", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return -1;
- }
- /* Handle various incoming SIP methods in requests */
- switch (p->method) {
- case SIP_OPTIONS:
- res = handle_request_options(p, req, addr, e);
- break;
- case SIP_INVITE:
- res = handle_request_invite(p, req, addr, seqno, recount, e, nounlock);
- if (res < 9) {
- sip_report_security_event(p, req, res);
- }
- switch (res) {
- case INV_REQ_SUCCESS:
- res = 1;
- break;
- case INV_REQ_FAILED:
- res = 0;
- break;
- case INV_REQ_ERROR:
- res = -1;
- break;
- default:
- res = 0;
- break;
- }
- break;
- case SIP_REFER:
- res = handle_request_refer(p, req, seqno, nounlock);
- break;
- case SIP_CANCEL:
- res = handle_request_cancel(p, req);
- break;
- case SIP_BYE:
- res = handle_request_bye(p, req);
- break;
- case SIP_MESSAGE:
- res = handle_request_message(p, req, addr, e);
- break;
- case SIP_PUBLISH:
- res = handle_request_publish(p, req, addr, seqno, e);
- break;
- case SIP_SUBSCRIBE:
- res = handle_request_subscribe(p, req, addr, seqno, e);
- break;
- case SIP_REGISTER:
- res = handle_request_register(p, req, addr, e);
- sip_report_security_event(p, req, res);
- break;
- case SIP_INFO:
- if (req->debug)
- ast_verbose("Receiving INFO!\n");
- if (!req->ignore)
- handle_request_info(p, req);
- else /* if ignoring, transmit response */
- transmit_response(p, "200 OK", req);
- break;
- case SIP_NOTIFY:
- res = handle_request_notify(p, req, addr, seqno, e);
- break;
- case SIP_UPDATE:
- res = handle_request_update(p, req);
- break;
- case SIP_ACK:
- /* Make sure we don't ignore this */
- if (seqno == p->pendinginvite) {
- p->invitestate = INV_TERMINATED;
- p->pendinginvite = 0;
- acked = __sip_ack(p, seqno, 1 /* response */, 0);
- if (p->owner && find_sdp(req)) {
- if (process_sdp(p, req, SDP_T38_NONE)) {
- return -1;
- }
- if (ast_test_flag(&p->flags[0], SIP_DIRECT_MEDIA)) {
- ast_queue_control(p->owner, AST_CONTROL_SRCCHANGE);
- }
- }
- check_pendings(p);
- } else if (p->glareinvite == seqno) {
- /* handle ack for the 491 pending sent for glareinvite */
- p->glareinvite = 0;
- acked = __sip_ack(p, seqno, 1, 0);
- }
- if (!acked) {
- /* Got an ACK that did not match anything. Ignore
- * silently and restore previous method */
- p->method = oldmethod;
- }
- if (!p->lastinvite && ast_strlen_zero(p->nonce)) {
- pvt_set_needdestroy(p, "unmatched ACK");
- }
- break;
- default:
- transmit_response_with_allow(p, "501 Method Not Implemented", req, 0);
- ast_log(LOG_NOTICE, "Unknown SIP command '%s' from '%s'\n",
- cmd, ast_sockaddr_stringify(&p->sa));
- /* If this is some new method, and we don't have a call, destroy it now */
- if (!p->initreq.headers) {
- pvt_set_needdestroy(p, "unimplemented method");
- }
- break;
- }
- return res;
- }
- /*! \brief Read data from SIP UDP socket
- \note sipsock_read locks the owner channel while we are processing the SIP message
- \return 1 on error, 0 on success
- \note Successful messages is connected to SIP call and forwarded to handle_incoming()
- */
- static int sipsock_read(int *id, int fd, short events, void *ignore)
- {
- struct sip_request req;
- struct ast_sockaddr addr;
- int res;
- static char readbuf[65535];
- memset(&req, 0, sizeof(req));
- res = ast_recvfrom(fd, readbuf, sizeof(readbuf) - 1, 0, &addr);
- if (res < 0) {
- #if !defined(__FreeBSD__)
- if (errno == EAGAIN)
- ast_log(LOG_NOTICE, "SIP: Received packet with bad UDP checksum\n");
- else
- #endif
- if (errno != ECONNREFUSED)
- ast_log(LOG_WARNING, "Recv error: %s\n", strerror(errno));
- return 1;
- }
- readbuf[res] = '\0';
- if (!(req.data = ast_str_create(SIP_MIN_PACKET))) {
- return 1;
- }
- if (ast_str_set(&req.data, 0, "%s", readbuf) == AST_DYNSTR_BUILD_FAILED) {
- return -1;
- }
- req.socket.fd = sipsock;
- set_socket_transport(&req.socket, AST_TRANSPORT_UDP);
- req.socket.tcptls_session = NULL;
- req.socket.port = htons(ast_sockaddr_port(&bindaddr));
- handle_request_do(&req, &addr);
- deinit_req(&req);
- return 1;
- }
- /*! \brief Handle incoming SIP message - request or response
- This is used for all transports (udp, tcp and tcp/tls)
- */
- static int handle_request_do(struct sip_request *req, struct ast_sockaddr *addr)
- {
- struct sip_pvt *p;
- struct ast_channel *owner_chan_ref = NULL;
- int recount = 0;
- int nounlock = 0;
- if (sip_debug_test_addr(addr)) /* Set the debug flag early on packet level */
- req->debug = 1;
- if (sip_cfg.pedanticsipchecking)
- lws2sws(req->data); /* Fix multiline headers */
- if (req->debug) {
- ast_verbose("\n<--- SIP read from %s:%s --->\n%s\n<------------->\n",
- sip_get_transport(req->socket.type), ast_sockaddr_stringify(addr), ast_str_buffer(req->data));
- }
- if (parse_request(req) == -1) { /* Bad packet, can't parse */
- ast_str_reset(req->data); /* nulling this out is NOT a good idea here. */
- return 1;
- }
- req->method = find_sip_method(REQ_OFFSET_TO_STR(req, rlpart1));
- if (req->debug)
- ast_verbose("--- (%d headers %d lines)%s ---\n", req->headers, req->lines, (req->headers + req->lines == 0) ? " Nat keepalive" : "");
- if (req->headers < 2) { /* Must have at least two headers */
- ast_str_reset(req->data); /* nulling this out is NOT a good idea here. */
- return 1;
- }
- ast_mutex_lock(&netlock);
- /* Find the active SIP dialog or create a new one */
- p = find_call(req, addr, req->method); /* returns p with a reference only. _NOT_ locked*/
- if (p == NULL) {
- ast_debug(1, "Invalid SIP message - rejected , no callid, len %zu\n", ast_str_strlen(req->data));
- ast_mutex_unlock(&netlock);
- return 1;
- }
- if (p->logger_callid) {
- ast_callid_threadassoc_add(p->logger_callid);
- }
- /* Lock both the pvt and the owner if owner is present. This will
- * not fail. */
- owner_chan_ref = sip_pvt_lock_full(p);
- copy_socket_data(&p->socket, &req->socket);
- ast_sockaddr_copy(&p->recv, addr);
- /* if we have an owner, then this request has been authenticated */
- if (p->owner) {
- req->authenticated = 1;
- }
- if (p->do_history) /* This is a request or response, note what it was for */
- append_history(p, "Rx", "%s / %s / %s", ast_str_buffer(req->data), sip_get_header(req, "CSeq"), REQ_OFFSET_TO_STR(req, rlpart2));
- if (handle_incoming(p, req, addr, &recount, &nounlock) == -1) {
- /* Request failed */
- ast_debug(1, "SIP message could not be handled, bad request: %-70.70s\n", p->callid[0] ? p->callid : "<no callid>");
- }
- if (recount) {
- ast_update_use_count();
- }
- if (p->owner && !nounlock) {
- ast_channel_unlock(p->owner);
- }
- if (owner_chan_ref) {
- ast_channel_unref(owner_chan_ref);
- }
- sip_pvt_unlock(p);
- ast_mutex_unlock(&netlock);
- if (p->logger_callid) {
- ast_callid_threadassoc_remove();
- }
- ao2_t_ref(p, -1, "throw away dialog ptr from find_call at end of routine"); /* p is gone after the return */
- return 1;
- }
- /*! \brief Returns the port to use for this socket
- *
- * \param type The type of transport used
- * \param port Port we are checking to see if it's the standard port.
- * \note port is expected in host byte order
- */
- static int sip_standard_port(enum ast_transport type, int port)
- {
- if (type & AST_TRANSPORT_TLS)
- return port == STANDARD_TLS_PORT;
- else
- return port == STANDARD_SIP_PORT;
- }
- static int threadinfo_locate_cb(void *obj, void *arg, int flags)
- {
- struct sip_threadinfo *th = obj;
- struct ast_sockaddr *s = arg;
- if (!ast_sockaddr_cmp(s, &th->tcptls_session->remote_address)) {
- return CMP_MATCH | CMP_STOP;
- }
- return 0;
- }
- /*!
- * \brief Find thread for TCP/TLS session (based on IP/Port
- *
- * \note This function returns an astobj2 reference
- */
- static struct ast_tcptls_session_instance *sip_tcp_locate(struct ast_sockaddr *s)
- {
- struct sip_threadinfo *th;
- struct ast_tcptls_session_instance *tcptls_instance = NULL;
- if ((th = ao2_callback(threadt, 0, threadinfo_locate_cb, s))) {
- tcptls_instance = (ao2_ref(th->tcptls_session, +1), th->tcptls_session);
- ao2_t_ref(th, -1, "decrement ref from callback");
- }
- return tcptls_instance;
- }
- /*!
- * \brief Helper for dns resolution to filter by address family.
- *
- * \note return 0 if addr is [::] else it returns addr's family.
- */
- int get_address_family_filter(unsigned int transport)
- {
- const struct ast_sockaddr *addr = NULL;
- if ((transport == AST_TRANSPORT_UDP) || !transport) {
- addr = &bindaddr;
- } else if (transport == AST_TRANSPORT_TCP || transport == AST_TRANSPORT_WS) {
- addr = &sip_tcp_desc.local_address;
- } else if (transport == AST_TRANSPORT_TLS || transport == AST_TRANSPORT_WSS) {
- addr = &sip_tls_desc.local_address;
- }
- if (ast_sockaddr_is_ipv6(addr) && ast_sockaddr_is_any(addr)) {
- return 0;
- }
- return addr->ss.ss_family;
- }
- /*! \todo Get socket for dialog, prepare if needed, and return file handle */
- static int sip_prepare_socket(struct sip_pvt *p)
- {
- struct sip_socket *s = &p->socket;
- static const char name[] = "SIP socket";
- struct sip_threadinfo *th = NULL;
- struct ast_tcptls_session_instance *tcptls_session;
- struct ast_tcptls_session_args *ca;
- struct ast_sockaddr sa_tmp;
- pthread_t launched;
- /* check to see if a socket is already active */
- if ((s->fd != -1) && (s->type == AST_TRANSPORT_UDP)) {
- return s->fd;
- }
- if ((s->type & (AST_TRANSPORT_TCP | AST_TRANSPORT_TLS)) &&
- (s->tcptls_session) &&
- (s->tcptls_session->fd != -1)) {
- return s->tcptls_session->fd;
- }
- if ((s->type & (AST_TRANSPORT_WS | AST_TRANSPORT_WSS))) {
- return s->ws_session ? ast_websocket_fd(s->ws_session) : -1;
- }
- /*! \todo Check this... This might be wrong, depending on the proxy configuration
- If proxy is in "force" mode its correct.
- */
- if (p->outboundproxy && p->outboundproxy->transport) {
- s->type = p->outboundproxy->transport;
- }
- if (s->type == AST_TRANSPORT_UDP) {
- s->fd = sipsock;
- return s->fd;
- }
- /* At this point we are dealing with a TCP/TLS connection
- * 1. We need to check to see if a connection thread exists
- * for this address, if so use that.
- * 2. If a thread does not exist for this address, but the tcptls_session
- * exists on the socket, the connection was closed.
- * 3. If no tcptls_session thread exists for the address, and no tcptls_session
- * already exists on the socket, create a new one and launch a new thread.
- */
- /* 1. check for existing threads */
- ast_sockaddr_copy(&sa_tmp, sip_real_dst(p));
- if ((tcptls_session = sip_tcp_locate(&sa_tmp))) {
- s->fd = tcptls_session->fd;
- if (s->tcptls_session) {
- ao2_ref(s->tcptls_session, -1);
- s->tcptls_session = NULL;
- }
- s->tcptls_session = tcptls_session;
- return s->fd;
- /* 2. Thread not found, if tcptls_session already exists, it once had a thread and is now terminated */
- } else if (s->tcptls_session) {
- return s->fd; /* XXX whether reconnection is ever necessary here needs to be investigated further */
- }
- /* 3. Create a new TCP/TLS client connection */
- /* create new session arguments for the client connection */
- if (!(ca = ao2_alloc(sizeof(*ca), sip_tcptls_client_args_destructor)) ||
- !(ca->name = ast_strdup(name))) {
- goto create_tcptls_session_fail;
- }
- ca->accept_fd = -1;
- ast_sockaddr_copy(&ca->remote_address,sip_real_dst(p));
- /* if type is TLS, we need to create a tls cfg for this session arg */
- if (s->type == AST_TRANSPORT_TLS) {
- if (!(ca->tls_cfg = ast_calloc(1, sizeof(*ca->tls_cfg)))) {
- goto create_tcptls_session_fail;
- }
- memcpy(ca->tls_cfg, &default_tls_cfg, sizeof(*ca->tls_cfg));
- if (!(ca->tls_cfg->certfile = ast_strdup(default_tls_cfg.certfile)) ||
- !(ca->tls_cfg->pvtfile = ast_strdup(default_tls_cfg.pvtfile)) ||
- !(ca->tls_cfg->cipher = ast_strdup(default_tls_cfg.cipher)) ||
- !(ca->tls_cfg->cafile = ast_strdup(default_tls_cfg.cafile)) ||
- !(ca->tls_cfg->capath = ast_strdup(default_tls_cfg.capath))) {
- goto create_tcptls_session_fail;
- }
- /* this host is used as the common name in ssl/tls */
- if (!ast_strlen_zero(p->tohost)) {
- ast_copy_string(ca->hostname, p->tohost, sizeof(ca->hostname));
- }
- }
- /* Create a client connection for address, this does not start the connection, just sets it up. */
- if (!(s->tcptls_session = ast_tcptls_client_create(ca))) {
- goto create_tcptls_session_fail;
- }
- s->fd = s->tcptls_session->fd;
- /* client connections need to have the sip_threadinfo object created before
- * the thread is detached. This ensures the alert_pipe is up before it will
- * be used. Note that this function links the new threadinfo object into the
- * threadt container. */
- if (!(th = sip_threadinfo_create(s->tcptls_session, s->type))) {
- goto create_tcptls_session_fail;
- }
- /* Give the new thread a reference to the tcptls_session */
- ao2_ref(s->tcptls_session, +1);
- if (ast_pthread_create_detached_background(&launched, NULL, sip_tcp_worker_fn, s->tcptls_session)) {
- ast_debug(1, "Unable to launch '%s'.", ca->name);
- ao2_ref(s->tcptls_session, -1); /* take away the thread ref we just gave it */
- goto create_tcptls_session_fail;
- }
- return s->fd;
- create_tcptls_session_fail:
- if (ca) {
- ao2_t_ref(ca, -1, "failed to create client, getting rid of client tcptls_session arguments");
- }
- if (s->tcptls_session) {
- ast_tcptls_close_session_file(s->tcptls_session);
- s->fd = -1;
- ao2_ref(s->tcptls_session, -1);
- s->tcptls_session = NULL;
- }
- if (th) {
- ao2_t_unlink(threadt, th, "Removing tcptls thread info object, thread failed to open");
- }
- return -1;
- }
- /*!
- * \brief Get cached MWI info
- * \return TRUE if found MWI in cache
- */
- static int get_cached_mwi(struct sip_peer *peer, int *new, int *old)
- {
- struct sip_mailbox *mailbox;
- int in_cache;
- in_cache = 0;
- AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
- RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
- struct ast_mwi_state *mwi_state;
- msg = stasis_cache_get(ast_mwi_state_cache(), ast_mwi_state_type(), mailbox->id);
- if (!msg) {
- continue;
- }
- mwi_state = stasis_message_data(msg);
- *new += mwi_state->new_msgs;
- *old += mwi_state->old_msgs;
- in_cache = 1;
- }
- return in_cache;
- }
- /*! \brief Send message waiting indication to alert peer that they've got voicemail
- * \note Both peer and associated sip_pvt must be unlocked prior to calling this function
- * \returns -1 on failure, 0 on success
- */
- static int sip_send_mwi_to_peer(struct sip_peer *peer, int cache_only)
- {
- /* Called with peer lock, but releases it */
- struct sip_pvt *p;
- int newmsgs = 0, oldmsgs = 0;
- const char *vmexten = NULL;
- ao2_lock(peer);
- if (peer->vmexten) {
- vmexten = ast_strdupa(peer->vmexten);
- }
- if (ast_test_flag((&peer->flags[1]), SIP_PAGE2_SUBSCRIBEMWIONLY) && !peer->mwipvt) {
- update_peer_lastmsgssent(peer, -1, 1);
- ao2_unlock(peer);
- return -1;
- }
- /* Do we have an IP address? If not, skip this peer */
- if (ast_sockaddr_isnull(&peer->addr) && ast_sockaddr_isnull(&peer->defaddr)) {
- update_peer_lastmsgssent(peer, -1, 1);
- ao2_unlock(peer);
- return -1;
- }
- /* Attempt to use cached mwi to get message counts. */
- if (!get_cached_mwi(peer, &newmsgs, &oldmsgs) && !cache_only) {
- /* Fall back to manually checking the mailbox if not cache_only and get_cached_mwi failed */
- struct ast_str *mailbox_str = ast_str_alloca(512);
- peer_mailboxes_to_str(&mailbox_str, peer);
- /* if there is no mailbox do nothing */
- if (!ast_str_strlen(mailbox_str)) {
- ao2_unlock(peer);
- return -1;
- }
- ao2_unlock(peer);
- /* If there is no mailbox do nothing */
- if (!ast_str_strlen(mailbox_str)) {
- update_peer_lastmsgssent(peer, -1, 0);
- return 0;
- }
- ast_app_inboxcount(ast_str_buffer(mailbox_str), &newmsgs, &oldmsgs);
- ao2_lock(peer);
- }
- if (peer->mwipvt) {
- /* Base message on subscription */
- p = dialog_ref(peer->mwipvt, "sip_send_mwi_to_peer: Setting dialog ptr p from peer->mwipvt");
- ao2_unlock(peer);
- } else {
- ao2_unlock(peer);
- /* Build temporary dialog for this message */
- if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY, NULL, 0))) {
- update_peer_lastmsgssent(peer, -1, 0);
- return -1;
- }
- /* If we don't set the socket type to 0, then create_addr_from_peer will fail immediately if the peer
- * uses any transport other than UDP. We set the type to 0 here and then let create_addr_from_peer copy
- * the peer's socket information to the sip_pvt we just allocated
- */
- set_socket_transport(&p->socket, 0);
- if (create_addr_from_peer(p, peer)) {
- /* Maybe they're not registered, etc. */
- dialog_unlink_all(p);
- dialog_unref(p, "unref dialog p just created via sip_alloc");
- update_peer_lastmsgssent(peer, -1, 0);
- return -1;
- }
- /* Recalculate our side, and recalculate Call ID */
- ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
- build_via(p);
- ao2_lock(peer);
- if (!ast_strlen_zero(peer->mwi_from)) {
- ast_string_field_set(p, mwi_from, peer->mwi_from);
- } else if (!ast_strlen_zero(default_mwi_from)) {
- ast_string_field_set(p, mwi_from, default_mwi_from);
- }
- ao2_unlock(peer);
- /* Change the dialog callid. */
- change_callid_pvt(p, NULL);
- /* Destroy this session after 32 secs */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- }
- /* We have multiple threads (mwi events and monitor retransmits) working with this PVT and as we modify the sip history if that's turned on,
- we really need to have a lock on it */
- sip_pvt_lock(p);
- /* Send MWI */
- ast_set_flag(&p->flags[0], SIP_OUTGOING);
- /* the following will decrement the refcount on p as it finishes */
- transmit_notify_with_mwi(p, newmsgs, oldmsgs, vmexten);
- sip_pvt_unlock(p);
- dialog_unref(p, "unref dialog ptr p just before it goes out of scope at the end of sip_send_mwi_to_peer.");
- update_peer_lastmsgssent(peer, ((newmsgs > 0x7fff ? 0x7fff0000 : (newmsgs << 16)) | (oldmsgs > 0xffff ? 0xffff : oldmsgs)), 0);
- return 0;
- }
- static struct ast_manager_event_blob *session_timeout_to_ami(struct stasis_message *msg)
- {
- RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
- struct ast_channel_blob *obj = stasis_message_data(msg);
- const char *source = ast_json_string_get(ast_json_object_get(obj->blob, "source"));
- channel_string = ast_manager_build_channel_state_string(obj->snapshot);
- if (!channel_string) {
- return NULL;
- }
- return ast_manager_event_blob_create(EVENT_FLAG_CALL, "SessionTimeout",
- "%s"
- "Source: %s\r\n",
- ast_str_buffer(channel_string), source);
- }
- /*! \brief Sends a session timeout channel blob used to produce SessionTimeout AMI messages */
- static void send_session_timeout(struct ast_channel *chan, const char *source)
- {
- RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
- ast_assert(chan != NULL);
- ast_assert(source != NULL);
- blob = ast_json_pack("{s: s}", "source", source);
- if (!blob) {
- return;
- }
- ast_channel_publish_blob(chan, session_timeout_type(), blob);
- }
- /*!
- * \brief helper function for the monitoring thread -- seems to be called with the assumption that the dialog is locked
- *
- * \return CMP_MATCH for items to be unlinked from dialogs_rtpcheck.
- */
- static int check_rtp_timeout(struct sip_pvt *dialog, time_t t)
- {
- int timeout;
- int hold_timeout;
- int keepalive;
- if (!dialog->rtp) {
- /*
- * We have no RTP. Since we don't do much with video RTP for
- * now, stop checking this dialog.
- */
- return CMP_MATCH;
- }
- /* If we have no active owner, no need to check timers */
- if (!dialog->owner) {
- return CMP_MATCH;
- }
- /* If the call is redirected outside Asterisk, no need to check timers */
- if (!ast_sockaddr_isnull(&dialog->redirip)) {
- return CMP_MATCH;
- }
- /* If the call is involved in a T38 fax session do not check RTP timeout */
- if (dialog->t38.state == T38_ENABLED) {
- return CMP_MATCH;
- }
- /* If the call is not in UP state return for later check. */
- if (ast_channel_state(dialog->owner) != AST_STATE_UP) {
- return 0;
- }
- /* Store these values locally to avoid multiple function calls */
- timeout = ast_rtp_instance_get_timeout(dialog->rtp);
- hold_timeout = ast_rtp_instance_get_hold_timeout(dialog->rtp);
- keepalive = ast_rtp_instance_get_keepalive(dialog->rtp);
- /* If we have no timers set, return now */
- if (!keepalive && !timeout && !hold_timeout) {
- return CMP_MATCH;
- }
- /* Check AUDIO RTP keepalives */
- if (dialog->lastrtptx && keepalive && (t > dialog->lastrtptx + keepalive)) {
- /* Need to send an empty RTP packet */
- dialog->lastrtptx = time(NULL);
- ast_rtp_instance_sendcng(dialog->rtp, 0);
- }
- /*! \todo Check video RTP keepalives
- Do we need to move the lastrtptx to the RTP structure to have one for audio and one
- for video? It really does belong to the RTP structure.
- */
- /* Check AUDIO RTP timers */
- if (dialog->lastrtprx && (timeout || hold_timeout) && (t > dialog->lastrtprx + timeout)) {
- if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD) || (hold_timeout && (t > dialog->lastrtprx + hold_timeout))) {
- /* Needs a hangup */
- if (timeout) {
- if (!dialog->owner || ast_channel_trylock(dialog->owner)) {
- /*
- * Don't block, just try again later.
- * If there was no owner, the call is dead already.
- */
- return 0;
- }
- ast_log(LOG_NOTICE, "Disconnecting call '%s' for lack of RTP activity in %ld seconds\n",
- ast_channel_name(dialog->owner), (long) (t - dialog->lastrtprx));
- send_session_timeout(dialog->owner, "RTPTimeout");
- /* Issue a softhangup */
- ast_softhangup_nolock(dialog->owner, AST_SOFTHANGUP_DEV);
- ast_channel_unlock(dialog->owner);
- /* forget the timeouts for this call, since a hangup
- has already been requested and we don't want to
- repeatedly request hangups
- */
- ast_rtp_instance_set_timeout(dialog->rtp, 0);
- ast_rtp_instance_set_hold_timeout(dialog->rtp, 0);
- if (dialog->vrtp) {
- ast_rtp_instance_set_timeout(dialog->vrtp, 0);
- ast_rtp_instance_set_hold_timeout(dialog->vrtp, 0);
- }
- /* finally unlink the dialog from dialogs_rtpcheck. */
- return CMP_MATCH;
- }
- }
- }
- return 0;
- }
- /*! \brief The SIP monitoring thread
- \note This thread monitors all the SIP sessions and peers that needs notification of mwi
- (and thus do not have a separate thread) indefinitely
- */
- static void *do_monitor(void *data)
- {
- int res;
- time_t t;
- int reloading;
- /* Add an I/O event to our SIP UDP socket */
- if (sipsock > -1) {
- sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);
- }
- /* From here on out, we die whenever asked */
- for(;;) {
- /* Check for a reload request */
- ast_mutex_lock(&sip_reload_lock);
- reloading = sip_reloading;
- sip_reloading = FALSE;
- ast_mutex_unlock(&sip_reload_lock);
- if (reloading) {
- ast_verb(1, "Reloading SIP\n");
- sip_do_reload(sip_reloadreason);
- /* Change the I/O fd of our UDP socket */
- if (sipsock > -1) {
- if (sipsock_read_id) {
- sipsock_read_id = ast_io_change(io, sipsock_read_id, sipsock, NULL, 0, NULL);
- } else {
- sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);
- }
- } else if (sipsock_read_id) {
- ast_io_remove(io, sipsock_read_id);
- sipsock_read_id = NULL;
- }
- }
- /* Check for dialogs needing to be killed */
- t = time(NULL);
- /*
- * Check dialogs with rtp and rtptimeout.
- * All dialogs which have rtp are in dialogs_rtpcheck.
- */
- ao2_t_callback(dialogs_rtpcheck, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
- dialog_checkrtp_cb, &t,
- "callback to check rtptimeout and hangup calls if necessary");
- /*
- * Check dialogs marked to be destroyed.
- * All dialogs with needdestroy set are in dialogs_needdestroy.
- */
- ao2_t_callback(dialogs_needdestroy, OBJ_NODATA | OBJ_MULTIPLE, dialog_needdestroy,
- NULL, "callback to check dialogs which need to be destroyed");
- /* XXX TODO The scheduler usage in this module does not have sufficient
- * synchronization being done between running the scheduler and places
- * scheduling tasks. As it is written, any scheduled item may not run
- * any sooner than about 1 second, regardless of whether a sooner time
- * was asked for. */
- pthread_testcancel();
- /* Wait for sched or io */
- res = ast_sched_wait(sched);
- if ((res < 0) || (res > 1000)) {
- res = 1000;
- }
- res = ast_io_wait(io, res);
- if (res > 20) {
- ast_debug(1, "chan_sip: ast_io_wait ran %d all at once\n", res);
- }
- ast_mutex_lock(&monlock);
- res = ast_sched_runq(sched);
- if (res >= 20) {
- ast_debug(1, "chan_sip: ast_sched_runq ran %d all at once\n", res);
- }
- ast_mutex_unlock(&monlock);
- }
- /* Never reached */
- return NULL;
- }
- /*! \brief Start the channel monitor thread */
- static int restart_monitor(void)
- {
- /* If we're supposed to be stopped -- stay stopped */
- if (monitor_thread == AST_PTHREADT_STOP)
- return 0;
- ast_mutex_lock(&monlock);
- if (monitor_thread == pthread_self()) {
- ast_mutex_unlock(&monlock);
- ast_log(LOG_WARNING, "Cannot kill myself\n");
- return -1;
- }
- if (monitor_thread != AST_PTHREADT_NULL && monitor_thread != AST_PTHREADT_STOP) {
- /* Wake up the thread */
- pthread_kill(monitor_thread, SIGURG);
- } else {
- /* Start a new monitor */
- if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
- ast_mutex_unlock(&monlock);
- ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
- return -1;
- }
- }
- ast_mutex_unlock(&monlock);
- return 0;
- }
- static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub,
- struct stasis_message *message)
- {
- if (stasis_message_type(message) != ast_named_acl_change_type()) {
- return;
- }
- ast_log(LOG_NOTICE, "Reloading chan_sip in response to ACL change event.\n");
- ast_mutex_lock(&sip_reload_lock);
- if (sip_reloading) {
- ast_verbose("Previous SIP reload not yet done\n");
- } else {
- sip_reloading = TRUE;
- sip_reloadreason = CHANNEL_ACL_RELOAD;
- }
- ast_mutex_unlock(&sip_reload_lock);
- restart_monitor();
- }
- /*! \brief Session-Timers: Restart session timer */
- static void restart_session_timer(struct sip_pvt *p)
- {
- if (p->stimer->st_active == TRUE) {
- ast_debug(2, "Session timer stopped: %d - %s\n", p->stimer->st_schedid, p->callid);
- AST_SCHED_DEL_UNREF(sched, p->stimer->st_schedid,
- dialog_unref(p, "Removing session timer ref"));
- start_session_timer(p);
- }
- }
- /*! \brief Session-Timers: Stop session timer */
- static void stop_session_timer(struct sip_pvt *p)
- {
- if (p->stimer->st_active == TRUE) {
- p->stimer->st_active = FALSE;
- ast_debug(2, "Session timer stopped: %d - %s\n", p->stimer->st_schedid, p->callid);
- AST_SCHED_DEL_UNREF(sched, p->stimer->st_schedid,
- dialog_unref(p, "removing session timer ref"));
- }
- }
- /*! \brief Session-Timers: Start session timer */
- static void start_session_timer(struct sip_pvt *p)
- {
- unsigned int timeout_ms;
- if (p->stimer->st_schedid > -1) {
- /* in the event a timer is already going, stop it */
- ast_debug(2, "Session timer stopped: %d - %s\n", p->stimer->st_schedid, p->callid);
- AST_SCHED_DEL_UNREF(sched, p->stimer->st_schedid,
- dialog_unref(p, "unref stimer->st_schedid from dialog"));
- }
- /*
- * RFC 4028 Section 10
- * If the side not performing refreshes does not receive a
- * session refresh request before the session expiration, it SHOULD send
- * a BYE to terminate the session, slightly before the session
- * expiration. The minimum of 32 seconds and one third of the session
- * interval is RECOMMENDED.
- */
- timeout_ms = (1000 * p->stimer->st_interval);
- if (p->stimer->st_ref == SESSION_TIMER_REFRESHER_US) {
- timeout_ms /= 2;
- } else {
- timeout_ms -= MIN(timeout_ms / 3, 32000);
- }
- p->stimer->st_schedid = ast_sched_add(sched, timeout_ms, proc_session_timer,
- dialog_ref(p, "adding session timer ref"));
- if (p->stimer->st_schedid < 0) {
- dialog_unref(p, "removing session timer ref");
- ast_log(LOG_ERROR, "ast_sched_add failed - %s\n", p->callid);
- } else {
- p->stimer->st_active = TRUE;
- ast_debug(2, "Session timer started: %d - %s %ums\n", p->stimer->st_schedid, p->callid, timeout_ms);
- }
- }
- /*! \brief Session-Timers: Process session refresh timeout event */
- static int proc_session_timer(const void *vp)
- {
- struct sip_pvt *p = (struct sip_pvt *) vp;
- int res = 0;
- if (!p->stimer) {
- ast_log(LOG_WARNING, "Null stimer in proc_session_timer - %s\n", p->callid);
- goto return_unref;
- }
- ast_debug(2, "Session timer expired: %d - %s\n", p->stimer->st_schedid, p->callid);
- if (!p->owner) {
- goto return_unref;
- }
- if ((p->stimer->st_active != TRUE) || (ast_channel_state(p->owner) != AST_STATE_UP)) {
- goto return_unref;
- }
- if (p->stimer->st_ref == SESSION_TIMER_REFRESHER_US) {
- res = 1;
- if (T38_ENABLED == p->t38.state) {
- transmit_reinvite_with_sdp(p, TRUE, TRUE);
- } else {
- transmit_reinvite_with_sdp(p, FALSE, TRUE);
- }
- } else {
- if (p->stimer->quit_flag) {
- goto return_unref;
- }
- ast_log(LOG_WARNING, "Session-Timer expired - %s\n", p->callid);
- sip_pvt_lock(p);
- while (p->owner && ast_channel_trylock(p->owner)) {
- sip_pvt_unlock(p);
- usleep(1);
- if (p->stimer && p->stimer->quit_flag) {
- goto return_unref;
- }
- sip_pvt_lock(p);
- }
- send_session_timeout(p->owner, "SIPSessionTimer");
- ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
- ast_channel_unlock(p->owner);
- sip_pvt_unlock(p);
- }
- return_unref:
- if (!res) {
- /* An error occurred. Stop session timer processing */
- if (p->stimer) {
- ast_debug(2, "Session timer stopped: %d - %s\n", p->stimer->st_schedid, p->callid);
- /* Don't pass go, don't collect $200.. we are the scheduled
- * callback. We can rip ourself out here. */
- p->stimer->st_schedid = -1;
- /* Calling stop_session_timer is nice for consistent debug
- * logs. */
- stop_session_timer(p);
- }
- /* If we are not asking to be rescheduled, then we need to release our
- * reference to the dialog. */
- dialog_unref(p, "removing session timer ref");
- }
- return res;
- }
- /*! \brief Session-Timers: Function for parsing Min-SE header */
- int parse_minse (const char *p_hdrval, int *const p_interval)
- {
- if (ast_strlen_zero(p_hdrval)) {
- ast_log(LOG_WARNING, "Null Min-SE header\n");
- return -1;
- }
- *p_interval = 0;
- p_hdrval = ast_skip_blanks(p_hdrval);
- if (!sscanf(p_hdrval, "%30d", p_interval)) {
- ast_log(LOG_WARNING, "Parsing of Min-SE header failed %s\n", p_hdrval);
- return -1;
- }
- ast_debug(2, "Received Min-SE: %d\n", *p_interval);
- return 0;
- }
- /*! \brief Session-Timers: Function for parsing Session-Expires header */
- int parse_session_expires(const char *p_hdrval, int *const p_interval, enum st_refresher_param *const p_ref)
- {
- char *p_token;
- int ref_idx;
- char *p_se_hdr;
- if (ast_strlen_zero(p_hdrval)) {
- ast_log(LOG_WARNING, "Null Session-Expires header\n");
- return -1;
- }
- *p_ref = SESSION_TIMER_REFRESHER_PARAM_UNKNOWN;
- *p_interval = 0;
- p_se_hdr = ast_strdupa(p_hdrval);
- p_se_hdr = ast_skip_blanks(p_se_hdr);
- while ((p_token = strsep(&p_se_hdr, ";"))) {
- p_token = ast_skip_blanks(p_token);
- if (!sscanf(p_token, "%30d", p_interval)) {
- ast_log(LOG_WARNING, "Parsing of Session-Expires failed\n");
- return -1;
- }
- ast_debug(2, "Session-Expires: %d\n", *p_interval);
- if (!p_se_hdr)
- continue;
- p_se_hdr = ast_skip_blanks(p_se_hdr);
- ref_idx = strlen("refresher=");
- if (!strncasecmp(p_se_hdr, "refresher=", ref_idx)) {
- p_se_hdr += ref_idx;
- p_se_hdr = ast_skip_blanks(p_se_hdr);
- if (!strncasecmp(p_se_hdr, "uac", strlen("uac"))) {
- *p_ref = SESSION_TIMER_REFRESHER_PARAM_UAC;
- ast_debug(2, "Refresher: UAC\n");
- } else if (!strncasecmp(p_se_hdr, "uas", strlen("uas"))) {
- *p_ref = SESSION_TIMER_REFRESHER_PARAM_UAS;
- ast_debug(2, "Refresher: UAS\n");
- } else {
- ast_log(LOG_WARNING, "Invalid refresher value %s\n", p_se_hdr);
- return -1;
- }
- break;
- }
- }
- return 0;
- }
- /*! \brief Handle 422 response to INVITE with session-timer requested
- Session-Timers: An INVITE originated by Asterisk that asks for session-timers support
- from the UAS can result into a 422 response. This is how a UAS or an intermediary proxy
- server tells Asterisk that the session refresh interval offered by Asterisk is too low
- for them. The proc_422_rsp() function handles a 422 response. It extracts the Min-SE
- header that comes back in 422 and sends a new INVITE accordingly. */
- static void proc_422_rsp(struct sip_pvt *p, struct sip_request *rsp)
- {
- int rtn;
- const char *p_hdrval;
- int minse;
- p_hdrval = sip_get_header(rsp, "Min-SE");
- if (ast_strlen_zero(p_hdrval)) {
- ast_log(LOG_WARNING, "422 response without a Min-SE header %s\n", p_hdrval);
- return;
- }
- rtn = parse_minse(p_hdrval, &minse);
- if (rtn != 0) {
- ast_log(LOG_WARNING, "Parsing of Min-SE header failed %s\n", p_hdrval);
- return;
- }
- p->stimer->st_cached_min_se = minse;
- if (p->stimer->st_interval < minse) {
- p->stimer->st_interval = minse;
- }
- transmit_invite(p, SIP_INVITE, 1, 2, NULL);
- }
- /*! \brief Get Max or Min SE (session timer expiry)
- * \param p pointer to the SIP dialog
- * \param max if true, get max se, otherwise min se
- */
- int st_get_se(struct sip_pvt *p, int max)
- {
- if (max == TRUE) {
- if (p->stimer->st_cached_max_se) {
- return p->stimer->st_cached_max_se;
- }
- if (p->relatedpeer) {
- p->stimer->st_cached_max_se = p->relatedpeer->stimer.st_max_se;
- return (p->stimer->st_cached_max_se);
- }
- p->stimer->st_cached_max_se = global_max_se;
- return (p->stimer->st_cached_max_se);
- }
- /* Find Min SE timer */
- if (p->stimer->st_cached_min_se) {
- return p->stimer->st_cached_min_se;
- }
- if (p->relatedpeer) {
- p->stimer->st_cached_min_se = p->relatedpeer->stimer.st_min_se;
- return (p->stimer->st_cached_min_se);
- }
- p->stimer->st_cached_min_se = global_min_se;
- return (p->stimer->st_cached_min_se);
- }
- /*! \brief Get the entity (UAC or UAS) that's acting as the session-timer refresher
- * \note This is only called when processing an INVITE, so in that case Asterisk is
- * always currently the UAS. If this is ever used to process responses, the
- * function will have to be changed.
- * \param p pointer to the SIP dialog
- */
- enum st_refresher st_get_refresher(struct sip_pvt *p)
- {
- if (p->stimer->st_cached_ref != SESSION_TIMER_REFRESHER_AUTO) {
- return p->stimer->st_cached_ref;
- }
- if (p->relatedpeer) {
- p->stimer->st_cached_ref = (p->relatedpeer->stimer.st_ref == SESSION_TIMER_REFRESHER_PARAM_UAC) ? SESSION_TIMER_REFRESHER_THEM : SESSION_TIMER_REFRESHER_US;
- return p->stimer->st_cached_ref;
- }
- p->stimer->st_cached_ref = (global_st_refresher == SESSION_TIMER_REFRESHER_PARAM_UAC) ? SESSION_TIMER_REFRESHER_THEM : SESSION_TIMER_REFRESHER_US;
- return p->stimer->st_cached_ref;
- }
- /*!
- * \brief Get the session-timer mode
- * \param p pointer to the SIP dialog
- * \param no_cached Set this to true in order to force a peername lookup on
- * the session timer mode.
- */
- enum st_mode st_get_mode(struct sip_pvt *p, int no_cached)
- {
- if (!p->stimer) {
- sip_st_alloc(p);
- if (!p->stimer) {
- return SESSION_TIMER_MODE_INVALID;
- }
- }
- if (!no_cached && p->stimer->st_cached_mode != SESSION_TIMER_MODE_INVALID)
- return p->stimer->st_cached_mode;
- if (p->relatedpeer) {
- p->stimer->st_cached_mode = p->relatedpeer->stimer.st_mode_oper;
- return p->stimer->st_cached_mode;
- }
- p->stimer->st_cached_mode = global_st_mode;
- return global_st_mode;
- }
- /*! \brief Send keep alive packet to peer */
- static int sip_send_keepalive(const void *data)
- {
- struct sip_peer *peer = (struct sip_peer*) data;
- int res = 0;
- const char keepalive[] = "\r\n";
- peer->keepalivesend = -1;
- if (!peer->keepalive || ast_sockaddr_isnull(&peer->addr)) {
- sip_unref_peer(peer, "release keepalive peer ref");
- return 0;
- }
- /* Send the packet out using the proper method for this peer */
- if ((peer->socket.fd != -1) && (peer->socket.type == AST_TRANSPORT_UDP)) {
- res = ast_sendto(peer->socket.fd, keepalive, sizeof(keepalive), 0, &peer->addr);
- } else if ((peer->socket.type & (AST_TRANSPORT_TCP | AST_TRANSPORT_TLS)) &&
- (peer->socket.tcptls_session) &&
- (peer->socket.tcptls_session->fd != -1)) {
- res = sip_tcptls_write(peer->socket.tcptls_session, keepalive, sizeof(keepalive));
- } else if (peer->socket.type == AST_TRANSPORT_UDP) {
- res = ast_sendto(sipsock, keepalive, sizeof(keepalive), 0, &peer->addr);
- }
- if (res == -1) {
- switch (errno) {
- case EBADF: /* Bad file descriptor - seems like this is generated when the host exist, but doesn't accept the UDP packet */
- case EHOSTUNREACH: /* Host can't be reached */
- case ENETDOWN: /* Interface down */
- case ENETUNREACH: /* Network failure */
- case ECONNREFUSED: /* ICMP port unreachable */
- res = XMIT_ERROR; /* Don't bother with trying to transmit again */
- }
- }
- if (res != sizeof(keepalive)) {
- ast_log(LOG_WARNING, "sip_send_keepalive to %s returned %d: %s\n", ast_sockaddr_stringify(&peer->addr), res, strerror(errno));
- }
- AST_SCHED_REPLACE_UNREF(peer->keepalivesend, sched,
- peer->keepalive * 1000, sip_send_keepalive, peer,
- sip_unref_peer(_data, "removing keepalive peer ref"),
- sip_unref_peer(peer, "removing keepalive peer ref"),
- sip_ref_peer(peer, "adding keepalive peer ref"));
- sip_unref_peer(peer, "release keepalive peer ref");
- return 0;
- }
- /*! \brief React to lack of answer to Qualify poke */
- static int sip_poke_noanswer(const void *data)
- {
- struct sip_peer *peer = (struct sip_peer *)data;
- peer->pokeexpire = -1;
- if (peer->lastms > -1) {
- ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE! Last qualify: %d\n", peer->name, peer->lastms);
- if (sip_cfg.peer_rtupdate) {
- ast_update_realtime(ast_check_realtime("sipregs") ? "sipregs" : "sippeers", "name", peer->name, "lastms", "-1", SENTINEL);
- }
- if (peer->endpoint) {
- RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
- ast_endpoint_set_state(peer->endpoint, AST_ENDPOINT_OFFLINE);
- blob = ast_json_pack("{s: s, s: s}",
- "peer_status", "Unreachable",
- "time", "-1");
- ast_endpoint_blob_publish(peer->endpoint, ast_endpoint_state_type(), blob);
- }
- if (sip_cfg.regextenonqualify) {
- register_peer_exten(peer, FALSE);
- }
- }
- if (peer->call) {
- dialog_unlink_all(peer->call);
- peer->call = dialog_unref(peer->call, "unref dialog peer->call");
- /* peer->call = sip_destroy(peer->call);*/
- }
- /* Don't send a devstate change if nothing changed. */
- if (peer->lastms > -1) {
- peer->lastms = -1;
- ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name);
- }
- /* Try again quickly */
- AST_SCHED_REPLACE_UNREF(peer->pokeexpire, sched,
- DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer,
- sip_unref_peer(_data, "removing poke peer ref"),
- sip_unref_peer(peer, "removing poke peer ref"),
- sip_ref_peer(peer, "adding poke peer ref"));
- /* Release the ref held by the running scheduler entry */
- sip_unref_peer(peer, "release peer poke noanswer ref");
- return 0;
- }
- /*! \brief Check availability of peer, also keep NAT open
- \note This is done with 60 seconds between each ping,
- unless forced by cli or manager. If peer is unreachable,
- we check every 10th second by default.
- \note Do *not* hold a pvt lock while calling this function.
- This function calls sip_alloc, which can cause a deadlock
- if another sip_pvt is held.
- */
- static int sip_poke_peer(struct sip_peer *peer, int force)
- {
- struct sip_pvt *p;
- int xmitres = 0;
- if ((!peer->maxms && !force) || ast_sockaddr_isnull(&peer->addr)) {
- /* IF we have no IP, or this isn't to be monitored, return
- immediately after clearing things out */
- AST_SCHED_DEL_UNREF(sched, peer->pokeexpire,
- sip_unref_peer(peer, "removing poke peer ref"));
- peer->lastms = 0;
- if (peer->call) {
- peer->call = dialog_unref(peer->call, "unref dialog peer->call");
- }
- return 0;
- }
- if (peer->call) {
- if (sipdebug) {
- ast_log(LOG_NOTICE, "Still have a QUALIFY dialog active, deleting\n");
- }
- dialog_unlink_all(peer->call);
- peer->call = dialog_unref(peer->call, "unref dialog peer->call");
- /* peer->call = sip_destroy(peer->call); */
- }
- if (!(p = sip_alloc(NULL, NULL, 0, SIP_OPTIONS, NULL, 0))) {
- return -1;
- }
- peer->call = dialog_ref(p, "copy sip alloc from p to peer->call");
- p->sa = peer->addr;
- p->recv = peer->addr;
- copy_socket_data(&p->socket, &peer->socket);
- ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
- ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
- ast_copy_flags(&p->flags[2], &peer->flags[2], SIP_PAGE3_FLAGS_TO_COPY);
- sip_route_copy(&p->route, &peer->path);
- if (!sip_route_empty(&p->route)) {
- /* Parse SIP URI of first route-set hop and use it as target address */
- __set_address_from_contact(sip_route_first_uri(&p->route), &p->sa, p->socket.type == AST_TRANSPORT_TLS ? 1 : 0);
- }
- /* Get the outbound proxy information */
- ref_proxy(p, obproxy_get(p, peer));
- /* Send OPTIONs to peer's fullcontact */
- if (!ast_strlen_zero(peer->fullcontact)) {
- ast_string_field_set(p, fullcontact, peer->fullcontact);
- }
- if (!ast_strlen_zero(peer->fromuser)) {
- ast_string_field_set(p, fromuser, peer->fromuser);
- }
- if (!ast_strlen_zero(peer->tohost)) {
- ast_string_field_set(p, tohost, peer->tohost);
- } else {
- ast_string_field_set(p, tohost, ast_sockaddr_stringify_host_remote(&peer->addr));
- }
- /* Recalculate our side, and recalculate Call ID */
- ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
- build_via(p);
- /* Change the dialog callid. */
- change_callid_pvt(p, NULL);
- AST_SCHED_DEL_UNREF(sched, peer->pokeexpire,
- sip_unref_peer(peer, "removing poke peer ref"));
- if (p->relatedpeer)
- p->relatedpeer = sip_unref_peer(p->relatedpeer,"unsetting the relatedpeer field in the dialog, before it is set to something else.");
- p->relatedpeer = sip_ref_peer(peer, "setting the relatedpeer field in the dialog");
- ast_set_flag(&p->flags[0], SIP_OUTGOING);
- #ifdef VOCAL_DATA_HACK
- ast_copy_string(p->username, "__VOCAL_DATA_SHOULD_READ_THE_SIP_SPEC__", sizeof(p->username));
- xmitres = transmit_invite(p, SIP_INVITE, 0, 2, NULL); /* sinks the p refcount */
- #else
- xmitres = transmit_invite(p, SIP_OPTIONS, 0, 2, NULL); /* sinks the p refcount */
- #endif
- peer->ps = ast_tvnow();
- if (xmitres == XMIT_ERROR) {
- /* Immediately unreachable, network problems */
- sip_poke_noanswer(sip_ref_peer(peer, "add ref for peerexpire (fake, for sip_poke_noanswer to remove)"));
- } else if (!force) {
- AST_SCHED_REPLACE_UNREF(peer->pokeexpire, sched, peer->maxms * 2, sip_poke_noanswer, peer,
- sip_unref_peer(_data, "removing poke peer ref"),
- sip_unref_peer(peer, "removing poke peer ref"),
- sip_ref_peer(peer, "adding poke peer ref"));
- }
- dialog_unref(p, "unref dialog at end of sip_poke_peer, obtained from sip_alloc, just before it goes out of scope");
- return 0;
- }
- /*! \brief Part of PBX channel interface
- \note
- \par Return values:---
- If we have qualify on and the device is not reachable, regardless of registration
- state we return AST_DEVICE_UNAVAILABLE
- For peers with call limit:
- - not registered AST_DEVICE_UNAVAILABLE
- - registered, no call AST_DEVICE_NOT_INUSE
- - registered, active calls AST_DEVICE_INUSE
- - registered, call limit reached AST_DEVICE_BUSY
- - registered, onhold AST_DEVICE_ONHOLD
- - registered, ringing AST_DEVICE_RINGING
- For peers without call limit:
- - not registered AST_DEVICE_UNAVAILABLE
- - registered AST_DEVICE_NOT_INUSE
- - fixed IP (!dynamic) AST_DEVICE_NOT_INUSE
- Peers that does not have a known call and can't be reached by OPTIONS
- - unreachable AST_DEVICE_UNAVAILABLE
- If we return AST_DEVICE_UNKNOWN, the device state engine will try to find
- out a state by walking the channel list.
- The queue system (\ref app_queue.c) treats a member as "active"
- if devicestate is != AST_DEVICE_UNAVAILBALE && != AST_DEVICE_INVALID
- When placing a call to the queue member, queue system sets a member to busy if
- != AST_DEVICE_NOT_INUSE and != AST_DEVICE_UNKNOWN
- */
- static int sip_devicestate(const char *data)
- {
- char *host;
- char *tmp;
- struct sip_peer *p;
- int res = AST_DEVICE_INVALID;
- /* make sure data is not null. Maybe unnecessary, but better be safe */
- host = ast_strdupa(data ? data : "");
- if ((tmp = strchr(host, '@')))
- host = tmp + 1;
- ast_debug(3, "Checking device state for peer %s\n", host);
- /* If sip_find_peer asks for a realtime peer, then this breaks rtautoclear. This
- * is because when a peer tries to autoexpire, the last thing it does is to
- * queue up an event telling the system that the devicestate has changed
- * (presumably to unavailable). If we ask for a realtime peer here, this would
- * load it BACK into memory, thus defeating the point of trying to clear dead
- * hosts out of memory.
- */
- if ((p = sip_find_peer(host, NULL, FALSE, FINDALLDEVICES, TRUE, 0))) {
- if (!(ast_sockaddr_isnull(&p->addr) && ast_sockaddr_isnull(&p->defaddr))) {
- /* we have an address for the peer */
- /* Check status in this order
- - Hold
- - Ringing
- - Busy (enforced only by call limit)
- - Inuse (we have a call)
- - Unreachable (qualify)
- If we don't find any of these state, report AST_DEVICE_NOT_INUSE
- for registered devices */
- if (p->onhold)
- /* First check for hold or ring states */
- res = AST_DEVICE_ONHOLD;
- else if (p->ringing) {
- if (p->ringing == p->inuse)
- res = AST_DEVICE_RINGING;
- else
- res = AST_DEVICE_RINGINUSE;
- } else if (p->call_limit && (p->inuse == p->call_limit))
- /* check call limit */
- res = AST_DEVICE_BUSY;
- else if (p->call_limit && p->busy_level && p->inuse >= p->busy_level)
- /* We're forcing busy before we've reached the call limit */
- res = AST_DEVICE_BUSY;
- else if (p->call_limit && p->inuse)
- /* Not busy, but we do have a call */
- res = AST_DEVICE_INUSE;
- else if (p->maxms && ((p->lastms > p->maxms) || (p->lastms < 0)))
- /* We don't have a call. Are we reachable at all? Requires qualify= */
- res = AST_DEVICE_UNAVAILABLE;
- else /* Default reply if we're registered and have no other data */
- res = AST_DEVICE_NOT_INUSE;
- } else {
- /* there is no address, it's unavailable */
- res = AST_DEVICE_UNAVAILABLE;
- }
- sip_unref_peer(p, "sip_unref_peer, from sip_devicestate, release ref from sip_find_peer");
- }
- return res;
- }
- /*! \brief PBX interface function -build SIP pvt structure
- * SIP calls initiated by the PBX arrive here.
- *
- * \verbatim
- * SIP Dial string syntax:
- * SIP/devicename
- * or SIP/username@domain (SIP uri)
- * or SIP/username[:password[:md5secret[:authname[:transport]]]]@host[:port]
- * or SIP/devicename/extension
- * or SIP/devicename/extension/IPorHost
- * or SIP/username@domain//IPorHost
- * and there is an optional [!dnid] argument you can append to alter the
- * To: header.
- * \endverbatim
- */
- static struct ast_channel *sip_request_call(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *dest, int *cause)
- {
- struct sip_pvt *p;
- struct ast_channel *tmpc = NULL;
- char *ext = NULL, *host;
- char tmp[256];
- struct ast_str *codec_buf = ast_str_alloca(64);
- struct ast_str *cap_buf = ast_str_alloca(64);
- char *dnid;
- char *secret = NULL;
- char *md5secret = NULL;
- char *authname = NULL;
- char *trans = NULL;
- char dialstring[256];
- char *remote_address;
- enum ast_transport transport = 0;
- ast_callid callid;
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(peerorhost);
- AST_APP_ARG(exten);
- AST_APP_ARG(remote_address);
- );
- /* mask request with some set of allowed formats.
- * XXX this needs to be fixed.
- * The original code uses AST_FORMAT_AUDIO_MASK, but it is
- * unclear what to use here. We have global_capabilities, which is
- * configured from sip.conf, and sip_tech.capabilities, which is
- * hardwired to all audio formats.
- */
- if (!(ast_format_cap_has_type(cap, AST_MEDIA_TYPE_AUDIO))) {
- ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format %s while capability is %s\n",
- ast_format_cap_get_names(cap, &codec_buf),
- ast_format_cap_get_names(sip_cfg.caps, &cap_buf));
- *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL; /* Can't find codec to connect to host */
- return NULL;
- }
- ast_debug(1, "Asked to create a SIP channel with formats: %s\n", ast_format_cap_get_names(cap, &codec_buf));
- if (ast_strlen_zero(dest)) {
- ast_log(LOG_ERROR, "Unable to create channel with empty destination.\n");
- *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
- return NULL;
- }
- callid = ast_read_threadstorage_callid();
- if (!(p = sip_alloc(NULL, NULL, 0, SIP_INVITE, NULL, callid))) {
- ast_log(LOG_ERROR, "Unable to build sip pvt data for '%s' (Out of memory or socket error)\n", dest);
- *cause = AST_CAUSE_SWITCH_CONGESTION;
- return NULL;
- }
- p->outgoing_call = TRUE;
- snprintf(dialstring, sizeof(dialstring), "%s/%s", type, dest);
- ast_string_field_set(p, dialstring, dialstring);
- if (!(p->options = ast_calloc(1, sizeof(*p->options)))) {
- dialog_unlink_all(p);
- dialog_unref(p, "unref dialog p from mem fail");
- /* sip_destroy(p); */
- ast_log(LOG_ERROR, "Unable to build option SIP data structure - Out of memory\n");
- *cause = AST_CAUSE_SWITCH_CONGESTION;
- return NULL;
- }
- /* Save the destination, the SIP dial string */
- ast_copy_string(tmp, dest, sizeof(tmp));
- /* Find DNID and take it away */
- dnid = strchr(tmp, '!');
- if (dnid != NULL) {
- *dnid++ = '\0';
- ast_string_field_set(p, todnid, dnid);
- }
- /* Divvy up the items separated by slashes */
- AST_NONSTANDARD_APP_ARGS(args, tmp, '/');
- /* Find at sign - @ */
- host = strchr(args.peerorhost, '@');
- if (host) {
- *host++ = '\0';
- ext = args.peerorhost;
- secret = strchr(ext, ':');
- }
- if (secret) {
- *secret++ = '\0';
- md5secret = strchr(secret, ':');
- }
- if (md5secret) {
- *md5secret++ = '\0';
- authname = strchr(md5secret, ':');
- }
- if (authname) {
- *authname++ = '\0';
- trans = strchr(authname, ':');
- }
- if (trans) {
- *trans++ = '\0';
- if (!strcasecmp(trans, "tcp"))
- transport = AST_TRANSPORT_TCP;
- else if (!strcasecmp(trans, "tls"))
- transport = AST_TRANSPORT_TLS;
- else {
- if (strcasecmp(trans, "udp"))
- ast_log(LOG_WARNING, "'%s' is not a valid transport option to Dial() for SIP calls, using udp by default.\n", trans);
- transport = AST_TRANSPORT_UDP;
- }
- } else { /* use default */
- transport = AST_TRANSPORT_UDP;
- }
- if (!host) {
- ext = args.exten;
- host = args.peerorhost;
- remote_address = args.remote_address;
- } else {
- remote_address = args.remote_address;
- if (!ast_strlen_zero(args.exten)) {
- ast_log(LOG_NOTICE, "Conflicting extension values given. Using '%s' and not '%s'\n", ext, args.exten);
- }
- }
- if (!ast_strlen_zero(remote_address)) {
- p->options->outboundproxy = proxy_from_config(remote_address, 0, NULL);
- if (!p->options->outboundproxy) {
- ast_log(LOG_WARNING, "Unable to parse outboundproxy %s. We will not use this remote IP address\n", remote_address);
- }
- }
- set_socket_transport(&p->socket, transport);
- /* We now have
- host = peer name, DNS host name or DNS domain (for SRV)
- ext = extension (user part of URI)
- dnid = destination of the call (applies to the To: header)
- */
- if (create_addr(p, host, NULL, 1)) {
- *cause = AST_CAUSE_UNREGISTERED;
- ast_debug(3, "Cant create SIP call - target device not registered\n");
- dialog_unlink_all(p);
- dialog_unref(p, "unref dialog p UNREGISTERED");
- /* sip_destroy(p); */
- return NULL;
- }
- if (ast_strlen_zero(p->peername) && ext)
- ast_string_field_set(p, peername, ext);
- /* Recalculate our side, and recalculate Call ID */
- ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
- /* When chan_sip is first loaded or reloaded, we need to check for NAT and set the appropiate flags
- now that we have the auto_* settings. */
- check_for_nat(&p->sa, p);
- /* If there is a peer related to this outgoing call and it hasn't re-registered after
- a reload, we need to set the peer's NAT flags accordingly. */
- if (p->relatedpeer) {
- if (!ast_strlen_zero(p->relatedpeer->fullcontact) && !p->natdetected &&
- (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) && !ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT))) {
- /* We need to make an attempt to determine if a peer is behind NAT
- if the peer has the auto_force_rport flag set. */
- struct ast_sockaddr tmpaddr;
- __set_address_from_contact(p->relatedpeer->fullcontact, &tmpaddr, 0);
- check_for_nat(&tmpaddr, p);
- }
- set_peer_nat(p, p->relatedpeer);
- }
- do_setnat(p);
- build_via(p);
- /* Change the dialog callid. */
- change_callid_pvt(p, NULL);
- /* We have an extension to call, don't use the full contact here */
- /* This to enable dialing registered peers with extension dialling,
- like SIP/peername/extension
- SIP/peername will still use the full contact
- */
- if (ext) {
- ast_string_field_set(p, username, ext);
- ast_string_field_set(p, fullcontact, NULL);
- }
- if (secret && !ast_strlen_zero(secret))
- ast_string_field_set(p, peersecret, secret);
- if (md5secret && !ast_strlen_zero(md5secret))
- ast_string_field_set(p, peermd5secret, md5secret);
- if (authname && !ast_strlen_zero(authname))
- ast_string_field_set(p, authname, authname);
- #if 0
- printf("Setting up to call extension '%s' at '%s'\n", ext ? ext : "<none>", host);
- #endif
- ast_format_cap_append_from_cap(p->prefcaps, cap, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_get_compatible(cap, p->caps, p->jointcaps);
- sip_pvt_lock(p);
- tmpc = sip_new(p, AST_STATE_DOWN, host, assignedids, requestor, callid); /* Place the call */
- sip_pvt_unlock(p);
- if (!tmpc) {
- dialog_unlink_all(p);
- /* sip_destroy(p); */
- } else {
- ast_channel_unlock(tmpc);
- }
- dialog_unref(p, "toss pvt ptr at end of sip_request_call");
- ast_update_use_count();
- restart_monitor();
- return tmpc;
- }
- /*! \brief Parse insecure= setting in sip.conf and set flags according to setting */
- static void set_insecure_flags (struct ast_flags *flags, const char *value, int lineno)
- {
- if (ast_strlen_zero(value))
- return;
- if (!ast_false(value)) {
- char buf[64];
- char *word, *next;
- ast_copy_string(buf, value, sizeof(buf));
- next = buf;
- while ((word = strsep(&next, ","))) {
- if (!strcasecmp(word, "port"))
- ast_set_flag(&flags[0], SIP_INSECURE_PORT);
- else if (!strcasecmp(word, "invite"))
- ast_set_flag(&flags[0], SIP_INSECURE_INVITE);
- else
- ast_log(LOG_WARNING, "Unknown insecure mode '%s' on line %d\n", value, lineno);
- }
- }
- }
- /*!
- \brief Handle T.38 configuration options common to users and peers
- \returns non-zero if any config options were handled, zero otherwise
- */
- static int handle_t38_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v,
- unsigned int *maxdatagram)
- {
- int res = 1;
- if (!strcasecmp(v->name, "t38pt_udptl")) {
- char *buf = ast_strdupa(v->value);
- char *word, *next = buf;
- ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT);
- while ((word = strsep(&next, ","))) {
- if (ast_true(word) || !strcasecmp(word, "fec")) {
- ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT);
- ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL_FEC);
- } else if (!strcasecmp(word, "redundancy")) {
- ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT);
- ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY);
- } else if (!strcasecmp(word, "none")) {
- ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT);
- ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL);
- } else if (!strncasecmp(word, "maxdatagram=", 12)) {
- if (sscanf(&word[12], "%30u", maxdatagram) != 1) {
- ast_log(LOG_WARNING, "Invalid maxdatagram '%s' at line %d of %s\n", v->value, v->lineno, config);
- *maxdatagram = global_t38_maxdatagram;
- }
- }
- }
- } else if (!strcasecmp(v->name, "t38pt_usertpsource")) {
- ast_set_flag(&mask[1], SIP_PAGE2_UDPTL_DESTINATION);
- ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_UDPTL_DESTINATION);
- } else {
- res = 0;
- }
- return res;
- }
- /*!
- \brief Handle flag-type options common to configuration of devices - peers
- \param flags array of three struct ast_flags
- \param mask array of three struct ast_flags
- \param v linked list of config variables to process
- \returns non-zero if any config options were handled, zero otherwise
- */
- static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v)
- {
- int res = 1;
- if (!strcasecmp(v->name, "trustrpid")) {
- ast_set_flag(&mask[0], SIP_TRUSTRPID);
- ast_set2_flag(&flags[0], ast_true(v->value), SIP_TRUSTRPID);
- } else if (!strcasecmp(v->name, "supportpath")) {
- ast_set_flag(&mask[0], SIP_USEPATH);
- ast_set2_flag(&flags[0], ast_true(v->value), SIP_USEPATH);
- } else if (!strcasecmp(v->name, "sendrpid")) {
- ast_set_flag(&mask[0], SIP_SENDRPID);
- if (!strcasecmp(v->value, "pai")) {
- ast_set_flag(&flags[0], SIP_SENDRPID_PAI);
- } else if (!strcasecmp(v->value, "rpid")) {
- ast_set_flag(&flags[0], SIP_SENDRPID_RPID);
- } else if (ast_true(v->value)) {
- ast_set_flag(&flags[0], SIP_SENDRPID_RPID);
- }
- } else if (!strcasecmp(v->name, "rpid_update")) {
- ast_set_flag(&mask[1], SIP_PAGE2_RPID_UPDATE);
- ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RPID_UPDATE);
- } else if (!strcasecmp(v->name, "rpid_immediate")) {
- ast_set_flag(&mask[1], SIP_PAGE2_RPID_IMMEDIATE);
- ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RPID_IMMEDIATE);
- } else if (!strcasecmp(v->name, "trust_id_outbound")) {
- ast_set_flag(&mask[1], SIP_PAGE2_TRUST_ID_OUTBOUND);
- ast_clear_flag(&flags[1], SIP_PAGE2_TRUST_ID_OUTBOUND);
- if (!strcasecmp(v->value, "legacy")) {
- ast_set_flag(&flags[1], SIP_PAGE2_TRUST_ID_OUTBOUND_LEGACY);
- } else if (ast_true(v->value)) {
- ast_set_flag(&flags[1], SIP_PAGE2_TRUST_ID_OUTBOUND_YES);
- } else if (ast_false(v->value)) {
- ast_set_flag(&flags[1], SIP_PAGE2_TRUST_ID_OUTBOUND_NO);
- } else {
- ast_log(LOG_WARNING, "Unknown trust_id_outbound mode '%s' on line %d, using legacy\n", v->value, v->lineno);
- ast_set_flag(&flags[1], SIP_PAGE2_TRUST_ID_OUTBOUND_LEGACY);
- }
- } else if (!strcasecmp(v->name, "g726nonstandard")) {
- ast_set_flag(&mask[0], SIP_G726_NONSTANDARD);
- ast_set2_flag(&flags[0], ast_true(v->value), SIP_G726_NONSTANDARD);
- } else if (!strcasecmp(v->name, "useclientcode")) {
- ast_set_flag(&mask[0], SIP_USECLIENTCODE);
- ast_set2_flag(&flags[0], ast_true(v->value), SIP_USECLIENTCODE);
- } else if (!strcasecmp(v->name, "dtmfmode")) {
- ast_set_flag(&mask[0], SIP_DTMF);
- ast_clear_flag(&flags[0], SIP_DTMF);
- if (!strcasecmp(v->value, "inband"))
- ast_set_flag(&flags[0], SIP_DTMF_INBAND);
- else if (!strcasecmp(v->value, "rfc2833"))
- ast_set_flag(&flags[0], SIP_DTMF_RFC2833);
- else if (!strcasecmp(v->value, "info"))
- ast_set_flag(&flags[0], SIP_DTMF_INFO);
- else if (!strcasecmp(v->value, "shortinfo"))
- ast_set_flag(&flags[0], SIP_DTMF_SHORTINFO);
- else if (!strcasecmp(v->value, "auto"))
- ast_set_flag(&flags[0], SIP_DTMF_AUTO);
- else {
- ast_log(LOG_WARNING, "Unknown dtmf mode '%s' on line %d, using rfc2833\n", v->value, v->lineno);
- ast_set_flag(&flags[0], SIP_DTMF_RFC2833);
- }
- } else if (!strcasecmp(v->name, "nat")) {
- sip_parse_nat_option(v->value, mask, flags);
- } else if (!strcasecmp(v->name, "directmedia") || !strcasecmp(v->name, "canreinvite")) {
- ast_set_flag(&mask[0], SIP_REINVITE);
- ast_clear_flag(&flags[0], SIP_REINVITE);
- if (ast_true(v->value)) {
- ast_set_flag(&flags[0], SIP_DIRECT_MEDIA | SIP_DIRECT_MEDIA_NAT);
- } else if (!ast_false(v->value)) {
- char buf[64];
- char *word, *next = buf;
- ast_copy_string(buf, v->value, sizeof(buf));
- while ((word = strsep(&next, ","))) {
- if (!strcasecmp(word, "update")) {
- ast_set_flag(&flags[0], SIP_REINVITE_UPDATE | SIP_DIRECT_MEDIA);
- } else if (!strcasecmp(word, "nonat")) {
- ast_set_flag(&flags[0], SIP_DIRECT_MEDIA);
- ast_clear_flag(&flags[0], SIP_DIRECT_MEDIA_NAT);
- } else if (!strcasecmp(word, "outgoing")) {
- ast_set_flag(&flags[0], SIP_DIRECT_MEDIA);
- ast_set_flag(&mask[2], SIP_PAGE3_DIRECT_MEDIA_OUTGOING);
- ast_set_flag(&flags[2], SIP_PAGE3_DIRECT_MEDIA_OUTGOING);
- } else {
- ast_log(LOG_WARNING, "Unknown directmedia mode '%s' on line %d\n", v->value, v->lineno);
- }
- }
- }
- } else if (!strcasecmp(v->name, "insecure")) {
- ast_set_flag(&mask[0], SIP_INSECURE);
- ast_clear_flag(&flags[0], SIP_INSECURE);
- set_insecure_flags(&flags[0], v->value, v->lineno);
- } else if (!strcasecmp(v->name, "progressinband")) {
- ast_set_flag(&mask[0], SIP_PROG_INBAND);
- ast_clear_flag(&flags[0], SIP_PROG_INBAND);
- if (ast_true(v->value))
- ast_set_flag(&flags[0], SIP_PROG_INBAND_YES);
- else if (!strcasecmp(v->value, "never"))
- ast_set_flag(&flags[0], SIP_PROG_INBAND_NEVER);
- } else if (!strcasecmp(v->name, "promiscredir")) {
- ast_set_flag(&mask[0], SIP_PROMISCREDIR);
- ast_set2_flag(&flags[0], ast_true(v->value), SIP_PROMISCREDIR);
- } else if (!strcasecmp(v->name, "videosupport")) {
- if (!strcasecmp(v->value, "always")) {
- ast_set_flag(&mask[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS);
- ast_set_flag(&flags[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS);
- } else {
- ast_set_flag(&mask[1], SIP_PAGE2_VIDEOSUPPORT);
- ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_VIDEOSUPPORT);
- }
- } else if (!strcasecmp(v->name, "textsupport")) {
- ast_set_flag(&mask[1], SIP_PAGE2_TEXTSUPPORT);
- ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_TEXTSUPPORT);
- res = 1;
- } else if (!strcasecmp(v->name, "allowoverlap")) {
- ast_set_flag(&mask[1], SIP_PAGE2_ALLOWOVERLAP);
- ast_clear_flag(&flags[1], SIP_PAGE2_ALLOWOVERLAP);
- if (ast_true(v->value)) {
- ast_set_flag(&flags[1], SIP_PAGE2_ALLOWOVERLAP_YES);
- } else if (!strcasecmp(v->value, "dtmf")){
- ast_set_flag(&flags[1], SIP_PAGE2_ALLOWOVERLAP_DTMF);
- }
- } else if (!strcasecmp(v->name, "allowsubscribe")) {
- ast_set_flag(&mask[1], SIP_PAGE2_ALLOWSUBSCRIBE);
- ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWSUBSCRIBE);
- } else if (!strcasecmp(v->name, "ignoresdpversion")) {
- ast_set_flag(&mask[1], SIP_PAGE2_IGNORESDPVERSION);
- ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_IGNORESDPVERSION);
- } else if (!strcasecmp(v->name, "faxdetect")) {
- ast_set_flag(&mask[1], SIP_PAGE2_FAX_DETECT);
- if (ast_true(v->value)) {
- ast_set_flag(&flags[1], SIP_PAGE2_FAX_DETECT_BOTH);
- } else if (ast_false(v->value)) {
- ast_clear_flag(&flags[1], SIP_PAGE2_FAX_DETECT_BOTH);
- } else {
- char *buf = ast_strdupa(v->value);
- char *word, *next = buf;
- while ((word = strsep(&next, ","))) {
- if (!strcasecmp(word, "cng")) {
- ast_set_flag(&flags[1], SIP_PAGE2_FAX_DETECT_CNG);
- } else if (!strcasecmp(word, "t38")) {
- ast_set_flag(&flags[1], SIP_PAGE2_FAX_DETECT_T38);
- } else {
- ast_log(LOG_WARNING, "Unknown faxdetect mode '%s' on line %d.\n", word, v->lineno);
- }
- }
- }
- } else if (!strcasecmp(v->name, "rfc2833compensate")) {
- ast_set_flag(&mask[1], SIP_PAGE2_RFC2833_COMPENSATE);
- ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RFC2833_COMPENSATE);
- } else if (!strcasecmp(v->name, "buggymwi")) {
- ast_set_flag(&mask[1], SIP_PAGE2_BUGGY_MWI);
- ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_BUGGY_MWI);
- } else
- res = 0;
- return res;
- }
- /*! \brief Add SIP domain to list of domains we are responsible for */
- static int add_sip_domain(const char *domain, const enum domain_mode mode, const char *context)
- {
- struct domain *d;
- if (ast_strlen_zero(domain)) {
- ast_log(LOG_WARNING, "Zero length domain.\n");
- return 1;
- }
- if (!(d = ast_calloc(1, sizeof(*d))))
- return 0;
- ast_copy_string(d->domain, domain, sizeof(d->domain));
- if (!ast_strlen_zero(context))
- ast_copy_string(d->context, context, sizeof(d->context));
- d->mode = mode;
- AST_LIST_LOCK(&domain_list);
- AST_LIST_INSERT_TAIL(&domain_list, d, list);
- AST_LIST_UNLOCK(&domain_list);
- if (sipdebug)
- ast_debug(1, "Added local SIP domain '%s'\n", domain);
- return 1;
- }
- /*! \brief check_sip_domain: Check if domain part of uri is local to our server */
- static int check_sip_domain(const char *domain, char *context, size_t len)
- {
- struct domain *d;
- int result = 0;
- AST_LIST_LOCK(&domain_list);
- AST_LIST_TRAVERSE(&domain_list, d, list) {
- if (strcasecmp(d->domain, domain)) {
- continue;
- }
- if (len && !ast_strlen_zero(d->context))
- ast_copy_string(context, d->context, len);
- result = 1;
- break;
- }
- AST_LIST_UNLOCK(&domain_list);
- return result;
- }
- /*! \brief Clear our domain list (at reload) */
- static void clear_sip_domains(void)
- {
- struct domain *d;
- AST_LIST_LOCK(&domain_list);
- while ((d = AST_LIST_REMOVE_HEAD(&domain_list, list)))
- ast_free(d);
- AST_LIST_UNLOCK(&domain_list);
- }
- /*!
- * \internal
- * \brief Realm authentication container destructor.
- *
- * \param obj Container object to destroy.
- *
- * \return Nothing
- */
- static void destroy_realm_authentication(void *obj)
- {
- struct sip_auth_container *credentials = obj;
- struct sip_auth *auth;
- while ((auth = AST_LIST_REMOVE_HEAD(&credentials->list, node))) {
- ast_free(auth);
- }
- }
- /*!
- * \internal
- * \brief Add realm authentication to credentials.
- *
- * \param credentials Realm authentication container to create/add authentication credentials.
- * \param configuration Credential configuration value.
- * \param lineno Line number in config file.
- *
- * \return Nothing
- */
- static void add_realm_authentication(struct sip_auth_container **credentials, const char *configuration, int lineno)
- {
- char *authcopy;
- char *username=NULL, *realm=NULL, *secret=NULL, *md5secret=NULL;
- struct sip_auth *auth;
- if (ast_strlen_zero(configuration)) {
- /* Nothing to add */
- return;
- }
- ast_debug(1, "Auth config :: %s\n", configuration);
- authcopy = ast_strdupa(configuration);
- username = authcopy;
- /* split user[:secret] and relm */
- realm = strrchr(username, '@');
- if (realm)
- *realm++ = '\0';
- if (ast_strlen_zero(username) || ast_strlen_zero(realm)) {
- ast_log(LOG_WARNING, "Format for authentication entry is user[:secret]@realm at line %d\n", lineno);
- return;
- }
- /* parse username at ':' for secret, or '#" for md5secret */
- if ((secret = strchr(username, ':'))) {
- *secret++ = '\0';
- } else if ((md5secret = strchr(username, '#'))) {
- *md5secret++ = '\0';
- }
- /* Create the continer if needed. */
- if (!*credentials) {
- *credentials = ao2_t_alloc(sizeof(**credentials), destroy_realm_authentication,
- "Create realm auth container.");
- if (!*credentials) {
- /* Failed to create the credentials container. */
- return;
- }
- }
- /* Create the authentication credential entry. */
- auth = ast_calloc(1, sizeof(*auth));
- if (!auth) {
- return;
- }
- ast_copy_string(auth->realm, realm, sizeof(auth->realm));
- ast_copy_string(auth->username, username, sizeof(auth->username));
- if (secret)
- ast_copy_string(auth->secret, secret, sizeof(auth->secret));
- if (md5secret)
- ast_copy_string(auth->md5secret, md5secret, sizeof(auth->md5secret));
- /* Add credential to container list. */
- AST_LIST_INSERT_TAIL(&(*credentials)->list, auth, node);
- ast_verb(3, "Added authentication for realm %s\n", realm);
- }
- /*!
- * \internal
- * \brief Find authentication for a specific realm.
- *
- * \param credentials Realm authentication container to search.
- * \param realm Authentication realm to find.
- *
- * \return Found authentication credential or NULL.
- */
- static struct sip_auth *find_realm_authentication(struct sip_auth_container *credentials, const char *realm)
- {
- struct sip_auth *auth;
- if (credentials) {
- AST_LIST_TRAVERSE(&credentials->list, auth, node) {
- if (!strcasecmp(auth->realm, realm)) {
- break;
- }
- }
- } else {
- auth = NULL;
- }
- return auth;
- }
- /*! \brief
- * implement the setvar config line
- */
- static struct ast_variable *add_var(const char *buf, struct ast_variable *list)
- {
- struct ast_variable *tmpvar = NULL;
- char *varname = ast_strdupa(buf), *varval = NULL;
- if ((varval = strchr(varname, '='))) {
- *varval++ = '\0';
- if ((tmpvar = ast_variable_new(varname, varval, ""))) {
- tmpvar->next = list;
- list = tmpvar;
- }
- }
- return list;
- }
- /*! \brief Set peer defaults before configuring specific configurations */
- static void set_peer_defaults(struct sip_peer *peer)
- {
- if (peer->expire == 0) {
- /* Don't reset expire or port time during reload
- if we have an active registration
- */
- peer->expire = -1;
- peer->pokeexpire = -1;
- peer->keepalivesend = -1;
- set_socket_transport(&peer->socket, AST_TRANSPORT_UDP);
- }
- peer->type = SIP_TYPE_PEER;
- ast_copy_flags(&peer->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
- ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
- ast_copy_flags(&peer->flags[2], &global_flags[2], SIP_PAGE3_FLAGS_TO_COPY);
- ast_string_field_set(peer, context, sip_cfg.default_context);
- ast_string_field_set(peer, record_on_feature, sip_cfg.default_record_on_feature);
- ast_string_field_set(peer, record_off_feature, sip_cfg.default_record_off_feature);
- ast_string_field_set(peer, messagecontext, sip_cfg.messagecontext);
- ast_string_field_set(peer, subscribecontext, sip_cfg.default_subscribecontext);
- ast_string_field_set(peer, language, default_language);
- ast_string_field_set(peer, mohinterpret, default_mohinterpret);
- ast_string_field_set(peer, mohsuggest, default_mohsuggest);
- ast_string_field_set(peer, engine, default_engine);
- ast_sockaddr_setnull(&peer->addr);
- ast_sockaddr_setnull(&peer->defaddr);
- ast_format_cap_append_from_cap(peer->caps, sip_cfg.caps, AST_MEDIA_TYPE_UNKNOWN);
- peer->maxcallbitrate = default_maxcallbitrate;
- peer->rtptimeout = global_rtptimeout;
- peer->rtpholdtimeout = global_rtpholdtimeout;
- peer->rtpkeepalive = global_rtpkeepalive;
- peer->allowtransfer = sip_cfg.allowtransfer;
- peer->autoframing = global_autoframing;
- peer->t38_maxdatagram = global_t38_maxdatagram;
- peer->qualifyfreq = global_qualifyfreq;
- if (global_callcounter)
- peer->call_limit=INT_MAX;
- ast_string_field_set(peer, vmexten, default_vmexten);
- ast_string_field_set(peer, secret, "");
- ast_string_field_set(peer, description, "");
- ast_string_field_set(peer, remotesecret, "");
- ast_string_field_set(peer, md5secret, "");
- ast_string_field_set(peer, cid_num, "");
- ast_string_field_set(peer, cid_name, "");
- ast_string_field_set(peer, cid_tag, "");
- ast_string_field_set(peer, fromdomain, "");
- ast_string_field_set(peer, fromuser, "");
- ast_string_field_set(peer, regexten, "");
- peer->callgroup = 0;
- peer->pickupgroup = 0;
- peer->maxms = default_qualify;
- peer->keepalive = default_keepalive;
- ast_string_field_set(peer, zone, default_zone);
- peer->stimer.st_mode_oper = global_st_mode; /* Session-Timers */
- peer->stimer.st_ref = global_st_refresher;
- peer->stimer.st_min_se = global_min_se;
- peer->stimer.st_max_se = global_max_se;
- peer->timer_t1 = global_t1;
- peer->timer_b = global_timer_b;
- clear_peer_mailboxes(peer);
- peer->disallowed_methods = sip_cfg.disallowed_methods;
- peer->transports = default_transports;
- peer->default_outbound_transport = default_primary_transport;
- if (peer->outboundproxy) {
- ao2_ref(peer->outboundproxy, -1);
- peer->outboundproxy = NULL;
- }
- }
- /*! \brief Create temporary peer (used in autocreatepeer mode) */
- static struct sip_peer *temp_peer(const char *name)
- {
- struct sip_peer *peer;
- if (!(peer = ao2_t_alloc(sizeof(*peer), sip_destroy_peer_fn, "allocate a peer struct")))
- return NULL;
- if (ast_string_field_init(peer, 512)) {
- ao2_t_ref(peer, -1, "failed to string_field_init, drop peer");
- return NULL;
- }
- if (!(peer->cc_params = ast_cc_config_params_init())) {
- ao2_t_ref(peer, -1, "failed to allocate cc_params for peer");
- return NULL;
- }
- if (!(peer->caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
- ao2_t_ref(peer, -1, "failed to allocate format capabilities, drop peer");
- return NULL;
- }
- ast_atomic_fetchadd_int(&apeerobjs, 1);
- set_peer_defaults(peer);
- ast_copy_string(peer->name, name, sizeof(peer->name));
- peer->selfdestruct = TRUE;
- peer->host_dynamic = TRUE;
- reg_source_db(peer);
- return peer;
- }
- /*! \todo document this function */
- static void add_peer_mailboxes(struct sip_peer *peer, const char *value)
- {
- char *next;
- char *mbox;
- next = ast_strdupa(value);
- while ((mbox = strsep(&next, ","))) {
- struct sip_mailbox *mailbox;
- int duplicate = 0;
- /* remove leading/trailing whitespace from mailbox string */
- mbox = ast_strip(mbox);
- if (ast_strlen_zero(mbox)) {
- continue;
- }
- /* Check whether the mailbox is already in the list */
- AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
- if (!strcmp(mailbox->id, mbox)) {
- duplicate = 1;
- break;
- }
- }
- if (duplicate) {
- continue;
- }
- mailbox = ast_calloc(1, sizeof(*mailbox) + strlen(mbox));
- if (!mailbox) {
- continue;
- }
- strcpy(mailbox->id, mbox); /* SAFE */
- AST_LIST_INSERT_TAIL(&peer->mailboxes, mailbox, entry);
- }
- }
- /*! \brief Build peer from configuration (file or realtime static/dynamic) */
- static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime, int devstate_only)
- {
- struct sip_peer *peer = NULL;
- struct ast_acl_list *oldacl = NULL;
- struct ast_acl_list *olddirectmediaacl = NULL;
- int found = 0;
- int firstpass = 1;
- uint16_t port = 0;
- int format = 0; /* Ama flags */
- int timerb_set = 0, timert1_set = 0;
- time_t regseconds = 0;
- struct ast_flags peerflags[3] = {{(0)}};
- struct ast_flags mask[3] = {{(0)}};
- struct sip_peer tmp_peer;
- const char *srvlookup = NULL;
- static int deprecation_warning = 1;
- int alt_fullcontact = alt ? 1 : 0, headercount = 0;
- struct ast_str *fullcontact = ast_str_alloca(512);
- int acl_change_subscription_needed = 0;
- if (!realtime || ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
- /* Note we do NOT use sip_find_peer here, to avoid realtime recursion */
- /* We also use a case-sensitive comparison (unlike sip_find_peer) so
- that case changes made to the peer name will be properly handled
- during reload
- */
- ast_copy_string(tmp_peer.name, name, sizeof(tmp_peer.name));
- peer = ao2_t_find(peers, &tmp_peer, OBJ_POINTER | OBJ_UNLINK, "find and unlink peer from peers table");
- }
- if (peer) {
- /* Already in the list, remove it and it will be added back (or FREE'd) */
- found++;
- /* we've unlinked the peer from the peers container but not unlinked from the peers_by_ip container yet
- this leads to a wrong refcounter and the peer object is never destroyed */
- if (!ast_sockaddr_isnull(&peer->addr)) {
- ao2_t_unlink(peers_by_ip, peer, "ao2_unlink peer from peers_by_ip table");
- }
- if (!(peer->the_mark))
- firstpass = 0;
- } else {
- if (!(peer = ao2_t_alloc(sizeof(*peer), sip_destroy_peer_fn, "allocate a peer struct"))) {
- return NULL;
- }
- if (!(peer->endpoint = ast_endpoint_create("SIP", name))) {
- return NULL;
- }
- if (!(peer->caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
- ao2_t_ref(peer, -1, "failed to allocate format capabilities, drop peer");
- return NULL;
- }
- if (ast_string_field_init(peer, 512)) {
- ao2_t_ref(peer, -1, "failed to string_field_init, drop peer");
- return NULL;
- }
- if (!(peer->cc_params = ast_cc_config_params_init())) {
- ao2_t_ref(peer, -1, "failed to allocate cc_params for peer");
- return NULL;
- }
- if (realtime && !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
- ast_atomic_fetchadd_int(&rpeerobjs, 1);
- ast_debug(3, "-REALTIME- peer built. Name: %s. Peer objects: %d\n", name, rpeerobjs);
- } else
- ast_atomic_fetchadd_int(&speerobjs, 1);
- }
- /* Note that our peer HAS had its reference count increased */
- if (firstpass) {
- oldacl = peer->acl;
- peer->acl = NULL;
- olddirectmediaacl = peer->directmediaacl;
- peer->directmediaacl = NULL;
- set_peer_defaults(peer); /* Set peer defaults */
- peer->type = 0;
- }
- /* in case the case of the peer name has changed, update the name */
- ast_copy_string(peer->name, name, sizeof(peer->name));
- /* If we have channel variables, remove them (reload) */
- if (peer->chanvars) {
- ast_variables_destroy(peer->chanvars);
- peer->chanvars = NULL;
- /* XXX should unregister ? */
- }
- if (found)
- peer->portinuri = 0;
- /* If we have realm authentication information, remove them (reload) */
- ao2_lock(peer);
- if (peer->auth) {
- ao2_t_ref(peer->auth, -1, "Removing old peer authentication");
- peer->auth = NULL;
- }
- ao2_unlock(peer);
- /* clear the transport information. We will detect if a default value is required after parsing the config */
- peer->default_outbound_transport = 0;
- peer->transports = 0;
- if (!devstate_only) {
- struct sip_mailbox *mailbox;
- AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
- mailbox->delme = 1;
- }
- }
- /* clear named callgroup and named pickup group container */
- peer->named_callgroups = ast_unref_namedgroups(peer->named_callgroups);
- peer->named_pickupgroups = ast_unref_namedgroups(peer->named_pickupgroups);
- /* Set the default DTLS settings from default_tls_cfg */
- ast_rtp_dtls_cfg_free(&peer->dtls_cfg);
- ast_rtp_dtls_cfg_copy(&default_dtls_cfg, &peer->dtls_cfg);
- peer->dtls_cfg.enabled = FALSE;
- for (; v || ((v = alt) && !(alt=NULL)); v = v->next) {
- if (!devstate_only) {
- if (handle_common_options(&peerflags[0], &mask[0], v)) {
- continue;
- }
- if (handle_t38_options(&peerflags[0], &mask[0], v, &peer->t38_maxdatagram)) {
- continue;
- }
- if (!strcasecmp(v->name, "transport")) {
- char *val = ast_strdupa(v->value);
- char *trans;
- peer->transports = peer->default_outbound_transport = 0;
- while ((trans = strsep(&val, ","))) {
- trans = ast_skip_blanks(trans);
- if (!strncasecmp(trans, "udp", 3)) {
- peer->transports |= AST_TRANSPORT_UDP;
- } else if (!strncasecmp(trans, "wss", 3)) {
- peer->transports |= AST_TRANSPORT_WSS;
- } else if (!strncasecmp(trans, "ws", 2)) {
- peer->transports |= AST_TRANSPORT_WS;
- } else if (sip_cfg.tcp_enabled && !strncasecmp(trans, "tcp", 3)) {
- peer->transports |= AST_TRANSPORT_TCP;
- } else if (default_tls_cfg.enabled && !strncasecmp(trans, "tls", 3)) {
- peer->transports |= AST_TRANSPORT_TLS;
- } else if (!strncasecmp(trans, "tcp", 3) || !strncasecmp(trans, "tls", 3)) {
- ast_log(LOG_WARNING, "'%.3s' is not a valid transport type when %.3senable=no. If no other is specified, the defaults from general will be used.\n", trans, trans);
- } else {
- ast_log(LOG_NOTICE, "'%s' is not a valid transport type. if no other is specified, the defaults from general will be used.\n", trans);
- }
- if (!peer->default_outbound_transport) { /*!< The first transport listed should be default outbound */
- peer->default_outbound_transport = peer->transports;
- }
- }
- } else if (realtime && !strcasecmp(v->name, "regseconds")) {
- ast_get_time_t(v->value, ®seconds, 0, NULL);
- } else if (realtime && !strcasecmp(v->name, "name")) {
- ast_copy_string(peer->name, v->value, sizeof(peer->name));
- } else if (realtime && !strcasecmp(v->name, "useragent")) {
- ast_string_field_set(peer, useragent, v->value);
- } else if (!strcasecmp(v->name, "type")) {
- if (!strcasecmp(v->value, "peer")) {
- peer->type |= SIP_TYPE_PEER;
- } else if (!strcasecmp(v->value, "user")) {
- peer->type |= SIP_TYPE_USER;
- } else if (!strcasecmp(v->value, "friend")) {
- peer->type = SIP_TYPE_USER | SIP_TYPE_PEER;
- }
- } else if (!strcasecmp(v->name, "remotesecret")) {
- ast_string_field_set(peer, remotesecret, v->value);
- } else if (!strcasecmp(v->name, "secret")) {
- ast_string_field_set(peer, secret, v->value);
- } else if (!strcasecmp(v->name, "description")) {
- ast_string_field_set(peer, description, v->value);
- } else if (!strcasecmp(v->name, "md5secret")) {
- ast_string_field_set(peer, md5secret, v->value);
- } else if (!strcasecmp(v->name, "auth")) {
- add_realm_authentication(&peer->auth, v->value, v->lineno);
- } else if (!strcasecmp(v->name, "callerid")) {
- char cid_name[80] = { '\0' }, cid_num[80] = { '\0' };
- ast_callerid_split(v->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
- ast_string_field_set(peer, cid_name, cid_name);
- ast_string_field_set(peer, cid_num, cid_num);
- } else if (!strcasecmp(v->name, "mwi_from")) {
- ast_string_field_set(peer, mwi_from, v->value);
- } else if (!strcasecmp(v->name, "fullname")) {
- ast_string_field_set(peer, cid_name, v->value);
- } else if (!strcasecmp(v->name, "trunkname")) {
- /* This is actually for a trunk, so we don't want to override callerid */
- ast_string_field_set(peer, cid_name, "");
- } else if (!strcasecmp(v->name, "cid_number")) {
- ast_string_field_set(peer, cid_num, v->value);
- } else if (!strcasecmp(v->name, "cid_tag")) {
- ast_string_field_set(peer, cid_tag, v->value);
- } else if (!strcasecmp(v->name, "context")) {
- ast_string_field_set(peer, context, v->value);
- ast_set_flag(&peer->flags[1], SIP_PAGE2_HAVEPEERCONTEXT);
- } else if (!strcasecmp(v->name, "recordonfeature")) {
- ast_string_field_set(peer, record_on_feature, v->value);
- } else if (!strcasecmp(v->name, "recordofffeature")) {
- ast_string_field_set(peer, record_off_feature, v->value);
- } else if (!strcasecmp(v->name, "outofcall_message_context")) {
- ast_string_field_set(peer, messagecontext, v->value);
- } else if (!strcasecmp(v->name, "subscribecontext")) {
- ast_string_field_set(peer, subscribecontext, v->value);
- } else if (!strcasecmp(v->name, "fromdomain")) {
- char *fromdomainport;
- ast_string_field_set(peer, fromdomain, v->value);
- if ((fromdomainport = strchr(peer->fromdomain, ':'))) {
- *fromdomainport++ = '\0';
- if (!(peer->fromdomainport = port_str2int(fromdomainport, 0))) {
- ast_log(LOG_NOTICE, "'%s' is not a valid port number for fromdomain.\n",fromdomainport);
- }
- } else {
- peer->fromdomainport = STANDARD_SIP_PORT;
- }
- } else if (!strcasecmp(v->name, "usereqphone")) {
- ast_set2_flag(&peer->flags[0], ast_true(v->value), SIP_USEREQPHONE);
- } else if (!strcasecmp(v->name, "fromuser")) {
- ast_string_field_set(peer, fromuser, v->value);
- } else if (!strcasecmp(v->name, "outboundproxy")) {
- struct sip_proxy *proxy;
- if (ast_strlen_zero(v->value)) {
- ast_log(LOG_WARNING, "no value given for outbound proxy on line %d of sip.conf\n", v->lineno);
- continue;
- }
- proxy = proxy_from_config(v->value, v->lineno, peer->outboundproxy);
- if (!proxy) {
- ast_log(LOG_WARNING, "failure parsing the outbound proxy on line %d of sip.conf.\n", v->lineno);
- continue;
- }
- peer->outboundproxy = proxy;
- } else if (!strcasecmp(v->name, "host")) {
- if (!strcasecmp(v->value, "dynamic")) {
- /* They'll register with us */
- if ((!found && !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) || !peer->host_dynamic) {
- /* Initialize stuff if this is a new peer, or if it used to
- * not be dynamic before the reload. */
- ast_sockaddr_setnull(&peer->addr);
- }
- peer->host_dynamic = TRUE;
- } else {
- /* Non-dynamic. Make sure we become that way if we're not */
- AST_SCHED_DEL_UNREF(sched, peer->expire,
- sip_unref_peer(peer, "removing register expire ref"));
- peer->host_dynamic = FALSE;
- srvlookup = v->value;
- }
- } else if (!strcasecmp(v->name, "defaultip")) {
- peer->defaddr.ss.ss_family = AST_AF_UNSPEC;
- if (!ast_strlen_zero(v->value) && ast_get_ip(&peer->defaddr, v->value)) {
- sip_unref_peer(peer, "sip_unref_peer: from build_peer defaultip");
- return NULL;
- }
- } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny") || !strcasecmp(v->name, "acl")) {
- int ha_error = 0;
- if (!ast_strlen_zero(v->value)) {
- ast_append_acl(v->name, v->value, &peer->acl, &ha_error, &acl_change_subscription_needed);
- }
- if (ha_error) {
- ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
- }
- } else if (!strcasecmp(v->name, "contactpermit") || !strcasecmp(v->name, "contactdeny") || !strcasecmp(v->name, "contactacl")) {
- int ha_error = 0;
- if (!ast_strlen_zero(v->value)) {
- ast_append_acl(v->name + 7, v->value, &peer->contactacl, &ha_error, &acl_change_subscription_needed);
- }
- if (ha_error) {
- ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
- }
- } else if (!strcasecmp(v->name, "directmediapermit") || !strcasecmp(v->name, "directmediadeny") || !strcasecmp(v->name, "directmediaacl")) {
- int ha_error = 0;
- ast_append_acl(v->name + 11, v->value, &peer->directmediaacl, &ha_error, &acl_change_subscription_needed);
- if (ha_error) {
- ast_log(LOG_ERROR, "Bad directmedia ACL entry in configuration line %d : %s\n", v->lineno, v->value);
- }
- } else if (!strcasecmp(v->name, "port")) {
- peer->portinuri = 1;
- if (!(port = port_str2int(v->value, 0))) {
- if (realtime) {
- /* If stored as integer, could be 0 for some DBs (notably MySQL) */
- peer->portinuri = 0;
- } else {
- ast_log(LOG_WARNING, "Invalid peer port configuration at line %d : %s\n", v->lineno, v->value);
- }
- }
- } else if (!strcasecmp(v->name, "callingpres")) {
- peer->callingpres = ast_parse_caller_presentation(v->value);
- if (peer->callingpres == -1) {
- peer->callingpres = atoi(v->value);
- }
- } else if (!strcasecmp(v->name, "username") || !strcasecmp(v->name, "defaultuser")) { /* "username" is deprecated */
- ast_string_field_set(peer, username, v->value);
- if (!strcasecmp(v->name, "username")) {
- if (deprecation_warning) {
- ast_log(LOG_NOTICE, "The 'username' field for sip peers has been deprecated in favor of the term 'defaultuser'\n");
- deprecation_warning = 0;
- }
- peer->deprecated_username = 1;
- }
- } else if (!strcasecmp(v->name, "tonezone")) {
- struct ast_tone_zone *new_zone;
- if (!(new_zone = ast_get_indication_zone(v->value))) {
- ast_log(LOG_ERROR, "Unknown country code '%s' for tonezone in device [%s] at line %d. Check indications.conf for available country codes.\n", v->value, peer->name, v->lineno);
- } else {
- ast_tone_zone_unref(new_zone);
- ast_string_field_set(peer, zone, v->value);
- }
- } else if (!strcasecmp(v->name, "language")) {
- ast_string_field_set(peer, language, v->value);
- } else if (!strcasecmp(v->name, "regexten")) {
- ast_string_field_set(peer, regexten, v->value);
- } else if (!strcasecmp(v->name, "callbackextension")) {
- ast_string_field_set(peer, callback, v->value);
- } else if (!strcasecmp(v->name, "amaflags")) {
- format = ast_channel_string2amaflag(v->value);
- if (format < 0) {
- ast_log(LOG_WARNING, "Invalid AMA Flags for peer: %s at line %d\n", v->value, v->lineno);
- } else {
- peer->amaflags = format;
- }
- } else if (!strcasecmp(v->name, "maxforwards")) {
- if (sscanf(v->value, "%30d", &peer->maxforwards) != 1
- || peer->maxforwards < 1 || 255 < peer->maxforwards) {
- ast_log(LOG_WARNING, "'%s' is not a valid maxforwards value at line %d. Using default.\n", v->value, v->lineno);
- peer->maxforwards = sip_cfg.default_max_forwards;
- }
- } else if (!strcasecmp(v->name, "accountcode")) {
- ast_string_field_set(peer, accountcode, v->value);
- } 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, "parkinglot")) {
- ast_string_field_set(peer, parkinglot, v->value);
- } else if (!strcasecmp(v->name, "rtp_engine")) {
- ast_string_field_set(peer, engine, v->value);
- } else if (!strcasecmp(v->name, "mailbox")) {
- add_peer_mailboxes(peer, v->value);
- } else if (!strcasecmp(v->name, "hasvoicemail")) {
- /* People expect that if 'hasvoicemail' is set, that the mailbox will
- * be also set, even if not explicitly specified. */
- if (ast_true(v->value) && AST_LIST_EMPTY(&peer->mailboxes)) {
- /*
- * hasvoicemail is a users.conf legacy voicemail enable method.
- * hasvoicemail is only going to work for app_voicemail mailboxes.
- */
- if (strchr(name, '@')) {
- add_peer_mailboxes(peer, name);
- } else {
- char mailbox[AST_MAX_MAILBOX_UNIQUEID];
- snprintf(mailbox, sizeof(mailbox), "%s@default", name);
- add_peer_mailboxes(peer, mailbox);
- }
- }
- } else if (!strcasecmp(v->name, "subscribemwi")) {
- ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_SUBSCRIBEMWIONLY);
- } else if (!strcasecmp(v->name, "vmexten")) {
- ast_string_field_set(peer, vmexten, v->value);
- } else if (!strcasecmp(v->name, "callgroup")) {
- peer->callgroup = ast_get_group(v->value);
- } else if (!strcasecmp(v->name, "allowtransfer")) {
- peer->allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED;
- } else if (!strcasecmp(v->name, "pickupgroup")) {
- peer->pickupgroup = ast_get_group(v->value);
- } else if (!strcasecmp(v->name, "namedcallgroup")) {
- peer->named_callgroups = ast_get_namedgroups(v->value);
- } else if (!strcasecmp(v->name, "namedpickupgroup")) {
- peer->named_pickupgroups = ast_get_namedgroups(v->value);
- } else if (!strcasecmp(v->name, "allow")) {
- int error = ast_format_cap_update_by_allow_disallow(peer->caps, v->value, TRUE);
- if (error) {
- ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
- }
- } else if (!strcasecmp(v->name, "disallow")) {
- int error = ast_format_cap_update_by_allow_disallow(peer->caps, v->value, FALSE);
- if (error) {
- ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
- }
- } else if (!strcasecmp(v->name, "preferred_codec_only")) {
- ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_PREFERRED_CODEC);
- } else if (!strcasecmp(v->name, "autoframing")) {
- peer->autoframing = ast_true(v->value);
- } else if (!strcasecmp(v->name, "rtptimeout")) {
- if ((sscanf(v->value, "%30d", &peer->rtptimeout) != 1) || (peer->rtptimeout < 0)) {
- ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d. Using default.\n", v->value, v->lineno);
- peer->rtptimeout = global_rtptimeout;
- }
- } else if (!strcasecmp(v->name, "rtpholdtimeout")) {
- if ((sscanf(v->value, "%30d", &peer->rtpholdtimeout) != 1) || (peer->rtpholdtimeout < 0)) {
- ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d. Using default.\n", v->value, v->lineno);
- peer->rtpholdtimeout = global_rtpholdtimeout;
- }
- } else if (!strcasecmp(v->name, "rtpkeepalive")) {
- if ((sscanf(v->value, "%30d", &peer->rtpkeepalive) != 1) || (peer->rtpkeepalive < 0)) {
- ast_log(LOG_WARNING, "'%s' is not a valid RTP keepalive time at line %d. Using default.\n", v->value, v->lineno);
- peer->rtpkeepalive = global_rtpkeepalive;
- }
- } else if (!strcasecmp(v->name, "timert1")) {
- if ((sscanf(v->value, "%30d", &peer->timer_t1) != 1) || (peer->timer_t1 < 200) || (peer->timer_t1 < global_t1min)) {
- ast_log(LOG_WARNING, "'%s' is not a valid T1 time at line %d. Using default.\n", v->value, v->lineno);
- peer->timer_t1 = global_t1min;
- }
- timert1_set = 1;
- } else if (!strcasecmp(v->name, "timerb")) {
- if ((sscanf(v->value, "%30d", &peer->timer_b) != 1) || (peer->timer_b < 200)) {
- ast_log(LOG_WARNING, "'%s' is not a valid Timer B time at line %d. Using default.\n", v->value, v->lineno);
- peer->timer_b = global_timer_b;
- }
- timerb_set = 1;
- } else if (!strcasecmp(v->name, "setvar")) {
- peer->chanvars = add_var(v->value, peer->chanvars);
- } else if (!strcasecmp(v->name, "header")) {
- char tmp[4096];
- snprintf(tmp, sizeof(tmp), "__SIPADDHEADERpre%2d=%s", ++headercount, v->value);
- peer->chanvars = add_var(tmp, peer->chanvars);
- } else if (!strcasecmp(v->name, "qualifyfreq")) {
- int i;
- if (sscanf(v->value, "%30d", &i) == 1) {
- peer->qualifyfreq = i * 1000;
- } else {
- ast_log(LOG_WARNING, "Invalid qualifyfreq number '%s' at line %d of %s\n", v->value, v->lineno, config);
- peer->qualifyfreq = global_qualifyfreq;
- }
- } else if (!strcasecmp(v->name, "maxcallbitrate")) {
- peer->maxcallbitrate = atoi(v->value);
- if (peer->maxcallbitrate < 0) {
- peer->maxcallbitrate = default_maxcallbitrate;
- }
- } else if (!strcasecmp(v->name, "session-timers")) {
- int i = (int) str2stmode(v->value);
- if (i < 0) {
- ast_log(LOG_WARNING, "Invalid session-timers '%s' at line %d of %s\n", v->value, v->lineno, config);
- peer->stimer.st_mode_oper = global_st_mode;
- } else {
- peer->stimer.st_mode_oper = i;
- }
- } else if (!strcasecmp(v->name, "session-expires")) {
- if (sscanf(v->value, "%30d", &peer->stimer.st_max_se) != 1) {
- ast_log(LOG_WARNING, "Invalid session-expires '%s' at line %d of %s\n", v->value, v->lineno, config);
- peer->stimer.st_max_se = global_max_se;
- }
- } else if (!strcasecmp(v->name, "session-minse")) {
- if (sscanf(v->value, "%30d", &peer->stimer.st_min_se) != 1) {
- ast_log(LOG_WARNING, "Invalid session-minse '%s' at line %d of %s\n", v->value, v->lineno, config);
- peer->stimer.st_min_se = global_min_se;
- }
- if (peer->stimer.st_min_se < DEFAULT_MIN_SE) {
- ast_log(LOG_WARNING, "session-minse '%s' at line %d of %s is not allowed to be < %d secs\n", v->value, v->lineno, config, DEFAULT_MIN_SE);
- peer->stimer.st_min_se = global_min_se;
- }
- } else if (!strcasecmp(v->name, "session-refresher")) {
- int i = (int) str2strefresherparam(v->value);
- if (i < 0) {
- ast_log(LOG_WARNING, "Invalid session-refresher '%s' at line %d of %s\n", v->value, v->lineno, config);
- peer->stimer.st_ref = global_st_refresher;
- } else {
- peer->stimer.st_ref = i;
- }
- } else if (!strcasecmp(v->name, "disallowed_methods")) {
- char *disallow = ast_strdupa(v->value);
- mark_parsed_methods(&peer->disallowed_methods, disallow);
- } else if (!strcasecmp(v->name, "unsolicited_mailbox")) {
- ast_string_field_set(peer, unsolicited_mailbox, v->value);
- } else if (!strcasecmp(v->name, "use_q850_reason")) {
- ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_Q850_REASON);
- } else if (!strcasecmp(v->name, "encryption")) {
- ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_USE_SRTP);
- } else if (!strcasecmp(v->name, "encryption_taglen")) {
- ast_set2_flag(&peer->flags[2], !strcasecmp(v->value, "32"), SIP_PAGE3_SRTP_TAG_32);
- } else if (!strcasecmp(v->name, "snom_aoc_enabled")) {
- ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_SNOM_AOC);
- } else if (!strcasecmp(v->name, "avpf")) {
- ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_USE_AVPF);
- } else if (!strcasecmp(v->name, "icesupport")) {
- ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_ICE_SUPPORT);
- } else if (!strcasecmp(v->name, "ignore_requested_pref")) {
- ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_IGNORE_PREFCAPS);
- } else if (!strcasecmp(v->name, "discard_remote_hold_retrieval")) {
- ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_DISCARD_REMOTE_HOLD_RETRIEVAL);
- } else if (!strcasecmp(v->name, "force_avp")) {
- ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_FORCE_AVP);
- } else {
- ast_rtp_dtls_cfg_parse(&peer->dtls_cfg, v->name, v->value);
- }
- }
- /* Apply the encryption tag length to the DTLS configuration, in case DTLS is in use */
- peer->dtls_cfg.suite = (ast_test_flag(&peer->flags[2], SIP_PAGE3_SRTP_TAG_32) ? AST_AES_CM_128_HMAC_SHA1_32 : AST_AES_CM_128_HMAC_SHA1_80);
- /* These apply to devstate lookups */
- if (realtime && !strcasecmp(v->name, "lastms")) {
- sscanf(v->value, "%30d", &peer->lastms);
- } else if (realtime && !strcasecmp(v->name, "ipaddr") && !ast_strlen_zero(v->value) ) {
- ast_sockaddr_parse(&peer->addr, v->value, PARSE_PORT_FORBID);
- } else if (realtime && !strcasecmp(v->name, "fullcontact")) {
- if (alt_fullcontact && !alt) {
- /* Reset, because the alternate also has a fullcontact and we
- * do NOT want the field value to be doubled. It might be
- * tempting to skip this, but the first table might not have
- * fullcontact and since we're here, we know that the alternate
- * absolutely does. */
- alt_fullcontact = 0;
- ast_str_reset(fullcontact);
- }
- /* Reconstruct field, because realtime separates our value at the ';' */
- if (ast_str_strlen(fullcontact) > 0) {
- ast_str_append(&fullcontact, 0, ";%s", v->value);
- } else {
- ast_str_set(&fullcontact, 0, "%s", 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_qualify ? default_qualify : 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 sip.conf\n", peer->name, v->lineno);
- peer->maxms = 0;
- }
- if (realtime && !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) && peer->maxms > 0) {
- /* This would otherwise cause a network storm, where the
- * qualify response refreshes the peer from the database,
- * which in turn causes another qualify to be sent, ad
- * infinitum. */
- ast_log(LOG_WARNING, "Qualify is incompatible with dynamic uncached realtime. Please either turn rtcachefriends on or turn qualify off on peer '%s'\n", peer->name);
- peer->maxms = 0;
- }
- } else if (!strcasecmp(v->name, "keepalive")) {
- if (!strcasecmp(v->value, "no")) {
- peer->keepalive = 0;
- } else if (!strcasecmp(v->value, "yes")) {
- peer->keepalive = DEFAULT_KEEPALIVE_INTERVAL;
- } else if (sscanf(v->value, "%30d", &peer->keepalive) != 1) {
- ast_log(LOG_WARNING, "Keep alive of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", peer->name, v->lineno);
- peer->keepalive = 0;
- }
- } else if (!strcasecmp(v->name, "callcounter")) {
- peer->call_limit = ast_true(v->value) ? INT_MAX : 0;
- } else if (!strcasecmp(v->name, "call-limit")) {
- peer->call_limit = atoi(v->value);
- if (peer->call_limit < 0) {
- peer->call_limit = 0;
- }
- } else if (!strcasecmp(v->name, "busylevel")) {
- peer->busy_level = atoi(v->value);
- if (peer->busy_level < 0) {
- peer->busy_level = 0;
- }
- } else if (ast_cc_is_config_param(v->name)) {
- ast_cc_set_param(peer->cc_params, v->name, v->value);
- }
- }
- if (!devstate_only) {
- struct sip_mailbox *mailbox;
- AST_LIST_TRAVERSE_SAFE_BEGIN(&peer->mailboxes, mailbox, entry) {
- if (mailbox->delme) {
- AST_LIST_REMOVE_CURRENT(entry);
- destroy_mailbox(mailbox);
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
- }
- if (!can_parse_xml && (ast_get_cc_agent_policy(peer->cc_params) == AST_CC_AGENT_NATIVE)) {
- ast_log(LOG_WARNING, "Peer %s has a cc_agent_policy of 'native' but required libxml2 dependency is not installed. Changing policy to 'never'\n", peer->name);
- ast_set_cc_agent_policy(peer->cc_params, AST_CC_AGENT_NEVER);
- }
- /* Note that Timer B is dependent upon T1 and MUST NOT be lower
- * than T1 * 64, according to RFC 3261, Section 17.1.1.2 */
- if (peer->timer_b < peer->timer_t1 * 64) {
- if (timerb_set && timert1_set) {
- ast_log(LOG_WARNING, "Timer B has been set lower than recommended for peer %s (%d < 64 * Timer-T1=%d)\n", peer->name, peer->timer_b, peer->timer_t1);
- } else if (timerb_set) {
- if ((peer->timer_t1 = peer->timer_b / 64) < global_t1min) {
- ast_log(LOG_WARNING, "Timer B has been set lower than recommended (%d < 64 * timert1=%d). (RFC 3261, 17.1.1.2)\n", peer->timer_b, peer->timer_t1);
- peer->timer_t1 = global_t1min;
- peer->timer_b = peer->timer_t1 * 64;
- }
- peer->timer_t1 = peer->timer_b / 64;
- } else {
- peer->timer_b = peer->timer_t1 * 64;
- }
- }
- if (!peer->default_outbound_transport) {
- /* Set default set of transports */
- peer->transports = default_transports;
- /* Set default primary transport */
- peer->default_outbound_transport = default_primary_transport;
- }
- /* The default transport type set during build_peer should only replace the socket.type when...
- * 1. Registration is not present and the socket.type and default transport types are different.
- * 2. The socket.type is not an acceptable transport type after rebuilding peer.
- * 3. The socket.type is not set yet. */
- if (((peer->socket.type != peer->default_outbound_transport) && (peer->expire == -1)) ||
- !(peer->socket.type & peer->transports) || !(peer->socket.type)) {
- set_socket_transport(&peer->socket, peer->default_outbound_transport);
- }
- ast_copy_flags(&peer->flags[0], &peerflags[0], mask[0].flags);
- ast_copy_flags(&peer->flags[1], &peerflags[1], mask[1].flags);
- ast_copy_flags(&peer->flags[2], &peerflags[2], mask[2].flags);
- if (ast_str_strlen(fullcontact)) {
- ast_string_field_set(peer, fullcontact, ast_str_buffer(fullcontact));
- peer->rt_fromcontact = TRUE;
- /* We have a hostname in the fullcontact, but if we don't have an
- * address listed on the entry (or if it's 'dynamic'), then we need to
- * parse the entry to obtain the IP address, so a dynamic host can be
- * contacted immediately after reload (as opposed to waiting for it to
- * register once again). But if we have an address for this peer and NAT was
- * specified, use that address instead. */
- /* XXX May need to revisit the final argument; does the realtime DB store whether
- * the original contact was over TLS or not? XXX */
- if ((!ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) && !ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT))
- || ast_sockaddr_isnull(&peer->addr)) {
- __set_address_from_contact(ast_str_buffer(fullcontact), &peer->addr, 0);
- }
- }
- if (srvlookup && peer->dnsmgr == NULL) {
- char transport[MAXHOSTNAMELEN];
- char _srvlookup[MAXHOSTNAMELEN];
- char *params;
- ast_copy_string(_srvlookup, srvlookup, sizeof(_srvlookup));
- if ((params = strchr(_srvlookup, ';'))) {
- *params++ = '\0';
- }
- snprintf(transport, sizeof(transport), "_%s._%s", get_srv_service(peer->socket.type), get_srv_protocol(peer->socket.type));
- peer->addr.ss.ss_family = get_address_family_filter(peer->socket.type); /* Filter address family */
- if (ast_dnsmgr_lookup_cb(_srvlookup, &peer->addr, &peer->dnsmgr, sip_cfg.srvlookup && !peer->portinuri ? transport : NULL,
- on_dns_update_peer, sip_ref_peer(peer, "Store peer on dnsmgr"))) {
- ast_log(LOG_ERROR, "srvlookup failed for host: %s, on peer %s, removing peer\n", _srvlookup, peer->name);
- sip_unref_peer(peer, "dnsmgr lookup failed, getting rid of peer dnsmgr ref");
- sip_unref_peer(peer, "getting rid of a peer pointer");
- return NULL;
- }
- if (!peer->dnsmgr) {
- /* dnsmgr refresh disabeld, release reference */
- sip_unref_peer(peer, "dnsmgr disabled, unref peer");
- }
- ast_string_field_set(peer, tohost, srvlookup);
- if (global_dynamic_exclude_static && !ast_sockaddr_isnull(&peer->addr)) {
- int ha_error = 0;
- ast_append_acl("deny", ast_sockaddr_stringify_addr(&peer->addr), &sip_cfg.contact_acl, &ha_error, NULL);
- if (ha_error) {
- ast_log(LOG_ERROR, "Bad or unresolved host/IP entry in configuration for peer %s, cannot add to contact ACL\n", peer->name);
- }
- }
- } else if (peer->dnsmgr && !peer->host_dynamic) {
- /* force a refresh here on reload if dnsmgr already exists and host is set. */
- ast_dnsmgr_refresh(peer->dnsmgr);
- }
- if (port && !realtime && peer->host_dynamic) {
- ast_sockaddr_set_port(&peer->defaddr, port);
- } else if (port) {
- ast_sockaddr_set_port(&peer->addr, port);
- }
- if (ast_sockaddr_port(&peer->addr) == 0) {
- ast_sockaddr_set_port(&peer->addr,
- (peer->socket.type & AST_TRANSPORT_TLS) ?
- STANDARD_TLS_PORT : STANDARD_SIP_PORT);
- }
- if (ast_sockaddr_port(&peer->defaddr) == 0) {
- ast_sockaddr_set_port(&peer->defaddr,
- (peer->socket.type & AST_TRANSPORT_TLS) ?
- STANDARD_TLS_PORT : STANDARD_SIP_PORT);
- }
- if (!peer->socket.port) {
- peer->socket.port = htons(((peer->socket.type & AST_TRANSPORT_TLS) ? STANDARD_TLS_PORT : STANDARD_SIP_PORT));
- }
- if (realtime) {
- int enablepoke = 1;
- if (!sip_cfg.ignore_regexpire && peer->host_dynamic) {
- time_t nowtime = time(NULL);
- if ((nowtime - regseconds) > 0) {
- destroy_association(peer);
- memset(&peer->addr, 0, sizeof(peer->addr));
- peer->lastms = -1;
- enablepoke = 0;
- ast_debug(1, "Bah, we're expired (%d/%d/%d)!\n", (int)(nowtime - regseconds), (int)regseconds, (int)nowtime);
- }
- }
- /* Startup regular pokes */
- if (!devstate_only && enablepoke) {
- /*
- * We cannot poke the peer now in this thread without
- * a lock inversion so pass it off to the scheduler
- * thread.
- */
- AST_SCHED_REPLACE_UNREF(peer->pokeexpire, sched,
- 0, /* Poke the peer ASAP */
- sip_poke_peer_now, peer,
- sip_unref_peer(_data, "removing poke peer ref"),
- sip_unref_peer(peer, "removing poke peer ref"),
- sip_ref_peer(peer, "adding poke peer ref"));
- }
- }
- if (ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) {
- sip_cfg.allowsubscribe = TRUE; /* No global ban any more */
- }
- /* If read-only RT backend, then refresh from local DB cache */
- if (peer->host_dynamic && (!peer->is_realtime || !sip_cfg.peer_rtupdate)) {
- reg_source_db(peer);
- }
- /* If they didn't request that MWI is sent *only* on subscribe, go ahead and
- * subscribe to it now. */
- if (!devstate_only && !ast_test_flag(&peer->flags[1], SIP_PAGE2_SUBSCRIBEMWIONLY) &&
- !AST_LIST_EMPTY(&peer->mailboxes)) {
- add_peer_mwi_subs(peer);
- /* Send MWI from the event cache only. This is so we can send initial
- * MWI if app_voicemail got loaded before chan_sip. If it is the other
- * way, then we will get events when app_voicemail gets loaded. */
- sip_send_mwi_to_peer(peer, 1);
- }
- peer->the_mark = 0;
- oldacl = ast_free_acl_list(oldacl);
- olddirectmediaacl = ast_free_acl_list(olddirectmediaacl);
- if (!ast_strlen_zero(peer->callback)) { /* build string from peer info */
- char *reg_string;
- if (ast_asprintf(®_string, "%s?%s:%s@%s/%s", peer->name, peer->username, !ast_strlen_zero(peer->remotesecret) ? peer->remotesecret : peer->secret, peer->tohost, peer->callback) >= 0) {
- sip_register(reg_string, 0); /* XXX TODO: count in registry_count */
- ast_free(reg_string);
- }
- }
- /* If an ACL change subscription is needed and doesn't exist, we need one. */
- if (acl_change_subscription_needed) {
- acl_change_stasis_subscribe();
- }
- return peer;
- }
- static int peer_markall_func(void *device, void *arg, int flags)
- {
- struct sip_peer *peer = device;
- if (!peer->selfdestruct) {
- peer->the_mark = 1;
- }
- return 0;
- }
- static int peer_markall_autopeers_func(void *device, void *arg, int flags)
- {
- struct sip_peer *peer = device;
- if (peer->selfdestruct) {
- peer->the_mark = 1;
- }
- return 0;
- }
- /*!
- * \internal
- * \brief If no default formats are set in config, these are used
- */
- static void sip_set_default_format_capabilities(struct ast_format_cap *cap)
- {
- ast_format_cap_remove_by_type(cap, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_append(cap, ast_format_ulaw, 0);
- ast_format_cap_append(cap, ast_format_alaw, 0);
- ast_format_cap_append(cap, ast_format_gsm, 0);
- ast_format_cap_append(cap, ast_format_h263, 0);
- }
- static void display_nat_warning(const char *cat, int reason, struct ast_flags *flags) {
- int global_nat, specific_nat;
- if (reason == CHANNEL_MODULE_LOAD && (specific_nat = ast_test_flag(&flags[0], SIP_NAT_FORCE_RPORT)) != (global_nat = ast_test_flag(&global_flags[0], SIP_NAT_FORCE_RPORT))) {
- ast_log(LOG_WARNING, "!!! PLEASE NOTE: Setting 'nat' for a peer/user that differs from the global setting can make\n");
- ast_log(LOG_WARNING, "!!! the name of that peer/user discoverable by an attacker. Replies for non-existent peers/users\n");
- ast_log(LOG_WARNING, "!!! will be sent to a different port than replies for an existing peer/user. If at all possible,\n");
- ast_log(LOG_WARNING, "!!! use the global 'nat' setting and do not set 'nat' per peer/user.\n");
- ast_log(LOG_WARNING, "!!! (config category='%s' global force_rport='%s' peer/user force_rport='%s')\n", cat, AST_CLI_YESNO(global_nat), AST_CLI_YESNO(specific_nat));
- }
- }
- static int cleanup_registration(void *obj, void *arg, int flags)
- {
- struct sip_registry *reg = obj;
- ao2_lock(reg);
- if (reg->call) {
- ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", reg->username, reg->hostname);
- /* This will also remove references to the registry */
- dialog_unlink_all(reg->call);
- reg->call = dialog_unref(reg->call, "remove iterator->call from registry traversal");
- }
- if (reg->expire > -1) {
- AST_SCHED_DEL_UNREF(sched, reg->expire, ao2_t_ref(reg, -1, "reg ptr unref from reload config"));
- }
- if (reg->timeout > -1) {
- AST_SCHED_DEL_UNREF(sched, reg->timeout, ao2_t_ref(reg, -1, "reg ptr unref from reload config"));
- }
- if (reg->dnsmgr) {
- ast_dnsmgr_release(reg->dnsmgr);
- reg->dnsmgr = NULL;
- ao2_t_ref(reg, -1, "reg ptr unref from dnsmgr");
- }
- ao2_unlock(reg);
- return CMP_MATCH;
- }
- static void cleanup_all_regs(void)
- {
- ao2_t_callback(registry_list, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
- cleanup_registration, NULL, "remove all SIP registry items");
- }
- /*! \brief Re-read SIP.conf config file
- \note This function reloads all config data, except for
- active peers (with registrations). They will only
- change configuration data at restart, not at reload.
- SIP debug and recordhistory state will not change
- */
- static int reload_config(enum channelreloadreason reason)
- {
- struct ast_config *cfg, *ucfg;
- struct ast_variable *v;
- struct sip_peer *peer;
- char *cat, *stringp, *context, *oldregcontext;
- char newcontexts[AST_MAX_CONTEXT], oldcontexts[AST_MAX_CONTEXT];
- struct ast_flags mask[3] = {{0}};
- struct ast_flags setflags[3] = {{0}};
- struct ast_flags config_flags = { (reason == CHANNEL_MODULE_LOAD || reason == CHANNEL_ACL_RELOAD) ? 0 : ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) ? 0 : CONFIG_FLAG_FILEUNCHANGED };
- int auto_sip_domains = FALSE;
- struct ast_sockaddr old_bindaddr = bindaddr;
- int registry_count = 0, peer_count = 0, timerb_set = 0, timert1_set = 0;
- int subscribe_network_change = 1;
- time_t run_start, run_end;
- int bindport = 0;
- int acl_change_subscription_needed = 0;
- int min_subexpiry_set = 0, max_subexpiry_set = 0;
- run_start = time(0);
- ast_unload_realtime("sipregs");
- ast_unload_realtime("sippeers");
- cfg = ast_config_load(config, config_flags);
- /* We *must* have a config file otherwise stop immediately */
- if (!cfg) {
- ast_log(LOG_NOTICE, "Unable to load config %s\n", config);
- return -1;
- } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
- ucfg = ast_config_load("users.conf", config_flags);
- if (ucfg == CONFIG_STATUS_FILEUNCHANGED) {
- return 1;
- } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
- ast_log(LOG_ERROR, "Contents of users.conf are invalid and cannot be parsed\n");
- return 1;
- }
- /* Must reread both files, because one changed */
- ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
- if ((cfg = ast_config_load(config, config_flags)) == CONFIG_STATUS_FILEINVALID) {
- ast_log(LOG_ERROR, "Contents of %s are invalid and cannot be parsed\n", config);
- ast_config_destroy(ucfg);
- return 1;
- }
- if (!cfg) {
- /* should have been able to reload here */
- ast_log(LOG_NOTICE, "Unable to load config %s\n", config);
- return -1;
- }
- } else if (cfg == CONFIG_STATUS_FILEINVALID) {
- ast_log(LOG_ERROR, "Contents of %s are invalid and cannot be parsed\n", config);
- return 1;
- } else {
- ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
- if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
- ast_log(LOG_ERROR, "Contents of users.conf are invalid and cannot be parsed\n");
- ast_config_destroy(cfg);
- return 1;
- }
- }
- sip_cfg.contact_acl = ast_free_acl_list(sip_cfg.contact_acl);
- default_tls_cfg.enabled = FALSE; /* Default: Disable TLS */
- default_dtls_cfg.enabled = FALSE; /* Default: Disable DTLS too */
- if (reason != CHANNEL_MODULE_LOAD) {
- ast_debug(4, "--------------- SIP reload started\n");
- clear_sip_domains();
- ast_mutex_lock(&authl_lock);
- if (authl) {
- ao2_t_ref(authl, -1, "Removing old global authentication");
- authl = NULL;
- }
- ast_mutex_unlock(&authl_lock);
- /* Then, actually destroy users and registry */
- cleanup_all_regs();
- ast_debug(4, "--------------- Done destroying registry list\n");
- ao2_t_callback(peers, OBJ_NODATA, peer_markall_func, NULL, "callback to mark all peers");
- }
- /* Reset certificate handling for TLS and DTLS sessions */
- if (reason != CHANNEL_MODULE_LOAD) {
- ast_free(default_tls_cfg.certfile);
- ast_free(default_tls_cfg.pvtfile);
- ast_free(default_tls_cfg.cipher);
- ast_free(default_tls_cfg.cafile);
- ast_free(default_tls_cfg.capath);
- ast_rtp_dtls_cfg_free(&default_dtls_cfg);
- }
- default_tls_cfg.certfile = ast_strdup(AST_CERTFILE); /*XXX Not sure if this is useful */
- default_tls_cfg.pvtfile = ast_strdup("");
- default_tls_cfg.cipher = ast_strdup("");
- default_tls_cfg.cafile = ast_strdup("");
- default_tls_cfg.capath = ast_strdup("");
- /* Using the same idea fro DTLS as the code block above for TLS */
- default_dtls_cfg.certfile = ast_strdup("");
- default_dtls_cfg.pvtfile = ast_strdup("");
- default_dtls_cfg.cipher = ast_strdup("");
- default_dtls_cfg.cafile = ast_strdup("");
- default_dtls_cfg.capath = ast_strdup("");
- /* Initialize copy of current sip_cfg.regcontext for later use in removing stale contexts */
- ast_copy_string(oldcontexts, sip_cfg.regcontext, sizeof(oldcontexts));
- oldregcontext = oldcontexts;
- /* Clear all flags before setting default values */
- /* Preserve debugging settings for console */
- sipdebug &= sip_debug_console;
- ast_clear_flag(&global_flags[0], AST_FLAGS_ALL);
- ast_clear_flag(&global_flags[1], AST_FLAGS_ALL);
- ast_clear_flag(&global_flags[2], AST_FLAGS_ALL);
- /* Reset IP addresses */
- ast_sockaddr_parse(&bindaddr, "0.0.0.0:0", 0);
- memset(&internip, 0, sizeof(internip));
- /* Free memory for local network address mask */
- ast_free_ha(localaddr);
- memset(&localaddr, 0, sizeof(localaddr));
- memset(&externaddr, 0, sizeof(externaddr));
- memset(&media_address, 0, sizeof(media_address));
- memset(&rtpbindaddr, 0, sizeof(rtpbindaddr));
- memset(&sip_cfg.outboundproxy, 0, sizeof(struct sip_proxy));
- sip_cfg.outboundproxy.force = FALSE; /*!< Don't force proxy usage, use route: headers */
- default_transports = AST_TRANSPORT_UDP;
- default_primary_transport = AST_TRANSPORT_UDP;
- ourport_tcp = STANDARD_SIP_PORT;
- ourport_tls = STANDARD_TLS_PORT;
- externtcpport = STANDARD_SIP_PORT;
- externtlsport = STANDARD_TLS_PORT;
- sip_cfg.srvlookup = DEFAULT_SRVLOOKUP;
- global_tos_sip = DEFAULT_TOS_SIP;
- global_tos_audio = DEFAULT_TOS_AUDIO;
- global_tos_video = DEFAULT_TOS_VIDEO;
- global_tos_text = DEFAULT_TOS_TEXT;
- global_cos_sip = DEFAULT_COS_SIP;
- global_cos_audio = DEFAULT_COS_AUDIO;
- global_cos_video = DEFAULT_COS_VIDEO;
- global_cos_text = DEFAULT_COS_TEXT;
- externhost[0] = '\0'; /* External host name (for behind NAT DynDNS support) */
- externexpire = 0; /* Expiration for DNS re-issuing */
- externrefresh = 10;
- /* Reset channel settings to default before re-configuring */
- sip_cfg.allow_external_domains = DEFAULT_ALLOW_EXT_DOM; /* Allow external invites */
- sip_cfg.regcontext[0] = '\0';
- sip_set_default_format_capabilities(sip_cfg.caps);
- sip_cfg.regextenonqualify = DEFAULT_REGEXTENONQUALIFY;
- sip_cfg.legacy_useroption_parsing = DEFAULT_LEGACY_USEROPTION_PARSING;
- sip_cfg.send_diversion = DEFAULT_SEND_DIVERSION;
- sip_cfg.notifyringing = DEFAULT_NOTIFYRINGING;
- sip_cfg.notifycid = DEFAULT_NOTIFYCID;
- sip_cfg.notifyhold = FALSE; /*!< Keep track of hold status for a peer */
- sip_cfg.directrtpsetup = FALSE; /* Experimental feature, disabled by default */
- sip_cfg.alwaysauthreject = DEFAULT_ALWAYSAUTHREJECT;
- sip_cfg.auth_options_requests = DEFAULT_AUTH_OPTIONS;
- sip_cfg.auth_message_requests = DEFAULT_AUTH_MESSAGE;
- sip_cfg.messagecontext[0] = '\0';
- sip_cfg.accept_outofcall_message = DEFAULT_ACCEPT_OUTOFCALL_MESSAGE;
- sip_cfg.allowsubscribe = FALSE;
- sip_cfg.disallowed_methods = SIP_UNKNOWN;
- sip_cfg.contact_acl = NULL; /* Reset the contact ACL */
- snprintf(global_useragent, sizeof(global_useragent), "%s %s", DEFAULT_USERAGENT, ast_get_version());
- snprintf(global_sdpsession, sizeof(global_sdpsession), "%s %s", DEFAULT_SDPSESSION, ast_get_version());
- snprintf(global_sdpowner, sizeof(global_sdpowner), "%s", DEFAULT_SDPOWNER);
- global_prematuremediafilter = TRUE;
- ast_copy_string(default_notifymime, DEFAULT_NOTIFYMIME, sizeof(default_notifymime));
- ast_copy_string(sip_cfg.realm, S_OR(ast_config_AST_SYSTEM_NAME, DEFAULT_REALM), sizeof(sip_cfg.realm));
- sip_cfg.domainsasrealm = DEFAULT_DOMAINSASREALM;
- ast_copy_string(default_callerid, DEFAULT_CALLERID, sizeof(default_callerid));
- ast_copy_string(default_mwi_from, DEFAULT_MWI_FROM, sizeof(default_mwi_from));
- sip_cfg.compactheaders = DEFAULT_COMPACTHEADERS;
- global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT;
- global_regattempts_max = 0;
- global_reg_retry_403 = 0;
- sip_cfg.pedanticsipchecking = DEFAULT_PEDANTIC;
- sip_cfg.autocreatepeer = DEFAULT_AUTOCREATEPEER;
- global_autoframing = 0;
- sip_cfg.allowguest = DEFAULT_ALLOWGUEST;
- global_callcounter = DEFAULT_CALLCOUNTER;
- global_match_auth_username = FALSE; /*!< Match auth username if available instead of From: Default off. */
- global_rtptimeout = 0;
- global_rtpholdtimeout = 0;
- global_rtpkeepalive = DEFAULT_RTPKEEPALIVE;
- sip_cfg.allowtransfer = TRANSFER_OPENFORALL; /* Merrily accept all transfers by default */
- sip_cfg.rtautoclear = 120;
- ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWSUBSCRIBE); /* Default for all devices: TRUE */
- ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP_YES); /* Default for all devices: Yes */
- sip_cfg.peer_rtupdate = TRUE;
- global_dynamic_exclude_static = 0; /* Exclude static peers */
- sip_cfg.tcp_enabled = FALSE;
- /* Session-Timers */
- global_st_mode = SESSION_TIMER_MODE_ACCEPT;
- global_st_refresher = SESSION_TIMER_REFRESHER_PARAM_UAS;
- global_min_se = DEFAULT_MIN_SE;
- global_max_se = DEFAULT_MAX_SE;
- /* Peer poking settings */
- global_qualify_gap = DEFAULT_QUALIFY_GAP;
- global_qualify_peers = DEFAULT_QUALIFY_PEERS;
- /* Initialize some reasonable defaults at SIP reload (used both for channel and as default for devices */
- ast_copy_string(sip_cfg.default_context, DEFAULT_CONTEXT, sizeof(sip_cfg.default_context));
- ast_copy_string(sip_cfg.default_record_on_feature, DEFAULT_RECORD_FEATURE, sizeof(sip_cfg.default_record_on_feature));
- ast_copy_string(sip_cfg.default_record_off_feature, DEFAULT_RECORD_FEATURE, sizeof(sip_cfg.default_record_off_feature));
- sip_cfg.default_subscribecontext[0] = '\0';
- sip_cfg.default_max_forwards = DEFAULT_MAX_FORWARDS;
- default_language[0] = '\0';
- default_fromdomain[0] = '\0';
- default_fromdomainport = 0;
- default_qualify = DEFAULT_QUALIFY;
- default_keepalive = DEFAULT_KEEPALIVE;
- default_zone[0] = '\0';
- default_maxcallbitrate = DEFAULT_MAX_CALL_BITRATE;
- ast_copy_string(default_mohinterpret, DEFAULT_MOHINTERPRET, sizeof(default_mohinterpret));
- ast_copy_string(default_mohsuggest, DEFAULT_MOHSUGGEST, sizeof(default_mohsuggest));
- ast_copy_string(default_vmexten, DEFAULT_VMEXTEN, sizeof(default_vmexten));
- ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833); /*!< Default DTMF setting: RFC2833 */
- ast_set_flag(&global_flags[0], SIP_DIRECT_MEDIA); /*!< Allow re-invites */
- ast_set_flag(&global_flags[2], SIP_PAGE3_NAT_AUTO_RPORT); /*!< Default to nat=auto_force_rport */
- ast_copy_string(default_engine, DEFAULT_ENGINE, sizeof(default_engine));
- ast_copy_string(default_parkinglot, DEFAULT_PARKINGLOT, sizeof(default_parkinglot));
- /* Debugging settings, always default to off */
- dumphistory = FALSE;
- recordhistory = FALSE;
- sipdebug &= ~sip_debug_config;
- /* Misc settings for the channel */
- global_relaxdtmf = FALSE;
- global_authfailureevents = FALSE;
- global_t1 = DEFAULT_TIMER_T1;
- global_timer_b = 64 * DEFAULT_TIMER_T1;
- global_t1min = DEFAULT_T1MIN;
- global_qualifyfreq = DEFAULT_QUALIFYFREQ;
- global_t38_maxdatagram = -1;
- global_shrinkcallerid = 1;
- global_refer_addheaders = TRUE;
- authlimit = DEFAULT_AUTHLIMIT;
- authtimeout = DEFAULT_AUTHTIMEOUT;
- global_store_sip_cause = DEFAULT_STORE_SIP_CAUSE;
- min_expiry = DEFAULT_MIN_EXPIRY;
- max_expiry = DEFAULT_MAX_EXPIRY;
- default_expiry = DEFAULT_DEFAULT_EXPIRY;
- sip_cfg.matchexternaddrlocally = DEFAULT_MATCHEXTERNADDRLOCALLY;
- /* Copy the default jb config over global_jbconf */
- memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
- ast_clear_flag(&global_flags[1], SIP_PAGE2_FAX_DETECT);
- ast_clear_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT | SIP_PAGE2_VIDEOSUPPORT_ALWAYS);
- ast_clear_flag(&global_flags[1], SIP_PAGE2_TEXTSUPPORT);
- ast_clear_flag(&global_flags[1], SIP_PAGE2_IGNORESDPVERSION);
- /* Read the [general] config section of sip.conf (or from realtime config) */
- for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
- if (handle_common_options(&setflags[0], &mask[0], v)) {
- continue;
- }
- if (handle_t38_options(&setflags[0], &mask[0], v, &global_t38_maxdatagram)) {
- continue;
- }
- /* handle jb conf */
- if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) {
- continue;
- }
- /* Load default dtls configuration */
- ast_rtp_dtls_cfg_parse(&default_dtls_cfg, v->name, v->value);
- /* handle tls conf, don't allow setting of tlsverifyclient as it isn't supported by chan_sip */
- if (!strcasecmp(v->name, "tlsverifyclient")) {
- ast_log(LOG_WARNING, "Ignoring unsupported option 'tlsverifyclient'\n");
- continue;
- } else if (!ast_tls_read_conf(&default_tls_cfg, &sip_tls_desc, v->name, v->value)) {
- continue;
- }
- if (!strcasecmp(v->name, "context")) {
- ast_copy_string(sip_cfg.default_context, v->value, sizeof(sip_cfg.default_context));
- } else if (!strcasecmp(v->name, "recordonfeature")) {
- ast_copy_string(sip_cfg.default_record_on_feature, v->value, sizeof(sip_cfg.default_record_on_feature));
- } else if (!strcasecmp(v->name, "recordofffeature")) {
- ast_copy_string(sip_cfg.default_record_off_feature, v->value, sizeof(sip_cfg.default_record_off_feature));
- } else if (!strcasecmp(v->name, "subscribecontext")) {
- ast_copy_string(sip_cfg.default_subscribecontext, v->value, sizeof(sip_cfg.default_subscribecontext));
- } else if (!strcasecmp(v->name, "callcounter")) {
- global_callcounter = ast_true(v->value) ? 1 : 0;
- } else if (!strcasecmp(v->name, "allowguest")) {
- sip_cfg.allowguest = ast_true(v->value) ? 1 : 0;
- } else if (!strcasecmp(v->name, "realm")) {
- ast_copy_string(sip_cfg.realm, v->value, sizeof(sip_cfg.realm));
- } else if (!strcasecmp(v->name, "domainsasrealm")) {
- sip_cfg.domainsasrealm = ast_true(v->value);
- } else if (!strcasecmp(v->name, "useragent")) {
- ast_copy_string(global_useragent, v->value, sizeof(global_useragent));
- ast_debug(1, "Setting SIP channel User-Agent Name to %s\n", global_useragent);
- } else if (!strcasecmp(v->name, "sdpsession")) {
- ast_copy_string(global_sdpsession, v->value, sizeof(global_sdpsession));
- } else if (!strcasecmp(v->name, "sdpowner")) {
- /* Field cannot contain spaces */
- if (!strstr(v->value, " ")) {
- ast_copy_string(global_sdpowner, v->value, sizeof(global_sdpowner));
- } else {
- ast_log(LOG_WARNING, "'%s' must not contain spaces at line %d. Using default.\n", v->value, v->lineno);
- }
- } else if (!strcasecmp(v->name, "allowtransfer")) {
- sip_cfg.allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED;
- } else if (!strcasecmp(v->name, "rtcachefriends")) {
- ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_RTCACHEFRIENDS);
- } else if (!strcasecmp(v->name, "rtsavesysname")) {
- sip_cfg.rtsave_sysname = ast_true(v->value);
- } else if (!strcasecmp(v->name, "rtsavepath")) {
- sip_cfg.rtsave_path = ast_true(v->value);
- } else if (!strcasecmp(v->name, "rtupdate")) {
- sip_cfg.peer_rtupdate = ast_true(v->value);
- } else if (!strcasecmp(v->name, "ignoreregexpire")) {
- sip_cfg.ignore_regexpire = ast_true(v->value);
- } else if (!strcasecmp(v->name, "timert1")) {
- /* Defaults to 500ms, but RFC 3261 states that it is recommended
- * for the value to be set higher, though a lower value is only
- * allowed on private networks unconnected to the Internet. */
- global_t1 = atoi(v->value);
- } else if (!strcasecmp(v->name, "timerb")) {
- int tmp = atoi(v->value);
- if (tmp < 500) {
- global_timer_b = global_t1 * 64;
- ast_log(LOG_WARNING, "Invalid value for timerb ('%s'). Setting to default ('%d').\n", v->value, global_timer_b);
- }
- timerb_set = 1;
- } else if (!strcasecmp(v->name, "t1min")) {
- global_t1min = atoi(v->value);
- } else if (!strcasecmp(v->name, "transport")) {
- char *val = ast_strdupa(v->value);
- char *trans;
- default_transports = default_primary_transport = 0;
- while ((trans = strsep(&val, ","))) {
- trans = ast_skip_blanks(trans);
- if (!strncasecmp(trans, "udp", 3)) {
- default_transports |= AST_TRANSPORT_UDP;
- } else if (!strncasecmp(trans, "tcp", 3)) {
- default_transports |= AST_TRANSPORT_TCP;
- } else if (!strncasecmp(trans, "tls", 3)) {
- default_transports |= AST_TRANSPORT_TLS;
- } else if (!strncasecmp(trans, "wss", 3)) {
- default_transports |= AST_TRANSPORT_WSS;
- } else if (!strncasecmp(trans, "ws", 2)) {
- default_transports |= AST_TRANSPORT_WS;
- } else {
- ast_log(LOG_NOTICE, "'%s' is not a valid transport type. if no other is specified, udp will be used.\n", trans);
- }
- if (default_primary_transport == 0) {
- default_primary_transport = default_transports;
- }
- }
- } else if (!strcasecmp(v->name, "tcpenable")) {
- if (!ast_false(v->value)) {
- ast_debug(2, "Enabling TCP socket for listening\n");
- sip_cfg.tcp_enabled = TRUE;
- }
- } else if (!strcasecmp(v->name, "tcpbindaddr")) {
- if (ast_parse_arg(v->value, PARSE_ADDR,
- &sip_tcp_desc.local_address)) {
- ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of %s\n",
- v->name, v->value, v->lineno, config);
- }
- ast_debug(2, "Setting TCP socket address to %s\n",
- ast_sockaddr_stringify(&sip_tcp_desc.local_address));
- } else if (!strcasecmp(v->name, "dynamic_exclude_static") || !strcasecmp(v->name, "dynamic_excludes_static")) {
- global_dynamic_exclude_static = ast_true(v->value);
- } else if (!strcasecmp(v->name, "contactpermit") || !strcasecmp(v->name, "contactdeny") || !strcasecmp(v->name, "contactacl")) {
- int ha_error = 0;
- ast_append_acl(v->name + 7, v->value, &sip_cfg.contact_acl, &ha_error, &acl_change_subscription_needed);
- if (ha_error) {
- ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
- }
- } else if (!strcasecmp(v->name, "rtautoclear")) {
- int i = atoi(v->value);
- if (i > 0) {
- sip_cfg.rtautoclear = i;
- } else {
- i = 0;
- }
- ast_set2_flag(&global_flags[1], i || ast_true(v->value), SIP_PAGE2_RTAUTOCLEAR);
- } else if (!strcasecmp(v->name, "usereqphone")) {
- ast_set2_flag(&global_flags[0], ast_true(v->value), SIP_USEREQPHONE);
- } else if (!strcasecmp(v->name, "prematuremedia")) {
- global_prematuremediafilter = ast_true(v->value);
- } else if (!strcasecmp(v->name, "relaxdtmf")) {
- global_relaxdtmf = ast_true(v->value);
- } else if (!strcasecmp(v->name, "vmexten")) {
- ast_copy_string(default_vmexten, v->value, sizeof(default_vmexten));
- } else if (!strcasecmp(v->name, "rtptimeout")) {
- if ((sscanf(v->value, "%30d", &global_rtptimeout) != 1) || (global_rtptimeout < 0)) {
- ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d. Using default.\n", v->value, v->lineno);
- global_rtptimeout = 0;
- }
- } else if (!strcasecmp(v->name, "rtpholdtimeout")) {
- if ((sscanf(v->value, "%30d", &global_rtpholdtimeout) != 1) || (global_rtpholdtimeout < 0)) {
- ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d. Using default.\n", v->value, v->lineno);
- global_rtpholdtimeout = 0;
- }
- } else if (!strcasecmp(v->name, "rtpkeepalive")) {
- if ((sscanf(v->value, "%30d", &global_rtpkeepalive) != 1) || (global_rtpkeepalive < 0)) {
- ast_log(LOG_WARNING, "'%s' is not a valid RTP keepalive time at line %d. Using default.\n", v->value, v->lineno);
- global_rtpkeepalive = DEFAULT_RTPKEEPALIVE;
- }
- } else if (!strcasecmp(v->name, "compactheaders")) {
- sip_cfg.compactheaders = ast_true(v->value);
- } else if (!strcasecmp(v->name, "notifymimetype")) {
- ast_copy_string(default_notifymime, v->value, sizeof(default_notifymime));
- } else if (!strcasecmp(v->name, "directrtpsetup")) {
- sip_cfg.directrtpsetup = ast_true(v->value);
- } else if (!strcasecmp(v->name, "notifyringing")) {
- sip_cfg.notifyringing = ast_true(v->value);
- } else if (!strcasecmp(v->name, "notifyhold")) {
- sip_cfg.notifyhold = ast_true(v->value);
- } else if (!strcasecmp(v->name, "notifycid")) {
- if (!strcasecmp(v->value, "ignore-context")) {
- sip_cfg.notifycid = IGNORE_CONTEXT;
- } else {
- sip_cfg.notifycid = ast_true(v->value) ? ENABLED : DISABLED;
- }
- } else if (!strcasecmp(v->name, "alwaysauthreject")) {
- sip_cfg.alwaysauthreject = ast_true(v->value);
- } else if (!strcasecmp(v->name, "auth_options_requests")) {
- if (ast_true(v->value)) {
- sip_cfg.auth_options_requests = 1;
- }
- } else if (!strcasecmp(v->name, "auth_message_requests")) {
- sip_cfg.auth_message_requests = ast_true(v->value) ? 1 : 0;
- } else if (!strcasecmp(v->name, "accept_outofcall_message")) {
- sip_cfg.accept_outofcall_message = ast_true(v->value) ? 1 : 0;
- } else if (!strcasecmp(v->name, "outofcall_message_context")) {
- ast_copy_string(sip_cfg.messagecontext, v->value, sizeof(sip_cfg.messagecontext));
- } else if (!strcasecmp(v->name, "mohinterpret")) {
- ast_copy_string(default_mohinterpret, v->value, sizeof(default_mohinterpret));
- } else if (!strcasecmp(v->name, "mohsuggest")) {
- ast_copy_string(default_mohsuggest, v->value, sizeof(default_mohsuggest));
- } else if (!strcasecmp(v->name, "tonezone")) {
- struct ast_tone_zone *new_zone;
- if (!(new_zone = ast_get_indication_zone(v->value))) {
- ast_log(LOG_ERROR, "Unknown country code '%s' for tonezone in [general] at line %d. Check indications.conf for available country codes.\n", v->value, v->lineno);
- } else {
- ast_tone_zone_unref(new_zone);
- ast_copy_string(default_zone, v->value, sizeof(default_zone));
- }
- } else if (!strcasecmp(v->name, "language")) {
- ast_copy_string(default_language, v->value, sizeof(default_language));
- } else if (!strcasecmp(v->name, "regcontext")) {
- ast_copy_string(newcontexts, v->value, sizeof(newcontexts));
- stringp = newcontexts;
- /* Let's remove any contexts that are no longer defined in regcontext */
- cleanup_stale_contexts(stringp, oldregcontext);
- /* Create contexts if they don't exist already */
- while ((context = strsep(&stringp, "&"))) {
- ast_copy_string(used_context, context, sizeof(used_context));
- ast_context_find_or_create(NULL, NULL, context, "SIP");
- }
- ast_copy_string(sip_cfg.regcontext, v->value, sizeof(sip_cfg.regcontext));
- } else if (!strcasecmp(v->name, "regextenonqualify")) {
- sip_cfg.regextenonqualify = ast_true(v->value);
- } else if (!strcasecmp(v->name, "legacy_useroption_parsing")) {
- sip_cfg.legacy_useroption_parsing = ast_true(v->value);
- } else if (!strcasecmp(v->name, "send_diversion")) {
- sip_cfg.send_diversion = ast_true(v->value);
- } else if (!strcasecmp(v->name, "callerid")) {
- ast_copy_string(default_callerid, v->value, sizeof(default_callerid));
- } else if (!strcasecmp(v->name, "mwi_from")) {
- ast_copy_string(default_mwi_from, v->value, sizeof(default_mwi_from));
- } else if (!strcasecmp(v->name, "fromdomain")) {
- char *fromdomainport;
- ast_copy_string(default_fromdomain, v->value, sizeof(default_fromdomain));
- if ((fromdomainport = strchr(default_fromdomain, ':'))) {
- *fromdomainport++ = '\0';
- if (!(default_fromdomainport = port_str2int(fromdomainport, 0))) {
- ast_log(LOG_NOTICE, "'%s' is not a valid port number for fromdomain.\n",fromdomainport);
- }
- } else {
- default_fromdomainport = STANDARD_SIP_PORT;
- }
- } else if (!strcasecmp(v->name, "outboundproxy")) {
- struct sip_proxy *proxy;
- if (ast_strlen_zero(v->value)) {
- ast_log(LOG_WARNING, "no value given for outbound proxy on line %d of sip.conf\n", v->lineno);
- continue;
- }
- proxy = proxy_from_config(v->value, v->lineno, &sip_cfg.outboundproxy);
- if (!proxy) {
- ast_log(LOG_WARNING, "failure parsing the outbound proxy on line %d of sip.conf.\n", v->lineno);
- continue;
- }
- } else if (!strcasecmp(v->name, "autocreatepeer")) {
- if (!strcasecmp(v->value, "persist")) {
- sip_cfg.autocreatepeer = AUTOPEERS_PERSIST;
- } else {
- sip_cfg.autocreatepeer = ast_true(v->value) ? AUTOPEERS_VOLATILE : AUTOPEERS_DISABLED;
- }
- } else if (!strcasecmp(v->name, "match_auth_username")) {
- global_match_auth_username = ast_true(v->value);
- } else if (!strcasecmp(v->name, "srvlookup")) {
- sip_cfg.srvlookup = ast_true(v->value);
- } else if (!strcasecmp(v->name, "pedantic")) {
- sip_cfg.pedanticsipchecking = ast_true(v->value);
- } else if (!strcasecmp(v->name, "maxexpirey") || !strcasecmp(v->name, "maxexpiry")) {
- max_expiry = atoi(v->value);
- if (max_expiry < 1) {
- max_expiry = DEFAULT_MAX_EXPIRY;
- }
- } else if (!strcasecmp(v->name, "minexpirey") || !strcasecmp(v->name, "minexpiry")) {
- min_expiry = atoi(v->value);
- if (min_expiry < 1) {
- min_expiry = DEFAULT_MIN_EXPIRY;
- }
- } else if (!strcasecmp(v->name, "defaultexpiry") || !strcasecmp(v->name, "defaultexpirey")) {
- default_expiry = atoi(v->value);
- if (default_expiry < 1) {
- default_expiry = DEFAULT_DEFAULT_EXPIRY;
- }
- } else if (!strcasecmp(v->name, "submaxexpirey") || !strcasecmp(v->name, "submaxexpiry")) {
- max_subexpiry = atoi(v->value);
- if (max_subexpiry < 1) {
- max_subexpiry = DEFAULT_MAX_EXPIRY;
- }
- max_subexpiry_set = 1;
- } else if (!strcasecmp(v->name, "subminexpirey") || !strcasecmp(v->name, "subminexpiry")) {
- min_subexpiry = atoi(v->value);
- if (min_subexpiry < 1) {
- min_subexpiry = DEFAULT_MIN_EXPIRY;
- }
- min_subexpiry_set = 1;
- } else if (!strcasecmp(v->name, "mwiexpiry") || !strcasecmp(v->name, "mwiexpirey")) {
- mwi_expiry = atoi(v->value);
- if (mwi_expiry < 1) {
- mwi_expiry = DEFAULT_MWI_EXPIRY;
- }
- } else if (!strcasecmp(v->name, "tcpauthtimeout")) {
- if (ast_parse_arg(v->value, PARSE_INT32|PARSE_DEFAULT|PARSE_IN_RANGE,
- &authtimeout, DEFAULT_AUTHTIMEOUT, 1, INT_MAX)) {
- ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of %s\n",
- v->name, v->value, v->lineno, config);
- }
- } else if (!strcasecmp(v->name, "tcpauthlimit")) {
- if (ast_parse_arg(v->value, PARSE_INT32|PARSE_DEFAULT|PARSE_IN_RANGE,
- &authlimit, DEFAULT_AUTHLIMIT, 1, INT_MAX)) {
- ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of %s\n",
- v->name, v->value, v->lineno, config);
- }
- } else if (!strcasecmp(v->name, "sipdebug")) {
- if (ast_true(v->value))
- sipdebug |= sip_debug_config;
- } else if (!strcasecmp(v->name, "dumphistory")) {
- dumphistory = ast_true(v->value);
- } else if (!strcasecmp(v->name, "recordhistory")) {
- recordhistory = ast_true(v->value);
- } else if (!strcasecmp(v->name, "registertimeout")) {
- global_reg_timeout = atoi(v->value);
- if (global_reg_timeout < 1) {
- global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT;
- }
- } else if (!strcasecmp(v->name, "registerattempts")) {
- global_regattempts_max = atoi(v->value);
- } else if (!strcasecmp(v->name, "register_retry_403")) {
- global_reg_retry_403 = ast_true(v->value);
- } else if (!strcasecmp(v->name, "bindaddr") || !strcasecmp(v->name, "udpbindaddr")) {
- if (ast_parse_arg(v->value, PARSE_ADDR, &bindaddr)) {
- ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
- }
- } else if (!strcasecmp(v->name, "localnet")) {
- struct ast_ha *na;
- int ha_error = 0;
- if (!(na = ast_append_ha("d", v->value, localaddr, &ha_error))) {
- ast_log(LOG_WARNING, "Invalid localnet value: %s\n", v->value);
- } else {
- localaddr = na;
- }
- if (ha_error) {
- ast_log(LOG_ERROR, "Bad localnet configuration value line %d : %s\n", v->lineno, v->value);
- }
- } else if (!strcasecmp(v->name, "media_address")) {
- if (ast_parse_arg(v->value, PARSE_ADDR, &media_address))
- ast_log(LOG_WARNING, "Invalid address for media_address keyword: %s\n", v->value);
- } else if (!strcasecmp(v->name, "rtpbindaddr")) {
- if (ast_parse_arg(v->value, PARSE_ADDR, &rtpbindaddr)) {
- ast_log(LOG_WARNING, "Invalid address for rtpbindaddr keyword: %s\n", v->value);
- }
- } else if (!strcasecmp(v->name, "externaddr") || !strcasecmp(v->name, "externip")) {
- if (ast_parse_arg(v->value, PARSE_ADDR, &externaddr)) {
- ast_log(LOG_WARNING,
- "Invalid address for externaddr keyword: %s\n",
- v->value);
- }
- externexpire = 0;
- } else if (!strcasecmp(v->name, "externhost")) {
- ast_copy_string(externhost, v->value, sizeof(externhost));
- if (ast_sockaddr_resolve_first(&externaddr, externhost, 0)) {
- ast_log(LOG_WARNING, "Invalid address for externhost keyword: %s\n", externhost);
- }
- externexpire = time(NULL);
- } else if (!strcasecmp(v->name, "externrefresh")) {
- if (sscanf(v->value, "%30d", &externrefresh) != 1) {
- ast_log(LOG_WARNING, "Invalid externrefresh value '%s', must be an integer >0 at line %d\n", v->value, v->lineno);
- externrefresh = 10;
- }
- } else if (!strcasecmp(v->name, "externtcpport")) {
- if (!(externtcpport = port_str2int(v->value, 0))) {
- ast_log(LOG_WARNING, "Invalid externtcpport value, must be a positive integer between 1 and 65535 at line %d\n", v->lineno);
- externtcpport = 0;
- }
- } else if (!strcasecmp(v->name, "externtlsport")) {
- if (!(externtlsport = port_str2int(v->value, STANDARD_TLS_PORT))) {
- ast_log(LOG_WARNING, "Invalid externtlsport value, must be a positive integer between 1 and 65535 at line %d\n", v->lineno);
- }
- } else if (!strcasecmp(v->name, "allow")) {
- int error = ast_format_cap_update_by_allow_disallow(sip_cfg.caps, v->value, TRUE);
- if (error) {
- ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
- }
- } else if (!strcasecmp(v->name, "disallow")) {
- int error = ast_format_cap_update_by_allow_disallow(sip_cfg.caps, v->value, FALSE);
- if (error) {
- ast_log(LOG_WARNING, "Codec configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value);
- }
- } else if (!strcasecmp(v->name, "preferred_codec_only")) {
- ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_PREFERRED_CODEC);
- } else if (!strcasecmp(v->name, "autoframing")) {
- global_autoframing = ast_true(v->value);
- } else if (!strcasecmp(v->name, "allowexternaldomains")) {
- sip_cfg.allow_external_domains = ast_true(v->value);
- } else if (!strcasecmp(v->name, "autodomain")) {
- auto_sip_domains = ast_true(v->value);
- } else if (!strcasecmp(v->name, "domain")) {
- char *domain = ast_strdupa(v->value);
- char *cntx = strchr(domain, ',');
- if (cntx) {
- *cntx++ = '\0';
- }
- if (ast_strlen_zero(cntx)) {
- ast_debug(1, "No context specified at line %d for domain '%s'\n", v->lineno, domain);
- }
- if (ast_strlen_zero(domain)) {
- ast_log(LOG_WARNING, "Empty domain specified at line %d\n", v->lineno);
- } else {
- add_sip_domain(ast_strip(domain), SIP_DOMAIN_CONFIG, cntx ? ast_strip(cntx) : "");
- }
- } else if (!strcasecmp(v->name, "register")) {
- if (sip_register(v->value, v->lineno) == 0) {
- registry_count++;
- }
- } else if (!strcasecmp(v->name, "mwi")) {
- sip_subscribe_mwi(v->value, v->lineno);
- } else if (!strcasecmp(v->name, "tos_sip")) {
- if (ast_str2tos(v->value, &global_tos_sip)) {
- ast_log(LOG_WARNING, "Invalid tos_sip value at line %d, refer to QoS documentation\n", v->lineno);
- }
- } else if (!strcasecmp(v->name, "tos_audio")) {
- if (ast_str2tos(v->value, &global_tos_audio)) {
- ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, refer to QoS documentation\n", v->lineno);
- }
- } else if (!strcasecmp(v->name, "tos_video")) {
- if (ast_str2tos(v->value, &global_tos_video)) {
- ast_log(LOG_WARNING, "Invalid tos_video value at line %d, refer to QoS documentation\n", v->lineno);
- }
- } else if (!strcasecmp(v->name, "tos_text")) {
- if (ast_str2tos(v->value, &global_tos_text)) {
- ast_log(LOG_WARNING, "Invalid tos_text value at line %d, refer to QoS documentation\n", v->lineno);
- }
- } else if (!strcasecmp(v->name, "cos_sip")) {
- if (ast_str2cos(v->value, &global_cos_sip)) {
- ast_log(LOG_WARNING, "Invalid cos_sip value at line %d, refer to QoS documentation\n", v->lineno);
- }
- } else if (!strcasecmp(v->name, "cos_audio")) {
- if (ast_str2cos(v->value, &global_cos_audio)) {
- ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
- }
- } else if (!strcasecmp(v->name, "cos_video")) {
- if (ast_str2cos(v->value, &global_cos_video)) {
- ast_log(LOG_WARNING, "Invalid cos_video value at line %d, refer to QoS documentation\n", v->lineno);
- }
- } else if (!strcasecmp(v->name, "cos_text")) {
- if (ast_str2cos(v->value, &global_cos_text)) {
- ast_log(LOG_WARNING, "Invalid cos_text value at line %d, refer to QoS documentation\n", v->lineno);
- }
- } else if (!strcasecmp(v->name, "bindport")) {
- if (sscanf(v->value, "%5d", &bindport) != 1) {
- ast_log(LOG_WARNING, "Invalid port number '%s' at line %d of %s\n", v->value, v->lineno, config);
- }
- } else if (!strcasecmp(v->name, "qualify")) {
- if (!strcasecmp(v->value, "no")) {
- default_qualify = 0;
- } else if (!strcasecmp(v->value, "yes")) {
- default_qualify = DEFAULT_MAXMS;
- } else if (sscanf(v->value, "%30d", &default_qualify) != 1) {
- ast_log(LOG_WARNING, "Qualification default should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", v->lineno);
- default_qualify = 0;
- }
- } else if (!strcasecmp(v->name, "keepalive")) {
- if (!strcasecmp(v->value, "no")) {
- default_keepalive = 0;
- } else if (!strcasecmp(v->value, "yes")) {
- default_keepalive = DEFAULT_KEEPALIVE_INTERVAL;
- } else if (sscanf(v->value, "%30d", &default_keepalive) != 1) {
- ast_log(LOG_WARNING, "Keep alive default should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", v->lineno);
- default_keepalive = 0;
- }
- } else if (!strcasecmp(v->name, "qualifyfreq")) {
- int i;
- if (sscanf(v->value, "%30d", &i) == 1) {
- global_qualifyfreq = i * 1000;
- } else {
- ast_log(LOG_WARNING, "Invalid qualifyfreq number '%s' at line %d of %s\n", v->value, v->lineno, config);
- global_qualifyfreq = DEFAULT_QUALIFYFREQ;
- }
- } else if (!strcasecmp(v->name, "authfailureevents")) {
- global_authfailureevents = ast_true(v->value);
- } else if (!strcasecmp(v->name, "maxcallbitrate")) {
- default_maxcallbitrate = atoi(v->value);
- if (default_maxcallbitrate < 0) {
- default_maxcallbitrate = DEFAULT_MAX_CALL_BITRATE;
- }
- } else if (!strcasecmp(v->name, "matchexternaddrlocally") || !strcasecmp(v->name, "matchexterniplocally")) {
- sip_cfg.matchexternaddrlocally = ast_true(v->value);
- } else if (!strcasecmp(v->name, "session-timers")) {
- int i = (int) str2stmode(v->value);
- if (i < 0) {
- ast_log(LOG_WARNING, "Invalid session-timers '%s' at line %d of %s\n", v->value, v->lineno, config);
- global_st_mode = SESSION_TIMER_MODE_ACCEPT;
- } else {
- global_st_mode = i;
- }
- } else if (!strcasecmp(v->name, "session-expires")) {
- if (sscanf(v->value, "%30d", &global_max_se) != 1) {
- ast_log(LOG_WARNING, "Invalid session-expires '%s' at line %d of %s\n", v->value, v->lineno, config);
- global_max_se = DEFAULT_MAX_SE;
- }
- } else if (!strcasecmp(v->name, "session-minse")) {
- if (sscanf(v->value, "%30d", &global_min_se) != 1) {
- ast_log(LOG_WARNING, "Invalid session-minse '%s' at line %d of %s\n", v->value, v->lineno, config);
- global_min_se = DEFAULT_MIN_SE;
- }
- if (global_min_se < DEFAULT_MIN_SE) {
- ast_log(LOG_WARNING, "session-minse '%s' at line %d of %s is not allowed to be < %d secs\n", v->value, v->lineno, config, DEFAULT_MIN_SE);
- global_min_se = DEFAULT_MIN_SE;
- }
- } else if (!strcasecmp(v->name, "session-refresher")) {
- int i = (int) str2strefresherparam(v->value);
- if (i < 0) {
- ast_log(LOG_WARNING, "Invalid session-refresher '%s' at line %d of %s\n", v->value, v->lineno, config);
- global_st_refresher = SESSION_TIMER_REFRESHER_PARAM_UAS;
- } else {
- global_st_refresher = i;
- }
- } else if (!strcasecmp(v->name, "storesipcause")) {
- global_store_sip_cause = ast_true(v->value);
- if (global_store_sip_cause) {
- ast_log(LOG_WARNING, "Usage of SIP_CAUSE is deprecated. Please use HANGUPCAUSE instead.\n");
- }
- } else if (!strcasecmp(v->name, "qualifygap")) {
- if (sscanf(v->value, "%30d", &global_qualify_gap) != 1) {
- ast_log(LOG_WARNING, "Invalid qualifygap '%s' at line %d of %s\n", v->value, v->lineno, config);
- global_qualify_gap = DEFAULT_QUALIFY_GAP;
- }
- } else if (!strcasecmp(v->name, "qualifypeers")) {
- if (sscanf(v->value, "%30d", &global_qualify_peers) != 1) {
- ast_log(LOG_WARNING, "Invalid pokepeers '%s' at line %d of %s\n", v->value, v->lineno, config);
- global_qualify_peers = DEFAULT_QUALIFY_PEERS;
- }
- } else if (!strcasecmp(v->name, "disallowed_methods")) {
- char *disallow = ast_strdupa(v->value);
- mark_parsed_methods(&sip_cfg.disallowed_methods, disallow);
- } else if (!strcasecmp(v->name, "shrinkcallerid")) {
- if (ast_true(v->value)) {
- global_shrinkcallerid = 1;
- } else if (ast_false(v->value)) {
- global_shrinkcallerid = 0;
- } else {
- ast_log(LOG_WARNING, "shrinkcallerid value %s is not valid at line %d.\n", v->value, v->lineno);
- }
- } else if (!strcasecmp(v->name, "use_q850_reason")) {
- ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_Q850_REASON);
- } else if (!strcasecmp(v->name, "maxforwards")) {
- if (sscanf(v->value, "%30d", &sip_cfg.default_max_forwards) != 1
- || sip_cfg.default_max_forwards < 1 || 255 < sip_cfg.default_max_forwards) {
- ast_log(LOG_WARNING, "'%s' is not a valid maxforwards value at line %d. Using default.\n", v->value, v->lineno);
- sip_cfg.default_max_forwards = DEFAULT_MAX_FORWARDS;
- }
- } else if (!strcasecmp(v->name, "subscribe_network_change_event")) {
- if (ast_true(v->value)) {
- subscribe_network_change = 1;
- } else if (ast_false(v->value)) {
- subscribe_network_change = 0;
- } else {
- ast_log(LOG_WARNING, "subscribe_network_change_event value %s is not valid at line %d.\n", v->value, v->lineno);
- }
- } else if (!strcasecmp(v->name, "snom_aoc_enabled")) {
- ast_set2_flag(&global_flags[2], ast_true(v->value), SIP_PAGE3_SNOM_AOC);
- } else if (!strcasecmp(v->name, "icesupport")) {
- ast_set2_flag(&global_flags[2], ast_true(v->value), SIP_PAGE3_ICE_SUPPORT);
- } else if (!strcasecmp(v->name, "discard_remote_hold_retrieval")) {
- ast_set2_flag(&global_flags[2], ast_true(v->value), SIP_PAGE3_DISCARD_REMOTE_HOLD_RETRIEVAL);
- } else if (!strcasecmp(v->name, "parkinglot")) {
- ast_copy_string(default_parkinglot, v->value, sizeof(default_parkinglot));
- } else if (!strcasecmp(v->name, "refer_addheaders")) {
- global_refer_addheaders = ast_true(v->value);
- } else if (!strcasecmp(v->name, "websocket_write_timeout")) {
- if (sscanf(v->value, "%30d", &sip_cfg.websocket_write_timeout) != 1
- || sip_cfg.websocket_write_timeout < 0) {
- ast_log(LOG_WARNING, "'%s' is not a valid websocket_write_timeout value at line %d. Using default '%d'.\n", v->value, v->lineno, AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT);
- sip_cfg.websocket_write_timeout = AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT;
- }
- }
- }
- /* Override global defaults if setting found in general section */
- ast_copy_flags(&global_flags[0], &setflags[0], mask[0].flags);
- ast_copy_flags(&global_flags[1], &setflags[1], mask[1].flags);
- ast_copy_flags(&global_flags[2], &setflags[2], mask[2].flags);
- /* For backwards compatibility the corresponding registration timer value is used if subscription timer value isn't set by configuration */
- if (!min_subexpiry_set) {
- min_subexpiry = min_expiry;
- }
- if (!max_subexpiry_set) {
- max_subexpiry = max_expiry;
- }
- if (reason != CHANNEL_MODULE_LOAD && sip_cfg.autocreatepeer != AUTOPEERS_PERSIST) {
- ao2_t_callback(peers, OBJ_NODATA, peer_markall_autopeers_func, NULL, "callback to mark autopeers for destruction");
- }
- if (subscribe_network_change) {
- network_change_stasis_subscribe();
- } else {
- network_change_stasis_unsubscribe();
- }
- if (global_t1 < global_t1min) {
- ast_log(LOG_WARNING, "'t1min' (%d) cannot be greater than 't1timer' (%d). Resetting 't1timer' to the value of 't1min'\n", global_t1min, global_t1);
- global_t1 = global_t1min;
- }
- if (global_timer_b < global_t1 * 64) {
- if (timerb_set && timert1_set) {
- ast_log(LOG_WARNING, "Timer B has been set lower than recommended (%d < 64 * timert1=%d). (RFC 3261, 17.1.1.2)\n", global_timer_b, global_t1);
- } else if (timerb_set) {
- if ((global_t1 = global_timer_b / 64) < global_t1min) {
- ast_log(LOG_WARNING, "Timer B has been set lower than recommended (%d < 64 * timert1=%d). (RFC 3261, 17.1.1.2)\n", global_timer_b, global_t1);
- global_t1 = global_t1min;
- global_timer_b = global_t1 * 64;
- }
- } else {
- global_timer_b = global_t1 * 64;
- }
- }
- if (!sip_cfg.allow_external_domains && AST_LIST_EMPTY(&domain_list)) {
- ast_log(LOG_WARNING, "To disallow external domains, you need to configure local SIP domains.\n");
- sip_cfg.allow_external_domains = 1;
- }
- /* If not or badly configured, set default transports */
- if (!sip_cfg.tcp_enabled && (default_transports & AST_TRANSPORT_TCP)) {
- ast_log(LOG_WARNING, "Cannot use 'tcp' transport with tcpenable=no. Removing from available transports.\n");
- default_primary_transport &= ~AST_TRANSPORT_TCP;
- default_transports &= ~AST_TRANSPORT_TCP;
- }
- if (!default_tls_cfg.enabled && (default_transports & AST_TRANSPORT_TLS)) {
- ast_log(LOG_WARNING, "Cannot use 'tls' transport with tlsenable=no. Removing from available transports.\n");
- default_primary_transport &= ~AST_TRANSPORT_TLS;
- default_transports &= ~AST_TRANSPORT_TLS;
- }
- if (!default_transports) {
- ast_log(LOG_WARNING, "No valid transports available, falling back to 'udp'.\n");
- default_transports = default_primary_transport = AST_TRANSPORT_UDP;
- } else if (!default_primary_transport) {
- ast_log(LOG_WARNING, "No valid default transport. Selecting 'udp' as default.\n");
- default_primary_transport = AST_TRANSPORT_UDP;
- }
- /* Build list of authentication to various SIP realms, i.e. service providers */
- for (v = ast_variable_browse(cfg, "authentication"); v ; v = v->next) {
- /* Format for authentication is auth = username:password@realm */
- if (!strcasecmp(v->name, "auth")) {
- add_realm_authentication(&authl, v->value, v->lineno);
- }
- }
- if (bindport) {
- if (ast_sockaddr_port(&bindaddr)) {
- ast_log(LOG_WARNING, "bindport is also specified in bindaddr. "
- "Using %d.\n", bindport);
- }
- ast_sockaddr_set_port(&bindaddr, bindport);
- }
- if (!ast_sockaddr_port(&bindaddr)) {
- ast_sockaddr_set_port(&bindaddr, STANDARD_SIP_PORT);
- }
- /* Set UDP address and open socket */
- ast_sockaddr_copy(&internip, &bindaddr);
- if (ast_find_ourip(&internip, &bindaddr, 0)) {
- ast_log(LOG_WARNING, "Unable to get own IP address, SIP disabled\n");
- ast_config_destroy(cfg);
- return 0;
- }
- ast_mutex_lock(&netlock);
- if ((sipsock > -1) && (ast_sockaddr_cmp(&old_bindaddr, &bindaddr))) {
- close(sipsock);
- sipsock = -1;
- }
- if (sipsock < 0) {
- sipsock = socket(ast_sockaddr_is_ipv6(&bindaddr) ?
- AF_INET6 : AF_INET, SOCK_DGRAM, 0);
- if (sipsock < 0) {
- ast_log(LOG_WARNING, "Unable to create SIP socket: %s\n", strerror(errno));
- ast_config_destroy(cfg);
- ast_mutex_unlock(&netlock);
- return -1;
- } else {
- /* Allow SIP clients on the same host to access us: */
- const int reuseFlag = 1;
- setsockopt(sipsock, SOL_SOCKET, SO_REUSEADDR,
- (const char*)&reuseFlag,
- sizeof reuseFlag);
- ast_enable_packet_fragmentation(sipsock);
- if (ast_bind(sipsock, &bindaddr) < 0) {
- ast_log(LOG_WARNING, "Failed to bind to %s: %s\n",
- ast_sockaddr_stringify(&bindaddr), strerror(errno));
- close(sipsock);
- sipsock = -1;
- } else {
- ast_verb(2, "SIP Listening on %s\n", ast_sockaddr_stringify(&bindaddr));
- ast_set_qos(sipsock, global_tos_sip, global_cos_sip, "SIP");
- }
- }
- } else {
- ast_set_qos(sipsock, global_tos_sip, global_cos_sip, "SIP");
- }
- ast_mutex_unlock(&netlock);
- /* Start TCP server */
- if (sip_cfg.tcp_enabled) {
- if (ast_sockaddr_isnull(&sip_tcp_desc.local_address)) {
- ast_sockaddr_copy(&sip_tcp_desc.local_address, &bindaddr);
- }
- if (!ast_sockaddr_port(&sip_tcp_desc.local_address)) {
- ast_sockaddr_set_port(&sip_tcp_desc.local_address, STANDARD_SIP_PORT);
- }
- } else {
- ast_sockaddr_setnull(&sip_tcp_desc.local_address);
- }
- ast_tcptls_server_start(&sip_tcp_desc);
- if (sip_cfg.tcp_enabled && sip_tcp_desc.accept_fd == -1) {
- /* TCP server start failed. Tell the admin */
- ast_log(LOG_ERROR, "SIP TCP Server start failed. Not listening on TCP socket.\n");
- } else {
- ast_debug(2, "SIP TCP server started\n");
- }
- /* Start TLS server if needed */
- memcpy(sip_tls_desc.tls_cfg, &default_tls_cfg, sizeof(default_tls_cfg));
- if (ast_ssl_setup(sip_tls_desc.tls_cfg)) {
- if (ast_sockaddr_isnull(&sip_tls_desc.local_address)) {
- ast_sockaddr_copy(&sip_tls_desc.local_address, &bindaddr);
- ast_sockaddr_set_port(&sip_tls_desc.local_address,
- STANDARD_TLS_PORT);
- }
- if (!ast_sockaddr_port(&sip_tls_desc.local_address)) {
- ast_sockaddr_set_port(&sip_tls_desc.local_address,
- STANDARD_TLS_PORT);
- }
- ast_tcptls_server_start(&sip_tls_desc);
- if (default_tls_cfg.enabled && sip_tls_desc.accept_fd == -1) {
- ast_log(LOG_ERROR, "TLS Server start failed. Not listening on TLS socket.\n");
- sip_tls_desc.tls_cfg = NULL;
- }
- } else if (sip_tls_desc.tls_cfg->enabled) {
- sip_tls_desc.tls_cfg = NULL;
- ast_log(LOG_WARNING, "SIP TLS server did not load because of errors.\n");
- }
- if (ucfg) {
- struct ast_variable *gen;
- int genhassip, genregistersip;
- const char *hassip, *registersip;
- genhassip = ast_true(ast_variable_retrieve(ucfg, "general", "hassip"));
- genregistersip = ast_true(ast_variable_retrieve(ucfg, "general", "registersip"));
- gen = ast_variable_browse(ucfg, "general");
- cat = ast_category_browse(ucfg, NULL);
- while (cat) {
- if (strcasecmp(cat, "general")) {
- hassip = ast_variable_retrieve(ucfg, cat, "hassip");
- registersip = ast_variable_retrieve(ucfg, cat, "registersip");
- if (ast_true(hassip) || (!hassip && genhassip)) {
- peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0, 0);
- if (peer) {
- /* user.conf entries are always of type friend */
- peer->type = SIP_TYPE_USER | SIP_TYPE_PEER;
- ao2_t_link(peers, peer, "link peer into peer table");
- if ((peer->type & SIP_TYPE_PEER) && !ast_sockaddr_isnull(&peer->addr)) {
- ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
- }
- sip_unref_peer(peer, "sip_unref_peer: from reload_config");
- peer_count++;
- }
- }
- if (ast_true(registersip) || (!registersip && genregistersip)) {
- 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");
- const char *contact = ast_variable_retrieve(ucfg, cat, "contact");
- const char *authuser = ast_variable_retrieve(ucfg, cat, "authuser");
- 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 (!contact) {
- contact = "s";
- }
- if (!ast_strlen_zero(username) && !ast_strlen_zero(host)) {
- if (!ast_strlen_zero(secret)) {
- if (!ast_strlen_zero(authuser)) {
- snprintf(tmp, sizeof(tmp), "%s?%s:%s:%s@%s/%s", cat, username, secret, authuser, host, contact);
- } else {
- snprintf(tmp, sizeof(tmp), "%s?%s:%s@%s/%s", cat, username, secret, host, contact);
- }
- } else if (!ast_strlen_zero(authuser)) {
- snprintf(tmp, sizeof(tmp), "%s?%s::%s@%s/%s", cat, username, authuser, host, contact);
- } else {
- snprintf(tmp, sizeof(tmp), "%s?%s@%s/%s", cat, username, host, contact);
- }
- if (sip_register(tmp, 0) == 0) {
- registry_count++;
- }
- }
- }
- }
- cat = ast_category_browse(ucfg, cat);
- }
- ast_config_destroy(ucfg);
- }
- /* Load peers, users and friends */
- cat = NULL;
- while ( (cat = ast_category_browse(cfg, cat)) ) {
- const char *utype;
- if (!strcasecmp(cat, "general") || !strcasecmp(cat, "authentication"))
- continue;
- utype = ast_variable_retrieve(cfg, cat, "type");
- if (!utype) {
- ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
- continue;
- } else {
- if (!strcasecmp(utype, "user")) {
- ;
- } else if (!strcasecmp(utype, "friend")) {
- ;
- } else if (!strcasecmp(utype, "peer")) {
- ;
- } else {
- ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, "sip.conf");
- continue;
- }
- peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0, 0);
- if (peer) {
- display_nat_warning(cat, reason, &peer->flags[0]);
- ao2_t_link(peers, peer, "link peer into peers table");
- if ((peer->type & SIP_TYPE_PEER) && !ast_sockaddr_isnull(&peer->addr)) {
- ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
- }
- sip_unref_peer(peer, "unref the result of the build_peer call. Now, the links from the tables are the only ones left.");
- peer_count++;
- }
- }
- }
- /* Add default domains - host name, IP address and IP:port
- * Only do this if user added any sip domain with "localdomains"
- * In order to *not* break backwards compatibility
- * Some phones address us at IP only, some with additional port number
- */
- if (auto_sip_domains) {
- char temp[MAXHOSTNAMELEN];
- /* First our default IP address */
- if (!ast_sockaddr_isnull(&bindaddr) && !ast_sockaddr_is_any(&bindaddr)) {
- add_sip_domain(ast_sockaddr_stringify_addr(&bindaddr),
- SIP_DOMAIN_AUTO, NULL);
- } else if (!ast_sockaddr_isnull(&internip) && !ast_sockaddr_is_any(&internip)) {
- /* Our internal IP address, if configured */
- add_sip_domain(ast_sockaddr_stringify_addr(&internip),
- SIP_DOMAIN_AUTO, NULL);
- } else {
- ast_log(LOG_NOTICE, "Can't add wildcard IP address to domain list, please add IP address to domain manually.\n");
- }
- /* If TCP is running on a different IP than UDP, then add it too */
- if (!ast_sockaddr_isnull(&sip_tcp_desc.local_address) &&
- !ast_sockaddr_cmp(&bindaddr, &sip_tcp_desc.local_address)) {
- add_sip_domain(ast_sockaddr_stringify_addr(&sip_tcp_desc.local_address),
- SIP_DOMAIN_AUTO, NULL);
- }
- /* If TLS is running on a different IP than UDP and TCP, then add that too */
- if (!ast_sockaddr_isnull(&sip_tls_desc.local_address) &&
- !ast_sockaddr_cmp(&bindaddr, &sip_tls_desc.local_address) &&
- !ast_sockaddr_cmp(&sip_tcp_desc.local_address,
- &sip_tls_desc.local_address)) {
- add_sip_domain(ast_sockaddr_stringify_addr(&sip_tcp_desc.local_address),
- SIP_DOMAIN_AUTO, NULL);
- }
- /* Our extern IP address, if configured */
- if (!ast_sockaddr_isnull(&externaddr)) {
- add_sip_domain(ast_sockaddr_stringify_addr(&externaddr),
- SIP_DOMAIN_AUTO, NULL);
- }
- /* Extern host name (NAT traversal support) */
- if (!ast_strlen_zero(externhost)) {
- add_sip_domain(externhost, SIP_DOMAIN_AUTO, NULL);
- }
- /* Our host name */
- if (!gethostname(temp, sizeof(temp))) {
- add_sip_domain(temp, SIP_DOMAIN_AUTO, NULL);
- }
- }
- /* Release configuration from memory */
- ast_config_destroy(cfg);
- register_realtime_peers_with_callbackextens();
- /* Load the list of manual NOTIFY types to support */
- if (notify_types) {
- ast_config_destroy(notify_types);
- }
- if ((notify_types = ast_config_load(notify_config, config_flags)) == CONFIG_STATUS_FILEINVALID) {
- ast_log(LOG_ERROR, "Contents of %s are invalid and cannot be parsed.\n", notify_config);
- notify_types = NULL;
- }
- run_end = time(0);
- ast_debug(4, "SIP reload_config done...Runtime= %d sec\n", (int)(run_end-run_start));
- /* If an ACL change subscription is needed and doesn't exist, we need one. */
- if (acl_change_subscription_needed) {
- acl_change_stasis_subscribe();
- }
- return 0;
- }
- static int sip_allow_anyrtp_remote(struct ast_channel *chan1, struct ast_rtp_instance *instance, const char *rtptype)
- {
- struct sip_pvt *p;
- struct ast_acl_list *acl = NULL;
- int res = 1;
- if (!(p = ast_channel_tech_pvt(chan1))) {
- return 0;
- }
- sip_pvt_lock(p);
- if (p->relatedpeer && p->relatedpeer->directmediaacl) {
- acl = ast_duplicate_acl_list(p->relatedpeer->directmediaacl);
- }
- sip_pvt_unlock(p);
- if (!acl) {
- return res;
- }
- if (ast_test_flag(&p->flags[0], SIP_DIRECT_MEDIA)) {
- struct ast_sockaddr us = { { 0, }, }, them = { { 0, }, };
- ast_rtp_instance_get_requested_target_address(instance, &them);
- ast_rtp_instance_get_local_address(instance, &us);
- if (ast_apply_acl(acl, &them, "SIP Direct Media ACL: ") == AST_SENSE_DENY) {
- const char *us_addr = ast_strdupa(ast_sockaddr_stringify(&us));
- const char *them_addr = ast_strdupa(ast_sockaddr_stringify(&them));
- ast_debug(3, "Reinvite %s to %s denied by directmedia ACL on %s\n",
- rtptype, them_addr, us_addr);
- res = 0;
- }
- }
- ast_free_acl_list(acl);
- return res;
- }
- static int sip_allow_rtp_remote(struct ast_channel *chan1, struct ast_rtp_instance *instance)
- {
- return sip_allow_anyrtp_remote(chan1, instance, "audio");
- }
- static int sip_allow_vrtp_remote(struct ast_channel *chan1, struct ast_rtp_instance *instance)
- {
- return sip_allow_anyrtp_remote(chan1, instance, "video");
- }
- static enum ast_rtp_glue_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
- {
- struct sip_pvt *p = NULL;
- enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_LOCAL;
- if (!(p = ast_channel_tech_pvt(chan))) {
- return AST_RTP_GLUE_RESULT_FORBID;
- }
- sip_pvt_lock(p);
- if (!(p->rtp)) {
- sip_pvt_unlock(p);
- return AST_RTP_GLUE_RESULT_FORBID;
- }
- ao2_ref(p->rtp, +1);
- *instance = p->rtp;
- if (ast_test_flag(&p->flags[0], SIP_DIRECT_MEDIA)) {
- res = AST_RTP_GLUE_RESULT_REMOTE;
- } else if (ast_test_flag(&p->flags[0], SIP_DIRECT_MEDIA_NAT)) {
- res = AST_RTP_GLUE_RESULT_REMOTE;
- } else if (ast_test_flag(&global_jbconf, AST_JB_FORCED)) {
- res = AST_RTP_GLUE_RESULT_FORBID;
- }
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) {
- switch (p->t38.state) {
- case T38_LOCAL_REINVITE:
- case T38_PEER_REINVITE:
- case T38_ENABLED:
- res = AST_RTP_GLUE_RESULT_LOCAL;
- break;
- case T38_REJECTED:
- default:
- break;
- }
- }
- if (p->srtp) {
- res = AST_RTP_GLUE_RESULT_FORBID;
- }
- sip_pvt_unlock(p);
- return res;
- }
- static enum ast_rtp_glue_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
- {
- struct sip_pvt *p = NULL;
- enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID;
- if (!(p = ast_channel_tech_pvt(chan))) {
- return AST_RTP_GLUE_RESULT_FORBID;
- }
- sip_pvt_lock(p);
- if (!(p->vrtp)) {
- sip_pvt_unlock(p);
- return AST_RTP_GLUE_RESULT_FORBID;
- }
- ao2_ref(p->vrtp, +1);
- *instance = p->vrtp;
- if (ast_test_flag(&p->flags[0], SIP_DIRECT_MEDIA)) {
- res = AST_RTP_GLUE_RESULT_REMOTE;
- }
- sip_pvt_unlock(p);
- return res;
- }
- static enum ast_rtp_glue_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
- {
- struct sip_pvt *p = NULL;
- enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_FORBID;
- if (!(p = ast_channel_tech_pvt(chan))) {
- return AST_RTP_GLUE_RESULT_FORBID;
- }
- sip_pvt_lock(p);
- if (!(p->trtp)) {
- sip_pvt_unlock(p);
- return AST_RTP_GLUE_RESULT_FORBID;
- }
- ao2_ref(p->trtp, +1);
- *instance = p->trtp;
- if (ast_test_flag(&p->flags[0], SIP_DIRECT_MEDIA)) {
- res = AST_RTP_GLUE_RESULT_REMOTE;
- }
- sip_pvt_unlock(p);
- return res;
- }
- static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, const struct ast_format_cap *cap, int nat_active)
- {
- struct sip_pvt *p;
- int changed = 0;
- p = ast_channel_tech_pvt(chan);
- if (!p) {
- return -1;
- }
- sip_pvt_lock(p);
- if (p->owner != chan) {
- /* I suppose it could be argued that if this happens it is a bug. */
- ast_debug(1, "The private is not owned by channel %s anymore.\n", ast_channel_name(chan));
- sip_pvt_unlock(p);
- return 0;
- }
- /* Disable early RTP bridge */
- if ((instance || vinstance || tinstance) &&
- !ast_channel_is_bridged(chan) &&
- !sip_cfg.directrtpsetup) {
- sip_pvt_unlock(p);
- return 0;
- }
- if (p->alreadygone) {
- /* If we're destroyed, don't bother */
- sip_pvt_unlock(p);
- return 0;
- }
- /* if this peer cannot handle reinvites of the media stream to devices
- that are known to be behind a NAT, then stop the process now
- */
- if (nat_active && !ast_test_flag(&p->flags[0], SIP_DIRECT_MEDIA_NAT)) {
- sip_pvt_unlock(p);
- return 0;
- }
- if (instance) {
- changed |= ast_rtp_instance_get_and_cmp_remote_address(instance, &p->redirip);
- if (p->rtp) {
- /* Prevent audio RTCP reads */
- ast_channel_set_fd(chan, 1, -1);
- /* Silence RTCP while audio RTP is inactive */
- ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_RTCP, 0);
- }
- } else if (!ast_sockaddr_isnull(&p->redirip)) {
- memset(&p->redirip, 0, sizeof(p->redirip));
- changed = 1;
- if (p->rtp) {
- /* Enable RTCP since it will be inactive if we're coming back
- * from a reinvite */
- ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_RTCP, 1);
- /* Enable audio RTCP reads */
- ast_channel_set_fd(chan, 1, ast_rtp_instance_fd(p->rtp, 1));
- }
- }
- if (vinstance) {
- changed |= ast_rtp_instance_get_and_cmp_remote_address(vinstance, &p->vredirip);
- if (p->vrtp) {
- /* Prevent video RTCP reads */
- ast_channel_set_fd(chan, 3, -1);
- /* Silence RTCP while video RTP is inactive */
- ast_rtp_instance_set_prop(p->vrtp, AST_RTP_PROPERTY_RTCP, 0);
- }
- } else if (!ast_sockaddr_isnull(&p->vredirip)) {
- memset(&p->vredirip, 0, sizeof(p->vredirip));
- changed = 1;
- if (p->vrtp) {
- /* Enable RTCP since it will be inactive if we're coming back
- * from a reinvite */
- ast_rtp_instance_set_prop(p->vrtp, AST_RTP_PROPERTY_RTCP, 1);
- /* Enable video RTCP reads */
- ast_channel_set_fd(chan, 3, ast_rtp_instance_fd(p->vrtp, 1));
- }
- }
- if (tinstance) {
- changed |= ast_rtp_instance_get_and_cmp_remote_address(tinstance, &p->tredirip);
- } else if (!ast_sockaddr_isnull(&p->tredirip)) {
- memset(&p->tredirip, 0, sizeof(p->tredirip));
- changed = 1;
- }
- if (cap && ast_format_cap_count(cap) && !ast_format_cap_identical(cap, p->redircaps)) {
- ast_format_cap_remove_by_type(p->redircaps, AST_MEDIA_TYPE_UNKNOWN);
- ast_format_cap_append_from_cap(p->redircaps, cap, AST_MEDIA_TYPE_UNKNOWN);
- changed = 1;
- }
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_DIRECT_MEDIA_OUTGOING) && !p->outgoing_call) {
- /* We only wish to withhold sending the initial direct media reinvite on the incoming dialog.
- * Further direct media reinvites beyond the initial should be sent. In order to allow further
- * direct media reinvites to be sent, we clear this flag.
- */
- ast_clear_flag(&p->flags[2], SIP_PAGE3_DIRECT_MEDIA_OUTGOING);
- sip_pvt_unlock(p);
- return 0;
- }
- if (changed && !ast_test_flag(&p->flags[0], SIP_GOTREFER) && !ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) {
- if (ast_channel_state(chan) != AST_STATE_UP) { /* We are in early state */
- if (p->do_history)
- append_history(p, "ExtInv", "Initial invite sent with remote bridge proposal.");
- ast_debug(1, "Early remote bridge setting SIP '%s' - Sending media to %s\n", p->callid, ast_sockaddr_stringify(instance ? &p->redirip : &p->ourip));
- } else if (!p->pendinginvite) { /* We are up, and have no outstanding invite */
- ast_debug(3, "Sending reinvite on SIP '%s' - It's audio soon redirected to IP %s\n", p->callid, ast_sockaddr_stringify(instance ? &p->redirip : &p->ourip));
- transmit_reinvite_with_sdp(p, FALSE, FALSE);
- } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
- ast_debug(3, "Deferring reinvite on SIP '%s' - It's audio will be redirected to IP %s\n", p->callid, ast_sockaddr_stringify(instance ? &p->redirip : &p->ourip));
- /* We have a pending Invite. Send re-invite when we're done with the invite */
- ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
- }
- }
- /* Reset lastrtprx timer */
- p->lastrtprx = p->lastrtptx = time(NULL);
- sip_pvt_unlock(p);
- return 0;
- }
- static void sip_get_codec(struct ast_channel *chan, struct ast_format_cap *result)
- {
- struct sip_pvt *p = ast_channel_tech_pvt(chan);
- ast_format_cap_append_from_cap(result, !ast_format_cap_count(p->peercaps) ? p->caps : p->peercaps, AST_MEDIA_TYPE_UNKNOWN);
- }
- static struct ast_rtp_glue sip_rtp_glue = {
- .type = "SIP",
- .get_rtp_info = sip_get_rtp_peer,
- .allow_rtp_remote = sip_allow_rtp_remote,
- .get_vrtp_info = sip_get_vrtp_peer,
- .allow_vrtp_remote = sip_allow_vrtp_remote,
- .get_trtp_info = sip_get_trtp_peer,
- .update_peer = sip_set_rtp_peer,
- .get_codec = sip_get_codec,
- };
- static char *app_dtmfmode = "SIPDtmfMode";
- static char *app_sipaddheader = "SIPAddHeader";
- static char *app_sipremoveheader = "SIPRemoveHeader";
- #ifdef TEST_FRAMEWORK
- static char *app_sipsendcustominfo = "SIPSendCustomINFO";
- #endif
- /*! \brief Set the DTMFmode for an outbound SIP call (application) */
- static int sip_dtmfmode(struct ast_channel *chan, const char *data)
- {
- struct sip_pvt *p;
- const char *mode = data;
- if (!data) {
- ast_log(LOG_WARNING, "This application requires the argument: info, inband, rfc2833\n");
- return 0;
- }
- ast_channel_lock(chan);
- if (!IS_SIP_TECH(ast_channel_tech(chan))) {
- ast_log(LOG_WARNING, "Call this application only on SIP incoming calls\n");
- ast_channel_unlock(chan);
- return 0;
- }
- p = ast_channel_tech_pvt(chan);
- if (!p) {
- ast_channel_unlock(chan);
- return 0;
- }
- sip_pvt_lock(p);
- if (!strcasecmp(mode, "info")) {
- ast_clear_flag(&p->flags[0], SIP_DTMF);
- ast_set_flag(&p->flags[0], SIP_DTMF_INFO);
- p->jointnoncodeccapability &= ~AST_RTP_DTMF;
- } else if (!strcasecmp(mode, "shortinfo")) {
- ast_clear_flag(&p->flags[0], SIP_DTMF);
- ast_set_flag(&p->flags[0], SIP_DTMF_SHORTINFO);
- p->jointnoncodeccapability &= ~AST_RTP_DTMF;
- } else if (!strcasecmp(mode, "rfc2833")) {
- ast_clear_flag(&p->flags[0], SIP_DTMF);
- ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833);
- p->jointnoncodeccapability |= AST_RTP_DTMF;
- } else if (!strcasecmp(mode, "inband")) {
- ast_clear_flag(&p->flags[0], SIP_DTMF);
- ast_set_flag(&p->flags[0], SIP_DTMF_INBAND);
- p->jointnoncodeccapability &= ~AST_RTP_DTMF;
- } else {
- ast_log(LOG_WARNING, "I don't know about this dtmf mode: %s\n", mode);
- }
- if (p->rtp)
- ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
- if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
- (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
- enable_dsp_detect(p);
- } else {
- disable_dsp_detect(p);
- }
- sip_pvt_unlock(p);
- ast_channel_unlock(chan);
- return 0;
- }
- /*! \brief Add a SIP header to an outbound INVITE */
- static int sip_addheader(struct ast_channel *chan, const char *data)
- {
- int no = 0;
- int ok = FALSE;
- char varbuf[30];
- const char *inbuf = data;
- char *subbuf;
- if (ast_strlen_zero(inbuf)) {
- ast_log(LOG_WARNING, "This application requires the argument: Header\n");
- return 0;
- }
- ast_channel_lock(chan);
- /* Check for headers */
- while (!ok && no <= 50) {
- no++;
- snprintf(varbuf, sizeof(varbuf), "__SIPADDHEADER%.2d", no);
- /* Compare without the leading underscores */
- if ((pbx_builtin_getvar_helper(chan, (const char *) varbuf + 2) == (const char *) NULL)) {
- ok = TRUE;
- }
- }
- if (ok) {
- size_t len = strlen(inbuf);
- subbuf = ast_alloca(len + 1);
- ast_get_encoded_str(inbuf, subbuf, len + 1);
- pbx_builtin_setvar_helper(chan, varbuf, subbuf);
- if (sipdebug) {
- ast_debug(1, "SIP Header added \"%s\" as %s\n", inbuf, varbuf);
- }
- } else {
- ast_log(LOG_WARNING, "Too many SIP headers added, max 50\n");
- }
- ast_channel_unlock(chan);
- return 0;
- }
- /*! \brief Remove SIP headers added previously with SipAddHeader application */
- static int sip_removeheader(struct ast_channel *chan, const char *data)
- {
- struct ast_var_t *newvariable;
- struct varshead *headp;
- int removeall = 0;
- char *inbuf = (char *) data;
- if (ast_strlen_zero(inbuf)) {
- removeall = 1;
- }
- ast_channel_lock(chan);
- headp=ast_channel_varshead(chan);
- AST_LIST_TRAVERSE_SAFE_BEGIN (headp, newvariable, entries) {
- if (strncmp(ast_var_name(newvariable), "SIPADDHEADER", strlen("SIPADDHEADER")) == 0) {
- if (removeall || (!strncasecmp(ast_var_value(newvariable),inbuf,strlen(inbuf)))) {
- if (sipdebug) {
- ast_debug(1,"removing SIP Header \"%s\" as %s\n",
- ast_var_value(newvariable),
- ast_var_name(newvariable));
- }
- AST_LIST_REMOVE_CURRENT(entries);
- ast_var_delete(newvariable);
- }
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
- ast_channel_unlock(chan);
- return 0;
- }
- #ifdef TEST_FRAMEWORK
- /*! \brief Send a custom INFO message via AST_CONTROL_CUSTOM indication */
- static int sip_sendcustominfo(struct ast_channel *chan, const char *data)
- {
- char *info_data, *useragent;
- if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "You must provide data to be sent\n");
- return 0;
- }
- useragent = ast_strdupa(data);
- info_data = strsep(&useragent, ",");
- if (ast_sipinfo_send(chan, NULL, "text/plain", info_data, useragent)) {
- ast_log(LOG_WARNING, "Failed to create payload for custom SIP INFO\n");
- return 0;
- }
- return 0;
- }
- #endif
- /*! \brief Transfer call before connect with a 302 redirect
- \note Called by the transfer() dialplan application through the sip_transfer()
- pbx interface function if the call is in ringing state
- \todo Fix this function so that we wait for reply to the REFER and
- react to errors, denials or other issues the other end might have.
- */
- static int sip_sipredirect(struct sip_pvt *p, const char *dest)
- {
- char *cdest;
- char *extension, *domain;
- cdest = ast_strdupa(dest);
- extension = strsep(&cdest, "@");
- domain = cdest;
- if (ast_strlen_zero(extension)) {
- ast_log(LOG_ERROR, "Missing mandatory argument: extension\n");
- return 0;
- }
- /* we'll issue the redirect message here */
- if (!domain) {
- char *local_to_header;
- char to_header[256];
- ast_copy_string(to_header, sip_get_header(&p->initreq, "To"), sizeof(to_header));
- if (ast_strlen_zero(to_header)) {
- ast_log(LOG_ERROR, "Cannot retrieve the 'To' header from the original SIP request!\n");
- return 0;
- }
- if (((local_to_header = strcasestr(to_header, "sip:")) || (local_to_header = strcasestr(to_header, "sips:")))
- && (local_to_header = strchr(local_to_header, '@'))) {
- char ldomain[256];
- memset(ldomain, 0, sizeof(ldomain));
- local_to_header++;
- /* This is okey because lhost and lport are as big as tmp */
- sscanf(local_to_header, "%256[^<>; ]", ldomain);
- if (ast_strlen_zero(ldomain)) {
- ast_log(LOG_ERROR, "Can't find the host address\n");
- return 0;
- }
- domain = ast_strdupa(ldomain);
- }
- }
- ast_string_field_build(p, our_contact, "Transfer <sip:%s@%s>", extension, domain);
- transmit_response_reliable(p, "302 Moved Temporarily", &p->initreq);
- sip_scheddestroy(p, SIP_TRANS_TIMEOUT); /* Make sure we stop send this reply. */
- sip_alreadygone(p);
- if (p->owner) {
- enum ast_control_transfer message = AST_TRANSFER_SUCCESS;
- ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
- }
- /* hangup here */
- return 0;
- }
- static int sip_is_xml_parsable(void)
- {
- #ifdef HAVE_LIBXML2
- return TRUE;
- #else
- return FALSE;
- #endif
- }
- /*! \brief Send a poke to all known peers */
- static void sip_poke_all_peers(void)
- {
- int ms = 0, num = 0;
- struct ao2_iterator i;
- struct sip_peer *peer;
- if (!speerobjs) { /* No peers, just give up */
- return;
- }
- i = ao2_iterator_init(peers, 0);
- while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
- ao2_lock(peer);
- /* Don't schedule poking on a peer without qualify */
- if (peer->maxms) {
- if (num == global_qualify_peers) {
- ms += global_qualify_gap;
- num = 0;
- } else {
- num++;
- }
- AST_SCHED_REPLACE_UNREF(peer->pokeexpire, sched, ms, sip_poke_peer_s, peer,
- sip_unref_peer(_data, "removing poke peer ref"),
- sip_unref_peer(peer, "removing poke peer ref"),
- sip_ref_peer(peer, "adding poke peer ref"));
- }
- ao2_unlock(peer);
- sip_unref_peer(peer, "toss iterator peer ptr");
- }
- ao2_iterator_destroy(&i);
- }
- /*! \brief Send a keepalive to all known peers */
- static void sip_keepalive_all_peers(void)
- {
- struct ao2_iterator i;
- struct sip_peer *peer;
- if (!speerobjs) { /* No peers, just give up */
- return;
- }
- i = ao2_iterator_init(peers, 0);
- while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
- ao2_lock(peer);
- AST_SCHED_REPLACE_UNREF(peer->keepalivesend, sched, 0, sip_send_keepalive, peer,
- sip_unref_peer(_data, "removing poke peer ref"),
- sip_unref_peer(peer, "removing poke peer ref"),
- sip_ref_peer(peer, "adding poke peer ref"));
- ao2_unlock(peer);
- sip_unref_peer(peer, "toss iterator peer ptr");
- }
- ao2_iterator_destroy(&i);
- }
- /*! \brief Send all known registrations */
- static void sip_send_all_registers(void)
- {
- int ms;
- int regspacing;
- struct ao2_iterator iter;
- struct sip_registry *iterator;
- if (!ao2_container_count(registry_list)) {
- return;
- }
- regspacing = default_expiry * 1000 / ao2_container_count(registry_list);
- if (regspacing > 100) {
- regspacing = 100;
- }
- ms = regspacing;
- iter = ao2_iterator_init(registry_list, 0);
- while ((iterator = ao2_t_iterator_next(&iter, "sip_send_all_registers iter"))) {
- ao2_lock(iterator);
- ms += regspacing;
- AST_SCHED_REPLACE_UNREF(iterator->expire, sched, ms, sip_reregister, iterator,
- ao2_t_ref(_data, -1, "REPLACE sched del decs the refcount"),
- ao2_t_ref(iterator, -1, "REPLACE sched add failure decs the refcount"),
- ao2_t_ref(iterator, +1, "REPLACE sched add incs the refcount"));
- ao2_unlock(iterator);
- ao2_t_ref(iterator, -1, "sip_send_all_registers iter");
- }
- ao2_iterator_destroy(&iter);
- }
- /*! \brief Send all MWI subscriptions */
- static void sip_send_all_mwi_subscriptions(void)
- {
- struct ao2_iterator iter;
- struct sip_subscription_mwi *iterator;
- iter = ao2_iterator_init(subscription_mwi_list, 0);
- while ((iterator = ao2_t_iterator_next(&iter, "sip_send_all_mwi_subscriptions iter"))) {
- ao2_lock(iterator);
- AST_SCHED_DEL(sched, iterator->resub);
- ao2_t_ref(iterator, +1, "mwi added to schedule");
- if ((iterator->resub = ast_sched_add(sched, 1, sip_subscribe_mwi_do, iterator)) < 0) {
- ao2_t_ref(iterator, -1, "mwi failed to schedule");
- }
- ao2_unlock(iterator);
- ao2_t_ref(iterator, -1, "sip_send_all_mwi_subscriptions iter");
- }
- ao2_iterator_destroy(&iter);
- }
- static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct ast_sdp_srtp **srtp, const char *a)
- {
- struct ast_rtp_engine_dtls *dtls;
- /* If no RTP instance exists for this media stream don't bother processing the crypto line */
- if (!rtp) {
- ast_debug(3, "Received offer with crypto line for media stream that is not enabled\n");
- return FALSE;
- }
- if (strncasecmp(a, "crypto:", 7)) {
- return FALSE;
- }
- /* skip "crypto:" */
- a += strlen("crypto:");
- if (!*srtp) {
- if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- ast_log(LOG_WARNING, "Ignoring unexpected crypto attribute in SDP answer\n");
- return FALSE;
- }
- if (!(*srtp = ast_sdp_srtp_alloc())) {
- return FALSE;
- }
- }
- if (!(*srtp)->crypto && !((*srtp)->crypto = ast_sdp_crypto_alloc())) {
- return FALSE;
- }
- if (ast_sdp_crypto_process(rtp, *srtp, a) < 0) {
- return FALSE;
- }
- if ((dtls = ast_rtp_instance_get_dtls(rtp))) {
- dtls->stop(rtp);
- p->dtls_cfg.enabled = 0;
- }
- return TRUE;
- }
- /*! \brief Reload module */
- static int sip_do_reload(enum channelreloadreason reason)
- {
- time_t start_poke, end_poke;
- reload_config(reason);
- ast_sched_dump(sched);
- start_poke = time(0);
- /* Prune peers who still are supposed to be deleted */
- unlink_marked_peers_from_tables();
- ast_debug(4, "--------------- Done destroying pruned peers\n");
- /* Send qualify (OPTIONS) to all peers */
- sip_poke_all_peers();
- /* Send keepalive to all peers */
- sip_keepalive_all_peers();
- /* Register with all services */
- sip_send_all_registers();
- sip_send_all_mwi_subscriptions();
- end_poke = time(0);
- ast_debug(4, "do_reload finished. peer poke/prune reg contact time = %d sec.\n", (int)(end_poke-start_poke));
- ast_debug(4, "--------------- SIP reload done\n");
- return 0;
- }
- /*! \brief Force reload of module from cli */
- static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- static struct sip_peer *tmp_peer, *new_peer;
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip reload";
- e->usage =
- "Usage: sip reload\n"
- " Reloads SIP configuration from sip.conf\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- ast_mutex_lock(&sip_reload_lock);
- if (sip_reloading) {
- ast_verbose("Previous SIP reload not yet done\n");
- } else {
- sip_reloading = TRUE;
- sip_reloadreason = (a && a->fd) ? CHANNEL_CLI_RELOAD : CHANNEL_MODULE_RELOAD;
- }
- ast_mutex_unlock(&sip_reload_lock);
- restart_monitor();
- tmp_peer = bogus_peer;
- /* Create new bogus peer possibly with new global settings. */
- if ((new_peer = temp_peer("(bogus_peer)"))) {
- ast_string_field_set(new_peer, md5secret, BOGUS_PEER_MD5SECRET);
- ast_clear_flag(&new_peer->flags[0], SIP_INSECURE);
- bogus_peer = new_peer;
- ao2_t_ref(tmp_peer, -1, "unref the old bogus_peer during reload");
- } else {
- ast_log(LOG_ERROR, "Could not update the fake authentication peer.\n");
- /* You probably have bigger (memory?) issues to worry about though.. */
- }
- return CLI_SUCCESS;
- }
- /*! \brief Part of Asterisk module interface */
- static int reload(void)
- {
- if (sip_reload(0, 0, NULL)) {
- return 0;
- }
- return 1;
- }
- /*! \brief Return the first entry from ast_sockaddr_resolve filtered by address family
- *
- * \warning Using this function probably means you have a faulty design.
- */
- static int ast_sockaddr_resolve_first_af(struct ast_sockaddr *addr,
- const char* name, int flag, int family)
- {
- struct ast_sockaddr *addrs;
- int addrs_cnt;
- addrs_cnt = ast_sockaddr_resolve(&addrs, name, flag, family);
- if (addrs_cnt <= 0) {
- return 1;
- }
- if (addrs_cnt > 1) {
- ast_debug(1, "Multiple addresses, using the first one only\n");
- }
- ast_sockaddr_copy(addr, &addrs[0]);
- ast_free(addrs);
- return 0;
- }
- /*! \brief Return the first entry from ast_sockaddr_resolve filtered by family of binddaddr
- *
- * \warning Using this function probably means you have a faulty design.
- */
- static int ast_sockaddr_resolve_first(struct ast_sockaddr *addr,
- const char* name, int flag)
- {
- return ast_sockaddr_resolve_first_af(addr, name, flag, get_address_family_filter(AST_TRANSPORT_UDP));
- }
- /*! \brief Return the first entry from ast_sockaddr_resolve filtered by family of binddaddr
- *
- * \warning Using this function probably means you have a faulty design.
- */
- static int ast_sockaddr_resolve_first_transport(struct ast_sockaddr *addr,
- const char* name, int flag, unsigned int transport)
- {
- return ast_sockaddr_resolve_first_af(addr, name, flag, get_address_family_filter(transport));
- }
- /*! \brief
- * \note The only member of the peer used here is the name field
- */
- static int peer_hash_cb(const void *obj, const int flags)
- {
- const struct sip_peer *peer = obj;
- return ast_str_case_hash(peer->name);
- }
- /*!
- * \note The only member of the peer used here is the name field
- */
- static int peer_cmp_cb(void *obj, void *arg, int flags)
- {
- struct sip_peer *peer = obj, *peer2 = arg;
- return !strcasecmp(peer->name, peer2->name) ? CMP_MATCH | CMP_STOP : 0;
- }
- /*!
- * Hash function based on the the peer's ip address. For IPv6, we use the end
- * of the address.
- * \todo Find a better hashing function
- */
- static int peer_iphash_cb(const void *obj, const int flags)
- {
- const struct sip_peer *peer = obj;
- int ret = 0;
- if (ast_sockaddr_isnull(&peer->addr)) {
- ast_log(LOG_ERROR, "Empty address\n");
- }
- ret = ast_sockaddr_hash(&peer->addr);
- if (ret < 0) {
- ret = -ret;
- }
- return ret;
- }
- /*!
- * Match Peers by IP and Port number.
- *
- * This function has two modes.
- * - If the peer arg does not have INSECURE_PORT set, then we will only return
- * a match for a peer that matches both the IP and port.
- * - If the peer arg does have the INSECURE_PORT flag set, then we will only
- * return a match for a peer that matches the IP and has insecure=port
- * in its configuration.
- *
- * This callback will be used twice when doing peer matching. There is a first
- * pass for full IP+port matching, and a second pass in case there is a match
- * that meets the insecure=port criteria.
- *
- * \note Connections coming in over TCP or TLS should never be matched by port.
- *
- * \note the peer's addr struct provides to fields combined to make a key: the sin_addr.s_addr and sin_port fields.
- */
- static int peer_ipcmp_cb_full(void *obj, void *arg, void *data, int flags)
- {
- struct sip_peer *peer = obj, *peer2 = arg;
- char *callback = data;
- if (!ast_strlen_zero(callback) && strcasecmp(peer->callback, callback)) {
- /* We require a callback extension match, but don't have one */
- return 0;
- }
- /* At this point, we match the callback extension if we need to. Carry on. */
- if (ast_sockaddr_cmp_addr(&peer->addr, &peer2->addr)) {
- /* IP doesn't match */
- return 0;
- }
- /* We matched the IP, check to see if we need to match by port as well. */
- if ((peer->transports & peer2->transports) & (AST_TRANSPORT_TLS | AST_TRANSPORT_TCP)) {
- /* peer matching on port is not possible with TCP/TLS */
- return CMP_MATCH | CMP_STOP;
- } else if (ast_test_flag(&peer2->flags[0], SIP_INSECURE_PORT)) {
- /* We are allowing match without port for peers configured that
- * way in this pass through the peers. */
- return ast_test_flag(&peer->flags[0], SIP_INSECURE_PORT) ?
- (CMP_MATCH | CMP_STOP) : 0;
- }
- /* Now only return a match if the port matches, as well. */
- return ast_sockaddr_port(&peer->addr) == ast_sockaddr_port(&peer2->addr) ?
- (CMP_MATCH | CMP_STOP) : 0;
- }
- static int peer_ipcmp_cb(void *obj, void *arg, int flags)
- {
- return peer_ipcmp_cb_full(obj, arg, NULL, flags);
- }
- static int threadt_hash_cb(const void *obj, const int flags)
- {
- const struct sip_threadinfo *th = obj;
- return ast_sockaddr_hash(&th->tcptls_session->remote_address);
- }
- static int threadt_cmp_cb(void *obj, void *arg, int flags)
- {
- struct sip_threadinfo *th = obj, *th2 = arg;
- return (th->tcptls_session == th2->tcptls_session) ? CMP_MATCH | CMP_STOP : 0;
- }
- /*!
- * \note The only member of the dialog used here callid string
- */
- static int dialog_hash_cb(const void *obj, const int flags)
- {
- const struct sip_pvt *pvt = obj;
- return ast_str_case_hash(pvt->callid);
- }
- /*!
- * \note Same as dialog_cmp_cb, except without the CMP_STOP on match
- */
- static int dialog_find_multiple(void *obj, void *arg, int flags)
- {
- struct sip_pvt *pvt = obj, *pvt2 = arg;
- return !strcasecmp(pvt->callid, pvt2->callid) ? CMP_MATCH : 0;
- }
- /*!
- * \note The only member of the dialog used here callid string
- */
- static int dialog_cmp_cb(void *obj, void *arg, int flags)
- {
- struct sip_pvt *pvt = obj, *pvt2 = arg;
- return !strcasecmp(pvt->callid, pvt2->callid) ? CMP_MATCH | CMP_STOP : 0;
- }
- static int registry_hash_cb(const void *obj, const int flags)
- {
- const struct sip_registry *object;
- const char *key;
- switch (flags & OBJ_SEARCH_MASK) {
- case OBJ_SEARCH_KEY:
- key = obj;
- break;
- case OBJ_SEARCH_OBJECT:
- object = obj;
- key = object->configvalue;
- break;
- default:
- /* Hash can only work on something with a full key. */
- ast_assert(0);
- return 0;
- }
- return ast_str_hash(key);
- }
- static int registry_cmp_cb(void *obj, void *arg, int flags)
- {
- const struct sip_registry *object_left = obj;
- const struct sip_registry *object_right = arg;
- const char *right_key = arg;
- int cmp;
- switch (flags & OBJ_SEARCH_MASK) {
- case OBJ_SEARCH_OBJECT:
- right_key = object_right->configvalue;
- /* Fall through */
- case OBJ_SEARCH_KEY:
- cmp = strcmp(object_left->configvalue, right_key);
- break;
- default:
- cmp = 0;
- break;
- }
- if (cmp) {
- return 0;
- }
- return CMP_MATCH;
- }
- /*! \brief SIP Cli commands definition */
- static struct ast_cli_entry cli_sip[] = {
- AST_CLI_DEFINE(sip_show_channels, "List active SIP channels or subscriptions"),
- AST_CLI_DEFINE(sip_show_channelstats, "List statistics for active SIP channels"),
- AST_CLI_DEFINE(sip_show_domains, "List our local SIP domains"),
- AST_CLI_DEFINE(sip_show_inuse, "List all inuse/limits"),
- AST_CLI_DEFINE(sip_show_objects, "List all SIP object allocations"),
- AST_CLI_DEFINE(sip_show_peers, "List defined SIP peers"),
- AST_CLI_DEFINE(sip_show_registry, "List SIP registration status"),
- AST_CLI_DEFINE(sip_unregister, "Unregister (force expiration) a SIP peer from the registry"),
- AST_CLI_DEFINE(sip_show_settings, "Show SIP global settings"),
- AST_CLI_DEFINE(sip_show_mwi, "Show MWI subscriptions"),
- AST_CLI_DEFINE(sip_cli_notify, "Send a notify packet to a SIP peer"),
- AST_CLI_DEFINE(sip_show_channel, "Show detailed SIP channel info"),
- AST_CLI_DEFINE(sip_show_history, "Show SIP dialog history"),
- AST_CLI_DEFINE(sip_show_peer, "Show details on specific SIP peer"),
- AST_CLI_DEFINE(sip_show_users, "List defined SIP users"),
- AST_CLI_DEFINE(sip_show_user, "Show details on specific SIP user"),
- AST_CLI_DEFINE(sip_qualify_peer, "Send an OPTIONS packet to a peer"),
- AST_CLI_DEFINE(sip_show_sched, "Present a report on the status of the scheduler queue"),
- AST_CLI_DEFINE(sip_prune_realtime, "Prune cached Realtime users/peers"),
- AST_CLI_DEFINE(sip_do_debug, "Enable/Disable SIP debugging"),
- AST_CLI_DEFINE(sip_set_history, "Enable/Disable SIP history"),
- AST_CLI_DEFINE(sip_reload, "Reload SIP configuration"),
- AST_CLI_DEFINE(sip_show_tcp, "List TCP Connections")
- };
- /*! \brief SIP test registration */
- static void sip_register_tests(void)
- {
- sip_config_parser_register_tests();
- sip_request_parser_register_tests();
- sip_dialplan_function_register_tests();
- }
- /*! \brief SIP test registration */
- static void sip_unregister_tests(void)
- {
- sip_config_parser_unregister_tests();
- sip_request_parser_unregister_tests();
- sip_dialplan_function_unregister_tests();
- }
- #ifdef TEST_FRAMEWORK
- AST_TEST_DEFINE(test_sip_mwi_subscribe_parse)
- {
- struct ao2_iterator iter;
- struct sip_subscription_mwi *iterator;
- int found = 0;
- enum ast_test_result_state res = AST_TEST_PASS;
- const char *mwi1 = "1234@mysipprovider.com/1234";
- const char *mwi2 = "1234:password@mysipprovider.com/1234";
- const char *mwi3 = "1234:password@mysipprovider.com:5061/1234";
- const char *mwi4 = "1234:password:authuser@mysipprovider.com/1234";
- const char *mwi5 = "1234:password:authuser@mysipprovider.com:5061/1234";
- const char *mwi6 = "1234:password";
- switch (cmd) {
- case TEST_INIT:
- info->name = "sip_mwi_subscribe_parse_test";
- info->category = "/channels/chan_sip/";
- info->summary = "SIP MWI subscribe line parse unit test";
- info->description =
- "Tests the parsing of mwi subscription lines (e.g., mwi => from sip.conf)";
- return AST_TEST_NOT_RUN;
- case TEST_EXECUTE:
- break;
- }
- if (sip_subscribe_mwi(mwi1, 1)) {
- res = AST_TEST_FAIL;
- } else {
- found = 0;
- res = AST_TEST_FAIL;
- iter = ao2_iterator_init(subscription_mwi_list, 0);
- while ((iterator = ao2_t_iterator_next(&iter, "test_sip_mwi_subscribe_parse mwi1"))) {
- ao2_lock(iterator);
- if (
- !strcmp(iterator->hostname, "mysipprovider.com") &&
- !strcmp(iterator->username, "1234") &&
- !strcmp(iterator->secret, "") &&
- !strcmp(iterator->authuser, "") &&
- !strcmp(iterator->mailbox, "1234") &&
- iterator->portno == 0) {
- found = 1;
- res = AST_TEST_PASS;
- }
- ao2_unlock(iterator);
- ao2_t_ref(iterator, -1, "test_sip_mwi_subscribe_parse mwi1");
- }
- ao2_iterator_destroy(&iter);
- if (!found) {
- ast_test_status_update(test, "sip_subscribe_mwi test 1 failed\n");
- }
- }
- if (sip_subscribe_mwi(mwi2, 1)) {
- res = AST_TEST_FAIL;
- } else {
- found = 0;
- res = AST_TEST_FAIL;
- iter = ao2_iterator_init(subscription_mwi_list, 0);
- while ((iterator = ao2_t_iterator_next(&iter, "test_sip_mwi_subscribe_parse mwi2"))) {
- ao2_lock(iterator);
- if (
- !strcmp(iterator->hostname, "mysipprovider.com") &&
- !strcmp(iterator->username, "1234") &&
- !strcmp(iterator->secret, "password") &&
- !strcmp(iterator->authuser, "") &&
- !strcmp(iterator->mailbox, "1234") &&
- iterator->portno == 0) {
- found = 1;
- res = AST_TEST_PASS;
- }
- ao2_unlock(iterator);
- ao2_t_ref(iterator, -1, "test_sip_mwi_subscribe_parse mwi2");
- }
- ao2_iterator_destroy(&iter);
- if (!found) {
- ast_test_status_update(test, "sip_subscribe_mwi test 2 failed\n");
- }
- }
- if (sip_subscribe_mwi(mwi3, 1)) {
- res = AST_TEST_FAIL;
- } else {
- found = 0;
- res = AST_TEST_FAIL;
- iter = ao2_iterator_init(subscription_mwi_list, 0);
- while ((iterator = ao2_t_iterator_next(&iter, "test_sip_mwi_subscribe_parse mwi3"))) {
- ao2_lock(iterator);
- if (
- !strcmp(iterator->hostname, "mysipprovider.com") &&
- !strcmp(iterator->username, "1234") &&
- !strcmp(iterator->secret, "password") &&
- !strcmp(iterator->authuser, "") &&
- !strcmp(iterator->mailbox, "1234") &&
- iterator->portno == 5061) {
- found = 1;
- res = AST_TEST_PASS;
- }
- ao2_unlock(iterator);
- ao2_t_ref(iterator, -1, "test_sip_mwi_subscribe_parse mwi3");
- }
- ao2_iterator_destroy(&iter);
- if (!found) {
- ast_test_status_update(test, "sip_subscribe_mwi test 3 failed\n");
- }
- }
- if (sip_subscribe_mwi(mwi4, 1)) {
- res = AST_TEST_FAIL;
- } else {
- found = 0;
- res = AST_TEST_FAIL;
- iter = ao2_iterator_init(subscription_mwi_list, 0);
- while ((iterator = ao2_t_iterator_next(&iter, "test_sip_mwi_subscribe_parse mwi4"))) {
- ao2_lock(iterator);
- if (
- !strcmp(iterator->hostname, "mysipprovider.com") &&
- !strcmp(iterator->username, "1234") &&
- !strcmp(iterator->secret, "password") &&
- !strcmp(iterator->authuser, "authuser") &&
- !strcmp(iterator->mailbox, "1234") &&
- iterator->portno == 0) {
- found = 1;
- res = AST_TEST_PASS;
- }
- ao2_unlock(iterator);
- ao2_t_ref(iterator, -1, "test_sip_mwi_subscribe_parse mwi4");
- }
- ao2_iterator_destroy(&iter);
- if (!found) {
- ast_test_status_update(test, "sip_subscribe_mwi test 4 failed\n");
- }
- }
- if (sip_subscribe_mwi(mwi5, 1)) {
- res = AST_TEST_FAIL;
- } else {
- found = 0;
- res = AST_TEST_FAIL;
- iter = ao2_iterator_init(subscription_mwi_list, 0);
- while ((iterator = ao2_t_iterator_next(&iter, "test_sip_mwi_subscribe_parse mwi4"))) {
- ao2_lock(iterator);
- if (
- !strcmp(iterator->hostname, "mysipprovider.com") &&
- !strcmp(iterator->username, "1234") &&
- !strcmp(iterator->secret, "password") &&
- !strcmp(iterator->authuser, "authuser") &&
- !strcmp(iterator->mailbox, "1234") &&
- iterator->portno == 5061) {
- found = 1;
- res = AST_TEST_PASS;
- }
- ao2_unlock(iterator);
- ao2_t_ref(iterator, -1, "test_sip_mwi_subscribe_parse mwi4");
- }
- ao2_iterator_destroy(&iter);
- if (!found) {
- ast_test_status_update(test, "sip_subscribe_mwi test 5 failed\n");
- }
- }
- if (sip_subscribe_mwi(mwi6, 1)) {
- res = AST_TEST_PASS;
- } else {
- res = AST_TEST_FAIL;
- }
- return res;
- }
- AST_TEST_DEFINE(test_sip_peers_get)
- {
- struct sip_peer *peer;
- struct ast_data *node;
- struct ast_data_query query = {
- .path = "/asterisk/channel/sip/peers",
- .search = "peers/peer/name=test_peer_data_provider"
- };
- switch (cmd) {
- case TEST_INIT:
- info->name = "sip_peers_get_data_test";
- info->category = "/main/data/sip/peers/";
- info->summary = "SIP peers data providers unit test";
- info->description =
- "Tests whether the SIP peers data provider implementation works as expected.";
- return AST_TEST_NOT_RUN;
- case TEST_EXECUTE:
- break;
- }
- /* Create the peer that we will retrieve. */
- peer = build_peer("test_peer_data_provider", NULL, NULL, 0, 0);
- if (!peer) {
- return AST_TEST_FAIL;
- }
- peer->type = SIP_TYPE_USER;
- peer->call_limit = 10;
- ao2_link(peers, peer);
- /* retrieve the chan_sip/peers tree and check the created peer. */
- node = ast_data_get(&query);
- if (!node) {
- ao2_unlink(peers, peer);
- ao2_ref(peer, -1);
- return AST_TEST_FAIL;
- }
- /* compare item. */
- if (strcmp(ast_data_retrieve_string(node, "peer/name"), "test_peer_data_provider")) {
- ao2_unlink(peers, peer);
- ao2_ref(peer, -1);
- ast_data_free(node);
- return AST_TEST_FAIL;
- }
- if (strcmp(ast_data_retrieve_string(node, "peer/type"), "user")) {
- ao2_unlink(peers, peer);
- ao2_ref(peer, -1);
- ast_data_free(node);
- return AST_TEST_FAIL;
- }
- if (ast_data_retrieve_int(node, "peer/call_limit") != 10) {
- ao2_unlink(peers, peer);
- ao2_ref(peer, -1);
- ast_data_free(node);
- return AST_TEST_FAIL;
- }
- /* release resources */
- ast_data_free(node);
- ao2_unlink(peers, peer);
- ao2_ref(peer, -1);
- return AST_TEST_PASS;
- }
- /*!
- * \brief Imitation TCP reception loop
- *
- * This imitates the logic used by SIP's TCP code. Its purpose
- * is to either
- * 1) Combine fragments into a single message
- * 2) Break up combined messages into single messages
- *
- * \param fragments The message fragments. This simulates the data received on a TCP socket.
- * \param num_fragments This indicates the number of fragments to receive
- * \param overflow This is a place to stash extra data if more than one message is received
- * in a single fragment
- * \param[out] messages The parsed messages are placed in this array
- * \param[out] num_messages The number of messages that were parsed
- * \param test Used for printing messages
- * \retval 0 Success
- * \retval -1 Failure
- */
- static int mock_tcp_loop(char *fragments[], size_t num_fragments,
- struct ast_str **overflow, char **messages, int *num_messages, struct ast_test* test)
- {
- struct ast_str *req_data;
- int i = 0;
- int res = 0;
- req_data = ast_str_create(128);
- ast_str_reset(*overflow);
- while (i < num_fragments || ast_str_strlen(*overflow) > 0) {
- enum message_integrity message_integrity = MESSAGE_FRAGMENT;
- ast_str_reset(req_data);
- while (message_integrity == MESSAGE_FRAGMENT) {
- if (ast_str_strlen(*overflow) > 0) {
- ast_str_append(&req_data, 0, "%s", ast_str_buffer(*overflow));
- ast_str_reset(*overflow);
- } else {
- ast_str_append(&req_data, 0, "%s", fragments[i++]);
- }
- message_integrity = check_message_integrity(&req_data, overflow);
- }
- if (strcmp(ast_str_buffer(req_data), messages[*num_messages])) {
- ast_test_status_update(test, "Mismatch in SIP messages.\n");
- ast_test_status_update(test, "Expected message:\n%s", messages[*num_messages]);
- ast_test_status_update(test, "Parsed message:\n%s", ast_str_buffer(req_data));
- res = -1;
- goto end;
- } else {
- ast_test_status_update(test, "Successfully read message:\n%s", ast_str_buffer(req_data));
- }
- (*num_messages)++;
- }
- end:
- ast_free(req_data);
- return res;
- };
- AST_TEST_DEFINE(test_tcp_message_fragmentation)
- {
- /* Normal single message in one fragment */
- char *normal[] = {
- "INVITE sip:bob@example.org SIP/2.0\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n"
- "Contact: sip:127.0.0.1:5061\r\n"
- "Max-Forwards: 70\r\n"
- "Content-Type: application/sdp\r\n"
- "Content-Length: 130\r\n"
- "\r\n"
- "v=0\r\n"
- "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 10000 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- };
- /* Single message in two fragments.
- * Fragments combine to make "normal"
- */
- char *fragmented[] = {
- "INVITE sip:bob@example.org SIP/2.0\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n"
- "Contact: sip:127.0.0.1:5061\r\n"
- "Max-Forwards: ",
- "70\r\n"
- "Content-Type: application/sdp\r\n"
- "Content-Length: 130\r\n"
- "\r\n"
- "v=0\r\n"
- "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 10000 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- };
- /* Single message in two fragments, divided precisely at the body
- * Fragments combine to make "normal"
- */
- char *fragmented_body[] = {
- "INVITE sip:bob@example.org SIP/2.0\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n"
- "Contact: sip:127.0.0.1:5061\r\n"
- "Max-Forwards: 70\r\n"
- "Content-Type: application/sdp\r\n"
- "Content-Length: 130\r\n"
- "\r\n",
- "v=0\r\n"
- "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 10000 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- };
- /* Single message in three fragments
- * Fragments combine to make "normal"
- */
- char *multi_fragment[] = {
- "INVITE sip:bob@example.org SIP/2.0\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n",
- "Contact: sip:127.0.0.1:5061\r\n"
- "Max-Forwards: 70\r\n"
- "Content-Type: application/sdp\r\n"
- "Content-Length: 130\r\n"
- "\r\n"
- "v=0\r\n"
- "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4",
- " 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 10000 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- };
- /* Two messages in a single fragment
- * Fragments split into "multi_message_divided"
- */
- char *multi_message[] = {
- "SIP/2.0 100 Trying\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n"
- "Contact: <sip:bob@example.org:5060>\r\n"
- "Content-Length: 0\r\n"
- "\r\n"
- "SIP/2.0 180 Ringing\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n"
- "Contact: <sip:bob@example.org:5060>\r\n"
- "Content-Length: 0\r\n"
- "\r\n"
- };
- char *multi_message_divided[] = {
- "SIP/2.0 100 Trying\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n"
- "Contact: <sip:bob@example.org:5060>\r\n"
- "Content-Length: 0\r\n"
- "\r\n",
- "SIP/2.0 180 Ringing\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n"
- "Contact: <sip:bob@example.org:5060>\r\n"
- "Content-Length: 0\r\n"
- "\r\n"
- };
- /* Two messages with bodies combined into one fragment
- * Fragments split into "multi_message_body_divided"
- */
- char *multi_message_body[] = {
- "INVITE sip:bob@example.org SIP/2.0\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n"
- "Contact: sip:127.0.0.1:5061\r\n"
- "Max-Forwards: 70\r\n"
- "Content-Type: application/sdp\r\n"
- "Content-Length: 130\r\n"
- "\r\n"
- "v=0\r\n"
- "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 10000 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- "INVITE sip:bob@example.org SIP/2.0\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 2 INVITE\r\n"
- "Contact: sip:127.0.0.1:5061\r\n"
- "Max-Forwards: 70\r\n"
- "Content-Type: application/sdp\r\n"
- "Content-Length: 130\r\n"
- "\r\n"
- "v=0\r\n"
- "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 10000 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- };
- char *multi_message_body_divided[] = {
- "INVITE sip:bob@example.org SIP/2.0\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n"
- "Contact: sip:127.0.0.1:5061\r\n"
- "Max-Forwards: 70\r\n"
- "Content-Type: application/sdp\r\n"
- "Content-Length: 130\r\n"
- "\r\n"
- "v=0\r\n"
- "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 10000 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n",
- "INVITE sip:bob@example.org SIP/2.0\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 2 INVITE\r\n"
- "Contact: sip:127.0.0.1:5061\r\n"
- "Max-Forwards: 70\r\n"
- "Content-Type: application/sdp\r\n"
- "Content-Length: 130\r\n"
- "\r\n"
- "v=0\r\n"
- "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 10000 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- };
- /* Two messages that appear in two fragments. Fragment
- * boundaries do not align with message boundaries.
- * Fragments combine to make "multi_message_divided"
- */
- char *multi_message_in_fragments[] = {
- "SIP/2.0 100 Trying\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVI",
- "TE\r\n"
- "Contact: <sip:bob@example.org:5060>\r\n"
- "Content-Length: 0\r\n"
- "\r\n"
- "SIP/2.0 180 Ringing\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n"
- "Contact: <sip:bob@example.org:5060>\r\n"
- "Content-Length: 0\r\n"
- "\r\n"
- };
- /* Message with compact content-length header
- * Same as "normal" but with compact content-length header
- */
- char *compact[] = {
- "INVITE sip:bob@example.org SIP/2.0\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n"
- "Contact: sip:127.0.0.1:5061\r\n"
- "Max-Forwards: 70\r\n"
- "Content-Type: application/sdp\r\n"
- "l:130\r\n" /* intentionally no space */
- "\r\n"
- "v=0\r\n"
- "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 10000 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- };
- /* Message with faux content-length headers
- * Same as "normal" but with extra fake content-length headers
- */
- char *faux[] = {
- "INVITE sip:bob@example.org SIP/2.0\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n"
- "Contact: sip:127.0.0.1:5061\r\n"
- "Max-Forwards: 70\r\n"
- "Content-Type: application/sdp\r\n"
- "DisContent-Length: 0\r\n"
- "MalContent-Length: 60\r\n"
- "Content-Length:130\r\n" /* intentionally no space */
- "\r\n"
- "v=0\r\n"
- "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 10000 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- };
- /* Message with folded Content-Length header
- * Message is "normal" with Content-Length spread across three lines
- *
- * This is the test that requires pedantic=yes in order to pass
- */
- char *folded[] = {
- "INVITE sip:bob@example.org SIP/2.0\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n"
- "Contact: sip:127.0.0.1:5061\r\n"
- "Max-Forwards: 70\r\n"
- "Content-Type: application/sdp\r\n"
- "Content-Length: \t\r\n"
- "\t \r\n"
- " 130\t \r\n"
- "\r\n"
- "v=0\r\n"
- "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 10000 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- };
- /* Message with compact Content-length header in message and
- * full Content-Length header in the body. Ensure that the header
- * in the message is read and that the one in the body is ignored
- */
- char *cl_in_body[] = {
- "INVITE sip:bob@example.org SIP/2.0\r\n"
- "Via: SIP/2.0/TCP 127.0.0.1:5060;branch=[branch]\r\n"
- "From: sipp <sip:127.0.0.1:5061>;tag=12345\r\n"
- "To: <sip:bob@example.org:5060>\r\n"
- "Call-ID: 12345\r\n"
- "CSeq: 1 INVITE\r\n"
- "Contact: sip:127.0.0.1:5061\r\n"
- "Max-Forwards: 70\r\n"
- "Content-Type: application/sdp\r\n"
- "l: 149\r\n"
- "\r\n"
- "v=0\r\n"
- "Content-Length: 0\r\n"
- "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 10000 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- };
- struct ast_str *overflow;
- struct {
- char **fragments;
- char **expected;
- int num_expected;
- const char *description;
- } tests[] = {
- { normal, normal, 1, "normal" },
- { fragmented, normal, 1, "fragmented" },
- { fragmented_body, normal, 1, "fragmented_body" },
- { multi_fragment, normal, 1, "multi_fragment" },
- { multi_message, multi_message_divided, 2, "multi_message" },
- { multi_message_body, multi_message_body_divided, 2, "multi_message_body" },
- { multi_message_in_fragments, multi_message_divided, 2, "multi_message_in_fragments" },
- { compact, compact, 1, "compact" },
- { faux, faux, 1, "faux" },
- { folded, folded, 1, "folded" },
- { cl_in_body, cl_in_body, 1, "cl_in_body" },
- };
- int i;
- enum ast_test_result_state res = AST_TEST_PASS;
- switch (cmd) {
- case TEST_INIT:
- info->name = "sip_tcp_message_fragmentation";
- info->category = "/main/sip/transport/";
- info->summary = "SIP TCP message fragmentation test";
- info->description =
- "Tests reception of different TCP messages that have been fragmented or"
- "run together. This test mimicks the code that TCP reception uses.";
- return AST_TEST_NOT_RUN;
- case TEST_EXECUTE:
- break;
- }
- if (!sip_cfg.pedanticsipchecking) {
- ast_log(LOG_WARNING, "Not running test. Pedantic SIP checking is not enabled, so it is guaranteed to fail\n");
- return AST_TEST_NOT_RUN;
- }
- overflow = ast_str_create(128);
- if (!overflow) {
- return AST_TEST_FAIL;
- }
- for (i = 0; i < ARRAY_LEN(tests); ++i) {
- int num_messages = 0;
- if (mock_tcp_loop(tests[i].fragments, ARRAY_LEN(tests[i].fragments),
- &overflow, tests[i].expected, &num_messages, test)) {
- ast_test_status_update(test, "Failed to parse message '%s'\n", tests[i].description);
- res = AST_TEST_FAIL;
- break;
- }
- if (num_messages != tests[i].num_expected) {
- ast_test_status_update(test, "Did not receive the expected number of messages. "
- "Expected %d but received %d\n", tests[i].num_expected, num_messages);
- res = AST_TEST_FAIL;
- break;
- }
- }
- ast_free(overflow);
- return res;
- }
- AST_TEST_DEFINE(get_in_brackets_const_test)
- {
- const char *input;
- const char *start = NULL;
- int len = 0;
- int res;
- #define CHECK_RESULTS(in, expected_res, expected_start, expected_len) do { \
- input = (in); \
- res = get_in_brackets_const(input, &start, &len); \
- if ((expected_res) != res) { \
- ast_test_status_update(test, "Unexpected result: %d != %d\n", expected_res, res); \
- return AST_TEST_FAIL; \
- } \
- if ((expected_start) != start) { \
- const char *e = expected_start ? expected_start : "(null)"; \
- const char *a = start ? start : "(null)"; \
- ast_test_status_update(test, "Unexpected start: %s != %s\n", e, a); \
- return AST_TEST_FAIL; \
- } \
- if ((expected_len) != len) { \
- ast_test_status_update(test, "Unexpected len: %d != %d\n", expected_len, len); \
- return AST_TEST_FAIL; \
- } \
- } while(0)
- switch (cmd) {
- case TEST_INIT:
- info->name = __func__;
- info->category = "/channels/chan_sip/";
- info->summary = "get_in_brackets_const test";
- info->description =
- "Tests the get_in_brackets_const function";
- return AST_TEST_NOT_RUN;
- case TEST_EXECUTE:
- break;
- }
- CHECK_RESULTS("", 1, NULL, -1);
- CHECK_RESULTS("normal <test>", 0, input + 8, 4);
- CHECK_RESULTS("\"normal\" <test>", 0, input + 10, 4);
- CHECK_RESULTS("not normal <test", -1, NULL, -1);
- CHECK_RESULTS("\"yes < really\" <test>", 0, input + 16, 4);
- CHECK_RESULTS("\"even > this\" <test>", 0, input + 15, 4);
- CHECK_RESULTS("<sip:id1@10.10.10.10;lr>", 0, input + 1, 22);
- CHECK_RESULTS("<sip:id1@10.10.10.10;lr>, <sip:id1@10.10.10.20;lr>", 0, input + 1, 22);
- CHECK_RESULTS("<sip:id1,id2@10.10.10.10;lr>", 0, input + 1, 26);
- CHECK_RESULTS("<sip:id1@10., <sip:id2@10.10.10.10;lr>", 0, input + 1, 36);
- CHECK_RESULTS("\"quoted text\" <sip:dlg1@10.10.10.10;lr>", 0, input + 15, 23);
- return AST_TEST_PASS;
- }
- #endif
- #define DATA_EXPORT_SIP_PEER(MEMBER) \
- MEMBER(sip_peer, name, AST_DATA_STRING) \
- MEMBER(sip_peer, secret, AST_DATA_PASSWORD) \
- MEMBER(sip_peer, md5secret, AST_DATA_PASSWORD) \
- MEMBER(sip_peer, remotesecret, AST_DATA_PASSWORD) \
- MEMBER(sip_peer, context, AST_DATA_STRING) \
- MEMBER(sip_peer, subscribecontext, AST_DATA_STRING) \
- MEMBER(sip_peer, username, AST_DATA_STRING) \
- MEMBER(sip_peer, accountcode, AST_DATA_STRING) \
- MEMBER(sip_peer, tohost, AST_DATA_STRING) \
- MEMBER(sip_peer, regexten, AST_DATA_STRING) \
- MEMBER(sip_peer, fromuser, AST_DATA_STRING) \
- MEMBER(sip_peer, fromdomain, AST_DATA_STRING) \
- MEMBER(sip_peer, fullcontact, AST_DATA_STRING) \
- MEMBER(sip_peer, cid_num, AST_DATA_STRING) \
- MEMBER(sip_peer, cid_name, AST_DATA_STRING) \
- MEMBER(sip_peer, vmexten, AST_DATA_STRING) \
- MEMBER(sip_peer, language, AST_DATA_STRING) \
- MEMBER(sip_peer, mohinterpret, AST_DATA_STRING) \
- MEMBER(sip_peer, mohsuggest, AST_DATA_STRING) \
- MEMBER(sip_peer, parkinglot, AST_DATA_STRING) \
- MEMBER(sip_peer, useragent, AST_DATA_STRING) \
- MEMBER(sip_peer, mwi_from, AST_DATA_STRING) \
- MEMBER(sip_peer, engine, AST_DATA_STRING) \
- MEMBER(sip_peer, unsolicited_mailbox, AST_DATA_STRING) \
- MEMBER(sip_peer, is_realtime, AST_DATA_BOOLEAN) \
- MEMBER(sip_peer, host_dynamic, AST_DATA_BOOLEAN) \
- MEMBER(sip_peer, autoframing, AST_DATA_BOOLEAN) \
- MEMBER(sip_peer, inuse, AST_DATA_INTEGER) \
- MEMBER(sip_peer, ringing, AST_DATA_INTEGER) \
- MEMBER(sip_peer, onhold, AST_DATA_INTEGER) \
- MEMBER(sip_peer, call_limit, AST_DATA_INTEGER) \
- MEMBER(sip_peer, t38_maxdatagram, AST_DATA_INTEGER) \
- MEMBER(sip_peer, maxcallbitrate, AST_DATA_INTEGER) \
- MEMBER(sip_peer, rtptimeout, AST_DATA_SECONDS) \
- MEMBER(sip_peer, rtpholdtimeout, AST_DATA_SECONDS) \
- MEMBER(sip_peer, rtpkeepalive, AST_DATA_SECONDS) \
- MEMBER(sip_peer, lastms, AST_DATA_MILLISECONDS) \
- MEMBER(sip_peer, maxms, AST_DATA_MILLISECONDS) \
- MEMBER(sip_peer, qualifyfreq, AST_DATA_MILLISECONDS) \
- MEMBER(sip_peer, timer_t1, AST_DATA_MILLISECONDS) \
- MEMBER(sip_peer, timer_b, AST_DATA_MILLISECONDS) \
- MEMBER(sip_peer, description, AST_DATA_STRING)
- AST_DATA_STRUCTURE(sip_peer, DATA_EXPORT_SIP_PEER);
- static int peers_data_provider_get(const struct ast_data_search *search,
- struct ast_data *data_root)
- {
- struct sip_peer *peer;
- struct ao2_iterator i;
- struct ast_data *data_peer, *data_peer_mailboxes = NULL, *data_peer_mailbox, *enum_node;
- struct ast_data *data_sip_options;
- int total_mailboxes, x;
- struct sip_mailbox *mailbox;
- i = ao2_iterator_init(peers, 0);
- while ((peer = ao2_iterator_next(&i))) {
- ao2_lock(peer);
- data_peer = ast_data_add_node(data_root, "peer");
- if (!data_peer) {
- ao2_unlock(peer);
- ao2_ref(peer, -1);
- continue;
- }
- ast_data_add_structure(sip_peer, data_peer, peer);
- /* transfer mode */
- enum_node = ast_data_add_node(data_peer, "allowtransfer");
- if (!enum_node) {
- ao2_unlock(peer);
- ao2_ref(peer, -1);
- continue;
- }
- ast_data_add_str(enum_node, "text", transfermode2str(peer->allowtransfer));
- ast_data_add_int(enum_node, "value", peer->allowtransfer);
- /* transports */
- ast_data_add_str(data_peer, "transports", get_transport_list(peer->transports));
- /* peer type */
- if ((peer->type & SIP_TYPE_USER) && (peer->type & SIP_TYPE_PEER)) {
- ast_data_add_str(data_peer, "type", "friend");
- } else if (peer->type & SIP_TYPE_PEER) {
- ast_data_add_str(data_peer, "type", "peer");
- } else if (peer->type & SIP_TYPE_USER) {
- ast_data_add_str(data_peer, "type", "user");
- }
- /* mailboxes */
- total_mailboxes = 0;
- AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
- if (!total_mailboxes) {
- data_peer_mailboxes = ast_data_add_node(data_peer, "mailboxes");
- if (!data_peer_mailboxes) {
- break;
- }
- total_mailboxes++;
- }
- data_peer_mailbox = ast_data_add_node(data_peer_mailboxes, "mailbox");
- if (!data_peer_mailbox) {
- continue;
- }
- ast_data_add_str(data_peer_mailbox, "id", mailbox->id);
- }
- /* amaflags */
- enum_node = ast_data_add_node(data_peer, "amaflags");
- if (!enum_node) {
- ao2_unlock(peer);
- ao2_ref(peer, -1);
- continue;
- }
- ast_data_add_int(enum_node, "value", peer->amaflags);
- ast_data_add_str(enum_node, "text", ast_channel_amaflags2string(peer->amaflags));
- /* sip options */
- data_sip_options = ast_data_add_node(data_peer, "sipoptions");
- if (!data_sip_options) {
- ao2_unlock(peer);
- ao2_ref(peer, -1);
- continue;
- }
- for (x = 0 ; x < ARRAY_LEN(sip_options); x++) {
- ast_data_add_bool(data_sip_options, sip_options[x].text, peer->sipoptions & sip_options[x].id);
- }
- /* callingpres */
- enum_node = ast_data_add_node(data_peer, "callingpres");
- if (!enum_node) {
- ao2_unlock(peer);
- ao2_ref(peer, -1);
- continue;
- }
- ast_data_add_int(enum_node, "value", peer->callingpres);
- ast_data_add_str(enum_node, "text", ast_describe_caller_presentation(peer->callingpres));
- /* codecs */
- ast_data_add_codecs(data_peer, "codecs", peer->caps);
- if (!ast_data_search_match(search, data_peer)) {
- ast_data_remove_node(data_root, data_peer);
- }
- ao2_unlock(peer);
- ao2_ref(peer, -1);
- }
- ao2_iterator_destroy(&i);
- return 0;
- }
- static const struct ast_data_handler peers_data_provider = {
- .version = AST_DATA_HANDLER_VERSION,
- .get = peers_data_provider_get
- };
- static const struct ast_data_entry sip_data_providers[] = {
- AST_DATA_ENTRY("asterisk/channel/sip/peers", &peers_data_provider),
- };
- static const struct ast_sip_api_tech chan_sip_api_provider = {
- .version = AST_SIP_API_VERSION,
- .name = "chan_sip",
- .sipinfo_send = sipinfo_send,
- };
- static int unload_module(void);
- /*!
- * \brief Load the module
- *
- * Module loading including tests for configuration or dependencies.
- * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
- * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
- * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
- * configuration file or other non-critical problem return
- * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
- */
- static int load_module(void)
- {
- ast_verbose("SIP channel loading...\n");
- if (STASIS_MESSAGE_TYPE_INIT(session_timeout_type)) {
- unload_module();
- return AST_MODULE_LOAD_FAILURE;
- }
- if (!(sip_tech.capabilities = ast_format_cap_alloc(0))) {
- unload_module();
- return AST_MODULE_LOAD_FAILURE;
- }
- if (ast_sip_api_provider_register(&chan_sip_api_provider)) {
- unload_module();
- return AST_MODULE_LOAD_FAILURE;
- }
- /* the fact that ao2_containers can't resize automatically is a major worry! */
- /* if the number of objects gets above MAX_XXX_BUCKETS, things will slow down */
- peers = ao2_t_container_alloc(HASH_PEER_SIZE, peer_hash_cb, peer_cmp_cb, "allocate peers");
- peers_by_ip = ao2_t_container_alloc(HASH_PEER_SIZE, peer_iphash_cb, peer_ipcmp_cb, "allocate peers_by_ip");
- dialogs = ao2_t_container_alloc(HASH_DIALOG_SIZE, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs");
- dialogs_needdestroy = ao2_t_container_alloc(1, NULL, NULL, "allocate dialogs_needdestroy");
- dialogs_rtpcheck = ao2_t_container_alloc(HASH_DIALOG_SIZE, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs for rtpchecks");
- threadt = ao2_t_container_alloc(HASH_DIALOG_SIZE, threadt_hash_cb, threadt_cmp_cb, "allocate threadt table");
- if (!peers || !peers_by_ip || !dialogs || !dialogs_needdestroy || !dialogs_rtpcheck
- || !threadt) {
- ast_log(LOG_ERROR, "Unable to create primary SIP container(s)\n");
- unload_module();
- return AST_MODULE_LOAD_FAILURE;
- }
- if (!(sip_cfg.caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
- unload_module();
- return AST_MODULE_LOAD_FAILURE;
- }
- ast_format_cap_append_by_type(sip_tech.capabilities, AST_MEDIA_TYPE_AUDIO);
- registry_list = ao2_t_container_alloc(HASH_REGISTRY_SIZE, registry_hash_cb, registry_cmp_cb, "allocate registry_list");
- subscription_mwi_list = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX,
- AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN, NULL, NULL, "allocate subscription_mwi_list");
- if (!(sched = ast_sched_context_create())) {
- ast_log(LOG_ERROR, "Unable to create scheduler context\n");
- unload_module();
- return AST_MODULE_LOAD_FAILURE;
- }
- if (!(io = io_context_create())) {
- ast_log(LOG_ERROR, "Unable to create I/O context\n");
- unload_module();
- return AST_MODULE_LOAD_FAILURE;
- }
- sip_reloadreason = CHANNEL_MODULE_LOAD;
- can_parse_xml = sip_is_xml_parsable();
- if (reload_config(sip_reloadreason)) { /* Load the configuration from sip.conf */
- unload_module();
- return AST_MODULE_LOAD_DECLINE;
- }
- /* Initialize bogus peer. Can be done first after reload_config() */
- if (!(bogus_peer = temp_peer("(bogus_peer)"))) {
- ast_log(LOG_ERROR, "Unable to create bogus_peer for authentication\n");
- unload_module();
- return AST_MODULE_LOAD_FAILURE;
- }
- /* Make sure the auth will always fail. */
- ast_string_field_set(bogus_peer, md5secret, BOGUS_PEER_MD5SECRET);
- ast_clear_flag(&bogus_peer->flags[0], SIP_INSECURE);
- /* Prepare the version that does not require DTMF BEGIN frames.
- * We need to use tricks such as memcpy and casts because the variable
- * has const fields.
- */
- memcpy(&sip_tech_info, &sip_tech, sizeof(sip_tech));
- memset((void *) &sip_tech_info.send_digit_begin, 0, sizeof(sip_tech_info.send_digit_begin));
- if (ast_msg_tech_register(&sip_msg_tech)) {
- unload_module();
- return AST_MODULE_LOAD_FAILURE;
- }
- /* Make sure we can register our sip channel type */
- if (ast_channel_register(&sip_tech)) {
- ast_log(LOG_ERROR, "Unable to register channel type 'SIP'\n");
- unload_module();
- return AST_MODULE_LOAD_FAILURE;
- }
- #ifdef TEST_FRAMEWORK
- AST_TEST_REGISTER(test_sip_peers_get);
- AST_TEST_REGISTER(test_sip_mwi_subscribe_parse);
- AST_TEST_REGISTER(test_tcp_message_fragmentation);
- AST_TEST_REGISTER(get_in_brackets_const_test);
- #endif
- /* Register AstData providers */
- ast_data_register_multiple(sip_data_providers, ARRAY_LEN(sip_data_providers));
- /* Register all CLI functions for SIP */
- ast_cli_register_multiple(cli_sip, ARRAY_LEN(cli_sip));
- /* Tell the RTP engine about our RTP glue */
- ast_rtp_glue_register(&sip_rtp_glue);
- /* Register dialplan applications */
- ast_register_application_xml(app_dtmfmode, sip_dtmfmode);
- ast_register_application_xml(app_sipaddheader, sip_addheader);
- ast_register_application_xml(app_sipremoveheader, sip_removeheader);
- #ifdef TEST_FRAMEWORK
- ast_register_application_xml(app_sipsendcustominfo, sip_sendcustominfo);
- #endif
- /* Register dialplan functions */
- ast_custom_function_register(&sip_header_function);
- ast_custom_function_register(&sippeer_function);
- ast_custom_function_register(&checksipdomain_function);
- /* Register manager commands */
- ast_manager_register_xml("SIPpeers", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_show_peers);
- ast_manager_register_xml("SIPshowpeer", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_show_peer);
- ast_manager_register_xml("SIPqualifypeer", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_qualify_peer);
- ast_manager_register_xml("SIPshowregistry", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_show_registry);
- ast_manager_register_xml("SIPnotify", EVENT_FLAG_SYSTEM, manager_sipnotify);
- ast_manager_register_xml("SIPpeerstatus", EVENT_FLAG_SYSTEM, manager_sip_peer_status);
- sip_poke_all_peers();
- sip_keepalive_all_peers();
- sip_send_all_registers();
- sip_send_all_mwi_subscriptions();
- initialize_escs();
- if (sip_epa_register(&cc_epa_static_data)) {
- unload_module();
- return AST_MODULE_LOAD_DECLINE;
- }
- if (sip_reqresp_parser_init() == -1) {
- ast_log(LOG_ERROR, "Unable to initialize the SIP request and response parser\n");
- unload_module();
- return AST_MODULE_LOAD_DECLINE;
- }
- if (can_parse_xml) {
- /* SIP CC agents require the ability to parse XML PIDF bodies
- * in incoming PUBLISH requests
- */
- if (ast_cc_agent_register(&sip_cc_agent_callbacks)) {
- unload_module();
- return AST_MODULE_LOAD_DECLINE;
- }
- }
- if (ast_cc_monitor_register(&sip_cc_monitor_callbacks)) {
- unload_module();
- return AST_MODULE_LOAD_DECLINE;
- }
- if (!(sip_monitor_instances = ao2_container_alloc(37, sip_monitor_instance_hash_fn, sip_monitor_instance_cmp_fn))) {
- unload_module();
- return AST_MODULE_LOAD_DECLINE;
- }
- /* And start the monitor for the first time */
- restart_monitor();
- ast_realtime_require_field(ast_check_realtime("sipregs") ? "sipregs" : "sippeers",
- "name", RQ_CHAR, 10,
- "ipaddr", RQ_CHAR, INET6_ADDRSTRLEN - 1,
- "port", RQ_UINTEGER2, 5,
- "regseconds", RQ_INTEGER4, 11,
- "defaultuser", RQ_CHAR, 10,
- "fullcontact", RQ_CHAR, 35,
- "regserver", RQ_CHAR, 20,
- "useragent", RQ_CHAR, 20,
- "lastms", RQ_INTEGER4, 11,
- SENTINEL);
- sip_register_tests();
- network_change_stasis_subscribe();
- ast_websocket_add_protocol("sip", sip_websocket_callback);
- return AST_MODULE_LOAD_SUCCESS;
- }
- /*! \brief PBX unload module API */
- static int unload_module(void)
- {
- struct sip_pvt *p;
- struct sip_threadinfo *th;
- struct ast_context *con;
- struct ao2_iterator i;
- int wait_count;
- ast_sip_api_provider_unregister();
- ast_websocket_remove_protocol("sip", sip_websocket_callback);
- network_change_stasis_unsubscribe();
- acl_change_event_stasis_unsubscribe();
- ast_sched_dump(sched);
- /* First, take us out of the channel type list */
- ast_channel_unregister(&sip_tech);
- ast_msg_tech_unregister(&sip_msg_tech);
- /* Unregister dial plan functions */
- ast_custom_function_unregister(&sippeer_function);
- ast_custom_function_unregister(&sip_header_function);
- ast_custom_function_unregister(&checksipdomain_function);
- /* Unregister dial plan applications */
- ast_unregister_application(app_dtmfmode);
- ast_unregister_application(app_sipaddheader);
- ast_unregister_application(app_sipremoveheader);
- #ifdef TEST_FRAMEWORK
- ast_unregister_application(app_sipsendcustominfo);
- AST_TEST_UNREGISTER(test_sip_peers_get);
- AST_TEST_UNREGISTER(test_sip_mwi_subscribe_parse);
- AST_TEST_UNREGISTER(test_tcp_message_fragmentation);
- AST_TEST_UNREGISTER(get_in_brackets_const_test);
- #endif
- /* Unregister all the AstData providers */
- ast_data_unregister(NULL);
- /* Unregister CLI commands */
- ast_cli_unregister_multiple(cli_sip, ARRAY_LEN(cli_sip));
- /* Disconnect from RTP engine */
- ast_rtp_glue_unregister(&sip_rtp_glue);
- /* Unregister AMI actions */
- ast_manager_unregister("SIPpeers");
- ast_manager_unregister("SIPshowpeer");
- ast_manager_unregister("SIPqualifypeer");
- ast_manager_unregister("SIPshowregistry");
- ast_manager_unregister("SIPnotify");
- ast_manager_unregister("SIPpeerstatus");
- /* Kill TCP/TLS server threads */
- if (sip_tcp_desc.master) {
- ast_tcptls_server_stop(&sip_tcp_desc);
- }
- if (sip_tls_desc.master) {
- ast_tcptls_server_stop(&sip_tls_desc);
- }
- ast_ssl_teardown(sip_tls_desc.tls_cfg);
- /* Kill all existing TCP/TLS threads */
- i = ao2_iterator_init(threadt, 0);
- while ((th = ao2_t_iterator_next(&i, "iterate through tcp threads for 'sip show tcp'"))) {
- pthread_t thread = th->threadid;
- th->stop = 1;
- pthread_kill(thread, SIGURG);
- ao2_t_ref(th, -1, "decrement ref from iterator");
- }
- ao2_iterator_destroy(&i);
- /* Hangup all dialogs if they have an owner */
- i = ao2_iterator_init(dialogs, 0);
- while ((p = ao2_t_iterator_next(&i, "iterate thru dialogs"))) {
- if (p->owner)
- ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
- ao2_t_ref(p, -1, "toss dialog ptr from iterator_next");
- }
- ao2_iterator_destroy(&i);
- unlink_all_peers_from_tables();
- ast_mutex_lock(&monlock);
- if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) {
- pthread_t th = monitor_thread;
- monitor_thread = AST_PTHREADT_STOP;
- pthread_cancel(th);
- pthread_kill(th, SIGURG);
- ast_mutex_unlock(&monlock);
- pthread_join(th, NULL);
- } else {
- monitor_thread = AST_PTHREADT_STOP;
- ast_mutex_unlock(&monlock);
- }
- /* Destroy all the dialogs and free their memory */
- i = ao2_iterator_init(dialogs, 0);
- while ((p = ao2_t_iterator_next(&i, "iterate thru dialogs"))) {
- dialog_unlink_all(p);
- ao2_t_ref(p, -1, "throw away iterator result");
- }
- ao2_iterator_destroy(&i);
- /* Free memory for local network address mask */
- ast_free_ha(localaddr);
- ast_mutex_lock(&authl_lock);
- if (authl) {
- ao2_t_cleanup(authl, "Removing global authentication");
- authl = NULL;
- }
- ast_mutex_unlock(&authl_lock);
- sip_epa_unregister_all();
- destroy_escs();
- ast_free(default_tls_cfg.certfile);
- ast_free(default_tls_cfg.pvtfile);
- ast_free(default_tls_cfg.cipher);
- ast_free(default_tls_cfg.cafile);
- ast_free(default_tls_cfg.capath);
- ast_rtp_dtls_cfg_free(&default_dtls_cfg);
- cleanup_all_regs();
- ao2_cleanup(registry_list);
- {
- struct ao2_iterator iter;
- struct sip_subscription_mwi *iterator;
- iter = ao2_iterator_init(subscription_mwi_list, 0);
- while ((iterator = ao2_t_iterator_next(&iter, "unload_module iter"))) {
- ao2_lock(iterator);
- if (iterator->dnsmgr) {
- ast_dnsmgr_release(iterator->dnsmgr);
- iterator->dnsmgr = NULL;
- ao2_t_ref(iterator, -1, "dnsmgr release");
- }
- ao2_unlock(iterator);
- ao2_t_ref(iterator, -1, "unload_module iter");
- }
- ao2_iterator_destroy(&iter);
- }
- ao2_cleanup(subscription_mwi_list);
- /*
- * Wait awhile for the TCP/TLS thread container to become empty.
- *
- * XXX This is a hack, but the worker threads cannot be created
- * joinable. They can die on their own and remove themselves
- * from the container thus resulting in a huge memory leak.
- */
- wait_count = 1000;
- while (ao2_container_count(threadt) && --wait_count) {
- sched_yield();
- }
- if (!wait_count) {
- ast_debug(2, "TCP/TLS thread container did not become empty :(\n");
- }
- ao2_t_cleanup(bogus_peer, "unref the bogus_peer");
- ao2_t_cleanup(peers, "unref the peers table");
- ao2_t_cleanup(peers_by_ip, "unref the peers_by_ip table");
- ao2_t_cleanup(dialogs, "unref the dialogs table");
- ao2_t_cleanup(dialogs_needdestroy, "unref dialogs_needdestroy");
- ao2_t_cleanup(dialogs_rtpcheck, "unref dialogs_rtpcheck");
- ao2_t_cleanup(threadt, "unref the thread table");
- ao2_t_cleanup(sip_monitor_instances, "unref the sip_monitor_instances table");
- clear_sip_domains();
- sip_cfg.contact_acl = ast_free_acl_list(sip_cfg.contact_acl);
- if (sipsock_read_id) {
- ast_io_remove(io, sipsock_read_id);
- sipsock_read_id = NULL;
- }
- close(sipsock);
- io_context_destroy(io);
- ast_sched_context_destroy(sched);
- con = ast_context_find(used_context);
- if (con) {
- ast_context_destroy(con, "SIP");
- }
- ast_unload_realtime("sipregs");
- ast_unload_realtime("sippeers");
- ast_cc_monitor_unregister(&sip_cc_monitor_callbacks);
- ast_cc_agent_unregister(&sip_cc_agent_callbacks);
- sip_reqresp_parser_exit();
- sip_unregister_tests();
- if (notify_types) {
- ast_config_destroy(notify_types);
- notify_types = NULL;
- }
- ao2_cleanup(sip_tech.capabilities);
- sip_tech.capabilities = NULL;
- ao2_cleanup(sip_cfg.caps);
- sip_cfg.caps = NULL;
- STASIS_MESSAGE_TYPE_CLEANUP(session_timeout_type);
- return 0;
- }
- AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Session Initiation Protocol (SIP)",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
- .nonoptreq = "res_crypto,res_http_websocket",
- );
|