1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984329853298632987329883298932990329913299232993329943299532996329973299832999330003300133002330033300433005330063300733008330093301033011330123301333014330153301633017330183301933020330213302233023330243302533026330273302833029330303303133032330333303433035330363303733038330393304033041330423304333044330453304633047330483304933050330513305233053330543305533056330573305833059330603306133062330633306433065330663306733068330693307033071330723307333074330753307633077330783307933080330813308233083330843308533086330873308833089330903309133092330933309433095330963309733098330993310033101331023310333104331053310633107331083310933110331113311233113331143311533116331173311833119331203312133122331233312433125331263312733128331293313033131331323313333134331353313633137331383313933140331413314233143331443314533146331473314833149331503315133152331533315433155331563315733158331593316033161331623316333164331653316633167331683316933170331713317233173331743317533176331773317833179331803318133182331833318433185331863318733188331893319033191331923319333194331953319633197331983319933200332013320233203332043320533206332073320833209332103321133212332133321433215332163321733218332193322033221332223322333224332253322633227332283322933230332313323233233332343323533236332373323833239332403324133242332433324433245332463324733248332493325033251332523325333254332553325633257332583325933260332613326233263332643326533266332673326833269332703327133272332733327433275332763327733278332793328033281332823328333284332853328633287332883328933290332913329233293332943329533296332973329833299333003330133302333033330433305333063330733308333093331033311333123331333314333153331633317333183331933320333213332233323333243332533326333273332833329333303333133332333333333433335333363333733338333393334033341333423334333344333453334633347333483334933350333513335233353333543335533356333573335833359333603336133362333633336433365333663336733368333693337033371333723337333374333753337633377333783337933380333813338233383333843338533386333873338833389333903339133392333933339433395333963339733398333993340033401334023340333404334053340633407334083340933410334113341233413334143341533416334173341833419334203342133422334233342433425334263342733428334293343033431334323343333434334353343633437334383343933440334413344233443334443344533446334473344833449334503345133452334533345433455334563345733458334593346033461334623346333464334653346633467334683346933470334713347233473334743347533476334773347833479334803348133482334833348433485334863348733488334893349033491334923349333494334953349633497334983349933500335013350233503335043350533506335073350833509335103351133512335133351433515335163351733518335193352033521335223352333524335253352633527335283352933530335313353233533335343353533536335373353833539335403354133542335433354433545335463354733548335493355033551335523355333554335553355633557335583355933560335613356233563335643356533566335673356833569335703357133572335733357433575335763357733578335793358033581335823358333584335853358633587335883358933590335913359233593335943359533596335973359833599336003360133602336033360433605336063360733608336093361033611336123361333614336153361633617336183361933620336213362233623336243362533626336273362833629336303363133632336333363433635336363363733638336393364033641336423364333644336453364633647336483364933650336513365233653336543365533656336573365833659336603366133662336633366433665336663366733668336693367033671336723367333674336753367633677336783367933680336813368233683336843368533686336873368833689336903369133692336933369433695336963369733698336993370033701337023370333704337053370633707337083370933710337113371233713337143371533716337173371833719337203372133722337233372433725337263372733728337293373033731337323373333734337353373633737337383373933740337413374233743337443374533746337473374833749337503375133752337533375433755337563375733758337593376033761337623376333764337653376633767337683376933770337713377233773337743377533776337773377833779337803378133782337833378433785337863378733788337893379033791337923379333794337953379633797337983379933800338013380233803338043380533806338073380833809338103381133812338133381433815338163381733818338193382033821338223382333824338253382633827338283382933830338313383233833338343383533836338373383833839338403384133842338433384433845338463384733848338493385033851338523385333854338553385633857338583385933860338613386233863338643386533866338673386833869338703387133872338733387433875338763387733878338793388033881338823388333884338853388633887338883388933890338913389233893338943389533896338973389833899339003390133902339033390433905339063390733908339093391033911339123391333914339153391633917339183391933920339213392233923339243392533926339273392833929339303393133932339333393433935339363393733938339393394033941339423394333944339453394633947339483394933950339513395233953339543395533956339573395833959339603396133962339633396433965339663396733968339693397033971339723397333974339753397633977339783397933980339813398233983339843398533986339873398833989339903399133992339933399433995339963399733998339993400034001340023400334004340053400634007340083400934010340113401234013340143401534016340173401834019340203402134022340233402434025340263402734028340293403034031340323403334034340353403634037340383403934040340413404234043340443404534046340473404834049340503405134052340533405434055340563405734058340593406034061340623406334064340653406634067340683406934070340713407234073340743407534076340773407834079340803408134082340833408434085340863408734088340893409034091340923409334094340953409634097340983409934100341013410234103341043410534106341073410834109341103411134112341133411434115341163411734118341193412034121341223412334124341253412634127341283412934130341313413234133341343413534136341373413834139341403414134142341433414434145341463414734148341493415034151341523415334154341553415634157341583415934160341613416234163341643416534166341673416834169341703417134172341733417434175341763417734178341793418034181341823418334184341853418634187341883418934190341913419234193341943419534196341973419834199342003420134202342033420434205342063420734208342093421034211342123421334214342153421634217342183421934220342213422234223342243422534226342273422834229342303423134232342333423434235342363423734238342393424034241342423424334244342453424634247342483424934250342513425234253342543425534256342573425834259342603426134262342633426434265342663426734268342693427034271342723427334274342753427634277342783427934280342813428234283342843428534286342873428834289342903429134292342933429434295342963429734298342993430034301343023430334304343053430634307343083430934310343113431234313343143431534316343173431834319343203432134322343233432434325343263432734328343293433034331343323433334334343353433634337343383433934340343413434234343343443434534346343473434834349343503435134352343533435434355343563435734358343593436034361343623436334364343653436634367343683436934370343713437234373343743437534376343773437834379343803438134382343833438434385343863438734388343893439034391343923439334394343953439634397343983439934400344013440234403344043440534406344073440834409344103441134412344133441434415344163441734418344193442034421344223442334424344253442634427344283442934430344313443234433344343443534436344373443834439344403444134442344433444434445344463444734448344493445034451344523445334454344553445634457344583445934460344613446234463344643446534466344673446834469344703447134472344733447434475344763447734478344793448034481344823448334484344853448634487344883448934490344913449234493344943449534496344973449834499345003450134502345033450434505345063450734508345093451034511345123451334514345153451634517345183451934520345213452234523345243452534526345273452834529345303453134532345333453434535345363453734538345393454034541345423454334544345453454634547345483454934550345513455234553345543455534556345573455834559345603456134562345633456434565345663456734568345693457034571345723457334574345753457634577345783457934580345813458234583345843458534586345873458834589345903459134592345933459434595345963459734598345993460034601346023460334604346053460634607346083460934610346113461234613346143461534616346173461834619346203462134622346233462434625346263462734628346293463034631346323463334634346353463634637346383463934640346413464234643346443464534646346473464834649346503465134652346533465434655346563465734658346593466034661346623466334664346653466634667346683466934670346713467234673346743467534676346773467834679346803468134682346833468434685346863468734688346893469034691346923469334694346953469634697346983469934700347013470234703347043470534706347073470834709347103471134712347133471434715347163471734718347193472034721347223472334724347253472634727347283472934730347313473234733347343473534736347373473834739347403474134742347433474434745347463474734748347493475034751347523475334754347553475634757347583475934760347613476234763347643476534766347673476834769347703477134772347733477434775347763477734778347793478034781347823478334784347853478634787347883478934790347913479234793347943479534796347973479834799348003480134802348033480434805348063480734808348093481034811348123481334814348153481634817348183481934820348213482234823348243482534826348273482834829348303483134832348333483434835348363483734838348393484034841348423484334844348453484634847348483484934850348513485234853348543485534856348573485834859348603486134862348633486434865348663486734868348693487034871348723487334874348753487634877348783487934880348813488234883348843488534886348873488834889348903489134892348933489434895348963489734898348993490034901349023490334904349053490634907349083490934910349113491234913349143491534916349173491834919349203492134922349233492434925349263492734928349293493034931349323493334934349353493634937349383493934940349413494234943349443494534946349473494834949349503495134952349533495434955349563495734958349593496034961349623496334964349653496634967349683496934970349713497234973349743497534976349773497834979349803498134982349833498434985349863498734988349893499034991349923499334994349953499634997349983499935000350013500235003350043500535006350073500835009350103501135012350133501435015350163501735018350193502035021350223502335024350253502635027350283502935030350313503235033350343503535036350373503835039350403504135042350433504435045350463504735048350493505035051350523505335054350553505635057350583505935060350613506235063350643506535066350673506835069350703507135072350733507435075350763507735078 |
- /*
- * 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
- */
- /*!
- * \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 SIPCHANINFO
- * \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>
- <depend>chan_local</depend>
- <support_level>core</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/features.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/event.h"
- #include "asterisk/cel.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 "sip/include/srtp.h"
- #include "sip/include/sdp_crypto.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 "asterisk/sip_api.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="SIPCHANINFO" language="en_US">
- <synopsis>
- Gets the specified SIP parameter from the current channel.
- </synopsis>
- <syntax>
- <parameter name="item" required="true">
- <enumlist>
- <enum name="peerip">
- <para>The IP address of the peer.</para>
- </enum>
- <enum name="recvip">
- <para>The source IP address of the peer.</para>
- </enum>
- <enum name="from">
- <para>The SIP URI from the <literal>From:</literal> header.</para>
- </enum>
- <enum name="uri">
- <para>The SIP URI from the <literal>Contact:</literal> header.</para>
- </enum>
- <enum name="useragent">
- <para>The Useragent header used by the peer.</para>
- </enum>
- <enum name="peername">
- <para>The name of the peer.</para>
- </enum>
- <enum name="t38passthrough">
- <para><literal>1</literal> if T38 is offered or enabled in this channel,
- otherwise <literal>0</literal>.</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>
- </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>
- ***/
- 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 struct ast_codec_pref default_prefs; /*!< Default codec prefs */
- 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 sip_transport) that are acceptable */
- static unsigned int default_primary_transport; /*!< Default primary Transport (enum sip_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 int regobjs = 0; /*!< Registry 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 ast_event_sub *network_change_event_subscription; /*!< subscription id for network change events */
- static struct ast_event_sub *acl_change_event_subscription; /*!< subscription id for named ACL system change events */
- static int network_change_event_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;
- #else
- static const int HASH_PEER_SIZE = 563; /*!< Size of peer hash table, prime number preferred! */
- static const int HASH_DIALOG_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 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;
- }
- 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);
- 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;
- }
- /*!
- * 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
- };
- static const int ESC_MAX_BUCKETS = 37;
- 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_ref(event_state_compositors[i].compositor, -1);
- }
- }
- struct state_notify_data {
- int state;
- struct ao2_container *device_state_info;
- int presence_state;
- const char *presence_subtype;
- const char *presence_message;
- };
- /*!
- * \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 ast_register_list {
- ASTOBJ_CONTAINER_COMPONENTS(struct sip_registry);
- int recheck;
- } regl;
- /*! \brief The MWI subscription list */
- static struct ast_subscription_mwi_list {
- ASTOBJ_CONTAINER_COMPONENTS(struct sip_subscription_mwi);
- } submwil;
- 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);
- /* --- 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 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_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 sip_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 void *registry_unref(struct sip_registry *reg, char *tag);
- 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 free_old_route(struct sip_route *route);
- static void list_route(struct sip_route *route);
- static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards, int resp);
- static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sockaddr *addr,
- struct sip_request *req, const char *uri);
- static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag);
- static void check_pendings(struct sip_pvt *p);
- static void *sip_park_thread(void *stuff);
- static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, uint32_t seqno, const char *park_exten, const char *park_context);
- 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);
- static void add_noncodec_to_sdp(const struct sip_pvt *p, int format,
- struct ast_str **m_buf, struct ast_str **a_buf,
- int debug);
- static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp, int add_audio, int add_t38);
- static void do_setnat(struct sip_pvt *p);
- static void stop_media_flows(struct sip_pvt *p);
- /*--- Authentication stuff */
- static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, int sipmethod, char *digest, int digest_len);
- static int build_reply_digest(struct sip_pvt *p, int method, char *digest, int digest_len);
- static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username,
- const char *secret, const char *md5secret, int sipmethod,
- const char *uri, enum xmittype reliable);
- static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_request *req,
- int sipmethod, const char *uri, enum xmittype reliable,
- struct ast_sockaddr *addr, struct sip_peer **authpeer);
- static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod, const char *uri, enum xmittype reliable, struct ast_sockaddr *addr);
- /*--- Domain handling */
- static int check_sip_domain(const char *domain, char *context, size_t len); /* Check if domain is one of our local domains */
- static int add_sip_domain(const char *domain, const enum domain_mode mode, const char *context);
- static void clear_sip_domains(void);
- /*--- SIP realm authentication */
- static void add_realm_authentication(struct sip_auth_container **credentials, const char *configuration, int lineno);
- static struct sip_auth *find_realm_authentication(struct sip_auth_container *credentials, const char *realm);
- /*--- Misc functions */
- static int check_rtp_timeout(struct sip_pvt *dialog, time_t t);
- static int reload_config(enum channelreloadreason reason);
- static void add_diversion(struct sip_request *req, struct sip_pvt *pvt);
- static int expire_register(const void *data);
- static void *do_monitor(void *data);
- static int restart_monitor(void);
- static void peer_mailboxes_to_str(struct ast_str **mailbox_str, struct sip_peer *peer);
- static struct ast_variable *copy_vars(struct ast_variable *src);
- static int dialog_find_multiple(void *obj, void *arg, int flags);
- static struct ast_channel *sip_pvt_lock_full(struct sip_pvt *pvt);
- /* static int sip_addrcmp(char *name, struct sockaddr_in *sin); Support for peer matching */
- static int sip_refer_alloc(struct sip_pvt *p);
- static void sip_refer_destroy(struct sip_pvt *p);
- static int sip_notify_alloc(struct sip_pvt *p);
- static void ast_quiet_chan(struct ast_channel *chan);
- static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target);
- 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(const struct ast_event *, void *);
- static void network_change_event_cb(const struct ast_event *, void *);
- static void acl_change_event_cb(const struct ast_event *event, void *userdata);
- 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 void print_codec_to_cli(int fd, struct ast_codec_pref *pref);
- 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);
- 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(struct sip_registry *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);
- 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);
- 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, char *uri);
- static void add_date(struct sip_request *req);
- static void add_expires(struct sip_request *req, int expires);
- static void build_contact(struct sip_pvt *p, struct sip_request *req, int incoming);
- /*------Request handling functions */
- static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, int *recount, int *nounlock);
- static int handle_request_update(struct sip_pvt *p, struct sip_request *req);
- static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, int *recount, const char *e, int *nounlock);
- static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, uint32_t seqno, int *nounlock);
- static int handle_request_bye(struct sip_pvt *p, struct sip_request *req);
- static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *sin, const char *e);
- static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req);
- static int handle_request_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
- static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, const char *e);
- static void handle_request_info(struct sip_pvt *p, struct sip_request *req);
- static int handle_request_options(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e);
- static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, int *nounlock);
- 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 sip_dual *current, struct sip_request *req, 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 setup_srtp(struct sip_srtp **srtp);
- static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct sip_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 struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan);
- static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl);
- static void change_t38_state(struct sip_pvt *p, int state);
- /*------ Session-Timers functions --------- */
- static void proc_422_rsp(struct sip_pvt *p, struct sip_request *rsp);
- static int proc_session_timer(const void *vp);
- static void stop_session_timer(struct sip_pvt *p);
- static void start_session_timer(struct sip_pvt *p);
- static void restart_session_timer(struct sip_pvt *p);
- static const char *strefresherparam2str(enum st_refresher_param r);
- static int parse_session_expires(const char *p_hdrval, int *const p_interval, enum st_refresher_param *const p_ref);
- static int parse_minse(const char *p_hdrval, int *const p_interval);
- static int st_get_se(struct sip_pvt *, int max);
- static enum st_refresher st_get_refresher(struct sip_pvt *);
- static enum st_mode st_get_mode(struct sip_pvt *, int no_cached);
- static struct sip_st_dlg* sip_st_alloc(struct sip_pvt *const p);
- /*------- RTP Glue functions -------- */
- static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, const struct ast_format_cap *cap, int nat_active);
- /*!--- SIP MWI Subscription support */
- static int sip_subscribe_mwi(const char *value, int lineno);
- static void sip_subscribe_mwi_destroy(struct sip_subscription_mwi *mwi);
- 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,
- .bridge = ast_rtp_instance_bridge, /* XXX chan unlocked ? */
- .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,
- };
- 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);
- }
- struct ao2_container *sip_monitor_instances;
- 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(enum AST_REDIRECTING_REASON code)
- {
- 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 & SIP_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 ? SIP_TRANSPORT_TLS: SIP_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) ? SIP_TRANSPORT_WSS : SIP_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 ? SIP_TRANSPORT_TLS : SIP_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, SIP_TRANSPORT_TLS);
- req.socket.port = htons(ourport_tls);
- } else {
- set_socket_transport(&req.socket, SIP_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");
- }
- /* \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);
- dialog->owner = 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");
- }
- dialog->registry = registry_unref(dialog->registry, "delete dialog->registry");
- }
- if (dialog->stateid != -1) {
- ast_extension_state_del(dialog->stateid, cb_extensionstate);
- dialog->stateid = -1;
- }
- /* Remove link from peer to subscription of MWI */
- if (dialog->relatedpeer && dialog->relatedpeer->mwipvt == dialog) {
- dialog->relatedpeer->mwipvt = dialog_unref(dialog->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt");
- }
- if (dialog->relatedpeer && dialog->relatedpeer->call == dialog) {
- dialog->relatedpeer->call = dialog_unref(dialog->relatedpeer->call, "unset the relatedpeer->call field in tandem with relatedpeer field itself");
- }
- /* remove all current packets in this dialog */
- while((cp = dialog->packets)) {
- dialog->packets = dialog->packets->next;
- AST_SCHED_DEL(sched, cp->retransid);
- dialog_unref(cp->owner, "remove all current packets in this dialog, and the pointer to the dialog too as part of __sip_destroy");
- if (cp->data) {
- ast_free(cp->data);
- }
- ast_free(cp);
- }
- AST_SCHED_DEL_UNREF(sched, dialog->waitid, dialog_unref(dialog, "when you delete the waitid sched, you should dec the refcount for the stored dialog ptr"));
- AST_SCHED_DEL_UNREF(sched, dialog->initid, dialog_unref(dialog, "when you delete the initid sched, you should dec the refcount for the stored dialog ptr"));
- if (dialog->reinviteid > -1) {
- AST_SCHED_DEL_UNREF(sched, dialog->reinviteid, dialog_unref(dialog, "clear ref for reinvite_timeout"));
- }
- if (dialog->autokillid > -1) {
- AST_SCHED_DEL_UNREF(sched, dialog->autokillid, dialog_unref(dialog, "when you delete the autokillid sched, you should dec the refcount for the stored dialog ptr"));
- }
- if (dialog->request_queue_sched_id > -1) {
- AST_SCHED_DEL_UNREF(sched, dialog->request_queue_sched_id, dialog_unref(dialog, "when you delete the request_queue_sched_id sched, you should dec the refcount for the stored dialog ptr"));
- }
- AST_SCHED_DEL_UNREF(sched, dialog->provisional_keepalive_sched_id, dialog_unref(dialog, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
- if (dialog->t38id > -1) {
- AST_SCHED_DEL_UNREF(sched, dialog->t38id, dialog_unref(dialog, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
- }
- if (dialog->stimer) {
- stop_session_timer(dialog);
- }
- dialog_unref(dialog, "Let's unbump the count in the unlink so the poor pvt can disappear if it is time");
- }
- void *registry_unref(struct sip_registry *reg, char *tag)
- {
- ast_debug(3, "SIP Registry %s: refcount now %u\n", reg->hostname, reg->refcount - 1);
- ASTOBJ_UNREF(reg, sip_registry_destroy);
- return NULL;
- }
- /*! \brief Add object reference to SIP registry */
- static struct sip_registry *registry_addref(struct sip_registry *reg, char *tag)
- {
- ast_debug(3, "SIP Registry %s: refcount now %u\n", reg->hostname, reg->refcount + 1);
- return ASTOBJ_REF(reg); /* Add pointer to registry in packet */
- }
- /*! \brief Interface structure with callbacks used to connect to UDPTL module*/
- static struct ast_udptl_protocol sip_udptl = {
- .type = "SIP",
- .get_udptl_info = sip_get_udptl_peer,
- .set_udptl_peer = sip_set_udptl_peer,
- };
- 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(SIP_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 |= SIP_TRANSPORT_UDP;
- }
- if (!strcasecmp(transport, "tcp")) {
- res |= SIP_TRANSPORT_TCP;
- }
- if (!strcasecmp(transport, "tls")) {
- res |= SIP_TRANSPORT_TLS;
- }
- if (!strcasecmp(transport, "ws")) {
- res |= SIP_TRANSPORT_WS;
- }
- if (!strcasecmp(transport, "wss")) {
- res |= SIP_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 & SIP_TRANSPORT_UDP) {
- strncat(buf, "UDP,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
- }
- if (transports & SIP_TRANSPORT_TCP) {
- strncat(buf, "TCP,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
- }
- if (transports & SIP_TRANSPORT_TLS) {
- strncat(buf, "TLS,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
- }
- if (transports & SIP_TRANSPORT_WS) {
- strncat(buf, "WS,", SIP_TRANSPORT_STR_BUFSIZE - strlen(buf));
- }
- if (transports & SIP_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 sip_transport t)
- {
- switch (t) {
- case SIP_TRANSPORT_UDP:
- return "UDP";
- case SIP_TRANSPORT_TCP:
- return "TCP";
- case SIP_TRANSPORT_TLS:
- return "TLS";
- case SIP_TRANSPORT_WS:
- case SIP_TRANSPORT_WSS:
- return "WS";
- }
- return "UNKNOWN";
- }
- /*! \brief Return protocol string for srv dns query */
- static inline const char *get_srv_protocol(enum sip_transport t)
- {
- switch (t) {
- case SIP_TRANSPORT_UDP:
- return "udp";
- case SIP_TRANSPORT_WS:
- return "ws";
- case SIP_TRANSPORT_TLS:
- case SIP_TRANSPORT_TCP:
- return "tcp";
- case SIP_TRANSPORT_WSS:
- return "wss";
- }
- return "udp";
- }
- /*! \brief Return service string for srv dns query */
- static inline const char *get_srv_service(enum sip_transport t)
- {
- switch (t) {
- case SIP_TRANSPORT_TCP:
- case SIP_TRANSPORT_UDP:
- case SIP_TRANSPORT_WS:
- return "sip";
- case SIP_TRANSPORT_TLS:
- case SIP_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 == SIP_TRANSPORT_UDP) {
- res = ast_sendto(p->socket.fd, ast_str_buffer(data), ast_str_strlen(data), 0, dst);
- } else if (p->socket.tcptls_session) {
- res = sip_tcptls_write(p->socket.tcptls_session, ast_str_buffer(data), ast_str_strlen(data));
- } else if (p->socket.ws_session) {
- if (!(res = ast_websocket_write(p->socket.ws_session, AST_WEBSOCKET_OPCODE_TEXT, ast_str_buffer(data), ast_str_strlen(data)))) {
- /* The WebSocket API just returns 0 on success and -1 on failure, while this code expects the payload length to be returned */
- res = ast_str_strlen(data);
- }
- } else {
- ast_debug(2, "Socket type is TCP but no tcptls_session is present to write to\n");
- return XMIT_ERROR;
- }
- if (res == -1) {
- switch (errno) {
- case EBADF: /* Bad file descriptor - seems like this is generated when the host exist, but doesn't accept the UDP packet */
- case EHOSTUNREACH: /* Host can't be reached */
- case ENETDOWN: /* Interface down */
- case ENETUNREACH: /* Network failure */
- case ECONNREFUSED: /* ICMP port unreachable */
- res = XMIT_ERROR; /* Don't bother with trying to transmit again */
- }
- }
- if (res != ast_str_strlen(data)) {
- ast_log(LOG_WARNING, "sip_xmit of %p (len %zu) to %s returned %d: %s\n", data, ast_str_strlen(data), ast_sockaddr_stringify(dst), res, strerror(errno));
- }
- return res;
- }
- /*! \brief Build a Via header for a request */
- static void build_via(struct sip_pvt *p)
- {
- /* Work around buggy UNIDEN UIP200 firmware */
- const char *rport = (ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT) || ast_test_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT)) ? ";rport" : "";
- /* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */
- snprintf(p->via, sizeof(p->via), "SIP/2.0/%s %s;branch=z9hG4bK%08x%s",
- get_transport_pvt(p),
- ast_sockaddr_stringify_remote(&p->ourip),
- (unsigned)p->branch, rport);
- }
- /*! \brief NAT fix - decide which IP address to use for Asterisk server?
- *
- * Using the localaddr structure built up with localnet statements in sip.conf
- * apply it to their address to see if we need to substitute our
- * externaddr or can get away with our internal bindaddr
- * 'us' is always overwritten.
- */
- static void ast_sip_ouraddrfor(const struct ast_sockaddr *them, struct ast_sockaddr *us, struct sip_pvt *p)
- {
- struct ast_sockaddr theirs;
- /* Set want_remap to non-zero if we want to remap 'us' to an externally
- * reachable IP address and port. This is done if:
- * 1. we have a localaddr list (containing 'internal' addresses marked
- * as 'deny', so ast_apply_ha() will return AST_SENSE_DENY on them,
- * and AST_SENSE_ALLOW on 'external' ones);
- * 2. externaddr is set, so we know what to use as the
- * externally visible address;
- * 3. the remote address, 'them', is external;
- * 4. the address returned by ast_ouraddrfor() is 'internal' (AST_SENSE_DENY
- * when passed to ast_apply_ha() so it does need to be remapped.
- * This fourth condition is checked later.
- */
- int want_remap = 0;
- ast_sockaddr_copy(us, &internip); /* starting guess for the internal address */
- /* now ask the system what would it use to talk to 'them' */
- ast_ouraddrfor(them, us);
- ast_sockaddr_copy(&theirs, them);
- if (ast_sockaddr_is_ipv6(&theirs) && !ast_sockaddr_is_ipv4_mapped(&theirs)) {
- if (localaddr && !ast_sockaddr_isnull(&externaddr) && !ast_sockaddr_is_any(&bindaddr)) {
- ast_log(LOG_WARNING, "Address remapping activated in sip.conf "
- "but we're using IPv6, which doesn't need it. Please "
- "remove \"localnet\" and/or \"externaddr\" settings.\n");
- }
- } else {
- want_remap = localaddr &&
- !ast_sockaddr_isnull(&externaddr) &&
- ast_apply_ha(localaddr, &theirs) == AST_SENSE_ALLOW ;
- }
- if (want_remap &&
- (!sip_cfg.matchexternaddrlocally || !ast_apply_ha(localaddr, us)) ) {
- /* if we used externhost, see if it is time to refresh the info */
- if (externexpire && time(NULL) >= externexpire) {
- if (ast_sockaddr_resolve_first(&externaddr, externhost, 0)) {
- ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost);
- }
- externexpire = time(NULL) + externrefresh;
- }
- if (!ast_sockaddr_isnull(&externaddr)) {
- ast_sockaddr_copy(us, &externaddr);
- switch (p->socket.type) {
- case SIP_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 SIP_TRANSPORT_TLS:
- ast_sockaddr_set_port(us, externtlsport);
- break;
- case SIP_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 SIP_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 SIP_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 SIP_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 SIP_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 & SIP_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_MAKE_COMPATIBLE:
- if (p->rtp) {
- res = ast_rtp_instance_make_compatible(chan, p->rtp, (struct ast_channel *) 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)
- {
- 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
- */
- 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)
- ast_event_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;
- }
- 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);
- peer->caps = ast_format_cap_destroy(peer->caps);
- ast_rtp_dtls_cfg_free(&peer->dtls_cfg);
- }
- /*! \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)) {
- realtime_update_peer(p->name, &p->addr, p->username, p->fullcontact, p->useragent, expire, p->deprecated_username, p->lastms);
- }
- }
- 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(SIP_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, sin, 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 sip_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 = sip_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_FORMAT_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;
- }
- /*! \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);
- ast_format_cap_copy(dialog->caps, peer->caps);
- dialog->prefs = peer->prefs;
- 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;
- 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 */
- ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(dialog->rtp), dialog->rtp, &dialog->prefs);
- dialog->autoframing = peer->autoframing;
- }
- /* 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 sip_transport type)
- {
- return type == SIP_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 : SIP_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, SIP_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 && !strcasecmp(ast_var_name(current), "VXML_URL")) {
- p->options->vxml_url = ast_var_value(current);
- } else if (!p->options->uri_options && !strcasecmp(ast_var_name(current), "SIP_URI_OPTIONS")) {
- p->options->uri_options = ast_var_value(current);
- } else if (!p->options->addsipheaders && !strncasecmp(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 (!strcasecmp(ast_var_name(current), "SIPFROMDOMAIN")) {
- ast_string_field_set(p, fromdomain, ast_var_value(current));
- } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER")) {
- /* This is a transferred call */
- p->options->transfer = 1;
- } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REFERER")) {
- /* This is the referrer */
- referer = ast_var_value(current);
- } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) {
- /* We're replacing a call. */
- p->options->replaces = ast_var_value(current);
- } else if (!strcasecmp(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 != SIP_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 && setup_srtp(&p->srtp) < 0) {
- ast_log(LOG_WARNING, "SRTP audio setup failed\n");
- return -1;
- }
- if (p->vrtp && !p->vsrtp && setup_srtp(&p->vsrtp) < 0) {
- ast_log(LOG_WARNING, "SRTP video setup failed\n");
- return -1;
- }
- if (p->trtp && !p->tsrtp && setup_srtp(&p->tsrtp) < 0) {
- 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_FORMAT_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(struct sip_registry *reg)
- {
- /* 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 */
- reg->call->registry = registry_unref(reg->call->registry, "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);
- ast_atomic_fetchadd_int(®objs, -1);
- ast_free(reg);
- }
- /*! \brief Destroy MWI subscription object */
- static void sip_subscribe_mwi_destroy(struct sip_subscription_mwi *mwi)
- {
- 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);
- ast_free(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");
- p->registry = registry_unref(p->registry, "delete p->registry");
- }
-
- if (p->mwi) {
- p->mwi->call = NULL;
- p->mwi = NULL;
- }
- if (dumphistory)
- sip_dump_history(p);
- if (p->options) {
- if (p->options->outboundproxy) {
- ao2_ref(p->options->outboundproxy, -1);
- }
- ast_free(p->options);
- p->options = NULL;
- }
- if (p->outboundproxy) {
- ref_proxy(p, NULL);
- }
- if (p->notify) {
- ast_variables_destroy(p->notify->headers);
- ast_free(p->notify->content);
- ast_free(p->notify);
- p->notify = NULL;
- }
- if (p->rtp) {
- ast_rtp_instance_destroy(p->rtp);
- p->rtp = NULL;
- }
- if (p->vrtp) {
- ast_rtp_instance_destroy(p->vrtp);
- p->vrtp = NULL;
- }
- if (p->trtp) {
- ast_rtp_instance_destroy(p->trtp);
- p->trtp = NULL;
- }
- if (p->udptl) {
- ast_udptl_destroy(p->udptl);
- p->udptl = NULL;
- }
- sip_refer_destroy(p);
- if (p->route) {
- free_old_route(p->route);
- p->route = NULL;
- }
- 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) {
- sip_srtp_destroy(p->srtp);
- p->srtp = NULL;
- }
- if (p->vsrtp) {
- sip_srtp_destroy(p->vsrtp);
- p->vsrtp = NULL;
- }
- if (p->tsrtp) {
- sip_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);
- p->caps = ast_format_cap_destroy(p->caps);
- p->jointcaps = ast_format_cap_destroy(p->jointcaps);
- p->peercaps = ast_format_cap_destroy(p->peercaps);
- p->redircaps = ast_format_cap_destroy(p->redircaps);
- p->prefcaps = ast_format_cap_destroy(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;
- p->owner = 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);
- p->owner = 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) {
- struct ast_channel *bridge = ast_bridged_channel(oldowner);
- char quality_buf[AST_MAX_USER_FIELD], *quality;
- /* We need to get the lock on bridge because ast_rtp_instance_set_stats_vars will attempt
- * to lock the bridge. This may get hairy...
- */
- while (bridge && ast_channel_trylock(bridge)) {
- sip_pvt_unlock(p);
- do {
- CHANNEL_DEADLOCK_AVOIDANCE(oldowner);
- } while (sip_pvt_trylock(p));
- bridge = ast_bridged_channel(oldowner);
- }
- if (p->rtp) {
- ast_rtp_instance_set_stats_vars(oldowner, p->rtp);
- }
- if (bridge) {
- struct sip_pvt *q = ast_channel_tech_pvt(bridge);
- if (IS_SIP_TECH(ast_channel_tech(bridge)) && q && q->rtp) {
- ast_rtp_instance_set_stats_vars(bridge, q->rtp);
- }
- ast_channel_unlock(bridge);
- }
- /*
- * The channel variables are set below just to get the AMI
- * VarSet event because the channel is being hungup.
- */
- 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);
- }
- /* Send a hangup */
- if (ast_channel_state(oldowner) == AST_STATE_UP || p->invitereplaces) {
- 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 codec suggested by the SIP_CODEC channel variable */
- static void try_suggested_sip_codec(struct sip_pvt *p)
- {
- struct ast_format fmt;
- const char *codec;
- ast_format_clear(&fmt);
- if (p->outgoing_call) {
- codec = pbx_builtin_getvar_helper(p->owner, "SIP_CODEC_OUTBOUND");
- } else if (!(codec = pbx_builtin_getvar_helper(p->owner, "SIP_CODEC_INBOUND"))) {
- codec = pbx_builtin_getvar_helper(p->owner, "SIP_CODEC");
- }
- if (!codec)
- return;
- ast_getformatbyname(codec, &fmt);
- if (fmt.id) {
- ast_log(LOG_NOTICE, "Changing codec to '%s' for this call because of ${SIP_CODEC} variable\n", codec);
- if (ast_format_cap_iscompatible(p->jointcaps, &fmt)) {
- ast_format_cap_set(p->jointcaps, &fmt);
- ast_format_cap_set(p->caps, &fmt);
- } else
- ast_log(LOG_NOTICE, "Ignoring ${SIP_CODEC} variable because it is not shared by both ends.\n");
- } else
- ast_log(LOG_NOTICE, "Ignoring ${SIP_CODEC} variable because of unrecognized/not configured codec (check allow/disallow in sip.conf): %s\n", codec);
- 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(ast_channel_nativeformats(ast), &frame->subclass.format))) {
- char s1[512];
- ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s read/write = %s/%s\n",
- ast_getformatname(&frame->subclass.format),
- ast_getformatname_multiple(s1, sizeof(s1), ast_channel_nativeformats(ast)),
- ast_getformatname(ast_channel_readformat(ast)),
- ast_getformatname(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);
- }
- }
- 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);
- }
- 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);
- }
- 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 {
- p->owner = 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;
- transmit_provisional_response(p, "183 Session Progress", &p->initreq, TRUE);
- ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
- 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) {
- transmit_info_with_vidupdate(p);
- /* ast_rtcp_send_h261fur(p->vrtp); */
- } 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 -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 char *linkedid, struct ast_callid *callid)
- {
- struct ast_channel *tmp;
- struct ast_variable *v = NULL;
- struct ast_format fmt;
- struct ast_format_cap *what = NULL; /* SHALLOW COPY DO NOT DESTROY! */
- int needvideo = 0;
- int needtext = 0;
- char buf[SIPBUFSIZE];
- char *exten;
- {
- const char *my_name; /* pick a good name */
-
- if (title) {
- my_name = title;
- } else {
- my_name = ast_strdupa(i->fromdomain);
- }
- sip_pvt_unlock(i);
- /* Don't hold a sip pvt lock while we allocate a channel */
- tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, linkedid, 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");
- sip_pvt_lock(i);
- return NULL;
- }
- /* If we sent in a callid, bind it to the channel. */
- if (callid) {
- ast_channel_callid_set(tmp, callid);
- }
- ast_channel_lock(tmp);
- 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_is_empty(i->jointcaps))) { /* The joint capabilities of us and peer */
- what = i->jointcaps;
- } else if (!(ast_format_cap_is_empty(i->caps))) { /* Our configured capability for this peer */
- what = i->caps;
- } else {
- what = sip_cfg.caps;
- }
- /* Set the native formats */
- ast_format_cap_copy(ast_channel_nativeformats(tmp), what);
- /* choose and use only the best audio format for our native formats */
- ast_codec_choose(&i->prefs, ast_channel_nativeformats(tmp), 1, &fmt); /* get the best audio format */
- ast_format_cap_remove_bytype(ast_channel_nativeformats(tmp), AST_FORMAT_TYPE_AUDIO); /* remove only the other audio formats */
- ast_format_cap_add(ast_channel_nativeformats(tmp), &fmt); /* add our best choice back */
- ast_debug(3, "*** Our native formats are %s \n", ast_getformatname_multiple(buf, SIPBUFSIZE, ast_channel_nativeformats(tmp)));
- ast_debug(3, "*** Joint capabilities are %s \n", ast_getformatname_multiple(buf, SIPBUFSIZE, i->jointcaps));
- ast_debug(3, "*** Our capabilities are %s \n", ast_getformatname_multiple(buf, SIPBUFSIZE, i->caps));
- ast_debug(3, "*** AST_CODEC_CHOOSE formats are %s \n", ast_getformatname(&fmt));
- if (!ast_format_cap_is_empty(i->prefcaps)) {
- ast_debug(3, "*** Our preferred formats from the incoming channel are %s \n", ast_getformatname_multiple(buf, SIPBUFSIZE, i->prefcaps));
- }
- /* 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_is_empty(i->prefcaps))
- needvideo = ast_format_cap_has_type(i->prefcaps, AST_FORMAT_TYPE_VIDEO); /* Outbound call */
- else
- needvideo = ast_format_cap_has_type(i->jointcaps, AST_FORMAT_TYPE_VIDEO); /* Inbound call */
- }
- if (i->trtp) {
- if (!ast_format_cap_is_empty(i->prefcaps))
- needtext = ast_format_cap_has_type(i->prefcaps, AST_FORMAT_TYPE_TEXT); /* Outbound call */
- else
- needtext = ast_format_cap_has_type(i->jointcaps, AST_FORMAT_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_format_copy(ast_channel_writeformat(tmp), &fmt);
- ast_format_copy(ast_channel_rawwriteformat(tmp), &fmt);
- ast_format_copy(ast_channel_readformat(tmp), &fmt);
- ast_format_copy(ast_channel_rawreadformat(tmp), &fmt);
- 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);
- }
- i->owner = 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->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);
- }
- /* Inform manager user about new channel and their SIP call ID */
- if (sip_cfg.callevents) {
- manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
- "Channel: %s\r\nUniqueid: %s\r\nChanneltype: %s\r\nSIPcallid: %s\r\nSIPfullcontact: %s\r\n",
- ast_channel_name(tmp), ast_channel_uniqueid(tmp), "SIP", i->callid, i->fullcontact);
- }
- return tmp;
- }
- /*! \brief Lookup 'name' in the SDP starting
- * at the 'start' line. Returns the matching line, and 'start'
- * is updated with the next line number.
- */
- static const char *get_sdp_iterate(int *start, struct sip_request *req, const char *name)
- {
- int len = strlen(name);
- const char *line;
- while (*start < (req->sdp_start + req->sdp_count)) {
- line = REQ_OFFSET_TO_STR(req, line[(*start)++]);
- if (!strncasecmp(line, name, len) && line[len] == '=') {
- return ast_skip_blanks(line + len + 1);
- }
- }
- /* if the line was not found, ensure that *start points past the SDP */
- (*start)++;
- return "";
- }
- /*! \brief Fetches the next valid SDP line between the 'start' line
- * (inclusive) and the 'stop' line (exclusive). Returns the type
- * ('a', 'c', ...) and matching line in reference 'start' is updated
- * with the next line number.
- */
- static char get_sdp_line(int *start, int stop, struct sip_request *req, const char **value)
- {
- char type = '\0';
- const char *line = NULL;
- if (stop > (req->sdp_start + req->sdp_count)) {
- stop = req->sdp_start + req->sdp_count;
- }
- while (*start < stop) {
- line = REQ_OFFSET_TO_STR(req, line[(*start)++]);
- if (line[1] == '=') {
- type = line[0];
- *value = ast_skip_blanks(line + 2);
- break;
- }
- }
- return type;
- }
- /*! \brief Get a specific line from the message content */
- static char *get_content_line(struct sip_request *req, char *name, char delimiter)
- {
- int i;
- int len = strlen(name);
- const char *line;
- for (i = 0; i < req->lines; i++) {
- line = REQ_OFFSET_TO_STR(req, line[i]);
- if (!strncasecmp(line, name, len) && line[len] == delimiter) {
- return ast_skip_blanks(line + len + 1);
- }
- }
- return "";
- }
- /*! \brief Structure for conversion between compressed SIP and "normal" SIP headers */
- struct cfalias {
- const char *fullname;
- const char *shortname;
- };
- static const struct cfalias aliases[] = {
- { "Content-Type", "c" },
- { "Content-Encoding", "e" },
- { "From", "f" },
- { "Call-ID", "i" },
- { "Contact", "m" },
- { "Content-Length", "l" },
- { "Subject", "s" },
- { "To", "t" },
- { "Supported", "k" },
- { "Refer-To", "r" },
- { "Referred-By", "b" },
- { "Allow-Events", "u" },
- { "Event", "o" },
- { "Via", "v" },
- { "Accept-Contact", "a" },
- { "Reject-Contact", "j" },
- { "Request-Disposition", "d" },
- { "Session-Expires", "x" },
- { "Identity", "y" },
- { "Identity-Info", "n" },
- };
- /*! \brief Find compressed SIP alias */
- static const char *find_alias(const char *name, const char *_default)
- {
- int x;
- for (x = 0; x < ARRAY_LEN(aliases); x++) {
- if (!strcasecmp(aliases[x].fullname, name))
- return aliases[x].shortname;
- }
- return _default;
- }
- /*! \brief Find full SIP alias */
- static const char *find_full_alias(const char *name, const char *_default)
- {
- int x;
- if (strlen(name) == 1) {
- /* We have a short header name to convert. */
- for (x = 0; x < ARRAY_LEN(aliases); ++x) {
- if (!strcasecmp(aliases[x].shortname, name))
- return aliases[x].fullname;
- }
- }
- return _default;
- }
- static const char *__get_header(const struct sip_request *req, const char *name, int *start)
- {
- /*
- * Technically you can place arbitrary whitespace both before and after the ':' in
- * a header, although RFC3261 clearly says you shouldn't before, and place just
- * one afterwards. If you shouldn't do it, what absolute idiot decided it was
- * a good idea to say you can do it, and if you can do it, why in the hell would.
- * you say you shouldn't.
- * Anyways, pedanticsipchecking controls whether we allow spaces before ':',
- * and we always allow spaces after that for compatibility.
- */
- const char *sname = find_alias(name, NULL);
- int x, len = strlen(name), slen = (sname ? 1 : 0);
- for (x = *start; x < req->headers; x++) {
- const char *header = REQ_OFFSET_TO_STR(req, header[x]);
- int smatch = 0, match = !strncasecmp(header, name, len);
- if (slen) {
- smatch = !strncasecmp(header, sname, slen);
- }
- if (match || smatch) {
- /* skip name */
- const char *r = header + (match ? len : slen );
- if (sip_cfg.pedanticsipchecking) {
- r = ast_skip_blanks(r);
- }
- if (*r == ':') {
- *start = x+1;
- return ast_skip_blanks(r+1);
- }
- }
- }
- /* Don't return NULL, so sip_get_header is always a valid pointer */
- return "";
- }
- /*! \brief Get header from SIP request
- \return Always return something, so don't check for NULL because it won't happen :-)
- */
- const char *sip_get_header(const struct sip_request *req, const char *name)
- {
- int start = 0;
- return __get_header(req, name, &start);
- }
- AST_THREADSTORAGE(sip_content_buf);
- /*! \brief Get message body content */
- static char *get_content(struct sip_request *req)
- {
- struct ast_str *str;
- int i;
- if (!(str = ast_str_thread_get(&sip_content_buf, 128))) {
- return NULL;
- }
- ast_str_reset(str);
- for (i = 0; i < req->lines; i++) {
- if (ast_str_append(&str, 0, "%s\n", REQ_OFFSET_TO_STR(req, line[i])) < 0) {
- return NULL;
- }
- }
- return ast_str_buffer(str);
- }
- /*! \brief Read RTP from network */
- static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect)
- {
- /* Retrieve audio/etc from channel. Assumes p->lock is already held. */
- struct ast_frame *f;
-
- if (!p->rtp) {
- /* We have no RTP allocated for this channel */
- return &ast_null_frame;
- }
- switch(ast_channel_fdno(ast)) {
- case 0:
- f = ast_rtp_instance_read(p->rtp, 0); /* RTP Audio */
- break;
- case 1:
- f = ast_rtp_instance_read(p->rtp, 1); /* RTCP Control Channel */
- break;
- case 2:
- f = ast_rtp_instance_read(p->vrtp, 0); /* RTP Video */
- break;
- case 3:
- f = ast_rtp_instance_read(p->vrtp, 1); /* RTCP Control Channel for video */
- break;
- case 4:
- f = ast_rtp_instance_read(p->trtp, 0); /* RTP Text */
- if (sipdebug_text) {
- struct ast_str *out = ast_str_create(f->datalen * 4 + 6);
- int i;
- unsigned char* arr = f->data.ptr;
- do {
- if (!out) {
- break;
- }
- for (i = 0; i < f->datalen; i++) {
- ast_str_append(&out, 0, "%c", (arr[i] > ' ' && arr[i] < '}') ? arr[i] : '.');
- }
- ast_str_append(&out, 0, " -> ");
- for (i = 0; i < f->datalen; i++) {
- ast_str_append(&out, 0, "%02hhX ", arr[i]);
- }
- ast_verb(0, "%s\n", ast_str_buffer(out));
- ast_free(out);
- } while (0);
- }
- break;
- case 5:
- f = ast_udptl_read(p->udptl); /* UDPTL for T.38 */
- break;
- default:
- f = &ast_null_frame;
- }
- /* Don't forward RFC2833 if we're not supposed to */
- if (f && (f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END) &&
- (ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_RFC2833)) {
- ast_debug(1, "Ignoring DTMF (%c) RTP frame because dtmfmode is not RFC2833\n", f->subclass.integer);
- ast_frfree(f);
- return &ast_null_frame;
- }
- /* We already hold the channel lock */
- if (!p->owner || (f && f->frametype != AST_FRAME_VOICE)) {
- return f;
- }
- if (f && !ast_format_cap_iscompatible(ast_channel_nativeformats(p->owner), &f->subclass.format)) {
- if (!ast_format_cap_iscompatible(p->jointcaps, &f->subclass.format)) {
- ast_debug(1, "Bogus frame of format '%s' received from '%s'!\n",
- ast_getformatname(&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_getformatname(&f->subclass.format));
- ast_format_cap_remove_bytype(ast_channel_nativeformats(p->owner), AST_FORMAT_TYPE_AUDIO);
- ast_format_cap_add(ast_channel_nativeformats(p->owner), &f->subclass.format);
- 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 == SIP_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_nolock();
- p->jointcaps = ast_format_cap_alloc_nolock();
- p->peercaps = ast_format_cap_alloc_nolock();
- p->redircaps = ast_format_cap_alloc_nolock();
- p->prefcaps = ast_format_cap_alloc_nolock();
- if (!p->caps|| !p->jointcaps || !p->peercaps || !p->redircaps) {
- p->caps = ast_format_cap_destroy(p->caps);
- p->jointcaps = ast_format_cap_destroy(p->jointcaps);
- p->peercaps = ast_format_cap_destroy(p->peercaps);
- p->redircaps = ast_format_cap_destroy(p->redircaps);
- p->prefcaps = ast_format_cap_destroy(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, SIP_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;
- p->prefs = default_prefs; /* Set default codecs for this call */
- 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;
- p->invitereplaces = 0;
- 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_copy(p->caps, sip_cfg.caps);
- p->allowtransfer = sip_cfg.allowtransfer;
- if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
- (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
- p->noncodeccapability |= AST_RTP_DTMF;
- }
- ast_string_field_set(p, context, sip_cfg.default_context);
- ast_string_field_set(p, parkinglot, default_parkinglot);
- ast_string_field_set(p, engine, default_engine);
- AST_LIST_HEAD_INIT_NOLOCK(&p->request_queue);
- AST_LIST_HEAD_INIT_NOLOCK(&p->offered_media);
- /* Add to active dialog list */
- ao2_t_link(dialogs, p, "link pvt into dialogs table");
- ast_debug(1, "Allocating new SIP dialog for %s - %s (%s)\n", callid ? callid : p->callid, sip_methods[intended_method].text, p->rtp ? "With RTP" : "No RTP");
- return p;
- }
- /*!
- * \brief Process the Via header according to RFC 3261 section 18.2.2.
- * \param p a sip_pvt structure that will be modified according to the received
- * header
- * \param req a sip request with a Via header to process
- *
- * This function will update the destination of the response according to the
- * Via header in the request and RFC 3261 section 18.2.2. We do not have a
- * transport layer so we ignore certain values like the 'received' param (we
- * set the destination address to the address the request came from in the
- * respprep() function).
- *
- * \retval -1 error
- * \retval 0 success
- */
- static int process_via(struct sip_pvt *p, const struct sip_request *req)
- {
- struct sip_via *via = parse_via(sip_get_header(req, "Via"));
- if (!via) {
- ast_log(LOG_ERROR, "error processing via header\n");
- return -1;
- }
- if (via->maddr) {
- if (ast_sockaddr_resolve_first_transport(&p->sa, via->maddr, PARSE_PORT_FORBID, p->socket.type)) {
- ast_log(LOG_WARNING, "Can't find address for maddr '%s'\n", via->maddr);
- ast_log(LOG_ERROR, "error processing via header\n");
- free_via(via);
- return -1;
- }
- if (ast_sockaddr_is_ipv4_multicast(&p->sa)) {
- setsockopt(sipsock, IPPROTO_IP, IP_MULTICAST_TTL, &via->ttl, sizeof(via->ttl));
- }
- }
- ast_sockaddr_set_port(&p->sa, via->port ? via->port : STANDARD_SIP_PORT);
- free_via(via);
- return 0;
- }
- /* \brief arguments used for Request/Response to matching */
- struct match_req_args {
- int method;
- const char *callid;
- const char *totag;
- const char *fromtag;
- uint32_t seqno;
- /* Set if this method is a Response */
- int respid;
- /* Set if the method is a Request */
- const char *ruri;
- const char *viabranch;
- const char *viasentby;
- /* Set this if the Authentication header is present in the Request. */
- int authentication_present;
- };
- enum match_req_res {
- SIP_REQ_MATCH,
- SIP_REQ_NOT_MATCH,
- SIP_REQ_LOOP_DETECTED, /* multiple incoming requests with same call-id but different branch parameters have been detected */
- SIP_REQ_FORKED, /* An outgoing request has been forked as result of receiving two differing 200ok responses. */
- };
- /*
- * \brief Match a incoming Request/Response to a dialog
- *
- * \retval enum match_req_res indicating if the dialog matches the arg
- */
- static enum match_req_res match_req_to_dialog(struct sip_pvt *sip_pvt_ptr, struct match_req_args *arg)
- {
- const char *init_ruri = NULL;
- if (sip_pvt_ptr->initreq.headers) {
- init_ruri = REQ_OFFSET_TO_STR(&sip_pvt_ptr->initreq, rlpart2);
- }
- /*
- * Match Tags and call-id to Dialog
- */
- if (!ast_strlen_zero(arg->callid) && strcmp(sip_pvt_ptr->callid, arg->callid)) {
- /* call-id does not match. */
- return SIP_REQ_NOT_MATCH;
- }
- if (arg->method == SIP_RESPONSE) {
- /* Verify fromtag of response matches the tag we gave them. */
- if (strcmp(arg->fromtag, sip_pvt_ptr->tag)) {
- /* fromtag from response does not match our tag */
- return SIP_REQ_NOT_MATCH;
- }
- /* Verify totag if we have one stored for this dialog, but never be strict about this for
- * a response until the dialog is established */
- if (!ast_strlen_zero(sip_pvt_ptr->theirtag) && ast_test_flag(&sip_pvt_ptr->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) {
- if (ast_strlen_zero(arg->totag)) {
- /* missing totag when they already gave us one earlier */
- return SIP_REQ_NOT_MATCH;
- }
- /* compare the totag of response with the tag we have stored for them */
- if (strcmp(arg->totag, sip_pvt_ptr->theirtag)) {
- /* totag did not match what we had stored for them. */
- char invite_branch[32] = { 0, };
- if (sip_pvt_ptr->invite_branch) {
- snprintf(invite_branch, sizeof(invite_branch), "z9hG4bK%08x", (unsigned)sip_pvt_ptr->invite_branch);
- }
- /* Forked Request Detection
- *
- * If this is a 200ok response and the totags do not match, this
- * might be a forked response to an outgoing Request. Detection of
- * a forked response must meet the criteria below.
- *
- * 1. must be a 2xx Response
- * 2. call-d equal to call-id of Request. this is done earlier
- * 3. from-tag equal to from-tag of Request. this is done earlier
- * 4. branch parameter equal to branch of inital Request
- * 5. to-tag _NOT_ equal to previous 2xx response that already established the dialog.
- */
- if ((arg->respid == 200) &&
- !ast_strlen_zero(invite_branch) &&
- !ast_strlen_zero(arg->viabranch) &&
- !strcmp(invite_branch, arg->viabranch)) {
- return SIP_REQ_FORKED;
- }
- /* The totag did not match the one we had stored, and this is not a Forked Request. */
- return SIP_REQ_NOT_MATCH;
- }
- }
- } else {
- /* Verify the fromtag of Request matches the tag they provided earlier.
- * If this is a Request with authentication credentials, forget their old
- * tag as it is not valid after the 401 or 407 response. */
- if (!arg->authentication_present && strcmp(arg->fromtag, sip_pvt_ptr->theirtag)) {
- /* their tag does not match the one was have stored for them */
- return SIP_REQ_NOT_MATCH;
- }
- /* Verify if totag is present in Request, that it matches what we gave them as our tag earlier */
- if (!ast_strlen_zero(arg->totag) && (strcmp(arg->totag, sip_pvt_ptr->tag))) {
- /* totag from Request does not match our tag */
- return SIP_REQ_NOT_MATCH;
- }
- }
- /*
- * Compare incoming request against initial transaction.
- *
- * This is a best effort attempt at distinguishing forked requests from
- * our initial transaction. If all the elements are NOT in place to evaluate
- * this, this block is ignored and the dialog match is made regardless.
- * Once the totag is established after the dialog is confirmed, this is not necessary.
- *
- * CRITERIA required for initial transaction matching.
- *
- * 1. Is a Request
- * 2. Callid and theirtag match (this is done in the dialog matching block)
- * 3. totag is NOT present
- * 4. CSeq matchs our initial transaction's cseq number
- * 5. pvt has init via branch parameter stored
- */
- if ((arg->method != SIP_RESPONSE) && /* must be a Request */
- ast_strlen_zero(arg->totag) && /* must not have a totag */
- (sip_pvt_ptr->init_icseq == arg->seqno) && /* the cseq must be the same as this dialogs initial cseq */
- !ast_strlen_zero(sip_pvt_ptr->initviabranch) && /* The dialog must have started with a RFC3261 compliant branch tag */
- init_ruri) { /* the dialog must have an initial request uri associated with it */
- /* This Request matches all the criteria required for Loop/Merge detection.
- * Now we must go down the path of comparing VIA's and RURIs. */
- if (ast_strlen_zero(arg->viabranch) ||
- strcmp(arg->viabranch, sip_pvt_ptr->initviabranch) ||
- ast_strlen_zero(arg->viasentby) ||
- strcmp(arg->viasentby, sip_pvt_ptr->initviasentby)) {
- /* At this point, this request does not match this Dialog.*/
- /* if methods are different this is just a mismatch */
- if ((sip_pvt_ptr->method != arg->method)) {
- return SIP_REQ_NOT_MATCH;
- }
- /* If RUIs are different, this is a forked request to a separate URI.
- * Returning a mismatch allows this Request to be processed separately. */
- if (sip_uri_cmp(init_ruri, arg->ruri)) {
- /* not a match, request uris are different */
- return SIP_REQ_NOT_MATCH;
- }
- /* Loop/Merge Detected
- *
- * ---Current Matches to Initial Request---
- * request uri
- * Call-id
- * their-tag
- * no totag present
- * method
- * cseq
- *
- * --- Does not Match Initial Request ---
- * Top Via
- *
- * Without the same Via, this can not match our initial transaction for this dialog,
- * but given that this Request matches everything else associated with that initial
- * Request this is most certainly a Forked request in which we have already received
- * part of the fork.
- */
- return SIP_REQ_LOOP_DETECTED;
- }
- } /* end of Request Via check */
- /* Match Authentication Request.
- *
- * A Request with an Authentication header must come back with the
- * same Request URI. Otherwise it is not a match.
- */
- if ((arg->method != SIP_RESPONSE) && /* Must be a Request type to even begin checking this */
- ast_strlen_zero(arg->totag) && /* no totag is present to match */
- arg->authentication_present && /* Authentication header is present in Request */
- sip_uri_cmp(init_ruri, arg->ruri)) { /* Compare the Request URI of both the last Request and this new one */
- /* Authentication was provided, but the Request URI did not match the last one on this dialog. */
- return SIP_REQ_NOT_MATCH;
- }
- return SIP_REQ_MATCH;
- }
- /*! \brief This function creates a dialog to handle a forked request. This dialog
- * exists only to properly terminiate the the forked request immediately.
- */
- static void forked_invite_init(struct sip_request *req, const char *new_theirtag, struct sip_pvt *original, struct ast_sockaddr *addr)
- {
- struct sip_pvt *p;
- const char *callid;
- 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 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;
- }
- }
- /* match on callid only for REGISTERs */
- if (!sip_cfg.pedanticsipchecking || req->method == SIP_REGISTER) {
- struct sip_pvt tmp_dialog = {
- .callid = callid,
- };
- sip_pvt_ptr = ao2_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, *tmp;
- if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
- ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry entry\n");
- return -1;
- }
- ASTOBJ_INIT(reg);
- ast_copy_string(reg->name, value, sizeof(reg->name));
- if (sip_parse_register_line(reg, default_expiry, value, lineno)) {
- registry_unref(reg, "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;
- }
- /* Add the new registry entry to the list, but only if it isn't already there */
- if ((tmp = ASTOBJ_CONTAINER_FIND(®l, reg->name))) {
- registry_unref(tmp, "throw away found registry");
- } else {
- ast_atomic_fetchadd_int(®objs, 1);
- ASTOBJ_CONTAINER_LINK(®l, reg);
- }
- /* release the reference given by ASTOBJ_INIT. The container has another reference */
- registry_unref(reg, "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 sip_transport transport = SIP_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 = ast_calloc_with_stringfields(1, struct sip_subscription_mwi, 256))) {
- return -1;
- }
- ASTOBJ_INIT(mwi);
- 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;
- ASTOBJ_CONTAINER_LINK(&submwil, mwi);
- ASTOBJ_UNREF(mwi, sip_subscribe_mwi_destroy);
- return 0;
- }
- static void mark_method_allowed(unsigned int *allowed_methods, enum sipmethod method)
- {
- (*allowed_methods) |= (1 << method);
- }
- static void mark_method_unallowed(unsigned int *allowed_methods, enum sipmethod method)
- {
- (*allowed_methods) &= ~(1 << method);
- }
- /*! \brief Check if method is allowed for a device or a dialog */
- static int is_method_allowed(unsigned int *allowed_methods, enum sipmethod method)
- {
- return ((*allowed_methods) >> method) & 1;
- }
- static void mark_parsed_methods(unsigned int *methods, char *methods_str)
- {
- char *method;
- for (method = strsep(&methods_str, ","); !ast_strlen_zero(method); method = strsep(&methods_str, ",")) {
- int id = find_sip_method(ast_skip_blanks(method));
- if (id == SIP_UNKNOWN) {
- continue;
- }
- mark_method_allowed(methods, id);
- }
- }
- /*!
- * \brief parse the Allow header to see what methods the endpoint we
- * are communicating with allows.
- *
- * We parse the allow header on incoming Registrations and save the
- * result to the SIP peer that is registering. When the registration
- * expires, we clear what we know about the peer's allowed methods.
- * When the peer re-registers, we once again parse to see if the
- * list of allowed methods has changed.
- *
- * For peers that do not register, we parse the first message we receive
- * during a call to see what is allowed, and save the information
- * for the duration of the call.
- * \param req The SIP request we are parsing
- * \retval The methods allowed
- */
- static unsigned int parse_allowed_methods(struct sip_request *req)
- {
- char *allow = ast_strdupa(sip_get_header(req, "Allow"));
- unsigned int allowed_methods = SIP_UNKNOWN;
- if (ast_strlen_zero(allow)) {
- /* I have witnessed that REGISTER requests from Polycom phones do not
- * place the phone's allowed methods in an Allow header. Instead, they place the
- * allowed methods in a methods= parameter in the Contact header.
- */
- char *contact = ast_strdupa(sip_get_header(req, "Contact"));
- char *methods = strstr(contact, ";methods=");
- if (ast_strlen_zero(methods)) {
- /* RFC 3261 states:
- *
- * "The absence of an Allow header field MUST NOT be
- * interpreted to mean that the UA sending the message supports no
- * methods. Rather, it implies that the UA is not providing any
- * information on what methods it supports."
- *
- * For simplicity, we'll assume that the peer allows all known
- * SIP methods if they have no Allow header. We can then clear out the necessary
- * bits if the peer lets us know that we have sent an unsupported method.
- */
- return UINT_MAX;
- }
- allow = ast_strip_quoted(methods + 9, "\"", "\"");
- }
- mark_parsed_methods(&allowed_methods, allow);
- return allowed_methods;
- }
- /*! A wrapper for parse_allowed_methods geared toward sip_pvts
- *
- * This function, in addition to setting the allowed methods for a sip_pvt
- * also will take into account the setting of the SIP_PAGE2_RPID_UPDATE flag.
- *
- * \param pvt The sip_pvt we are setting the allowed_methods for
- * \param req The request which we are parsing
- * \retval The methods alloweded by the sip_pvt
- */
- static unsigned int set_pvt_allowed_methods(struct sip_pvt *pvt, struct sip_request *req)
- {
- pvt->allowed_methods = parse_allowed_methods(req);
-
- if (ast_test_flag(&pvt->flags[1], SIP_PAGE2_RPID_UPDATE)) {
- mark_method_allowed(&pvt->allowed_methods, SIP_UPDATE);
- }
- pvt->allowed_methods &= ~(pvt->disallowed_methods);
- return pvt->allowed_methods;
- }
- /*! \brief Parse multiline SIP headers into one header
- This is enabled if pedanticsipchecking is enabled */
- static void lws2sws(struct ast_str *data)
- {
- char *msgbuf = ast_str_buffer(data);
- int len = ast_str_strlen(data);
- int h = 0, t = 0;
- int lws = 0;
- for (; h < len;) {
- /* Eliminate all CRs */
- if (msgbuf[h] == '\r') {
- h++;
- continue;
- }
- /* Check for end-of-line */
- if (msgbuf[h] == '\n') {
- /* Check for end-of-message */
- if (h + 1 == len)
- break;
- /* Check for a continuation line */
- if (msgbuf[h + 1] == ' ' || msgbuf[h + 1] == '\t') {
- /* Merge continuation line */
- h++;
- continue;
- }
- /* Propagate LF and start new line */
- msgbuf[t++] = msgbuf[h++];
- lws = 0;
- continue;
- }
- if (msgbuf[h] == ' ' || msgbuf[h] == '\t') {
- if (lws) {
- h++;
- continue;
- }
- msgbuf[t++] = msgbuf[h++];
- lws = 1;
- continue;
- }
- msgbuf[t++] = msgbuf[h++];
- if (lws)
- lws = 0;
- }
- msgbuf[t] = '\0';
- ast_str_update(data);
- }
- /*! \brief Parse a SIP message
- \note this function is used both on incoming and outgoing packets
- */
- static int parse_request(struct sip_request *req)
- {
- char *c = ast_str_buffer(req->data);
- ptrdiff_t *dst = req->header;
- int i = 0;
- unsigned int lim = SIP_MAX_HEADERS - 1;
- unsigned int skipping_headers = 0;
- ptrdiff_t current_header_offset = 0;
- char *previous_header = "";
- req->header[0] = 0;
- req->headers = -1; /* mark that we are working on the header */
- for (; *c; c++) {
- if (*c == '\r') { /* remove \r */
- *c = '\0';
- } else if (*c == '\n') { /* end of this line */
- *c = '\0';
- current_header_offset = (c + 1) - ast_str_buffer(req->data);
- previous_header = ast_str_buffer(req->data) + dst[i];
- if (skipping_headers) {
- /* check to see if this line is blank; if so, turn off
- the skipping flag, so the next line will be processed
- as a body line */
- if (ast_strlen_zero(previous_header)) {
- skipping_headers = 0;
- }
- dst[i] = current_header_offset; /* record start of next line */
- continue;
- }
- if (sipdebug) {
- ast_debug(4, "%7s %2d [%3d]: %s\n",
- req->headers < 0 ? "Header" : "Body",
- i, (int) strlen(previous_header), previous_header);
- }
- if (ast_strlen_zero(previous_header) && req->headers < 0) {
- req->headers = i; /* record number of header lines */
- dst = req->line; /* start working on the body */
- i = 0;
- lim = SIP_MAX_LINES - 1;
- } else { /* move to next line, check for overflows */
- if (i++ == lim) {
- /* if we're processing headers, then skip any remaining
- headers and move on to processing the body, otherwise
- we're done */
- if (req->headers != -1) {
- break;
- } else {
- req->headers = i;
- dst = req->line;
- i = 0;
- lim = SIP_MAX_LINES - 1;
- skipping_headers = 1;
- }
- }
- }
- dst[i] = current_header_offset; /* record start of next line */
- }
- }
- /* Check for last header or body line without CRLF. The RFC for SDP requires CRLF,
- but since some devices send without, we'll be generous in what we accept. However,
- if we've already reached the maximum number of lines for portion of the message
- we were parsing, we can't accept any more, so just ignore it.
- */
- previous_header = ast_str_buffer(req->data) + dst[i];
- if ((i < lim) && !ast_strlen_zero(previous_header)) {
- if (sipdebug) {
- ast_debug(4, "%7s %2d [%3d]: %s\n",
- req->headers < 0 ? "Header" : "Body",
- i, (int) strlen(previous_header), previous_header );
- }
- i++;
- }
- /* update count of header or body lines */
- if (req->headers >= 0) { /* we are in the body */
- req->lines = i;
- } else { /* no body */
- req->headers = i;
- req->lines = 0;
- /* req->data->used will be a NULL byte */
- req->line[0] = ast_str_strlen(req->data);
- }
- if (*c) {
- ast_log(LOG_WARNING, "Too many lines, skipping <%s>\n", c);
- }
- /* Split up the first line parts */
- return determine_firstline_parts(req);
- }
- /*!
- \brief Determine whether a SIP message contains an SDP in its body
- \param req the SIP request to process
- \return 1 if SDP found, 0 if not found
- Also updates req->sdp_start and req->sdp_count to indicate where the SDP
- lives in the message body.
- */
- static int find_sdp(struct sip_request *req)
- {
- const char *content_type;
- const char *content_length;
- const char *search;
- char *boundary;
- unsigned int x;
- int boundaryisquoted = FALSE;
- int found_application_sdp = FALSE;
- int found_end_of_headers = FALSE;
- content_length = sip_get_header(req, "Content-Length");
- if (!ast_strlen_zero(content_length)) {
- if (sscanf(content_length, "%30u", &x) != 1) {
- ast_log(LOG_WARNING, "Invalid Content-Length: %s\n", content_length);
- return 0;
- }
- /* Content-Length of zero means there can't possibly be an
- SDP here, even if the Content-Type says there is */
- if (x == 0)
- return 0;
- }
- content_type = sip_get_header(req, "Content-Type");
- /* if the body contains only SDP, this is easy */
- if (!strncasecmp(content_type, "application/sdp", 15)) {
- req->sdp_start = 0;
- req->sdp_count = req->lines;
- return req->lines ? 1 : 0;
- }
- /* if it's not multipart/mixed, there cannot be an SDP */
- if (strncasecmp(content_type, "multipart/mixed", 15))
- return 0;
- /* if there is no boundary marker, it's invalid */
- if ((search = strcasestr(content_type, ";boundary=")))
- search += 10;
- else if ((search = strcasestr(content_type, "; boundary=")))
- search += 11;
- else
- return 0;
- if (ast_strlen_zero(search))
- return 0;
- /* If the boundary is quoted with ", remove quote */
- if (*search == '\"') {
- search++;
- boundaryisquoted = TRUE;
- }
- /* make a duplicate of the string, with two extra characters
- at the beginning */
- boundary = ast_strdupa(search - 2);
- boundary[0] = boundary[1] = '-';
- /* Remove final quote */
- if (boundaryisquoted)
- boundary[strlen(boundary) - 1] = '\0';
- /* search for the boundary marker, the empty line delimiting headers from
- sdp part and the end boundry if it exists */
- for (x = 0; x < (req->lines); x++) {
- const char *line = REQ_OFFSET_TO_STR(req, line[x]);
- if (!strncasecmp(line, boundary, strlen(boundary))){
- if (found_application_sdp && found_end_of_headers) {
- req->sdp_count = (x - 1) - req->sdp_start;
- return 1;
- }
- found_application_sdp = FALSE;
- }
- if (!strcasecmp(line, "Content-Type: application/sdp"))
- found_application_sdp = TRUE;
-
- if (ast_strlen_zero(line)) {
- if (found_application_sdp && !found_end_of_headers){
- req->sdp_start = x;
- found_end_of_headers = TRUE;
- }
- }
- }
- if (found_application_sdp && found_end_of_headers) {
- req->sdp_count = x - req->sdp_start;
- return TRUE;
- }
- return FALSE;
- }
- /*! \brief Change hold state for a call */
- static void change_hold_state(struct sip_pvt *dialog, struct sip_request *req, int holdstate, int sendonly)
- {
- if (sip_cfg.notifyhold && (!holdstate || !ast_test_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD)))
- sip_peer_hold(dialog, holdstate);
- if (sip_cfg.callevents)
- manager_event(EVENT_FLAG_CALL, "Hold",
- "Status: %s\r\n"
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n",
- holdstate ? "On" : "Off",
- ast_channel_name(dialog->owner),
- ast_channel_uniqueid(dialog->owner));
- 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;
- }
- static int get_ip_and_port_from_sdp(struct sip_request *req, const enum media_type media, struct ast_sockaddr *addr)
- {
- const char *m;
- const char *c;
- int miterator = req->sdp_start;
- int citerator = req->sdp_start;
- unsigned int x = 0;
- unsigned int numberofports;
- int len;
- int af;
- char proto[4], host[258] = ""; /*Initialize to empty so we will know if we have any input */
- c = get_sdp_iterate(&citerator, req, "c");
- if (sscanf(c, "IN %3s %256s", proto, host) != 2) {
- ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c);
- /* Continue since there may be a valid host in a c= line specific to the audio stream */
- }
- /* We only want the m and c lines for audio */
- for (m = get_sdp_iterate(&miterator, req, "m"); !ast_strlen_zero(m); m = get_sdp_iterate(&miterator, req, "m")) {
- if ((media == SDP_AUDIO && ((sscanf(m, "audio %30u/%30u RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
- (sscanf(m, "audio %30u RTP/AVP %n", &x, &len) == 1 && len > 0))) ||
- (media == SDP_VIDEO && ((sscanf(m, "video %30u/%30u RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
- (sscanf(m, "video %30u RTP/AVP %n", &x, &len) == 1 && len > 0)))) {
- /* See if there's a c= line for this media stream.
- * XXX There is no guarantee that we'll be grabbing the c= line for this
- * particular media stream here. However, this is the same logic used in process_sdp.
- */
- c = get_sdp_iterate(&citerator, req, "c");
- if (!ast_strlen_zero(c)) {
- sscanf(c, "IN %3s %256s", proto, host);
- }
- break;
- }
- }
- 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 -1;
- }
- if (ast_strlen_zero(host) || x == 0) {
- ast_log(LOG_WARNING, "Failed to read an alternate host or port in SDP. Expect %s problems\n", media == SDP_AUDIO ? "audio" : "video");
- return -1;
- }
- if (ast_sockaddr_resolve_first_af(addr, host, 0, af)) {
- ast_log(LOG_WARNING, "Could not look up IP address of alternate hostname. Expect %s problems\n", media == SDP_AUDIO? "audio" : "video");
- return -1;
- }
- return 0;
- }
- /*! \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_nolock();
- struct ast_format_cap *vpeercapability = ast_format_cap_alloc_nolock();
- struct ast_format_cap *tpeercapability = ast_format_cap_alloc_nolock();
- int peernoncodeccapability = 0, vpeernoncodeccapability = 0, tpeernoncodeccapability = 0;
- struct ast_rtp_codecs newaudiortp = { 0, }, newvideortp = { 0, }, newtextrtp = { 0, };
- struct ast_format_cap *newjointcapability = ast_format_cap_alloc_nolock(); /* Negotiated capability */
- struct ast_format_cap *newpeercapability = ast_format_cap_alloc_nolock();
- 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 */
- char buf[SIPBUFSIZE];
- 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, SRTP_CRYPTO_OFFER_OK);
- }
- }
- if (process_sdp_a_dtls(value, p, p->vrtp)) {
- processed = TRUE;
- if (p->vsrtp) {
- ast_set_flag(p->vsrtp, SRTP_CRYPTO_OFFER_OK);
- }
- }
- if (process_sdp_a_dtls(value, p, p->trtp)) {
- processed = TRUE;
- if (p->tsrtp) {
- ast_set_flag(p->tsrtp, 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_log(LOG_WARNING, "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;
- if (p->srtp) {
- ast_set_flag(p->srtp, 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_log(LOG_WARNING, "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;
- if (p->vsrtp || (p->vsrtp = sip_srtp_alloc())) {
- ast_set_flag(p->vsrtp, 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_log(LOG_WARNING, "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_log(LOG_WARNING, "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, 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, 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_b;
- } 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_b;
- }
- }
- /* Sanity checks */
- if (!sa && !vsa && !tsa && !isa) {
- ast_log(LOG_WARNING, "Insufficient information in SDP (c=)...\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if ((portno == -1) &&
- (vportno == -1) &&
- (tportno == -1) &&
- (udptlportno == -1)) {
- ast_log(LOG_WARNING, "Failing due to no acceptable offer found\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if (p->srtp && p->udptl && udptlportno != -1) {
- ast_debug(1, "Terminating SRTP due to T.38 UDPTL\n");
- sip_srtp_destroy(p->srtp);
- p->srtp = NULL;
- }
- if (secure_audio && !(p->srtp && (ast_test_flag(p->srtp, SRTP_CRYPTO_OFFER_OK)))) {
- ast_log(LOG_WARNING, "Can't provide secure audio requested in SDP offer\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if (!secure_audio && p->srtp) {
- ast_log(LOG_WARNING, "Failed to receive SDP offer/answer with required SRTP crypto attributes for audio\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if (secure_video && !(p->vsrtp && (ast_test_flag(p->vsrtp, SRTP_CRYPTO_OFFER_OK)))) {
- ast_log(LOG_WARNING, "Can't provide secure video requested in SDP offer\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if (!p->novideo && !secure_video && p->vsrtp) {
- ast_log(LOG_WARNING, "Failed to receive SDP offer/answer with required SRTP crypto attributes for video\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if (!(secure_audio || secure_video || (p->udptl && udptlportno != -1)) && ast_test_flag(&p->flags[1], SIP_PAGE2_USE_SRTP)) {
- ast_log(LOG_WARNING, "Matched device setup to use SRTP, but request was not!\n");
- res = -1;
- goto process_sdp_cleanup;
- }
- if (udptlportno == -1) {
- change_t38_state(p, T38_DISABLED);
- }
- /* Now gather all of the codecs that we are asked for: */
- ast_rtp_codecs_payload_formats(&newaudiortp, peercapability, &peernoncodeccapability);
- ast_rtp_codecs_payload_formats(&newvideortp, vpeercapability, &vpeernoncodeccapability);
- ast_rtp_codecs_payload_formats(&newtextrtp, tpeercapability, &tpeernoncodeccapability);
- ast_format_cap_append(newpeercapability, peercapability);
- ast_format_cap_append(newpeercapability, vpeercapability);
- ast_format_cap_append(newpeercapability, tpeercapability);
- ast_format_cap_joint_copy(p->caps, newpeercapability, newjointcapability);
- if (ast_format_cap_is_empty(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.... */
- char s1[SIPBUFSIZE], s2[SIPBUFSIZE], s3[SIPBUFSIZE], s4[SIPBUFSIZE], s5[SIPBUFSIZE];
- ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s/text=%s, combined - %s\n",
- ast_getformatname_multiple(s1, SIPBUFSIZE, p->caps),
- ast_getformatname_multiple(s2, SIPBUFSIZE, peercapability),
- ast_getformatname_multiple(s3, SIPBUFSIZE, vpeercapability),
- ast_getformatname_multiple(s4, SIPBUFSIZE, tpeercapability),
- ast_getformatname_multiple(s5, SIPBUFSIZE, newjointcapability));
- }
- 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_copy(p->jointcaps, newjointcapability); /* Our joint codec profile for this call */
- ast_format_cap_copy(p->peercaps, newpeercapability); /* 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)) {
- ast_codec_choose(&p->prefs, p->jointcaps, 1, &tmp_fmt);
- ast_format_cap_set(p->jointcaps, &tmp_fmt);
- }
- }
- /* 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(p->jointcaps, ast_format_set(&tmp_fmt, AST_FORMAT_T140RED, 0))) {
- 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_getformatname_multiple(buf, SIPBUFSIZE, p->jointcaps));
- 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_FORMAT_TYPE_AUDIO)) {
- if (debug) {
- char s1[SIPBUFSIZE], s2[SIPBUFSIZE];
- ast_debug(1, "Setting native formats after processing SDP. peer joint formats %s, old nativeformats %s\n",
- ast_getformatname_multiple(s1, SIPBUFSIZE, p->jointcaps),
- ast_getformatname_multiple(s2, SIPBUFSIZE, ast_channel_nativeformats(p->owner)));
- }
- ast_codec_choose(&p->prefs, p->jointcaps, 1, &tmp_fmt);
- ast_format_cap_set(ast_channel_nativeformats(p->owner), &tmp_fmt);
- ast_format_cap_joint_append(p->caps, vpeercapability, ast_channel_nativeformats(p->owner));
- ast_format_cap_joint_append(p->caps, tpeercapability, ast_channel_nativeformats(p->owner));
- 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)) {
- ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
- /* 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)) {
- ast_queue_control_data(p->owner, AST_CONTROL_HOLD,
- S_OR(p->mohsuggest, NULL),
- !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
- 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);
- }
- process_sdp_cleanup_b:
- ast_rtp_codecs_payloads_destroy(&newtextrtp);
- ast_rtp_codecs_payloads_destroy(&newvideortp);
- ast_rtp_codecs_payloads_destroy(&newaudiortp);
- ast_format_cap_destroy(peercapability);
- ast_format_cap_destroy(vpeercapability);
- ast_format_cap_destroy(tpeercapability);
- ast_format_cap_destroy(newjointcapability);
- ast_format_cap_destroy(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) {
- struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(p->rtp)->pref;
- int codec_n;
- for (codec_n = 0; codec_n < AST_RTP_MAX_PT; codec_n++) {
- struct ast_rtp_payload_type format = ast_rtp_codecs_payload_lookup(ast_rtp_instance_get_codecs(p->rtp), codec_n);
- if (!format.asterisk_format) /* non-codec or not found */
- continue;
- ast_debug(1, "Setting framing for %s to %ld\n", ast_getformatname(&format.format), framing);
- ast_codec_pref_setsize(pref, &format.format, framing);
- }
- ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, pref);
- }
- 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;
- if (!ast_format_sdp_parse(format, fmtp_string)) {
- found = TRUE;
- } else {
- ast_rtp_codecs_payloads_unset(newaudiortp, NULL, codec);
- }
- switch ((int) format->id) {
- case AST_FORMAT_SIREN7:
- 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;
- }
- }
- break;
- case AST_FORMAT_SIREN14:
- 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;
- }
- }
- break;
- case AST_FORMAT_G719:
- 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;
- }
- }
- break;
- }
- }
- }
- 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)) {
- 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))) {
- if (!ast_format_sdp_parse(format, fmtp_string)) {
- found = TRUE;
- } else {
- ast_rtp_codecs_payloads_unset(newvideortp, NULL, codec);
- }
- }
- }
- 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 '%u'\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", 20) == 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", 20) == 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", 21) == 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)
- {
- int res;
- if (st_get_mode(pvt, 0) != SESSION_TIMER_MODE_REFUSE) {
- res = add_header(req, "Supported", "replaces, timer");
- } else {
- res = add_header(req, "Supported", "replaces");
- }
- 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)
- {
- char r[SIPBUFSIZE*2], *p;
- int n, rem = sizeof(r);
- if (!route)
- return;
- p = r;
- for (;route ; route = route->next) {
- n = strlen(route->hop);
- if (rem < n+3) /* we need room for ",<route>" */
- break;
- if (p != r) { /* add a separator after fist route */
- *p++ = ',';
- --rem;
- }
- *p++ = '<';
- ast_copy_string(p, route->hop, rem); /* cannot fail */
- p += n;
- *p++ = '>';
- rem -= (n+2);
- }
- *p = '\0';
- add_header(req, "Route", 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 sip[s]:username[:password][;parameter]@host[:port][;...]
- * 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, char *uri)
- {
- char *trans, *h, *maddr, hostname[256];
- 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 */
- }
- } 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 (p->route && !ast_strlen_zero(p->route->hop) && strstr(p->route->hop, ";lr") == NULL) {
- 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 ? p->route->hop : p->okcontacturi;
- else
- c = REQ_OFFSET_TO_STR(&p->initreq, rlpart2);
- } else if (!ast_strlen_zero(p->okcontacturi))
- c = is_strict ? p->route->hop : p->okcontacturi; /* Use for BYE or REINVITE */
- else if (!ast_strlen_zero(p->uri))
- c = p->uri;
- else {
- char *n;
- /* We have no URI, use To: or From: header as URI (depending on direction) */
- ast_copy_string(stripped, sip_get_header(orig, is_outbound ? "To" : "From"),
- sizeof(stripped));
- n = get_in_brackets(stripped);
- c = remove_uri_parameters(n);
- }
- init_req(req, sipmethod, c);
- snprintf(tmp, sizeof(tmp), "%u %s", seqno, sip_methods[sipmethod].text);
- add_header(req, "Via", p->via);
- /*
- * Use the learned route set unless this is a CANCEL or an ACK for a non-2xx
- * final response. For a CANCEL or ACK, we have to send to the same destination
- * as the original INVITE.
- * Send UPDATE to the same destination as CANCEL, if call is not in final state.
- */
- if (p->route &&
- !(sipmethod == SIP_CANCEL ||
- (sipmethod == SIP_ACK && (p->invitestate == INV_COMPLETED || p->invitestate == INV_CANCELLED)) ||
- (sipmethod == SIP_UPDATE && (p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA)))) {
- set_destination(p, p->route->hop);
- add_route(req, is_strict ? p->route->next : p->route);
- }
- 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 rtp_code;
- struct ast_format_list fmt;
- const char *mime;
- unsigned int rate;
- if (debug)
- ast_verbose("Adding codec %u (%s) to SDP\n", format->id, ast_getformatname(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;
- }
- if (p->rtp) {
- struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(p->rtp)->pref;
- fmt = ast_codec_pref_getsize(pref, format);
- } else /* I don't see how you couldn't have p->rtp, but good to check for and error out if not there like earlier code */
- 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, mime, rate);
- ast_format_sdp_generate(format, rtp_code, a_buf);
- switch ((int) format->id) {
- case AST_FORMAT_G729A:
- /* 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);
- break;
- case AST_FORMAT_G723_1:
- /* 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);
- break;
- case AST_FORMAT_ILBC:
- /* Add information about us using only 20/30 ms packetization */
- ast_str_append(a_buf, 0, "a=fmtp:%d mode=%d\r\n", rtp_code, fmt.cur_ms);
- break;
- case AST_FORMAT_SIREN7:
- /* Indicate that we only expect 32Kbps */
- ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=32000\r\n", rtp_code);
- break;
- case AST_FORMAT_SIREN14:
- /* Indicate that we only expect 48Kbps */
- ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=48000\r\n", rtp_code);
- break;
- case AST_FORMAT_G719:
- /* Indicate that we only expect 64Kbps */
- ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=64000\r\n", rtp_code);
- break;
- }
- if (fmt.cur_ms && (fmt.cur_ms < *min_packet_size))
- *min_packet_size = fmt.cur_ms;
- /* Our first codec packetization processed cannot be zero */
- if ((*min_packet_size)==0 && fmt.cur_ms)
- *min_packet_size = fmt.cur_ms;
- }
- /*! \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 %u (%s) to SDP\n", format->id, ast_getformatname(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);
- ast_format_sdp_generate(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 %u (%s) to SDP\n", format->id, ast_getformatname(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 (format->id == AST_FORMAT_T140RED) {
- struct ast_format tmp_fmt;
- int t140code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(p->trtp), 1, ast_format_set(&tmp_fmt, AST_FORMAT_T140, 0), 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 void get_crypto_attrib(struct sip_pvt *p, struct sip_srtp *srtp, const char **a_crypto)
- {
- int taglen = 80;
- /* Set encryption properties */
- if (srtp) {
- if (!srtp->crypto) {
- srtp->crypto = sdp_crypto_setup();
- }
- if (p->dtls_cfg.enabled) {
- /* If DTLS-SRTP is enabled the key details will be pulled from TLS */
- return;
- }
- /* set the key length based on INVITE or settings */
- if (ast_test_flag(srtp, SRTP_CRYPTO_TAG_80)) {
- taglen = 80;
- } else if (ast_test_flag(&p->flags[2], SIP_PAGE3_SRTP_TAG_32) ||
- ast_test_flag(srtp, SRTP_CRYPTO_TAG_32)) {
- taglen = 32;
- }
- if (srtp->crypto && (sdp_crypto_offer(srtp->crypto, taglen) >= 0)) {
- *a_crypto = sdp_crypto_attrib(srtp->crypto);
- }
- if (!*a_crypto) {
- ast_log(LOG_WARNING, "No SRTP key management enabled\n");
- }
- }
- }
- static char *get_sdp_rtp_profile(const struct sip_pvt *p, unsigned int secure, struct ast_rtp_instance *instance)
- {
- struct ast_rtp_engine_dtls *dtls;
- if ((dtls = ast_rtp_instance_get_dtls(instance)) && dtls->active(instance)) {
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_FORCE_AVP)) {
- return ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF) ? "RTP/SAVPF" : "RTP/SAVP";
- } else {
- return ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF) ? "UDP/TLS/RTP/SAVPF" : "UDP/TLS/RTP/SAVP";
- }
- } else {
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
- return secure ? "RTP/SAVPF" : "RTP/AVPF";
- } else {
- return secure ? "RTP/SAVP" : "RTP/AVP";
- }
- }
- }
- /*! \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_nolock();
- struct ast_format_cap *tmpcap = ast_format_cap_alloc_nolock();
- 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 */
- const char *a_crypto = NULL;
- const char *v_a_crypto = NULL;
- const char *t_a_crypto = NULL;
- 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 min_video_packet_size = 0;
- int min_text_packet_size = 0;
- char codecbuf[SIPBUFSIZE];
- char buf[SIPBUFSIZE];
- /* 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_is_empty(p->redircaps))) ? TRUE : FALSE;
- /* Check if we need video in this call */
- if ((ast_format_cap_has_type(p->jointcaps, AST_FORMAT_TYPE_VIDEO)) && !p->novideo) {
- ast_format_cap_joint_copy(p->jointcaps, p->redircaps, tmpcap);
- if (doing_directmedia && !ast_format_cap_has_type(tmpcap, AST_FORMAT_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_FORMAT_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");
- }
- }
- }
- 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";
- }
- ast_format_cap_copy(tmpcap, p->jointcaps);
- /* XXX note, Video and Text are negated - 'true' means 'no' */
- ast_debug(1, "** Our capability: %s Video flag: %s Text flag: %s\n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), tmpcap),
- p->novideo ? "True" : "False", p->notext ? "True" : "False");
- ast_debug(1, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcaps));
- if (doing_directmedia) {
- ast_format_cap_joint_copy(p->jointcaps, p->redircaps, tmpcap);
- ast_debug(1, "** Our native-bridge filtered capablity: %s\n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), tmpcap));
- }
- /* Check if we need audio */
- if (ast_format_cap_has_type(tmpcap, AST_FORMAT_TYPE_AUDIO))
- needaudio = TRUE;
- 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) {
- get_crypto_attrib(p, p->vsrtp, &v_a_crypto);
- ast_str_append(&m_video, 0, "m=video %d %s", ast_sockaddr_port(&vdest),
- get_sdp_rtp_profile(p, v_a_crypto ? 1 : 0, p->vrtp));
- /* 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");
- get_crypto_attrib(p, p->tsrtp, &t_a_crypto);
- ast_str_append(&m_text, 0, "m=text %d %s", ast_sockaddr_port(&tdest),
- get_sdp_rtp_profile(p, t_a_crypto ? 1 : 0, p->trtp));
- 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 */
- get_crypto_attrib(p, p->srtp, &a_crypto);
- ast_str_append(&m_audio, 0, "m=audio %d %s", ast_sockaddr_port(&dest),
- get_sdp_rtp_profile(p, a_crypto ? 1 : 0, p->rtp));
- /* Now, start adding audio codecs. These are added in this order:
- - First what was requested by the calling channel
- - Then preferences in order from sip.conf device config for this peer/user
- - Then other codecs in capabilities, including video
- */
- /* Prefer the audio codec we were requested to use, first, no matter what
- Note that p->prefcodec can include video codecs, so ignore them
- */
- ast_format_cap_iter_start(p->prefcaps);
- while (!(ast_format_cap_iter_next(p->prefcaps, &tmp_fmt))) {
- if (AST_FORMAT_GET_TYPE(tmp_fmt.id) != AST_FORMAT_TYPE_AUDIO ||
- !ast_format_cap_iscompatible(tmpcap, &tmp_fmt)) {
- continue;
- }
- add_codec_to_sdp(p, &tmp_fmt, &m_audio, &a_audio, debug, &min_audio_packet_size);
- ast_format_cap_add(alreadysent, &tmp_fmt);
- }
- ast_format_cap_iter_end(p->prefcaps);
- /* Start by sending our preferred audio/video codecs */
- for (x = 0; x < AST_CODEC_PREF_SIZE; x++) {
- struct ast_format pref;
- if (!(ast_codec_pref_index(&p->prefs, x, &pref)))
- break;
- if (!ast_format_cap_get_compatible_format(tmpcap, &pref, &tmp_fmt))
- continue;
- if (ast_format_cap_iscompatible(alreadysent, &tmp_fmt))
- continue;
- if (AST_FORMAT_GET_TYPE(tmp_fmt.id) == AST_FORMAT_TYPE_AUDIO) {
- add_codec_to_sdp(p, &tmp_fmt, &m_audio, &a_audio, debug, &min_audio_packet_size);
- } else if (needvideo && (AST_FORMAT_GET_TYPE(tmp_fmt.id) == AST_FORMAT_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.id) == AST_FORMAT_TYPE_TEXT)) {
- add_tcodec_to_sdp(p, &tmp_fmt, &m_text, &a_text, debug, &min_text_packet_size);
- }
- ast_format_cap_add(alreadysent, &tmp_fmt);
- }
- /* Now send any other common audio and video codecs, and non-codec formats: */
- ast_format_cap_iter_start(tmpcap);
- while (!(ast_format_cap_iter_next(tmpcap, &tmp_fmt))) {
- if (ast_format_cap_iscompatible(alreadysent, &tmp_fmt))
- continue;
- if (AST_FORMAT_GET_TYPE(tmp_fmt.id) == AST_FORMAT_TYPE_AUDIO) {
- add_codec_to_sdp(p, &tmp_fmt, &m_audio, &a_audio, debug, &min_audio_packet_size);
- } else if (needvideo && (AST_FORMAT_GET_TYPE(tmp_fmt.id) == AST_FORMAT_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.id) == AST_FORMAT_TYPE_TEXT)) {
- add_tcodec_to_sdp(p, &tmp_fmt, &m_text, &a_text, debug, &min_text_packet_size);
- }
- }
- ast_format_cap_iter_end(tmpcap);
- /* 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 (!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_getformatname_multiple(buf, SIPBUFSIZE, tmpcap));
- add_sdp_cleanup:
- ast_free(a_text);
- ast_free(a_video);
- ast_free(a_audio);
- alreadysent = ast_format_cap_destroy(alreadysent);
- tmpcap = ast_format_cap_destroy(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_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &p->prefs);
- }
- ast_rtp_instance_activate(p->rtp);
- try_suggested_sip_codec(p);
- if (p->t38.state == T38_ENABLED) {
- add_sdp(&resp, p, oldsdp, TRUE, TRUE);
- } else {
- add_sdp(&resp, p, oldsdp, TRUE, FALSE);
- }
- } else
- ast_log(LOG_ERROR, "Can't add SDP to response, since we have no RTP session allocated. Call-ID %s\n", p->callid);
- if (reliable && !p->pendinginvite)
- p->pendinginvite = seqno; /* Buggy clients sends ACK on RINGING too */
- add_required_respheader(&resp);
- return send_response(p, &resp, reliable, seqno);
- }
- /*! \brief Parse first line of incoming SIP request */
- static int determine_firstline_parts(struct sip_request *req)
- {
- char *e = ast_skip_blanks(ast_str_buffer(req->data)); /* there shouldn't be any */
- char *local_rlpart1;
- if (!*e)
- return -1;
- req->rlpart1 = e - ast_str_buffer(req->data); /* method or protocol */
- local_rlpart1 = e;
- e = ast_skip_nonblanks(e);
- if (*e)
- *e++ = '\0';
- /* Get URI or status code */
- e = ast_skip_blanks(e);
- if ( !*e )
- return -1;
- ast_trim_blanks(e);
- if (!strcasecmp(local_rlpart1, "SIP/2.0") ) { /* We have a response */
- if (strlen(e) < 3) /* status code is 3 digits */
- return -1;
- req->rlpart2 = e - ast_str_buffer(req->data);
- } else { /* We have a request */
- if ( *e == '<' ) { /* XXX the spec says it must not be in <> ! */
- ast_debug(3, "Oops. Bogus uri in <> %s\n", e);
- e++;
- if (!*e)
- return -1;
- }
- req->rlpart2 = e - ast_str_buffer(req->data); /* URI */
- e = ast_skip_nonblanks(e);
- if (*e)
- *e++ = '\0';
- e = ast_skip_blanks(e);
- if (strcasecmp(e, "SIP/2.0") ) {
- ast_debug(3, "Skipping packet - Bad request protocol %s\n", e);
- return -1;
- }
- }
- return 1;
- }
- /*! \brief Transmit reinvite with SDP
- \note A re-invite is basically a new INVITE with the same CALL-ID and TAG as the
- INVITE that opened the SIP dialogue
- We reinvite so that the audio stream (RTP) go directly between
- the SIP UAs. SIP Signalling stays with * in the path.
-
- If t38version is TRUE, we send T38 SDP for re-invite from audio/video to
- T38 UDPTL transmission on the channel
- If oldsdp is TRUE then the SDP version number is not incremented. This
- is needed for Session-Timers so we can send a re-invite to refresh the
- SIP session without modifying the media session.
- */
- static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int oldsdp)
- {
- struct sip_request req;
-
- reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1);
- add_header(&req, "Allow", ALLOWED_METHODS);
- add_supported(p, &req);
- if (sipdebug) {
- if (oldsdp == TRUE)
- add_header(&req, "X-asterisk-Info", "SIP re-invite (Session-Timers)");
- else
- add_header(&req, "X-asterisk-Info", "SIP re-invite (External RTP bridge)");
- }
- if (ast_test_flag(&p->flags[0], SIP_SENDRPID))
- add_rpid(&req, p);
- if (p->do_history) {
- append_history(p, "ReInv", "Re-invite sent");
- }
- offered_media_list_destroy(p);
- try_suggested_sip_codec(p);
- if (t38version) {
- add_sdp(&req, p, oldsdp, FALSE, TRUE);
- } else {
- add_sdp(&req, p, oldsdp, TRUE, FALSE);
- }
- /* Use this as the basis */
- initialize_initreq(p, &req);
- p->lastinvite = p->ocseq;
- ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Change direction of this dialog */
- p->ongoing_reinvite = 1;
- return send_request(p, &req, XMIT_CRITICAL, p->ocseq);
- }
- /* \brief Remove URI parameters at end of URI, not in username part though */
- static char *remove_uri_parameters(char *uri)
- {
- char *atsign;
- atsign = strchr(uri, '@'); /* First, locate the at sign */
- if (!atsign) {
- atsign = uri; /* Ok hostname only, let's stick with the rest */
- }
- atsign = strchr(atsign, ';'); /* Locate semi colon */
- if (atsign)
- *atsign = '\0'; /* Kill at the semi colon */
- return uri;
- }
- /*! \brief Check Contact: URI of SIP message */
- static void extract_uri(struct sip_pvt *p, struct sip_request *req)
- {
- char stripped[SIPBUFSIZE];
- char *c;
- ast_copy_string(stripped, sip_get_header(req, "Contact"), sizeof(stripped));
- c = get_in_brackets(stripped);
- /* Cut the URI at the at sign after the @, not in the username part */
- c = remove_uri_parameters(c);
- if (!ast_strlen_zero(c)) {
- ast_string_field_set(p, uri, c);
- }
- }
- /*!
- * \brief Determine if, as a UAS, we need to use a SIPS Contact.
- *
- * This uses the rules defined in RFC 3261 section 12.1.1 to
- * determine if a SIPS URI should be used as the Contact header
- * when responding to incoming SIP requests.
- *
- * \param req The incoming SIP request
- * \retval 0 SIPS is not required
- * \retval 1 SIPS is required
- */
- static int uas_sips_contact(struct sip_request *req)
- {
- const char *record_route = sip_get_header(req, "Record-Route");
- if (!strncmp(REQ_OFFSET_TO_STR(req, rlpart2), "sips:", 5)) {
- return 1;
- }
- if (record_route) {
- char *record_route_uri = get_in_brackets(ast_strdupa(record_route));
- if (!strncmp(record_route_uri, "sips:", 5)) {
- return 1;
- }
- } else {
- const char *contact = sip_get_header(req, "Contact");
- char *contact_uri = get_in_brackets(ast_strdupa(contact));
- if (!strncmp(contact_uri, "sips:", 5)) {
- return 1;
- }
- }
- return 0;
- }
- /*!
- * \brief Determine if, as a UAC, we need to use a SIPS Contact.
- *
- * This uses the rules defined in RFC 3621 section 8.1.1.8 to
- * determine if a SIPS URI should be used as the Contact header
- * on our outgoing request.
- *
- * \param req The outgoing SIP request
- * \retval 0 SIPS is not required
- * \retval 1 SIPS is required
- */
- static int uac_sips_contact(struct sip_request *req)
- {
- const char *route = sip_get_header(req, "Route");
- if (!strncmp(REQ_OFFSET_TO_STR(req, rlpart2), "sips:", 5)) {
- return 1;
- }
- if (route) {
- char *route_uri = get_in_brackets(ast_strdupa(route));
- if (!strncmp(route_uri, "sips:", 5)) {
- return 1;
- }
- }
- return 0;
- }
- /*!
- * \brief Build contact header
- *
- * This is the Contact header that we send out in SIP requests and responses
- * involving this sip_pvt.
- *
- * The incoming parameter is used to tell if we are building the request parameter
- * is an incoming SIP request that we are building the Contact header in response to,
- * or if the req parameter is an outbound SIP request that we will later be adding
- * the Contact header to.
- *
- * \param p The sip_pvt where the built Contact will be saved.
- * \param req The request that triggered the creation of a Contact header.
- * \param incoming Indicates if the Contact header is being created for a response to an incoming request
- */
- static void build_contact(struct sip_pvt *p, struct sip_request *req, int incoming)
- {
- char tmp[SIPBUFSIZE];
- char *user = ast_uri_encode(p->exten, tmp, sizeof(tmp), ast_uri_sip_user);
- int use_sips;
- if (incoming) {
- use_sips = uas_sips_contact(req);
- } else {
- use_sips = uac_sips_contact(req);
- }
- if (p->socket.type == SIP_TRANSPORT_UDP) {
- ast_string_field_build(p, our_contact, "<%s:%s%s%s>", use_sips ? "sips" : "sip",
- user, ast_strlen_zero(user) ? "" : "@",
- ast_sockaddr_stringify_remote(&p->ourip));
- } else {
- ast_string_field_build(p, our_contact, "<%s:%s%s%s;transport=%s>",
- use_sips ? "sips" : "sip", user, ast_strlen_zero(user) ? "" : "@",
- ast_sockaddr_stringify_remote(&p->ourip), sip_get_transport(p->socket.type));
- }
- }
- /*! \brief Initiate new SIP request to peer/user */
- static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, const char * const explicit_uri)
- {
- struct ast_str *invite = ast_str_alloca(256);
- char from[256];
- char to[256];
- char tmp_n[SIPBUFSIZE/2]; /* build a local copy of 'n' if needed */
- char tmp_l[SIPBUFSIZE/2]; /* build a local copy of 'l' if needed */
- const char *l = NULL; /* XXX what is this, exactly ? */
- const char *n = NULL; /* XXX what is this, exactly ? */
- const char *d = NULL; /* domain in from header */
- const char *urioptions = "";
- int ourport;
- int cid_has_name = 1;
- int cid_has_num = 1;
- struct ast_party_id connected_id;
- if (ast_test_flag(&p->flags[0], SIP_USEREQPHONE)) {
- const char *s = p->username; /* being a string field, cannot be NULL */
- /* Test p->username against allowed characters in AST_DIGIT_ANY
- If it matches the allowed characters list, then sipuser = ";user=phone"
- If not, then sipuser = ""
- */
- /* + is allowed in first position in a tel: uri */
- if (*s == '+')
- s++;
- for (; *s; s++) {
- if (!strchr(AST_DIGIT_ANYNUM, *s) )
- break;
- }
- /* If we have only digits, add ;user=phone to the uri */
- if (!*s)
- urioptions = ";user=phone";
- }
- snprintf(p->lastmsg, sizeof(p->lastmsg), "Init: %s", sip_methods[sipmethod].text);
- if (ast_strlen_zero(p->fromdomain)) {
- d = ast_sockaddr_stringify_host_remote(&p->ourip);
- }
- if (p->owner) {
- connected_id = ast_channel_connected_effective_id(p->owner);
- if ((ast_party_id_presentation(&connected_id) & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
- if (connected_id.number.valid) {
- l = connected_id.number.str;
- }
- if (connected_id.name.valid) {
- n = connected_id.name.str;
- }
- } else {
- /* Even if we are using RPID, we shouldn't leak information in the From if the user wants
- * their callerid restricted */
- l = "anonymous";
- n = CALLERID_UNKNOWN;
- d = FROMDOMAIN_INVALID;
- }
- }
- /* Hey, it's a NOTIFY! See if they've configured a mwi_from.
- * XXX Right now, this logic works because the only place that mwi_from
- * is set on the sip_pvt is in sip_send_mwi_to_peer. If things changed, then
- * we might end up putting the mwi_from setting into other types of NOTIFY
- * messages as well.
- */
- if (sipmethod == SIP_NOTIFY && !ast_strlen_zero(p->mwi_from)) {
- l = p->mwi_from;
- }
- if (ast_strlen_zero(l)) {
- cid_has_num = 0;
- l = default_callerid;
- }
- if (ast_strlen_zero(n)) {
- cid_has_name = 0;
- n = l;
- }
- /* Allow user to be overridden */
- if (!ast_strlen_zero(p->fromuser))
- l = p->fromuser;
- else /* Save for any further attempts */
- ast_string_field_set(p, fromuser, l);
- /* Allow user to be overridden */
- if (!ast_strlen_zero(p->fromname))
- n = p->fromname;
- else /* Save for any further attempts */
- ast_string_field_set(p, fromname, n);
- /* Allow domain to be overridden */
- if (!ast_strlen_zero(p->fromdomain))
- d = p->fromdomain;
- else /* Save for any further attempts */
- ast_string_field_set(p, fromdomain, d);
- ast_copy_string(tmp_l, l, sizeof(tmp_l));
- if (sip_cfg.pedanticsipchecking) {
- ast_escape_quoted(n, tmp_n, sizeof(tmp_n));
- n = tmp_n;
- ast_uri_encode(l, tmp_l, sizeof(tmp_l), ast_uri_sip_user);
- }
- ourport = (p->fromdomainport && (p->fromdomainport != STANDARD_SIP_PORT)) ? p->fromdomainport : ast_sockaddr_port(&p->ourip);
- /* If a caller id name was specified, add a display name. */
- if (cid_has_name || !cid_has_num) {
- snprintf(from, sizeof(from), "\"%s\" ", n);
- } else {
- from[0] = '\0';
- }
- if (!sip_standard_port(p->socket.type, ourport)) {
- size_t offset = strlen(from);
- snprintf(&from[offset], sizeof(from) - offset, "<sip:%s@%s:%d>;tag=%s", tmp_l, d, ourport, p->tag);
- } else {
- size_t offset = strlen(from);
- snprintf(&from[offset], sizeof(from) - offset, "<sip:%s@%s>;tag=%s", tmp_l, d, p->tag);
- }
- if (!ast_strlen_zero(explicit_uri)) {
- ast_str_set(&invite, 0, "%s", explicit_uri);
- } else {
- /* If we're calling a registered SIP peer, use the fullcontact to dial to the peer */
- if (!ast_strlen_zero(p->fullcontact)) {
- /* If we have full contact, trust it */
- ast_str_append(&invite, 0, "%s", p->fullcontact);
- } else {
- /* Otherwise, use the username while waiting for registration */
- ast_str_append(&invite, 0, "sip:");
- if (!ast_strlen_zero(p->username)) {
- n = p->username;
- if (sip_cfg.pedanticsipchecking) {
- ast_uri_encode(n, tmp_n, sizeof(tmp_n), ast_uri_sip_user);
- n = tmp_n;
- }
- ast_str_append(&invite, 0, "%s@", n);
- }
- ast_str_append(&invite, 0, "%s", p->tohost);
- if (p->portinuri) {
- ast_str_append(&invite, 0, ":%d", ast_sockaddr_port(&p->sa));
- }
- ast_str_append(&invite, 0, "%s", urioptions);
- }
- }
- /* If custom URI options have been provided, append them */
- if (p->options && !ast_strlen_zero(p->options->uri_options))
- ast_str_append(&invite, 0, ";%s", p->options->uri_options);
-
- /* This is the request URI, which is the next hop of the call
- which may or may not be the destination of the call
- */
- ast_string_field_set(p, uri, ast_str_buffer(invite));
- if (!ast_strlen_zero(p->todnid)) {
- /*! \todo Need to add back the VXML URL here at some point, possibly use build_string for all this junk */
- if (!strchr(p->todnid, '@')) {
- /* We have no domain in the dnid */
- snprintf(to, sizeof(to), "<sip:%s@%s>%s%s", p->todnid, p->tohost, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
- } else {
- snprintf(to, sizeof(to), "<sip:%s>%s%s", p->todnid, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
- }
- } else {
- if (sipmethod == SIP_NOTIFY && !ast_strlen_zero(p->theirtag)) {
- /* If this is a NOTIFY, use the From: tag in the subscribe (RFC 3265) */
- snprintf(to, sizeof(to), "<%s%s>;tag=%s", (strncasecmp(p->uri, "sip:", 4) ? "sip:" : ""), p->uri, p->theirtag);
- } else if (p->options && p->options->vxml_url) {
- /* If there is a VXML URL append it to the SIP URL */
- snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url);
- } else {
- snprintf(to, sizeof(to), "<%s>", p->uri);
- }
- }
- init_req(req, sipmethod, p->uri);
- /* now tmp_n is available so reuse it to build the CSeq */
- snprintf(tmp_n, sizeof(tmp_n), "%u %s", ++p->ocseq, sip_methods[sipmethod].text);
- add_header(req, "Via", p->via);
- add_max_forwards(p, req);
- /* This will be a no-op most of the time. However, under certain circumstances,
- * NOTIFY messages will use this function for preparing the request and should
- * have Route headers present.
- */
- add_route(req, p->route);
- add_header(req, "From", from);
- add_header(req, "To", to);
- ast_string_field_set(p, exten, l);
- build_contact(p, req, 0);
- add_header(req, "Contact", p->our_contact);
- add_header(req, "Call-ID", p->callid);
- add_header(req, "CSeq", tmp_n);
- if (!ast_strlen_zero(global_useragent)) {
- add_header(req, "User-Agent", global_useragent);
- }
- }
- /*! \brief Add "Diversion" header to outgoing message
- *
- * We need to add a Diversion header if the owner channel of
- * this dialog has redirecting information associated with it.
- *
- * \param req The request/response to which we will add the header
- * \param pvt The sip_pvt which represents the call-leg
- */
- static void add_diversion(struct sip_request *req, struct sip_pvt *pvt)
- {
- struct ast_party_id diverting_from;
- const char *reason;
- char header_text[256];
- /* 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;
- }
- reason = sip_reason_code_to_str(ast_channel_redirecting(pvt->owner)->reason);
- /* 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", diverting_from.number.str,
- ast_sockaddr_stringify_host_remote(&pvt->ourip), reason);
- } else {
- char diverting_name_buf[128];
- ast_escape_quoted(diverting_from.name.str, diverting_name_buf, sizeof(diverting_name_buf));
- snprintf(header_text, sizeof(header_text), "\"%s\" <sip:%s@%s>;reason=%s",
- diverting_name_buf, diverting_from.number.str,
- ast_sockaddr_stringify_host_remote(&pvt->ourip), reason);
- }
- 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 (!strncasecmp(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);
- ASTOBJ_UNREF(mwi, sip_subscribe_mwi_destroy);
-
- 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];
- struct sip_subscription_mwi *saved;
- 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 */
- saved = ASTOBJ_REF(mwi);
- ast_dnsmgr_lookup_cb(mwi->hostname, &mwi->us, &mwi->dnsmgr, sip_cfg.srvlookup ? transport : NULL, on_dns_update_mwi, saved);
- if (!mwi->dnsmgr) {
- ASTOBJ_UNREF(saved, sip_subscribe_mwi_destroy); /* 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_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 = ASTOBJ_REF(mwi);
- 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 == SIP_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 == SIP_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");
- }
- /*! \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);
- registry_unref(r, "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 */
- if (p->registry) {
- p->registry = registry_unref(p->registry, "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);
- }
- manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelType: SIP\r\nUsername: %s\r\nDomain: %s\r\nStatus: %s\r\n", r->username, r->hostname, regstate2str(r->regstate));
- registry_unref(r, "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)) {
- registry_addref(r, "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! */
- registry_unref(r, "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,
- registry_unref(_data, "del for REPLACE of registry ptr"),
- registry_unref(r, "object ptr dec when SCHED_REPLACE add failed"),
- registry_addref(r,"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, registry_addref(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 = registry_addref(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 == SIP_TRANSPORT_TLS || r->transport == SIP_TRANSPORT_TCP) {
- p->socket.port =
- htons(ast_sockaddr_port(&sip_tcp_desc.local_address));
- }
- /*
- check which address we should use in our contact header
- based on whether the remote host is on the external or
- internal network so we can register through nat
- */
- ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
- }
- /* set up a timeout */
- if (auth == NULL) {
- if (r->timeout > -1) {
- ast_log(LOG_WARNING, "Still have a registration timeout, #%d - deleting it\n", r->timeout);
- }
- AST_SCHED_REPLACE_UNREF(r->timeout, sched, global_reg_timeout * 1000, sip_reg_timeout, r,
- registry_unref(_data,"reg ptr unrefed from del in SCHED_REPLACE"),
- registry_unref(r,"reg ptr unrefed from add failure in SCHED_REPLACE"),
- registry_addref(r,"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);
- if (!ast_strlen_zero(global_useragent))
- add_header(&req, "User-Agent", global_useragent);
- if (auth) { /* Add auth header */
- add_header(&req, authheader, auth);
- } else if (!ast_strlen_zero(r->nonce)) {
- char digest[1024];
- /* We have auth data to reuse, build a digest header.
- * Note, this is not always useful because some parties do not
- * like nonces to be reused (for good reasons!) so they will
- * challenge us anyways.
- */
- if (sipdebug) {
- ast_debug(1, " >>> Re-using Auth data for %s@%s\n", r->username, r->hostname);
- }
- ast_string_field_set(p, realm, r->realm);
- ast_string_field_set(p, nonce, r->nonce);
- ast_string_field_set(p, domain, r->authdomain);
- ast_string_field_set(p, opaque, r->opaque);
- ast_string_field_set(p, qop, r->qop);
- p->noncecount = ++r->noncecount;
- memset(digest, 0, sizeof(digest));
- if(!build_reply_digest(p, sipmethod, digest, sizeof(digest))) {
- add_header(&req, "Authorization", digest);
- } else {
- ast_log(LOG_NOTICE, "No authorization available for authentication of registration to %s@%s\n", r->username, r->hostname);
- }
- }
- add_expires(&req, r->expiry);
- build_contact(p, &req, 0);
- add_header(&req, "Contact", p->our_contact);
- initialize_initreq(p, &req);
- if (sip_debug_test_pvt(p)) {
- ast_verbose("REGISTER %d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
- }
- r->regstate = auth ? REG_STATE_AUTHSENT : REG_STATE_REGSENT;
- r->regattempts++; /* Another attempt */
- ast_debug(4, "REGISTER attempt %d to %s@%s\n", r->regattempts, r->username, r->hostname);
- res = send_request(p, &req, XMIT_CRITICAL, p->ocseq);
- dialog_unref(p, "p is finished here at the end of transmit_register");
- return res;
- }
- /*!
- * \brief Transmit with SIP MESSAGE method
- * \note The p->msg_headers and p->msg_body are already setup.
- */
- static int transmit_message(struct sip_pvt *p, int init, int auth)
- {
- struct sip_request req;
- if (init) {
- initreqprep(&req, p, SIP_MESSAGE, NULL);
- initialize_initreq(p, &req);
- } else {
- reqprep(&req, p, SIP_MESSAGE, 0, 1);
- }
- if (auth) {
- return transmit_request_with_auth(p, SIP_MESSAGE, p->ocseq, XMIT_RELIABLE, 0);
- } else {
- add_text(&req, p);
- return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
- }
- }
- /*! \brief Allocate SIP refer structure */
- static int sip_refer_alloc(struct sip_pvt *p)
- {
- sip_refer_destroy(p);
- p->refer = ast_calloc_with_stringfields(1, struct sip_refer, 512);
- return p->refer ? 1 : 0;
- }
- /*! \brief Destroy SIP refer structure */
- static void sip_refer_destroy(struct sip_pvt *p)
- {
- if (p->refer) {
- if (p->refer->refer_call) {
- p->refer->refer_call = dialog_unref(p->refer->refer_call, "unref dialog p->refer->refer_call");
- }
- ast_string_field_free_memory(p->refer);
- ast_free(p->refer);
- p->refer = NULL;
- }
- }
- /*! \brief Allocate SIP refer structure */
- static int sip_notify_alloc(struct sip_pvt *p)
- {
- p->notify = ast_calloc(1, sizeof(struct sip_notify));
- if (p->notify) {
- p->notify->content = ast_str_create(128);
- }
- return p->notify ? 1 : 0;
- }
- /*! \brief Transmit SIP REFER message (initiated by the transfer() dialplan application
- \note this is currently broken as we have no way of telling the dialplan
- engine whether a transfer succeeds or fails.
- \todo Fix the transfer() dialplan function so that a transfer may fail
- */
- static int transmit_refer(struct sip_pvt *p, const char *dest)
- {
- char from[256];
- const char *of;
- char *c;
- char referto[256];
- int use_tls=FALSE;
- if (sipdebug) {
- ast_debug(1, "SIP transfer of %s to %s\n", p->callid, dest);
- }
- /* Are we transfering an inbound or outbound call ? */
- if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- of = sip_get_header(&p->initreq, "To");
- } else {
- of = sip_get_header(&p->initreq, "From");
- }
- ast_copy_string(from, of, sizeof(from));
- of = get_in_brackets(from);
- ast_string_field_set(p, from, of);
- if (!strncasecmp(of, "sip:", 4)) {
- of += 4;
- } else if (!strncasecmp(of, "sips:", 5)) {
- of += 5;
- use_tls = TRUE;
- } else {
- ast_log(LOG_NOTICE, "From address missing 'sip(s):', assuming sip:\n");
- }
- /* Get just the username part */
- if (strchr(dest, '@')) {
- c = NULL;
- } else if ((c = strchr(of, '@'))) {
- *c++ = '\0';
- }
- if (c) {
- snprintf(referto, sizeof(referto), "<sip%s:%s@%s>", use_tls ? "s" : "", dest, c);
- } else {
- snprintf(referto, sizeof(referto), "<sip%s:%s>", use_tls ? "s" : "", dest);
- }
- /* save in case we get 407 challenge */
- sip_refer_alloc(p);
- ast_string_field_set(p->refer, refer_to, referto);
- ast_string_field_set(p->refer, referred_by, p->our_contact);
- p->refer->status = REFER_SENT; /* Set refer status */
- return transmit_invite(p, SIP_REFER, FALSE, 0, NULL);
- /* We should propably wait for a NOTIFY here until we ack the transfer */
- /* Maybe fork a new thread and wait for a STATUS of REFER_200OK on the refer status before returning to app_transfer */
- /*! \todo In theory, we should hang around and wait for a reply, before
- returning to the dial plan here. Don't know really how that would
- affect the transfer() app or the pbx, but, well, to make this
- useful we should have a STATUS code on transfer().
- */
- }
- /*! \brief Send SIP INFO advice of charge message */
- static int transmit_info_with_aoc(struct sip_pvt *p, struct ast_aoc_decoded *decoded)
- {
- struct sip_request req;
- struct ast_str *str = ast_str_alloca(512);
- const struct ast_aoc_unit_entry *unit_entry = ast_aoc_get_unit_info(decoded, 0);
- enum ast_aoc_charge_type charging = ast_aoc_get_charge_type(decoded);
- reqprep(&req, p, SIP_INFO, 0, 1);
- if (ast_aoc_get_msg_type(decoded) == AST_AOC_D) {
- ast_str_append(&str, 0, "type=active;");
- } else if (ast_aoc_get_msg_type(decoded) == AST_AOC_E) {
- ast_str_append(&str, 0, "type=terminated;");
- } else {
- /* unsupported message type */
- return -1;
- }
- switch (charging) {
- case AST_AOC_CHARGE_FREE:
- ast_str_append(&str, 0, "free-of-charge;");
- break;
- case AST_AOC_CHARGE_CURRENCY:
- ast_str_append(&str, 0, "charging;");
- ast_str_append(&str, 0, "charging-info=currency;");
- ast_str_append(&str, 0, "amount=%u;", ast_aoc_get_currency_amount(decoded));
- ast_str_append(&str, 0, "multiplier=%s;", ast_aoc_get_currency_multiplier_decimal(decoded));
- if (!ast_strlen_zero(ast_aoc_get_currency_name(decoded))) {
- ast_str_append(&str, 0, "currency=%s;", ast_aoc_get_currency_name(decoded));
- }
- break;
- case AST_AOC_CHARGE_UNIT:
- ast_str_append(&str, 0, "charging;");
- ast_str_append(&str, 0, "charging-info=pulse;");
- if (unit_entry) {
- ast_str_append(&str, 0, "recorded-units=%u;", unit_entry->amount);
- }
- break;
- default:
- ast_str_append(&str, 0, "not-available;");
- };
- add_header(&req, "AOC", ast_str_buffer(str));
- return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
- }
- /*! \brief Send SIP INFO dtmf message, see Cisco documentation on cisco.com */
- static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration)
- {
- struct sip_request req;
-
- reqprep(&req, p, SIP_INFO, 0, 1);
- add_digit(&req, digit, duration, (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_SHORTINFO));
- return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
- }
- /*! \brief Send SIP INFO with video update request */
- static int transmit_info_with_vidupdate(struct sip_pvt *p)
- {
- struct sip_request req;
-
- reqprep(&req, p, SIP_INFO, 0, 1);
- add_vidupdate(&req);
- return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
- }
- /*! \brief Transmit generic SIP request
- returns XMIT_ERROR if transmit failed with a critical error (don't retry)
- */
- static int transmit_request(struct sip_pvt *p, int sipmethod, uint32_t seqno, enum xmittype reliable, int newbranch)
- {
- struct sip_request resp;
-
- reqprep(&resp, p, sipmethod, seqno, newbranch);
- if (sipmethod == SIP_CANCEL && p->answered_elsewhere) {
- add_header(&resp, "Reason", "SIP;cause=200;text=\"Call completed elsewhere\"");
- }
- if (sipmethod == SIP_ACK) {
- p->invitestate = INV_CONFIRMED;
- }
- return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);
- }
- /*! \brief return the request and response header for a 401 or 407 code */
- void sip_auth_headers(enum sip_auth_type code, char **header, char **respheader)
- {
- if (code == WWW_AUTH) { /* 401 */
- *header = "WWW-Authenticate";
- *respheader = "Authorization";
- } else if (code == PROXY_AUTH) { /* 407 */
- *header = "Proxy-Authenticate";
- *respheader = "Proxy-Authorization";
- } else {
- ast_verbose("-- wrong response code %u\n", code);
- *header = *respheader = "Invalid";
- }
- }
- /*! \brief Transmit SIP request, auth added */
- static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, uint32_t seqno, enum xmittype reliable, int newbranch)
- {
- struct sip_request resp;
-
- reqprep(&resp, p, sipmethod, seqno, newbranch);
- if (!ast_strlen_zero(p->realm)) {
- char digest[1024];
- memset(digest, 0, sizeof(digest));
- if(!build_reply_digest(p, sipmethod, digest, sizeof(digest))) {
- char *dummy, *response;
- enum sip_auth_type code = p->options ? p->options->auth_type : PROXY_AUTH; /* XXX force 407 if unknown */
- sip_auth_headers(code, &dummy, &response);
- add_header(&resp, response, digest);
- } else {
- ast_log(LOG_WARNING, "No authentication available for call %s\n", p->callid);
- }
- }
- switch (sipmethod) {
- case SIP_BYE:
- {
- char buf[20];
- /*
- * We are hanging up. If we know a cause for that, send it in
- * clear text to make debugging easier.
- */
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_Q850_REASON) && p->hangupcause) {
- snprintf(buf, sizeof(buf), "Q.850;cause=%d", p->hangupcause & 0x7f);
- add_header(&resp, "Reason", buf);
- }
- add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->hangupcause));
- snprintf(buf, sizeof(buf), "%d", p->hangupcause);
- add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
- break;
- }
- case SIP_MESSAGE:
- add_text(&resp, p);
- break;
- default:
- break;
- }
- return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);
- }
- /*! \brief Remove registration data from realtime database or AST/DB when registration expires */
- static void destroy_association(struct sip_peer *peer)
- {
- int realtimeregs = ast_check_realtime("sipregs");
- char *tablename = (realtimeregs) ? "sipregs" : "sippeers";
- if (!sip_cfg.ignore_regexpire) {
- if (peer->rt_fromcontact && sip_cfg.peer_rtupdate) {
- ast_update_realtime(tablename, "name", peer->name, "fullcontact", "", "ipaddr", "", "port", "0", "regseconds", "0", "regserver", "", "useragent", "", "lastms", "0", SENTINEL);
- } else {
- ast_db_del("SIP/Registry", peer->name);
- ast_db_del("SIP/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;
- }
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
- register_peer_exten(peer, FALSE); /* Remove regexten */
- ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "SIP/%s", peer->name);
- /* Do we need to release this peer from memory?
- Only for realtime peers and autocreated peers
- */
- if (peer->is_realtime) {
- ast_debug(3, "-REALTIME- peer expired registration. Name: %s. Realtime peer objects now %d\n", peer->name, rpeerobjs);
- }
- if (peer->selfdestruct ||
- ast_test_flag(&peer->flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
- ao2_t_unlink(peers, peer, "ao2_unlink of peer from peers table");
- }
- if (!ast_sockaddr_isnull(&peer->addr)) {
- /* We still need to unlink the peer from the peers_by_ip table,
- * otherwise we end up with multiple copies hanging around each
- * time a registration expires and the peer re-registers. */
- ao2_t_unlink(peers_by_ip, peer, "ao2_unlink of peer from peers_by_ip table");
- }
- /* Only clear the addr after we check for destruction. The addr must remain
- * in order to unlink from the peers_by_ip container correctly */
- memset(&peer->addr, 0, sizeof(peer->addr));
- sip_unref_peer(peer, "removing peer ref for expire_register");
- return 0;
- }
- /*! \brief Poke peer (send qualify to check if peer is alive and well) */
- static int sip_poke_peer_s(const void *data)
- {
- struct sip_peer *peer = (struct sip_peer *)data;
- struct sip_peer *foundpeer;
- peer->pokeexpire = -1;
- foundpeer = ao2_find(peers, peer, OBJ_POINTER);
- if (!foundpeer) {
- sip_unref_peer(peer, "removing poke peer ref");
- return 0;
- } else if (foundpeer->name != peer->name) {
- sip_unref_peer(foundpeer, "removing above peer ref");
- sip_unref_peer(peer, "removing poke peer ref");
- return 0;
- }
- sip_unref_peer(foundpeer, "removing above peer ref");
- sip_poke_peer(peer, 0);
- sip_unref_peer(peer, "removing poke peer ref");
- return 0;
- }
- static int sip_poke_peer_now(const void *data)
- {
- struct sip_peer *peer = (struct sip_peer *) data;
- peer->pokeexpire = -1;
- sip_poke_peer(peer, 0);
- sip_unref_peer(peer, "removing poke peer ref");
- return 0;
- }
- /*! \brief Get registration details from Asterisk DB */
- static void reg_source_db(struct sip_peer *peer)
- {
- char data[256];
- 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);
- }
- /*! \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) ==
- SIP_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 == SIP_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 != SIP_TRANSPORT_WS) && (transport_type != SIP_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");
- }
- }
- 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)
- ast_db_put("SIP/Registry", peer->name, data);
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Registered\r\nAddress: %s\r\n", peer->name, ast_sockaddr_stringify(&peer->addr));
- /* 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 Remove route from route list */
- static void free_old_route(struct sip_route *route)
- {
- struct sip_route *next;
- while (route) {
- next = route->next;
- ast_free(route);
- route = next;
- }
- }
- /*! \brief List all routes - mostly for debugging */
- static void list_route(struct sip_route *route)
- {
- if (!route) {
- ast_verbose("list_route: no route\n");
- } else {
- for (;route; route = route->next)
- ast_verbose("list_route: hop: <%s>\n", route->hop);
- }
- }
- /*! \brief Build route list from Record-Route header
- \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)
- {
- struct sip_route *thishop, *head, *tail;
- int start = 0;
- int len;
- const char *rr, *c;
- /* Once a persistent route is set, don't fool with it */
- if (p->route && p->route_persistent) {
- ast_debug(1, "build_route: Retaining previous route: <%s>\n", p->route->hop);
- return;
- }
- if (p->route) {
- free_old_route(p->route);
- p->route = NULL;
- }
- /* 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.
- */
- head = NULL;
- tail = head;
- /* 1st we pass through all the hops in any Record-Route headers */
- for (;;) {
- /* Each Record-Route header */
- int len = 0;
- const char *uri;
- rr = __get_header(req, "Record-Route", &start);
- if (*rr == '\0') {
- break;
- }
- while (!get_in_brackets_const(rr, &uri, &len)) {
- len++;
- rr = strchr(rr, ',');
- if(rr >= uri && rr < (uri + len)) {
- /* comma inside brackets*/
- const char *next_br = strchr(rr, '<');
- if (next_br && next_br < (uri + len)) {
- rr++;
- continue;
- }
- continue;
- }
- if ((thishop = ast_malloc(sizeof(*thishop) + len))) {
- ast_copy_string(thishop->hop, uri, len);
- ast_debug(2, "build_route: Record-Route hop: <%s>\n", thishop->hop);
- /* Link in */
- if (backwards) {
- /* Link in at head so they end up in reverse order */
- thishop->next = head;
- head = thishop;
- /* If this was the first then it'll be the tail */
- if (!tail) {
- tail = thishop;
- }
- } else {
- thishop->next = NULL;
- /* Link in at the end */
- if (tail) {
- tail->next = thishop;
- } else {
- head = thishop;
- }
- tail = thishop;
- }
- }
- rr = strchr(uri + len, ',');
- if (rr == NULL) {
- /* No more field-values, we're done with this header */
- break;
- }
- /* Advance past comma */
- rr++;
- }
- }
- /* Only append the contact if we are dealing with a strict router */
- if (!head || (!ast_strlen_zero(head->hop) && strstr(head->hop, ";lr") == NULL) ) {
- /* 2nd append the Contact: if there is one */
- /* Can be multiple Contact headers, comma separated values - we just take the first */
- char *contact = ast_strdupa(sip_get_header(req, "Contact"));
- if (!ast_strlen_zero(contact)) {
- ast_debug(2, "build_route: Contact hop: %s\n", contact);
- /* Look for <: delimited address */
- c = get_in_brackets(contact);
- len = strlen(c) + 1;
- if ((thishop = ast_malloc(sizeof(*thishop) + len))) {
- /* ast_calloc is not needed because all fields are initialized in this block */
- ast_copy_string(thishop->hop, c, len);
- thishop->next = NULL;
- /* Goes at the end */
- if (tail) {
- tail->next = thishop;
- } else {
- head = thishop;
- }
- }
- }
- }
- /* Store as new route */
- p->route = head;
- /* For debugging dump what we ended up with */
- if (sip_debug_test_pvt(p)) {
- list_route(p->route);
- }
- }
- /*! \brief builds the sip_pvt's nonce field which is used for the authentication
- * challenge. When forceupdate is not set, the nonce is only updated if
- * the current one is stale. In this case, a stalenonce is one which
- * has already received a response, if a nonce has not received a response
- * it is not always necessary or beneficial to create a new one. */
- static void build_nonce(struct sip_pvt *p, int forceupdate)
- {
- if (p->stalenonce || forceupdate || ast_strlen_zero(p->nonce)) {
- ast_string_field_build(p, nonce, "%08lx", (unsigned long)ast_random()); /* Create nonce for challenge */
- p->stalenonce = 0;
- }
- }
- /*! \brief Takes the digest response and parses it */
- void sip_digest_parser(char *c, struct digestkeys *keys)
- {
- struct digestkeys *i = i;
- while(c && *(c = ast_skip_blanks(c)) ) { /* lookup for keys */
- for (i = keys; i->key != NULL; i++) {
- const char *separator = ","; /* default */
- if (strncasecmp(c, i->key, strlen(i->key)) != 0) {
- continue;
- }
- /* Found. Skip keyword, take text in quotes or up to the separator. */
- c += strlen(i->key);
- if (*c == '"') { /* in quotes. Skip first and look for last */
- c++;
- separator = "\"";
- }
- i->s = c;
- strsep(&c, separator);
- break;
- }
- if (i->key == NULL) { /* not found, jump after space or comma */
- strsep(&c, " ,");
- }
- }
- }
- /*! \brief Check user authorization from peer definition
- Some actions, like REGISTER and INVITEs from peers require
- authentication (if peer have secret set)
- \return 0 on success, non-zero on error
- */
- static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username,
- const char *secret, const char *md5secret, int sipmethod,
- const char *uri, enum xmittype reliable)
- {
- const char *response;
- char *reqheader, *respheader;
- const char *authtoken;
- char a1_hash[256];
- char resp_hash[256]="";
- char *c;
- int is_bogus_peer = 0;
- int wrongnonce = FALSE;
- int good_response;
- const char *usednonce = p->nonce;
- struct ast_str *buf;
- int res;
- /* table of recognised keywords, and their value in the digest */
- struct digestkeys keys[] = {
- [K_RESP] = { "response=", "" },
- [K_URI] = { "uri=", "" },
- [K_USER] = { "username=", "" },
- [K_NONCE] = { "nonce=", "" },
- [K_LAST] = { NULL, NULL}
- };
- /* Always OK if no secret */
- if (ast_strlen_zero(secret) && ast_strlen_zero(md5secret)) {
- return AUTH_SUCCESSFUL;
- }
- /* Always auth with WWW-auth since we're NOT a proxy */
- /* Using proxy-auth in a B2BUA may block proxy authorization in the same transaction */
- response = "401 Unauthorized";
- /*
- * Note the apparent swap of arguments below, compared to other
- * usages of sip_auth_headers().
- */
- sip_auth_headers(WWW_AUTH, &respheader, &reqheader);
- authtoken = sip_get_header(req, reqheader);
- if (req->ignore && !ast_strlen_zero(p->nonce) && ast_strlen_zero(authtoken)) {
- /* This is a retransmitted invite/register/etc, don't reconstruct authentication
- information */
- if (!reliable) {
- /* Resend message if this was NOT a reliable delivery. Otherwise the
- retransmission should get it */
- transmit_response_with_auth(p, response, req, p->nonce, reliable, respheader, 0);
- /* Schedule auto destroy in 32 seconds (according to RFC 3261) */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- }
- return AUTH_CHALLENGE_SENT;
- } else if (ast_strlen_zero(p->nonce) || ast_strlen_zero(authtoken)) {
- /* We have no auth, so issue challenge and request authentication */
- build_nonce(p, 1); /* Create nonce for challenge */
- transmit_response_with_auth(p, response, req, p->nonce, reliable, respheader, 0);
- /* Schedule auto destroy in 32 seconds */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return AUTH_CHALLENGE_SENT;
- }
- /* --- We have auth, so check it */
- /* Whoever came up with the authentication section of SIP can suck my %&#$&* for not putting
- an example in the spec of just what it is you're doing a hash on. */
- if (!(buf = ast_str_thread_get(&check_auth_buf, CHECK_AUTH_BUF_INITLEN))) {
- return AUTH_SECRET_FAILED; /*! XXX \todo need a better return code here */
- }
- /* Make a copy of the response and parse it */
- res = ast_str_set(&buf, 0, "%s", authtoken);
- if (res == AST_DYNSTR_BUILD_FAILED) {
- return AUTH_SECRET_FAILED; /*! XXX \todo need a better return code here */
- }
- c = ast_str_buffer(buf);
- sip_digest_parser(c, keys);
- /* We cannot rely on the bogus_peer having a bad md5 value. Someone could
- * use it to construct valid auth. */
- if (md5secret && strcmp(md5secret, BOGUS_PEER_MD5SECRET) == 0) {
- is_bogus_peer = 1;
- }
- /* Verify that digest username matches the username we auth as */
- if (strcmp(username, keys[K_USER].s) && !is_bogus_peer) {
- ast_log(LOG_WARNING, "username mismatch, have <%s>, digest has <%s>\n",
- username, keys[K_USER].s);
- /* Oops, we're trying something here */
- return AUTH_USERNAME_MISMATCH;
- }
- /* Verify nonce from request matches our nonce, and the nonce has not already been responded to.
- * If this check fails, send 401 with new nonce */
- if (strcasecmp(p->nonce, keys[K_NONCE].s) || p->stalenonce) { /* XXX it was 'n'casecmp ? */
- wrongnonce = TRUE;
- usednonce = keys[K_NONCE].s;
- } else {
- p->stalenonce = 1; /* now, since the nonce has a response, mark it as stale so it can't be sent or responded to again */
- }
- if (!ast_strlen_zero(md5secret)) {
- ast_copy_string(a1_hash, md5secret, sizeof(a1_hash));
- } else {
- char a1[256];
- snprintf(a1, sizeof(a1), "%s:%s:%s", username, p->realm, secret);
- ast_md5_hash(a1_hash, a1);
- }
- /* compute the expected response to compare with what we received */
- {
- char a2[256];
- char a2_hash[256];
- char resp[256];
- snprintf(a2, sizeof(a2), "%s:%s", sip_methods[sipmethod].text,
- S_OR(keys[K_URI].s, uri));
- ast_md5_hash(a2_hash, a2);
- snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, usednonce, a2_hash);
- ast_md5_hash(resp_hash, resp);
- }
- good_response = keys[K_RESP].s &&
- !strncasecmp(keys[K_RESP].s, resp_hash, strlen(resp_hash)) &&
- !is_bogus_peer; /* lastly, check that the peer isn't the fake peer */
- if (wrongnonce) {
- if (good_response) {
- if (sipdebug)
- ast_log(LOG_NOTICE, "Correct auth, but based on stale nonce received from '%s'\n", sip_get_header(req, "From"));
- /* We got working auth token, based on stale nonce . */
- build_nonce(p, 0);
- transmit_response_with_auth(p, response, req, p->nonce, reliable, respheader, TRUE);
- } else {
- /* Everything was wrong, so give the device one more try with a new challenge */
- if (!req->ignore) {
- if (sipdebug) {
- ast_log(LOG_NOTICE, "Bad authentication received from '%s'\n", sip_get_header(req, "To"));
- }
- build_nonce(p, 1);
- } else {
- if (sipdebug) {
- ast_log(LOG_NOTICE, "Duplicate authentication received from '%s'\n", sip_get_header(req, "To"));
- }
- }
- transmit_response_with_auth(p, response, req, p->nonce, reliable, respheader, FALSE);
- }
- /* Schedule auto destroy in 32 seconds */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return AUTH_CHALLENGE_SENT;
- }
- if (good_response) {
- append_history(p, "AuthOK", "Auth challenge successful for %s", username);
- return AUTH_SUCCESSFUL;
- }
- /* Ok, we have a bad username/secret pair */
- /* Tell the UAS not to re-send this authentication data, because
- it will continue to fail
- */
- return AUTH_SECRET_FAILED;
- }
- /*! \brief Change onhold state of a peer using a pvt structure */
- static void sip_peer_hold(struct sip_pvt *p, int hold)
- {
- if (!p->relatedpeer) {
- return;
- }
- /* If they put someone on hold, increment the value... otherwise decrement it */
- ast_atomic_fetchadd_int(&p->relatedpeer->onhold, (hold ? +1 : -1));
- /* Request device state update */
- ast_devstate_changed(AST_DEVICE_UNKNOWN, (ast_test_flag(ast_channel_flags(p->owner), AST_FLAG_DISABLE_DEVSTATE_CACHE) ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE),
- "SIP/%s", p->relatedpeer->name);
- return;
- }
- /*! \brief Receive MWI events that we have subscribed to */
- static void mwi_event_cb(const struct ast_event *event, void *userdata)
- {
- struct sip_peer *peer = userdata;
- sip_send_mwi_to_peer(peer, 0);
- }
- static void network_change_event_subscribe(void)
- {
- if (!network_change_event_subscription) {
- network_change_event_subscription = ast_event_subscribe(AST_EVENT_NETWORK_CHANGE,
- network_change_event_cb, "SIP Network Change", NULL, AST_EVENT_IE_END);
- }
- }
- static void network_change_event_unsubscribe(void)
- {
- if (network_change_event_subscription) {
- network_change_event_subscription = ast_event_unsubscribe(network_change_event_subscription);
- }
- }
- static void acl_change_event_subscribe(void)
- {
- if (!acl_change_event_subscription) {
- acl_change_event_subscription = ast_event_subscribe(AST_EVENT_ACL_CHANGE,
- acl_change_event_cb, "Manager must react to Named ACL changes", NULL, AST_EVENT_IE_END);
- }
- }
- static void acl_change_event_unsubscribe(void)
- {
- if (acl_change_event_subscription) {
- acl_change_event_subscription = ast_event_unsubscribe(acl_change_event_subscription);
- }
- }
- static int network_change_event_sched_cb(const void *data)
- {
- network_change_event_sched_id = -1;
- sip_send_all_registers();
- sip_send_all_mwi_subscriptions();
- return 0;
- }
- static void network_change_event_cb(const struct ast_event *event, void *userdata)
- {
- ast_debug(1, "SIP, got a network change event, renewing all SIP registrations.\n");
- if (network_change_event_sched_id == -1) {
- network_change_event_sched_id = ast_sched_add(sched, 1000, network_change_event_sched_cb, NULL);
- }
- }
- static void cb_extensionstate_destroy(int id, void *data)
- {
- struct sip_pvt *p = data;
- dialog_unref(p, "the extensionstate containing this dialog ptr was destroyed");
- }
- /*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
- \note If you add an "hint" priority to the extension in the dial plan,
- you will get notifications on device state changes */
- static int extensionstate_update(const char *context, const char *exten, struct state_notify_data *data, struct sip_pvt *p, int force)
- {
- sip_pvt_lock(p);
- switch (data->state) {
- case AST_EXTENSION_DEACTIVATED: /* Retry after a while */
- case AST_EXTENSION_REMOVED: /* Extension is gone */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); /* Delete subscription in 32 secs */
- ast_verb(2, "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, data->state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username);
- p->subscribed = NONE;
- append_history(p, "Subscribestatus", "%s", data->state == AST_EXTENSION_REMOVED ? "HintRemoved" : "Deactivated");
- break;
- default: /* Tell user */
- if (force) {
- /* we must skip the next two checks for a queued state change or resubscribe */
- } else if ((p->laststate == data->state && (~data->state & AST_EXTENSION_RINGING)) &&
- (p->last_presence_state == data->presence_state &&
- !strcmp(p->last_presence_subtype, data->presence_subtype) &&
- !strcmp(p->last_presence_message, data->presence_message))) {
- /* don't notify unchanged state or unchanged early-state causing parties again */
- sip_pvt_unlock(p);
- return 0;
- } else if (data->state & AST_EXTENSION_RINGING) {
- /* check if another channel than last time is ringing now to be notified */
- struct ast_channel *ringing = find_ringing_channel(data->device_state_info, p);
- if (ringing) {
- if (!ast_tvcmp(ast_channel_creationtime(ringing), p->last_ringing_channel_time)) {
- /* we assume here that no two channels have the exact same creation time */
- ao2_ref(ringing, -1);
- sip_pvt_unlock(p);
- return 0;
- } else {
- p->last_ringing_channel_time = ast_channel_creationtime(ringing);
- ao2_ref(ringing, -1);
- }
- }
- /* If no ringing channel was found, it doesn't necessarily indicate anything bad.
- * Likely, a device state change occurred for a custom device state, which does not
- * correspond to any channel. In such a case, just go ahead and pass the notification
- * along.
- */
- }
- /* ref before unref because the new could be the same as the old one. Don't risk destruction! */
- if (data->device_state_info) {
- ao2_ref(data->device_state_info, 1);
- }
- ao2_cleanup(p->last_device_state_info);
- p->last_device_state_info = data->device_state_info;
- p->laststate = data->state;
- p->last_presence_state = data->presence_state;
- ast_string_field_set(p, last_presence_subtype, S_OR(data->presence_subtype, ""));
- ast_string_field_set(p, last_presence_message, S_OR(data->presence_message, ""));
- break;
- }
- if (p->subscribed != NONE) { /* Only send state NOTIFY if we know the format */
- if (!p->pendinginvite) {
- transmit_state_notify(p, data, 1, FALSE);
- if (p->last_device_state_info) {
- /* We don't need the saved ref anymore, don't keep channels ref'd. */
- ao2_ref(p->last_device_state_info, -1);
- p->last_device_state_info = NULL;
- }
- } else {
- /* We already have a NOTIFY sent that is not answered. Queue the state up.
- if many state changes happen meanwhile, we will only send a notification of the last one */
- ast_set_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE);
- }
- }
- if (!force) {
- ast_verb(2, "Extension Changed %s[%s] new state %s for Notify User %s %s\n", exten, context, ast_extension_state2str(data->state), p->username,
- ast_test_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE) ? "(queued)" : "");
- }
- sip_pvt_unlock(p);
- return 0;
- }
- /*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
- \note If you add an "hint" priority to the extension in the dial plan,
- you will get notifications on device state changes */
- static int cb_extensionstate(char *context, char *exten, struct ast_state_cb_info *info, void *data)
- {
- struct sip_pvt *p = data;
- struct state_notify_data notify_data = {
- .state = info->exten_state,
- .device_state_info = info->device_state_info,
- .presence_state = info->presence_state,
- .presence_subtype = info->presence_subtype,
- .presence_message = info->presence_message,
- };
- if ((info->reason == AST_HINT_UPDATE_PRESENCE) && !(allow_notify_user_presence(p))) {
- /* ignore a presence triggered update if we know the useragent doesn't care */
- return 0;
- }
- return extensionstate_update(context, exten, ¬ify_data, p, FALSE);
- }
- /*! \brief Send a fake 401 Unauthorized response when the administrator
- wants to hide the names of local devices from fishers
- */
- static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable)
- {
- /* We have to emulate EXACTLY what we'd get with a good peer
- * and a bad password, or else we leak information. */
- const char *response = "401 Unauthorized";
- const char *reqheader = "Authorization";
- const char *respheader = "WWW-Authenticate";
- const char *authtoken;
- struct ast_str *buf;
- char *c;
- /* table of recognised keywords, and their value in the digest */
- enum keys { K_NONCE, K_LAST };
- struct x {
- const char *key;
- const char *s;
- } *i, keys[] = {
- [K_NONCE] = { "nonce=", "" },
- [K_LAST] = { NULL, NULL}
- };
- authtoken = sip_get_header(req, reqheader);
- if (req->ignore && !ast_strlen_zero(p->nonce) && ast_strlen_zero(authtoken)) {
- /* This is a retransmitted invite/register/etc, don't reconstruct authentication
- * information */
- transmit_response_with_auth(p, response, req, p->nonce, reliable, respheader, 0);
- /* Schedule auto destroy in 32 seconds (according to RFC 3261) */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- } else if (ast_strlen_zero(p->nonce) || ast_strlen_zero(authtoken)) {
- /* We have no auth, so issue challenge and request authentication */
- build_nonce(p, 1);
- transmit_response_with_auth(p, response, req, p->nonce, reliable, respheader, 0);
- /* Schedule auto destroy in 32 seconds */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- }
- if (!(buf = ast_str_thread_get(&check_auth_buf, CHECK_AUTH_BUF_INITLEN))) {
- __transmit_response(p, "403 Forbidden", &p->initreq, reliable);
- return;
- }
- /* Make a copy of the response and parse it */
- if (ast_str_set(&buf, 0, "%s", authtoken) == AST_DYNSTR_BUILD_FAILED) {
- __transmit_response(p, "403 Forbidden", &p->initreq, reliable);
- return;
- }
- c = ast_str_buffer(buf);
- while (c && *(c = ast_skip_blanks(c))) { /* lookup for keys */
- for (i = keys; i->key != NULL; i++) {
- const char *separator = ","; /* default */
- if (strncasecmp(c, i->key, strlen(i->key)) != 0) {
- continue;
- }
- /* Found. Skip keyword, take text in quotes or up to the separator. */
- c += strlen(i->key);
- if (*c == '"') { /* in quotes. Skip first and look for last */
- c++;
- separator = "\"";
- }
- i->s = c;
- strsep(&c, separator);
- break;
- }
- if (i->key == NULL) { /* not found, jump after space or comma */
- strsep(&c, " ,");
- }
- }
- /* Verify nonce from request matches our nonce. If not, send 401 with new nonce */
- if (strcasecmp(p->nonce, keys[K_NONCE].s)) {
- if (!req->ignore) {
- build_nonce(p, 1);
- }
- transmit_response_with_auth(p, response, req, p->nonce, reliable, respheader, FALSE);
- /* Schedule auto destroy in 32 seconds */
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- } else {
- __transmit_response(p, "403 Forbidden", &p->initreq, reliable);
- }
- }
- /*!
- * Terminate the uri at the first ';' or space.
- * Technically we should ignore escaped space per RFC3261 (19.1.1 etc)
- * but don't do it for the time being. Remember the uri format is:
- * (User-parameters was added after RFC 3261)
- *\verbatim
- *
- * sip:user:password;user-parameters@host:port;uri-parameters?headers
- * sips:user:password;user-parameters@host:port;uri-parameters?headers
- *
- *\endverbatim
- * \todo As this function does not support user-parameters, it's considered broken
- * and needs fixing.
- */
- static char *terminate_uri(char *uri)
- {
- char *t = uri;
- while (*t && *t > ' ' && *t != ';') {
- t++;
- }
- *t = '\0';
- return uri;
- }
- /*! \brief Terminate a host:port at the ':'
- * \param hostport The address of the hostport string
- *
- * \note In the case of a bracket-enclosed IPv6 address, the hostport variable
- * will contain the non-bracketed host as a result of calling this function.
- */
- static void extract_host_from_hostport(char **hostport)
- {
- char *dont_care;
- ast_sockaddr_split_hostport(*hostport, hostport, &dont_care, PARSE_PORT_IGNORE);
- }
- /*! \internal \brief Helper function to update a peer's lastmsgssent value
- */
- static void update_peer_lastmsgssent(struct sip_peer *peer, int value, int locked)
- {
- if (!locked) {
- ao2_lock(peer);
- }
- peer->lastmsgssent = value;
- if (!locked) {
- ao2_unlock(peer);
- }
- }
- /*! \brief Verify registration of user
- - Registration is done in several steps, first a REGISTER without auth
- to get a challenge (nonce) then a second one with auth
- - Registration requests are only matched with peers that are marked as "dynamic"
- */
- static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sockaddr *addr,
- struct sip_request *req, const char *uri)
- {
- enum check_auth_result res = AUTH_NOT_FOUND;
- struct sip_peer *peer;
- char tmp[256];
- char *c, *name, *unused_password, *domain;
- char *uri2 = ast_strdupa(uri);
- int send_mwi = 0;
- terminate_uri(uri2);
- ast_copy_string(tmp, sip_get_header(req, "To"), sizeof(tmp));
- c = get_in_brackets(tmp);
- c = remove_uri_parameters(c);
- if (parse_uri_legacy_check(c, "sip:,sips:", &name, &unused_password, &domain, NULL)) {
- ast_log(LOG_NOTICE, "Invalid to address: '%s' from %s (missing sip:) trying to use anyway...\n", c, ast_sockaddr_stringify_addr(addr));
- return -1;
- }
- SIP_PEDANTIC_DECODE(name);
- SIP_PEDANTIC_DECODE(domain);
- extract_host_from_hostport(&domain);
- if (ast_strlen_zero(domain)) {
- /* <sip:name@[EMPTY]>, never good */
- transmit_response(p, "404 Not found", &p->initreq);
- return AUTH_UNKNOWN_DOMAIN;
- }
- if (ast_strlen_zero(name)) {
- /* <sip:[EMPTY][@]hostport>, unsure whether valid for
- * registration. RFC 3261, 10.2 states:
- * "The To header field and the Request-URI field typically
- * differ, as the former contains a user name."
- * But, Asterisk has always treated the domain-only uri as a
- * username: we allow admins to create accounts described by
- * domain name. */
- name = domain;
- }
- /* This here differs from 1.4 and 1.6: the domain matching ACLs were
- * skipped if it was a domain-only URI (used as username). Here we treat
- * <sip:hostport> as <sip:host@hostport> and won't forget to test the
- * domain ACLs against host. */
- if (!AST_LIST_EMPTY(&domain_list)) {
- if (!check_sip_domain(domain, NULL, 0)) {
- if (sip_cfg.alwaysauthreject) {
- transmit_fake_auth_response(p, &p->initreq, XMIT_UNRELIABLE);
- } else {
- transmit_response(p, "404 Not found (unknown domain)", &p->initreq);
- }
- return AUTH_UNKNOWN_DOMAIN;
- }
- }
- ast_string_field_set(p, exten, name);
- build_contact(p, req, 1);
- if (req->ignore) {
- /* Expires is a special case, where we only want to load the peer if this isn't a deregistration attempt */
- const char *expires = sip_get_header(req, "Expires");
- int expire = atoi(expires);
- if (ast_strlen_zero(expires)) { /* No expires header; look in Contact */
- if ((expires = strcasestr(sip_get_header(req, "Contact"), ";expires="))) {
- expire = atoi(expires + 9);
- }
- }
- if (!ast_strlen_zero(expires) && expire == 0) {
- transmit_response_with_date(p, "200 OK", req);
- return 0;
- }
- }
- peer = sip_find_peer(name, NULL, TRUE, FINDPEERS, FALSE, 0);
- /* If we don't want username disclosure, use the bogus_peer when a user
- * is not found. */
- if (!peer && sip_cfg.alwaysauthreject && sip_cfg.autocreatepeer == AUTOPEERS_DISABLED) {
- peer = bogus_peer;
- sip_ref_peer(peer, "register_verify: ref the bogus_peer");
- }
- if (!(peer && ast_apply_acl(peer->acl, addr, "SIP Peer ACL: "))) {
- /* Peer fails ACL check */
- if (peer) {
- sip_unref_peer(peer, "register_verify: sip_unref_peer: from sip_find_peer operation");
- peer = NULL;
- res = AUTH_ACL_FAILED;
- } else {
- res = AUTH_NOT_FOUND;
- }
- }
- if (peer) {
- ao2_lock(peer);
- if (!peer->host_dynamic) {
- ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name);
- res = AUTH_PEER_NOT_DYNAMIC;
- } else {
- set_peer_nat(p, peer);
- ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_NAT_FORCE_RPORT);
- if (!(res = check_auth(p, req, peer->name, peer->secret, peer->md5secret, SIP_REGISTER, uri2, XMIT_UNRELIABLE))) {
- if (sip_cancel_destroy(p))
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- if (check_request_transport(peer, req)) {
- ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
- transmit_response_with_date(p, "403 Forbidden", req);
- res = AUTH_BAD_TRANSPORT;
- } else {
- /* We have a successful registration attempt with proper authentication,
- now, update the peer */
- switch (parse_register_contact(p, peer, req)) {
- case PARSE_REGISTER_DENIED:
- ast_log(LOG_WARNING, "Registration denied because of contact ACL\n");
- transmit_response_with_date(p, "603 Denied", req);
- res = 0;
- break;
- case PARSE_REGISTER_FAILED:
- ast_log(LOG_WARNING, "Failed to parse contact info\n");
- transmit_response_with_date(p, "400 Bad Request", req);
- res = 0;
- break;
- case PARSE_REGISTER_QUERY:
- ast_string_field_set(p, fullcontact, peer->fullcontact);
- transmit_response_with_date(p, "200 OK", req);
- res = 0;
- send_mwi = 1;
- break;
- case PARSE_REGISTER_UPDATE:
- ast_string_field_set(p, fullcontact, peer->fullcontact);
- /* If expiry is 0, peer has been unregistered already */
- if (p->expiry != 0) {
- update_peer(peer, p->expiry);
- }
- /* Say OK and ask subsystem to retransmit msg counter */
- transmit_response_with_date(p, "200 OK", req);
- send_mwi = 1;
- res = 0;
- break;
- }
- }
- }
- }
- ao2_unlock(peer);
- }
- if (!peer && sip_cfg.autocreatepeer != AUTOPEERS_DISABLED) {
- /* Create peer if we have autocreate mode enabled */
- peer = temp_peer(name);
- if (peer) {
- ao2_t_link(peers, peer, "link peer into peer table");
- if (!ast_sockaddr_isnull(&peer->addr)) {
- ao2_t_link(peers_by_ip, peer, "link peer into peers-by-ip table");
- }
- ao2_lock(peer);
- if (sip_cancel_destroy(p))
- ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
- switch (parse_register_contact(p, peer, req)) {
- case PARSE_REGISTER_DENIED:
- ast_log(LOG_WARNING, "Registration denied because of contact ACL\n");
- transmit_response_with_date(p, "403 Forbidden", req);
- res = 0;
- break;
- case PARSE_REGISTER_FAILED:
- ast_log(LOG_WARNING, "Failed to parse contact info\n");
- transmit_response_with_date(p, "400 Bad Request", req);
- res = 0;
- break;
- case PARSE_REGISTER_QUERY:
- ast_string_field_set(p, fullcontact, peer->fullcontact);
- transmit_response_with_date(p, "200 OK", req);
- send_mwi = 1;
- res = 0;
- break;
- case PARSE_REGISTER_UPDATE:
- ast_string_field_set(p, fullcontact, peer->fullcontact);
- /* Say OK and ask subsystem to retransmit msg counter */
- transmit_response_with_date(p, "200 OK", req);
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Registered\r\nAddress: %s\r\n", peer->name, ast_sockaddr_stringify(addr));
- 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) {
- 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));
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus",
- "ChannelType: SIP\r\n"
- "Peer: SIP/%s\r\n"
- "PeerStatus: Rejected\r\n"
- "Cause: AUTH_SECRET_FAILED\r\n"
- "Address: %s\r\n"
- "Port: %s\r\n",
- name, peer_addr, 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));
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus",
- "ChannelType: SIP\r\n"
- "Peer: SIP/%s\r\n"
- "PeerStatus: Rejected\r\n"
- "Cause: %s\r\n"
- "Address: %s\r\n"
- "Port: %s\r\n",
- name,
- res == AUTH_PEER_NOT_DYNAMIC ? "AUTH_PEER_NOT_DYNAMIC" : "URI_NOT_FOUND",
- peer_addr, 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));
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus",
- "ChannelType: SIP\r\n"
- "Peer: SIP/%s\r\n"
- "PeerStatus: Rejected\r\n"
- "Cause: AUTH_PEER_NOT_DYNAMIC\r\n"
- "Address: %s\r\n"
- "Port: %s\r\n",
- name, peer_addr, 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));
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus",
- "ChannelType: SIP\r\n"
- "Peer: SIP/%s\r\n"
- "PeerStatus: Rejected\r\n"
- "Cause: %s\r\n"
- "Address: %s\r\n"
- "Port: %s\r\n",
- name,
- (res == AUTH_USERNAME_MISMATCH) ? "AUTH_USERNAME_MISMATCH" : "URI_NOT_FOUND",
- peer_addr, peer_port);
- }
- }
- }
- break;
- case AUTH_BAD_TRANSPORT:
- default:
- break;
- }
- }
- 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 */
- static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason)
- {
- 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 (reason && !ast_strlen_zero(reason_param)) {
- *reason = sip_reason_str_to_code(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;
- 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:", &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 (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";
- }
- ast_string_field_set(p, domain, domain);
- /* 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:", &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, ast_pickup_ext())) {
- 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, ast_pickup_ext(), 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 Lock dialog lock and find matching pvt lock
- \return a reference, remember to release it when done
- */
- static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag)
- {
- 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 */
- sip_pvt_lock(sip_pvt_ptr);
- if (sip_cfg.pedanticsipchecking) {
- unsigned char frommismatch = 0, tomismatch = 0;
- if (ast_strlen_zero(fromtag)) {
- sip_pvt_unlock(sip_pvt_ptr);
- 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 NULL;
- }
- if (ast_strlen_zero(totag)) {
- sip_pvt_unlock(sip_pvt_ptr);
- 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 NULL;
- }
- /* 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) {
- sip_pvt_unlock(sip_pvt_ptr);
- 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 NULL;
- }
- }
- 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);
- /* deadlock avoidance... */
- while (sip_pvt_ptr->owner && ast_channel_trylock(sip_pvt_ptr->owner)) {
- sip_pvt_unlock(sip_pvt_ptr);
- usleep(1);
- sip_pvt_lock(sip_pvt_ptr);
- }
- }
-
- return sip_pvt_ptr;
- }
- /*! \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) {
- struct ast_channel *peer = ast_bridged_channel(transferer->owner);
- if (peer) {
- pbx_builtin_setvar_helper(peer, "SIPREFERRINGCONTEXT", transferer->context);
- pbx_builtin_setvar_helper(peer, "SIPREFERREDBYHDR", p_referred_by);
- }
- }
- 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, referred_by_uri);
- }
- /* We are ready to transfer to the extension */
- return 0;
- }
- if (sip_debug_test_pvt(transferer))
- ast_verbose("Failed SIP Transfer to non-existing extension %s in context %s\n n", refer_to, transfer_context);
- /* Failure, we can't find this extension */
- return -1;
- }
- /*! \brief Call transfer support (old way, deprecated by the IETF)
- * \note does not account for SIPS: uri requirements, nor check transport
- */
- static int get_also_info(struct sip_pvt *p, struct sip_request *oreq)
- {
- char tmp[256] = "", *c, *a;
- struct sip_request *req = oreq ? oreq : &p->initreq;
- struct sip_refer *refer = NULL;
- const char *transfer_context = NULL;
- if (!sip_refer_alloc(p)) {
- return -1;
- }
- refer = p->refer;
- ast_copy_string(tmp, sip_get_header(req, "Also"), sizeof(tmp));
- c = get_in_brackets(tmp);
- if (parse_uri_legacy_check(c, "sip:,sips:", &c, NULL, &a, NULL)) {
- ast_log(LOG_WARNING, "Huh? Not a SIP header in Also: transfer (%s)?\n", c);
- return -1;
- }
- SIP_PEDANTIC_DECODE(c);
- SIP_PEDANTIC_DECODE(a);
- if (!ast_strlen_zero(a)) {
- ast_string_field_set(refer, refer_to_domain, a);
- }
- if (sip_debug_test_pvt(p))
- ast_verbose("Looking for %s in %s\n", c, p->context);
- /* Determine transfer context */
- if (p->owner) {
- /* By default, use the context in the channel sending the REFER */
- transfer_context = pbx_builtin_getvar_helper(p->owner, "TRANSFER_CONTEXT");
- if (ast_strlen_zero(transfer_context)) {
- transfer_context = ast_channel_macrocontext(p->owner);
- }
- }
- if (ast_strlen_zero(transfer_context)) {
- transfer_context = S_OR(p->context, sip_cfg.default_context);
- }
- if (ast_exists_extension(NULL, transfer_context, c, 1, NULL)) {
- /* This is a blind transfer */
- ast_debug(1, "SIP Bye-also transfer to Extension %s@%s \n", c, transfer_context);
- ast_string_field_set(refer, refer_to, c);
- ast_string_field_set(refer, referred_by, "");
- ast_string_field_set(refer, refer_contact, "");
- /* Set new context */
- ast_string_field_set(p, context, transfer_context);
- return 0;
- } else if (ast_canmatch_extension(NULL, p->context, c, 1, NULL)) {
- return 1;
- }
- return -1;
- }
- /*! \brief Set the peers nat flags if they are using auto_* settings */
- static void set_peer_nat(const struct sip_pvt *p, struct sip_peer *peer)
- {
- if (!p || !peer) {
- return;
- }
- if (ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
- if (p->natdetected) {
- ast_set_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT);
- } else {
- ast_clear_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT);
- }
- }
- if (ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) {
- if (p->natdetected) {
- ast_set_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP);
- } else {
- ast_clear_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP);
- }
- }
- }
- /*! \brief Check and see if the requesting UA is likely to be behind a NAT.
- *
- * If the requesting NAT is behind NAT, set the * natdetected flag so that
- * later, peers with nat=auto_* can use the value. Also, set the flags so
- * that Asterisk responds identically whether or not a peer exists so as
- * not to leak peer name information.
- */
- static void check_for_nat(const struct ast_sockaddr *addr, struct sip_pvt *p)
- {
- if (!addr || !p) {
- return;
- }
- if (ast_sockaddr_cmp_addr(addr, &p->recv)) {
- char *tmp_str = ast_strdupa(ast_sockaddr_stringify_addr(addr));
- ast_debug(3, "NAT detected for %s / %s\n", tmp_str, ast_sockaddr_stringify_addr(&p->recv));
- p->natdetected = 1;
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
- ast_set_flag(&p->flags[0], SIP_NAT_FORCE_RPORT);
- }
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) {
- ast_set_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP);
- }
- } else {
- p->natdetected = 0;
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
- ast_clear_flag(&p->flags[0], SIP_NAT_FORCE_RPORT);
- }
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) {
- ast_clear_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP);
- }
- }
- }
- /*! \brief check Via: header for hostname, port and rport request/answer */
- static void check_via(struct sip_pvt *p, const struct sip_request *req)
- {
- char via[512];
- char *c, *maddr;
- struct ast_sockaddr tmp = { { 0, } };
- uint16_t port;
- ast_copy_string(via, sip_get_header(req, "Via"), sizeof(via));
- /* If this is via WebSocket we don't use the Via header contents at all */
- if (!strncasecmp(via, "SIP/2.0/WS", 10)) {
- return;
- }
- /* Work on the leftmost value of the topmost Via header */
- c = strchr(via, ',');
- if (c)
- *c = '\0';
- /* Check for rport */
- c = strstr(via, ";rport");
- if (c && (c[6] != '=')) { /* rport query, not answer */
- ast_set_flag(&p->flags[1], SIP_PAGE2_RPORT_PRESENT);
- ast_set_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT);
- }
- /* Check for maddr */
- maddr = strstr(via, "maddr=");
- if (maddr) {
- maddr += 6;
- c = maddr + strspn(maddr, "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.:[]");
- *c = '\0';
- }
- c = strchr(via, ';');
- if (c)
- *c = '\0';
- c = strchr(via, ' ');
- if (c) {
- *c = '\0';
- c = ast_skip_blanks(c+1);
- if (strcasecmp(via, "SIP/2.0/UDP") && strcasecmp(via, "SIP/2.0/TCP") && strcasecmp(via, "SIP/2.0/TLS")) {
- ast_log(LOG_WARNING, "Don't know how to respond via '%s'\n", via);
- return;
- }
- if (maddr && ast_sockaddr_resolve_first(&p->sa, maddr, 0)) {
- p->sa = p->recv;
- }
- if (ast_sockaddr_resolve_first(&tmp, c, 0)) {
- ast_log(LOG_WARNING, "Could not resolve socket address for '%s'\n", c);
- port = STANDARD_SIP_PORT;
- } else if (!(port = ast_sockaddr_port(&tmp))) {
- port = STANDARD_SIP_PORT;
- ast_sockaddr_set_port(&tmp, port);
- }
- ast_sockaddr_set_port(&p->sa, port);
- check_for_nat(&tmp, p);
- if (sip_debug_test_pvt(p)) {
- ast_verbose("Sending to %s (%s)\n",
- ast_sockaddr_stringify(sip_real_dst(p)),
- sip_nat_mode(p));
- }
- }
- }
- /*! \brief Validate device authentication */
- static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
- struct sip_request *req, int sipmethod, struct ast_sockaddr *addr,
- struct sip_peer **authpeer,
- enum xmittype reliable, char *calleridname, char *uri2)
- {
- enum check_auth_result res;
- int debug = sip_debug_test_addr(addr);
- struct sip_peer *peer;
- if (sipmethod == SIP_SUBSCRIBE) {
- /* For subscribes, match on device name only; for other methods,
- * match on IP address-port of the incoming request.
- */
- peer = sip_find_peer(of, NULL, TRUE, FINDALLDEVICES, FALSE, 0);
- } else {
- /* First find devices based on username (avoid all type=peer's) */
- peer = sip_find_peer(of, NULL, TRUE, FINDUSERS, FALSE, 0);
- /* Then find devices based on IP */
- if (!peer) {
- char *uri_tmp, *callback = NULL, *dummy;
- uri_tmp = ast_strdupa(uri2);
- parse_uri(uri_tmp, "sip:,sips:", &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));
- }
- /* XXX what about p->prefs = peer->prefs; ? */
- /* Set Frame packetization */
- if (p->rtp) {
- ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &peer->prefs);
- 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_copy(p->caps, peer->caps);
- ast_format_cap_copy(p->jointcaps, peer->caps);
- p->prefs = peer->prefs;
- ast_copy_string(p->zone, peer->zone, sizeof(p->zone));
- if (peer->maxforwards > 0) {
- p->maxforwards = peer->maxforwards;
- }
- if (!(ast_format_cap_is_empty(p->peercaps))) {
- struct ast_format_cap *tmp = ast_format_cap_joint(p->jointcaps, p->peercaps);
- struct ast_format_cap *tmp2;
- if (tmp) {
- tmp2 = p->jointcaps;
- p->jointcaps = tmp;
- ast_format_cap_destroy(tmp2);
- }
- }
- 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_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &peer->prefs);
- 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;
- ast_string_field_set(p, exten, t);
- t = strchr(p->exten, '@');
- if (t)
- *t = '\0';
- if (ast_strlen_zero(p->our_contact)) {
- build_contact(p, req, 1);
- }
- }
- of = get_in_brackets(of);
- /* save the URI part of the From header */
- ast_string_field_set(p, from, of);
- if (parse_uri_legacy_check(of, "sip:,sips:", &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 in ASTOBJ, 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];
- 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);
- }
- switch (get_destination(p, NULL, NULL)) {
- 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;
- case SIP_GET_DEST_EXTEN_NOT_FOUND:
- case SIP_GET_DEST_EXTEN_MATCHMORE:
- transmit_response(p, "404 Not Found", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return;
- case SIP_GET_DEST_EXTEN_FOUND:
- 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));
- if (!ast_strlen_zero(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);
- } else {
- ast_msg_queue(msg);
- transmit_response(p, "202 Accepted", req);
- }
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- }
- /*! \brief CLI Command to show calls within limits set by call_limit */
- static char *sip_show_inuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- #define FORMAT "%-25.25s %-15.15s %-15.15s \n"
- #define FORMAT2 "%-25.25s %-15.15s %-15.15s \n"
- char ilimits[40];
- char iused[40];
- int showall = FALSE;
- struct ao2_iterator i;
- struct sip_peer *peer;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show inuse";
- e->usage =
- "Usage: sip show inuse [all]\n"
- " List all SIP devices usage counters and limits.\n"
- " Add option \"all\" to show all devices, not only those with a limit.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc < 3)
- return CLI_SHOWUSAGE;
- if (a->argc == 4 && !strcmp(a->argv[3], "all"))
- showall = TRUE;
-
- ast_cli(a->fd, FORMAT, "* Peer name", "In use", "Limit");
- i = ao2_iterator_init(peers, 0);
- while ((peer = ao2_t_iterator_next(&i, "iterate thru peer table"))) {
- ao2_lock(peer);
- if (peer->call_limit)
- snprintf(ilimits, sizeof(ilimits), "%d", peer->call_limit);
- else
- ast_copy_string(ilimits, "N/A", sizeof(ilimits));
- snprintf(iused, sizeof(iused), "%d/%d/%d", peer->inuse, peer->ringing, peer->onhold);
- if (showall || peer->call_limit)
- ast_cli(a->fd, FORMAT2, peer->name, iused, ilimits);
- ao2_unlock(peer);
- sip_unref_peer(peer, "toss iterator pointer");
- }
- ao2_iterator_destroy(&i);
- return CLI_SUCCESS;
- #undef FORMAT
- #undef FORMAT2
- }
- /*! \brief Convert transfer mode to text string */
- static char *transfermode2str(enum transfermodes mode)
- {
- if (mode == TRANSFER_OPENFORALL)
- return "open";
- else if (mode == TRANSFER_CLOSED)
- return "closed";
- return "strict";
- }
- /*! \brief Report Peer status in character string
- * \return 0 if peer is unreachable, 1 if peer is online, -1 if unmonitored
- */
- /* Session-Timer Modes */
- static const struct _map_x_s stmodes[] = {
- { SESSION_TIMER_MODE_ACCEPT, "Accept"},
- { SESSION_TIMER_MODE_ORIGINATE, "Originate"},
- { SESSION_TIMER_MODE_REFUSE, "Refuse"},
- { -1, NULL},
- };
- static const char *stmode2str(enum st_mode m)
- {
- return map_x_s(stmodes, m, "Unknown");
- }
- static enum st_mode str2stmode(const char *s)
- {
- return map_s_x(stmodes, s, -1);
- }
- /* Session-Timer Refreshers */
- static const struct _map_x_s strefresher_params[] = {
- { SESSION_TIMER_REFRESHER_PARAM_UNKNOWN, "unknown" },
- { SESSION_TIMER_REFRESHER_PARAM_UAC, "uac" },
- { SESSION_TIMER_REFRESHER_PARAM_UAS, "uas" },
- { -1, NULL },
- };
- static const struct _map_x_s strefreshers[] = {
- { SESSION_TIMER_REFRESHER_AUTO, "auto" },
- { SESSION_TIMER_REFRESHER_US, "us" },
- { SESSION_TIMER_REFRESHER_THEM, "them" },
- { -1, NULL },
- };
- static const char *strefresherparam2str(enum st_refresher_param r)
- {
- return map_x_s(strefresher_params, r, "Unknown");
- }
- static enum st_refresher_param str2strefresherparam(const char *s)
- {
- return map_s_x(strefresher_params, s, -1);
- }
- /* Autocreatepeer modes */
- static struct _map_x_s autopeermodes[] = {
- { AUTOPEERS_DISABLED, "Off"},
- { AUTOPEERS_VOLATILE, "Volatile"},
- { AUTOPEERS_PERSIST, "Persisted"},
- { -1, NULL},
- };
- static const char *strefresher2str(enum st_refresher r)
- {
- return map_x_s(strefreshers, r, "Unknown");
- }
- static const char *autocreatepeer2str(enum autocreatepeer_mode r)
- {
- return map_x_s(autopeermodes, r, "Unknown");
- }
- static int peer_status(struct sip_peer *peer, char *status, int statuslen)
- {
- int res = 0;
- if (peer->maxms) {
- if (peer->lastms < 0) {
- ast_copy_string(status, "UNREACHABLE", statuslen);
- } else if (peer->lastms > peer->maxms) {
- snprintf(status, statuslen, "LAGGED (%d ms)", peer->lastms);
- res = 1;
- } else if (peer->lastms) {
- snprintf(status, statuslen, "OK (%d ms)", peer->lastms);
- res = 1;
- } else {
- ast_copy_string(status, "UNKNOWN", statuslen);
- }
- } else {
- ast_copy_string(status, "Unmonitored", statuslen);
- /* Checking if port is 0 */
- res = -1;
- }
- return res;
- }
- /*! \brief Show active TCP connections */
- static char *sip_show_tcp(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct sip_threadinfo *th;
- struct ao2_iterator i;
- #define FORMAT2 "%-47.47s %9.9s %6.6s\n"
- #define FORMAT "%-47.47s %-9.9s %-6.6s\n"
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show tcp";
- e->usage =
- "Usage: sip show tcp\n"
- " Lists all active TCP/TLS sessions.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
- ast_cli(a->fd, FORMAT2, "Address", "Transport", "Type");
- i = ao2_iterator_init(threadt, 0);
- while ((th = ao2_t_iterator_next(&i, "iterate through tcp threads for 'sip show tcp'"))) {
- ast_cli(a->fd, FORMAT,
- ast_sockaddr_stringify(&th->tcptls_session->remote_address),
- sip_get_transport(th->type),
- (th->tcptls_session->client ? "Client" : "Server"));
- ao2_t_ref(th, -1, "decrement ref from iterator");
- }
- ao2_iterator_destroy(&i);
- return CLI_SUCCESS;
- #undef FORMAT
- #undef FORMAT2
- }
- /*! \brief CLI Command 'SIP Show Users' */
- static char *sip_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- regex_t regexbuf;
- int havepattern = FALSE;
- struct ao2_iterator user_iter;
- struct sip_peer *user;
- #define FORMAT "%-25.25s %-15.15s %-15.15s %-15.15s %-5.5s%-10.10s\n"
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show users";
- e->usage =
- "Usage: sip show users [like <pattern>]\n"
- " Lists all known SIP users.\n"
- " Optional regular expression pattern is used to filter the user list.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- switch (a->argc) {
- case 5:
- if (!strcasecmp(a->argv[3], "like")) {
- if (regcomp(®exbuf, a->argv[4], REG_EXTENDED | REG_NOSUB))
- return CLI_SHOWUSAGE;
- havepattern = TRUE;
- } else
- return CLI_SHOWUSAGE;
- case 3:
- break;
- default:
- return CLI_SHOWUSAGE;
- }
- ast_cli(a->fd, FORMAT, "Username", "Secret", "Accountcode", "Def.Context", "ACL", "Forcerport");
- user_iter = ao2_iterator_init(peers, 0);
- while ((user = ao2_t_iterator_next(&user_iter, "iterate thru peers table"))) {
- ao2_lock(user);
- if (!(user->type & SIP_TYPE_USER)) {
- ao2_unlock(user);
- sip_unref_peer(user, "sip show users");
- continue;
- }
- if (havepattern && regexec(®exbuf, user->name, 0, NULL, 0)) {
- ao2_unlock(user);
- sip_unref_peer(user, "sip show users");
- continue;
- }
- ast_cli(a->fd, FORMAT, user->name,
- user->secret,
- user->accountcode,
- user->context,
- AST_CLI_YESNO(ast_acl_list_is_empty(user->acl) == 0),
- AST_CLI_YESNO(ast_test_flag(&user->flags[0], SIP_NAT_FORCE_RPORT)));
- ao2_unlock(user);
- sip_unref_peer(user, "sip show users");
- }
- ao2_iterator_destroy(&user_iter);
- if (havepattern)
- regfree(®exbuf);
- return CLI_SUCCESS;
- #undef FORMAT
- }
- /*! \brief Show SIP registrations in the manager API */
- static int manager_show_registry(struct mansession *s, const struct message *m)
- {
- const char *id = astman_get_header(m, "ActionID");
- char idtext[256] = "";
- int total = 0;
- if (!ast_strlen_zero(id))
- snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
- astman_send_listack(s, m, "Registrations will follow", "start");
- ASTOBJ_CONTAINER_TRAVERSE(®l, 1, do {
- ASTOBJ_RDLOCK(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);
- ASTOBJ_UNLOCK(iterator);
- total++;
- } while(0));
- 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)
- {
- char tmp[256];
-
- 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");
- ast_cli(a->fd, "-= Registry objects: %d =-\n\n", regobjs);
- ASTOBJ_CONTAINER_DUMP(a->fd, tmp, sizeof(tmp), ®l);
- ast_cli(a->fd, "-= Dialog objects:\n\n");
- ao2_t_callback(dialogs, OBJ_NODATA, dialog_dump_func, a, "initiate ao2_callback to dump dialogs");
- return CLI_SUCCESS;
- }
- /*! \brief Print call group and pickup group */
- static void print_group(int fd, ast_group_t group, int crlf)
- {
- char buf[256];
- ast_cli(fd, crlf ? "%s\r\n" : "%s\n", ast_print_group(buf, sizeof(buf), group) );
- }
- /*! \brief Print named call groups and pickup groups */
- static void print_named_groups(int fd, struct ast_namedgroups *group, int crlf)
- {
- struct ast_str *buf = ast_str_create(1024);
- if (buf) {
- ast_cli(fd, crlf ? "%s\r\n" : "%s\n", ast_print_namedgroups(&buf, group) );
- ast_free(buf);
- }
- }
- /*! \brief mapping between dtmf flags and strings */
- static const struct _map_x_s dtmfstr[] = {
- { SIP_DTMF_RFC2833, "rfc2833" },
- { SIP_DTMF_INFO, "info" },
- { SIP_DTMF_SHORTINFO, "shortinfo" },
- { SIP_DTMF_INBAND, "inband" },
- { SIP_DTMF_AUTO, "auto" },
- { -1, NULL }, /* terminator */
- };
- /*! \brief Convert DTMF mode to printable string */
- static const char *dtmfmode2str(int mode)
- {
- return map_x_s(dtmfstr, mode, "<error>");
- }
- /*! \brief maps a string to dtmfmode, returns -1 on error */
- static int str2dtmfmode(const char *str)
- {
- return map_s_x(dtmfstr, str, -1);
- }
- static const struct _map_x_s insecurestr[] = {
- { SIP_INSECURE_PORT, "port" },
- { SIP_INSECURE_INVITE, "invite" },
- { SIP_INSECURE_PORT | SIP_INSECURE_INVITE, "port,invite" },
- { 0, "no" },
- { -1, NULL }, /* terminator */
- };
- /*! \brief Convert Insecure setting to printable string */
- static const char *insecure2str(int mode)
- {
- return map_x_s(insecurestr, mode, "<error>");
- }
- static const struct _map_x_s allowoverlapstr[] = {
- { SIP_PAGE2_ALLOWOVERLAP_YES, "Yes" },
- { SIP_PAGE2_ALLOWOVERLAP_DTMF, "DTMF" },
- { SIP_PAGE2_ALLOWOVERLAP_NO, "No" },
- { -1, NULL }, /* terminator */
- };
- /*! \brief Convert AllowOverlap setting to printable string */
- static const char *allowoverlap2str(int mode)
- {
- return map_x_s(allowoverlapstr, mode, "<error>");
- }
- static const struct _map_x_s trust_id_outboundstr[] = {
- { SIP_PAGE2_TRUST_ID_OUTBOUND_LEGACY, "Legacy" },
- { SIP_PAGE2_TRUST_ID_OUTBOUND_NO, "No" },
- { SIP_PAGE2_TRUST_ID_OUTBOUND_YES, "Yes" },
- { -1, NULL }, /* terminator */
- };
- static const char *trust_id_outbound2str(int mode)
- {
- return map_x_s(trust_id_outboundstr, mode, "<error>");
- }
- /*! \brief Destroy disused contexts between reloads
- Only used in reload_config so the code for regcontext doesn't get ugly
- */
- static void cleanup_stale_contexts(char *new, char *old)
- {
- char *oldcontext, *newcontext, *stalecontext, *stringp, newlist[AST_MAX_CONTEXT];
- while ((oldcontext = strsep(&old, "&"))) {
- stalecontext = NULL;
- ast_copy_string(newlist, new, sizeof(newlist));
- stringp = newlist;
- while ((newcontext = strsep(&stringp, "&"))) {
- if (!strcmp(newcontext, oldcontext)) {
- /* This is not the context you're looking for */
- stalecontext = NULL;
- break;
- } else if (strcmp(newcontext, oldcontext)) {
- stalecontext = oldcontext;
- }
-
- }
- if (stalecontext)
- ast_context_destroy(ast_context_find(stalecontext), "SIP");
- }
- }
- /*!
- * \brief Check RTP Timeout on dialogs
- *
- * \details This is used with ao2_callback to check rtptimeout
- * rtponholdtimeout and send rtpkeepalive packets.
- *
- * \return CMP_MATCH for items to be unlinked from dialogs_rtpcheck.
- */
- static int dialog_checkrtp_cb(void *dialogobj, void *arg, int flags)
- {
- struct sip_pvt *dialog = dialogobj;
- time_t *t = arg;
- int match_status;
- if (sip_pvt_trylock(dialog)) {
- return 0;
- }
- if (dialog->rtp || dialog->vrtp) {
- match_status = check_rtp_timeout(dialog, *t);
- } else {
- /* Dialog has no active RTP or VRTP. unlink it from dialogs_rtpcheck. */
- match_status = CMP_MATCH;
- }
- sip_pvt_unlock(dialog);
- return match_status;
- }
- /*!
- * \brief Match dialogs that need to be destroyed
- *
- * \details This is used with ao2_callback to unlink/delete all dialogs that
- * are marked needdestroy.
- *
- * \todo Re-work this to improve efficiency. Currently, this function is called
- * on _every_ dialog after processing _every_ incoming SIP/UDP packet, or
- * potentially even more often when the scheduler has entries to run.
- */
- static int dialog_needdestroy(void *dialogobj, void *arg, int flags)
- {
- struct sip_pvt *dialog = dialogobj;
- if (sip_pvt_trylock(dialog)) {
- /* Don't block the monitor thread. This function is called often enough
- * that we can wait for the next time around. */
- return 0;
- }
- /* If we have sessions that needs to be destroyed, do it now */
- /* Check if we have outstanding requests not responsed to or an active call
- - if that's the case, wait with destruction */
- if (dialog->needdestroy && !dialog->packets && !dialog->owner) {
- /* We absolutely cannot destroy the rtp struct while a bridge is active or we WILL crash */
- if (dialog->rtp && ast_rtp_instance_get_bridged(dialog->rtp)) {
- ast_debug(2, "Bridge still active. Delaying destruction of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text);
- sip_pvt_unlock(dialog);
- return 0;
- }
- if (dialog->vrtp && ast_rtp_instance_get_bridged(dialog->vrtp)) {
- ast_debug(2, "Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text);
- sip_pvt_unlock(dialog);
- return 0;
- }
- sip_pvt_unlock(dialog);
- /* no, the unlink should handle this: dialog_unref(dialog, "needdestroy: one more refcount decrement to allow dialog to be destroyed"); */
- /* the CMP_MATCH will unlink this dialog from the dialog hash table */
- dialog_unlink_all(dialog);
- return 0; /* the unlink_all should unlink this from the table, so.... no need to return a match */
- }
- sip_pvt_unlock(dialog);
- return 0;
- }
- /*! \brief Remove temporary realtime objects from memory (CLI) */
- /*! \todo XXXX Propably needs an overhaul after removal of the devices */
- static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct sip_peer *peer, *pi;
- int prunepeer = FALSE;
- int multi = FALSE;
- const char *name = NULL;
- regex_t regexbuf;
- int havepattern = 0;
- struct ao2_iterator i;
- static const char * const choices[] = { "all", "like", NULL };
- char *cmplt;
-
- if (cmd == CLI_INIT) {
- e->command = "sip prune realtime [peer|all]";
- e->usage =
- "Usage: sip prune realtime [peer [<name>|all|like <pattern>]|all]\n"
- " Prunes object(s) from the cache.\n"
- " Optional regular expression pattern is used to filter the objects.\n";
- return NULL;
- } else if (cmd == CLI_GENERATE) {
- if (a->pos == 4 && !strcasecmp(a->argv[3], "peer")) {
- cmplt = ast_cli_complete(a->word, choices, a->n);
- if (!cmplt)
- cmplt = complete_sip_peer(a->word, a->n - sizeof(choices), SIP_PAGE2_RTCACHEFRIENDS);
- return cmplt;
- }
- if (a->pos == 5 && !strcasecmp(a->argv[4], "like"))
- return complete_sip_peer(a->word, a->n, SIP_PAGE2_RTCACHEFRIENDS);
- return NULL;
- }
- switch (a->argc) {
- case 4:
- name = a->argv[3];
- /* we accept a name in position 3, but keywords are not good. */
- if (!strcasecmp(name, "peer") || !strcasecmp(name, "like"))
- return CLI_SHOWUSAGE;
- prunepeer = TRUE;
- if (!strcasecmp(name, "all")) {
- multi = TRUE;
- name = NULL;
- }
- /* else a single name, already set */
- break;
- case 5:
- /* sip prune realtime {peer|like} name */
- name = a->argv[4];
- if (!strcasecmp(a->argv[3], "peer"))
- prunepeer = TRUE;
- else if (!strcasecmp(a->argv[3], "like")) {
- prunepeer = TRUE;
- multi = TRUE;
- } else
- return CLI_SHOWUSAGE;
- if (!strcasecmp(name, "like"))
- return CLI_SHOWUSAGE;
- if (!multi && !strcasecmp(name, "all")) {
- multi = TRUE;
- name = NULL;
- }
- break;
- case 6:
- name = a->argv[5];
- multi = TRUE;
- /* sip prune realtime {peer} like name */
- if (strcasecmp(a->argv[4], "like"))
- return CLI_SHOWUSAGE;
- if (!strcasecmp(a->argv[3], "peer")) {
- prunepeer = TRUE;
- } else
- return CLI_SHOWUSAGE;
- break;
- default:
- return CLI_SHOWUSAGE;
- }
- if (multi && name) {
- if (regcomp(®exbuf, name, REG_EXTENDED | REG_NOSUB)) {
- return CLI_SHOWUSAGE;
- }
- havepattern = 1;
- }
- if (multi) {
- if (prunepeer) {
- int pruned = 0;
-
- i = ao2_iterator_init(peers, 0);
- while ((pi = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
- ao2_lock(pi);
- if (name && regexec(®exbuf, pi->name, 0, NULL, 0)) {
- ao2_unlock(pi);
- sip_unref_peer(pi, "toss iterator peer ptr before continue");
- continue;
- };
- if (ast_test_flag(&pi->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
- pi->the_mark = 1;
- pruned++;
- }
- ao2_unlock(pi);
- sip_unref_peer(pi, "toss iterator peer ptr");
- }
- ao2_iterator_destroy(&i);
- if (pruned) {
- unlink_marked_peers_from_tables();
- ast_cli(a->fd, "%d peers pruned.\n", pruned);
- } else
- ast_cli(a->fd, "No peers found to prune.\n");
- }
- } else {
- if (prunepeer) {
- struct sip_peer tmp;
- ast_copy_string(tmp.name, name, sizeof(tmp.name));
- if ((peer = ao2_t_find(peers, &tmp, OBJ_POINTER | OBJ_UNLINK, "finding to unlink from peers"))) {
- if (!ast_sockaddr_isnull(&peer->addr)) {
- ao2_t_unlink(peers_by_ip, peer, "unlinking peer from peers_by_ip also");
- }
- if (!ast_test_flag(&peer->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
- ast_cli(a->fd, "Peer '%s' is not a Realtime peer, cannot be pruned.\n", name);
- /* put it back! */
- ao2_t_link(peers, peer, "link peer into peer table");
- if (!ast_sockaddr_isnull(&peer->addr)) {
- ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
- }
- } else
- ast_cli(a->fd, "Peer '%s' pruned.\n", name);
- sip_unref_peer(peer, "sip_prune_realtime: sip_unref_peer: tossing temp peer ptr");
- } else
- ast_cli(a->fd, "Peer '%s' not found.\n", name);
- }
- }
- if (havepattern) {
- regfree(®exbuf);
- }
- return CLI_SUCCESS;
- }
- /*! \brief Print codec list from preference to CLI/manager */
- static void print_codec_to_cli(int fd, struct ast_codec_pref *pref)
- {
- int x;
- struct ast_format codec;
- for(x = 0; x < AST_CODEC_PREF_SIZE; x++) {
- if (!(ast_codec_pref_index(pref, x, &codec))) {
- break;
- }
- ast_cli(fd, "%s", ast_getformatname(&codec));
- ast_cli(fd, ":%d", pref->framing[x]);
- if (x < 31 && ast_codec_pref_index(pref, x + 1, &codec))
- ast_cli(fd, ",");
- }
- if (!x)
- ast_cli(fd, "none");
- }
- /*! \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;
- }
- /*! \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))) {
- sip_poke_peer(peer, 1);
- 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);
- astman_append(s, "\r\n\r\n" );
- 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%s%s",
- mailbox->mailbox,
- ast_strlen_zero(mailbox->context) ? "" : "@",
- S_OR(mailbox->context, ""),
- 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;
- char codec_buf[512];
- struct ast_codec_pref *pref;
- struct ast_variable *v;
- int x = 0, load_realtime;
- struct ast_format codec;
- 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 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_cdr_flags2str(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, " 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 : ");
- ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, peer->caps);
- ast_cli(fd, "%s\n", codec_buf);
- ast_cli(fd, " Codec Order : (");
- print_codec_to_cli(fd, &peer->prefs);
- ast_cli(fd, ")\n");
- 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);
- 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_cdr_flags2str(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: ");
- ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, peer->caps);
- astman_append(s, "%s\r\n", codec_buf);
- astman_append(s, "CodecOrder: ");
- pref = &peer->prefs;
- for(x = 0; x < AST_CODEC_PREF_SIZE ; x++) {
- if (!(ast_codec_pref_index(pref, x, &codec))) {
- break;
- }
- astman_append(s, "%s", ast_getformatname(&codec));
- if ((x < (AST_CODEC_PREF_SIZE - 1)) && ast_codec_pref_index(pref, x+1, &codec))
- astman_append(s, ",");
- }
- astman_append(s, "\r\n");
- 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_cdr_flags2str(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, " Codec Order : (");
- print_codec_to_cli(a->fd, &user->prefs);
- ast_cli(a->fd, ")\n");
- ast_cli(a->fd, " Auto-Framing: %s \n", AST_CLI_YESNO(user->autoframing));
- if (user->chanvars) {
- ast_cli(a->fd, " Variables :\n");
- for (v = user->chanvars ; v ; v = v->next)
- ast_cli(a->fd, " %s = %s\n", v->name, v->value);
- }
- ast_cli(a->fd, "\n");
- ao2_unlock(user);
- sip_unref_peer(user, "sip show user");
- } else {
- ast_cli(a->fd, "User %s not found.\n", a->argv[3]);
- ast_cli(a->fd, "\n");
- }
- return CLI_SUCCESS;
- }
- static char *sip_show_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- struct ast_str *cbuf;
- struct ast_cb_names cbnames = {
- 10,
- {
- "retrans_pkt",
- "__sip_autodestruct",
- "expire_register",
- "auto_congest",
- "sip_reg_timeout",
- "sip_poke_peer_s",
- "sip_poke_peer_now",
- "sip_poke_noanswer",
- "sip_reregister",
- "sip_reinvite_retry"
- },
- {
- retrans_pkt,
- __sip_autodestruct,
- expire_register,
- auto_congest,
- sip_reg_timeout,
- sip_poke_peer_s,
- sip_poke_peer_now,
- sip_poke_noanswer,
- sip_reregister,
- sip_reinvite_retry
- }
- };
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "sip show sched";
- e->usage =
- "Usage: sip show sched\n"
- " Shows stats on what's in the sched queue at the moment\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
- cbuf = ast_str_alloca(2048);
- ast_cli(a->fd, "\n");
- ast_sched_report(sched, &cbuf, &cbnames);
- ast_cli(a->fd, "%s", ast_str_buffer(cbuf));
- return CLI_SUCCESS;
- }
- /*! \brief Show SIP Registry (registrations with other SIP proxies */
- static char *sip_show_registry(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
- {
- #define FORMAT2 "%-39.39s %-6.6s %-12.12s %8.8s %-20.20s %-25.25s\n"
- #define FORMAT "%-39.39s %-6.6s %-12.12s %8d %-20.20s %-25.25s\n"
- char host[80];
- char user[80];
- char tmpdat[256];
- struct ast_tm tm;
- int counter = 0;
- 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");
-
- ASTOBJ_CONTAINER_TRAVERSE(®l, 1, do {
- ASTOBJ_RDLOCK(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);
- ASTOBJ_UNLOCK(iterator);
- counter++;
- } while(0));
- 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];
- int duration;
- int durh, durm, durs;
- 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_channel_cdr(c) && !ast_tvzero(ast_channel_cdr(c)->start)) {
- duration = (int)(ast_tvdiff_ms(ast_tvnow(), ast_channel_cdr(c)->start) / 1000);
- durh = duration / 3600;
- durm = (duration % 3600) / 60;
- durs = duration % 60;
- snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
- } 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;
- char codec_buf[SIPBUFSIZE];
- 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, " 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, " Call Events: %s\n", AST_CLI_ONOFF(sip_cfg.callevents));
- 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: ");
- ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, sip_cfg.caps);
- ast_cli(a->fd, "%s\n", codec_buf);
- ast_cli(a->fd, " Codec Order: ");
- print_codec_to_cli(a->fd, &default_prefs);
- ast_cli(a->fd, "\n");
- 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, " 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];
-
- 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");
-
- ASTOBJ_CONTAINER_TRAVERSE(&submwil, 1, do {
- ASTOBJ_RDLOCK(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));
- ASTOBJ_UNLOCK(iterator);
- } while(0));
- 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) : "";
- char formatbuf[SIPBUFSIZE/2];
-
- 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_getformatname_multiple(formatbuf, sizeof(formatbuf), ast_channel_nativeformats(cur->owner)) : "(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;
- }
- static const char *transport2str(enum sip_transport transport)
- {
- switch (transport) {
- case SIP_TRANSPORT_TLS:
- return "TLS";
- case SIP_TRANSPORT_UDP:
- return "UDP";
- case SIP_TRANSPORT_TCP:
- return "TCP";
- case SIP_TRANSPORT_WS:
- return "WS";
- case SIP_TRANSPORT_WSS:
- return "WSS";
- }
- return "Undefined";
- }
- /*! \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)) {
- char formatbuf[SIPBUFSIZE/2];
- 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_getformatname_multiple(formatbuf, sizeof(formatbuf), cur->caps));
- ast_cli(a->fd, " Non-Codec Capability (DTMF): %d\n", cur->noncodeccapability);
- ast_cli(a->fd, " Their Codec Capability: %s\n", ast_getformatname_multiple(formatbuf, sizeof(formatbuf), cur->peercaps));
- ast_cli(a->fd, " Joint Codec Capability: %s\n", ast_getformatname_multiple(formatbuf, sizeof(formatbuf), cur->jointcaps));
- ast_cli(a->fd, " Format: %s\n", cur->owner ? ast_getformatname_multiple(formatbuf, sizeof(formatbuf), ast_channel_nativeformats(cur->owner)) : "(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)));
- ast_cli(a->fd, " Route: ");
- if (cur->route) {
- struct sip_route *route;
- int first = 1;
- for (route = cur->route; route; route = route->next) {
- ast_cli(a->fd, "%s<%s>", first ? "" : ", ", route->hop);
- first = 0;
- }
- ast_cli(a->fd, "\n");
- } else {
- ast_cli(a->fd, "N/A\n");
- }
- ast_cli(a->fd, " DTMF Mode: %s\n", dtmfmode2str(ast_test_flag(&cur->flags[0], SIP_DTMF)));
- ast_cli(a->fd, " SIP Options: ");
- if (cur->sipoptions) {
- int x;
- for (x = 0 ; x < ARRAY_LEN(sip_options); x++) {
- if (cur->sipoptions & sip_options[x].id)
- ast_cli(a->fd, "%s ", sip_options[x].text);
- }
- ast_cli(a->fd, "\n");
- } else {
- ast_cli(a->fd, "(none)\n");
- }
- if (!cur->stimer) {
- ast_cli(a->fd, " Session-Timer: Uninitiallized\n");
- } else {
- ast_cli(a->fd, " Session-Timer: %s\n", cur->stimer->st_active ? "Active" : "Inactive");
- if (cur->stimer->st_active == TRUE) {
- ast_cli(a->fd, " S-Timer Interval: %d\n", cur->stimer->st_interval);
- ast_cli(a->fd, " S-Timer Refresher: %s\n", strefresher2str(cur->stimer->st_ref));
- ast_cli(a->fd, " S-Timer Sched Id: %d\n", cur->stimer->st_schedid);
- ast_cli(a->fd, " S-Timer Peer Sts: %s\n", cur->stimer->st_active_peer_ua ? "Active" : "Inactive");
- ast_cli(a->fd, " S-Timer Cached Min-SE: %d\n", cur->stimer->st_cached_min_se);
- ast_cli(a->fd, " S-Timer Cached SE: %d\n", cur->stimer->st_cached_max_se);
- ast_cli(a->fd, " S-Timer Cached Ref: %s\n", strefresher2str(cur->stimer->st_cached_ref));
- ast_cli(a->fd, " S-Timer Cached Mode: %s\n", stmode2str(cur->stimer->st_cached_mode));
- }
- }
- /* add transport and media types */
- ast_cli(a->fd, " Transport: %s\n", 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_channel_cdr(p->owner)) {
- ast_cdr_setuserfield(p->owner, c);
- }
- if (p->owner && ast_bridged_channel(p->owner) && ast_channel_cdr(ast_bridged_channel(p->owner))) {
- ast_cdr_setuserfield(ast_bridged_channel(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.
- */
- struct ast_call_feature *feat = NULL;
- 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 */
- ast_rdlock_call_features();
- if (p->relatedpeer) {
- if (!strcasecmp(c, "on")) {
- if (ast_strlen_zero(p->relatedpeer->record_on_feature)) {
- suppress_warning = 1;
- } else {
- feat = ast_find_call_feature(p->relatedpeer->record_on_feature);
- }
- } else if (!strcasecmp(c, "off")) {
- if (ast_strlen_zero(p->relatedpeer->record_off_feature)) {
- suppress_warning = 1;
- } else {
- feat = ast_find_call_feature(p->relatedpeer->record_off_feature);
- }
- } else {
- ast_log(LOG_ERROR, "Received INFO requesting to record with invalid value: %s\n", c);
- }
- }
- if (!feat || ast_strlen_zero(feat->exten)) {
- 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);
- ast_unlock_call_features();
- 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->exten); j++) {
- f.subclass.integer = feat->exten[j];
- ast_queue_frame(p->owner, &f);
- if (sipdebug) {
- ast_verbose("* DTMF-relay event faked: %c\n", f.subclass.integer);
- }
- }
- ast_unlock_call_features();
- 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 == SIP_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 == SIP_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, ':'))) { /*! \todo Will be deprecated after 1.4 */
- static int deprecation_warning = 0;
- *colname++ = '\0';
- if (deprecation_warning++ % 10 == 0)
- ast_log(LOG_WARNING, "SIPPEER(): usage of ':' to separate arguments is deprecated. Please use ',' instead.\n");
- } else 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")) {
- ast_getformatname_multiple(buf, len -1, peer->caps);
- } 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 */
- if((ast_codec_pref_index(&peer->prefs, atoi(codecnum), &codec))) {
- ast_copy_string(buf, ast_getformatname(&codec), len);
- } 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 ${SIPCHANINFO()} Dialplan function - reads sip channel data */
- static int function_sipchaninfo_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
- {
- struct sip_pvt *p;
- static int deprecated = 0;
- *buf = 0;
- if (!chan) {
- ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
- return -1;
- }
- if (!data) {
- ast_log(LOG_WARNING, "This function requires a parameter 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;
- }
- if (deprecated++ % 20 == 0) {
- /* Deprecated in 1.6.1 */
- ast_log(LOG_WARNING, "SIPCHANINFO() is deprecated. Please transition to using CHANNEL().\n");
- }
- 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;
- }
- if (!strcasecmp(data, "peerip")) {
- ast_copy_string(buf, ast_sockaddr_stringify_addr(&p->sa), len);
- } else if (!strcasecmp(data, "recvip")) {
- ast_copy_string(buf, ast_sockaddr_stringify_addr(&p->recv), len);
- } else if (!strcasecmp(data, "from")) {
- ast_copy_string(buf, p->from, len);
- } else if (!strcasecmp(data, "uri")) {
- ast_copy_string(buf, p->uri, len);
- } else if (!strcasecmp(data, "useragent")) {
- ast_copy_string(buf, p->useragent, len);
- } else if (!strcasecmp(data, "peername")) {
- ast_copy_string(buf, p->peername, len);
- } else if (!strcasecmp(data, "t38passthrough")) {
- if ((p->t38.state == T38_DISABLED) || (p->t38.state == T38_REJECTED)) {
- ast_copy_string(buf, "0", len);
- } else { /* T38 is offered or enabled in this call */
- ast_copy_string(buf, "1", len);
- }
- } else {
- ast_channel_unlock(chan);
- return -1;
- }
- ast_channel_unlock(chan);
- return 0;
- }
- /*! \brief Structure to declare a dialplan function: SIPCHANINFO */
- static struct ast_custom_function sipchaninfo_function = {
- .name = "SIPCHANINFO",
- .read = function_sipchaninfo_read,
- };
- /*! \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;
- 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);
- 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 = reason;
- }
- /*! \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 sip_transport transport = SIP_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 = SIP_TRANSPORT_TCP;
- else if (!strncasecmp(trans, "tls", 3))
- transport = SIP_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 = SIP_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);
- }
- 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);
- }
- 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 (!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);
- if (sip_cfg.callevents) {
- manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
- "Channel: %s\r\nChanneltype: %s\r\nUniqueid: %s\r\nSIPcallid: %s\r\nSIPfullcontact: %s\r\nPeername: %s\r\n",
- ast_channel_name(p->owner), "SIP", ast_channel_uniqueid(p->owner), p->callid, p->fullcontact, p->peername);
- }
- } else { /* RE-invite */
- if (p->t38.state == T38_DISABLED || p->t38.state == T38_REJECTED) {
- ast_queue_control(p->owner, AST_CONTROL_UPDATE_RTP_PEER);
- } else {
- ast_queue_frame(p->owner, &ast_null_frame);
- }
- }
- } else {
- /* It's possible we're getting an 200 OK after we've tried to disconnect
- by sending CANCEL */
- /* First send ACK, then send bye */
- if (!req->ignore) {
- ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
- }
- }
- /* Check for Session-Timers related headers */
- if (st_get_mode(p, 0) != SESSION_TIMER_MODE_REFUSE) {
- p_hdrval = (char*)sip_get_header(req, "Session-Expires");
- if (!ast_strlen_zero(p_hdrval)) {
- /* UAS supports Session-Timers */
- enum st_refresher_param st_ref_param;
- int tmp_st_interval = 0;
- rtn = parse_session_expires(p_hdrval, &tmp_st_interval, &st_ref_param);
- if (rtn != 0) {
- ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
- } else if (tmp_st_interval < st_get_se(p, FALSE)) {
- ast_log(LOG_WARNING, "Got Session-Expires less than local Min-SE in 200 OK, tearing down call\n");
- ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
- }
- if (st_ref_param == SESSION_TIMER_REFRESHER_PARAM_UAC) {
- p->stimer->st_ref = SESSION_TIMER_REFRESHER_US;
- } else if (st_ref_param == SESSION_TIMER_REFRESHER_PARAM_UAS) {
- p->stimer->st_ref = SESSION_TIMER_REFRESHER_THEM;
- } else {
- ast_log(LOG_WARNING, "Unknown refresher on %s\n", p->callid);
- }
- if (tmp_st_interval) {
- p->stimer->st_interval = tmp_st_interval;
- }
- p->stimer->st_active = TRUE;
- p->stimer->st_active_peer_ua = TRUE;
- start_session_timer(p);
- } else {
- /* UAS doesn't support Session-Timers */
- if (st_get_mode(p, 0) == SESSION_TIMER_MODE_ORIGINATE) {
- p->stimer->st_ref = SESSION_TIMER_REFRESHER_US;
- p->stimer->st_active_peer_ua = FALSE;
- start_session_timer(p);
- }
- }
- }
- /* If I understand this right, the branch is different for a non-200 ACK only */
- p->invitestate = INV_TERMINATED;
- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, TRUE);
- check_pendings(p);
- break;
- case 407: /* Proxy authentication */
- case 401: /* Www auth */
- /* First we ACK */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- if (p->options) {
- p->options->auth_type = resp;
- }
- /* Then we AUTH */
- ast_string_field_set(p, theirtag, NULL); /* forget their old tag, so we don't match tags when getting response */
- if (!req->ignore) {
- if (p->authtries < MAX_AUTHTRIES) {
- p->invitestate = INV_CALLING;
- }
- if (p->authtries == MAX_AUTHTRIES || do_proxy_auth(p, req, resp, SIP_INVITE, 1)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on INVITE to '%s'\n", sip_get_header(&p->initreq, "From"));
- pvt_set_needdestroy(p, "failed to authenticate on INVITE");
- sip_alreadygone(p);
- if (p->owner) {
- ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
- }
- }
- }
- break;
- case 403: /* Forbidden */
- /* First we ACK */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- ast_log(LOG_WARNING, "Received response: \"Forbidden\" from '%s'\n", sip_get_header(&p->initreq, "From"));
- if (!req->ignore && p->owner) {
- sip_queue_hangup_cause(p, hangup_sip2cause(resp));
- }
- break;
- case 414: /* Bad request URI */
- case 493: /* Undecipherable */
- case 404: /* Not found */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- if (p->owner && !req->ignore) {
- sip_queue_hangup_cause(p, hangup_sip2cause(resp));
- }
- break;
- case 481: /* Call leg does not exist */
- /* Could be REFER caused INVITE with replaces */
- ast_log(LOG_WARNING, "Re-invite to non-existing call leg on other UA. SIP dialog '%s'. Giving up.\n", p->callid);
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- if (p->owner) {
- ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(resp));
- }
- break;
- case 422: /* Session-Timers: Session interval too small */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- ast_string_field_set(p, theirtag, NULL);
- proc_422_rsp(p, req);
- break;
- case 428: /* Use identity header - rfc 4474 - not supported by Asterisk yet */
- xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- append_history(p, "Identity", "SIP identity is required. Not supported by Asterisk.");
- ast_log(LOG_WARNING, "SIP identity required by proxy. SIP dialog '%s'. Giving up.\n", p->callid);
- if (p->owner && !req->ignore) {
- ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(resp));
- }
- break;
- case 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, ASTOBJ_REF(p->mwi))) < 0) {
- ASTOBJ_UNREF(p->mwi, sip_subscribe_mwi_destroy);
- }
- 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;
- ASTOBJ_UNREF(p->mwi, sip_subscribe_mwi_destroy);
- 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;
- ASTOBJ_UNREF(p->mwi, sip_subscribe_mwi_destroy);
- 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;
- ASTOBJ_UNREF(p->mwi, sip_subscribe_mwi_destroy);
- 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;
- ASTOBJ_UNREF(p->mwi, sip_subscribe_mwi_destroy);
- pvt_set_needdestroy(p, "received 481 response");
- break;
- case 400: /* Bad Request */
- case 414: /* Request URI too long */
- case 493: /* Undecipherable */
- case 500:
- case 501:
- ast_log(LOG_WARNING, "Subscription failed for MWI. The remote side may have suffered a heart attack.\n");
- p->mwi->call = NULL;
- ASTOBJ_UNREF(p->mwi, sip_subscribe_mwi_destroy);
- pvt_set_needdestroy(p, "received serious error (500/501/493/414/400) response");
- break;
- }
- }
- /* \brief Handle SIP response in REFER transaction
- We've sent a REFER, now handle responses to it
- */
- static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
- {
- enum ast_control_transfer message = AST_TRANSFER_FAILED;
- /* If no refer structure exists, then do nothing */
- if (!p->refer)
- return;
- switch (resp) {
- case 202: /* Transfer accepted */
- /* We need to do something here */
- /* The transferee is now sending INVITE to target */
- p->refer->status = REFER_ACCEPTED;
- /* Now wait for next message */
- ast_debug(3, "Got 202 accepted on transfer\n");
- /* We should hang along, waiting for NOTIFY's here */
- break;
- case 401: /* Not www-authorized on SIP method */
- case 407: /* Proxy auth */
- if (ast_strlen_zero(p->authname)) {
- ast_log(LOG_WARNING, "Asked to authenticate REFER to %s but we have no matching peer or realm auth!\n",
- ast_sockaddr_stringify(&p->recv));
- if (p->owner) {
- ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
- }
- pvt_set_needdestroy(p, "unable to authenticate REFER");
- }
- if (p->authtries > 1 || do_proxy_auth(p, req, resp, SIP_REFER, 0)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on REFER to '%s'\n", sip_get_header(&p->initreq, "From"));
- p->refer->status = REFER_NOAUTH;
- if (p->owner) {
- ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
- }
- pvt_set_needdestroy(p, "failed to authenticate REFER");
- }
- break;
-
- case 405: /* Method not allowed */
- /* Return to the current call onhold */
- /* Status flag needed to be reset */
- ast_log(LOG_NOTICE, "SIP transfer to %s failed, REFER not allowed. \n", p->refer->refer_to);
- pvt_set_needdestroy(p, "received 405 response");
- p->refer->status = REFER_FAILED;
- if (p->owner) {
- ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
- }
- break;
- case 481: /* Call leg does not exist */
- /* A transfer with Replaces did not work */
- /* OEJ: We should Set flag, cancel the REFER, go back
- to original call - but right now we can't */
- ast_log(LOG_WARNING, "Remote host can't match REFER request to call '%s'. Giving up.\n", p->callid);
- if (p->owner)
- ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
- pvt_set_needdestroy(p, "received 481 response");
- break;
- case 500: /* Server error */
- case 501: /* Method not implemented */
- /* Return to the current call onhold */
- /* Status flag needed to be reset */
- ast_log(LOG_NOTICE, "SIP transfer to %s failed, call miserably fails. \n", p->refer->refer_to);
- pvt_set_needdestroy(p, "received 500/501 response");
- p->refer->status = REFER_FAILED;
- if (p->owner) {
- ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
- }
- break;
- case 603: /* Transfer declined */
- ast_log(LOG_NOTICE, "SIP transfer to %s declined, call miserably fails. \n", p->refer->refer_to);
- p->refer->status = REFER_FAILED;
- pvt_set_needdestroy(p, "received 603 response");
- if (p->owner) {
- ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
- }
- break;
- default:
- /* We should treat unrecognized 9xx as 900. 400 is actually
- specified as a possible response, but any 4-6xx is
- theoretically possible. */
- if (resp < 299) { /* 1xx cases don't get here */
- ast_log(LOG_WARNING, "SIP transfer to %s had unexpected 2xx response (%d), confusion is possible. \n", p->refer->refer_to, resp);
- } else {
- ast_log(LOG_WARNING, "SIP transfer to %s with response (%d). \n", p->refer->refer_to, resp);
- }
- p->refer->status = REFER_FAILED;
- pvt_set_needdestroy(p, "received failure response");
- if (p->owner) {
- ast_queue_control_data(p->owner, AST_CONTROL_TRANSFER, &message, sizeof(message));
- }
- break;
- }
- }
- /*! \brief Handle responses on REGISTER to services */
- static int handle_response_register(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno)
- {
- int expires, expires_ms;
- struct sip_registry *r;
- r=p->registry;
-
- switch (resp) {
- case 401: /* Unauthorized */
- if (p->authtries == MAX_AUTHTRIES || do_register_auth(p, req, resp)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on REGISTER to '%s@%s' (Tries %d)\n", p->registry->username, p->registry->hostname, p->authtries);
- pvt_set_needdestroy(p, "failed to authenticate REGISTER");
- }
- break;
- case 403: /* Forbidden */
- if (global_reg_retry_403) {
- ast_log(LOG_NOTICE, "Treating 403 response to REGISTER as non-fatal for %s@%s\n",
- p->registry->username, p->registry->hostname);
- ast_string_field_set(r, nonce, "");
- ast_string_field_set(p, nonce, "");
- break;
- }
- ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for REGISTER for '%s' to '%s'\n", p->registry->username, p->registry->hostname);
- AST_SCHED_DEL_UNREF(sched, r->timeout, registry_unref(r, "reg ptr unref from handle_response_register 403"));
- r->regstate = REG_STATE_NOAUTH;
- 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;
- AST_SCHED_DEL_UNREF(sched, r->timeout, registry_unref(r, "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, registry_unref(r, "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);
- }
- manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelType: SIP\r\nUsername: %s\r\nDomain: %s\r\nStatus: %s\r\n", r->username, r->hostname, regstate2str(r->regstate));
- break;
- case 400: /* Bad request */
- case 414: /* Request URI too long */
- case 493: /* Undecipherable */
- case 479: /* Kamailio/OpenSIPS: Not able to process the URI - address is wrong in register*/
- ast_log(LOG_WARNING, "Got error %d on register to %s@%s, giving up (check config)\n", resp, p->registry->username, p->registry->hostname);
- pvt_set_needdestroy(p, "received 4xx response");
- if (r->call)
- r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 4xx");
- r->regstate = REG_STATE_REJECTED;
- AST_SCHED_DEL_UNREF(sched, r->timeout, registry_unref(r, "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 */
- manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelType: SIP\r\nUsername: %s\r\nDomain: %s\r\nStatus: %s\r\n", 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, registry_unref(r, "reg ptr unref from handle_response_register 200"));
- if (r->call)
- r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 200");
- p->registry = registry_unref(p->registry, "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,
- registry_unref(_data,"unref in REPLACE del fail"),
- registry_unref(r,"unref in REPLACE add fail"),
- registry_addref(r,"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);
- }
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus",
- "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: %s\r\nTime: %d\r\n",
- peer->name, s, pingtime);
- 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 == SIP_TRANSPORT_UDP) {
- int ack_res = FALSE;
- /* Acknowledge whatever it is destined for */
- if ((resp >= 100) && (resp <= 199)) {
- /* NON-INVITE messages do not ack a 1XX response. RFC 3261 section 17.1.2.2 */
- if (sipmethod == SIP_INVITE) {
- ack_res = __sip_semi_ack(p, seqno, 0, sipmethod);
- }
- } else {
- ack_res = __sip_ack(p, seqno, 0, sipmethod);
- }
- if (ack_res == FALSE) {
- /* RFC 3261 13.2.2.4 and 17.1.1.2 - We must re-send ACKs to re-transmitted final responses */
- if (sipmethod == SIP_INVITE && resp >= 200) {
- transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, resp < 300 ? TRUE: FALSE);
- }
- append_history(p, "Ignore", "Ignoring this retransmit\n");
- return;
- }
- }
- /* If this is a NOTIFY for a subscription clear the flag that indicates that we have a NOTIFY pending */
- if (!p->owner && sipmethod == SIP_NOTIFY && p->pendinginvite) {
- p->pendinginvite = 0;
- }
- /* Get their tag if we haven't already */
- if (ast_strlen_zero(p->theirtag) || (resp >= 200)) {
- char tag[128];
- gettag(req, "To", tag, sizeof(tag));
- ast_string_field_set(p, theirtag, tag);
- } else {
- /* Store theirtag to track for changes when 200 responses to invites are received without SDP */
- ast_string_field_set(p, theirprovtag, p->theirtag);
- }
- /* This needs to be configurable on a channel/peer level,
- not mandatory for all communication. Sadly enough, NAT implementations
- are not so stable so we can always rely on these headers.
- Temporarily disabled, while waiting for fix.
- Fix assigned to Rizzo :-)
- */
- /* check_via_response(p, req); */
- /* RFC 3261 Section 15 specifies that if we receive a 408 or 481
- * in response to a BYE, then we should end the current dialog
- * and session. It is known that at least one phone manufacturer
- * potentially will send a 404 in response to a BYE, so we'll be
- * liberal in what we accept and end the dialog and session if we
- * receive any of those responses to a BYE.
- */
- if ((resp == 404 || resp == 408 || resp == 481) && sipmethod == SIP_BYE) {
- pvt_set_needdestroy(p, "received 4XX response to a BYE");
- return;
- }
- if (p->relatedpeer && sipmethod == SIP_OPTIONS) {
- /* We don't really care what the response is, just that it replied back.
- Well, as long as it's not a 100 response... since we might
- need to hang around for something more "definitive" */
- if (resp != 100)
- handle_response_peerpoke(p, resp, req);
- } else if (sipmethod == SIP_REFER && resp >= 200) {
- handle_response_refer(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_PUBLISH) {
- /* SIP PUBLISH transcends this morass of doodoo and instead
- * we just always call the response handler. Good gravy!
- */
- handle_response_publish(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_INFO) {
- /* More good gravy! */
- handle_response_info(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_MESSAGE) {
- /* More good gravy! */
- handle_response_message(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_NOTIFY) {
- /* The gravy train continues to roll */
- handle_response_notify(p, resp, rest, req, seqno);
- } else if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- switch(resp) {
- case 100: /* 100 Trying */
- case 101: /* 101 Dialog establishment */
- case 183: /* 183 Session Progress */
- case 180: /* 180 Ringing */
- case 182: /* 182 Queued */
- case 181: /* 181 Call Is Being Forwarded */
- if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- break;
- case 200: /* 200 OK */
- p->authtries = 0; /* Reset authentication counter */
- if (sipmethod == SIP_INVITE) {
- handle_response_invite(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_REGISTER) {
- handle_response_register(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_SUBSCRIBE) {
- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- handle_response_subscribe(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_BYE) { /* Ok, we're ready to go */
- pvt_set_needdestroy(p, "received 200 response");
- ast_clear_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- }
- break;
- case 401: /* Not www-authorized on SIP method */
- case 407: /* Proxy auth required */
- if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_SUBSCRIBE)
- handle_response_subscribe(p, resp, rest, req, seqno);
- else if (p->registry && sipmethod == SIP_REGISTER)
- handle_response_register(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_UPDATE) {
- handle_response_update(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_BYE) {
- if (p->options)
- p->options->auth_type = resp;
- if (ast_strlen_zero(p->authname)) {
- ast_log(LOG_WARNING, "Asked to authenticate %s, to %s but we have no matching peer!\n",
- msg, ast_sockaddr_stringify(&p->recv));
- pvt_set_needdestroy(p, "unable to authenticate BYE");
- } else if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, resp, sipmethod, 0)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on %s to '%s'\n", msg, sip_get_header(&p->initreq, "From"));
- pvt_set_needdestroy(p, "failed to authenticate BYE");
- }
- } else {
- ast_log(LOG_WARNING, "Got authentication request (%d) on %s to '%s'\n", resp, sip_methods[sipmethod].text, sip_get_header(req, "To"));
- pvt_set_needdestroy(p, "received 407 response");
- }
- break;
- case 403: /* Forbidden - we failed authentication */
- if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_SUBSCRIBE)
- handle_response_subscribe(p, resp, rest, req, seqno);
- else if (p->registry && sipmethod == SIP_REGISTER)
- handle_response_register(p, resp, rest, req, seqno);
- else {
- ast_log(LOG_WARNING, "Forbidden - maybe wrong password on authentication for %s\n", msg);
- pvt_set_needdestroy(p, "received 403 response");
- }
- break;
- case 400: /* Bad Request */
- case 414: /* Request URI too long */
- case 493: /* Undecipherable */
- case 404: /* Not found */
- if (p->registry && sipmethod == SIP_REGISTER)
- handle_response_register(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_SUBSCRIBE)
- handle_response_subscribe(p, resp, rest, req, seqno);
- else if (owner)
- ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
- break;
- case 423: /* Interval too brief */
- if (sipmethod == SIP_REGISTER)
- handle_response_register(p, resp, rest, req, seqno);
- break;
- case 408: /* Request timeout - terminate dialog */
- if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_REGISTER)
- handle_response_register(p, resp, rest, req, seqno);
- else if (sipmethod == SIP_BYE) {
- pvt_set_needdestroy(p, "received 408 response");
- ast_debug(4, "Got timeout on bye. Thanks for the answer. Now, kill this call\n");
- } else {
- if (owner)
- ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
- pvt_set_needdestroy(p, "received 408 response");
- }
- break;
- case 428:
- case 422: /* Session-Timers: Session Interval Too Small */
- if (sipmethod == SIP_INVITE) {
- handle_response_invite(p, resp, rest, req, seqno);
- }
- break;
- case 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 480: /* Temporarily Unavailable */
- 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 Park SIP call support function
- Starts in a new thread, then parks the call
- XXX Should we add a wait period after streaming audio and before hangup?? Sometimes the
- audio can't be heard before hangup
- */
- static void *sip_park_thread(void *stuff)
- {
- struct ast_channel *transferee, *transferer; /* Chan1: The transferee, Chan2: The transferer */
- struct sip_pvt *transferer_pvt;
- struct sip_dual *d;
- int ext;
- int res;
- d = stuff;
- transferee = d->chan1;
- transferer = d->chan2;
- transferer_pvt = ast_channel_tech_pvt(transferer);
- ast_debug(4, "SIP Park: Transferer channel %s, Transferee %s\n", ast_channel_name(transferer), ast_channel_name(transferee));
- res = ast_park_call_exten(transferee, transferer, d->park_exten, d->park_context, 0, &ext);
- sip_pvt_lock(transferer_pvt);
- #ifdef WHEN_WE_KNOW_THAT_THE_CLIENT_SUPPORTS_MESSAGE
- if (res) {
- destroy_msg_headers(transferer_pvt);
- ast_string_field_set(transferer_pvt, msg_body, "Unable to park call.");
- transmit_message(transferer_pvt, 0, 0);
- } else {
- /* Then tell the transferer what happened */
- destroy_msg_headers(transferer_pvt);
- sprintf(buf, "Call parked on extension '%d'.", ext);
- ast_string_field_set(transferer_pvt, msg_body, buf);
- transmit_message(transferer_pvt, 0, 0);
- }
- #endif
- /* Any way back to the current call??? */
- /* Transmit response to the REFER request */
- ast_set_flag(&transferer_pvt->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
- if (!res) {
- /* Transfer succeeded */
- append_history(transferer_pvt, "SIPpark", "Parked call on %d", ext);
- transmit_notify_with_sipfrag(transferer_pvt, d->seqno, "200 OK", TRUE);
- sip_pvt_unlock(transferer_pvt);
- ast_channel_hangupcause_set(transferer, AST_CAUSE_NORMAL_CLEARING);
- ast_debug(1, "SIP Call parked on extension '%d'\n", ext);
- } else {
- transmit_notify_with_sipfrag(transferer_pvt, d->seqno, "503 Service Unavailable", TRUE);
- append_history(transferer_pvt, "SIPpark", "Parking failed\n");
- sip_pvt_unlock(transferer_pvt);
- ast_log(AST_LOG_NOTICE, "SIP Call parked failed for %s\n", ast_channel_name(transferee));
- ast_hangup(transferee);
- }
- ast_hangup(transferer);
- deinit_req(&d->req);
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d);
- return NULL;
- }
- /*! DO NOT hold any locks while calling sip_park */
- static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, uint32_t seqno, const char *park_exten, const char *park_context)
- {
- struct sip_dual *d;
- struct ast_channel *transferee, *transferer;
- pthread_t th;
- transferee = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, ast_channel_accountcode(chan1), ast_channel_exten(chan1), ast_channel_context(chan1), ast_channel_linkedid(chan1), ast_channel_amaflags(chan1), "Parking/%s", ast_channel_name(chan1));
- transferer = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, ast_channel_accountcode(chan2), ast_channel_exten(chan2), ast_channel_context(chan2), ast_channel_linkedid(chan2), ast_channel_amaflags(chan2), "SIPPeer/%s", ast_channel_name(chan2));
- d = ast_calloc(1, sizeof(*d));
- if (!transferee || !transferer || !d) {
- if (transferee) {
- ast_hangup(transferee);
- }
- if (transferer) {
- ast_hangup(transferer);
- }
- ast_free(d);
- return -1;
- }
- d->park_exten = ast_strdup(park_exten);
- d->park_context = ast_strdup(park_context);
- if (!d->park_exten || !d->park_context) {
- ast_hangup(transferee);
- ast_hangup(transferer);
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d);
- return -1;
- }
- /* Make formats okay */
- ast_format_copy(ast_channel_readformat(transferee), ast_channel_readformat(chan1));
- ast_format_copy(ast_channel_writeformat(transferee), ast_channel_writeformat(chan1));
- /* Prepare for taking over the channel */
- if (ast_channel_masquerade(transferee, chan1)) {
- ast_hangup(transferee);
- ast_hangup(transferer);
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d);
- return -1;
- }
- /* Setup the extensions and such */
- ast_channel_context_set(transferee, ast_channel_context(chan1));
- ast_channel_exten_set(transferee, ast_channel_exten(chan1));
- ast_channel_priority_set(transferee, ast_channel_priority(chan1));
- ast_do_masquerade(transferee);
- /* We make a clone of the peer channel too, so we can play
- back the announcement */
- /* Make formats okay */
- ast_format_copy(ast_channel_readformat(transferer), ast_channel_readformat(chan2));
- ast_format_copy(ast_channel_writeformat(transferer), ast_channel_writeformat(chan2));
- ast_channel_parkinglot_set(transferer, ast_channel_parkinglot(chan2));
- /* Prepare for taking over the channel */
- if (ast_channel_masquerade(transferer, chan2)) {
- ast_hangup(transferee);
- ast_hangup(transferer);
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d);
- return -1;
- }
- /* Setup the extensions and such */
- ast_channel_context_set(transferer, ast_channel_context(chan2));
- ast_channel_exten_set(transferer, ast_channel_exten(chan2));
- ast_channel_priority_set(transferer, ast_channel_priority(chan2));
- ast_do_masquerade(transferer);
- /* Save original request for followup */
- copy_request(&d->req, req);
- d->chan1 = transferee; /* Transferee */
- d->chan2 = transferer; /* Transferer */
- d->seqno = seqno;
- if (ast_pthread_create_detached_background(&th, NULL, sip_park_thread, d) < 0) {
- /* Could not start thread */
- ast_hangup(transferer);
- ast_hangup(transferee);
- deinit_req(&d->req);
- ast_free(d->park_exten);
- ast_free(d->park_context);
- ast_free(d); /* We don't need it anymore. If thread is created, d will be free'd
- by sip_park_thread() */
- return -1;
- }
- return 0;
- }
- /*! \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 Turn off generator data
- XXX Does this function belong in the SIP channel?
- */
- static void ast_quiet_chan(struct ast_channel *chan)
- {
- if (chan && ast_channel_state(chan) == AST_STATE_UP) {
- if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_MOH))
- ast_moh_stop(chan);
- else if (ast_channel_generatordata(chan))
- ast_deactivate_generator(chan);
- }
- }
- /*! \brief Attempt transfer of SIP call
- This fix for attended transfers on a local PBX */
- static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target)
- {
- int res = 0;
- struct ast_channel *peera = NULL,
- *peerb = NULL,
- *peerc = NULL,
- *peerd = NULL;
- /* We will try to connect the transferee with the target and hangup
- all channels to the transferer */
- ast_debug(4, "Sip transfer:--------------------\n");
- if (transferer->chan1)
- ast_debug(4, "-- Transferer to PBX channel: %s State %s\n", ast_channel_name(transferer->chan1), ast_state2str(ast_channel_state(transferer->chan1)));
- else
- ast_debug(4, "-- No transferer first channel - odd??? \n");
- if (target->chan1)
- ast_debug(4, "-- Transferer to PBX second channel (target): %s State %s\n", ast_channel_name(target->chan1), ast_state2str(ast_channel_state(target->chan1)));
- else
- ast_debug(4, "-- No target first channel ---\n");
- if (transferer->chan2)
- ast_debug(4, "-- Bridged call to transferee: %s State %s\n", ast_channel_name(transferer->chan2), ast_state2str(ast_channel_state(transferer->chan2)));
- else
- ast_debug(4, "-- No bridged call to transferee\n");
- if (target->chan2)
- ast_debug(4, "-- Bridged call to transfer target: %s State %s\n", target->chan2 ? ast_channel_name(target->chan2) : "<none>", target->chan2 ? ast_state2str(ast_channel_state(target->chan2)) : "(none)");
- else
- ast_debug(4, "-- No target second channel ---\n");
- ast_debug(4, "-- END Sip transfer:--------------------\n");
- if (transferer->chan2) { /* We have a bridge on the transferer's channel */
- peera = transferer->chan1; /* Transferer - PBX -> transferee channel * the one we hangup */
- peerb = target->chan1; /* Transferer - PBX -> target channel - This will get lost in masq */
- peerc = transferer->chan2; /* Asterisk to Transferee */
- peerd = target->chan2; /* Asterisk to Target */
- ast_debug(3, "SIP transfer: Four channels to handle\n");
- } else if (target->chan2) { /* Transferer has no bridge (IVR), but transferee */
- peera = target->chan1; /* Transferer to PBX -> target channel */
- peerb = transferer->chan1; /* Transferer to IVR*/
- peerc = target->chan2; /* Asterisk to Target */
- peerd = transferer->chan2; /* Nothing */
- ast_debug(3, "SIP transfer: Three channels to handle\n");
- }
- if (peera && peerb && peerc && (peerb != peerc)) {
- ast_quiet_chan(peera); /* Stop generators */
- /* no need to quiet peerb since it should be hungup after the
- transfer and the masquerade needs to be able to see if MOH is
- playing on it */
- ast_quiet_chan(peerc);
- if (peerd)
- ast_quiet_chan(peerd);
- ast_debug(4, "SIP transfer: trying to masquerade %s into %s\n", ast_channel_name(peerc), ast_channel_name(peerb));
- if (ast_channel_masquerade(peerb, peerc)) {
- ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", ast_channel_name(peerb), ast_channel_name(peerc));
- res = -1;
- } else
- ast_debug(4, "SIP transfer: Succeeded to masquerade channels.\n");
- return res;
- } else {
- ast_log(LOG_NOTICE, "SIP Transfer attempted with no appropriate bridged calls to transfer\n");
- if (transferer->chan1)
- ast_softhangup_nolock(transferer->chan1, AST_SOFTHANGUP_DEV);
- if (target->chan1)
- ast_softhangup_nolock(target->chan1, AST_SOFTHANGUP_DEV);
- return -1;
- }
- 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, "/");
- struct ast_event *event;
- if ((event = ast_event_new(AST_EVENT_MWI,
- AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
- AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "SIP_Remote",
- AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, atoi(new),
- AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, atoi(old),
- AST_EVENT_IE_END))) {
- ast_event_queue_and_cache(event);
- }
- transmit_response(p, "200 OK", req);
- } else {
- transmit_response(p, "489 Bad event", req);
- res = -1;
- }
- } else if (!strcmp(event, "keep-alive")) {
- /* Used by Sipura/Linksys for NAT pinhole,
- * just confirm that we received the packet. */
- transmit_response(p, "200 OK", req);
- } else if (!strcmp(event, "call-completion")) {
- res = handle_cc_notify(p, req);
- } else {
- /* We don't understand this event. */
- transmit_response(p, "489 Bad event", req);
- res = -1;
- }
- if (!p->lastinvite)
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return res;
- }
- /*! \brief Handle incoming OPTIONS request
- An OPTIONS request should be answered like an INVITE from the same UA, including SDP
- */
- static int handle_request_options(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e)
- {
- const char *msg;
- enum sip_get_dest_result gotdest;
- int res;
- if (p->lastinvite) {
- /* if this is a request in an active dialog, just confirm that the dialog exists. */
- transmit_response_with_allow(p, "200 OK", req, 0);
- return 0;
- }
- if (sip_cfg.auth_options_requests) {
- /* Do authentication if this OPTIONS request began the dialog */
- copy_request(&p->initreq, req);
- set_pvt_allowed_methods(p, req);
- res = check_user(p, req, SIP_OPTIONS, e, XMIT_UNRELIABLE, addr);
- if (res == AUTH_CHALLENGE_SENT) {
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return 0;
- }
- if (res < 0) { /* Something failed in authentication */
- ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", sip_get_header(req, "From"));
- transmit_response(p, "403 Forbidden", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- return 0;
- }
- }
- /* must go through authentication before getting here */
- gotdest = get_destination(p, req, NULL);
- build_contact(p, req, 1);
- if (ast_strlen_zero(p->context))
- ast_string_field_set(p, context, sip_cfg.default_context);
- if (ast_shutting_down()) {
- 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,
- meaning a target pickup or an attended transfer.
- Used only once.
- XXX 'ignore' is unused.
- \note this function is called by handle_request_invite(). Four locks
- held at the beginning of this function, p, p->owner, p->refer->refer_call and
- p->refere->refer_call->owner. only p's lock should remain at the end of this
- function. p's lock as well as the channel p->owner's lock are held by
- handle_request_do(), we unlock p->owner before the masq. By setting nounlock
- we are indicating to handle_request_do() that we have already unlocked the owner.
- */
- static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, int *nounlock)
- {
- int earlyreplace = 0;
- int oneleggedreplace = 0; /* Call with no bridge, propably IVR or voice message */
- struct ast_channel *c = p->owner; /* Our incoming call */
- struct ast_channel *replacecall = p->refer->refer_call->owner; /* The channel we're about to take over */
- struct ast_channel *targetcall; /* The bridge to the take-over target */
- p->refer->refer_call->invitereplaces = 1;
- /* Check if we're in ring state */
- if (ast_channel_state(replacecall) == AST_STATE_RING)
- earlyreplace = 1;
- /* Check if we have a bridge */
- if (!(targetcall = ast_bridged_channel(replacecall))) {
- /* We have no bridge */
- if (!earlyreplace) {
- ast_debug(2, " Attended transfer attempted to replace call with no bridge (maybe ringing). Channel %s!\n", ast_channel_name(replacecall));
- oneleggedreplace = 1;
- }
- }
- if (targetcall && ast_channel_state(targetcall) == AST_STATE_RINGING)
- ast_debug(4, "SIP transfer: Target channel is in ringing state\n");
- if (targetcall)
- ast_debug(4, "SIP transfer: Invite Replace incoming channel should bridge to channel %s while hanging up channel %s\n", ast_channel_name(targetcall), ast_channel_name(replacecall));
- else
- ast_debug(4, "SIP transfer: Invite Replace incoming channel should replace and hang up channel %s (one call leg)\n", ast_channel_name(replacecall));
- if (req->ignore) {
- ast_log(LOG_NOTICE, "Ignoring this INVITE with replaces in a stupid way.\n");
- /* We should answer something here. If we are here, the
- call we are replacing exists, so an accepted
- can't harm */
- transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE, FALSE);
- /* Do something more clever here */
- if (c) {
- *nounlock = 1;
- ast_channel_unlock(c);
- }
- ast_channel_unlock(replacecall);
- sip_pvt_unlock(p->refer->refer_call);
- return 1;
- }
- if (!c) {
- /* 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);
- ast_channel_unlock(replacecall);
- sip_pvt_unlock(p->refer->refer_call);
- return 1;
- }
- append_history(p, "Xfer", "INVITE/Replace received");
- /* We have three channels to play with
- channel c: New incoming call
- targetcall: Call from PBX to target
- p->refer->refer_call: SIP pvt dialog from transferer to pbx.
- replacecall: The owner of the previous
- We need to masq C into refer_call to connect to
- targetcall;
- If we are talking to internal audio stream, target call is null.
- */
- /* Fake call progress */
- transmit_response(p, "100 Trying", req);
- ast_setstate(c, AST_STATE_RING);
- /* Masquerade the new call into the referred call to connect to target call
- Targetcall is not touched by the masq */
- /* Answer the incoming call and set channel to UP state */
- transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE, FALSE);
- /* Is this a call pickup? */
- if (earlyreplace || oneleggedreplace) {
- /* Report pickup event, in this order: PICKUP, CHAN_UP, ANSWER */
- ast_cel_report_event(replacecall, AST_CEL_PICKUP, NULL, NULL, c);
- ast_setstate(c, AST_STATE_UP);
- ast_cel_report_event(c, AST_CEL_ANSWER, NULL, NULL, NULL);
- } else {
- ast_setstate(c, AST_STATE_UP);
- }
- /* Stop music on hold and other generators */
- ast_quiet_chan(replacecall);
- ast_quiet_chan(targetcall);
- ast_debug(4, "Invite/Replaces: preparing to masquerade %s into %s\n", ast_channel_name(c), ast_channel_name(replacecall));
- /* Make sure that the masq does not free our PVT for the old call */
- if (! earlyreplace && ! oneleggedreplace )
- ast_set_flag(&p->refer->refer_call->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */
- /* Prepare the masquerade - if this does not happen, we will be gone */
- if(ast_channel_masquerade(replacecall, c))
- ast_log(LOG_ERROR, "Failed to masquerade C into Replacecall\n");
- else
- ast_debug(4, "Invite/Replaces: Going to masquerade %s into %s\n", ast_channel_name(c), ast_channel_name(replacecall));
- /* C should now be in place of replacecall. all channel locks and pvt locks should be removed
- * before issuing the masq. Since we are unlocking both the pvt (p) and its owner channel (c)
- * it is possible for channel c to be destroyed on us. To prevent this, we must give c a reference
- * before any unlocking takes place and remove it only once we are completely done with it */
- ast_channel_ref(c);
- ast_channel_unlock(replacecall);
- ast_channel_unlock(c);
- sip_pvt_unlock(p->refer->refer_call);
- sip_pvt_unlock(p);
- if (ast_do_masquerade(replacecall)) {
- ast_log(LOG_WARNING, "Failed to perform masquerade with INVITE replaces\n");
- }
- if (earlyreplace || oneleggedreplace ) {
- ast_channel_lock(c);
- ast_channel_hangupcause_set(c, AST_CAUSE_SWITCH_CONGESTION);
- ast_channel_unlock(c);
- }
- /* Clear SIP_DEFER_BYE_ON_TRANSFER after the masq to avoid delay hanging up replaced channel */
- sip_pvt_lock(p);
- ast_clear_flag(&p->refer->refer_call->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
- sip_pvt_unlock(p);
- /* c and c's tech pvt must be unlocked at this point for ast_hangup */
- ast_hangup(c);
- /* this indicates to handle_request_do that the owner channel has already been unlocked */
- *nounlock = 1;
- /* lock PVT structure again after hangup */
- sip_pvt_lock(p);
- ast_channel_unref(c);
- 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;
- int refer_locked = 0;
- const char *required;
- unsigned int required_profile = 0;
- struct ast_channel *c = NULL; /* New channel */
- struct sip_peer *authpeer = NULL; /* Matching Peer */
- int reinvite = 0;
- struct ast_party_redirecting redirecting;
- struct ast_set_party_redirecting update_redirecting;
- int supported_start = 0;
- int require_start = 0;
- char unsupported[256] = { 0, };
- struct {
- char exten[AST_MAX_EXTENSION];
- char context[AST_MAX_CONTEXT];
- } pickup = {
- .exten = "",
- };
- /* Find out what they support */
- if (!p->sipoptions) {
- const char *supported = NULL;
- do {
- supported = __get_header(req, "Supported", &supported_start);
- if (!ast_strlen_zero(supported)) {
- p->sipoptions |= parse_sip_options(supported, NULL, 0);
- }
- } while (!ast_strlen_zero(supported));
- }
- /* Find out what they require */
- do {
- required = __get_header(req, "Require", &require_start);
- if (!ast_strlen_zero(required)) {
- required_profile |= parse_sip_options(required, unsupported, ARRAY_LEN(unsupported));
- }
- } while (!ast_strlen_zero(required));
- /* If there are any options required that we do not support,
- * then send a 420 with only those unsupported options listed */
- if (!ast_strlen_zero(unsupported)) {
- transmit_response_with_unsupported(p, "420 Bad extension (unsupported)", req, unsupported);
- ast_log(LOG_WARNING, "Received SIP INVITE with unsupported required extension: required:%s unsupported:%s\n", required, unsupported);
- p->invitestate = INV_COMPLETED;
- if (!p->lastinvite) {
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- }
- res = -1;
- goto request_invite_cleanup;
- }
- /* The option tags may be present in Supported: or Require: headers.
- Include the Require: option tags for further processing as well */
- p->sipoptions |= required_profile;
- p->reqsipoptions = required_profile;
- /* Check if this is a loop */
- if (ast_test_flag(&p->flags[0], SIP_OUTGOING) && p->owner && (p->invitestate != INV_TERMINATED && p->invitestate != INV_CONFIRMED) && ast_channel_state(p->owner) != AST_STATE_UP) {
- /* This is a call to ourself. Send ourselves an error code and stop
- processing immediately, as SIP really has no good mechanism for
- being able to call yourself */
- /* If pedantic is on, we need to check the tags. If they're different, this is
- in fact a forked call through a SIP proxy somewhere. */
- int different;
- const char *initial_rlpart2 = REQ_OFFSET_TO_STR(&p->initreq, rlpart2);
- const char *this_rlpart2 = REQ_OFFSET_TO_STR(req, rlpart2);
- if (sip_cfg.pedanticsipchecking)
- different = sip_uri_cmp(initial_rlpart2, this_rlpart2);
- else
- different = strcmp(initial_rlpart2, this_rlpart2);
- if (!different) {
- transmit_response(p, "482 Loop Detected", req);
- p->invitestate = INV_COMPLETED;
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- res = INV_REQ_FAILED;
- goto request_invite_cleanup;
- } else {
- /*! This is a spiral. What we need to do is to just change the outgoing INVITE
- * so that it now routes to the new Request URI. Since we created the INVITE ourselves
- * that should be all we need to do.
- *
- * \todo XXX This needs to be reviewed. YOu don't change the request URI really, you route the packet
- * correctly instead...
- */
- char *uri = ast_strdupa(this_rlpart2);
- char *at = strchr(uri, '@');
- char *peerorhost;
- ast_debug(2, "Potential spiral detected. Original RURI was %s, new RURI is %s\n", initial_rlpart2, this_rlpart2);
- transmit_response(p, "100 Trying", req);
- if (at) {
- *at = '\0';
- }
- /* Parse out "sip:" */
- if ((peerorhost = strchr(uri, ':'))) {
- *peerorhost++ = '\0';
- }
- ast_string_field_set(p, theirtag, NULL);
- /* Treat this as if there were a call forward instead...
- */
- ast_channel_call_forward_set(p->owner, peerorhost);
- ast_queue_control(p->owner, AST_CONTROL_BUSY);
- res = INV_REQ_FAILED;
- goto request_invite_cleanup;
- }
- }
- if (!req->ignore && p->pendinginvite) {
- if (!ast_test_flag(&p->flags[0], SIP_OUTGOING) && (p->invitestate == INV_COMPLETED || p->invitestate == INV_TERMINATED)) {
- /* What do these circumstances mean? We have received an INVITE for an "incoming" dialog for which we
- * have sent a final response. We have not yet received an ACK, though (which is why p->pendinginvite is non-zero).
- * We also know that the INVITE is not a retransmission, because otherwise the "ignore" flag would be set.
- * This means that either we are receiving a reinvite for a terminated dialog, or we are receiving an INVITE with
- * credentials based on one we challenged earlier.
- *
- * The action to take in either case is to treat the INVITE as though it contains an implicit ACK for the previous
- * transaction. Calling __sip_ack will take care of this by clearing the p->pendinginvite and removing the response
- * from the previous transaction from the list of outstanding packets.
- */
- __sip_ack(p, p->pendinginvite, 1, 0);
- } else {
- /* We already have a pending invite. Sorry. You are on hold. */
- p->glareinvite = seqno;
- if (p->rtp && find_sdp(req)) {
- struct ast_sockaddr addr;
- if (get_ip_and_port_from_sdp(req, SDP_AUDIO, &addr)) {
- ast_log(LOG_WARNING, "Failed to set an alternate media source on glared reinvite. Audio may not work properly on this call.\n");
- } else {
- ast_rtp_instance_set_alt_remote_address(p->rtp, &addr);
- }
- if (p->vrtp) {
- if (get_ip_and_port_from_sdp(req, SDP_VIDEO, &addr)) {
- ast_log(LOG_WARNING, "Failed to set an alternate media source on glared reinvite. Video may not work properly on this call.\n");
- } else {
- ast_rtp_instance_set_alt_remote_address(p->vrtp, &addr);
- }
- }
- }
- transmit_response_reliable(p, "491 Request Pending", req);
- check_via(p, req);
- ast_debug(1, "Got INVITE on call where we already have pending INVITE, deferring that - %s\n", p->callid);
- /* Don't destroy dialog here */
- res = INV_REQ_FAILED;
- goto request_invite_cleanup;
- }
- }
- p_replaces = sip_get_header(req, "Replaces");
- if (!ast_strlen_zero(p_replaces)) {
- /* We have a replaces header */
- char *ptr;
- char *fromtag = NULL;
- char *totag = NULL;
- char *start, *to;
- int error = 0;
- if (p->owner) {
- ast_debug(3, "INVITE w Replaces on existing call? Refusing action. [%s]\n", p->callid);
- transmit_response_reliable(p, "400 Bad request", req); /* The best way to not not accept the transfer */
- check_via(p, req);
- copy_request(&p->initreq, req);
- /* Do not destroy existing call */
- res = INV_REQ_ERROR;
- goto request_invite_cleanup;
- }
- if (sipdebug)
- ast_debug(3, "INVITE part of call transfer. Replaces [%s]\n", p_replaces);
- /* Create a buffer we can manipulate */
- replace_id = ast_strdupa(p_replaces);
- ast_uri_decode(replace_id, ast_uri_sip_user);
- if (!sip_refer_alloc(p)) {
- transmit_response_reliable(p, "500 Server Internal Error", req);
- append_history(p, "Xfer", "INVITE/Replace Failed. Out of memory.");
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- p->invitestate = INV_COMPLETED;
- res = INV_REQ_ERROR;
- check_via(p, req);
- copy_request(&p->initreq, req);
- goto request_invite_cleanup;
- }
- /* Todo: (When we find phones that support this)
- if the replaces header contains ";early-only"
- we can only replace the call in early
- stage, not after it's up.
- If it's not in early mode, 486 Busy.
- */
- /* Skip leading whitespace */
- replace_id = ast_skip_blanks(replace_id);
- start = replace_id;
- while ( (ptr = strsep(&start, ";")) ) {
- ptr = ast_skip_blanks(ptr); /* XXX maybe unnecessary ? */
- if ( (to = strcasestr(ptr, "to-tag=") ) )
- totag = to + 7; /* skip the keyword */
- else if ( (to = strcasestr(ptr, "from-tag=") ) ) {
- fromtag = to + 9; /* skip the keyword */
- fromtag = strsep(&fromtag, "&"); /* trim what ? */
- }
- }
- if (sipdebug)
- ast_debug(4, "Invite/replaces: Will use Replace-Call-ID : %s Fromtag: %s Totag: %s\n",
- replace_id,
- fromtag ? fromtag : "<no from tag>",
- totag ? totag : "<no to tag>");
- /* Try to find call that we are replacing.
- If we have a Replaces header, we need to cancel that call if we succeed with this call.
- First we cheat a little and look for a magic call-id from phones that support
- dialog-info+xml so we can do technology independent pickup... */
- if (strncmp(replace_id, "pickup-", 7) == 0) {
- struct sip_pvt *subscription = NULL;
- replace_id += 7; /* Worst case we are looking at \0 */
- if ((subscription = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) {
- 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 {
- 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));
- sip_pvt_unlock(subscription);
- if (subscription->owner) {
- ast_channel_unlock(subscription->owner);
- }
- subscription = dialog_unref(subscription, "unref dialog subscription");
- }
- }
- /* This locks both refer_call pvt and refer_call pvt's owner!!!*/
- if (!error && ast_strlen_zero(pickup.exten) && (p->refer->refer_call = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) {
- 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;
- } else {
- refer_locked = 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 (p->refer->refer_call == 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) && !p->refer->refer_call->owner) {
- /* 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(p->refer->refer_call->owner) != AST_STATE_RINGING && ast_channel_state(p->refer->refer_call->owner) != AST_STATE_RING && ast_channel_state(p->refer->refer_call->owner) != 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);
- sip_pvt_unlock(p);
- if (p->refer->refer_call) {
- sip_pvt_unlock(p->refer->refer_call);
- if (p->refer->refer_call->owner) {
- ast_channel_unlock(p->refer->refer_call->owner);
- }
- p->refer->refer_call = dialog_unref(p->refer->refer_call, "unref dialog p->refer->refer_call");
- }
- refer_locked = 0;
- 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_copy(p->jointcaps, p->caps);
- 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_control(p->owner, AST_CONTROL_UNHOLD);
- /* 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_copy(p->jointcaps, p->caps);
- ast_debug(2, "No SDP in Invite, third party call control\n");
- }
- /* Initialize the context if it hasn't been already */
- if (ast_strlen_zero(p->context))
- ast_string_field_set(p, context, sip_cfg.default_context);
- /* Check number of concurrent calls -vs- incoming limit HERE */
- ast_debug(1, "Checking SIP call limits for device %s\n", p->username);
- if ((res = update_call_counter(p, INC_CALL_LIMIT))) {
- if (res < 0) {
- ast_log(LOG_NOTICE, "Failed to place call for device %s, too many calls\n", p->username);
- transmit_response_reliable(p, "480 Temporarily Unavailable (Call limit) ", req);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- p->invitestate = INV_COMPLETED;
- res = AUTH_SESSION_LIMIT;
- }
- goto request_invite_cleanup;
- }
- gotdest = get_destination(p, NULL, &cc_recall_core_id); /* Get destination right away */
- extract_uri(p, req); /* Get the Contact URI */
- build_contact(p, req, 1); /* Build our contact header */
- if (p->rtp) {
- ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
- ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
- }
- if (!replace_id && (gotdest != SIP_GET_DEST_EXTEN_FOUND)) { /* No matching extension found */
- switch(gotdest) {
- case SIP_GET_DEST_INVALID_URI:
- transmit_response_reliable(p, "416 Unsupported URI scheme", req);
- break;
- case SIP_GET_DEST_EXTEN_MATCHMORE:
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)
- == SIP_PAGE2_ALLOWOVERLAP_YES) {
- transmit_response_reliable(p, "484 Address Incomplete", req);
- break;
- }
- /*
- * XXX We would have to implement collecting more digits in
- * chan_sip for any other schemes of overlap dialing.
- *
- * For SIP_PAGE2_ALLOWOVERLAP_DTMF it is better to do this in
- * the dialplan using the Incomplete application rather than
- * having the channel driver do it.
- */
- /* Fall through */
- case SIP_GET_DEST_EXTEN_NOT_FOUND:
- {
- char *decoded_exten = ast_strdupa(p->exten);
- transmit_response_reliable(p, "404 Not Found", req);
- ast_uri_decode(decoded_exten, ast_uri_sip_user);
- ast_log(LOG_NOTICE, "Call from '%s' (%s) to extension"
- " '%s' rejected because extension not found in context '%s'.\n",
- S_OR(p->username, p->peername), ast_sockaddr_stringify(&p->recv), decoded_exten, p->context);
- }
- break;
- case SIP_GET_DEST_REFUSED:
- default:
- transmit_response_reliable(p, "403 Forbidden", req);
- } /* end switch */
- p->invitestate = INV_COMPLETED;
- update_call_counter(p, DEC_CALL_LIMIT);
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- res = INV_REQ_FAILED;
- goto request_invite_cleanup;
- } else {
- /* If no extension was specified, use the s one */
- /* Basically for calling to IP/Host name only */
- if (ast_strlen_zero(p->exten))
- ast_string_field_set(p, exten, "s");
- /* Initialize our tag */
- make_our_tag(p);
- if (handle_request_invite_st(p, req, required, reinvite)) {
- p->invitestate = INV_COMPLETED;
- sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- res = INV_REQ_ERROR;
- goto request_invite_cleanup;
- }
- /* First invitation - create the channel. Allocation
- * failures are handled below. */
- c = sip_new(p, AST_STATE_DOWN, S_OR(p->peername, NULL), NULL, 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, addr, seqno, nounlock);
- refer_locked = 0;
- 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);
- 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, ast_pickup_ext())) { /* Call to extension -start pbx on this call */
- enum ast_pbx_result result;
- result = ast_pbx_start(c);
- switch(result) {
- case AST_PBX_FAILED:
- sip_alreadygone(p);
- ast_log(LOG_WARNING, "Failed to start PBX :(\n");
- p->invitestate = INV_COMPLETED;
- transmit_response_reliable(p, "503 Unavailable", req);
- break;
- case AST_PBX_CALL_LIMIT:
- sip_alreadygone(p);
- ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
- p->invitestate = INV_COMPLETED;
- transmit_response_reliable(p, "480 Temporarily Unavailable", req);
- res = AUTH_SESSION_LIMIT;
- break;
- case AST_PBX_SUCCESS:
- /* nothing to do */
- break;
- }
- if (result) {
- /* Unlock locks so ast_hangup can do its magic */
- ast_channel_unlock(c);
- *nounlock = 1;
- sip_pvt_unlock(p);
- ast_hangup(c);
- sip_pvt_lock(p);
- c = NULL;
- }
- } else { /* Pickup call in call group */
- if (sip_pickup(c)) {
- ast_log(LOG_WARNING, "Failed to start Group pickup by %s\n", ast_channel_name(c));
- transmit_response_reliable(p, "480 Temporarily Unavailable", req);
- sip_alreadygone(p);
- ast_channel_hangupcause_set(c, AST_CAUSE_FAILURE);
- /* Unlock locks so ast_hangup can do its magic */
- ast_channel_unlock(c);
- *nounlock = 1;
- p->invitestate = INV_COMPLETED;
- sip_pvt_unlock(p);
- ast_hangup(c);
- sip_pvt_lock(p);
- c = NULL;
- }
- }
- break;
- case AST_STATE_RING:
- transmit_provisional_response(p, "100 Trying", req, 0);
- p->invitestate = INV_PROCEEDING;
- break;
- case AST_STATE_RINGING:
- transmit_provisional_response(p, "180 Ringing", req, 0);
- p->invitestate = INV_PROCEEDING;
- break;
- case AST_STATE_UP:
- ast_debug(2, "%s: This call is UP.... \n", ast_channel_name(c));
- transmit_response(p, "100 Trying", req);
- if (p->t38.state == T38_PEER_REINVITE) {
- if (p->t38id > -1) {
- /* reset t38 abort timer */
- AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "remove ref for t38id"));
- }
- p->t38id = ast_sched_add(sched, 5000, sip_t38_abort, dialog_ref(p, "passing dialog ptr into sched structure based on t38id for sip_t38_abort."));
- } else if (p->t38.state == T38_ENABLED) {
- ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
- transmit_response_with_t38_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL)));
- } else if ((p->t38.state == T38_DISABLED) || (p->t38.state == T38_REJECTED)) {
- /* If this is not a re-invite or something to ignore - it's critical */
- if (p->srtp && !ast_test_flag(p->srtp, 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_is_empty(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 (refer_locked && p->refer && p->refer->refer_call) {
- sip_pvt_unlock(p->refer->refer_call);
- if (p->refer->refer_call->owner) {
- ast_channel_unlock(p->refer->refer_call->owner);
- }
- p->refer->refer_call = dialog_unref(p->refer->refer_call, "unref dialog p->refer->refer_call");
- }
- 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 sip_dual *current, struct sip_request *req, uint32_t seqno, int *nounlock)
- {
- struct sip_dual target; /* Chan 1: Call from tranferer to Asterisk */
- /* Chan 2: Call from Asterisk to target */
- int res = 0;
- struct sip_pvt *targetcall_pvt;
- struct ast_party_connected_line connected_to_transferee;
- struct ast_party_connected_line connected_to_target;
- char transferer_linkedid[32];
- struct ast_channel *chans[2];
- /* Check if the call ID of the replaces header does exist locally */
- if (!(targetcall_pvt = get_sip_pvt_byid_locked(transferer->refer->replaces_callid, transferer->refer->replaces_callid_totag,
- transferer->refer->replaces_callid_fromtag))) {
- 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;
- }
- /* Ok, we can accept this transfer */
- append_history(transferer, "Xfer", "Refer accepted");
- if (!targetcall_pvt->owner) { /* 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;
- sip_pvt_unlock(targetcall_pvt);
- if (targetcall_pvt)
- ao2_t_ref(targetcall_pvt, -1, "Drop targetcall_pvt pointer");
- return -1;
- }
- /* We have a channel, find the bridge */
- target.chan1 = ast_channel_ref(targetcall_pvt->owner); /* Transferer to Asterisk */
- target.chan2 = ast_bridged_channel(targetcall_pvt->owner); /* Asterisk to target */
- if (target.chan2) {
- ast_channel_ref(target.chan2);
- }
- if (!target.chan2 || !(ast_channel_state(target.chan2) == AST_STATE_UP || ast_channel_state(target.chan2) == AST_STATE_RINGING) ) {
- /* Wrong state of new channel */
- if (target.chan2)
- ast_debug(4, "SIP attended transfer: Error: Wrong state of target call: %s\n", ast_state2str(ast_channel_state(target.chan2)));
- else if (ast_channel_state(target.chan1) != AST_STATE_RING)
- ast_debug(4, "SIP attended transfer: Error: No target channel\n");
- else
- ast_debug(4, "SIP attended transfer: Attempting transfer in ringing state\n");
- }
- /* Transfer */
- if (sipdebug) {
- if (current->chan2) /* We have two bridges */
- ast_debug(4, "SIP attended transfer: trying to bridge %s and %s\n", ast_channel_name(target.chan1), ast_channel_name(current->chan2));
- else /* One bridge, propably transfer of IVR/voicemail etc */
- ast_debug(4, "SIP attended transfer: trying to make %s take over (masq) %s\n", ast_channel_name(target.chan1), ast_channel_name(current->chan1));
- }
- ast_set_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */
- ast_copy_string(transferer_linkedid, ast_channel_linkedid(transferer->owner), sizeof(transferer_linkedid));
- /* Perform the transfer */
- chans[0] = transferer->owner;
- chans[1] = target.chan1;
- ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans,
- "TransferMethod: SIP\r\n"
- "TransferType: Attended\r\n"
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "SIP-Callid: %s\r\n"
- "TargetChannel: %s\r\n"
- "TargetUniqueid: %s\r\n",
- ast_channel_name(transferer->owner),
- ast_channel_uniqueid(transferer->owner),
- transferer->callid,
- ast_channel_name(target.chan1),
- ast_channel_uniqueid(target.chan1));
- ast_party_connected_line_init(&connected_to_transferee);
- ast_party_connected_line_init(&connected_to_target);
- /* No need to lock current->chan1 here since it was locked in sipsock_read */
- ast_party_connected_line_copy(&connected_to_transferee, ast_channel_connected(current->chan1));
- /* No need to lock target.chan1 here since it was locked in get_sip_pvt_byid_locked */
- ast_party_connected_line_copy(&connected_to_target, ast_channel_connected(target.chan1));
- /* Reset any earlier private connected id representation */
- ast_party_id_reset(&connected_to_transferee.priv);
- ast_party_id_reset(&connected_to_target.priv);
- connected_to_target.source = connected_to_transferee.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
- res = attempt_transfer(current, &target);
- if (res) {
- /* Failed transfer */
- transmit_notify_with_sipfrag(transferer, seqno, "486 Busy Here", TRUE);
- append_history(transferer, "Xfer", "Refer failed");
- ast_clear_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
- /* if transfer failed, go ahead and unlock targetcall_pvt and it's owner channel */
- sip_pvt_unlock(targetcall_pvt);
- ast_channel_unlock(target.chan1);
- } else {
- /* Transfer succeeded! */
- const char *xfersound = pbx_builtin_getvar_helper(target.chan1, "ATTENDED_TRANSFER_COMPLETE_SOUND");
- /* target.chan1 was locked in get_sip_pvt_byid_locked, do not unlock target.chan1 before this */
- ast_cel_report_event(target.chan1, AST_CEL_ATTENDEDTRANSFER, NULL, transferer_linkedid, target.chan2);
- /* Tell transferer that we're done. */
- transmit_notify_with_sipfrag(transferer, seqno, "200 OK", TRUE);
- append_history(transferer, "Xfer", "Refer succeeded");
- transferer->refer->status = REFER_200OK;
- if (target.chan2 && !ast_strlen_zero(xfersound) && ast_streamfile(target.chan2, xfersound, ast_channel_language(target.chan2)) >= 0) {
- ast_waitstream(target.chan2, "");
- }
- /* By forcing the masquerade, we know that target.chan1 and target.chan2 are bridged. We then
- * can queue connected line updates where they need to go.
- *
- * before a masquerade, all channel and pvt locks must be unlocked. Any recursive
- * channel locks held before this function invalidates channel container locking order.
- * Since we are unlocking both the pvt (transferer) and its owner channel (current.chan1)
- * it is possible for current.chan1 to be destroyed in the pbx thread. To prevent this
- * we must give c a reference before any unlocking takes place.
- */
- ast_channel_ref(current->chan1);
- ast_channel_unlock(current->chan1); /* current.chan1 is p->owner before the masq, it was locked by socket_read()*/
- ast_channel_unlock(target.chan1);
- *nounlock = 1; /* we just unlocked the dialog's channel and have no plans of locking it again. */
- sip_pvt_unlock(targetcall_pvt);
- sip_pvt_unlock(transferer);
- ast_do_masquerade(target.chan1);
- if (target.chan2) {
- ast_indicate(target.chan2, AST_CONTROL_UNHOLD);
- }
- if (current->chan2 && ast_channel_state(current->chan2) == AST_STATE_RING) {
- ast_indicate(target.chan1, AST_CONTROL_RINGING);
- }
- if (target.chan2) {
- ast_channel_queue_connected_line_update(target.chan1, &connected_to_transferee, NULL);
- ast_channel_queue_connected_line_update(target.chan2, &connected_to_target, NULL);
- } else {
- /* Since target.chan1 isn't actually connected to another channel, there is no way for us
- * to queue a frame so that its connected line status will be updated.
- *
- * Instead, we use the somewhat hackish approach of using a special control frame type that
- * instructs ast_read to perform a specific action. In this case, the frame we queue tells
- * ast_read to call the connected line interception macro configured for target.chan1.
- */
- struct ast_control_read_action_payload *frame_payload;
- int payload_size;
- int frame_size;
- unsigned char connected_line_data[1024];
- payload_size = ast_connected_line_build_data(connected_line_data,
- sizeof(connected_line_data), &connected_to_target, NULL);
- frame_size = payload_size + sizeof(*frame_payload);
- if (payload_size != -1) {
- frame_payload = ast_alloca(frame_size);
- frame_payload->payload_size = payload_size;
- memcpy(frame_payload->payload, connected_line_data, payload_size);
- frame_payload->action = AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO;
- ast_queue_control_data(target.chan1, AST_CONTROL_READ_ACTION, frame_payload, frame_size);
- }
- /* In addition to queueing the read action frame so that target.chan1's connected line info
- * will be updated, we also are going to queue a plain old connected line update on target.chan1. This
- * way, either Dial or Queue can apply this connected line update to the outgoing ringing channel.
- */
- ast_channel_queue_connected_line_update(target.chan1, &connected_to_transferee, NULL);
- }
- sip_pvt_lock(transferer); /* the transferer pvt is expected to remain locked on return */
- ast_channel_unref(current->chan1);
- }
- /* at this point if the transfer is successful only the transferer pvt should be locked. */
- ast_party_connected_line_free(&connected_to_target);
- ast_party_connected_line_free(&connected_to_transferee);
- ast_channel_unref(target.chan1);
- if (target.chan2) {
- ast_channel_unref(target.chan2);
- }
- if (targetcall_pvt)
- ao2_t_ref(targetcall_pvt, -1, "drop targetcall_pvt");
- return 1;
- }
- /*! \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)
- {
- /*!
- * Chan1: Call between asterisk and transferer
- * Chan2: Call between asterisk and transferee
- */
- struct sip_dual current = { 0, };
- struct ast_channel *chans[2] = { 0, };
- char *refer_to = NULL;
- char *refer_to_domain = NULL;
- char *refer_to_context = NULL;
- char *referred_by = NULL;
- char *callid = NULL;
- int localtransfer = 0;
- int attendedtransfer = 0;
- int res = 0;
- struct ast_party_redirecting redirecting;
- struct ast_set_party_redirecting update_redirecting;
- 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");
- }
- res = 0;
- goto handle_refer_cleanup;
- }
- /* 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 */
- res = 0;
- goto handle_refer_cleanup;
- }
- 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.");
- res = 0;
- goto handle_refer_cleanup;
- }
- /* Allocate memory for call transfer data */
- if (!sip_refer_alloc(p)) {
- transmit_response(p, "500 Internal Server Error", req);
- append_history(p, "Xfer", "Refer failed. Memory allocation error.");
- res = -3;
- goto handle_refer_cleanup;
- }
- 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;
- }
- res = 0;
- goto handle_refer_cleanup;
- }
- 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) {
- goto handle_refer_cleanup;
- }
- /* If this is a blind transfer, we have the following
- channels to work with:
- - chan1, chan2: The current call between transferer and transferee (2 channels)
- - target_channel: A new call from the transferee to the target (1 channel)
- We need to stay tuned to what happens in order to be able
- to bring back the call to the transferer */
- /* If this is a attended transfer, we should have all call legs within reach:
- - chan1, chan2: The call between the transferer and transferee (2 channels)
- - target_channel, targetcall_pvt: The call between the transferer and the target (2 channels)
- We want to bridge chan2 with targetcall_pvt!
-
- The replaces call id in the refer message points
- to the call leg between Asterisk and the transferer.
- So we need to connect the target and the transferee channel
- and hangup the two other channels silently
-
- If the target is non-local, the call ID could be on a remote
- machine and we need to send an INVITE with replaces to the
- target. We basically handle this as a blind transfer
- and let the sip_call function catch that we need replaces
- header in the INVITE.
- */
- /* Get the transferer's channel */
- chans[0] = current.chan1 = p->owner;
- /* Find the other part of the bridge (2) - transferee */
- chans[1] = current.chan2 = ast_bridged_channel(current.chan1);
- ast_channel_ref(current.chan1);
- if (current.chan2) {
- ast_channel_ref(current.chan2);
- }
- if (sipdebug) {
- ast_debug(3, "SIP %s transfer: Transferer channel %s, transferee channel %s\n",
- p->refer->attendedtransfer ? "attended" : "blind",
- ast_channel_name(current.chan1),
- current.chan2 ? ast_channel_name(current.chan2) : "<none>");
- }
- if (!current.chan2 && !p->refer->attendedtransfer) {
- /* No bridged channel, propably IVR or echo or similar... */
- /* Guess we should masquerade or something here */
- /* Until we figure it out, refuse transfer of such calls */
- if (sipdebug) {
- ast_debug(3, "Refused SIP transfer on non-bridged channel.\n");
- }
- p->refer->status = REFER_FAILED;
- append_history(p, "Xfer", "Refer failed. Non-bridged channel.");
- transmit_response(p, "603 Declined", req);
- res = -1;
- goto handle_refer_cleanup;
- }
- if (current.chan2) {
- if (sipdebug) {
- ast_debug(4, "Got SIP transfer, applying to bridged peer '%s'\n", ast_channel_name(current.chan2));
- }
- ast_queue_control(current.chan1, AST_CONTROL_UNHOLD);
- }
- 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, ¤t, req, seqno, nounlock))) {
- goto handle_refer_cleanup; /* We're done with the transfer */
- }
- /* 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_domain = ast_strdupa(p->refer->refer_to_domain);
- refer_to_context = ast_strdupa(p->refer->refer_to_context);
- referred_by = ast_strdupa(p->refer->referred_by);
- callid = ast_strdupa(p->callid);
- localtransfer = p->refer->localtransfer;
- attendedtransfer = p->refer->attendedtransfer;
- if (!*nounlock) {
- ast_channel_unlock(p->owner);
- *nounlock = 1;
- }
- sip_pvt_unlock(p);
- /* Parking a call. DO NOT hold any locks while calling ast_parking_ext_valid() */
- if (localtransfer && ast_parking_ext_valid(refer_to, current.chan1, refer_to_context)) {
- sip_pvt_lock(p);
- ast_clear_flag(&p->flags[0], SIP_GOTREFER);
- p->refer->status = REFER_200OK;
- append_history(p, "Xfer", "REFER to call parking.");
- sip_pvt_unlock(p);
- ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans,
- "TransferMethod: SIP\r\n"
- "TransferType: Blind\r\n"
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "SIP-Callid: %s\r\n"
- "TargetChannel: %s\r\n"
- "TargetUniqueid: %s\r\n"
- "TransferExten: %s\r\n"
- "Transfer2Parking: Yes\r\n",
- ast_channel_name(current.chan1),
- ast_channel_uniqueid(current.chan1),
- callid,
- ast_channel_name(current.chan2),
- ast_channel_uniqueid(current.chan2),
- refer_to);
- if (sipdebug) {
- ast_debug(4, "SIP transfer to parking: trying to park %s. Parked by %s\n", ast_channel_name(current.chan2), ast_channel_name(current.chan1));
- }
- /* DO NOT hold any locks while calling sip_park */
- if (sip_park(current.chan2, current.chan1, req, seqno, refer_to, refer_to_context)) {
- sip_pvt_lock(p);
- transmit_notify_with_sipfrag(p, seqno, "500 Internal Server Error", TRUE);
- } else {
- sip_pvt_lock(p);
- }
- goto handle_refer_cleanup;
- }
- /* Blind transfers and remote attended xfers.
- * Locks should not be held while calling pbx_builtin_setvar_helper. This function
- * locks the channel being passed into it.*/
- if (current.chan1 && current.chan2) {
- ast_debug(3, "chan1->name: %s\n", ast_channel_name(current.chan1));
- pbx_builtin_setvar_helper(current.chan1, "BLINDTRANSFER", ast_channel_name(current.chan2));
- }
- if (current.chan2) {
- pbx_builtin_setvar_helper(current.chan2, "BLINDTRANSFER", ast_channel_name(current.chan1));
- pbx_builtin_setvar_helper(current.chan2, "SIPDOMAIN", refer_to_domain);
- pbx_builtin_setvar_helper(current.chan2, "SIPTRANSFER", "yes");
- /* One for the new channel */
- pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER", "yes");
- /* Attended transfer to remote host, prepare headers for the INVITE */
- if (!ast_strlen_zero(referred_by)) {
- pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REFERER", referred_by);
- }
- /* When a call is transferred to voicemail from a Digium phone, there may be
- * a Diversion header present in the REFER with an appropriate reason parameter
- * set. We need to update the redirecting information appropriately.
- */
- ast_channel_lock(p->owner);
- sip_pvt_lock(p);
- ast_party_redirecting_init(&redirecting);
- memset(&update_redirecting, 0, sizeof(update_redirecting));
- change_redirecting_information(p, req, &redirecting, &update_redirecting, FALSE);
- /* Do not hold the pvt lock during a call that causes an indicate or an async_goto.
- * Those functions lock channels which will invalidate locking order if the pvt lock
- * is held.*/
- sip_pvt_unlock(p);
- ast_channel_unlock(p->owner);
- ast_channel_update_redirecting(current.chan2, &redirecting, &update_redirecting);
- ast_party_redirecting_free(&redirecting);
- }
- sip_pvt_lock(p);
- /* Generate a Replaces string to be used in the INVITE during attended transfer */
- if (!ast_strlen_zero(p->refer->replaces_callid)) {
- char tempheader[SIPBUFSIZE];
- snprintf(tempheader, sizeof(tempheader), "%s%s%s%s%s", p->refer->replaces_callid,
- p->refer->replaces_callid_totag ? ";to-tag=" : "",
- p->refer->replaces_callid_totag,
- p->refer->replaces_callid_fromtag ? ";from-tag=" : "",
- p->refer->replaces_callid_fromtag);
- if (current.chan2) {
- sip_pvt_unlock(p);
- pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REPLACES", tempheader);
- sip_pvt_lock(p);
- }
- }
- /* Connect the call */
- /* FAKE ringing if not attended transfer */
- if (!p->refer->attendedtransfer) {
- transmit_notify_with_sipfrag(p, seqno, "180 Ringing", FALSE);
- }
- /* For blind transfer, this will lead to a new call */
- /* For attended transfer to remote host, this will lead to
- a new SIP call with a replaces header, if the dial plan allows it
- */
- if (!current.chan2) {
- /* We have no bridge, so we're talking with Asterisk somehow */
- /* We need to masquerade this call */
- /* What to do to fix this situation:
- * Set up the new call in a new channel
- * Let the new channel masq into this channel
- Please add that code here :-)
- */
- p->refer->status = REFER_FAILED;
- transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable (can't handle one-legged xfers)", TRUE);
- ast_clear_flag(&p->flags[0], SIP_GOTREFER);
- append_history(p, "Xfer", "Refer failed (only bridged calls).");
- res = -1;
- goto handle_refer_cleanup;
- }
- ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */
- sip_pvt_unlock(p);
- /* For blind transfers, move the call to the new extensions. For attended transfers on multiple
- * servers - generate an INVITE with Replaces. Either way, let the dial plan decided
- * indicate before masquerade so the indication actually makes it to the real channel
- * when using local channels with MOH passthru */
- ast_indicate(current.chan2, AST_CONTROL_UNHOLD);
- res = ast_async_goto(current.chan2, refer_to_context, refer_to, 1);
- if (!res) {
- ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans,
- "TransferMethod: SIP\r\n"
- "TransferType: Blind\r\n"
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "SIP-Callid: %s\r\n"
- "TargetChannel: %s\r\n"
- "TargetUniqueid: %s\r\n"
- "TransferExten: %s\r\n"
- "TransferContext: %s\r\n",
- ast_channel_name(current.chan1),
- ast_channel_uniqueid(current.chan1),
- callid,
- ast_channel_name(current.chan2),
- ast_channel_uniqueid(current.chan2),
- refer_to,
- refer_to_context);
- /* Success - we have a new channel */
- ast_debug(3, "%s transfer succeeded. Telling transferer.\n", attendedtransfer? "Attended" : "Blind");
- /* XXX - what to we put in CEL 'extra' for attended transfers to external systems? NULL for now */
- ast_channel_lock(current.chan1);
- ast_cel_report_event(current.chan1, p->refer->attendedtransfer? AST_CEL_ATTENDEDTRANSFER : AST_CEL_BLINDTRANSFER, NULL, p->refer->attendedtransfer ? NULL : p->refer->refer_to, current.chan2);
- ast_channel_unlock(current.chan1);
- sip_pvt_lock(p);
- transmit_notify_with_sipfrag(p, seqno, "200 Ok", TRUE);
- if (p->refer->localtransfer) {
- p->refer->status = REFER_200OK;
- }
- if (p->owner) {
- ast_channel_hangupcause_set(p->owner, AST_CAUSE_NORMAL_CLEARING);
- }
- append_history(p, "Xfer", "Refer succeeded.");
- ast_clear_flag(&p->flags[0], SIP_GOTREFER);
- /* Do not hangup call, the other side do that when we say 200 OK */
- /* We could possibly implement a timer here, auto congestion */
- res = 0;
- } else {
- sip_pvt_lock(p);
- ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Don't delay hangup */
- ast_debug(3, "%s transfer failed. Resuming original call.\n", p->refer->attendedtransfer? "Attended" : "Blind");
- append_history(p, "Xfer", "Refer failed.");
- /* Failure of some kind */
- p->refer->status = REFER_FAILED;
- transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable", TRUE);
- ast_clear_flag(&p->flags[0], SIP_GOTREFER);
- res = -1;
- }
- handle_refer_cleanup:
- if (current.chan1) {
- ast_channel_unref(current.chan1);
- }
- if (current.chan2) {
- ast_channel_unref(current.chan2);
- }
- /* Make sure we exit with the pvt locked */
- 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;
- struct ast_channel *bridged_to;
- const char *required;
- /* 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);
- /* Get RTCP quality before end of call */
- if (p->do_history || p->owner) {
- char quality_buf[AST_MAX_USER_FIELD], *quality;
- struct ast_channel *bridge = p->owner ? ast_bridged_channel(p->owner) : NULL;
- /* We need to get the lock on bridge because ast_rtp_instance_set_stats_vars will attempt
- * to lock the bridge. This may get hairy...
- */
- while (bridge && ast_channel_trylock(bridge)) {
- ast_channel_unlock(p->owner);
- do {
- /* Can't use DEADLOCK_AVOIDANCE since p is an ao2 object */
- sip_pvt_unlock(p);
- usleep(1);
- sip_pvt_lock(p);
- } while (p->owner && ast_channel_trylock(p->owner));
- bridge = p->owner ? ast_bridged_channel(p->owner) : NULL;
- }
- 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);
- 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) {
- ast_rtp_instance_set_stats_vars(p->owner, p->rtp);
- }
- }
- if (bridge) {
- struct sip_pvt *q = ast_channel_tech_pvt(bridge);
- if (IS_SIP_TECH(ast_channel_tech(bridge)) && q && q->rtp) {
- ast_rtp_instance_set_stats_vars(bridge, q->rtp);
- }
- ast_channel_unlock(bridge);
- }
- 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) {
- bridged_to = ast_bridged_channel(c);
- if (bridged_to) {
- /* Don't actually hangup here... */
- ast_queue_control(c, AST_CONTROL_UNHOLD);
- ast_channel_unlock(c); /* async_goto can do a masquerade, no locks can be held during a masq */
- ast_async_goto(bridged_to, p->context, p->refer->refer_to, 1);
- ast_channel_lock(c);
- } 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) {
- if (mailbox->event_sub) {
- ast_event_unsubscribe(mailbox->event_sub);
- }
- mailbox->event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "SIP mbox event", peer,
- AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox->mailbox,
- AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, S_OR(mailbox->context, "default"),
- AST_EVENT_IE_END);
- }
- }
- static int handle_cc_subscribe(struct sip_pvt *p, struct sip_request *req)
- {
- const char *uri = REQ_OFFSET_TO_STR(req, rlpart2);
- char *param_separator;
- struct ast_cc_agent *agent;
- struct sip_cc_agent_pvt *agent_pvt;
- const char *expires_str = sip_get_header(req, "Expires");
- int expires = -1; /* Just need it to be non-zero */
- if (!ast_strlen_zero(expires_str)) {
- sscanf(expires_str, "%30d", &expires);
- }
- if ((param_separator = strchr(uri, ';'))) {
- *param_separator = '\0';
- }
- p->subscribed = CALL_COMPLETION;
- if (!(agent = find_sip_cc_agent_by_subscribe_uri(uri))) {
- if (!expires) {
- /* Typically, if a 0 Expires reaches us and we can't find
- * the corresponding agent, it means that the CC transaction
- * has completed and so the calling side is just trying to
- * clean up its subscription. We'll just respond with a
- * 200 OK and be done with it
- */
- transmit_response(p, "200 OK", req);
- return 0;
- }
- ast_log(LOG_WARNING, "Invalid URI '%s' in CC subscribe\n", uri);
- transmit_response(p, "404 Not Found", req);
- return -1;
- }
- agent_pvt = agent->private_data;
- if (!expires) {
- /* We got sent a SUBSCRIBE and found an agent. This means that CC
- * is being canceled.
- */
- ast_cc_failed(agent->core_id, "CC is being canceled by %s", agent->device_name);
- transmit_response(p, "200 OK", req);
- ao2_ref(agent, -1);
- return 0;
- }
- agent_pvt->subscribe_pvt = dialog_ref(p, "SIP CC agent gains reference to subscription dialog");
- ast_cc_agent_accept_request(agent->core_id, "SIP caller %s has requested CC via SUBSCRIBE",
- agent->device_name);
- /* We don't send a response here. That is done in the agent's ack callback or in the
- * agent destructor, should a failure occur before we have responded
- */
- ao2_ref(agent, -1);
- return 0;
- }
- /*! \brief Handle incoming SUBSCRIBE request */
- static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, uint32_t seqno, const char *e)
- {
- int res = 0;
- struct sip_peer *authpeer = NULL;
- char *event = ast_strdupa(sip_get_header(req, "Event")); /* Get Event package name */
- int resubscribe = (p->subscribed != NONE) && !req->ignore;
- char *options;
- if (p->initreq.headers) {
- /* We already have a dialog */
- if (p->initreq.method != SIP_SUBSCRIBE) {
- /* This is a SUBSCRIBE within another SIP dialog, which we do not support */
- /* For transfers, this could happen, but since we haven't seen it happening, let us just refuse this */
- transmit_response(p, "403 Forbidden (within dialog)", req);
- /* Do not destroy session, since we will break the call if we do */
- ast_debug(1, "Got a subscription within the context of another call, can't handle that - %s (Method %s)\n", p->callid, sip_methods[p->initreq.method].text);
- return 0;
- } else if (req->debug) {
- if (resubscribe)
- ast_debug(1, "Got a re-subscribe on existing subscription %s\n", p->callid);
- else
- ast_debug(1, "Got a new subscription %s (possibly with auth) or retransmission\n", p->callid);
- }
- }
- /* Check if we have a global disallow setting on subscriptions.
- if so, we don't have to check peer settings after auth, which saves a lot of processing
- */
- if (!sip_cfg.allowsubscribe) {
- transmit_response(p, "403 Forbidden (policy)", req);
- pvt_set_needdestroy(p, "forbidden");
- return 0;
- }
- if (!req->ignore && !resubscribe) { /* Set up dialog, new subscription */
- const char *to = sip_get_header(req, "To");
- char totag[128];
- set_pvt_allowed_methods(p, req);
- /* Check to see if a tag was provided, if so this is actually a resubscription of a dialog we no longer know about */
- if (!ast_strlen_zero(to) && gettag(req, "To", totag, sizeof(totag))) {
- if (req->debug)
- ast_verbose("Received resubscription for a dialog we no longer know about. Telling remote side to subscribe again.\n");
- transmit_response(p, "481 Subscription does not exist", req);
- pvt_set_needdestroy(p, "subscription does not exist");
- return 0;
- }
- /* Use this as the basis */
- if (req->debug)
- ast_verbose("Creating new subscription\n");
- copy_request(&p->initreq, req);
- if (sipdebug)
- ast_debug(4, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
- check_via(p, req);
- build_route(p, req, 0, 0);
- } else if (req->debug && req->ignore)
- ast_verbose("Ignoring this SUBSCRIBE request\n");
- /* Find parameters to Event: header value and remove them for now */
- if (ast_strlen_zero(event)) {
- transmit_response(p, "489 Bad Event", req);
- ast_debug(2, "Received SIP subscribe for unknown event package: <none>\n");
- pvt_set_needdestroy(p, "unknown event package in subscribe");
- return 0;
- }
- if ((options = strchr(event, ';')) != NULL) {
- *options++ = '\0';
- }
- /* Handle authentication if we're new and not a retransmission. We can't just
- * use if !req->ignore, because then we'll end up sending
- * a 200 OK if someone retransmits without sending auth */
- if (p->subscribed == NONE || resubscribe) {
- res = check_user_full(p, req, SIP_SUBSCRIBE, e, XMIT_UNRELIABLE, addr, &authpeer);
- /* if an authentication response was sent, we are done here */
- if (res == AUTH_CHALLENGE_SENT) /* authpeer = NULL here */
- return 0;
- if (res != AUTH_SUCCESSFUL) {
- ast_log(LOG_NOTICE, "Failed to authenticate device %s for SUBSCRIBE\n", sip_get_header(req, "From"));
- transmit_response(p, "403 Forbidden", req);
- pvt_set_needdestroy(p, "authentication failed");
- return 0;
- }
- }
- /* At this point, we hold a reference to authpeer (if not NULL). It
- * must be released when done.
- */
- /* Check if this device is allowed to subscribe at all */
- if (!ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) {
- transmit_response(p, "403 Forbidden (policy)", req);
- pvt_set_needdestroy(p, "subscription not allowed");
- if (authpeer) {
- sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 1)");
- }
- return 0;
- }
- /* Get full contact header - this needs to be used as a request URI in NOTIFY's */
- parse_ok_contact(p, req);
- build_contact(p, req, 1);
- /* Initialize tag for new subscriptions */
- if (ast_strlen_zero(p->tag)) {
- make_our_tag(p);
- }
- if (!strcmp(event, "presence") || !strcmp(event, "dialog")) { /* Presence, RFC 3842 */
- int gotdest;
- const char *accept;
- int start = 0;
- enum subscriptiontype subscribed = NONE;
- const char *unknown_accept = NULL;
- /* Get destination right away */
- gotdest = get_destination(p, NULL, NULL);
- if (gotdest != SIP_GET_DEST_EXTEN_FOUND) {
- if (gotdest == SIP_GET_DEST_INVALID_URI) {
- transmit_response(p, "416 Unsupported URI scheme", req);
- } else {
- transmit_response(p, "404 Not Found", req);
- }
- pvt_set_needdestroy(p, "subscription target not found");
- if (authpeer) {
- sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 2)");
- }
- return 0;
- }
- /* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */
- accept = __get_header(req, "Accept", &start);
- while ((subscribed == NONE) && !ast_strlen_zero(accept)) {
- if (strstr(accept, "application/pidf+xml")) {
- if (strstr(p->useragent, "Polycom")) {
- subscribed = XPIDF_XML; /* Older versions of Polycom firmware will claim pidf+xml, but really they only support xpidf+xml */
- } else {
- subscribed = PIDF_XML; /* RFC 3863 format */
- }
- } else if (strstr(accept, "application/dialog-info+xml")) {
- subscribed = DIALOG_INFO_XML;
- /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */
- } else if (strstr(accept, "application/cpim-pidf+xml")) {
- subscribed = CPIM_PIDF_XML; /* RFC 3863 format */
- } else if (strstr(accept, "application/xpidf+xml")) {
- subscribed = XPIDF_XML; /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */
- } else {
- unknown_accept = accept;
- }
- /* check to see if there is another Accept header present */
- accept = __get_header(req, "Accept", &start);
- }
- if (!start) {
- if (p->subscribed == NONE) { /* if the subscribed field is not already set, and there is no accept header... */
- transmit_response(p, "489 Bad Event", req);
- ast_log(LOG_WARNING,"SUBSCRIBE failure: no Accept header: pvt: "
- "stateid: %d, laststate: %d, dialogver: %u, subscribecont: "
- "'%s', subscribeuri: '%s'\n",
- p->stateid,
- p->laststate,
- p->dialogver,
- p->subscribecontext,
- p->subscribeuri);
- pvt_set_needdestroy(p, "no Accept header");
- if (authpeer) {
- sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 2)");
- }
- return 0;
- }
- /* if p->subscribed is non-zero, then accept is not obligatory; according to rfc 3265 section 3.1.3, at least.
- so, we'll just let it ride, keeping the value from a previous subscription, and not abort the subscription */
- } else if (subscribed == NONE) {
- /* Can't find a format for events that we know about */
- char buf[200];
- if (!ast_strlen_zero(unknown_accept)) {
- snprintf(buf, sizeof(buf), "489 Bad Event (format %s)", unknown_accept);
- } else {
- snprintf(buf, sizeof(buf), "489 Bad Event");
- }
- transmit_response(p, buf, req);
- ast_log(LOG_WARNING,"SUBSCRIBE failure: unrecognized format:"
- "'%s' pvt: subscribed: %d, stateid: %d, laststate: %d,"
- "dialogver: %u, subscribecont: '%s', subscribeuri: '%s'\n",
- unknown_accept,
- (int)p->subscribed,
- p->stateid,
- p->laststate,
- p->dialogver,
- p->subscribecontext,
- p->subscribeuri);
- pvt_set_needdestroy(p, "unrecognized format");
- if (authpeer) {
- sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 2)");
- }
- return 0;
- } else {
- p->subscribed = subscribed;
- }
- } else if (!strcmp(event, "message-summary")) {
- int start = 0;
- int found_supported = 0;
- const char *accept;
- accept = __get_header(req, "Accept", &start);
- while (!found_supported && !ast_strlen_zero(accept)) {
- found_supported = strcmp(accept, "application/simple-message-summary") ? 0 : 1;
- if (!found_supported && (option_debug > 2)) {
- ast_debug(1, "Received SIP mailbox subscription for unknown format: %s\n", accept);
- }
- accept = __get_header(req, "Accept", &start);
- }
- 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, SIP_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 sip_transport type, int port)
- {
- if (type & SIP_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 == SIP_TRANSPORT_UDP) || !transport) {
- addr = &bindaddr;
- } else if (transport == SIP_TRANSPORT_TCP || transport == SIP_TRANSPORT_WS) {
- addr = &sip_tcp_desc.local_address;
- } else if (transport == SIP_TRANSPORT_TLS || transport == SIP_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 == SIP_TRANSPORT_UDP)) {
- return s->fd;
- }
- if ((s->type & (SIP_TRANSPORT_TCP | SIP_TRANSPORT_TLS)) &&
- (s->tcptls_session) &&
- (s->tcptls_session->fd != -1)) {
- return s->tcptls_session->fd;
- }
- if ((s->type & (SIP_TRANSPORT_WS | SIP_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 == SIP_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 == SIP_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) {
- struct ast_event *event;
- event = ast_event_get_cached(AST_EVENT_MWI,
- AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox->mailbox,
- AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, S_OR(mailbox->context, "default"),
- AST_EVENT_IE_END);
- if (!event)
- continue;
- *new += ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
- *old += ast_event_get_ie_uint(event, AST_EVENT_IE_OLDMSGS);
- ast_event_destroy(event);
- 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;
- }
- /*!
- * \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));
- manager_event(EVENT_FLAG_CALL, "SessionTimeout", "Source: RTPTimeout\r\n"
- "Channel: %s\r\nUniqueid: %s\r\n", ast_channel_name(dialog->owner), ast_channel_uniqueid(dialog->owner));
- /* Issue a softhangup */
- ast_softhangup_nolock(dialog->owner, AST_SOFTHANGUP_DEV);
- ast_channel_unlock(dialog->owner);
- /* forget the timeouts for this call, since a hangup
- has already been requested and we don't want to
- repeatedly request hangups
- */
- ast_rtp_instance_set_timeout(dialog->rtp, 0);
- ast_rtp_instance_set_hold_timeout(dialog->rtp, 0);
- if (dialog->vrtp) {
- ast_rtp_instance_set_timeout(dialog->vrtp, 0);
- ast_rtp_instance_set_hold_timeout(dialog->vrtp, 0);
- }
- /* finally unlink the dialog from dialogs_rtpcheck. */
- return CMP_MATCH;
- }
- }
- }
- return 0;
- }
- /*! \brief The SIP monitoring thread
- \note This thread monitors all the SIP sessions and peers that needs notification of mwi
- (and thus do not have a separate thread) indefinitely
- */
- static void *do_monitor(void *data)
- {
- int res;
- time_t t;
- int reloading;
- /* Add an I/O event to our SIP UDP socket */
- if (sipsock > -1) {
- sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);
- }
- /* From here on out, we die whenever asked */
- for(;;) {
- /* Check for a reload request */
- ast_mutex_lock(&sip_reload_lock);
- reloading = sip_reloading;
- sip_reloading = FALSE;
- ast_mutex_unlock(&sip_reload_lock);
- if (reloading) {
- ast_verb(1, "Reloading SIP\n");
- sip_do_reload(sip_reloadreason);
- /* Change the I/O fd of our UDP socket */
- if (sipsock > -1) {
- if (sipsock_read_id) {
- sipsock_read_id = ast_io_change(io, sipsock_read_id, sipsock, NULL, 0, NULL);
- } else {
- sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);
- }
- } else if (sipsock_read_id) {
- ast_io_remove(io, sipsock_read_id);
- sipsock_read_id = NULL;
- }
- }
- /* Check for dialogs needing to be killed */
- t = time(NULL);
- /*
- * Check dialogs with rtp and rtptimeout.
- * All dialogs which have rtp are in dialogs_rtpcheck.
- */
- ao2_t_callback(dialogs_rtpcheck, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
- dialog_checkrtp_cb, &t,
- "callback to check rtptimeout and hangup calls if necessary");
- /*
- * Check dialogs marked to be destroyed.
- * All dialogs with needdestroy set are in dialogs_needdestroy.
- */
- ao2_t_callback(dialogs_needdestroy, OBJ_NODATA | OBJ_MULTIPLE, dialog_needdestroy,
- NULL, "callback to check dialogs which need to be destroyed");
- /* XXX TODO The scheduler usage in this module does not have sufficient
- * synchronization being done between running the scheduler and places
- * scheduling tasks. As it is written, any scheduled item may not run
- * any sooner than about 1 second, regardless of whether a sooner time
- * was asked for. */
- pthread_testcancel();
- /* Wait for sched or io */
- res = ast_sched_wait(sched);
- if ((res < 0) || (res > 1000)) {
- res = 1000;
- }
- res = ast_io_wait(io, res);
- if (res > 20) {
- ast_debug(1, "chan_sip: ast_io_wait ran %d all at once\n", res);
- }
- ast_mutex_lock(&monlock);
- res = ast_sched_runq(sched);
- if (res >= 20) {
- ast_debug(1, "chan_sip: ast_sched_runq ran %d all at once\n", res);
- }
- ast_mutex_unlock(&monlock);
- }
- /* Never reached */
- return NULL;
- }
- /*! \brief Start the channel monitor thread */
- static int restart_monitor(void)
- {
- /* If we're supposed to be stopped -- stay stopped */
- if (monitor_thread == AST_PTHREADT_STOP)
- return 0;
- ast_mutex_lock(&monlock);
- if (monitor_thread == pthread_self()) {
- ast_mutex_unlock(&monlock);
- ast_log(LOG_WARNING, "Cannot kill myself\n");
- return -1;
- }
- if (monitor_thread != AST_PTHREADT_NULL && monitor_thread != AST_PTHREADT_STOP) {
- /* Wake up the thread */
- pthread_kill(monitor_thread, SIGURG);
- } else {
- /* Start a new monitor */
- if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
- ast_mutex_unlock(&monlock);
- ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
- return -1;
- }
- }
- ast_mutex_unlock(&monlock);
- return 0;
- }
- static void acl_change_event_cb(const struct ast_event *event, void *userdata)
- {
- 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);
- }
- manager_event(EVENT_FLAG_CALL, "SessionTimeout", "Source: SIPSessionTimer\r\n"
- "Channel: %s\r\nUniqueid: %s\r\n", ast_channel_name(p->owner), ast_channel_uniqueid(p->owner));
- 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 == SIP_TRANSPORT_UDP)) {
- res = ast_sendto(peer->socket.fd, keepalive, sizeof(keepalive), 0, &peer->addr);
- } else if ((peer->socket.type & (SIP_TRANSPORT_TCP | SIP_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 == SIP_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);
- }
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unreachable\r\nTime: %d\r\n", peer->name, -1);
- 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);
- /* 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_channel *requestor, const char *dest, int *cause)
- {
- struct sip_pvt *p;
- struct ast_channel *tmpc = NULL;
- char *ext = NULL, *host;
- char tmp[256];
- char tmp2[256];
- char *dnid;
- char *secret = NULL;
- char *md5secret = NULL;
- char *authname = NULL;
- char *trans = NULL;
- char dialstring[256];
- char *remote_address;
- enum sip_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_FORMAT_TYPE_AUDIO))) {
- ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format %s while capability is %s\n",
- ast_getformatname_multiple(tmp, sizeof(tmp), cap),
- ast_getformatname_multiple(tmp2, sizeof(tmp2), sip_cfg.caps));
- *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_getformatname_multiple(tmp, sizeof(tmp), cap));
- 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 = SIP_TRANSPORT_TCP;
- else if (!strcasecmp(trans, "tls"))
- transport = SIP_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 = SIP_TRANSPORT_UDP;
- }
- } else { /* use default */
- transport = SIP_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(p->prefcaps, cap);
- ast_format_cap_joint_copy(cap, p->caps, p->jointcaps);
- sip_pvt_lock(p);
- tmpc = sip_new(p, AST_STATE_DOWN, host, requestor ? ast_channel_linkedid(requestor) : NULL, callid); /* Place the call */
- if (callid) {
- callid = ast_callid_unref(callid);
- }
- if (sip_cfg.callevents)
- manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate",
- "Channel: %s\r\nChanneltype: %s\r\nSIPcallid: %s\r\nSIPfullcontact: %s\r\nPeername: %s\r\n",
- p->owner ? ast_channel_name(p->owner) : "", "SIP", p->callid, p->fullcontact, p->peername);
- 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, "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, SIP_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_copy(peer->caps, sip_cfg.caps);
- 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;
- peer->prefs = default_prefs;
- 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_nolock())) {
- 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;
- peer->prefs = default_prefs;
- reg_source_db(peer);
- return peer;
- }
- /*! \todo document this function */
- static void add_peer_mailboxes(struct sip_peer *peer, const char *value)
- {
- char *next, *mbox, *context;
- next = ast_strdupa(value);
- while ((mbox = context = strsep(&next, ","))) {
- struct sip_mailbox *mailbox;
- int duplicate = 0;
- /* remove leading/trailing whitespace from mailbox string */
- mbox = ast_strip(mbox);
- strsep(&context, "@");
- 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->mailbox, mbox) && !strcmp(S_OR(mailbox->context, ""), S_OR(context, ""))) {
- duplicate = 1;
- break;
- }
- }
- if (duplicate) {
- continue;
- }
- if (!(mailbox = ast_calloc(1, sizeof(*mailbox) + strlen(mbox) + strlen(S_OR(context, ""))))) {
- continue;
- }
- if (!ast_strlen_zero(context)) {
- mailbox->context = mailbox->mailbox + strlen(mbox) + 1;
- strcpy(mailbox->context, context); /* SAFE */
- }
- strcpy(mailbox->mailbox, 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->caps = ast_format_cap_alloc_nolock())) {
- 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 |= SIP_TRANSPORT_UDP;
- } else if (!strncasecmp(trans, "wss", 3)) {
- peer->transports |= SIP_TRANSPORT_WSS;
- } else if (!strncasecmp(trans, "ws", 2)) {
- peer->transports |= SIP_TRANSPORT_WS;
- } else if (sip_cfg.tcp_enabled && !strncasecmp(trans, "tcp", 3)) {
- peer->transports |= SIP_TRANSPORT_TCP;
- } else if (default_tls_cfg.enabled && !strncasecmp(trans, "tls", 3)) {
- peer->transports |= SIP_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")) {
- if (!ast_strlen_zero(v->value) && ast_get_ip(&peer->defaddr, v->value)) {
- sip_unref_peer(peer, "sip_unref_peer: from build_peer defaultip");
- return NULL;
- }
- } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny") || !strcasecmp(v->name, "acl")) {
- int ha_error = 0;
- if (!ast_strlen_zero(v->value)) {
- ast_append_acl(v->name, v->value, &peer->acl, &ha_error, &acl_change_subscription_needed);
- }
- if (ha_error) {
- ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
- }
- } else if (!strcasecmp(v->name, "contactpermit") || !strcasecmp(v->name, "contactdeny") || !strcasecmp(v->name, "contactacl")) {
- int ha_error = 0;
- if (!ast_strlen_zero(v->value)) {
- ast_append_acl(v->name + 7, v->value, &peer->contactacl, &ha_error, &acl_change_subscription_needed);
- }
- if (ha_error) {
- ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
- }
- } else if (!strcasecmp(v->name, "directmediapermit") || !strcasecmp(v->name, "directmediadeny") || !strcasecmp(v->name, "directmediaacl")) {
- int ha_error = 0;
- ast_append_acl(v->name + 11, v->value, &peer->directmediaacl, &ha_error, &acl_change_subscription_needed);
- if (ha_error) {
- ast_log(LOG_ERROR, "Bad directmedia ACL entry in configuration line %d : %s\n", v->lineno, v->value);
- }
- } else if (!strcasecmp(v->name, "port")) {
- peer->portinuri = 1;
- if (!(port = port_str2int(v->value, 0))) {
- if (realtime) {
- /* If stored as integer, could be 0 for some DBs (notably MySQL) */
- peer->portinuri = 0;
- } else {
- ast_log(LOG_WARNING, "Invalid peer port configuration at line %d : %s\n", v->lineno, v->value);
- }
- }
- } else if (!strcasecmp(v->name, "callingpres")) {
- peer->callingpres = ast_parse_caller_presentation(v->value);
- if (peer->callingpres == -1) {
- peer->callingpres = atoi(v->value);
- }
- } else if (!strcasecmp(v->name, "username") || !strcasecmp(v->name, "defaultuser")) { /* "username" is deprecated */
- ast_string_field_set(peer, username, v->value);
- if (!strcasecmp(v->name, "username")) {
- if (deprecation_warning) {
- ast_log(LOG_NOTICE, "The 'username' field for sip peers has been deprecated in favor of the term 'defaultuser'\n");
- deprecation_warning = 0;
- }
- peer->deprecated_username = 1;
- }
- } else if (!strcasecmp(v->name, "tonezone")) {
- struct ast_tone_zone *new_zone;
- if (!(new_zone = ast_get_indication_zone(v->value))) {
- ast_log(LOG_ERROR, "Unknown country code '%s' for tonezone in device [%s] at line %d. Check indications.conf for available country codes.\n", v->value, peer->name, v->lineno);
- } else {
- ast_tone_zone_unref(new_zone);
- ast_string_field_set(peer, zone, v->value);
- }
- } else if (!strcasecmp(v->name, "language")) {
- ast_string_field_set(peer, language, v->value);
- } else if (!strcasecmp(v->name, "regexten")) {
- ast_string_field_set(peer, regexten, v->value);
- } else if (!strcasecmp(v->name, "callbackextension")) {
- ast_string_field_set(peer, callback, v->value);
- } else if (!strcasecmp(v->name, "amaflags")) {
- format = ast_cdr_amaflags2int(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)) {
- add_peer_mailboxes(peer, name);
- }
- } 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_parse_allow_disallow(&peer->prefs, 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_parse_allow_disallow(&peer->prefs, 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, "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 & SIP_TRANSPORT_TLS) ?
- STANDARD_TLS_PORT : STANDARD_SIP_PORT);
- }
- if (ast_sockaddr_port(&peer->defaddr) == 0) {
- ast_sockaddr_set_port(&peer->defaddr,
- (peer->socket.type & SIP_TRANSPORT_TLS) ?
- STANDARD_TLS_PORT : STANDARD_SIP_PORT);
- }
- if (!peer->socket.port) {
- peer->socket.port = htons(((peer->socket.type & SIP_TRANSPORT_TLS) ? STANDARD_TLS_PORT : STANDARD_SIP_PORT));
- }
- if (realtime) {
- int enablepoke = 1;
- if (!sip_cfg.ignore_regexpire && peer->host_dynamic) {
- time_t nowtime = time(NULL);
- if ((nowtime - regseconds) > 0) {
- destroy_association(peer);
- memset(&peer->addr, 0, sizeof(peer->addr));
- peer->lastms = -1;
- enablepoke = 0;
- ast_debug(1, "Bah, we're expired (%d/%d/%d)!\n", (int)(nowtime - regseconds), (int)regseconds, (int)nowtime);
- }
- }
- /* Startup regular pokes */
- if (!devstate_only && enablepoke) {
- /*
- * We cannot poke the peer now in this thread without
- * a lock inversion so pass it off to the scheduler
- * thread.
- */
- AST_SCHED_REPLACE_UNREF(peer->pokeexpire, sched,
- 0, /* Poke the peer ASAP */
- sip_poke_peer_now, peer,
- sip_unref_peer(_data, "removing poke peer ref"),
- sip_unref_peer(peer, "removing poke peer ref"),
- sip_ref_peer(peer, "adding poke peer ref"));
- }
- }
- if (ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) {
- sip_cfg.allowsubscribe = TRUE; /* No global ban any more */
- }
- /* If read-only RT backend, then refresh from local DB cache */
- if (peer->host_dynamic && (!peer->is_realtime || !sip_cfg.peer_rtupdate)) {
- reg_source_db(peer);
- }
- /* If they didn't request that MWI is sent *only* on subscribe, go ahead and
- * subscribe to it now. */
- if (!devstate_only && !ast_test_flag(&peer->flags[1], SIP_PAGE2_SUBSCRIBEMWIONLY) &&
- !AST_LIST_EMPTY(&peer->mailboxes)) {
- add_peer_mwi_subs(peer);
- /* Send MWI from the event cache only. This is so we can send initial
- * MWI if app_voicemail got loaded before chan_sip. If it is the other
- * way, then we will get events when app_voicemail gets loaded. */
- sip_send_mwi_to_peer(peer, 1);
- }
- peer->the_mark = 0;
- oldacl = ast_free_acl_list(oldacl);
- olddirectmediaacl = ast_free_acl_list(olddirectmediaacl);
- if (!ast_strlen_zero(peer->callback)) { /* build string from peer info */
- char *reg_string;
- if (ast_asprintf(®_string, "%s?%s:%s@%s/%s", peer->name, peer->username, !ast_strlen_zero(peer->remotesecret) ? peer->remotesecret : peer->secret, peer->tohost, peer->callback) >= 0) {
- sip_register(reg_string, 0); /* XXX TODO: count in registry_count */
- ast_free(reg_string);
- }
- }
- /* If an ACL change subscription is needed and doesn't exist, we need one. */
- if (acl_change_subscription_needed) {
- acl_change_event_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)
- {
- struct ast_format tmp_fmt;
- ast_format_cap_remove_all(cap);
- ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_ULAW, 0));
- ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_TESTLAW, 0));
- ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_ALAW, 0));
- ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_GSM, 0));
- ast_format_cap_add(cap, ast_format_set(&tmp_fmt, 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)
- {
- /* First, destroy all outstanding registry calls */
- /* This is needed, since otherwise active registry entries will not be destroyed */
- ASTOBJ_CONTAINER_TRAVERSE(®l, 1, do { /* regl is locked */
- ASTOBJ_WRLOCK(iterator); /* now regl is locked, and the object is also locked */
- 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, registry_unref(iterator, "reg ptr unref from reload config"));
- }
- if (iterator->timeout > -1) {
- AST_SCHED_DEL_UNREF(sched, iterator->timeout, registry_unref(iterator, "reg ptr unref from reload config"));
- }
- if (iterator->dnsmgr) {
- ast_dnsmgr_release(iterator->dnsmgr);
- iterator->dnsmgr = NULL;
- registry_unref(iterator, "reg ptr unref from dnsmgr");
- }
- ASTOBJ_UNLOCK(iterator);
- } while(0));
- }
- /*! \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);
- cleanup_all_regs();
- /* Then, actually destroy users and registry */
- ASTOBJ_CONTAINER_DESTROYALL(®l, sip_registry_destroy);
- 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(&default_prefs, 0 , sizeof(default_prefs));
- memset(&sip_cfg.outboundproxy, 0, sizeof(struct sip_proxy));
- sip_cfg.outboundproxy.force = FALSE; /*!< Don't force proxy usage, use route: headers */
- default_transports = SIP_TRANSPORT_UDP;
- default_primary_transport = SIP_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;
- sip_cfg.callevents = DEFAULT_CALLEVENTS;
- 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, "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 |= SIP_TRANSPORT_UDP;
- } else if (!strncasecmp(trans, "tcp", 3)) {
- default_transports |= SIP_TRANSPORT_TCP;
- } else if (!strncasecmp(trans, "tls", 3)) {
- default_transports |= SIP_TRANSPORT_TLS;
- } else if (!strncasecmp(trans, "wss", 3)) {
- default_transports |= SIP_TRANSPORT_WSS;
- } else if (!strncasecmp(trans, "ws", 2)) {
- default_transports |= SIP_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_parse_allow_disallow(&default_prefs, 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_parse_allow_disallow(&default_prefs, 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, "callevents")) {
- sip_cfg.callevents = ast_true(v->value);
- } 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, "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_event_subscribe();
- } else {
- network_change_event_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 & SIP_TRANSPORT_TCP)) {
- ast_log(LOG_WARNING, "Cannot use 'tcp' transport with tcpenable=no. Removing from available transports.\n");
- default_primary_transport &= ~SIP_TRANSPORT_TCP;
- default_transports &= ~SIP_TRANSPORT_TCP;
- }
- if (!default_tls_cfg.enabled && (default_transports & SIP_TRANSPORT_TLS)) {
- ast_log(LOG_WARNING, "Cannot use 'tls' transport with tlsenable=no. Removing from available transports.\n");
- default_primary_transport &= ~SIP_TRANSPORT_TLS;
- default_transports &= ~SIP_TRANSPORT_TLS;
- }
- if (!default_transports) {
- ast_log(LOG_WARNING, "No valid transports available, falling back to 'udp'.\n");
- default_transports = default_primary_transport = SIP_TRANSPORT_UDP;
- } else if (!default_primary_transport) {
- ast_log(LOG_WARNING, "No valid default transport. Selecting 'udp' as default.\n");
- default_primary_transport = SIP_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;
- }
- /* Done, tell the manager */
- manager_event(EVENT_FLAG_SYSTEM, "ChannelReload", "ChannelType: SIP\r\nReloadReason: %s\r\nRegistry_Count: %d\r\nPeer_Count: %d\r\n", channelreloadreason2txt(reason), registry_count, peer_count);
- 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_event_subscribe();
- }
- return 0;
- }
- static int apply_directmedia_acl(struct sip_pvt *p, struct ast_acl_list *directmediaacl, const char *op)
- {
- struct ast_sockaddr us = { { 0, }, }, them = { { 0, }, };
- int res = AST_SENSE_ALLOW;
- ast_rtp_instance_get_remote_address(p->rtp, &them);
- ast_rtp_instance_get_local_address(p->rtp, &us);
- if ((res = ast_apply_acl(directmediaacl, &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",
- op, them_addr, us_addr);
- }
- return res;
- }
- static struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan)
- {
- struct sip_pvt *p;
- struct ast_udptl *udptl = NULL;
-
- p = ast_channel_tech_pvt(chan);
- if (!p) {
- return NULL;
- }
- sip_pvt_lock(p);
- if (p->udptl && ast_test_flag(&p->flags[0], SIP_DIRECT_MEDIA)) {
- udptl = p->udptl;
- }
- sip_pvt_unlock(p);
- return udptl;
- }
- static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl)
- {
- struct sip_pvt *p;
- /* Lock the channel and the private safely. */
- ast_channel_lock(chan);
- p = ast_channel_tech_pvt(chan);
- if (!p) {
- ast_channel_unlock(chan);
- 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);
- ast_channel_unlock(chan);
- return 0;
- }
- if (udptl) {
- ast_udptl_get_peer(udptl, &p->udptlredirip);
- } else {
- memset(&p->udptlredirip, 0, sizeof(p->udptlredirip));
- }
- if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
- if (!p->pendinginvite) {
- ast_debug(3, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to IP %s\n",
- p->callid, ast_sockaddr_stringify(udptl ? &p->udptlredirip : &p->ourip));
- transmit_reinvite_with_sdp(p, TRUE, FALSE);
- } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
- ast_debug(3, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to IP %s\n",
- p->callid, ast_sockaddr_stringify(udptl ? &p->udptlredirip : &p->ourip));
- ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
- }
- }
- /* Reset lastrtprx timer */
- p->lastrtprx = p->lastrtptx = time(NULL);
- sip_pvt_unlock(p);
- ast_channel_unlock(chan);
- return 0;
- }
- static int sip_allow_anyrtp_remote(struct ast_channel *chan1, struct ast_channel *chan2, char *rtptype)
- {
- struct sip_pvt *p1 = NULL, *p2 = NULL;
- struct ast_acl_list *p2_directmediaacl = NULL; /* opposed directmediaha for comparing against first channel host address */
- struct ast_acl_list *p1_directmediaacl = NULL; /* opposed directmediaha for comparing against second channel host address */
- int res = 1;
- if (!(p1 = ast_channel_tech_pvt(chan1))) {
- return 0;
- }
- if (!(p2 = ast_channel_tech_pvt(chan2))) {
- return 0;
- }
- sip_pvt_lock(p2);
- if (p2->relatedpeer && p2->relatedpeer->directmediaacl) {
- p2_directmediaacl = ast_duplicate_acl_list(p2->relatedpeer->directmediaacl);
- }
- sip_pvt_unlock(p2);
- sip_pvt_lock(p1);
- if (p1->relatedpeer && p1->relatedpeer->directmediaacl) {
- p1_directmediaacl = ast_duplicate_acl_list(p1->relatedpeer->directmediaacl);
- }
- if (p2_directmediaacl && ast_test_flag(&p1->flags[0], SIP_DIRECT_MEDIA)) {
- if (!apply_directmedia_acl(p1, p2_directmediaacl, rtptype)) {
- res = 0;
- }
- }
- sip_pvt_unlock(p1);
- if (res == 0) {
- goto allow_anyrtp_remote_end;
- }
- sip_pvt_lock(p2);
- if (p1_directmediaacl && ast_test_flag(&p2->flags[0], SIP_DIRECT_MEDIA)) {
- if (!apply_directmedia_acl(p2, p1_directmediaacl, rtptype)) {
- res = 0;
- }
- }
- sip_pvt_unlock(p2);
- allow_anyrtp_remote_end:
- if (p2_directmediaacl) {
- p2_directmediaacl = ast_free_acl_list(p2_directmediaacl);
- }
- if (p1_directmediaacl) {
- p1_directmediaacl = ast_free_acl_list(p1_directmediaacl);
- }
- return res;
- }
- static int sip_allow_rtp_remote(struct ast_channel *chan1, struct ast_channel *chan2)
- {
- return sip_allow_anyrtp_remote(chan1, chan2, "audio");
- }
- static int sip_allow_vrtp_remote(struct ast_channel *chan1, struct ast_channel *chan2)
- {
- return sip_allow_anyrtp_remote(chan1, chan2, "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 (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;
- /* Lock the channel and the private safely. */
- ast_channel_lock(chan);
- p = ast_channel_tech_pvt(chan);
- if (!p) {
- ast_channel_unlock(chan);
- 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);
- ast_channel_unlock(chan);
- return 0;
- }
- /* Disable early RTP bridge */
- if ((instance || vinstance || tinstance) &&
- !ast_bridged_channel(chan) &&
- !sip_cfg.directrtpsetup) {
- sip_pvt_unlock(p);
- ast_channel_unlock(chan);
- return 0;
- }
- if (p->alreadygone) {
- /* If we're destroyed, don't bother */
- sip_pvt_unlock(p);
- ast_channel_unlock(chan);
- 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);
- ast_channel_unlock(chan);
- 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_is_empty(cap) && !ast_format_cap_identical(p->redircaps, cap)) {
- ast_format_cap_copy(p->redircaps, cap);
- 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);
- ast_channel_unlock(chan);
- 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);
- ast_channel_unlock(chan);
- 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(result, ast_format_cap_is_empty(p->peercaps) ? p->caps : p->peercaps);
- }
- 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 (strncasecmp(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;
- if (!regobjs) {
- return;
- }
- regspacing = default_expiry * 1000/regobjs;
- if (regspacing > 100) {
- regspacing = 100;
- }
- ms = regspacing;
- ASTOBJ_CONTAINER_TRAVERSE(®l, 1, do {
- ASTOBJ_WRLOCK(iterator);
- ms += regspacing;
- AST_SCHED_REPLACE_UNREF(iterator->expire, sched, ms, sip_reregister, iterator,
- registry_unref(_data, "REPLACE sched del decs the refcount"),
- registry_unref(iterator, "REPLACE sched add failure decs the refcount"),
- registry_addref(iterator, "REPLACE sched add incs the refcount"));
- ASTOBJ_UNLOCK(iterator);
- } while (0)
- );
- }
- /*! \brief Send all MWI subscriptions */
- static void sip_send_all_mwi_subscriptions(void)
- {
- ASTOBJ_CONTAINER_TRAVERSE(&submwil, 1, do {
- struct sip_subscription_mwi *saved;
- ASTOBJ_WRLOCK(iterator);
- AST_SCHED_DEL(sched, iterator->resub);
- saved = ASTOBJ_REF(iterator);
- if ((iterator->resub = ast_sched_add(sched, 1, sip_subscribe_mwi_do, saved)) < 0) {
- ASTOBJ_UNREF(saved, sip_subscribe_mwi_destroy);
- }
- ASTOBJ_UNLOCK(iterator);
- } while (0));
- }
- /* SRTP */
- static int setup_srtp(struct sip_srtp **srtp)
- {
- if (!ast_rtp_engine_srtp_is_registered()) {
- ast_log(LOG_ERROR, "No SRTP module loaded, can't setup SRTP session.\n");
- return -1;
- }
- if (!(*srtp = sip_srtp_alloc())) { /* Allocate SRTP data structure */
- return -1;
- }
- return 0;
- }
- static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct sip_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;
- }
- 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 (setup_srtp(srtp) < 0) {
- return FALSE;
- }
- }
- if (!(*srtp)->crypto && !((*srtp)->crypto = sdp_crypto_setup())) {
- return FALSE;
- }
- if (sdp_crypto_process((*srtp)->crypto, a, rtp, *srtp) < 0) {
- return FALSE;
- }
- ast_set_flag(*srtp, SRTP_CRYPTO_OFFER_OK);
- 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
- *
- * \warn 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
- *
- * \warn 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(SIP_TRANSPORT_UDP));
- }
- /*! \brief Return the first entry from ast_sockaddr_resolve filtered by family of binddaddr
- *
- * \warn 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) & (SIP_TRANSPORT_TLS | SIP_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;
- }
- /*! \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)
- {
- 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;
- ASTOBJ_CONTAINER_TRAVERSE(&submwil, 1, do {
- ASTOBJ_WRLOCK(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;
- }
- ASTOBJ_UNLOCK(iterator);
- } while(0));
- 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;
- ASTOBJ_CONTAINER_TRAVERSE(&submwil, 1, do {
- ASTOBJ_WRLOCK(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;
- }
- ASTOBJ_UNLOCK(iterator);
- } while(0));
- 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;
- ASTOBJ_CONTAINER_TRAVERSE(&submwil, 1, do {
- ASTOBJ_WRLOCK(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;
- }
- ASTOBJ_UNLOCK(iterator);
- } while(0));
- 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;
- ASTOBJ_CONTAINER_TRAVERSE(&submwil, 1, do {
- ASTOBJ_WRLOCK(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;
- }
- ASTOBJ_UNLOCK(iterator);
- } while(0));
- 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;
- ASTOBJ_CONTAINER_TRAVERSE(&submwil, 1, do {
- ASTOBJ_WRLOCK(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;
- }
- ASTOBJ_UNLOCK(iterator);
- } while(0));
- 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, "mailbox", mailbox->mailbox);
- ast_data_add_str(data_peer_mailbox, "context", mailbox->context);
- }
- /* 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_cdr_flags2str(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,
- };
- /*! \brief PBX load module - initialization */
- static int load_module(void)
- {
- ast_verbose("SIP channel loading...\n");
- if (!(sip_tech.capabilities = ast_format_cap_alloc())) {
- return AST_MODULE_LOAD_FAILURE;
- }
- if (ast_sip_api_provider_register(&chan_sip_api_provider)) {
- 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");
- return AST_MODULE_LOAD_FAILURE;
- }
- if (!(sip_cfg.caps = ast_format_cap_alloc())) {
- return AST_MODULE_LOAD_FAILURE;
- }
- ast_format_cap_add_all_by_type(sip_tech.capabilities, AST_FORMAT_TYPE_AUDIO);
- ASTOBJ_CONTAINER_INIT(®l); /* Registry object list -- not searched for anything */
- ASTOBJ_CONTAINER_INIT(&submwil); /* MWI subscription object list */
- if (!(sched = ast_sched_context_create())) {
- ast_log(LOG_ERROR, "Unable to create scheduler context\n");
- return AST_MODULE_LOAD_FAILURE;
- }
- if (!(io = io_context_create())) {
- ast_log(LOG_ERROR, "Unable to create I/O context\n");
- 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 */
- ast_sip_api_provider_unregister();
- 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");
- io_context_destroy(io);
- ast_sched_context_destroy(sched);
- 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)) {
- /* LOAD_FAILURE stops Asterisk, so cleanup is a moot point. */
- 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");
- ao2_t_ref(bogus_peer, -1, "unref the bogus_peer");
- io_context_destroy(io);
- ast_sched_context_destroy(sched);
- 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 UDPTL subdriver that we're here */
- ast_udptl_proto_register(&sip_udptl);
- /* 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(&sipchaninfo_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)) {
- ast_sip_api_provider_unregister();
- 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");
- ast_sip_api_provider_unregister();
- 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)) {
- ast_sip_api_provider_unregister();
- return AST_MODULE_LOAD_DECLINE;
- }
- }
- if (ast_cc_monitor_register(&sip_cc_monitor_callbacks)) {
- ast_sip_api_provider_unregister();
- return AST_MODULE_LOAD_DECLINE;
- }
- if (!(sip_monitor_instances = ao2_container_alloc(37, sip_monitor_instance_hash_fn, sip_monitor_instance_cmp_fn))) {
- ast_sip_api_provider_unregister();
- 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_event_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_event_unsubscribe();
- acl_change_event_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(&sipchaninfo_function);
- 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 UDPTL */
- ast_udptl_proto_unregister(&sip_udptl);
- /* 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_ref(authl, -1, "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();
- ASTOBJ_CONTAINER_DESTROYALL(®l, sip_registry_destroy);
- ASTOBJ_CONTAINER_DESTROY(®l);
- ASTOBJ_CONTAINER_TRAVERSE(&submwil, 1, do {
- ASTOBJ_WRLOCK(iterator);
- if (iterator->dnsmgr) {
- ast_dnsmgr_release(iterator->dnsmgr);
- iterator->dnsmgr = NULL;
- ASTOBJ_UNREF(iterator, sip_subscribe_mwi_destroy);
- }
- ASTOBJ_UNLOCK(iterator);
- } while(0));
- ASTOBJ_CONTAINER_DESTROYALL(&submwil, sip_subscribe_mwi_destroy);
- ASTOBJ_CONTAINER_DESTROY(&submwil);
- /*
- * 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_ref(bogus_peer, -1, "unref the bogus_peer");
- ao2_t_ref(peers, -1, "unref the peers table");
- ao2_t_ref(peers_by_ip, -1, "unref the peers_by_ip table");
- ao2_t_ref(dialogs, -1, "unref the dialogs table");
- ao2_t_ref(dialogs_needdestroy, -1, "unref dialogs_needdestroy");
- ao2_t_ref(dialogs_rtpcheck, -1, "unref dialogs_rtpcheck");
- ao2_t_ref(threadt, -1, "unref the thread table");
- ao2_t_ref(sip_monitor_instances, -1, "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;
- }
- ast_format_cap_destroy(sip_tech.capabilities);
- sip_cfg.caps = ast_format_cap_destroy(sip_cfg.caps);
- return 0;
- }
- AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Session Initiation Protocol (SIP)",
- .load = load_module,
- .unload = unload_module,
- .reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DRIVER,
- .nonoptreq = "res_crypto,chan_local,res_http_websocket",
- );
|