123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115321163211732118321193212032121321223212332124321253212632127321283212932130321313213232133321343213532136321373213832139321403214132142321433214432145321463214732148321493215032151321523215332154321553215632157321583215932160321613216232163321643216532166321673216832169321703217132172321733217432175321763217732178321793218032181321823218332184321853218632187321883218932190321913219232193321943219532196321973219832199322003220132202322033220432205322063220732208322093221032211322123221332214322153221632217322183221932220322213222232223322243222532226322273222832229322303223132232322333223432235322363223732238322393224032241322423224332244322453224632247322483224932250322513225232253322543225532256322573225832259322603226132262322633226432265322663226732268322693227032271322723227332274322753227632277322783227932280322813228232283322843228532286322873228832289322903229132292322933229432295322963229732298322993230032301323023230332304323053230632307323083230932310323113231232313323143231532316323173231832319323203232132322323233232432325323263232732328323293233032331323323233332334323353233632337323383233932340323413234232343323443234532346323473234832349323503235132352323533235432355323563235732358323593236032361323623236332364323653236632367323683236932370323713237232373323743237532376323773237832379323803238132382323833238432385323863238732388323893239032391323923239332394323953239632397323983239932400324013240232403324043240532406324073240832409324103241132412324133241432415324163241732418324193242032421324223242332424324253242632427324283242932430324313243232433324343243532436324373243832439324403244132442324433244432445324463244732448324493245032451324523245332454324553245632457324583245932460324613246232463324643246532466324673246832469324703247132472324733247432475324763247732478324793248032481324823248332484324853248632487324883248932490324913249232493324943249532496324973249832499325003250132502325033250432505325063250732508325093251032511325123251332514325153251632517325183251932520325213252232523325243252532526325273252832529325303253132532325333253432535325363253732538325393254032541325423254332544325453254632547325483254932550325513255232553325543255532556325573255832559325603256132562325633256432565325663256732568325693257032571325723257332574325753257632577325783257932580325813258232583325843258532586325873258832589325903259132592325933259432595325963259732598325993260032601326023260332604326053260632607326083260932610326113261232613326143261532616326173261832619326203262132622326233262432625326263262732628326293263032631326323263332634326353263632637326383263932640326413264232643326443264532646326473264832649326503265132652326533265432655326563265732658326593266032661326623266332664326653266632667326683266932670326713267232673326743267532676326773267832679326803268132682326833268432685326863268732688326893269032691326923269332694326953269632697326983269932700327013270232703327043270532706327073270832709327103271132712327133271432715327163271732718327193272032721327223272332724327253272632727327283272932730327313273232733327343273532736327373273832739327403274132742327433274432745327463274732748327493275032751327523275332754327553275632757327583275932760327613276232763327643276532766327673276832769327703277132772327733277432775327763277732778327793278032781327823278332784327853278632787327883278932790327913279232793327943279532796327973279832799328003280132802328033280432805328063280732808328093281032811328123281332814328153281632817328183281932820328213282232823328243282532826328273282832829328303283132832328333283432835328363283732838328393284032841328423284332844328453284632847328483284932850328513285232853328543285532856328573285832859328603286132862328633286432865328663286732868328693287032871328723287332874328753287632877328783287932880328813288232883328843288532886328873288832889328903289132892328933289432895328963289732898328993290032901329023290332904329053290632907329083290932910329113291232913329143291532916329173291832919329203292132922329233292432925329263292732928329293293032931329323293332934329353293632937329383293932940329413294232943329443294532946329473294832949329503295132952329533295432955329563295732958329593296032961329623296332964329653296632967329683296932970329713297232973329743297532976329773297832979329803298132982329833298432985329863298732988329893299032991329923299332994329953299632997329983299933000330013300233003330043300533006330073300833009330103301133012330133301433015330163301733018330193302033021330223302333024330253302633027330283302933030330313303233033330343303533036330373303833039330403304133042330433304433045330463304733048330493305033051330523305333054330553305633057330583305933060330613306233063330643306533066330673306833069330703307133072330733307433075330763307733078330793308033081330823308333084330853308633087330883308933090330913309233093330943309533096330973309833099331003310133102331033310433105331063310733108331093311033111331123311333114331153311633117331183311933120331213312233123331243312533126331273312833129331303313133132331333313433135331363313733138331393314033141331423314333144331453314633147331483314933150331513315233153331543315533156331573315833159331603316133162331633316433165331663316733168331693317033171331723317333174331753317633177331783317933180331813318233183331843318533186331873318833189331903319133192331933319433195331963319733198331993320033201332023320333204332053320633207332083320933210332113321233213332143321533216332173321833219332203322133222332233322433225332263322733228332293323033231332323323333234332353323633237332383323933240332413324233243332443324533246332473324833249332503325133252332533325433255332563325733258332593326033261332623326333264332653326633267332683326933270332713327233273332743327533276332773327833279332803328133282332833328433285332863328733288332893329033291332923329333294332953329633297332983329933300333013330233303333043330533306333073330833309333103331133312333133331433315333163331733318333193332033321333223332333324333253332633327333283332933330333313333233333333343333533336333373333833339333403334133342333433334433345333463334733348333493335033351333523335333354333553335633357333583335933360333613336233363333643336533366333673336833369333703337133372333733337433375333763337733378333793338033381333823338333384333853338633387333883338933390333913339233393333943339533396333973339833399334003340133402334033340433405334063340733408334093341033411334123341333414334153341633417334183341933420334213342233423334243342533426334273342833429334303343133432334333343433435334363343733438334393344033441334423344333444334453344633447334483344933450334513345233453334543345533456334573345833459334603346133462334633346433465334663346733468334693347033471334723347333474334753347633477334783347933480334813348233483334843348533486334873348833489334903349133492334933349433495334963349733498334993350033501335023350333504335053350633507335083350933510335113351233513335143351533516335173351833519335203352133522335233352433525335263352733528335293353033531335323353333534335353353633537335383353933540335413354233543335443354533546335473354833549335503355133552335533355433555335563355733558335593356033561335623356333564335653356633567335683356933570335713357233573335743357533576335773357833579335803358133582335833358433585335863358733588335893359033591335923359333594335953359633597335983359933600336013360233603336043360533606336073360833609336103361133612336133361433615336163361733618336193362033621336223362333624336253362633627336283362933630336313363233633336343363533636336373363833639336403364133642336433364433645336463364733648336493365033651336523365333654336553365633657336583365933660336613366233663336643366533666336673366833669336703367133672336733367433675336763367733678336793368033681336823368333684336853368633687336883368933690336913369233693336943369533696336973369833699337003370133702337033370433705337063370733708337093371033711337123371333714337153371633717337183371933720337213372233723337243372533726337273372833729337303373133732337333373433735337363373733738337393374033741337423374333744337453374633747337483374933750337513375233753337543375533756337573375833759337603376133762337633376433765337663376733768337693377033771337723377333774337753377633777337783377933780337813378233783337843378533786337873378833789337903379133792337933379433795337963379733798337993380033801338023380333804338053380633807338083380933810338113381233813338143381533816338173381833819338203382133822338233382433825338263382733828338293383033831338323383333834338353383633837338383383933840338413384233843338443384533846338473384833849338503385133852338533385433855338563385733858338593386033861338623386333864338653386633867338683386933870338713387233873338743387533876338773387833879338803388133882338833388433885338863388733888338893389033891338923389333894338953389633897338983389933900339013390233903339043390533906339073390833909339103391133912339133391433915339163391733918339193392033921339223392333924339253392633927339283392933930339313393233933339343393533936339373393833939339403394133942339433394433945339463394733948339493395033951339523395333954339553395633957339583395933960339613396233963339643396533966339673396833969339703397133972339733397433975339763397733978339793398033981339823398333984339853398633987339883398933990339913399233993339943399533996339973399833999340003400134002340033400434005340063400734008340093401034011340123401334014340153401634017340183401934020340213402234023340243402534026340273402834029340303403134032340333403434035340363403734038340393404034041340423404334044340453404634047340483404934050340513405234053340543405534056340573405834059340603406134062340633406434065340663406734068340693407034071340723407334074340753407634077340783407934080340813408234083340843408534086340873408834089340903409134092340933409434095340963409734098340993410034101341023410334104341053410634107341083410934110341113411234113341143411534116341173411834119341203412134122341233412434125341263412734128341293413034131341323413334134341353413634137341383413934140341413414234143341443414534146341473414834149341503415134152341533415434155341563415734158341593416034161341623416334164341653416634167341683416934170341713417234173341743417534176341773417834179341803418134182341833418434185341863418734188341893419034191341923419334194341953419634197341983419934200342013420234203342043420534206342073420834209342103421134212342133421434215342163421734218342193422034221342223422334224342253422634227342283422934230342313423234233342343423534236342373423834239342403424134242342433424434245342463424734248342493425034251342523425334254342553425634257342583425934260342613426234263342643426534266342673426834269342703427134272342733427434275342763427734278342793428034281342823428334284342853428634287342883428934290342913429234293342943429534296342973429834299343003430134302343033430434305343063430734308343093431034311343123431334314343153431634317343183431934320343213432234323343243432534326343273432834329343303433134332343333433434335343363433734338343393434034341343423434334344343453434634347343483434934350343513435234353343543435534356343573435834359343603436134362343633436434365343663436734368343693437034371343723437334374343753437634377343783437934380343813438234383343843438534386343873438834389343903439134392343933439434395343963439734398343993440034401344023440334404344053440634407344083440934410344113441234413344143441534416344173441834419344203442134422344233442434425344263442734428344293443034431344323443334434344353443634437344383443934440344413444234443344443444534446344473444834449344503445134452344533445434455344563445734458344593446034461344623446334464344653446634467344683446934470344713447234473344743447534476344773447834479344803448134482344833448434485344863448734488344893449034491344923449334494344953449634497344983449934500345013450234503345043450534506345073450834509345103451134512345133451434515345163451734518345193452034521345223452334524345253452634527345283452934530345313453234533345343453534536345373453834539345403454134542345433454434545345463454734548345493455034551345523455334554345553455634557345583455934560345613456234563345643456534566345673456834569345703457134572345733457434575345763457734578345793458034581345823458334584345853458634587345883458934590345913459234593345943459534596345973459834599346003460134602346033460434605346063460734608346093461034611346123461334614346153461634617346183461934620346213462234623346243462534626346273462834629346303463134632346333463434635346363463734638346393464034641 |
- /*
- * 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 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);
- 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 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);
- /*------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 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, NULL))) {
- 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 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)
- struct sip_pvt *dialog_ref_debug(struct sip_pvt *p, const char *tag, char *file, int line, const char *func)
- {
- if (p)
- #ifdef REF_DEBUG
- __ao2_ref_debug(p, 1, tag, file, line, func);
- #else
- ao2_ref(p, 1);
- #endif
- else
- ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n");
- return p;
- }
- struct sip_pvt *dialog_unref_debug(struct sip_pvt *p, const char *tag, char *file, int line, const char *func)
- {
- if (p)
- #ifdef REF_DEBUG
- __ao2_ref_debug(p, -1, tag, file, line, func);
- #else
- ao2_ref(p, -1);
- #endif
- return NULL;
- }
- /*! \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;
- }
- #ifdef REF_DEBUG
- struct sip_peer *_ref_peer(struct sip_peer *peer, char *tag, char *file, int line, const char *func)
- {
- if (peer)
- __ao2_ref_debug(peer, 1, tag, file, line, func);
- else
- ast_log(LOG_ERROR, "Attempt to Ref a null peer pointer\n");
- return peer;
- }
- void *_unref_peer(struct sip_peer *peer, char *tag, char *file, int line, const char *func)
- {
- if (peer)
- __ao2_ref_debug(peer, -1, tag, file, line, func);
- return NULL;
- }
- #else
- /*!
- * helper functions to unreference various types of objects.
- * By handling them this way, we don't have to declare the
- * destructor on each call, which removes the chance of errors.
- */
- void *sip_unref_peer(struct sip_peer *peer, char *tag)
- {
- ao2_t_ref(peer, -1, tag);
- return NULL;
- }
- struct sip_peer *sip_ref_peer(struct sip_peer *peer, char *tag)
- {
- ao2_t_ref(peer, 1, tag);
- return peer;
- }
- #endif /* REF_DEBUG */
- 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->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)) {
- 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;
- }
- 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;
- }
- if (p->refer) {
- ast_string_field_free_memory(p->refer);
- ast_free(p->refer);
- p->refer = NULL;
- }
- 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;
- }
- /* Lastly, kill the callid associated with the pvt */
- if (p->logger_callid) {
- ast_callid_unref(p->logger_callid);
- }
- }
- /*! \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, struct 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, "%02X ", (unsigned)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, struct ast_callid *callid)
- {
- if (pvt->logger_callid) {
- ast_callid_unref(pvt->logger_callid);
- }
- ast_callid_ref(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, struct ast_callid *logger_callid)
- {
- struct sip_pvt *p;
- if (!(p = ao2_t_alloc(sizeof(*p), sip_destroy_fn, "allocate a dialog(pvt) struct")))
- 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 addres 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;
- struct ast_callid *logger_callid;
- sip_pvt_lock(original);
- callid = ast_strdupa(original->callid);
- logger_callid = original->logger_callid;
- if (logger_callid) {
- ast_callid_ref(logger_callid);
- }
- sip_pvt_unlock(original);
- p = sip_alloc(callid, addr, 1, SIP_INVITE, req, logger_callid);
- if (logger_callid) {
- ast_callid_unref(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)
- {
- 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;
- }
- }
- if (!sip_cfg.pedanticsipchecking) {
- struct sip_pvt tmp_dialog = {
- .callid = callid,
- };
- sip_pvt_ptr = ao2_t_find(dialogs, &tmp_dialog, OBJ_POINTER, "ao2_find in dialogs");
- 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_t_callback(dialogs,
- OBJ_POINTER | OBJ_MULTIPLE,
- dialog_find_multiple,
- &tmp_dialog,
- "pedantic ao2_find in dialogs");
- 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)");
- dialog_unref(sip_pvt_ptr, "pvt did not match incoming SIP msg, unref from search.");
- 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:
- dialog_unref(sip_pvt_ptr, "pvt did not match incoming SIP msg, unref from search");
- 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;
- struct ast_callid *logger_callid = NULL;
- 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");
- }
- /* If we created an ast_callid for an invite, we need to free it now. */
- if (logger_callid) {
- ast_callid_unref(logger_callid);
- }
- 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 = data->str;
- 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';
- data->used = t;
- }
- /*! \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 = req->data->str;
- ptrdiff_t *dst = req->header;
- int i = 0, 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 (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, "We are requesting SRTP for audio, but they responded without it!\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, "We are requesting SRTP for video, but they responded without it!\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if (!(secure_audio || secure_video) && 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 on 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.
- */
- if (!sip_route_empty(&p->route) &&
- !(sipmethod == SIP_CANCEL ||
- (sipmethod == SIP_ACK && (p->invitestate == INV_COMPLETED || p->invitestate == INV_CANCELLED)))) {
- 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(req->data->str); /* there shouldn't be any */
- char *local_rlpart1;
- if (!*e)
- return -1;
- req->rlpart1 = e - req->data->str; /* 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 - req->data->str;
- } 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 - req->data->str; /* 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 Build contact header - the contact header we send out */
- static void build_contact(struct sip_pvt *p)
- {
- char tmp[SIPBUFSIZE];
- char *user = ast_uri_encode(p->exten, tmp, sizeof(tmp), ast_uri_sip_user);
- if (p->socket.type == AST_TRANSPORT_UDP) {
- ast_string_field_build(p, our_contact, "<sip:%s%s%s>", user,
- ast_strlen_zero(user) ? "" : "@", ast_sockaddr_stringify_remote(&p->ourip));
- } else {
- ast_string_field_build(p, our_contact, "<sip:%s%s%s;transport=%s>", 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);
- 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, NULL))) {
- 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, NULL))) {
- 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_contact(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, NULL))) {
- 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, NULL))) {
- 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);
- build_contact(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);
- 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)
- {
- p->refer = ast_calloc_with_stringfields(1, struct sip_refer, 512);
- return p->refer ? 1 : 0;
- }
- /*! \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", "", "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;
- }
- /*! \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 = buf->str;
- 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 = buf->str;
- 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);
- 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 (!p->refer && !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);
- }
- 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 r)
- {
- return map_x_s(strefresher_params, r, "Unknown");
- }
- static enum st_refresher 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_append(s,
- "Event: RegistrationsComplete\r\n"
- "EventList: Complete\r\n"
- "ListItems: %d\r\n"
- "%s"
- "\r\n", total, idtext);
- 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_append(s,
- "Event: PeerlistComplete\r\n"
- "EventList: Complete\r\n"
- "ListItems: %d\r\n"
- "%s"
- "\r\n", total, idtext);
- 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 = '\0';
- 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 = '\0';
- 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;
- 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_ack(s, m, "Peer status will follow");
- 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");
- }
- 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");
- }
- astman_append(s,
- "Event: SIPpeerstatusComplete\r\n"
- "%s"
- "\r\n",
- idText);
- 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 = {9, { "retrans_pkt",
- "__sip_autodestruct",
- "expire_register",
- "auto_congest",
- "sip_reg_timeout",
- "sip_poke_peer_s",
- "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_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, " 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", strefresherparam2str(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, NULL))) {
- 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_remote_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 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 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 500/501 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 unxpected 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 479: /* SER: Not able to process the URI - address is wrong in register*/
- ast_log(LOG_WARNING, "Got error 479 on register to %s@%s, giving up (check config)\n", p->registry->username, p->registry->hostname);
- pvt_set_needdestroy(p, "received 479 response");
- if (r->call)
- r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 479");
- 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 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);
- if (ast_strlen_zero(p->context))
- ast_string_field_set(p, context, sip_cfg.default_context);
- if (ast_shutting_down()) {
- 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;
- 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 = sip_get_header(req, "Supported");
- if (!ast_strlen_zero(supported)) {
- p->sipoptions = parse_sip_options(supported, NULL, 0);
- }
- }
- /* Find out what they require */
- required = sip_get_header(req, "Require");
- if (!ast_strlen_zero(required)) {
- char unsupported[256] = { 0, };
- required_profile = 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 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 = INV_REQ_ERROR;
- 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 (!p->refer && !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); /* 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:
- 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:
- 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 (!p->refer && !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, NULL))) {
- 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(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);
- /* 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, NULL))) {
- 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) {
- /* 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, NULL))) {
- 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;
- struct 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;
- if (callid) {
- ast_callid_unref(callid);
- }
- 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;
- if (callid) {
- ast_callid_unref(callid);
- }
- 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); */
- if (callid) {
- ast_callid_unref(callid);
- }
- 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 */
- if (callid) {
- callid = ast_callid_unref(callid);
- }
- 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_NO);
- } 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);
- 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") || !strcmp(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) {
- sip_poke_peer(peer, 0);
- }
- }
- 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 void cleanup_all_regs(void)
- {
- struct ao2_iterator iter;
- struct sip_registry *iterator;
- /* First, destroy all outstanding registry calls */
- /* This is needed, since otherwise active registry entries will not be destroyed */
- iter = ao2_iterator_init(registry_list, 0);
- while ((iterator = ao2_t_iterator_next(&iter, "cleanup_all_regs iter"))) {
- ao2_lock(iterator);
- if (iterator->call) {
- ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", iterator->username, iterator->hostname);
- /* This will also remove references to the registry */
- dialog_unlink_all(iterator->call);
- iterator->call = dialog_unref(iterator->call, "remove iterator->call from registry traversal");
- }
- if (iterator->expire > -1) {
- AST_SCHED_DEL_UNREF(sched, iterator->expire, ao2_t_ref(iterator, -1, "reg ptr unref from reload config"));
- }
- if (iterator->timeout > -1) {
- AST_SCHED_DEL_UNREF(sched, iterator->timeout, ao2_t_ref(iterator, -1, "reg ptr unref from reload config"));
- }
- if (iterator->dnsmgr) {
- ast_dnsmgr_release(iterator->dnsmgr);
- iterator->dnsmgr = NULL;
- ao2_t_ref(iterator, -1, "reg ptr unref from dnsmgr");
- }
- ao2_unlock(iterator);
- ao2_t_ref(iterator, -1, "cleanup_all_regs iter");
- }
- ao2_iterator_destroy(&iter);
- }
- /*! \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 */
- 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 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);
- }
- 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("");
- /* 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(&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;
- }
- /* 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, "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_remote_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);
- 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",
- );
|