1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137 |
- // Copyright (c) 2004-2013 Sergey Lyubka
- // Copyright (c) 2013-2022 Cesanta Software Limited
- // All rights reserved
- //
- // This software is dual-licensed: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License version 2 as
- // published by the Free Software Foundation. For the terms of this
- // license, see http://www.gnu.org/licenses/
- //
- // You are free to use this software under the terms of the GNU General
- // Public License, but WITHOUT ANY WARRANTY; without even the implied
- // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- // See the GNU General Public License for more details.
- //
- // Alternatively, you can license this software under a commercial
- // license, as set out in https://www.mongoose.ws/licensing/
- //
- // SPDX-License-Identifier: GPL-2.0-only or commercial
- #include "mongoose.h"
- #ifdef MG_ENABLE_LINES
- #line 1 "src/base64.c"
- #endif
- static int mg_b64idx(int c) {
- if (c < 26) {
- return c + 'A';
- } else if (c < 52) {
- return c - 26 + 'a';
- } else if (c < 62) {
- return c - 52 + '0';
- } else {
- return c == 62 ? '+' : '/';
- }
- }
- static int mg_b64rev(int c) {
- if (c >= 'A' && c <= 'Z') {
- return c - 'A';
- } else if (c >= 'a' && c <= 'z') {
- return c + 26 - 'a';
- } else if (c >= '0' && c <= '9') {
- return c + 52 - '0';
- } else if (c == '+') {
- return 62;
- } else if (c == '/') {
- return 63;
- } else if (c == '=') {
- return 64;
- } else {
- return -1;
- }
- }
- int mg_base64_update(unsigned char ch, char *to, int n) {
- int rem = (n & 3) % 3;
- if (rem == 0) {
- to[n] = (char) mg_b64idx(ch >> 2);
- to[++n] = (char) ((ch & 3) << 4);
- } else if (rem == 1) {
- to[n] = (char) mg_b64idx(to[n] | (ch >> 4));
- to[++n] = (char) ((ch & 15) << 2);
- } else {
- to[n] = (char) mg_b64idx(to[n] | (ch >> 6));
- to[++n] = (char) mg_b64idx(ch & 63);
- n++;
- }
- return n;
- }
- int mg_base64_final(char *to, int n) {
- int saved = n;
- // printf("---[%.*s]\n", n, to);
- if (n & 3) n = mg_base64_update(0, to, n);
- if ((saved & 3) == 2) n--;
- // printf(" %d[%.*s]\n", n, n, to);
- while (n & 3) to[n++] = '=';
- to[n] = '\0';
- return n;
- }
- int mg_base64_encode(const unsigned char *p, int n, char *to) {
- int i, len = 0;
- for (i = 0; i < n; i++) len = mg_base64_update(p[i], to, len);
- len = mg_base64_final(to, len);
- return len;
- }
- int mg_base64_decode(const char *src, int n, char *dst) {
- const char *end = src == NULL ? NULL : src + n; // Cannot add to NULL
- int len = 0;
- while (src != NULL && src + 3 < end) {
- int a = mg_b64rev(src[0]), b = mg_b64rev(src[1]), c = mg_b64rev(src[2]),
- d = mg_b64rev(src[3]);
- if (a == 64 || a < 0 || b == 64 || b < 0 || c < 0 || d < 0) return 0;
- dst[len++] = (char) ((a << 2) | (b >> 4));
- if (src[2] != '=') {
- dst[len++] = (char) ((b << 4) | (c >> 2));
- if (src[3] != '=') dst[len++] = (char) ((c << 6) | d);
- }
- src += 4;
- }
- dst[len] = '\0';
- return len;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/dns.c"
- #endif
- struct dns_data {
- struct dns_data *next;
- struct mg_connection *c;
- uint64_t expire;
- uint16_t txnid;
- };
- static void mg_sendnsreq(struct mg_connection *, struct mg_str *, int,
- struct mg_dns *, bool);
- static void mg_dns_free(struct mg_connection *c, struct dns_data *d) {
- LIST_DELETE(struct dns_data,
- (struct dns_data **) &c->mgr->active_dns_requests, d);
- free(d);
- }
- void mg_resolve_cancel(struct mg_connection *c) {
- struct dns_data *tmp, *d = (struct dns_data *) c->mgr->active_dns_requests;
- for (; d != NULL; d = tmp) {
- tmp = d->next;
- if (d->c == c) mg_dns_free(c, d);
- }
- }
- static size_t mg_dns_parse_name_depth(const uint8_t *s, size_t len, size_t ofs,
- char *to, size_t tolen, size_t j,
- int depth) {
- size_t i = 0;
- if (tolen > 0 && depth == 0) to[0] = '\0';
- if (depth > 5) return 0;
- // MG_INFO(("ofs %lx %x %x", (unsigned long) ofs, s[ofs], s[ofs + 1]));
- while (ofs + i + 1 < len) {
- size_t n = s[ofs + i];
- if (n == 0) {
- i++;
- break;
- }
- if (n & 0xc0) {
- size_t ptr = (((n & 0x3f) << 8) | s[ofs + i + 1]); // 12 is hdr len
- // MG_INFO(("PTR %lx", (unsigned long) ptr));
- if (ptr + 1 < len && (s[ptr] & 0xc0) == 0 &&
- mg_dns_parse_name_depth(s, len, ptr, to, tolen, j, depth + 1) == 0)
- return 0;
- i += 2;
- break;
- }
- if (ofs + i + n + 1 >= len) return 0;
- if (j > 0) {
- if (j < tolen) to[j] = '.';
- j++;
- }
- if (j + n < tolen) memcpy(&to[j], &s[ofs + i + 1], n);
- j += n;
- i += n + 1;
- if (j < tolen) to[j] = '\0'; // Zero-terminate this chunk
- // MG_INFO(("--> [%s]", to));
- }
- if (tolen > 0) to[tolen - 1] = '\0'; // Make sure make sure it is nul-term
- return i;
- }
- static size_t mg_dns_parse_name(const uint8_t *s, size_t n, size_t ofs,
- char *dst, size_t dstlen) {
- return mg_dns_parse_name_depth(s, n, ofs, dst, dstlen, 0, 0);
- }
- size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
- bool is_question, struct mg_dns_rr *rr) {
- const uint8_t *s = buf + ofs, *e = &buf[len];
- memset(rr, 0, sizeof(*rr));
- if (len < sizeof(struct mg_dns_header)) return 0; // Too small
- if (len > 512) return 0; // Too large, we don't expect that
- if (s >= e) return 0; // Overflow
- if ((rr->nlen = (uint16_t) mg_dns_parse_name(buf, len, ofs, NULL, 0)) == 0)
- return 0;
- s += rr->nlen + 4;
- if (s > e) return 0;
- rr->atype = (uint16_t) (((uint16_t) s[-4] << 8) | s[-3]);
- rr->aclass = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]);
- if (is_question) return (size_t) (rr->nlen + 4);
- s += 6;
- if (s > e) return 0;
- rr->alen = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]);
- if (s + rr->alen > e) return 0;
- return (size_t) (rr->nlen + rr->alen + 10);
- }
- bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
- const struct mg_dns_header *h = (struct mg_dns_header *) buf;
- struct mg_dns_rr rr;
- size_t i, n, ofs = sizeof(*h);
- memset(dm, 0, sizeof(*dm));
- if (len < sizeof(*h)) return 0; // Too small, headers dont fit
- if (mg_ntohs(h->num_questions) > 1) return 0; // Sanity
- if (mg_ntohs(h->num_answers) > 10) return 0; // Sanity
- dm->txnid = mg_ntohs(h->txnid);
- for (i = 0; i < mg_ntohs(h->num_questions); i++) {
- if ((n = mg_dns_parse_rr(buf, len, ofs, true, &rr)) == 0) return false;
- // MG_INFO(("Q %lu %lu %hu/%hu", ofs, n, rr.atype, rr.aclass));
- ofs += n;
- }
- for (i = 0; i < mg_ntohs(h->num_answers); i++) {
- if ((n = mg_dns_parse_rr(buf, len, ofs, false, &rr)) == 0) return false;
- // MG_INFO(("A -- %lu %lu %hu/%hu %s", ofs, n, rr.atype, rr.aclass,
- // dm->name));
- mg_dns_parse_name(buf, len, ofs, dm->name, sizeof(dm->name));
- ofs += n;
- if (rr.alen == 4 && rr.atype == 1 && rr.aclass == 1) {
- dm->addr.is_ip6 = false;
- memcpy(&dm->addr.ip, &buf[ofs - 4], 4);
- dm->resolved = true;
- break; // Return success
- } else if (rr.alen == 16 && rr.atype == 28 && rr.aclass == 1) {
- dm->addr.is_ip6 = true;
- memcpy(&dm->addr.ip6, &buf[ofs - 16], 16);
- dm->resolved = true;
- break; // Return success
- }
- }
- return true;
- }
- static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
- void *fn_data) {
- struct dns_data *d, *tmp;
- if (ev == MG_EV_POLL) {
- uint64_t now = *(uint64_t *) ev_data;
- for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
- d = tmp) {
- tmp = d->next;
- // MG_DEBUG ("%lu %lu dns poll", d->expire, now));
- if (now > d->expire) mg_error(d->c, "DNS timeout");
- }
- } else if (ev == MG_EV_READ) {
- struct mg_dns_message dm;
- int resolved = 0;
- if (mg_dns_parse(c->recv.buf, c->recv.len, &dm) == false) {
- MG_ERROR(("Unexpected DNS response:"));
- mg_hexdump(c->recv.buf, c->recv.len);
- } else {
- // MG_VERBOSE(("%s %d", dm.name, dm.resolved));
- for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
- d = tmp) {
- tmp = d->next;
- // MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid));
- if (dm.txnid != d->txnid) continue;
- if (d->c->is_resolving) {
- if (dm.resolved) {
- dm.addr.port = d->c->rem.port; // Save port
- d->c->rem = dm.addr; // Copy resolved address
- MG_DEBUG(
- ("%lu %s is %M", d->c->id, dm.name, mg_print_ip, &d->c->rem));
- mg_connect_resolved(d->c);
- #if MG_ENABLE_IPV6
- } else if (dm.addr.is_ip6 == false && dm.name[0] != '\0' &&
- c->mgr->use_dns6 == false) {
- struct mg_str x = mg_str(dm.name);
- mg_sendnsreq(d->c, &x, c->mgr->dnstimeout, &c->mgr->dns6, true);
- #endif
- } else {
- mg_error(d->c, "%s DNS lookup failed", dm.name);
- }
- } else {
- MG_ERROR(("%lu already resolved", d->c->id));
- }
- mg_dns_free(c, d);
- resolved = 1;
- }
- }
- if (!resolved) MG_ERROR(("stray DNS reply"));
- c->recv.len = 0;
- } else if (ev == MG_EV_CLOSE) {
- for (d = (struct dns_data *) c->mgr->active_dns_requests; d != NULL;
- d = tmp) {
- tmp = d->next;
- mg_error(d->c, "DNS error");
- mg_dns_free(c, d);
- }
- }
- (void) fn_data;
- }
- static bool mg_dns_send(struct mg_connection *c, const struct mg_str *name,
- uint16_t txnid, bool ipv6) {
- struct {
- struct mg_dns_header header;
- uint8_t data[256];
- } pkt;
- size_t i, n;
- memset(&pkt, 0, sizeof(pkt));
- pkt.header.txnid = mg_htons(txnid);
- pkt.header.flags = mg_htons(0x100);
- pkt.header.num_questions = mg_htons(1);
- for (i = n = 0; i < sizeof(pkt.data) - 5; i++) {
- if (name->ptr[i] == '.' || i >= name->len) {
- pkt.data[n] = (uint8_t) (i - n);
- memcpy(&pkt.data[n + 1], name->ptr + n, i - n);
- n = i + 1;
- }
- if (i >= name->len) break;
- }
- memcpy(&pkt.data[n], "\x00\x00\x01\x00\x01", 5); // A query
- n += 5;
- if (ipv6) pkt.data[n - 3] = 0x1c; // AAAA query
- // memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query
- // n += 6;
- return mg_send(c, &pkt, sizeof(pkt.header) + n);
- }
- static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
- struct mg_dns *dnsc, bool ipv6) {
- struct dns_data *d = NULL;
- if (dnsc->url == NULL) {
- mg_error(c, "DNS server URL is NULL. Call mg_mgr_init()");
- } else if (dnsc->c == NULL) {
- dnsc->c = mg_connect(c->mgr, dnsc->url, NULL, NULL);
- if (dnsc->c != NULL) {
- dnsc->c->pfn = dns_cb;
- // dnsc->c->is_hexdumping = 1;
- }
- }
- if (dnsc->c == NULL) {
- mg_error(c, "resolver");
- } else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) {
- mg_error(c, "resolve OOM");
- } else {
- struct dns_data *reqs = (struct dns_data *) c->mgr->active_dns_requests;
- d->txnid = reqs ? (uint16_t) (reqs->txnid + 1) : 1;
- d->next = (struct dns_data *) c->mgr->active_dns_requests;
- c->mgr->active_dns_requests = d;
- d->expire = mg_millis() + (uint64_t) ms;
- d->c = c;
- c->is_resolving = 1;
- MG_VERBOSE(("%lu resolving %.*s @ %s, txnid %hu", c->id, (int) name->len,
- name->ptr, dnsc->url, d->txnid));
- if (!mg_dns_send(dnsc->c, name, d->txnid, ipv6)) {
- mg_error(dnsc->c, "DNS send");
- }
- }
- }
- void mg_resolve(struct mg_connection *c, const char *url) {
- struct mg_str host = mg_url_host(url);
- c->rem.port = mg_htons(mg_url_port(url));
- if (mg_aton(host, &c->rem)) {
- // host is an IP address, do not fire name resolution
- mg_connect_resolved(c);
- } else {
- // host is not an IP, send DNS resolution request
- struct mg_dns *dns = c->mgr->use_dns6 ? &c->mgr->dns6 : &c->mgr->dns4;
- mg_sendnsreq(c, &host, c->mgr->dnstimeout, dns, c->mgr->use_dns6);
- }
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/event.c"
- #endif
- void mg_call(struct mg_connection *c, int ev, void *ev_data) {
- // Run user-defined handler first, in order to give it an ability
- // to intercept processing (e.g. clean input buffer) before the
- // protocol handler kicks in
- if (c->fn != NULL) c->fn(c, ev, ev_data, c->fn_data);
- if (c->pfn != NULL) c->pfn(c, ev, ev_data, c->pfn_data);
- }
- void mg_error(struct mg_connection *c, const char *fmt, ...) {
- char buf[64];
- va_list ap;
- va_start(ap, fmt);
- mg_vsnprintf(buf, sizeof(buf), fmt, &ap);
- va_end(ap);
- MG_ERROR(("%lu %p %s", c->id, c->fd, buf));
- c->is_closing = 1; // Set is_closing before sending MG_EV_CALL
- mg_call(c, MG_EV_ERROR, buf); // Let user handler to override it
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/fmt.c"
- #endif
- static bool is_digit(int c) {
- return c >= '0' && c <= '9';
- }
- static int addexp(char *buf, int e, int sign) {
- int n = 0;
- buf[n++] = 'e';
- buf[n++] = (char) sign;
- if (e > 400) return 0;
- if (e < 10) buf[n++] = '0';
- if (e >= 100) buf[n++] = (char) (e / 100 + '0'), e -= 100 * (e / 100);
- if (e >= 10) buf[n++] = (char) (e / 10 + '0'), e -= 10 * (e / 10);
- buf[n++] = (char) (e + '0');
- return n;
- }
- static int xisinf(double x) {
- union {
- double f;
- uint64_t u;
- } ieee754 = {x};
- return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 &&
- ((unsigned) ieee754.u == 0);
- }
- static int xisnan(double x) {
- union {
- double f;
- uint64_t u;
- } ieee754 = {x};
- return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) +
- ((unsigned) ieee754.u != 0) >
- 0x7ff00000;
- }
- static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width, bool tz) {
- char buf[40];
- int i, s = 0, n = 0, e = 0;
- double t, mul, saved;
- if (d == 0.0) return mg_snprintf(dst, dstlen, "%s", "0");
- if (xisinf(d)) return mg_snprintf(dst, dstlen, "%s", d > 0 ? "inf" : "-inf");
- if (xisnan(d)) return mg_snprintf(dst, dstlen, "%s", "nan");
- if (d < 0.0) d = -d, buf[s++] = '-';
- // Round
- saved = d;
- mul = 1.0;
- while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0;
- while (d <= 1.0 && d / mul <= 1.0) mul /= 10.0;
- for (i = 0, t = mul * 5; i < width; i++) t /= 10.0;
- d += t;
- // Calculate exponent, and 'mul' for scientific representation
- mul = 1.0;
- while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0, e++;
- while (d < 1.0 && d / mul < 1.0) mul /= 10.0, e--;
- // printf(" --> %g %d %g %g\n", saved, e, t, mul);
- if (e >= width && width > 1) {
- n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz);
- // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, n, buf);
- n += addexp(buf + s + n, e, '+');
- return mg_snprintf(dst, dstlen, "%.*s", n, buf);
- } else if (e <= -width && width > 1) {
- n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz);
- // printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, n, buf);
- n += addexp(buf + s + n, -e, '-');
- return mg_snprintf(dst, dstlen, "%.*s", n, buf);
- } else {
- for (i = 0, t = mul; t >= 1.0 && s + n < (int) sizeof(buf); i++) {
- int ch = (int) (d / t);
- if (n > 0 || ch > 0) buf[s + n++] = (char) (ch + '0');
- d -= ch * t;
- t /= 10.0;
- }
- // printf(" --> [%g] -> %g %g (%d) [%.*s]\n", saved, d, t, n, s + n, buf);
- if (n == 0) buf[s++] = '0';
- while (t >= 1.0 && n + s < (int) sizeof(buf)) buf[n++] = '0', t /= 10.0;
- if (s + n < (int) sizeof(buf)) buf[n + s++] = '.';
- // printf(" 1--> [%g] -> [%.*s]\n", saved, s + n, buf);
- for (i = 0, t = 0.1; s + n < (int) sizeof(buf) && n < width; i++) {
- int ch = (int) (d / t);
- buf[s + n++] = (char) (ch + '0');
- d -= ch * t;
- t /= 10.0;
- }
- }
- while (tz && n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeroes
- if (n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot
- n += s;
- if (n >= (int) sizeof(buf)) n = (int) sizeof(buf) - 1;
- buf[n] = '\0';
- return mg_snprintf(dst, dstlen, "%s", buf);
- }
- static size_t mg_lld(char *buf, int64_t val, bool is_signed, bool is_hex) {
- const char *letters = "0123456789abcdef";
- uint64_t v = (uint64_t) val;
- size_t s = 0, n, i;
- if (is_signed && val < 0) buf[s++] = '-', v = (uint64_t) (-val);
- // This loop prints a number in reverse order. I guess this is because we
- // write numbers from right to left: least significant digit comes last.
- // Maybe because we use Arabic numbers, and Arabs write RTL?
- if (is_hex) {
- for (n = 0; v; v >>= 4) buf[s + n++] = letters[v & 15];
- } else {
- for (n = 0; v; v /= 10) buf[s + n++] = letters[v % 10];
- }
- // Reverse a string
- for (i = 0; i < n / 2; i++) {
- char t = buf[s + i];
- buf[s + i] = buf[s + n - i - 1], buf[s + n - i - 1] = t;
- }
- if (val == 0) buf[n++] = '0'; // Handle special case
- return n + s;
- }
- static size_t scpy(void (*out)(char, void *), void *ptr, char *buf,
- size_t len) {
- size_t i = 0;
- while (i < len && buf[i] != '\0') out(buf[i++], ptr);
- return i;
- }
- size_t mg_xprintf(void (*out)(char, void *), void *ptr, const char *fmt, ...) {
- size_t len = 0;
- va_list ap;
- va_start(ap, fmt);
- len = mg_vxprintf(out, ptr, fmt, &ap);
- va_end(ap);
- return len;
- }
- size_t mg_vxprintf(void (*out)(char, void *), void *param, const char *fmt,
- va_list *ap) {
- size_t i = 0, n = 0;
- while (fmt[i] != '\0') {
- if (fmt[i] == '%') {
- size_t j, k, x = 0, is_long = 0, w = 0 /* width */, pr = ~0U /* prec */;
- char pad = ' ', minus = 0, c = fmt[++i];
- if (c == '#') x++, c = fmt[++i];
- if (c == '-') minus++, c = fmt[++i];
- if (c == '0') pad = '0', c = fmt[++i];
- while (is_digit(c)) w *= 10, w += (size_t) (c - '0'), c = fmt[++i];
- if (c == '.') {
- c = fmt[++i];
- if (c == '*') {
- pr = (size_t) va_arg(*ap, int);
- c = fmt[++i];
- } else {
- pr = 0;
- while (is_digit(c)) pr *= 10, pr += (size_t) (c - '0'), c = fmt[++i];
- }
- }
- while (c == 'h') c = fmt[++i]; // Treat h and hh as int
- if (c == 'l') {
- is_long++, c = fmt[++i];
- if (c == 'l') is_long++, c = fmt[++i];
- }
- if (c == 'p') x = 1, is_long = 1;
- if (c == 'd' || c == 'u' || c == 'x' || c == 'X' || c == 'p' ||
- c == 'g' || c == 'f') {
- bool s = (c == 'd'), h = (c == 'x' || c == 'X' || c == 'p');
- char tmp[40];
- size_t xl = x ? 2 : 0;
- if (c == 'g' || c == 'f') {
- double v = va_arg(*ap, double);
- if (pr == ~0U) pr = 6;
- k = mg_dtoa(tmp, sizeof(tmp), v, (int) pr, c == 'g');
- } else if (is_long == 2) {
- int64_t v = va_arg(*ap, int64_t);
- k = mg_lld(tmp, v, s, h);
- } else if (is_long == 1) {
- long v = va_arg(*ap, long);
- k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned long) v, s, h);
- } else {
- int v = va_arg(*ap, int);
- k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned) v, s, h);
- }
- for (j = 0; j < xl && w > 0; j++) w--;
- for (j = 0; pad == ' ' && !minus && k < w && j + k < w; j++)
- n += scpy(out, param, &pad, 1);
- n += scpy(out, param, (char *) "0x", xl);
- for (j = 0; pad == '0' && k < w && j + k < w; j++)
- n += scpy(out, param, &pad, 1);
- n += scpy(out, param, tmp, k);
- for (j = 0; pad == ' ' && minus && k < w && j + k < w; j++)
- n += scpy(out, param, &pad, 1);
- } else if (c == 'm' || c == 'M') {
- mg_pm_t f = va_arg(*ap, mg_pm_t);
- if (c == 'm') out('"', param);
- n += f(out, param, ap);
- if (c == 'm') n += 2, out('"', param);
- } else if (c == 'c') {
- int ch = va_arg(*ap, int);
- out((char) ch, param);
- n++;
- } else if (c == 's') {
- char *p = va_arg(*ap, char *);
- if (pr == ~0U) pr = p == NULL ? 0 : strlen(p);
- for (j = 0; !minus && pr < w && j + pr < w; j++)
- n += scpy(out, param, &pad, 1);
- n += scpy(out, param, p, pr);
- for (j = 0; minus && pr < w && j + pr < w; j++)
- n += scpy(out, param, &pad, 1);
- } else if (c == '%') {
- out('%', param);
- n++;
- } else {
- out('%', param);
- out(c, param);
- n += 2;
- }
- i++;
- } else {
- out(fmt[i], param), n++, i++;
- }
- }
- return n;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/fs.c"
- #endif
- struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags) {
- struct mg_fd *fd = (struct mg_fd *) calloc(1, sizeof(*fd));
- if (fd != NULL) {
- fd->fd = fs->op(path, flags);
- fd->fs = fs;
- if (fd->fd == NULL) {
- free(fd);
- fd = NULL;
- }
- }
- return fd;
- }
- void mg_fs_close(struct mg_fd *fd) {
- if (fd != NULL) {
- fd->fs->cl(fd->fd);
- free(fd);
- }
- }
- char *mg_file_read(struct mg_fs *fs, const char *path, size_t *sizep) {
- struct mg_fd *fd;
- char *data = NULL;
- size_t size = 0;
- fs->st(path, &size, NULL);
- if ((fd = mg_fs_open(fs, path, MG_FS_READ)) != NULL) {
- data = (char *) calloc(1, size + 1);
- if (data != NULL) {
- if (fs->rd(fd->fd, data, size) != size) {
- free(data);
- data = NULL;
- } else {
- data[size] = '\0';
- if (sizep != NULL) *sizep = size;
- }
- }
- mg_fs_close(fd);
- }
- return data;
- }
- bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf,
- size_t len) {
- bool result = false;
- struct mg_fd *fd;
- char tmp[MG_PATH_MAX];
- mg_snprintf(tmp, sizeof(tmp), "%s..%d", path, rand());
- if ((fd = mg_fs_open(fs, tmp, MG_FS_WRITE)) != NULL) {
- result = fs->wr(fd->fd, buf, len) == len;
- mg_fs_close(fd);
- if (result) {
- fs->rm(path);
- fs->mv(tmp, path);
- } else {
- fs->rm(tmp);
- }
- }
- return result;
- }
- bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...) {
- va_list ap;
- char *data;
- bool result = false;
- va_start(ap, fmt);
- data = mg_vmprintf(fmt, &ap);
- va_end(ap);
- result = mg_file_write(fs, path, data, strlen(data));
- free(data);
- return result;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/fs_fat.c"
- #endif
- #if MG_ENABLE_FATFS
- #include <ff.h>
- static int mg_days_from_epoch(int y, int m, int d) {
- y -= m <= 2;
- int era = y / 400;
- int yoe = y - era * 400;
- int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
- int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
- return era * 146097 + doe - 719468;
- }
- static time_t mg_timegm(const struct tm *t) {
- int year = t->tm_year + 1900;
- int month = t->tm_mon; // 0-11
- if (month > 11) {
- year += month / 12;
- month %= 12;
- } else if (month < 0) {
- int years_diff = (11 - month) / 12;
- year -= years_diff;
- month += 12 * years_diff;
- }
- int x = mg_days_from_epoch(year, month + 1, t->tm_mday);
- return 60 * (60 * (24L * x + t->tm_hour) + t->tm_min) + t->tm_sec;
- }
- static time_t ff_time_to_epoch(uint16_t fdate, uint16_t ftime) {
- struct tm tm;
- memset(&tm, 0, sizeof(struct tm));
- tm.tm_sec = (ftime << 1) & 0x3e;
- tm.tm_min = ((ftime >> 5) & 0x3f);
- tm.tm_hour = ((ftime >> 11) & 0x1f);
- tm.tm_mday = (fdate & 0x1f);
- tm.tm_mon = ((fdate >> 5) & 0x0f) - 1;
- tm.tm_year = ((fdate >> 9) & 0x7f) + 80;
- return mg_timegm(&tm);
- }
- static int ff_stat(const char *path, size_t *size, time_t *mtime) {
- FILINFO fi;
- if (path[0] == '\0') {
- if (size) *size = 0;
- if (mtime) *mtime = 0;
- return MG_FS_DIR;
- } else if (f_stat(path, &fi) == 0) {
- if (size) *size = (size_t) fi.fsize;
- if (mtime) *mtime = ff_time_to_epoch(fi.fdate, fi.ftime);
- return MG_FS_READ | MG_FS_WRITE | ((fi.fattrib & AM_DIR) ? MG_FS_DIR : 0);
- } else {
- return 0;
- }
- }
- static void ff_list(const char *dir, void (*fn)(const char *, void *),
- void *userdata) {
- DIR d;
- FILINFO fi;
- if (f_opendir(&d, dir) == FR_OK) {
- while (f_readdir(&d, &fi) == FR_OK && fi.fname[0] != '\0') {
- if (!strcmp(fi.fname, ".") || !strcmp(fi.fname, "..")) continue;
- fn(fi.fname, userdata);
- }
- f_closedir(&d);
- }
- }
- static void *ff_open(const char *path, int flags) {
- FIL f;
- unsigned char mode = FA_READ;
- if (flags & MG_FS_WRITE) mode |= FA_WRITE | FA_OPEN_ALWAYS | FA_OPEN_APPEND;
- if (f_open(&f, path, mode) == 0) {
- FIL *fp = calloc(1, sizeof(*fp));
- memcpy(fp, &f, sizeof(*fp));
- return fp;
- } else {
- return NULL;
- }
- }
- static void ff_close(void *fp) {
- if (fp != NULL) {
- f_close((FIL *) fp);
- free(fp);
- }
- }
- static size_t ff_read(void *fp, void *buf, size_t len) {
- UINT n = 0, misalign = ((size_t) buf) & 3;
- if (misalign) {
- char aligned[4];
- f_read((FIL *) fp, aligned, len > misalign ? misalign : len, &n);
- memcpy(buf, aligned, n);
- } else {
- f_read((FIL *) fp, buf, len, &n);
- }
- return n;
- }
- static size_t ff_write(void *fp, const void *buf, size_t len) {
- UINT n = 0;
- return f_write((FIL *) fp, (char *) buf, len, &n) == FR_OK ? n : 0;
- }
- static size_t ff_seek(void *fp, size_t offset) {
- f_lseek((FIL *) fp, offset);
- return offset;
- }
- static bool ff_rename(const char *from, const char *to) {
- return f_rename(from, to) == FR_OK;
- }
- static bool ff_remove(const char *path) {
- return f_unlink(path) == FR_OK;
- }
- static bool ff_mkdir(const char *path) {
- return f_mkdir(path) == FR_OK;
- }
- struct mg_fs mg_fs_fat = {ff_stat, ff_list, ff_open, ff_close, ff_read,
- ff_write, ff_seek, ff_rename, ff_remove, ff_mkdir};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/fs_packed.c"
- #endif
- struct packed_file {
- const char *data;
- size_t size;
- size_t pos;
- };
- const char *mg_unpack(const char *path, size_t *size, time_t *mtime);
- const char *mg_unlist(size_t no);
- #if MG_ENABLE_PACKED_FS
- #else
- const char *mg_unpack(const char *path, size_t *size, time_t *mtime) {
- (void) path, (void) size, (void) mtime;
- return NULL;
- }
- const char *mg_unlist(size_t no) {
- (void) no;
- return NULL;
- }
- #endif
- static int is_dir_prefix(const char *prefix, size_t n, const char *path) {
- // MG_INFO(("[%.*s] [%s] %c", (int) n, prefix, path, path[n]));
- return n < strlen(path) && strncmp(prefix, path, n) == 0 &&
- (n == 0 || path[n] == '/' || path[n - 1] == '/');
- }
- static int packed_stat(const char *path, size_t *size, time_t *mtime) {
- const char *p;
- size_t i, n = strlen(path);
- if (mg_unpack(path, size, mtime)) return MG_FS_READ; // Regular file
- // Scan all files. If `path` is a dir prefix for any of them, it's a dir
- for (i = 0; (p = mg_unlist(i)) != NULL; i++) {
- if (is_dir_prefix(path, n, p)) return MG_FS_DIR;
- }
- return 0;
- }
- static void packed_list(const char *dir, void (*fn)(const char *, void *),
- void *userdata) {
- char buf[MG_PATH_MAX], tmp[sizeof(buf)];
- const char *path, *begin, *end;
- size_t i, n = strlen(dir);
- tmp[0] = '\0'; // Previously listed entry
- for (i = 0; (path = mg_unlist(i)) != NULL; i++) {
- if (!is_dir_prefix(dir, n, path)) continue;
- begin = &path[n + 1];
- end = strchr(begin, '/');
- if (end == NULL) end = begin + strlen(begin);
- mg_snprintf(buf, sizeof(buf), "%.*s", (int) (end - begin), begin);
- buf[sizeof(buf) - 1] = '\0';
- // If this entry has been already listed, skip
- // NOTE: we're assuming that file list is sorted alphabetically
- if (strcmp(buf, tmp) == 0) continue;
- fn(buf, userdata); // Not yet listed, call user function
- strcpy(tmp, buf); // And save this entry as listed
- }
- }
- static void *packed_open(const char *path, int flags) {
- size_t size = 0;
- const char *data = mg_unpack(path, &size, NULL);
- struct packed_file *fp = NULL;
- if (data == NULL) return NULL;
- if (flags & MG_FS_WRITE) return NULL;
- fp = (struct packed_file *) calloc(1, sizeof(*fp));
- fp->size = size;
- fp->data = data;
- return (void *) fp;
- }
- static void packed_close(void *fp) {
- if (fp != NULL) free(fp);
- }
- static size_t packed_read(void *fd, void *buf, size_t len) {
- struct packed_file *fp = (struct packed_file *) fd;
- if (fp->pos + len > fp->size) len = fp->size - fp->pos;
- memcpy(buf, &fp->data[fp->pos], len);
- fp->pos += len;
- return len;
- }
- static size_t packed_write(void *fd, const void *buf, size_t len) {
- (void) fd, (void) buf, (void) len;
- return 0;
- }
- static size_t packed_seek(void *fd, size_t offset) {
- struct packed_file *fp = (struct packed_file *) fd;
- fp->pos = offset;
- if (fp->pos > fp->size) fp->pos = fp->size;
- return fp->pos;
- }
- static bool packed_rename(const char *from, const char *to) {
- (void) from, (void) to;
- return false;
- }
- static bool packed_remove(const char *path) {
- (void) path;
- return false;
- }
- static bool packed_mkdir(const char *path) {
- (void) path;
- return false;
- }
- struct mg_fs mg_fs_packed = {
- packed_stat, packed_list, packed_open, packed_close, packed_read,
- packed_write, packed_seek, packed_rename, packed_remove, packed_mkdir};
- #ifdef MG_ENABLE_LINES
- #line 1 "src/fs_posix.c"
- #endif
- #if MG_ENABLE_FILE
- #ifndef MG_STAT_STRUCT
- #define MG_STAT_STRUCT stat
- #endif
- #ifndef MG_STAT_FUNC
- #define MG_STAT_FUNC stat
- #endif
- static int p_stat(const char *path, size_t *size, time_t *mtime) {
- #if !defined(S_ISDIR)
- MG_ERROR(("stat() API is not supported. %p %p %p", path, size, mtime));
- return 0;
- #else
- #if MG_ARCH == MG_ARCH_WIN32
- struct _stati64 st;
- wchar_t tmp[MG_PATH_MAX];
- MultiByteToWideChar(CP_UTF8, 0, path, -1, tmp, sizeof(tmp) / sizeof(tmp[0]));
- if (_wstati64(tmp, &st) != 0) return 0;
- // If path is a symlink, windows reports 0 in st.st_size.
- // Get a real file size by opening it and jumping to the end
- if (st.st_size == 0 && (st.st_mode & _S_IFREG)) {
- FILE *fp = _wfopen(tmp, L"rb");
- if (fp != NULL) {
- fseek(fp, 0, SEEK_END);
- if (ftell(fp) > 0) st.st_size = ftell(fp); // Use _ftelli64 on win10+
- fclose(fp);
- }
- }
- #else
- struct MG_STAT_STRUCT st;
- if (MG_STAT_FUNC(path, &st) != 0) return 0;
- #endif
- if (size) *size = (size_t) st.st_size;
- if (mtime) *mtime = st.st_mtime;
- return MG_FS_READ | MG_FS_WRITE | (S_ISDIR(st.st_mode) ? MG_FS_DIR : 0);
- #endif
- }
- #if MG_ARCH == MG_ARCH_WIN32
- struct dirent {
- char d_name[MAX_PATH];
- };
- typedef struct win32_dir {
- HANDLE handle;
- WIN32_FIND_DATAW info;
- struct dirent result;
- } DIR;
- int gettimeofday(struct timeval *tv, void *tz) {
- FILETIME ft;
- unsigned __int64 tmpres = 0;
- if (tv != NULL) {
- GetSystemTimeAsFileTime(&ft);
- tmpres |= ft.dwHighDateTime;
- tmpres <<= 32;
- tmpres |= ft.dwLowDateTime;
- tmpres /= 10; // convert into microseconds
- tmpres -= (int64_t) 11644473600000000;
- tv->tv_sec = (long) (tmpres / 1000000UL);
- tv->tv_usec = (long) (tmpres % 1000000UL);
- }
- (void) tz;
- return 0;
- }
- static int to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) {
- int ret;
- char buf[MAX_PATH * 2], buf2[MAX_PATH * 2], *p;
- strncpy(buf, path, sizeof(buf));
- buf[sizeof(buf) - 1] = '\0';
- // Trim trailing slashes. Leave backslash for paths like "X:\"
- p = buf + strlen(buf) - 1;
- while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0';
- memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
- ret = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
- // Convert back to Unicode. If doubly-converted string does not match the
- // original, something is fishy, reject.
- WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
- NULL, NULL);
- if (strcmp(buf, buf2) != 0) {
- wbuf[0] = L'\0';
- ret = 0;
- }
- return ret;
- }
- DIR *opendir(const char *name) {
- DIR *d = NULL;
- wchar_t wpath[MAX_PATH];
- DWORD attrs;
- if (name == NULL) {
- SetLastError(ERROR_BAD_ARGUMENTS);
- } else if ((d = (DIR *) calloc(1, sizeof(*d))) == NULL) {
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- } else {
- to_wchar(name, wpath, sizeof(wpath) / sizeof(wpath[0]));
- attrs = GetFileAttributesW(wpath);
- if (attrs != 0Xffffffff && (attrs & FILE_ATTRIBUTE_DIRECTORY)) {
- (void) wcscat(wpath, L"\\*");
- d->handle = FindFirstFileW(wpath, &d->info);
- d->result.d_name[0] = '\0';
- } else {
- free(d);
- d = NULL;
- }
- }
- return d;
- }
- int closedir(DIR *d) {
- int result = 0;
- if (d != NULL) {
- if (d->handle != INVALID_HANDLE_VALUE)
- result = FindClose(d->handle) ? 0 : -1;
- free(d);
- } else {
- result = -1;
- SetLastError(ERROR_BAD_ARGUMENTS);
- }
- return result;
- }
- struct dirent *readdir(DIR *d) {
- struct dirent *result = NULL;
- if (d != NULL) {
- memset(&d->result, 0, sizeof(d->result));
- if (d->handle != INVALID_HANDLE_VALUE) {
- result = &d->result;
- WideCharToMultiByte(CP_UTF8, 0, d->info.cFileName, -1, result->d_name,
- sizeof(result->d_name), NULL, NULL);
- if (!FindNextFileW(d->handle, &d->info)) {
- FindClose(d->handle);
- d->handle = INVALID_HANDLE_VALUE;
- }
- } else {
- SetLastError(ERROR_FILE_NOT_FOUND);
- }
- } else {
- SetLastError(ERROR_BAD_ARGUMENTS);
- }
- return result;
- }
- #endif
- static void p_list(const char *dir, void (*fn)(const char *, void *),
- void *userdata) {
- #if MG_ENABLE_DIRLIST
- struct dirent *dp;
- DIR *dirp;
- if ((dirp = (opendir(dir))) == NULL) return;
- while ((dp = readdir(dirp)) != NULL) {
- if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
- fn(dp->d_name, userdata);
- }
- closedir(dirp);
- #else
- (void) dir, (void) fn, (void) userdata;
- #endif
- }
- static void *p_open(const char *path, int flags) {
- const char *mode = flags == MG_FS_READ ? "rb" : "a+b";
- #if MG_ARCH == MG_ARCH_WIN32
- wchar_t b1[MG_PATH_MAX], b2[10];
- MultiByteToWideChar(CP_UTF8, 0, path, -1, b1, sizeof(b1) / sizeof(b1[0]));
- MultiByteToWideChar(CP_UTF8, 0, mode, -1, b2, sizeof(b2) / sizeof(b2[0]));
- return (void *) _wfopen(b1, b2);
- #else
- return (void *) fopen(path, mode);
- #endif
- }
- static void p_close(void *fp) {
- fclose((FILE *) fp);
- }
- static size_t p_read(void *fp, void *buf, size_t len) {
- return fread(buf, 1, len, (FILE *) fp);
- }
- static size_t p_write(void *fp, const void *buf, size_t len) {
- return fwrite(buf, 1, len, (FILE *) fp);
- }
- static size_t p_seek(void *fp, size_t offset) {
- #if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || \
- (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \
- (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600)
- if (fseeko((FILE *) fp, (off_t) offset, SEEK_SET) != 0) (void) 0;
- #else
- if (fseek((FILE *) fp, (long) offset, SEEK_SET) != 0) (void) 0;
- #endif
- return (size_t) ftell((FILE *) fp);
- }
- static bool p_rename(const char *from, const char *to) {
- return rename(from, to) == 0;
- }
- static bool p_remove(const char *path) {
- return remove(path) == 0;
- }
- static bool p_mkdir(const char *path) {
- return mkdir(path, 0775) == 0;
- }
- #else
- static int p_stat(const char *path, size_t *size, time_t *mtime) {
- (void) path, (void) size, (void) mtime;
- return 0;
- }
- static void p_list(const char *path, void (*fn)(const char *, void *),
- void *userdata) {
- (void) path, (void) fn, (void) userdata;
- }
- static void *p_open(const char *path, int flags) {
- (void) path, (void) flags;
- return NULL;
- }
- static void p_close(void *fp) {
- (void) fp;
- }
- static size_t p_read(void *fd, void *buf, size_t len) {
- (void) fd, (void) buf, (void) len;
- return 0;
- }
- static size_t p_write(void *fd, const void *buf, size_t len) {
- (void) fd, (void) buf, (void) len;
- return 0;
- }
- static size_t p_seek(void *fd, size_t offset) {
- (void) fd, (void) offset;
- return (size_t) ~0;
- }
- static bool p_rename(const char *from, const char *to) {
- (void) from, (void) to;
- return false;
- }
- static bool p_remove(const char *path) {
- (void) path;
- return false;
- }
- static bool p_mkdir(const char *path) {
- (void) path;
- return false;
- }
- #endif
- struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read,
- p_write, p_seek, p_rename, p_remove, p_mkdir};
- #ifdef MG_ENABLE_LINES
- #line 1 "src/http.c"
- #endif
- // Chunk deletion marker is the MSB in the "processed" counter
- #define MG_DMARK ((size_t) 1 << (sizeof(size_t) * 8 - 1))
- // Multipart POST example:
- // --xyz
- // Content-Disposition: form-data; name="val"
- //
- // abcdef
- // --xyz
- // Content-Disposition: form-data; name="foo"; filename="a.txt"
- // Content-Type: text/plain
- //
- // hello world
- //
- // --xyz--
- size_t mg_http_next_multipart(struct mg_str body, size_t ofs,
- struct mg_http_part *part) {
- struct mg_str cd = mg_str_n("Content-Disposition", 19);
- const char *s = body.ptr;
- size_t b = ofs, h1, h2, b1, b2, max = body.len;
- // Init part params
- if (part != NULL) part->name = part->filename = part->body = mg_str_n(0, 0);
- // Skip boundary
- while (b + 2 < max && s[b] != '\r' && s[b + 1] != '\n') b++;
- if (b <= ofs || b + 2 >= max) return 0;
- // MG_INFO(("B: %zu %zu [%.*s]", ofs, b - ofs, (int) (b - ofs), s));
- // Skip headers
- h1 = h2 = b + 2;
- for (;;) {
- while (h2 + 2 < max && s[h2] != '\r' && s[h2 + 1] != '\n') h2++;
- if (h2 == h1) break;
- if (h2 + 2 >= max) return 0;
- // MG_INFO(("Header: [%.*s]", (int) (h2 - h1), &s[h1]));
- if (part != NULL && h1 + cd.len + 2 < h2 && s[h1 + cd.len] == ':' &&
- mg_ncasecmp(&s[h1], cd.ptr, cd.len) == 0) {
- struct mg_str v = mg_str_n(&s[h1 + cd.len + 2], h2 - (h1 + cd.len + 2));
- part->name = mg_http_get_header_var(v, mg_str_n("name", 4));
- part->filename = mg_http_get_header_var(v, mg_str_n("filename", 8));
- }
- h1 = h2 = h2 + 2;
- }
- b1 = b2 = h2 + 2;
- while (b2 + 2 + (b - ofs) + 2 < max && !(s[b2] == '\r' && s[b2 + 1] == '\n' &&
- memcmp(&s[b2 + 2], s, b - ofs) == 0))
- b2++;
- if (b2 + 2 >= max) return 0;
- if (part != NULL) part->body = mg_str_n(&s[b1], b2 - b1);
- // MG_INFO(("Body: [%.*s]", (int) (b2 - b1), &s[b1]));
- return b2 + 2;
- }
- void mg_http_bauth(struct mg_connection *c, const char *user,
- const char *pass) {
- struct mg_str u = mg_str(user), p = mg_str(pass);
- size_t need = c->send.len + 36 + (u.len + p.len) * 2;
- if (c->send.size < need) mg_iobuf_resize(&c->send, need);
- if (c->send.size >= need) {
- int i, n = 0;
- char *buf = (char *) &c->send.buf[c->send.len];
- memcpy(buf, "Authorization: Basic ", 21); // DON'T use mg_send!
- for (i = 0; i < (int) u.len; i++) {
- n = mg_base64_update(((unsigned char *) u.ptr)[i], buf + 21, n);
- }
- if (p.len > 0) {
- n = mg_base64_update(':', buf + 21, n);
- for (i = 0; i < (int) p.len; i++) {
- n = mg_base64_update(((unsigned char *) p.ptr)[i], buf + 21, n);
- }
- }
- n = mg_base64_final(buf + 21, n);
- c->send.len += 21 + (size_t) n + 2;
- memcpy(&c->send.buf[c->send.len - 2], "\r\n", 2);
- } else {
- MG_ERROR(("%lu oom %d->%d ", c->id, (int) c->send.size, (int) need));
- }
- }
- struct mg_str mg_http_var(struct mg_str buf, struct mg_str name) {
- struct mg_str k, v, result = mg_str_n(NULL, 0);
- while (mg_split(&buf, &k, &v, '&')) {
- if (name.len == k.len && mg_ncasecmp(name.ptr, k.ptr, k.len) == 0) {
- result = v;
- break;
- }
- }
- return result;
- }
- int mg_http_get_var(const struct mg_str *buf, const char *name, char *dst,
- size_t dst_len) {
- int len;
- if (dst == NULL || dst_len == 0) {
- len = -2; // Bad destination
- } else if (buf->ptr == NULL || name == NULL || buf->len == 0) {
- len = -1; // Bad source
- dst[0] = '\0';
- } else {
- struct mg_str v = mg_http_var(*buf, mg_str(name));
- if (v.ptr == NULL) {
- len = -4; // Name does not exist
- } else {
- len = mg_url_decode(v.ptr, v.len, dst, dst_len, 1);
- if (len < 0) len = -3; // Failed to decode
- }
- }
- return len;
- }
- static bool isx(int c) {
- return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
- (c >= 'A' && c <= 'F');
- }
- int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len,
- int is_form_url_encoded) {
- size_t i, j;
- for (i = j = 0; i < src_len && j + 1 < dst_len; i++, j++) {
- if (src[i] == '%') {
- // Use `i + 2 < src_len`, not `i < src_len - 2`, note small src_len
- if (i + 2 < src_len && isx(src[i + 1]) && isx(src[i + 2])) {
- mg_unhex(src + i + 1, 2, (uint8_t *) &dst[j]);
- i += 2;
- } else {
- return -1;
- }
- } else if (is_form_url_encoded && src[i] == '+') {
- dst[j] = ' ';
- } else {
- dst[j] = src[i];
- }
- }
- if (j < dst_len) dst[j] = '\0'; // Null-terminate the destination
- return i >= src_len && j < dst_len ? (int) j : -1;
- }
- static bool isok(uint8_t c) { return c == '\n' || c == '\r' || c >= ' '; }
- int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) {
- size_t i;
- for (i = 0; i < buf_len; i++) {
- if (!isok(buf[i])) return -1;
- if ((i > 0 && buf[i] == '\n' && buf[i - 1] == '\n') ||
- (i > 3 && buf[i] == '\n' && buf[i - 1] == '\r' && buf[i - 2] == '\n'))
- return (int) i + 1;
- }
- return 0;
- }
- static const char *skip(const char *s, const char *e, const char *d,
- struct mg_str *v) {
- v->ptr = s;
- while (s < e && *s != '\n' && strchr(d, *s) == NULL) s++;
- v->len = (size_t) (s - v->ptr);
- while (s < e && strchr(d, *s) != NULL) s++;
- return s;
- }
- struct mg_str *mg_http_get_header(struct mg_http_message *h, const char *name) {
- size_t i, n = strlen(name), max = sizeof(h->headers) / sizeof(h->headers[0]);
- for (i = 0; i < max && h->headers[i].name.len > 0; i++) {
- struct mg_str *k = &h->headers[i].name, *v = &h->headers[i].value;
- if (n == k->len && mg_ncasecmp(k->ptr, name, n) == 0) return v;
- }
- return NULL;
- }
- static void mg_http_parse_headers(const char *s, const char *end,
- struct mg_http_header *h, int max_headers) {
- int i;
- for (i = 0; i < max_headers; i++) {
- struct mg_str k, v, tmp;
- const char *he = skip(s, end, "\n", &tmp);
- s = skip(s, he, ": \r\n", &k);
- s = skip(s, he, "\r\n", &v);
- if (k.len == tmp.len) continue;
- while (v.len > 0 && v.ptr[v.len - 1] == ' ') v.len--; // Trim spaces
- if (k.len == 0) break;
- // MG_INFO(("--HH [%.*s] [%.*s] [%.*s]", (int) tmp.len - 1, tmp.ptr,
- //(int) k.len, k.ptr, (int) v.len, v.ptr));
- h[i].name = k;
- h[i].value = v;
- }
- }
- int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
- int is_response, req_len = mg_http_get_request_len((unsigned char *) s, len);
- const char *end = s == NULL ? NULL : s + req_len, *qs; // Cannot add to NULL
- struct mg_str *cl;
- memset(hm, 0, sizeof(*hm));
- if (req_len <= 0) return req_len;
- hm->message.ptr = hm->head.ptr = s;
- hm->body.ptr = end;
- hm->head.len = (size_t) req_len;
- hm->chunk.ptr = end;
- hm->message.len = hm->body.len = (size_t) ~0; // Set body length to infinite
- // Parse request line
- s = skip(s, end, " ", &hm->method);
- s = skip(s, end, " ", &hm->uri);
- s = skip(s, end, "\r\n", &hm->proto);
- // Sanity check. Allow protocol/reason to be empty
- if (hm->method.len == 0 || hm->uri.len == 0) return -1;
- // If URI contains '?' character, setup query string
- if ((qs = (const char *) memchr(hm->uri.ptr, '?', hm->uri.len)) != NULL) {
- hm->query.ptr = qs + 1;
- hm->query.len = (size_t) (&hm->uri.ptr[hm->uri.len] - (qs + 1));
- hm->uri.len = (size_t) (qs - hm->uri.ptr);
- }
- mg_http_parse_headers(s, end, hm->headers,
- sizeof(hm->headers) / sizeof(hm->headers[0]));
- if ((cl = mg_http_get_header(hm, "Content-Length")) != NULL) {
- hm->body.len = (size_t) mg_to64(*cl);
- hm->message.len = (size_t) req_len + hm->body.len;
- }
- // mg_http_parse() is used to parse both HTTP requests and HTTP
- // responses. If HTTP response does not have Content-Length set, then
- // body is read until socket is closed, i.e. body.len is infinite (~0).
- //
- // For HTTP requests though, according to
- // http://tools.ietf.org/html/rfc7231#section-8.1.3,
- // only POST and PUT methods have defined body semantics.
- // Therefore, if Content-Length is not specified and methods are
- // not one of PUT or POST, set body length to 0.
- //
- // So, if it is HTTP request, and Content-Length is not set,
- // and method is not (PUT or POST) then reset body length to zero.
- is_response = mg_ncasecmp(hm->method.ptr, "HTTP/", 5) == 0;
- if (hm->body.len == (size_t) ~0 && !is_response &&
- mg_vcasecmp(&hm->method, "PUT") != 0 &&
- mg_vcasecmp(&hm->method, "POST") != 0) {
- hm->body.len = 0;
- hm->message.len = (size_t) req_len;
- }
- // The 204 (No content) responses also have 0 body length
- if (hm->body.len == (size_t) ~0 && is_response &&
- mg_vcasecmp(&hm->uri, "204") == 0) {
- hm->body.len = 0;
- hm->message.len = (size_t) req_len;
- }
- return req_len;
- }
- static void mg_http_vprintf_chunk(struct mg_connection *c, const char *fmt,
- va_list *ap) {
- size_t len = c->send.len;
- mg_send(c, " \r\n", 10);
- mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
- if (c->send.len >= len + 10) {
- mg_snprintf((char *) c->send.buf + len, 9, "%08lx", c->send.len - len - 10);
- c->send.buf[len + 8] = '\r';
- if (c->send.len == len + 10) c->is_resp = 0; // Last chunk, reset marker
- }
- mg_send(c, "\r\n", 2);
- }
- void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- mg_http_vprintf_chunk(c, fmt, &ap);
- va_end(ap);
- }
- void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) {
- mg_printf(c, "%lx\r\n", (unsigned long) len);
- mg_send(c, buf, len);
- mg_send(c, "\r\n", 2);
- if (len == 0) c->is_resp = 0;
- }
- // clang-format off
- static const char *mg_http_status_code_str(int status_code) {
- switch (status_code) {
- case 100: return "Continue";
- case 201: return "Created";
- case 202: return "Accepted";
- case 204: return "No Content";
- case 206: return "Partial Content";
- case 301: return "Moved Permanently";
- case 302: return "Found";
- case 304: return "Not Modified";
- case 400: return "Bad Request";
- case 401: return "Unauthorized";
- case 403: return "Forbidden";
- case 404: return "Not Found";
- case 418: return "I'm a teapot";
- case 500: return "Internal Server Error";
- case 501: return "Not Implemented";
- default: return "OK";
- }
- }
- // clang-format on
- void mg_http_reply(struct mg_connection *c, int code, const char *headers,
- const char *fmt, ...) {
- va_list ap;
- size_t len;
- mg_printf(c, "HTTP/1.1 %d %s\r\n%sContent-Length: \r\n\r\n", code,
- mg_http_status_code_str(code), headers == NULL ? "" : headers);
- len = c->send.len;
- va_start(ap, fmt);
- mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, &ap);
- va_end(ap);
- if (c->send.len > 16) {
- size_t n = mg_snprintf((char *) &c->send.buf[len - 15], 11, "%-10lu",
- (unsigned long) (c->send.len - len));
- c->send.buf[len - 15 + n] = ' '; // Change ending 0 to space
- }
- c->is_resp = 0;
- }
- static void http_cb(struct mg_connection *, int, void *, void *);
- static void restore_http_cb(struct mg_connection *c) {
- mg_fs_close((struct mg_fd *) c->pfn_data);
- c->pfn_data = NULL;
- c->pfn = http_cb;
- c->is_resp = 0;
- }
- char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime);
- char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime) {
- mg_snprintf(buf, len, "\"%lld.%lld\"", (int64_t) mtime, (int64_t) size);
- return buf;
- }
- static void static_cb(struct mg_connection *c, int ev, void *ev_data,
- void *fn_data) {
- if (ev == MG_EV_WRITE || ev == MG_EV_POLL) {
- struct mg_fd *fd = (struct mg_fd *) fn_data;
- // Read to send IO buffer directly, avoid extra on-stack buffer
- size_t n, max = MG_IO_SIZE, space;
- size_t *cl = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) /
- sizeof(size_t) * sizeof(size_t)];
- if (c->send.size < max) mg_iobuf_resize(&c->send, max);
- if (c->send.len >= c->send.size) return; // Rate limit
- if ((space = c->send.size - c->send.len) > *cl) space = *cl;
- n = fd->fs->rd(fd->fd, c->send.buf + c->send.len, space);
- c->send.len += n;
- *cl -= n;
- if (n == 0) restore_http_cb(c);
- } else if (ev == MG_EV_CLOSE) {
- restore_http_cb(c);
- }
- (void) ev_data;
- }
- // Known mime types. Keep it outside guess_content_type() function, since
- // some environments don't like it defined there.
- // clang-format off
- static struct mg_str s_known_types[] = {
- MG_C_STR("html"), MG_C_STR("text/html; charset=utf-8"),
- MG_C_STR("htm"), MG_C_STR("text/html; charset=utf-8"),
- MG_C_STR("css"), MG_C_STR("text/css; charset=utf-8"),
- MG_C_STR("js"), MG_C_STR("text/javascript; charset=utf-8"),
- MG_C_STR("gif"), MG_C_STR("image/gif"),
- MG_C_STR("png"), MG_C_STR("image/png"),
- MG_C_STR("jpg"), MG_C_STR("image/jpeg"),
- MG_C_STR("jpeg"), MG_C_STR("image/jpeg"),
- MG_C_STR("woff"), MG_C_STR("font/woff"),
- MG_C_STR("ttf"), MG_C_STR("font/ttf"),
- MG_C_STR("svg"), MG_C_STR("image/svg+xml"),
- MG_C_STR("txt"), MG_C_STR("text/plain; charset=utf-8"),
- MG_C_STR("avi"), MG_C_STR("video/x-msvideo"),
- MG_C_STR("csv"), MG_C_STR("text/csv"),
- MG_C_STR("doc"), MG_C_STR("application/msword"),
- MG_C_STR("exe"), MG_C_STR("application/octet-stream"),
- MG_C_STR("gz"), MG_C_STR("application/gzip"),
- MG_C_STR("ico"), MG_C_STR("image/x-icon"),
- MG_C_STR("json"), MG_C_STR("application/json"),
- MG_C_STR("mov"), MG_C_STR("video/quicktime"),
- MG_C_STR("mp3"), MG_C_STR("audio/mpeg"),
- MG_C_STR("mp4"), MG_C_STR("video/mp4"),
- MG_C_STR("mpeg"), MG_C_STR("video/mpeg"),
- MG_C_STR("pdf"), MG_C_STR("application/pdf"),
- MG_C_STR("shtml"), MG_C_STR("text/html; charset=utf-8"),
- MG_C_STR("tgz"), MG_C_STR("application/tar-gz"),
- MG_C_STR("wav"), MG_C_STR("audio/wav"),
- MG_C_STR("webp"), MG_C_STR("image/webp"),
- MG_C_STR("zip"), MG_C_STR("application/zip"),
- MG_C_STR("3gp"), MG_C_STR("video/3gpp"),
- {0, 0},
- };
- // clang-format on
- static struct mg_str guess_content_type(struct mg_str path, const char *extra) {
- struct mg_str k, v, s = mg_str(extra);
- size_t i = 0;
- // Shrink path to its extension only
- while (i < path.len && path.ptr[path.len - i - 1] != '.') i++;
- path.ptr += path.len - i;
- path.len = i;
- // Process user-provided mime type overrides, if any
- while (mg_commalist(&s, &k, &v)) {
- if (mg_strcmp(path, k) == 0) return v;
- }
- // Process built-in mime types
- for (i = 0; s_known_types[i].ptr != NULL; i += 2) {
- if (mg_strcmp(path, s_known_types[i]) == 0) return s_known_types[i + 1];
- }
- return mg_str("text/plain; charset=utf-8");
- }
- static int getrange(struct mg_str *s, int64_t *a, int64_t *b) {
- size_t i, numparsed = 0;
- // MG_INFO(("%.*s", (int) s->len, s->ptr));
- for (i = 0; i + 6 < s->len; i++) {
- if (memcmp(&s->ptr[i], "bytes=", 6) == 0) {
- struct mg_str p = mg_str_n(s->ptr + i + 6, s->len - i - 6);
- if (p.len > 0 && p.ptr[0] >= '0' && p.ptr[0] <= '9') numparsed++;
- *a = mg_to64(p);
- // MG_INFO(("PPP [%.*s] %d", (int) p.len, p.ptr, numparsed));
- while (p.len && p.ptr[0] >= '0' && p.ptr[0] <= '9') p.ptr++, p.len--;
- if (p.len && p.ptr[0] == '-') p.ptr++, p.len--;
- *b = mg_to64(p);
- if (p.len > 0 && p.ptr[0] >= '0' && p.ptr[0] <= '9') numparsed++;
- // MG_INFO(("PPP [%.*s] %d", (int) p.len, p.ptr, numparsed));
- break;
- }
- }
- return (int) numparsed;
- }
- void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
- const char *path,
- const struct mg_http_serve_opts *opts) {
- char etag[64], tmp[MG_PATH_MAX];
- struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
- struct mg_fd *fd = path == NULL ? NULL : mg_fs_open(fs, path, MG_FS_READ);
- size_t size = 0;
- time_t mtime = 0;
- struct mg_str *inm = NULL;
- struct mg_str mime = guess_content_type(mg_str(path), opts->mime_types);
- bool gzip = false;
- // If file does not exist, we try to open file PATH.gz - and if such
- // pre-compressed .gz file exists, serve it with the Content-Encoding: gzip
- // Note - we ignore Accept-Encoding, cause we don't have a choice
- if (fd == NULL) {
- MG_DEBUG(("NULL [%s]", path));
- mg_snprintf(tmp, sizeof(tmp), "%s.gz", path);
- if ((fd = mg_fs_open(fs, tmp, MG_FS_READ)) != NULL) {
- gzip = true;
- path = tmp;
- } else if (opts->page404 != NULL) {
- // No precompressed file, serve 404
- fd = mg_fs_open(fs, opts->page404, MG_FS_READ);
- mime = guess_content_type(mg_str(path), opts->mime_types);
- path = opts->page404;
- }
- }
- if (fd == NULL || fs->st(path, &size, &mtime) == 0) {
- mg_http_reply(c, 404, opts->extra_headers, "Not found\n");
- mg_fs_close(fd);
- // NOTE: mg_http_etag() call should go first!
- } else if (mg_http_etag(etag, sizeof(etag), size, mtime) != NULL &&
- (inm = mg_http_get_header(hm, "If-None-Match")) != NULL &&
- mg_vcasecmp(inm, etag) == 0) {
- mg_fs_close(fd);
- mg_http_reply(c, 304, opts->extra_headers, "");
- } else {
- int n, status = 200;
- char range[100];
- int64_t r1 = 0, r2 = 0, cl = (int64_t) size;
- // Handle Range header
- struct mg_str *rh = mg_http_get_header(hm, "Range");
- range[0] = '\0';
- if (rh != NULL && (n = getrange(rh, &r1, &r2)) > 0 && r1 >= 0 && r2 >= 0) {
- // If range is specified like "400-", set second limit to content len
- if (n == 1) r2 = cl - 1;
- if (r1 > r2 || r2 >= cl) {
- status = 416;
- cl = 0;
- mg_snprintf(range, sizeof(range), "Content-Range: bytes */%lld\r\n",
- (int64_t) size);
- } else {
- status = 206;
- cl = r2 - r1 + 1;
- mg_snprintf(range, sizeof(range),
- "Content-Range: bytes %lld-%lld/%lld\r\n", r1, r1 + cl - 1,
- (int64_t) size);
- fs->sk(fd->fd, (size_t) r1);
- }
- }
- mg_printf(c,
- "HTTP/1.1 %d %s\r\n"
- "Content-Type: %.*s\r\n"
- "Etag: %s\r\n"
- "Content-Length: %llu\r\n"
- "%s%s%s\r\n",
- status, mg_http_status_code_str(status), (int) mime.len, mime.ptr,
- etag, cl, gzip ? "Content-Encoding: gzip\r\n" : "", range,
- opts->extra_headers ? opts->extra_headers : "");
- if (mg_vcasecmp(&hm->method, "HEAD") == 0) {
- c->is_draining = 1;
- c->is_resp = 0;
- mg_fs_close(fd);
- } else {
- // Track to-be-sent content length at the end of c->data, aligned
- size_t *clp = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) /
- sizeof(size_t) * sizeof(size_t)];
- c->pfn = static_cb;
- c->pfn_data = fd;
- *clp = (size_t) cl;
- }
- }
- }
- struct printdirentrydata {
- struct mg_connection *c;
- struct mg_http_message *hm;
- const struct mg_http_serve_opts *opts;
- const char *dir;
- };
- static void printdirentry(const char *name, void *userdata) {
- struct printdirentrydata *d = (struct printdirentrydata *) userdata;
- struct mg_fs *fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs;
- size_t size = 0;
- time_t t = 0;
- char path[MG_PATH_MAX], sz[40], mod[40];
- int flags, n = 0;
- // MG_DEBUG(("[%s] [%s]", d->dir, name));
- if (mg_snprintf(path, sizeof(path), "%s%c%s", d->dir, '/', name) >
- sizeof(path)) {
- MG_ERROR(("%s truncated", name));
- } else if ((flags = fs->st(path, &size, &t)) == 0) {
- MG_ERROR(("%lu stat(%s): %d", d->c->id, path, errno));
- } else {
- const char *slash = flags & MG_FS_DIR ? "/" : "";
- if (flags & MG_FS_DIR) {
- mg_snprintf(sz, sizeof(sz), "%s", "[DIR]");
- } else {
- mg_snprintf(sz, sizeof(sz), "%lld", (uint64_t) size);
- }
- #if defined(MG_HTTP_DIRLIST_TIME_FMT)
- {
- char time_str[40];
- struct tm *time_info = localtime(&t);
- strftime(time_str, sizeof time_str, "%Y/%m/%d %H:%M:%S", time_info);
- mg_snprintf(mod, sizeof(mod), "%s", time_str);
- }
- #else
- mg_snprintf(mod, sizeof(mod), "%lu", (unsigned long) t);
- #endif
- n = (int) mg_url_encode(name, strlen(name), path, sizeof(path));
- mg_printf(d->c,
- " <tr><td><a href=\"%.*s%s\">%s%s</a></td>"
- "<td name=%lu>%s</td><td name=%lld>%s</td></tr>\n",
- n, path, slash, name, slash, (unsigned long) t, mod,
- flags & MG_FS_DIR ? (int64_t) -1 : (int64_t) size, sz);
- }
- }
- static void listdir(struct mg_connection *c, struct mg_http_message *hm,
- const struct mg_http_serve_opts *opts, char *dir) {
- const char *sort_js_code =
- "<script>function srt(tb, sc, so, d) {"
- "var tr = Array.prototype.slice.call(tb.rows, 0),"
- "tr = tr.sort(function (a, b) { var c1 = a.cells[sc], c2 = b.cells[sc],"
- "n1 = c1.getAttribute('name'), n2 = c2.getAttribute('name'), "
- "t1 = a.cells[2].getAttribute('name'), "
- "t2 = b.cells[2].getAttribute('name'); "
- "return so * (t1 < 0 && t2 >= 0 ? -1 : t2 < 0 && t1 >= 0 ? 1 : "
- "n1 ? parseInt(n2) - parseInt(n1) : "
- "c1.textContent.trim().localeCompare(c2.textContent.trim())); });";
- const char *sort_js_code2 =
- "for (var i = 0; i < tr.length; i++) tb.appendChild(tr[i]); "
- "if (!d) window.location.hash = ('sc=' + sc + '&so=' + so); "
- "};"
- "window.onload = function() {"
- "var tb = document.getElementById('tb');"
- "var m = /sc=([012]).so=(1|-1)/.exec(window.location.hash) || [0, 2, 1];"
- "var sc = m[1], so = m[2]; document.onclick = function(ev) { "
- "var c = ev.target.rel; if (c) {if (c == sc) so *= -1; srt(tb, c, so); "
- "sc = c; ev.preventDefault();}};"
- "srt(tb, sc, so, true);"
- "}"
- "</script>";
- struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
- struct printdirentrydata d = {c, hm, opts, dir};
- char tmp[10], buf[MG_PATH_MAX];
- size_t off, n;
- int len = mg_url_decode(hm->uri.ptr, hm->uri.len, buf, sizeof(buf), 0);
- struct mg_str uri = len > 0 ? mg_str_n(buf, (size_t) len) : hm->uri;
- mg_printf(c,
- "HTTP/1.1 200 OK\r\n"
- "Content-Type: text/html; charset=utf-8\r\n"
- "%s"
- "Content-Length: \r\n\r\n",
- opts->extra_headers == NULL ? "" : opts->extra_headers);
- off = c->send.len; // Start of body
- mg_printf(c,
- "<!DOCTYPE html><html><head><title>Index of %.*s</title>%s%s"
- "<style>th,td {text-align: left; padding-right: 1em; "
- "font-family: monospace; }</style></head>"
- "<body><h1>Index of %.*s</h1><table cellpadding=\"0\"><thead>"
- "<tr><th><a href=\"#\" rel=\"0\">Name</a></th><th>"
- "<a href=\"#\" rel=\"1\">Modified</a></th>"
- "<th><a href=\"#\" rel=\"2\">Size</a></th></tr>"
- "<tr><td colspan=\"3\"><hr></td></tr>"
- "</thead>"
- "<tbody id=\"tb\">\n",
- (int) uri.len, uri.ptr, sort_js_code, sort_js_code2, (int) uri.len,
- uri.ptr);
- mg_printf(c, "%s",
- " <tr><td><a href=\"..\">..</a></td>"
- "<td name=-1></td><td name=-1>[DIR]</td></tr>\n");
- fs->ls(dir, printdirentry, &d);
- mg_printf(c,
- "</tbody><tfoot><tr><td colspan=\"3\"><hr></td></tr></tfoot>"
- "</table><address>Mongoose v.%s</address></body></html>\n",
- MG_VERSION);
- n = mg_snprintf(tmp, sizeof(tmp), "%lu", (unsigned long) (c->send.len - off));
- if (n > sizeof(tmp)) n = 0;
- memcpy(c->send.buf + off - 12, tmp, n); // Set content length
- c->is_resp = 0; // Mark response end
- }
- // Resolve requested file into `path` and return its fs->st() result
- static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
- struct mg_fs *fs, struct mg_str url, struct mg_str dir,
- char *path, size_t path_size) {
- int flags, tmp;
- // Append URI to the root_dir, and sanitize it
- size_t n = mg_snprintf(path, path_size, "%.*s", (int) dir.len, dir.ptr);
- if (n > path_size) n = path_size;
- path[path_size - 1] = '\0';
- if (n + 2 < path_size) path[n++] = '/', path[n] = '\0';
- mg_url_decode(hm->uri.ptr + url.len, hm->uri.len - url.len, path + n,
- path_size - n, 0);
- path[path_size - 1] = '\0'; // Double-check
- mg_remove_double_dots(path);
- n = strlen(path);
- while (n > 1 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes
- flags = mg_vcmp(&hm->uri, "/") == 0 ? MG_FS_DIR : fs->st(path, NULL, NULL);
- MG_VERBOSE(("%lu %.*s -> %s %d", c->id, (int) hm->uri.len, hm->uri.ptr, path,
- flags));
- if (flags == 0) {
- // Do nothing - let's caller decide
- } else if ((flags & MG_FS_DIR) && hm->uri.len > 0 &&
- hm->uri.ptr[hm->uri.len - 1] != '/') {
- mg_printf(c,
- "HTTP/1.1 301 Moved\r\n"
- "Location: %.*s/\r\n"
- "Content-Length: 0\r\n"
- "\r\n",
- (int) hm->uri.len, hm->uri.ptr);
- c->is_resp = 0;
- flags = -1;
- } else if (flags & MG_FS_DIR) {
- if (((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX) > 0 &&
- (tmp = fs->st(path, NULL, NULL)) != 0) ||
- (mg_snprintf(path + n, path_size - n, "/index.shtml") > 0 &&
- (tmp = fs->st(path, NULL, NULL)) != 0))) {
- flags = tmp;
- } else if ((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX ".gz") >
- 0 &&
- (tmp = fs->st(path, NULL, NULL)) !=
- 0)) { // check for gzipped index
- flags = tmp;
- path[n + 1 + strlen(MG_HTTP_INDEX)] =
- '\0'; // Remove appended .gz in index file name
- } else {
- path[n] = '\0'; // Remove appended index file name
- }
- }
- return flags;
- }
- static int uri_to_path(struct mg_connection *c, struct mg_http_message *hm,
- const struct mg_http_serve_opts *opts, char *path,
- size_t path_size) {
- struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
- struct mg_str k, v, s = mg_str(opts->root_dir), u = {0, 0}, p = {0, 0};
- while (mg_commalist(&s, &k, &v)) {
- if (v.len == 0) v = k, k = mg_str("/");
- if (hm->uri.len < k.len) continue;
- if (mg_strcmp(k, mg_str_n(hm->uri.ptr, k.len)) != 0) continue;
- u = k, p = v;
- }
- return uri_to_path2(c, hm, fs, u, p, path, path_size);
- }
- void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
- const struct mg_http_serve_opts *opts) {
- char path[MG_PATH_MAX];
- const char *sp = opts->ssi_pattern;
- int flags = uri_to_path(c, hm, opts, path, sizeof(path));
- if (flags < 0) {
- // Do nothing: the response has already been sent by uri_to_path()
- } else if (flags & MG_FS_DIR) {
- listdir(c, hm, opts, path);
- } else if (flags && sp != NULL &&
- mg_globmatch(sp, strlen(sp), path, strlen(path))) {
- mg_http_serve_ssi(c, opts->root_dir, path);
- } else {
- mg_http_serve_file(c, hm, path, opts);
- }
- }
- static bool mg_is_url_safe(int c) {
- return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') || c == '.' || c == '_' || c == '-' || c == '~';
- }
- size_t mg_url_encode(const char *s, size_t sl, char *buf, size_t len) {
- size_t i, n = 0;
- for (i = 0; i < sl; i++) {
- int c = *(unsigned char *) &s[i];
- if (n + 4 >= len) return 0;
- if (mg_is_url_safe(c)) {
- buf[n++] = s[i];
- } else {
- buf[n++] = '%';
- mg_hex(&s[i], 1, &buf[n]);
- n += 2;
- }
- }
- if (len > 0 && n < len - 1) buf[n] = '\0'; // Null-terminate the destination
- if (len > 0) buf[len - 1] = '\0'; // Always.
- return n;
- }
- void mg_http_creds(struct mg_http_message *hm, char *user, size_t userlen,
- char *pass, size_t passlen) {
- struct mg_str *v = mg_http_get_header(hm, "Authorization");
- user[0] = pass[0] = '\0';
- if (v != NULL && v->len > 6 && memcmp(v->ptr, "Basic ", 6) == 0) {
- char buf[256];
- int n = mg_base64_decode(v->ptr + 6, (int) v->len - 6, buf);
- const char *p = (const char *) memchr(buf, ':', n > 0 ? (size_t) n : 0);
- if (p != NULL) {
- mg_snprintf(user, userlen, "%.*s", (int) (p - buf), buf);
- mg_snprintf(pass, passlen, "%.*s", n - (int) (p - buf) - 1, p + 1);
- }
- } else if (v != NULL && v->len > 7 && memcmp(v->ptr, "Bearer ", 7) == 0) {
- mg_snprintf(pass, passlen, "%.*s", (int) v->len - 7, v->ptr + 7);
- } else if ((v = mg_http_get_header(hm, "Cookie")) != NULL) {
- struct mg_str t = mg_http_get_header_var(*v, mg_str_n("access_token", 12));
- if (t.len > 0) mg_snprintf(pass, passlen, "%.*s", (int) t.len, t.ptr);
- } else {
- mg_http_get_var(&hm->query, "access_token", pass, passlen);
- }
- }
- static struct mg_str stripquotes(struct mg_str s) {
- return s.len > 1 && s.ptr[0] == '"' && s.ptr[s.len - 1] == '"'
- ? mg_str_n(s.ptr + 1, s.len - 2)
- : s;
- }
- struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v) {
- size_t i;
- for (i = 0; v.len > 0 && i + v.len + 2 < s.len; i++) {
- if (s.ptr[i + v.len] == '=' && memcmp(&s.ptr[i], v.ptr, v.len) == 0) {
- const char *p = &s.ptr[i + v.len + 1], *b = p, *x = &s.ptr[s.len];
- int q = p < x && *p == '"' ? 1 : 0;
- while (p < x &&
- (q ? p == b || *p != '"' : *p != ';' && *p != ' ' && *p != ','))
- p++;
- // MG_INFO(("[%.*s] [%.*s] [%.*s]", (int) s.len, s.ptr, (int) v.len,
- // v.ptr, (int) (p - b), b));
- return stripquotes(mg_str_n(b, (size_t) (p - b + q)));
- }
- }
- return mg_str_n(NULL, 0);
- }
- bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob) {
- return mg_match(hm->uri, mg_str(glob), NULL);
- }
- long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
- struct mg_fs *fs, const char *path, size_t max_size) {
- char buf[20] = "0";
- long res = 0, offset;
- mg_http_get_var(&hm->query, "offset", buf, sizeof(buf));
- offset = strtol(buf, NULL, 0);
- if (hm->body.len == 0) {
- mg_http_reply(c, 200, "", "%ld", res); // Nothing to write
- } else {
- struct mg_fd *fd;
- size_t current_size = 0;
- MG_DEBUG(("%s -> %d bytes @ %ld", path, (int) hm->body.len, offset));
- if (offset == 0) fs->rm(path); // If offset if 0, truncate file
- fs->st(path, ¤t_size, NULL);
- if (offset < 0) {
- mg_http_reply(c, 400, "", "offset required");
- res = -1;
- } else if (offset > 0 && current_size != (size_t) offset) {
- mg_http_reply(c, 400, "", "%s: offset mismatch", path);
- res = -2;
- } else if ((size_t) offset + hm->body.len > max_size) {
- mg_http_reply(c, 400, "", "%s: over max size of %lu", path,
- (unsigned long) max_size);
- res = -3;
- } else if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
- mg_http_reply(c, 400, "", "open(%s): %d", path, errno);
- res = -4;
- } else {
- res = offset + (long) fs->wr(fd->fd, hm->body.ptr, hm->body.len);
- mg_fs_close(fd);
- mg_http_reply(c, 200, "", "%ld", res);
- }
- }
- return res;
- }
- int mg_http_status(const struct mg_http_message *hm) {
- return atoi(hm->uri.ptr);
- }
- // If a server sends data to the client using chunked encoding, Mongoose strips
- // off the chunking prefix (hex length and \r\n) and suffix (\r\n), appends the
- // stripped data to the body, and fires the MG_EV_HTTP_CHUNK event. When zero
- // chunk is received, we fire MG_EV_HTTP_MSG, and the body already has all
- // chunking prefixes/suffixes stripped.
- //
- // If a server sends data without chunked encoding, we also fire a series of
- // MG_EV_HTTP_CHUNK events for every received piece of data, and then we fire
- // MG_EV_HTTP_MSG event in the end.
- //
- // We track total processed length in the c->pfn_data, which is a void *
- // pointer: we store a size_t value there.
- static bool getchunk(struct mg_str s, size_t *prefixlen, size_t *datalen) {
- size_t i = 0, n;
- while (i < s.len && s.ptr[i] != '\r' && s.ptr[i] != '\n') i++;
- n = mg_unhexn(s.ptr, i);
- // MG_INFO(("%d %d", (int) (i + n + 4), (int) s.len));
- if (s.len < i + n + 4) return false; // Chunk not yet fully buffered
- if (s.ptr[i] != '\r' || s.ptr[i + 1] != '\n') return false;
- if (s.ptr[i + n + 2] != '\r' || s.ptr[i + n + 3] != '\n') return false;
- *prefixlen = i + 2;
- *datalen = n;
- return true;
- }
- static bool mg_is_chunked(struct mg_http_message *hm) {
- const char *needle = "chunked";
- struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding");
- return te != NULL && mg_vcasecmp(te, needle) == 0;
- }
- void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm) {
- size_t ofs = (size_t) (hm->chunk.ptr - (char *) c->recv.buf);
- mg_iobuf_del(&c->recv, ofs, hm->chunk.len);
- c->pfn_data = (void *) ((size_t) c->pfn_data | MG_DMARK);
- }
- static void deliver_chunked_chunks(struct mg_connection *c, size_t hlen,
- struct mg_http_message *hm, bool *next) {
- // | ... headers ... | HEXNUM\r\n ..data.. \r\n | ......
- // +------------------+--------------------------+----
- // | hlen | chunk1 | ......
- char *buf = (char *) &c->recv.buf[hlen], *p = buf;
- size_t len = c->recv.len - hlen;
- size_t processed = ((size_t) c->pfn_data) & ~MG_DMARK;
- size_t mark, pl, dl, del = 0, ofs = 0;
- bool last = false;
- if (processed <= len) len -= processed, buf += processed;
- while (!last && getchunk(mg_str_n(buf + ofs, len - ofs), &pl, &dl)) {
- size_t saved = c->recv.len;
- memmove(p + processed, buf + ofs + pl, dl);
- // MG_INFO(("P2 [%.*s]", (int) (processed + dl), p));
- hm->chunk = mg_str_n(p + processed, dl);
- mg_call(c, MG_EV_HTTP_CHUNK, hm);
- ofs += pl + dl + 2, del += pl + 2; // 2 is for \r\n suffix
- processed += dl;
- if (c->recv.len != saved) processed -= dl, buf -= dl;
- // mg_hexdump(c->recv.buf, hlen + processed);
- last = (dl == 0);
- }
- mg_iobuf_del(&c->recv, hlen + processed, del);
- mark = ((size_t) c->pfn_data) & MG_DMARK;
- c->pfn_data = (void *) (processed | mark);
- if (last) {
- hm->body.len = processed;
- hm->message.len = hlen + processed;
- c->pfn_data = NULL;
- if (mark) mg_iobuf_del(&c->recv, 0, hlen), *next = true;
- // MG_INFO(("LAST, mark: %lx", mark));
- // mg_hexdump(c->recv.buf, c->recv.len);
- }
- }
- static void deliver_normal_chunks(struct mg_connection *c, size_t hlen,
- struct mg_http_message *hm, bool *next) {
- size_t left, processed = ((size_t) c->pfn_data) & ~MG_DMARK;
- size_t deleted = ((size_t) c->pfn_data) & MG_DMARK;
- hm->chunk = mg_str_n((char *) &c->recv.buf[hlen], c->recv.len - hlen);
- if (processed <= hm->chunk.len && !deleted) {
- hm->chunk.len -= processed;
- hm->chunk.ptr += processed;
- }
- left = hm->body.len < processed ? 0 : hm->body.len - processed;
- if (hm->chunk.len > left) hm->chunk.len = left;
- if (hm->chunk.len > 0) mg_call(c, MG_EV_HTTP_CHUNK, hm);
- processed += hm->chunk.len;
- deleted = ((size_t) c->pfn_data) & MG_DMARK; // Re-evaluate after user call
- if (processed >= hm->body.len) { // Last, 0-len chunk
- hm->chunk.len = 0; // Reset length
- mg_call(c, MG_EV_HTTP_CHUNK, hm); // Call user handler
- c->pfn_data = NULL; // Reset processed counter
- if (processed && deleted) mg_iobuf_del(&c->recv, 0, hlen), *next = true;
- } else {
- c->pfn_data = (void *) (processed | deleted); // if it is set
- }
- }
- static void http_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
- if (ev == MG_EV_READ || ev == MG_EV_CLOSE) {
- struct mg_http_message hm;
- // mg_hexdump(c->recv.buf, c->recv.len);
- while (c->recv.buf != NULL && c->recv.len > 0) {
- bool next = false;
- int hlen = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm);
- if (hlen < 0) {
- mg_error(c, "HTTP parse:\n%.*s", (int) c->recv.len, c->recv.buf);
- break;
- }
- if (c->is_resp) break; // Response is still generated
- if (hlen == 0) break; // Request is not buffered yet
- if (ev == MG_EV_CLOSE) { // If client did not set Content-Length
- hm.message.len = c->recv.len; // and closes now, deliver a MSG
- hm.body.len = hm.message.len - (size_t) (hm.body.ptr - hm.message.ptr);
- }
- if (mg_is_chunked(&hm)) {
- deliver_chunked_chunks(c, (size_t) hlen, &hm, &next);
- } else {
- deliver_normal_chunks(c, (size_t) hlen, &hm, &next);
- }
- if (next) continue; // Chunks & request were deleted
- // Chunk events are delivered. If we have full body, deliver MSG
- if (c->recv.len < hm.message.len) break;
- if (c->is_accepted) c->is_resp = 1; // Start generating response
- mg_call(c, MG_EV_HTTP_MSG, &hm); // User handler can clear is_resp
- mg_iobuf_del(&c->recv, 0, hm.message.len);
- }
- }
- (void) evd, (void) fnd;
- }
- static void mg_hfn(struct mg_connection *c, int ev, void *ev_data, void *fnd) {
- if (ev == MG_EV_HTTP_MSG) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- if (mg_http_match_uri(hm, "/quit")) {
- mg_http_reply(c, 200, "", "ok\n");
- c->is_draining = 1;
- c->data[0] = 'X';
- } else if (mg_http_match_uri(hm, "/debug")) {
- int level = (int) mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
- mg_log_set(level);
- mg_http_reply(c, 200, "", "Debug level set to %d\n", level);
- } else {
- mg_http_reply(c, 200, "", "hi\n");
- }
- } else if (ev == MG_EV_CLOSE) {
- if (c->data[0] == 'X') *(bool *) fnd = true;
- }
- }
- void mg_hello(const char *url) {
- struct mg_mgr mgr;
- bool done = false;
- mg_mgr_init(&mgr);
- if (mg_http_listen(&mgr, url, mg_hfn, &done) == NULL) done = true;
- while (done == false) mg_mgr_poll(&mgr, 100);
- mg_mgr_free(&mgr);
- }
- struct mg_connection *mg_http_connect(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data) {
- struct mg_connection *c = mg_connect(mgr, url, fn, fn_data);
- if (c != NULL) c->pfn = http_cb;
- return c;
- }
- struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data) {
- struct mg_connection *c = mg_listen(mgr, url, fn, fn_data);
- if (c != NULL) c->pfn = http_cb;
- return c;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/iobuf.c"
- #endif
- // Not using memset for zeroing memory, cause it can be dropped by compiler
- // See https://github.com/cesanta/mongoose/pull/1265
- static void zeromem(volatile unsigned char *buf, size_t len) {
- if (buf != NULL) {
- while (len--) *buf++ = 0;
- }
- }
- static size_t roundup(size_t size, size_t align) {
- return align == 0 ? size : (size + align - 1) / align * align;
- }
- int mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) {
- int ok = 1;
- new_size = roundup(new_size, io->align);
- if (new_size == 0) {
- zeromem(io->buf, io->size);
- free(io->buf);
- io->buf = NULL;
- io->len = io->size = 0;
- } else if (new_size != io->size) {
- // NOTE(lsm): do not use realloc here. Use calloc/free only, to ease the
- // porting to some obscure platforms like FreeRTOS
- void *p = calloc(1, new_size);
- if (p != NULL) {
- size_t len = new_size < io->len ? new_size : io->len;
- if (len > 0 && io->buf != NULL) memmove(p, io->buf, len);
- zeromem(io->buf, io->size);
- free(io->buf);
- io->buf = (unsigned char *) p;
- io->size = new_size;
- } else {
- ok = 0;
- MG_ERROR(("%lld->%lld", (uint64_t) io->size, (uint64_t) new_size));
- }
- }
- return ok;
- }
- int mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align) {
- io->buf = NULL;
- io->align = align;
- io->size = io->len = 0;
- return mg_iobuf_resize(io, size);
- }
- size_t mg_iobuf_add(struct mg_iobuf *io, size_t ofs, const void *buf,
- size_t len) {
- size_t new_size = roundup(io->len + len, io->align);
- mg_iobuf_resize(io, new_size); // Attempt to resize
- if (new_size != io->size) len = 0; // Resize failure, append nothing
- if (ofs < io->len) memmove(io->buf + ofs + len, io->buf + ofs, io->len - ofs);
- if (buf != NULL) memmove(io->buf + ofs, buf, len);
- if (ofs > io->len) io->len += ofs - io->len;
- io->len += len;
- return len;
- }
- size_t mg_iobuf_del(struct mg_iobuf *io, size_t ofs, size_t len) {
- if (ofs > io->len) ofs = io->len;
- if (ofs + len > io->len) len = io->len - ofs;
- if (io->buf) memmove(io->buf + ofs, io->buf + ofs + len, io->len - ofs - len);
- if (io->buf) zeromem(io->buf + io->len - len, len);
- io->len -= len;
- return len;
- }
- void mg_iobuf_free(struct mg_iobuf *io) {
- mg_iobuf_resize(io, 0);
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/json.c"
- #endif
- static const char *escapeseq(int esc) {
- return esc ? "\b\f\n\r\t\\\"" : "bfnrt\\\"";
- }
- static char json_esc(int c, int esc) {
- const char *p, *esc1 = escapeseq(esc), *esc2 = escapeseq(!esc);
- for (p = esc1; *p != '\0'; p++) {
- if (*p == c) return esc2[p - esc1];
- }
- return 0;
- }
- static int mg_pass_string(const char *s, int len) {
- int i;
- for (i = 0; i < len; i++) {
- if (s[i] == '\\' && i + 1 < len && json_esc(s[i + 1], 1)) {
- i++;
- } else if (s[i] == '\0') {
- return MG_JSON_INVALID;
- } else if (s[i] == '"') {
- return i;
- }
- }
- return MG_JSON_INVALID;
- }
- static double mg_atod(const char *p, int len, int *numlen) {
- double d = 0.0;
- int i = 0, sign = 1;
- // Sign
- if (i < len && *p == '-') {
- sign = -1, i++;
- } else if (i < len && *p == '+') {
- i++;
- }
- // Decimal
- for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) {
- d *= 10.0;
- d += p[i] - '0';
- }
- d *= sign;
- // Fractional
- if (i < len && p[i] == '.') {
- double frac = 0.0, base = 0.1;
- i++;
- for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) {
- frac += base * (p[i] - '0');
- base /= 10.0;
- }
- d += frac * sign;
- }
- // Exponential
- if (i < len && (p[i] == 'e' || p[i] == 'E')) {
- int j, exp = 0, minus = 0;
- i++;
- if (i < len && p[i] == '-') minus = 1, i++;
- if (i < len && p[i] == '+') i++;
- while (i < len && p[i] >= '0' && p[i] <= '9' && exp < 308)
- exp = exp * 10 + (p[i++] - '0');
- if (minus) exp = -exp;
- for (j = 0; j < exp; j++) d *= 10.0;
- for (j = 0; j < -exp; j++) d /= 10.0;
- }
- if (numlen != NULL) *numlen = i;
- return d;
- }
- int mg_json_get(struct mg_str json, const char *path, int *toklen) {
- const char *s = json.ptr;
- int len = (int) json.len;
- enum { S_VALUE, S_KEY, S_COLON, S_COMMA_OR_EOO } expecting = S_VALUE;
- unsigned char nesting[MG_JSON_MAX_DEPTH];
- int i = 0; // Current offset in `s`
- int j = 0; // Offset in `s` we're looking for (return value)
- int depth = 0; // Current depth (nesting level)
- int ed = 0; // Expected depth
- int pos = 1; // Current position in `path`
- int ci = -1, ei = -1; // Current and expected index in array
- if (toklen) *toklen = 0;
- if (path[0] != '$') return MG_JSON_INVALID;
- #define MG_CHECKRET(x) \
- do { \
- if (depth == ed && path[pos] == '\0' && ci == ei) { \
- if (toklen) *toklen = i - j + 1; \
- return j; \
- } \
- } while (0)
- // In the ascii table, the distance between `[` and `]` is 2.
- // Ditto for `{` and `}`. Hence +2 in the code below.
- #define MG_EOO(x) \
- do { \
- if (depth == ed && ci != ei) return MG_JSON_NOT_FOUND; \
- if (c != nesting[depth - 1] + 2) return MG_JSON_INVALID; \
- depth--; \
- MG_CHECKRET(x); \
- } while (0)
- for (i = 0; i < len; i++) {
- unsigned char c = ((unsigned char *) s)[i];
- if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue;
- switch (expecting) {
- case S_VALUE:
- // p("V %s [%.*s] %d %d %d %d\n", path, pos, path, depth, ed, ci, ei);
- if (depth == ed) j = i;
- if (c == '{') {
- if (depth >= (int) sizeof(nesting)) return MG_JSON_TOO_DEEP;
- if (depth == ed && path[pos] == '.' && ci == ei) {
- // If we start the object, reset array indices
- ed++, pos++, ci = ei = -1;
- }
- nesting[depth++] = c;
- expecting = S_KEY;
- break;
- } else if (c == '[') {
- if (depth >= (int) sizeof(nesting)) return MG_JSON_TOO_DEEP;
- if (depth == ed && path[pos] == '[' && ei == ci) {
- ed++, pos++, ci = 0;
- for (ei = 0; path[pos] != ']' && path[pos] != '\0'; pos++) {
- ei *= 10;
- ei += path[pos] - '0';
- }
- if (path[pos] != 0) pos++;
- }
- nesting[depth++] = c;
- break;
- } else if (c == ']' && depth > 0) { // Empty array
- MG_EOO(']');
- } else if (c == 't' && i + 3 < len && memcmp(&s[i], "true", 4) == 0) {
- i += 3;
- } else if (c == 'n' && i + 3 < len && memcmp(&s[i], "null", 4) == 0) {
- i += 3;
- } else if (c == 'f' && i + 4 < len && memcmp(&s[i], "false", 5) == 0) {
- i += 4;
- } else if (c == '-' || ((c >= '0' && c <= '9'))) {
- int numlen = 0;
- mg_atod(&s[i], len - i, &numlen);
- i += numlen - 1;
- } else if (c == '"') {
- int n = mg_pass_string(&s[i + 1], len - i - 1);
- if (n < 0) return n;
- i += n + 1;
- } else {
- return MG_JSON_INVALID;
- }
- MG_CHECKRET('V');
- if (depth == ed && ei >= 0) ci++;
- expecting = S_COMMA_OR_EOO;
- break;
- case S_KEY:
- if (c == '"') {
- int n = mg_pass_string(&s[i + 1], len - i - 1);
- if (n < 0) return n;
- if (i + 1 + n >= len) return MG_JSON_NOT_FOUND;
- if (depth < ed) return MG_JSON_NOT_FOUND;
- if (depth == ed && path[pos - 1] != '.') return MG_JSON_NOT_FOUND;
- // printf("K %s [%.*s] [%.*s] %d %d %d\n", path, pos, path, n,
- // &s[i + 1], n, depth, ed);
- // NOTE(cpq): in the check sequence below is important.
- // strncmp() must go first: it fails fast if the remaining length of
- // the path is smaller than `n`.
- if (depth == ed && path[pos - 1] == '.' &&
- strncmp(&s[i + 1], &path[pos], (size_t) n) == 0 &&
- (path[pos + n] == '\0' || path[pos + n] == '.' ||
- path[pos + n] == '[')) {
- pos += n;
- }
- i += n + 1;
- expecting = S_COLON;
- } else if (c == '}') { // Empty object
- MG_EOO('}');
- expecting = S_COMMA_OR_EOO;
- } else {
- return MG_JSON_INVALID;
- }
- break;
- case S_COLON:
- if (c == ':') {
- expecting = S_VALUE;
- } else {
- return MG_JSON_INVALID;
- }
- break;
- case S_COMMA_OR_EOO:
- if (depth <= 0) {
- return MG_JSON_INVALID;
- } else if (c == ',') {
- expecting = (nesting[depth - 1] == '{') ? S_KEY : S_VALUE;
- } else if (c == ']' || c == '}') {
- MG_EOO('O');
- if (depth == ed && ei >= 0) ci++;
- } else {
- return MG_JSON_INVALID;
- }
- break;
- }
- }
- return MG_JSON_NOT_FOUND;
- }
- bool mg_json_get_num(struct mg_str json, const char *path, double *v) {
- int n, toklen, found = 0;
- if ((n = mg_json_get(json, path, &toklen)) >= 0 &&
- (json.ptr[n] == '-' || (json.ptr[n] >= '0' && json.ptr[n] <= '9'))) {
- if (v != NULL) *v = mg_atod(json.ptr + n, toklen, NULL);
- found = 1;
- }
- return found;
- }
- bool mg_json_get_bool(struct mg_str json, const char *path, bool *v) {
- int found = 0, off = mg_json_get(json, path, NULL);
- if (off >= 0 && (json.ptr[off] == 't' || json.ptr[off] == 'f')) {
- if (v != NULL) *v = json.ptr[off] == 't';
- found = 1;
- }
- return found;
- }
- static bool json_unescape(const char *s, size_t len, char *to, size_t n) {
- size_t i, j;
- for (i = 0, j = 0; i < len && j < n; i++, j++) {
- if (s[i] == '\\' && i + 5 < len && s[i + 1] == 'u') {
- // \uXXXX escape. We could process a simple one-byte chars
- // \u00xx from the ASCII range. More complex chars would require
- // dragging in a UTF8 library, which is too much for us
- if (s[i + 2] != '0' || s[i + 3] != '0') return false; // Give up
- ((unsigned char *) to)[j] = (unsigned char) mg_unhexn(s + i + 4, 2);
- i += 5;
- } else if (s[i] == '\\' && i + 1 < len) {
- char c = json_esc(s[i + 1], 0);
- if (c == 0) return false;
- to[j] = c;
- i++;
- } else {
- to[j] = s[i];
- }
- }
- if (j >= n) return false;
- if (n > 0) to[j] = '\0';
- return true;
- }
- char *mg_json_get_str(struct mg_str json, const char *path) {
- char *result = NULL;
- int len = 0, off = mg_json_get(json, path, &len);
- if (off >= 0 && len > 1 && json.ptr[off] == '"') {
- if ((result = (char *) calloc(1, (size_t) len)) != NULL &&
- !json_unescape(json.ptr + off + 1, (size_t) (len - 2), result,
- (size_t) len)) {
- free(result);
- result = NULL;
- }
- }
- return result;
- }
- char *mg_json_get_b64(struct mg_str json, const char *path, int *slen) {
- char *result = NULL;
- int len = 0, off = mg_json_get(json, path, &len);
- if (off >= 0 && json.ptr[off] == '"' && len > 1 &&
- (result = (char *) calloc(1, (size_t) len)) != NULL) {
- int k = mg_base64_decode(json.ptr + off + 1, len - 2, result);
- if (slen != NULL) *slen = k;
- }
- return result;
- }
- char *mg_json_get_hex(struct mg_str json, const char *path, int *slen) {
- char *result = NULL;
- int len = 0, off = mg_json_get(json, path, &len);
- if (off >= 0 && json.ptr[off] == '"' && len > 1 &&
- (result = (char *) calloc(1, (size_t) len / 2)) != NULL) {
- mg_unhex(json.ptr + off + 1, (size_t) (len - 2), (uint8_t *) result);
- result[len / 2 - 1] = '\0';
- if (slen != NULL) *slen = len / 2 - 1;
- }
- return result;
- }
- long mg_json_get_long(struct mg_str json, const char *path, long dflt) {
- double dv;
- long result = dflt;
- if (mg_json_get_num(json, path, &dv)) result = (long) dv;
- return result;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/log.c"
- #endif
- static int s_level = MG_LL_INFO;
- static mg_pfn_t s_log_func = mg_pfn_stdout;
- static void *s_log_func_param = NULL;
- void mg_log_set_fn(mg_pfn_t fn, void *param) {
- s_log_func = fn;
- s_log_func_param = param;
- }
- static void logc(unsigned char c) {
- s_log_func((char) c, s_log_func_param);
- }
- static void logs(const char *buf, size_t len) {
- size_t i;
- for (i = 0; i < len; i++) logc(((unsigned char *) buf)[i]);
- }
- void mg_log_set(int log_level) {
- MG_DEBUG(("Setting log level to %d", log_level));
- s_level = log_level;
- }
- bool mg_log_prefix(int level, const char *file, int line, const char *fname) {
- if (level <= s_level) {
- const char *p = strrchr(file, '/');
- char buf[41];
- size_t n;
- if (p == NULL) p = strrchr(file, '\\');
- n = mg_snprintf(buf, sizeof(buf), "%-6llx %d %s:%d:%s", mg_millis(), level,
- p == NULL ? file : p + 1, line, fname);
- if (n > sizeof(buf) - 2) n = sizeof(buf) - 2;
- while (n < sizeof(buf)) buf[n++] = ' ';
- logs(buf, n - 1);
- return true;
- } else {
- return false;
- }
- }
- void mg_log(const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- mg_vxprintf(s_log_func, s_log_func_param, fmt, &ap);
- va_end(ap);
- logc((unsigned char) '\n');
- }
- static unsigned char nibble(unsigned c) {
- return (unsigned char) (c < 10 ? c + '0' : c + 'W');
- }
- #define ISPRINT(x) ((x) >= ' ' && (x) <= '~')
- void mg_hexdump(const void *buf, size_t len) {
- const unsigned char *p = (const unsigned char *) buf;
- unsigned char ascii[16], alen = 0;
- size_t i;
- for (i = 0; i < len; i++) {
- if ((i % 16) == 0) {
- // Print buffered ascii chars
- if (i > 0) logs(" ", 2), logs((char *) ascii, 16), logc('\n'), alen = 0;
- // Print hex address, then \t
- logc(nibble((i >> 12) & 15)), logc(nibble((i >> 8) & 15)),
- logc(nibble((i >> 4) & 15)), logc('0'), logs(" ", 3);
- }
- logc(nibble(p[i] >> 4)), logc(nibble(p[i] & 15)); // Two nibbles, e.g. c5
- logc(' '); // Space after hex number
- ascii[alen++] = ISPRINT(p[i]) ? p[i] : '.'; // Add to the ascii buf
- }
- while (alen < 16) logs(" ", 3), ascii[alen++] = ' ';
- logs(" ", 2), logs((char *) ascii, 16), logc('\n');
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/md5.c"
- #endif
- #if defined(MG_ENABLE_MD5) && MG_ENABLE_MD5
- static void mg_byte_reverse(unsigned char *buf, unsigned longs) {
- if (MG_BIG_ENDIAN) {
- do {
- uint32_t t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
- ((unsigned) buf[1] << 8 | buf[0]);
- *(uint32_t *) buf = t;
- buf += 4;
- } while (--longs);
- } else {
- (void) buf, (void) longs; // Little endian. Do nothing
- }
- }
- #define F1(x, y, z) (z ^ (x & (y ^ z)))
- #define F2(x, y, z) F1(z, x, y)
- #define F3(x, y, z) (x ^ y ^ z)
- #define F4(x, y, z) (y ^ (x | ~z))
- #define MD5STEP(f, w, x, y, z, data, s) \
- (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x)
- /*
- * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
- * initialization constants.
- */
- void mg_md5_init(mg_md5_ctx *ctx) {
- ctx->buf[0] = 0x67452301;
- ctx->buf[1] = 0xefcdab89;
- ctx->buf[2] = 0x98badcfe;
- ctx->buf[3] = 0x10325476;
- ctx->bits[0] = 0;
- ctx->bits[1] = 0;
- }
- static void mg_md5_transform(uint32_t buf[4], uint32_t const in[16]) {
- uint32_t a, b, c, d;
- a = buf[0];
- b = buf[1];
- c = buf[2];
- d = buf[3];
- MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
- MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
- MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
- MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
- MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
- MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
- MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
- MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
- MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
- MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
- MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
- MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
- MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
- MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
- MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
- MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
- MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
- MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
- MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
- MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
- MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
- MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
- MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
- MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
- MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
- MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
- MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
- MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
- MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
- MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
- MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
- MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
- MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
- MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
- MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
- MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
- MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
- MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
- MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
- MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
- MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
- MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
- MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
- MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
- MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
- MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
- MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
- MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
- MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
- MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
- MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
- MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
- MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
- MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
- MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
- MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
- MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
- MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
- MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
- MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
- MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
- MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
- MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
- MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
- buf[0] += a;
- buf[1] += b;
- buf[2] += c;
- buf[3] += d;
- }
- void mg_md5_update(mg_md5_ctx *ctx, const unsigned char *buf, size_t len) {
- uint32_t t;
- t = ctx->bits[0];
- if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++;
- ctx->bits[1] += (uint32_t) len >> 29;
- t = (t >> 3) & 0x3f;
- if (t) {
- unsigned char *p = (unsigned char *) ctx->in + t;
- t = 64 - t;
- if (len < t) {
- memcpy(p, buf, len);
- return;
- }
- memcpy(p, buf, t);
- mg_byte_reverse(ctx->in, 16);
- mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
- buf += t;
- len -= t;
- }
- while (len >= 64) {
- memcpy(ctx->in, buf, 64);
- mg_byte_reverse(ctx->in, 16);
- mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
- buf += 64;
- len -= 64;
- }
- memcpy(ctx->in, buf, len);
- }
- void mg_md5_final(mg_md5_ctx *ctx, unsigned char digest[16]) {
- unsigned count;
- unsigned char *p;
- uint32_t *a;
- count = (ctx->bits[0] >> 3) & 0x3F;
- p = ctx->in + count;
- *p++ = 0x80;
- count = 64 - 1 - count;
- if (count < 8) {
- memset(p, 0, count);
- mg_byte_reverse(ctx->in, 16);
- mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
- memset(ctx->in, 0, 56);
- } else {
- memset(p, 0, count - 8);
- }
- mg_byte_reverse(ctx->in, 14);
- a = (uint32_t *) ctx->in;
- a[14] = ctx->bits[0];
- a[15] = ctx->bits[1];
- mg_md5_transform(ctx->buf, (uint32_t *) ctx->in);
- mg_byte_reverse((unsigned char *) ctx->buf, 4);
- memcpy(digest, ctx->buf, 16);
- memset((char *) ctx, 0, sizeof(*ctx));
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/mqtt.c"
- #endif
- #define MQTT_CLEAN_SESSION 0x02
- #define MQTT_HAS_WILL 0x04
- #define MQTT_WILL_RETAIN 0x20
- #define MQTT_HAS_PASSWORD 0x40
- #define MQTT_HAS_USER_NAME 0x80
- void mg_mqtt_send_header(struct mg_connection *c, uint8_t cmd, uint8_t flags,
- uint32_t len) {
- uint8_t buf[1 + sizeof(len)], *vlen = &buf[1];
- buf[0] = (uint8_t) ((cmd << 4) | flags);
- do {
- *vlen = len % 0x80;
- len /= 0x80;
- if (len > 0) *vlen |= 0x80;
- vlen++;
- } while (len > 0 && vlen < &buf[sizeof(buf)]);
- mg_send(c, buf, (size_t) (vlen - buf));
- }
- static void mg_send_u16(struct mg_connection *c, uint16_t value) {
- mg_send(c, &value, sizeof(value));
- }
- void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
- char rnd[10], client_id[21], zero = 0;
- struct mg_str cid = opts->client_id;
- uint32_t total_len = 7 + 1 + 2 + 2;
- uint8_t hdr[8] = {0, 4, 'M', 'Q', 'T', 'T', opts->version, 0};
- if (cid.len == 0) {
- mg_random(rnd, sizeof(rnd));
- mg_hex(rnd, sizeof(rnd), client_id);
- client_id[sizeof(client_id) - 1] = '\0';
- cid = mg_str(client_id);
- }
- if (hdr[6] == 0) hdr[6] = 4; // If version is not set, use 4 (3.1.1)
- c->is_mqtt5 = hdr[6] == 5; // Set version 5 flag
- hdr[7] = (uint8_t) ((opts->will_qos & 3) << 3); // Connection flags
- if (opts->user.len > 0) {
- total_len += 2 + (uint32_t) opts->user.len;
- hdr[7] |= MQTT_HAS_USER_NAME;
- }
- if (opts->pass.len > 0) {
- total_len += 2 + (uint32_t) opts->pass.len;
- hdr[7] |= MQTT_HAS_PASSWORD;
- }
- if (opts->will_topic.len > 0 && opts->will_message.len > 0) {
- total_len +=
- 4 + (uint32_t) opts->will_topic.len + (uint32_t) opts->will_message.len;
- hdr[7] |= MQTT_HAS_WILL;
- }
- if (opts->clean || cid.len == 0) hdr[7] |= MQTT_CLEAN_SESSION;
- if (opts->will_retain) hdr[7] |= MQTT_WILL_RETAIN;
- total_len += (uint32_t) cid.len;
- if (c->is_mqtt5) total_len += 1U + (hdr[7] & MQTT_HAS_WILL ? 1U : 0);
- mg_mqtt_send_header(c, MQTT_CMD_CONNECT, 0, total_len);
- mg_send(c, hdr, sizeof(hdr));
- // keepalive == 0 means "do not disconnect us!"
- mg_send_u16(c, mg_htons((uint16_t) opts->keepalive));
- if (c->is_mqtt5) mg_send(c, &zero, sizeof(zero)); // V5 properties
- mg_send_u16(c, mg_htons((uint16_t) cid.len));
- mg_send(c, cid.ptr, cid.len);
- if (hdr[7] & MQTT_HAS_WILL) {
- if (c->is_mqtt5) mg_send(c, &zero, sizeof(zero)); // will props
- mg_send_u16(c, mg_htons((uint16_t) opts->will_topic.len));
- mg_send(c, opts->will_topic.ptr, opts->will_topic.len);
- mg_send_u16(c, mg_htons((uint16_t) opts->will_message.len));
- mg_send(c, opts->will_message.ptr, opts->will_message.len);
- }
- if (opts->user.len > 0) {
- mg_send_u16(c, mg_htons((uint16_t) opts->user.len));
- mg_send(c, opts->user.ptr, opts->user.len);
- }
- if (opts->pass.len > 0) {
- mg_send_u16(c, mg_htons((uint16_t) opts->pass.len));
- mg_send(c, opts->pass.ptr, opts->pass.len);
- }
- }
- void mg_mqtt_pub(struct mg_connection *c, struct mg_str topic,
- struct mg_str data, int qos, bool retain) {
- uint8_t flags = (uint8_t) (((qos & 3) << 1) | (retain ? 1 : 0)), zero = 0;
- uint32_t len = 2 + (uint32_t) topic.len + (uint32_t) data.len;
- MG_DEBUG(("%lu [%.*s] -> [%.*s]", c->id, (int) topic.len, (char *) topic.ptr,
- (int) data.len, (char *) data.ptr));
- if (qos > 0) len += 2;
- if (c->is_mqtt5) len++;
- mg_mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, len);
- mg_send_u16(c, mg_htons((uint16_t) topic.len));
- mg_send(c, topic.ptr, topic.len);
- if (qos > 0) {
- if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id;
- mg_send_u16(c, mg_htons(c->mgr->mqtt_id));
- }
- if (c->is_mqtt5) mg_send(c, &zero, sizeof(zero));
- mg_send(c, data.ptr, data.len);
- }
- void mg_mqtt_sub(struct mg_connection *c, struct mg_str topic, int qos) {
- uint8_t qos_ = qos & 3, zero = 0;
- uint32_t len = 2 + (uint32_t) topic.len + 2 + 1 + (c->is_mqtt5 ? 1 : 0);
- mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, len);
- if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id;
- mg_send_u16(c, mg_htons(c->mgr->mqtt_id));
- if (c->is_mqtt5) mg_send(c, &zero, sizeof(zero));
- mg_send_u16(c, mg_htons((uint16_t) topic.len));
- mg_send(c, topic.ptr, topic.len);
- mg_send(c, &qos_, sizeof(qos_));
- }
- int mg_mqtt_parse(const uint8_t *buf, size_t len, uint8_t version,
- struct mg_mqtt_message *m) {
- uint8_t lc = 0, *p, *end;
- uint32_t n = 0, len_len = 0;
- memset(m, 0, sizeof(*m));
- m->dgram.ptr = (char *) buf;
- if (len < 2) return MQTT_INCOMPLETE;
- m->cmd = (uint8_t) (buf[0] >> 4);
- m->qos = (buf[0] >> 1) & 3;
- n = len_len = 0;
- p = (uint8_t *) buf + 1;
- while ((size_t) (p - buf) < len) {
- lc = *((uint8_t *) p++);
- n += (uint32_t) ((lc & 0x7f) << 7 * len_len);
- len_len++;
- if (!(lc & 0x80)) break;
- if (len_len >= 4) return MQTT_MALFORMED;
- }
- end = p + n;
- if ((lc & 0x80) || (end > buf + len)) return MQTT_INCOMPLETE;
- m->dgram.len = (size_t) (end - buf);
- switch (m->cmd) {
- case MQTT_CMD_CONNACK:
- if (end - p < 2) return MQTT_MALFORMED;
- m->ack = p[1];
- break;
- case MQTT_CMD_PUBACK:
- case MQTT_CMD_PUBREC:
- case MQTT_CMD_PUBREL:
- case MQTT_CMD_PUBCOMP:
- case MQTT_CMD_SUBSCRIBE:
- case MQTT_CMD_SUBACK:
- case MQTT_CMD_UNSUBSCRIBE:
- case MQTT_CMD_UNSUBACK:
- if (p + 2 > end) return MQTT_MALFORMED;
- m->id = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]);
- p += 2;
- break;
- case MQTT_CMD_PUBLISH: {
- if (p + 2 > end) return MQTT_MALFORMED;
- m->topic.len = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]);
- m->topic.ptr = (char *) p + 2;
- p += 2 + m->topic.len;
- if (p > end) return MQTT_MALFORMED;
- if (m->qos > 0) {
- if (p + 2 > end) return MQTT_MALFORMED;
- m->id = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]);
- p += 2;
- }
- if (p > end) return MQTT_MALFORMED;
- if (version == 5 && p + 2 < end) p += 1 + p[0]; // Skip options
- if (p > end) return MQTT_MALFORMED;
- m->data.ptr = (char *) p;
- m->data.len = (size_t) (end - p);
- break;
- }
- default:
- break;
- }
- return MQTT_OK;
- }
- static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data,
- void *fn_data) {
- if (ev == MG_EV_READ) {
- for (;;) {
- uint8_t version = c->is_mqtt5 ? 5 : 4;
- struct mg_mqtt_message mm;
- int rc = mg_mqtt_parse(c->recv.buf, c->recv.len, version, &mm);
- if (rc == MQTT_MALFORMED) {
- MG_ERROR(("%lu MQTT malformed message", c->id));
- c->is_closing = 1;
- break;
- } else if (rc == MQTT_OK) {
- MG_VERBOSE(("%lu MQTT CMD %d len %d [%.*s]", c->id, mm.cmd,
- (int) mm.dgram.len, (int) mm.data.len, mm.data.ptr));
- switch (mm.cmd) {
- case MQTT_CMD_CONNACK:
- mg_call(c, MG_EV_MQTT_OPEN, &mm.ack);
- if (mm.ack == 0) {
- MG_DEBUG(("%lu Connected", c->id));
- } else {
- MG_ERROR(("%lu MQTT auth failed, code %d", c->id, mm.ack));
- c->is_closing = 1;
- }
- break;
- case MQTT_CMD_PUBLISH: {
- MG_DEBUG(("%lu [%.*s] -> [%.*s]", c->id, (int) mm.topic.len,
- mm.topic.ptr, (int) mm.data.len, mm.data.ptr));
- if (mm.qos > 0) {
- uint16_t id = mg_htons(mm.id);
- mg_mqtt_send_header(c, MQTT_CMD_PUBACK, 0, sizeof(id));
- mg_send(c, &id, sizeof(id));
- }
- mg_call(c, MG_EV_MQTT_MSG, &mm);
- break;
- }
- }
- mg_call(c, MG_EV_MQTT_CMD, &mm);
- mg_iobuf_del(&c->recv, 0, mm.dgram.len);
- } else {
- break;
- }
- }
- }
- (void) ev_data;
- (void) fn_data;
- }
- void mg_mqtt_ping(struct mg_connection *nc) {
- mg_mqtt_send_header(nc, MQTT_CMD_PINGREQ, 0, 0);
- }
- void mg_mqtt_pong(struct mg_connection *nc) {
- mg_mqtt_send_header(nc, MQTT_CMD_PINGRESP, 0, 0);
- }
- void mg_mqtt_disconnect(struct mg_connection *nc) {
- mg_mqtt_send_header(nc, MQTT_CMD_DISCONNECT, 0, 0);
- }
- struct mg_connection *mg_mqtt_connect(struct mg_mgr *mgr, const char *url,
- const struct mg_mqtt_opts *opts,
- mg_event_handler_t fn, void *fn_data) {
- struct mg_connection *c = mg_connect(mgr, url, fn, fn_data);
- if (c != NULL) {
- struct mg_mqtt_opts empty;
- memset(&empty, 0, sizeof(empty));
- mg_mqtt_login(c, opts == NULL ? &empty : opts);
- c->pfn = mqtt_cb;
- }
- return c;
- }
- struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data) {
- struct mg_connection *c = mg_listen(mgr, url, fn, fn_data);
- if (c != NULL) c->pfn = mqtt_cb, c->pfn_data = mgr;
- return c;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/net.c"
- #endif
- size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list *ap) {
- size_t old = c->send.len;
- mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
- return c->send.len - old;
- }
- size_t mg_printf(struct mg_connection *c, const char *fmt, ...) {
- size_t len = 0;
- va_list ap;
- va_start(ap, fmt);
- len = mg_vprintf(c, fmt, &ap);
- va_end(ap);
- return len;
- }
- static bool mg_atonl(struct mg_str str, struct mg_addr *addr) {
- if (mg_vcasecmp(&str, "localhost") != 0) return false;
- addr->ip = mg_htonl(0x7f000001);
- addr->is_ip6 = false;
- return true;
- }
- static bool mg_atone(struct mg_str str, struct mg_addr *addr) {
- if (str.len > 0) return false;
- addr->ip = 0;
- addr->is_ip6 = false;
- return true;
- }
- static bool mg_aton4(struct mg_str str, struct mg_addr *addr) {
- uint8_t data[4] = {0, 0, 0, 0};
- size_t i, num_dots = 0;
- for (i = 0; i < str.len; i++) {
- if (str.ptr[i] >= '0' && str.ptr[i] <= '9') {
- int octet = data[num_dots] * 10 + (str.ptr[i] - '0');
- if (octet > 255) return false;
- data[num_dots] = (uint8_t) octet;
- } else if (str.ptr[i] == '.') {
- if (num_dots >= 3 || i == 0 || str.ptr[i - 1] == '.') return false;
- num_dots++;
- } else {
- return false;
- }
- }
- if (num_dots != 3 || str.ptr[i - 1] == '.') return false;
- memcpy(&addr->ip, data, sizeof(data));
- addr->is_ip6 = false;
- return true;
- }
- static bool mg_v4mapped(struct mg_str str, struct mg_addr *addr) {
- int i;
- if (str.len < 14) return false;
- if (str.ptr[0] != ':' || str.ptr[1] != ':' || str.ptr[6] != ':') return false;
- for (i = 2; i < 6; i++) {
- if (str.ptr[i] != 'f' && str.ptr[i] != 'F') return false;
- }
- if (!mg_aton4(mg_str_n(&str.ptr[7], str.len - 7), addr)) return false;
- memset(addr->ip6, 0, sizeof(addr->ip6));
- addr->ip6[10] = addr->ip6[11] = 255;
- memcpy(&addr->ip6[12], &addr->ip, 4);
- addr->is_ip6 = true;
- return true;
- }
- static bool mg_aton6(struct mg_str str, struct mg_addr *addr) {
- size_t i, j = 0, n = 0, dc = 42;
- if (str.len > 2 && str.ptr[0] == '[') str.ptr++, str.len -= 2;
- if (mg_v4mapped(str, addr)) return true;
- for (i = 0; i < str.len; i++) {
- if ((str.ptr[i] >= '0' && str.ptr[i] <= '9') ||
- (str.ptr[i] >= 'a' && str.ptr[i] <= 'f') ||
- (str.ptr[i] >= 'A' && str.ptr[i] <= 'F')) {
- unsigned long val;
- if (i > j + 3) return false;
- // MG_DEBUG(("%zu %zu [%.*s]", i, j, (int) (i - j + 1), &str.ptr[j]));
- val = mg_unhexn(&str.ptr[j], i - j + 1);
- addr->ip6[n] = (uint8_t) ((val >> 8) & 255);
- addr->ip6[n + 1] = (uint8_t) (val & 255);
- } else if (str.ptr[i] == ':') {
- j = i + 1;
- if (i > 0 && str.ptr[i - 1] == ':') {
- dc = n; // Double colon
- if (i > 1 && str.ptr[i - 2] == ':') return false;
- } else if (i > 0) {
- n += 2;
- }
- if (n > 14) return false;
- addr->ip6[n] = addr->ip6[n + 1] = 0; // For trailing ::
- } else {
- return false;
- }
- }
- if (n < 14 && dc == 42) return false;
- if (n < 14) {
- memmove(&addr->ip6[dc + (14 - n)], &addr->ip6[dc], n - dc + 2);
- memset(&addr->ip6[dc], 0, 14 - n);
- }
- addr->is_ip6 = true;
- return true;
- }
- bool mg_aton(struct mg_str str, struct mg_addr *addr) {
- // MG_INFO(("[%.*s]", (int) str.len, str.ptr));
- return mg_atone(str, addr) || mg_atonl(str, addr) || mg_aton4(str, addr) ||
- mg_aton6(str, addr);
- }
- struct mg_connection *mg_alloc_conn(struct mg_mgr *mgr) {
- struct mg_connection *c =
- (struct mg_connection *) calloc(1, sizeof(*c) + mgr->extraconnsize);
- if (c != NULL) {
- c->mgr = mgr;
- c->send.align = c->recv.align = MG_IO_SIZE;
- c->id = ++mgr->nextid;
- }
- return c;
- }
- void mg_close_conn(struct mg_connection *c) {
- mg_resolve_cancel(c); // Close any pending DNS query
- LIST_DELETE(struct mg_connection, &c->mgr->conns, c);
- if (c == c->mgr->dns4.c) c->mgr->dns4.c = NULL;
- if (c == c->mgr->dns6.c) c->mgr->dns6.c = NULL;
- // Order of operations is important. `MG_EV_CLOSE` event must be fired
- // before we deallocate received data, see #1331
- mg_call(c, MG_EV_CLOSE, NULL);
- MG_DEBUG(("%lu %p closed", c->id, c->fd));
- mg_tls_free(c);
- mg_iobuf_free(&c->recv);
- mg_iobuf_free(&c->send);
- memset(c, 0, sizeof(*c));
- free(c);
- }
- struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data) {
- struct mg_connection *c = NULL;
- if (url == NULL || url[0] == '\0') {
- MG_ERROR(("null url"));
- } else if ((c = mg_alloc_conn(mgr)) == NULL) {
- MG_ERROR(("OOM"));
- } else {
- LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
- c->is_udp = (strncmp(url, "udp:", 4) == 0);
- c->fd = (void *) (size_t) MG_INVALID_SOCKET;
- c->fn = fn;
- c->is_client = true;
- c->fn_data = fn_data;
- MG_DEBUG(("%lu %p %s", c->id, c->fd, url));
- mg_call(c, MG_EV_OPEN, NULL);
- mg_resolve(c, url);
- }
- return c;
- }
- struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data) {
- struct mg_connection *c = NULL;
- if ((c = mg_alloc_conn(mgr)) == NULL) {
- MG_ERROR(("OOM %s", url));
- } else if (!mg_open_listener(c, url)) {
- MG_ERROR(("Failed: %s, errno %d", url, errno));
- free(c);
- c = NULL;
- } else {
- c->is_listening = 1;
- c->is_udp = strncmp(url, "udp:", 4) == 0;
- LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
- c->fn = fn;
- c->fn_data = fn_data;
- mg_call(c, MG_EV_OPEN, NULL);
- MG_DEBUG(("%lu %p %s", c->id, c->fd, url));
- }
- return c;
- }
- struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd,
- mg_event_handler_t fn, void *fn_data) {
- struct mg_connection *c = mg_alloc_conn(mgr);
- if (c != NULL) {
- c->fd = (void *) (size_t) fd;
- c->fn = fn;
- c->fn_data = fn_data;
- MG_EPOLL_ADD(c);
- mg_call(c, MG_EV_OPEN, NULL);
- LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
- }
- return c;
- }
- struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
- unsigned flags, void (*fn)(void *), void *arg) {
- struct mg_timer *t = (struct mg_timer *) calloc(1, sizeof(*t));
- if (t != NULL) {
- mg_timer_init(&mgr->timers, t, milliseconds, flags, fn, arg);
- t->id = mgr->timerid++;
- }
- return t;
- }
- void mg_mgr_free(struct mg_mgr *mgr) {
- struct mg_connection *c;
- struct mg_timer *tmp, *t = mgr->timers;
- while (t != NULL) tmp = t->next, free(t), t = tmp;
- mgr->timers = NULL; // Important. Next call to poll won't touch timers
- for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1;
- mg_mgr_poll(mgr, 0);
- #if MG_ENABLE_FREERTOS_TCP
- FreeRTOS_DeleteSocketSet(mgr->ss);
- #endif
- MG_DEBUG(("All connections closed"));
- #if MG_ENABLE_EPOLL
- if (mgr->epoll_fd >= 0) close(mgr->epoll_fd), mgr->epoll_fd = -1;
- #endif
- }
- void mg_mgr_init(struct mg_mgr *mgr) {
- memset(mgr, 0, sizeof(*mgr));
- #if MG_ENABLE_EPOLL
- if ((mgr->epoll_fd = epoll_create1(0)) < 0) MG_ERROR(("epoll: %d", errno));
- #else
- mgr->epoll_fd = -1;
- #endif
- #if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
- // clang-format off
- { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); }
- // clang-format on
- #elif MG_ENABLE_FREERTOS_TCP
- mgr->ss = FreeRTOS_CreateSocketSet();
- #elif defined(__unix) || defined(__unix__) || defined(__APPLE__)
- // Ignore SIGPIPE signal, so if client cancels the request, it
- // won't kill the whole process.
- signal(SIGPIPE, SIG_IGN);
- #endif
- mgr->dnstimeout = 3000;
- mgr->dns4.url = "udp://8.8.8.8:53";
- mgr->dns6.url = "udp://[2001:4860:4860::8888]:53";
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/printf.c"
- #endif
- size_t mg_queue_vprintf(struct mg_queue *q, const char *fmt, va_list *ap) {
- size_t len = mg_snprintf(NULL, 0, fmt, ap);
- char *buf;
- if (len == 0 || mg_queue_book(q, &buf, len + 1) < len + 1) {
- len = 0; // Nah. Not enough space
- } else {
- len = mg_vsnprintf((char *) buf, len + 1, fmt, ap);
- mg_queue_add(q, len);
- }
- return len;
- }
- size_t mg_queue_printf(struct mg_queue *q, const char *fmt, ...) {
- va_list ap;
- size_t len;
- va_start(ap, fmt);
- len = mg_queue_vprintf(q, fmt, &ap);
- va_end(ap);
- return len;
- }
- static void mg_pfn_iobuf_private(char ch, void *param, bool expand) {
- struct mg_iobuf *io = (struct mg_iobuf *) param;
- if (expand && io->len + 2 > io->size) mg_iobuf_resize(io, io->len + 2);
- if (io->len + 2 <= io->size) {
- io->buf[io->len++] = (uint8_t) ch;
- io->buf[io->len] = 0;
- } else if (io->len < io->size) {
- io->buf[io->len++] = 0; // Guarantee to 0-terminate
- }
- }
- static void mg_putchar_iobuf_static(char ch, void *param) {
- mg_pfn_iobuf_private(ch, param, false);
- }
- void mg_pfn_iobuf(char ch, void *param) {
- mg_pfn_iobuf_private(ch, param, true);
- }
- size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap) {
- struct mg_iobuf io = {(uint8_t *) buf, len, 0, 0};
- size_t n = mg_vxprintf(mg_putchar_iobuf_static, &io, fmt, ap);
- if (n < len) buf[n] = '\0';
- return n;
- }
- size_t mg_snprintf(char *buf, size_t len, const char *fmt, ...) {
- va_list ap;
- size_t n;
- va_start(ap, fmt);
- n = mg_vsnprintf(buf, len, fmt, &ap);
- va_end(ap);
- return n;
- }
- char *mg_vmprintf(const char *fmt, va_list *ap) {
- struct mg_iobuf io = {0, 0, 0, 256};
- mg_vxprintf(mg_pfn_iobuf, &io, fmt, ap);
- return (char *) io.buf;
- }
- char *mg_mprintf(const char *fmt, ...) {
- char *s;
- va_list ap;
- va_start(ap, fmt);
- s = mg_vmprintf(fmt, &ap);
- va_end(ap);
- return s;
- }
- void mg_pfn_stdout(char c, void *param) {
- putchar(c);
- (void) param;
- }
- static size_t print_ip4(void (*out)(char, void *), void *arg, uint8_t *p) {
- return mg_xprintf(out, arg, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
- }
- static size_t print_ip6(void (*out)(char, void *), void *arg, uint16_t *p) {
- return mg_xprintf(out, arg, "[%x:%x:%x:%x:%x:%x:%x:%x]", mg_ntohs(p[0]),
- mg_ntohs(p[1]), mg_ntohs(p[2]), mg_ntohs(p[3]),
- mg_ntohs(p[4]), mg_ntohs(p[5]), mg_ntohs(p[6]),
- mg_ntohs(p[7]));
- }
- size_t mg_print_ip4(void (*out)(char, void *), void *arg, va_list *ap) {
- uint8_t *p = va_arg(*ap, uint8_t *);
- return print_ip4(out, arg, p);
- }
- size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap) {
- uint16_t *p = va_arg(*ap, uint16_t *);
- return print_ip6(out, arg, p);
- }
- size_t mg_print_ip(void (*out)(char, void *), void *arg, va_list *ap) {
- struct mg_addr *addr = va_arg(*ap, struct mg_addr *);
- if (addr->is_ip6) return print_ip6(out, arg, (uint16_t *) addr->ip6);
- return print_ip4(out, arg, (uint8_t *) &addr->ip);
- }
- size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap) {
- struct mg_addr *a = va_arg(*ap, struct mg_addr *);
- return mg_xprintf(out, arg, "%M:%hu", mg_print_ip, a, mg_ntohs(a->port));
- }
- size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap) {
- uint8_t *p = va_arg(*ap, uint8_t *);
- return mg_xprintf(out, arg, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2],
- p[3], p[4], p[5]);
- }
- static char mg_esc(int c, bool esc) {
- const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\"";
- for (p = esc ? esc1 : esc2; *p != '\0'; p++) {
- if (*p == c) return esc ? esc2[p - esc1] : esc1[p - esc2];
- }
- return 0;
- }
- static char mg_escape(int c) {
- return mg_esc(c, true);
- }
- static size_t qcpy(void (*out)(char, void *), void *ptr, char *buf,
- size_t len) {
- size_t i = 0, extra = 0;
- for (i = 0; i < len && buf[i] != '\0'; i++) {
- char c = mg_escape(buf[i]);
- if (c) {
- out('\\', ptr), out(c, ptr), extra++;
- } else {
- out(buf[i], ptr);
- }
- }
- return i + extra;
- }
- static size_t bcpy(void (*out)(char, void *), void *arg, uint8_t *buf,
- size_t len) {
- size_t i, j, n = 0;
- const char *t =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- for (i = 0; i < len; i += 3) {
- uint8_t c1 = buf[i], c2 = i + 1 < len ? buf[i + 1] : 0,
- c3 = i + 2 < len ? buf[i + 2] : 0;
- char tmp[4] = {t[c1 >> 2], t[(c1 & 3) << 4 | (c2 >> 4)], '=', '='};
- if (i + 1 < len) tmp[2] = t[(c2 & 15) << 2 | (c3 >> 6)];
- if (i + 2 < len) tmp[3] = t[c3 & 63];
- for (j = 0; j < sizeof(tmp) && tmp[j] != '\0'; j++) out(tmp[j], arg);
- n += j;
- }
- return n;
- }
- size_t mg_print_hex(void (*out)(char, void *), void *arg, va_list *ap) {
- size_t bl = (size_t) va_arg(*ap, int);
- uint8_t *p = va_arg(*ap, uint8_t *);
- const char *hex = "0123456789abcdef";
- size_t j;
- for (j = 0; j < bl; j++) {
- out(hex[(p[j] >> 4) & 0x0F], arg);
- out(hex[p[j] & 0x0F], arg);
- }
- return 2 * bl;
- }
- size_t mg_print_base64(void (*out)(char, void *), void *arg, va_list *ap) {
- size_t len = (size_t) va_arg(*ap, int);
- uint8_t *buf = va_arg(*ap, uint8_t *);
- return bcpy(out, arg, buf, len);
- }
- size_t mg_print_esc(void (*out)(char, void *), void *arg, va_list *ap) {
- size_t len = (size_t) va_arg(*ap, int);
- char *p = va_arg(*ap, char *);
- if (len == 0) len = p == NULL ? 0 : strlen(p);
- return qcpy(out, arg, p, len);
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/queue.c"
- #endif
- #if defined(__GNUC__) || defined(__clang__)
- #define MG_MEMORY_BARRIER() __sync_synchronize()
- #elif defined(_MSC_VER) && _MSC_VER >= 1700
- #define MG_MEMORY_BARRIER() MemoryBarrier()
- #elif !defined(MG_MEMORY_BARRIER)
- #define MG_MEMORY_BARRIER()
- #endif
- // Every message in a queue is prepended by a 32-bit message length (ML).
- // If ML is 0, then it is the end, and reader must wrap to the beginning.
- //
- // Queue when q->tail <= q->head:
- // |----- free -----| ML | message1 | ML | message2 | ----- free ------|
- // ^ ^ ^ ^
- // buf tail head len
- //
- // Queue when q->tail > q->head:
- // | ML | message2 |----- free ------| ML | message1 | 0 |---- free ----|
- // ^ ^ ^ ^
- // buf head tail len
- void mg_queue_init(struct mg_queue *q, char *buf, size_t size) {
- q->size = size;
- q->buf = buf;
- q->head = q->tail = 0;
- }
- static size_t mg_queue_read_len(struct mg_queue *q) {
- uint32_t n = 0;
- MG_MEMORY_BARRIER();
- memcpy(&n, q->buf + q->tail, sizeof(n));
- assert(q->tail + n + sizeof(n) <= q->size);
- return n;
- }
- static void mg_queue_write_len(struct mg_queue *q, size_t len) {
- uint32_t n = (uint32_t) len;
- memcpy(q->buf + q->head, &n, sizeof(n));
- MG_MEMORY_BARRIER();
- }
- size_t mg_queue_book(struct mg_queue *q, char **buf, size_t len) {
- size_t space = 0, hs = sizeof(uint32_t) * 2; // *2 is for the 0 marker
- if (q->head >= q->tail && q->head + len + hs <= q->size) {
- space = q->size - q->head - hs; // There is enough space
- } else if (q->head >= q->tail && q->tail > hs) {
- mg_queue_write_len(q, 0); // Not enough space ahead
- q->head = 0; // Wrap head to the beginning
- }
- if (q->head + hs + len < q->tail) space = q->tail - q->head - hs;
- if (buf != NULL) *buf = q->buf + q->head + sizeof(uint32_t);
- return space;
- }
- size_t mg_queue_next(struct mg_queue *q, char **buf) {
- size_t len = 0;
- if (q->tail != q->head) {
- len = mg_queue_read_len(q);
- if (len == 0) { // Zero (head wrapped) ?
- q->tail = 0; // Reset tail to the start
- if (q->head > q->tail) len = mg_queue_read_len(q); // Read again
- }
- }
- if (buf != NULL) *buf = q->buf + q->tail + sizeof(uint32_t);
- assert(q->tail + len <= q->size);
- return len;
- }
- void mg_queue_add(struct mg_queue *q, size_t len) {
- assert(len > 0);
- mg_queue_write_len(q, len);
- assert(q->head + sizeof(uint32_t) * 2 + len <= q->size);
- q->head += len + sizeof(uint32_t);
- }
- void mg_queue_del(struct mg_queue *q, size_t len) {
- q->tail += len + sizeof(uint32_t);
- assert(q->tail + sizeof(uint32_t) <= q->size);
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/rpc.c"
- #endif
- void mg_rpc_add(struct mg_rpc **head, struct mg_str method,
- void (*fn)(struct mg_rpc_req *), void *fn_data) {
- struct mg_rpc *rpc = (struct mg_rpc *) calloc(1, sizeof(*rpc));
- if (rpc != NULL) {
- rpc->method = mg_strdup(method), rpc->fn = fn, rpc->fn_data = fn_data;
- rpc->next = *head, *head = rpc;
- }
- }
- void mg_rpc_del(struct mg_rpc **head, void (*fn)(struct mg_rpc_req *)) {
- struct mg_rpc *r;
- while ((r = *head) != NULL) {
- if (r->fn == fn || fn == NULL) {
- *head = r->next;
- free((void *) r->method.ptr);
- free(r);
- } else {
- head = &(*head)->next;
- }
- }
- }
- static void mg_rpc_call(struct mg_rpc_req *r, struct mg_str method) {
- struct mg_rpc *h = r->head == NULL ? NULL : *r->head;
- while (h != NULL && !mg_match(method, h->method, NULL)) h = h->next;
- if (h != NULL) {
- r->rpc = h;
- h->fn(r);
- } else {
- mg_rpc_err(r, -32601, "\"%.*s not found\"", (int) method.len, method.ptr);
- }
- }
- void mg_rpc_process(struct mg_rpc_req *r) {
- int len, off = mg_json_get(r->frame, "$.method", &len);
- if (off > 0 && r->frame.ptr[off] == '"') {
- struct mg_str method = mg_str_n(&r->frame.ptr[off + 1], (size_t) len - 2);
- mg_rpc_call(r, method);
- } else if ((off = mg_json_get(r->frame, "$.result", &len)) > 0 ||
- (off = mg_json_get(r->frame, "$.error", &len)) > 0) {
- mg_rpc_call(r, mg_str("")); // JSON response! call "" method handler
- } else {
- mg_rpc_err(r, -32700, "%m", mg_print_esc, (int) r->frame.len,
- r->frame.ptr); // Invalid
- }
- }
- void mg_rpc_vok(struct mg_rpc_req *r, const char *fmt, va_list *ap) {
- int len, off = mg_json_get(r->frame, "$.id", &len);
- if (off > 0) {
- mg_xprintf(r->pfn, r->pfn_data, "{%m:%.*s,%m:", mg_print_esc, 0, "id", len,
- &r->frame.ptr[off], mg_print_esc, 0, "result");
- mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap);
- mg_xprintf(r->pfn, r->pfn_data, "}");
- }
- }
- void mg_rpc_ok(struct mg_rpc_req *r, const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- mg_rpc_vok(r, fmt, &ap);
- va_end(ap);
- }
- void mg_rpc_verr(struct mg_rpc_req *r, int code, const char *fmt, va_list *ap) {
- int len, off = mg_json_get(r->frame, "$.id", &len);
- mg_xprintf(r->pfn, r->pfn_data, "{");
- if (off > 0) {
- mg_xprintf(r->pfn, r->pfn_data, "%m:%.*s,", mg_print_esc, 0, "id", len,
- &r->frame.ptr[off]);
- }
- mg_xprintf(r->pfn, r->pfn_data, "%m:{%m:%d,%m:", mg_print_esc, 0, "error",
- mg_print_esc, 0, "code", code, mg_print_esc, 0, "message");
- mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap);
- mg_xprintf(r->pfn, r->pfn_data, "}}");
- }
- void mg_rpc_err(struct mg_rpc_req *r, int code, const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- mg_rpc_verr(r, code, fmt, &ap);
- va_end(ap);
- }
- static size_t print_methods(mg_pfn_t pfn, void *pfn_data, va_list *ap) {
- struct mg_rpc *h, **head = (struct mg_rpc **) va_arg(*ap, void **);
- size_t len = 0;
- for (h = *head; h != NULL; h = h->next) {
- if (h->method.len == 0) continue; // Ignore response handler
- len += mg_xprintf(pfn, pfn_data, "%s%m", h == *head ? "" : ",",
- mg_print_esc, (int) h->method.len, h->method.ptr);
- }
- return len;
- }
- void mg_rpc_list(struct mg_rpc_req *r) {
- mg_rpc_ok(r, "[%M]", print_methods, r->head);
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/sha1.c"
- #endif
- /* Copyright(c) By Steve Reid <steve@edmweb.com> */
- /* 100% Public Domain */
- union char64long16 {
- unsigned char c[64];
- uint32_t l[16];
- };
- #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
- static uint32_t blk0(union char64long16 *block, int i) {
- if (MG_BIG_ENDIAN) {
- } else {
- block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
- (rol(block->l[i], 8) & 0x00FF00FF);
- }
- return block->l[i];
- }
- /* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */
- #undef blk
- #undef R0
- #undef R1
- #undef R2
- #undef R3
- #undef R4
- #define blk(i) \
- (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ \
- block->l[(i + 2) & 15] ^ block->l[i & 15], \
- 1))
- #define R0(v, w, x, y, z, i) \
- z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \
- w = rol(w, 30);
- #define R1(v, w, x, y, z, i) \
- z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
- w = rol(w, 30);
- #define R2(v, w, x, y, z, i) \
- z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
- w = rol(w, 30);
- #define R3(v, w, x, y, z, i) \
- z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
- w = rol(w, 30);
- #define R4(v, w, x, y, z, i) \
- z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
- w = rol(w, 30);
- static void mg_sha1_transform(uint32_t state[5],
- const unsigned char *buffer) {
- uint32_t a, b, c, d, e;
- union char64long16 block[1];
- memcpy(block, buffer, 64);
- a = state[0];
- b = state[1];
- c = state[2];
- d = state[3];
- e = state[4];
- R0(a, b, c, d, e, 0);
- R0(e, a, b, c, d, 1);
- R0(d, e, a, b, c, 2);
- R0(c, d, e, a, b, 3);
- R0(b, c, d, e, a, 4);
- R0(a, b, c, d, e, 5);
- R0(e, a, b, c, d, 6);
- R0(d, e, a, b, c, 7);
- R0(c, d, e, a, b, 8);
- R0(b, c, d, e, a, 9);
- R0(a, b, c, d, e, 10);
- R0(e, a, b, c, d, 11);
- R0(d, e, a, b, c, 12);
- R0(c, d, e, a, b, 13);
- R0(b, c, d, e, a, 14);
- R0(a, b, c, d, e, 15);
- R1(e, a, b, c, d, 16);
- R1(d, e, a, b, c, 17);
- R1(c, d, e, a, b, 18);
- R1(b, c, d, e, a, 19);
- R2(a, b, c, d, e, 20);
- R2(e, a, b, c, d, 21);
- R2(d, e, a, b, c, 22);
- R2(c, d, e, a, b, 23);
- R2(b, c, d, e, a, 24);
- R2(a, b, c, d, e, 25);
- R2(e, a, b, c, d, 26);
- R2(d, e, a, b, c, 27);
- R2(c, d, e, a, b, 28);
- R2(b, c, d, e, a, 29);
- R2(a, b, c, d, e, 30);
- R2(e, a, b, c, d, 31);
- R2(d, e, a, b, c, 32);
- R2(c, d, e, a, b, 33);
- R2(b, c, d, e, a, 34);
- R2(a, b, c, d, e, 35);
- R2(e, a, b, c, d, 36);
- R2(d, e, a, b, c, 37);
- R2(c, d, e, a, b, 38);
- R2(b, c, d, e, a, 39);
- R3(a, b, c, d, e, 40);
- R3(e, a, b, c, d, 41);
- R3(d, e, a, b, c, 42);
- R3(c, d, e, a, b, 43);
- R3(b, c, d, e, a, 44);
- R3(a, b, c, d, e, 45);
- R3(e, a, b, c, d, 46);
- R3(d, e, a, b, c, 47);
- R3(c, d, e, a, b, 48);
- R3(b, c, d, e, a, 49);
- R3(a, b, c, d, e, 50);
- R3(e, a, b, c, d, 51);
- R3(d, e, a, b, c, 52);
- R3(c, d, e, a, b, 53);
- R3(b, c, d, e, a, 54);
- R3(a, b, c, d, e, 55);
- R3(e, a, b, c, d, 56);
- R3(d, e, a, b, c, 57);
- R3(c, d, e, a, b, 58);
- R3(b, c, d, e, a, 59);
- R4(a, b, c, d, e, 60);
- R4(e, a, b, c, d, 61);
- R4(d, e, a, b, c, 62);
- R4(c, d, e, a, b, 63);
- R4(b, c, d, e, a, 64);
- R4(a, b, c, d, e, 65);
- R4(e, a, b, c, d, 66);
- R4(d, e, a, b, c, 67);
- R4(c, d, e, a, b, 68);
- R4(b, c, d, e, a, 69);
- R4(a, b, c, d, e, 70);
- R4(e, a, b, c, d, 71);
- R4(d, e, a, b, c, 72);
- R4(c, d, e, a, b, 73);
- R4(b, c, d, e, a, 74);
- R4(a, b, c, d, e, 75);
- R4(e, a, b, c, d, 76);
- R4(d, e, a, b, c, 77);
- R4(c, d, e, a, b, 78);
- R4(b, c, d, e, a, 79);
- state[0] += a;
- state[1] += b;
- state[2] += c;
- state[3] += d;
- state[4] += e;
- /* Erase working structures. The order of operations is important,
- * used to ensure that compiler doesn't optimize those out. */
- memset(block, 0, sizeof(block));
- a = b = c = d = e = 0;
- (void) a;
- (void) b;
- (void) c;
- (void) d;
- (void) e;
- }
- void mg_sha1_init(mg_sha1_ctx *context) {
- context->state[0] = 0x67452301;
- context->state[1] = 0xEFCDAB89;
- context->state[2] = 0x98BADCFE;
- context->state[3] = 0x10325476;
- context->state[4] = 0xC3D2E1F0;
- context->count[0] = context->count[1] = 0;
- }
- void mg_sha1_update(mg_sha1_ctx *context, const unsigned char *data,
- size_t len) {
- size_t i, j;
- j = context->count[0];
- if ((context->count[0] += (uint32_t) len << 3) < j) context->count[1]++;
- context->count[1] += (uint32_t) (len >> 29);
- j = (j >> 3) & 63;
- if ((j + len) > 63) {
- memcpy(&context->buffer[j], data, (i = 64 - j));
- mg_sha1_transform(context->state, context->buffer);
- for (; i + 63 < len; i += 64) {
- mg_sha1_transform(context->state, &data[i]);
- }
- j = 0;
- } else
- i = 0;
- memcpy(&context->buffer[j], &data[i], len - i);
- }
- void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *context) {
- unsigned i;
- unsigned char finalcount[8], c;
- for (i = 0; i < 8; i++) {
- finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >>
- ((3 - (i & 3)) * 8)) &
- 255);
- }
- c = 0200;
- mg_sha1_update(context, &c, 1);
- while ((context->count[0] & 504) != 448) {
- c = 0000;
- mg_sha1_update(context, &c, 1);
- }
- mg_sha1_update(context, finalcount, 8);
- for (i = 0; i < 20; i++) {
- digest[i] =
- (unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
- }
- memset(context, '\0', sizeof(*context));
- memset(&finalcount, '\0', sizeof(finalcount));
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/sntp.c"
- #endif
- #define SNTP_TIME_OFFSET 2208988800U // (1970 - 1900) in seconds
- #define SNTP_MAX_FRAC 4294967295.0 // 2 ** 32 - 1
- static int64_t gettimestamp(const uint32_t *data) {
- uint32_t sec = mg_ntohl(data[0]), frac = mg_ntohl(data[1]);
- if (sec) sec -= SNTP_TIME_OFFSET;
- return ((int64_t) sec) * 1000 + (int64_t) (frac / SNTP_MAX_FRAC * 1000.0);
- }
- int64_t mg_sntp_parse(const unsigned char *buf, size_t len) {
- int64_t res = -1;
- int mode = len > 0 ? buf[0] & 7 : 0;
- int version = len > 0 ? (buf[0] >> 3) & 7 : 0;
- if (len < 48) {
- MG_ERROR(("%s", "corrupt packet"));
- } else if (mode != 4 && mode != 5) {
- MG_ERROR(("%s", "not a server reply"));
- } else if (buf[1] == 0) {
- MG_ERROR(("%s", "server sent a kiss of death"));
- } else if (version == 4 || version == 3) {
- // int64_t ref = gettimestamp((uint32_t *) &buf[16]);
- int64_t t0 = gettimestamp((uint32_t *) &buf[24]);
- int64_t t1 = gettimestamp((uint32_t *) &buf[32]);
- int64_t t2 = gettimestamp((uint32_t *) &buf[40]);
- int64_t t3 = (int64_t) mg_millis();
- int64_t delta = (t3 - t0) - (t2 - t1);
- MG_VERBOSE(("%lld %lld %lld %lld delta:%lld", t0, t1, t2, t3, delta));
- res = t2 + delta / 2;
- } else {
- MG_ERROR(("unexpected version: %d", version));
- }
- return res;
- }
- static void sntp_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
- if (ev == MG_EV_READ) {
- int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len);
- if (milliseconds > 0) {
- MG_INFO(("%lu got time: %lld ms from epoch", c->id, milliseconds));
- mg_call(c, MG_EV_SNTP_TIME, (uint64_t *) &milliseconds);
- MG_VERBOSE(("%u.%u", (unsigned) (milliseconds / 1000),
- (unsigned) (milliseconds % 1000)));
- }
- mg_iobuf_del(&c->recv, 0, c->recv.len); // Free receive buffer
- } else if (ev == MG_EV_CONNECT) {
- mg_sntp_request(c);
- } else if (ev == MG_EV_CLOSE) {
- }
- (void) fnd;
- (void) evd;
- }
- void mg_sntp_request(struct mg_connection *c) {
- if (c->is_resolving) {
- MG_ERROR(("%lu wait until resolved", c->id));
- } else {
- int64_t now = (int64_t) mg_millis(); // Use int64_t, for vc98
- uint8_t buf[48] = {0};
- uint32_t *t = (uint32_t *) &buf[40];
- double frac = ((double) (now % 1000)) / 1000.0 * SNTP_MAX_FRAC;
- buf[0] = (0 << 6) | (4 << 3) | 3;
- t[0] = mg_htonl((uint32_t) (now / 1000) + SNTP_TIME_OFFSET);
- t[1] = mg_htonl((uint32_t) frac);
- mg_send(c, buf, sizeof(buf));
- }
- }
- struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fnd) {
- struct mg_connection *c = NULL;
- if (url == NULL) url = "udp://time.google.com:123";
- if ((c = mg_connect(mgr, url, fn, fnd)) != NULL) c->pfn = sntp_cb;
- return c;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/sock.c"
- #endif
- #if MG_ENABLE_SOCKET
- #ifndef closesocket
- #define closesocket(x) close(x)
- #endif
- #define FD(c_) ((MG_SOCKET_TYPE) (size_t) (c_)->fd)
- #define S2PTR(s_) ((void *) (size_t) (s_))
- #ifndef MSG_NONBLOCKING
- #define MSG_NONBLOCKING 0
- #endif
- #ifndef AF_INET6
- #define AF_INET6 10
- #endif
- #ifndef MG_SOCK_ERR
- #define MG_SOCK_ERR(errcode) ((errcode) < 0 ? errno : 0)
- #endif
- #ifndef MG_SOCK_INTR
- #define MG_SOCK_INTR(fd) (fd == MG_INVALID_SOCKET && MG_SOCK_ERR(-1) == EINTR)
- #endif
- #ifndef MG_SOCK_PENDING
- #define MG_SOCK_PENDING(errcode) \
- (((errcode) < 0) && (errno == EINPROGRESS || errno == EWOULDBLOCK))
- #endif
- #ifndef MG_SOCK_RESET
- #define MG_SOCK_RESET(errcode) \
- (((errcode) < 0) && (errno == EPIPE || errno == ECONNRESET))
- #endif
- union usa {
- struct sockaddr sa;
- struct sockaddr_in sin;
- #if MG_ENABLE_IPV6
- struct sockaddr_in6 sin6;
- #endif
- };
- static socklen_t tousa(struct mg_addr *a, union usa *usa) {
- socklen_t len = sizeof(usa->sin);
- memset(usa, 0, sizeof(*usa));
- usa->sin.sin_family = AF_INET;
- usa->sin.sin_port = a->port;
- *(uint32_t *) &usa->sin.sin_addr = a->ip;
- #if MG_ENABLE_IPV6
- if (a->is_ip6) {
- usa->sin.sin_family = AF_INET6;
- usa->sin6.sin6_port = a->port;
- memcpy(&usa->sin6.sin6_addr, a->ip6, sizeof(a->ip6));
- len = sizeof(usa->sin6);
- }
- #endif
- return len;
- }
- static void tomgaddr(union usa *usa, struct mg_addr *a, bool is_ip6) {
- a->is_ip6 = is_ip6;
- a->port = usa->sin.sin_port;
- memcpy(&a->ip, &usa->sin.sin_addr, sizeof(a->ip));
- #if MG_ENABLE_IPV6
- if (is_ip6) {
- memcpy(a->ip6, &usa->sin6.sin6_addr, sizeof(a->ip6));
- a->port = usa->sin6.sin6_port;
- }
- #endif
- }
- static void setlocaddr(MG_SOCKET_TYPE fd, struct mg_addr *addr) {
- union usa usa;
- socklen_t n = sizeof(usa);
- if (getsockname(fd, &usa.sa, &n) == 0) {
- tomgaddr(&usa, addr, n != sizeof(usa.sin));
- }
- }
- static void iolog(struct mg_connection *c, char *buf, long n, bool r) {
- if (n == MG_IO_WAIT) {
- // Do nothing
- } else if (n <= 0) {
- c->is_closing = 1; // Termination. Don't call mg_error(): #1529
- } else if (n > 0) {
- if (c->is_hexdumping) {
- union usa usa;
- socklen_t slen = sizeof(usa.sin);
- if (getsockname(FD(c), &usa.sa, &slen) < 0) (void) 0; // Ignore result
- MG_INFO(("\n-- %lu %M %s %M %ld", c->id, mg_print_ip_port, &c->loc,
- r ? "<-" : "->", mg_print_ip_port, &c->rem, n));
- mg_hexdump(buf, (size_t) n);
- }
- if (r) {
- c->recv.len += (size_t) n;
- mg_call(c, MG_EV_READ, &n);
- } else {
- mg_iobuf_del(&c->send, 0, (size_t) n);
- // if (c->send.len == 0) mg_iobuf_resize(&c->send, 0);
- if (c->send.len == 0) {
- MG_EPOLL_MOD(c, 0);
- }
- mg_call(c, MG_EV_WRITE, &n);
- }
- }
- }
- long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
- long n;
- if (c->is_udp) {
- union usa usa;
- socklen_t slen = tousa(&c->rem, &usa);
- n = sendto(FD(c), (char *) buf, len, 0, &usa.sa, slen);
- if (n > 0) setlocaddr(FD(c), &c->loc);
- } else {
- n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING);
- }
- if (MG_SOCK_PENDING(n)) return MG_IO_WAIT;
- if (MG_SOCK_RESET(n)) return MG_IO_RESET;
- if (n <= 0) return MG_IO_ERR;
- return n;
- }
- bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
- if (c->is_udp) {
- long n = mg_io_send(c, buf, len);
- MG_DEBUG(("%lu %p %d:%d %ld err %d", c->id, c->fd, (int) c->send.len,
- (int) c->recv.len, n, MG_SOCK_ERR(n)));
- iolog(c, (char *) buf, n, false);
- return n > 0;
- } else {
- return mg_iobuf_add(&c->send, c->send.len, buf, len);
- }
- }
- static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) {
- #if defined(MG_CUSTOM_NONBLOCK)
- MG_CUSTOM_NONBLOCK(fd);
- #elif MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK
- unsigned long on = 1;
- ioctlsocket(fd, FIONBIO, &on);
- #elif MG_ENABLE_RL
- unsigned long on = 1;
- ioctlsocket(fd, FIONBIO, &on);
- #elif MG_ENABLE_FREERTOS_TCP
- const BaseType_t off = 0;
- if (setsockopt(fd, 0, FREERTOS_SO_RCVTIMEO, &off, sizeof(off)) != 0) (void) 0;
- if (setsockopt(fd, 0, FREERTOS_SO_SNDTIMEO, &off, sizeof(off)) != 0) (void) 0;
- #elif MG_ENABLE_LWIP
- lwip_fcntl(fd, F_SETFL, O_NONBLOCK);
- #elif MG_ARCH == MG_ARCH_AZURERTOS
- fcntl(fd, F_SETFL, O_NONBLOCK);
- #elif MG_ARCH == MG_ARCH_TIRTOS
- int val = 0;
- setsockopt(fd, SOL_SOCKET, SO_BLOCKING, &val, sizeof(val));
- // SPRU524J section 3.3.3 page 63, SO_SNDLOWAT
- int sz = sizeof(val);
- getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &val, &sz);
- val /= 2; // set send low-water mark at half send buffer size
- setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val));
- #else
- fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); // Non-blocking mode
- fcntl(fd, F_SETFD, FD_CLOEXEC); // Set close-on-exec
- #endif
- }
- bool mg_open_listener(struct mg_connection *c, const char *url) {
- MG_SOCKET_TYPE fd = MG_INVALID_SOCKET;
- bool success = false;
- c->loc.port = mg_htons(mg_url_port(url));
- if (!mg_aton(mg_url_host(url), &c->loc)) {
- MG_ERROR(("invalid listening URL: %s", url));
- } else {
- union usa usa;
- socklen_t slen = tousa(&c->loc, &usa);
- int rc, on = 1, af = c->loc.is_ip6 ? AF_INET6 : AF_INET;
- int type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM;
- int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
- (void) on;
- if ((fd = socket(af, type, proto)) == MG_INVALID_SOCKET) {
- MG_ERROR(("socket: %d", MG_SOCK_ERR(-1)));
- #if defined(SO_REUSEADDR) && (!defined(LWIP_SOCKET) || SO_REUSE)
- } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
- sizeof(on))) != 0) {
- // 1. SO_RESUSEADDR semantics on UNIX and Windows is different. On
- // Windows, SO_REUSEADDR allows to bind a socket to a port without error
- // even if the port is already open by another program. This is not the
- // behavior SO_REUSEADDR was designed for, and leads to hard-to-track
- // failure scenarios.
- //
- // 2. For LWIP, SO_REUSEADDR should be explicitly enabled by defining
- // SO_REUSE = 1 in lwipopts.h, otherwise the code below will compile but
- // won't work! (setsockopt will return EINVAL)
- MG_ERROR(("setsockopt(SO_REUSEADDR): %d", MG_SOCK_ERR(rc)));
- #endif
- #if defined(SO_EXCLUSIVEADDRUSE)
- } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
- (char *) &on, sizeof(on))) != 0) {
- // "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE"
- MG_ERROR(("setsockopt(SO_EXCLUSIVEADDRUSE): %d", MG_SOCK_ERR(rc)));
- #endif
- #if defined(IPV6_V6ONLY)
- } else if (c->loc.is_ip6 &&
- (rc = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &on,
- sizeof(on))) != 0) {
- // See #2089. Allow to bind v4 and v6 sockets on the same port
- MG_ERROR(("setsockopt(IPV6_V6ONLY): %d", MG_SOCK_ERR(rc)));
- #endif
- } else if ((rc = bind(fd, &usa.sa, slen)) != 0) {
- MG_ERROR(("bind: %d", MG_SOCK_ERR(rc)));
- } else if ((type == SOCK_STREAM &&
- (rc = listen(fd, MG_SOCK_LISTEN_BACKLOG_SIZE)) != 0)) {
- // NOTE(lsm): FreeRTOS uses backlog value as a connection limit
- // In case port was set to 0, get the real port number
- MG_ERROR(("listen: %d", MG_SOCK_ERR(rc)));
- } else {
- setlocaddr(fd, &c->loc);
- mg_set_non_blocking_mode(fd);
- c->fd = S2PTR(fd);
- MG_EPOLL_ADD(c);
- success = true;
- }
- }
- if (success == false && fd != MG_INVALID_SOCKET) closesocket(fd);
- return success;
- }
- long mg_io_recv(struct mg_connection *c, void *buf, size_t len) {
- long n = 0;
- if (c->is_udp) {
- union usa usa;
- socklen_t slen = tousa(&c->rem, &usa);
- n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
- if (n > 0) tomgaddr(&usa, &c->rem, slen != sizeof(usa.sin));
- } else {
- n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
- }
- if (MG_SOCK_PENDING(n)) return MG_IO_WAIT;
- if (MG_SOCK_RESET(n)) return MG_IO_RESET;
- if (n <= 0) return MG_IO_ERR;
- return n;
- }
- // NOTE(lsm): do only one iteration of reads, cause some systems
- // (e.g. FreeRTOS stack) return 0 instead of -1/EWOULDBLOCK when no data
- static void read_conn(struct mg_connection *c) {
- long n = -1;
- if (c->recv.len >= MG_MAX_RECV_SIZE) {
- mg_error(c, "max_recv_buf_size reached");
- } else if (c->recv.size <= c->recv.len &&
- !mg_iobuf_resize(&c->recv, c->recv.size + MG_IO_SIZE)) {
- mg_error(c, "oom");
- } else {
- char *buf = (char *) &c->recv.buf[c->recv.len];
- size_t len = c->recv.size - c->recv.len;
- n = c->is_tls ? mg_tls_recv(c, buf, len) : mg_io_recv(c, buf, len);
- MG_DEBUG(("%lu %p snd %ld/%ld rcv %ld/%ld n=%ld err=%d", c->id, c->fd,
- (long) c->send.len, (long) c->send.size, (long) c->recv.len,
- (long) c->recv.size, n, MG_SOCK_ERR(n)));
- iolog(c, buf, n, true);
- }
- }
- static void write_conn(struct mg_connection *c) {
- char *buf = (char *) c->send.buf;
- size_t len = c->send.len;
- long n = c->is_tls ? mg_tls_send(c, buf, len) : mg_io_send(c, buf, len);
- MG_DEBUG(("%lu %p snd %ld/%ld rcv %ld/%ld n=%ld err=%d", c->id, c->fd,
- (long) c->send.len, (long) c->send.size, (long) c->recv.len,
- (long) c->recv.size, n, MG_SOCK_ERR(n)));
- iolog(c, buf, n, false);
- }
- static void close_conn(struct mg_connection *c) {
- if (FD(c) != MG_INVALID_SOCKET) {
- #if MG_ENABLE_EPOLL
- epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_DEL, FD(c), NULL);
- #endif
- closesocket(FD(c));
- #if MG_ENABLE_FREERTOS_TCP
- FreeRTOS_FD_CLR(c->fd, c->mgr->ss, eSELECT_ALL);
- #endif
- }
- mg_close_conn(c);
- }
- static void connect_conn(struct mg_connection *c) {
- union usa usa;
- socklen_t n = sizeof(usa);
- // Use getpeername() to test whether we have connected
- if (getpeername(FD(c), &usa.sa, &n) == 0) {
- c->is_connecting = 0;
- mg_call(c, MG_EV_CONNECT, NULL);
- MG_EPOLL_MOD(c, 0);
- if (c->is_tls_hs) mg_tls_handshake(c);
- } else {
- mg_error(c, "socket error");
- }
- }
- static void setsockopts(struct mg_connection *c) {
- #if MG_ENABLE_FREERTOS_TCP || MG_ARCH == MG_ARCH_AZURERTOS || \
- MG_ARCH == MG_ARCH_TIRTOS
- (void) c;
- #else
- int on = 1;
- #if !defined(SOL_TCP)
- #define SOL_TCP IPPROTO_TCP
- #endif
- if (setsockopt(FD(c), SOL_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) != 0)
- (void) 0;
- if (setsockopt(FD(c), SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) !=
- 0)
- (void) 0;
- #endif
- }
- void mg_connect_resolved(struct mg_connection *c) {
- int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM;
- int rc, af = c->rem.is_ip6 ? AF_INET6 : AF_INET; // c->rem has resolved IP
- c->fd = S2PTR(socket(af, type, 0)); // Create outbound socket
- c->is_resolving = 0; // Clear resolving flag
- if (FD(c) == MG_INVALID_SOCKET) {
- mg_error(c, "socket(): %d", MG_SOCK_ERR(-1));
- } else if (c->is_udp) {
- MG_EPOLL_ADD(c);
- #if MG_ARCH == MG_ARCH_TIRTOS
- union usa usa; // TI-RTOS NDK requires binding to receive on UDP sockets
- socklen_t slen = tousa(&c->loc, &usa);
- if ((rc = bind(c->fd, &usa.sa, slen)) != 0)
- MG_ERROR(("bind: %d", MG_SOCK_ERR(rc)));
- #endif
- mg_call(c, MG_EV_RESOLVE, NULL);
- mg_call(c, MG_EV_CONNECT, NULL);
- } else {
- union usa usa;
- socklen_t slen = tousa(&c->rem, &usa);
- mg_set_non_blocking_mode(FD(c));
- setsockopts(c);
- MG_EPOLL_ADD(c);
- mg_call(c, MG_EV_RESOLVE, NULL);
- rc = connect(FD(c), &usa.sa, slen); // Attempt to connect
- if (rc == 0) { // Success
- mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user
- } else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake
- MG_DEBUG(("%lu %p -> %M pend", c->id, c->fd, mg_print_ip_port, &c->rem));
- c->is_connecting = 1;
- } else {
- mg_error(c, "connect: %d", MG_SOCK_ERR(rc));
- }
- }
- }
- static MG_SOCKET_TYPE raccept(MG_SOCKET_TYPE sock, union usa *usa,
- socklen_t *len) {
- MG_SOCKET_TYPE fd = MG_INVALID_SOCKET;
- do {
- memset(usa, 0, sizeof(*usa));
- fd = accept(sock, &usa->sa, len);
- } while (MG_SOCK_INTR(fd));
- return fd;
- }
- static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) {
- struct mg_connection *c = NULL;
- union usa usa;
- socklen_t sa_len = sizeof(usa);
- MG_SOCKET_TYPE fd = raccept(FD(lsn), &usa, &sa_len);
- if (fd == MG_INVALID_SOCKET) {
- #if MG_ARCH == MG_ARCH_AZURERTOS
- // AzureRTOS, in non-block socket mode can mark listening socket readable
- // even it is not. See comment for 'select' func implementation in
- // nx_bsd.c That's not an error, just should try later
- if (errno != EAGAIN)
- #endif
- MG_ERROR(("%lu accept failed, errno %d", lsn->id, MG_SOCK_ERR(-1)));
- #if (MG_ARCH != MG_ARCH_WIN32) && !MG_ENABLE_FREERTOS_TCP && \
- (MG_ARCH != MG_ARCH_TIRTOS) && !MG_ENABLE_POLL
- } else if ((long) fd >= FD_SETSIZE) {
- MG_ERROR(("%ld > %ld", (long) fd, (long) FD_SETSIZE));
- closesocket(fd);
- #endif
- } else if ((c = mg_alloc_conn(mgr)) == NULL) {
- MG_ERROR(("%lu OOM", lsn->id));
- closesocket(fd);
- } else {
- tomgaddr(&usa, &c->rem, sa_len != sizeof(usa.sin));
- LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
- c->fd = S2PTR(fd);
- MG_EPOLL_ADD(c);
- mg_set_non_blocking_mode(FD(c));
- setsockopts(c);
- c->is_accepted = 1;
- c->is_hexdumping = lsn->is_hexdumping;
- c->loc = lsn->loc;
- c->pfn = lsn->pfn;
- c->pfn_data = lsn->pfn_data;
- c->fn = lsn->fn;
- c->fn_data = lsn->fn_data;
- MG_DEBUG(("%lu %p accepted %M -> %M", c->id, c->fd, mg_print_ip_port,
- &c->rem, mg_print_ip_port, &c->loc));
- mg_call(c, MG_EV_OPEN, NULL);
- mg_call(c, MG_EV_ACCEPT, NULL);
- }
- }
- static bool mg_socketpair(MG_SOCKET_TYPE sp[2], union usa usa[2], bool udp) {
- MG_SOCKET_TYPE sock;
- socklen_t n = sizeof(usa[0].sin);
- bool success = false;
- sock = sp[0] = sp[1] = MG_INVALID_SOCKET;
- (void) memset(&usa[0], 0, sizeof(usa[0]));
- usa[0].sin.sin_family = AF_INET;
- *(uint32_t *) &usa->sin.sin_addr = mg_htonl(0x7f000001U); // 127.0.0.1
- usa[1] = usa[0];
- if (udp && (sp[0] = socket(AF_INET, SOCK_DGRAM, 0)) != MG_INVALID_SOCKET &&
- (sp[1] = socket(AF_INET, SOCK_DGRAM, 0)) != MG_INVALID_SOCKET &&
- bind(sp[0], &usa[0].sa, n) == 0 && bind(sp[1], &usa[1].sa, n) == 0 &&
- getsockname(sp[0], &usa[0].sa, &n) == 0 &&
- getsockname(sp[1], &usa[1].sa, &n) == 0 &&
- connect(sp[0], &usa[1].sa, n) == 0 &&
- connect(sp[1], &usa[0].sa, n) == 0) {
- success = true;
- } else if (!udp &&
- (sock = socket(AF_INET, SOCK_STREAM, 0)) != MG_INVALID_SOCKET &&
- bind(sock, &usa[0].sa, n) == 0 &&
- listen(sock, MG_SOCK_LISTEN_BACKLOG_SIZE) == 0 &&
- getsockname(sock, &usa[0].sa, &n) == 0 &&
- (sp[0] = socket(AF_INET, SOCK_STREAM, 0)) != MG_INVALID_SOCKET &&
- connect(sp[0], &usa[0].sa, n) == 0 &&
- (sp[1] = raccept(sock, &usa[1], &n)) != MG_INVALID_SOCKET) {
- success = true;
- }
- if (success) {
- mg_set_non_blocking_mode(sp[1]);
- } else {
- if (sp[0] != MG_INVALID_SOCKET) closesocket(sp[0]);
- if (sp[1] != MG_INVALID_SOCKET) closesocket(sp[1]);
- sp[0] = sp[1] = MG_INVALID_SOCKET;
- }
- if (sock != MG_INVALID_SOCKET) closesocket(sock);
- return success;
- }
- int mg_mkpipe(struct mg_mgr *mgr, mg_event_handler_t fn, void *fn_data,
- bool udp) {
- union usa usa[2];
- MG_SOCKET_TYPE sp[2] = {MG_INVALID_SOCKET, MG_INVALID_SOCKET};
- struct mg_connection *c = NULL;
- if (!mg_socketpair(sp, usa, udp)) {
- MG_ERROR(("Cannot create socket pair"));
- } else if ((c = mg_wrapfd(mgr, (int) sp[1], fn, fn_data)) == NULL) {
- closesocket(sp[0]);
- closesocket(sp[1]);
- sp[0] = sp[1] = MG_INVALID_SOCKET;
- } else {
- tomgaddr(&usa[0], &c->rem, false);
- MG_DEBUG(("%lu %p pipe %lu", c->id, c->fd, (unsigned long) sp[0]));
- }
- return (int) sp[0];
- }
- static bool can_read(const struct mg_connection *c) {
- return c->is_full == false;
- }
- static bool can_write(const struct mg_connection *c) {
- return c->is_connecting || (c->send.len > 0 && c->is_tls_hs == 0);
- }
- static bool skip_iotest(const struct mg_connection *c) {
- return (c->is_closing || c->is_resolving || FD(c) == MG_INVALID_SOCKET) ||
- (can_read(c) == false && can_write(c) == false);
- }
- static void mg_iotest(struct mg_mgr *mgr, int ms) {
- #if MG_ENABLE_FREERTOS_TCP
- struct mg_connection *c;
- for (c = mgr->conns; c != NULL; c = c->next) {
- c->is_readable = c->is_writable = 0;
- if (skip_iotest(c)) continue;
- if (can_read(c))
- FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_READ | eSELECT_EXCEPT);
- if (can_write(c)) FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_WRITE);
- }
- FreeRTOS_select(mgr->ss, pdMS_TO_TICKS(ms));
- for (c = mgr->conns; c != NULL; c = c->next) {
- EventBits_t bits = FreeRTOS_FD_ISSET(c->fd, mgr->ss);
- c->is_readable = bits & (eSELECT_READ | eSELECT_EXCEPT) ? 1U : 0;
- c->is_writable = bits & eSELECT_WRITE ? 1U : 0;
- if (c->fd != MG_INVALID_SOCKET)
- FreeRTOS_FD_CLR(c->fd, mgr->ss,
- eSELECT_READ | eSELECT_EXCEPT | eSELECT_WRITE);
- }
- #elif MG_ENABLE_EPOLL
- size_t max = 1;
- for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
- c->is_readable = c->is_writable = 0;
- if (mg_tls_pending(c) > 0) ms = 1, c->is_readable = 1;
- if (can_write(c)) MG_EPOLL_MOD(c, 1);
- max++;
- }
- struct epoll_event *evs = (struct epoll_event *) alloca(max * sizeof(evs[0]));
- int n = epoll_wait(mgr->epoll_fd, evs, (int) max, ms);
- for (int i = 0; i < n; i++) {
- struct mg_connection *c = (struct mg_connection *) evs[i].data.ptr;
- if (evs[i].events & EPOLLERR) {
- mg_error(c, "socket error");
- } else if (c->is_readable == 0) {
- bool rd = evs[i].events & (EPOLLIN | EPOLLHUP);
- bool wr = evs[i].events & EPOLLOUT;
- c->is_readable = can_read(c) && rd ? 1U : 0;
- c->is_writable = can_write(c) && wr ? 1U : 0;
- }
- }
- (void) skip_iotest;
- #elif MG_ENABLE_POLL
- nfds_t n = 0;
- for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) n++;
- struct pollfd *fds = (struct pollfd *) alloca(n * sizeof(fds[0]));
- memset(fds, 0, n * sizeof(fds[0]));
- n = 0;
- for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
- c->is_readable = c->is_writable = 0;
- if (skip_iotest(c)) {
- // Socket not valid, ignore
- } else if (mg_tls_pending(c) > 0) {
- ms = 1; // Don't wait if TLS is ready
- } else {
- fds[n].fd = FD(c);
- if (can_read(c)) fds[n].events |= POLLIN;
- if (can_write(c)) fds[n].events |= POLLOUT;
- n++;
- }
- }
- // MG_INFO(("poll n=%d ms=%d", (int) n, ms));
- if (poll(fds, n, ms) < 0) {
- #if MG_ARCH == MG_ARCH_WIN32
- if (n == 0) Sleep(ms); // On Windows, poll fails if no sockets
- #endif
- memset(fds, 0, n * sizeof(fds[0]));
- }
- n = 0;
- for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
- if (skip_iotest(c)) {
- // Socket not valid, ignore
- } else if (mg_tls_pending(c) > 0) {
- c->is_readable = 1;
- } else {
- if (fds[n].revents & POLLERR) {
- mg_error(c, "socket error");
- } else {
- c->is_readable =
- (unsigned) (fds[n].revents & (POLLIN | POLLHUP) ? 1 : 0);
- c->is_writable = (unsigned) (fds[n].revents & POLLOUT ? 1 : 0);
- }
- n++;
- }
- }
- #else
- struct timeval tv = {ms / 1000, (ms % 1000) * 1000}, tv_zero = {0, 0}, *tvp;
- struct mg_connection *c;
- fd_set rset, wset, eset;
- MG_SOCKET_TYPE maxfd = 0;
- int rc;
- FD_ZERO(&rset);
- FD_ZERO(&wset);
- FD_ZERO(&eset);
- tvp = ms < 0 ? NULL : &tv;
- for (c = mgr->conns; c != NULL; c = c->next) {
- c->is_readable = c->is_writable = 0;
- if (skip_iotest(c)) continue;
- FD_SET(FD(c), &eset);
- if (can_read(c)) FD_SET(FD(c), &rset);
- if (can_write(c)) FD_SET(FD(c), &wset);
- if (mg_tls_pending(c) > 0) tvp = &tv_zero;
- if (FD(c) > maxfd) maxfd = FD(c);
- }
- if ((rc = select((int) maxfd + 1, &rset, &wset, &eset, tvp)) < 0) {
- #if MG_ARCH == MG_ARCH_WIN32
- if (maxfd == 0) Sleep(ms); // On Windows, select fails if no sockets
- #else
- MG_ERROR(("select: %d %d", rc, MG_SOCK_ERR(rc)));
- #endif
- FD_ZERO(&rset);
- FD_ZERO(&wset);
- FD_ZERO(&eset);
- }
- for (c = mgr->conns; c != NULL; c = c->next) {
- if (FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &eset)) {
- mg_error(c, "socket error");
- } else {
- c->is_readable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &rset);
- c->is_writable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &wset);
- if (mg_tls_pending(c) > 0) c->is_readable = 1;
- }
- }
- #endif
- }
- void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
- struct mg_connection *c, *tmp;
- uint64_t now;
- mg_iotest(mgr, ms);
- now = mg_millis();
- mg_timer_poll(&mgr->timers, now);
- for (c = mgr->conns; c != NULL; c = tmp) {
- bool is_resp = c->is_resp;
- tmp = c->next;
- mg_call(c, MG_EV_POLL, &now);
- if (is_resp && !c->is_resp) {
- long n = 0;
- mg_call(c, MG_EV_READ, &n);
- }
- MG_VERBOSE(("%lu %c%c %c%c%c%c%c", c->id, c->is_readable ? 'r' : '-',
- c->is_writable ? 'w' : '-', c->is_tls ? 'T' : 't',
- c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h',
- c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c'));
- if (c->is_resolving || c->is_closing) {
- // Do nothing
- } else if (c->is_listening && c->is_udp == 0) {
- if (c->is_readable) accept_conn(mgr, c);
- } else if (c->is_connecting) {
- if (c->is_readable || c->is_writable) connect_conn(c);
- } else if (c->is_tls_hs) {
- if ((c->is_readable || c->is_writable)) mg_tls_handshake(c);
- } else {
- if (c->is_readable) read_conn(c);
- if (c->is_writable) write_conn(c);
- }
- if (c->is_draining && c->send.len == 0) c->is_closing = 1;
- if (c->is_closing) close_conn(c);
- }
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/ssi.c"
- #endif
- #ifndef MG_MAX_SSI_DEPTH
- #define MG_MAX_SSI_DEPTH 5
- #endif
- #ifndef MG_SSI_BUFSIZ
- #define MG_SSI_BUFSIZ 1024
- #endif
- #if MG_ENABLE_SSI
- static char *mg_ssi(const char *path, const char *root, int depth) {
- struct mg_iobuf b = {NULL, 0, 0, MG_IO_SIZE};
- FILE *fp = fopen(path, "rb");
- if (fp != NULL) {
- char buf[MG_SSI_BUFSIZ], arg[sizeof(buf)];
- int ch, intag = 0;
- size_t len = 0;
- buf[0] = arg[0] = '\0';
- while ((ch = fgetc(fp)) != EOF) {
- if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') {
- buf[len++] = (char) (ch & 0xff);
- buf[len] = '\0';
- if (sscanf(buf, "<!--#include file=\"%[^\"]", arg)) {
- char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10],
- *p = (char *) path + strlen(path), *data;
- while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--;
- mg_snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg);
- if (depth < MG_MAX_SSI_DEPTH &&
- (data = mg_ssi(tmp, root, depth + 1)) != NULL) {
- mg_iobuf_add(&b, b.len, data, strlen(data));
- free(data);
- } else {
- MG_ERROR(("%s: file=%s error or too deep", path, arg));
- }
- } else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg)) {
- char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10], *data;
- mg_snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
- if (depth < MG_MAX_SSI_DEPTH &&
- (data = mg_ssi(tmp, root, depth + 1)) != NULL) {
- mg_iobuf_add(&b, b.len, data, strlen(data));
- free(data);
- } else {
- MG_ERROR(("%s: virtual=%s error or too deep", path, arg));
- }
- } else {
- // Unknown SSI tag
- MG_ERROR(("Unknown SSI tag: %.*s", (int) len, buf));
- mg_iobuf_add(&b, b.len, buf, len);
- }
- intag = 0;
- len = 0;
- } else if (ch == '<') {
- intag = 1;
- if (len > 0) mg_iobuf_add(&b, b.len, buf, len);
- len = 0;
- buf[len++] = (char) (ch & 0xff);
- } else if (intag) {
- if (len == 5 && strncmp(buf, "<!--#", 5) != 0) {
- intag = 0;
- } else if (len >= sizeof(buf) - 2) {
- MG_ERROR(("%s: SSI tag is too large", path));
- len = 0;
- }
- buf[len++] = (char) (ch & 0xff);
- } else {
- buf[len++] = (char) (ch & 0xff);
- if (len >= sizeof(buf)) {
- mg_iobuf_add(&b, b.len, buf, len);
- len = 0;
- }
- }
- }
- if (len > 0) mg_iobuf_add(&b, b.len, buf, len);
- if (b.len > 0) mg_iobuf_add(&b, b.len, "", 1); // nul-terminate
- fclose(fp);
- }
- (void) depth;
- (void) root;
- return (char *) b.buf;
- }
- void mg_http_serve_ssi(struct mg_connection *c, const char *root,
- const char *fullpath) {
- const char *headers = "Content-Type: text/html; charset=utf-8\r\n";
- char *data = mg_ssi(fullpath, root, 0);
- mg_http_reply(c, 200, headers, "%s", data == NULL ? "" : data);
- free(data);
- }
- #else
- void mg_http_serve_ssi(struct mg_connection *c, const char *root,
- const char *fullpath) {
- mg_http_reply(c, 501, NULL, "SSI not enabled");
- (void) root, (void) fullpath;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/str.c"
- #endif
- struct mg_str mg_str_s(const char *s) {
- struct mg_str str = {s, s == NULL ? 0 : strlen(s)};
- return str;
- }
- struct mg_str mg_str_n(const char *s, size_t n) {
- struct mg_str str = {s, n};
- return str;
- }
- int mg_lower(const char *s) {
- int c = *s;
- if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
- return c;
- }
- int mg_ncasecmp(const char *s1, const char *s2, size_t len) {
- int diff = 0;
- if (len > 0) do {
- diff = mg_lower(s1++) - mg_lower(s2++);
- } while (diff == 0 && s1[-1] != '\0' && --len > 0);
- return diff;
- }
- int mg_casecmp(const char *s1, const char *s2) {
- return mg_ncasecmp(s1, s2, (size_t) ~0);
- }
- int mg_vcmp(const struct mg_str *s1, const char *s2) {
- size_t n2 = strlen(s2), n1 = s1->len;
- int r = strncmp(s1->ptr, s2, (n1 < n2) ? n1 : n2);
- if (r == 0) return (int) (n1 - n2);
- return r;
- }
- int mg_vcasecmp(const struct mg_str *str1, const char *str2) {
- size_t n2 = strlen(str2), n1 = str1->len;
- int r = mg_ncasecmp(str1->ptr, str2, (n1 < n2) ? n1 : n2);
- if (r == 0) return (int) (n1 - n2);
- return r;
- }
- struct mg_str mg_strdup(const struct mg_str s) {
- struct mg_str r = {NULL, 0};
- if (s.len > 0 && s.ptr != NULL) {
- char *sc = (char *) calloc(1, s.len + 1);
- if (sc != NULL) {
- memcpy(sc, s.ptr, s.len);
- sc[s.len] = '\0';
- r.ptr = sc;
- r.len = s.len;
- }
- }
- return r;
- }
- int mg_strcmp(const struct mg_str str1, const struct mg_str str2) {
- size_t i = 0;
- while (i < str1.len && i < str2.len) {
- int c1 = str1.ptr[i];
- int c2 = str2.ptr[i];
- if (c1 < c2) return -1;
- if (c1 > c2) return 1;
- i++;
- }
- if (i < str1.len) return 1;
- if (i < str2.len) return -1;
- return 0;
- }
- const char *mg_strstr(const struct mg_str haystack,
- const struct mg_str needle) {
- size_t i;
- if (needle.len > haystack.len) return NULL;
- if (needle.len == 0) return haystack.ptr;
- for (i = 0; i <= haystack.len - needle.len; i++) {
- if (memcmp(haystack.ptr + i, needle.ptr, needle.len) == 0) {
- return haystack.ptr + i;
- }
- }
- return NULL;
- }
- static bool is_space(int c) {
- return c == ' ' || c == '\r' || c == '\n' || c == '\t';
- }
- struct mg_str mg_strstrip(struct mg_str s) {
- while (s.len > 0 && is_space((int) *s.ptr)) s.ptr++, s.len--;
- while (s.len > 0 && is_space((int) *(s.ptr + s.len - 1))) s.len--;
- return s;
- }
- bool mg_match(struct mg_str s, struct mg_str p, struct mg_str *caps) {
- size_t i = 0, j = 0, ni = 0, nj = 0;
- if (caps) caps->ptr = NULL, caps->len = 0;
- while (i < p.len || j < s.len) {
- if (i < p.len && j < s.len && (p.ptr[i] == '?' || s.ptr[j] == p.ptr[i])) {
- if (caps == NULL) {
- } else if (p.ptr[i] == '?') {
- caps->ptr = &s.ptr[j], caps->len = 1; // Finalize `?` cap
- caps++, caps->ptr = NULL, caps->len = 0; // Init next cap
- } else if (caps->ptr != NULL && caps->len == 0) {
- caps->len = (size_t) (&s.ptr[j] - caps->ptr); // Finalize current cap
- caps++, caps->len = 0, caps->ptr = NULL; // Init next cap
- }
- i++, j++;
- } else if (i < p.len && (p.ptr[i] == '*' || p.ptr[i] == '#')) {
- if (caps && !caps->ptr) caps->len = 0, caps->ptr = &s.ptr[j]; // Init cap
- ni = i++, nj = j + 1;
- } else if (nj > 0 && nj <= s.len && (p.ptr[ni] == '#' || s.ptr[j] != '/')) {
- i = ni, j = nj;
- if (caps && caps->ptr == NULL && caps->len == 0) {
- caps--, caps->len = 0; // Restart previous cap
- }
- } else {
- return false;
- }
- }
- if (caps && caps->ptr && caps->len == 0) {
- caps->len = (size_t) (&s.ptr[j] - caps->ptr);
- }
- return true;
- }
- bool mg_globmatch(const char *s1, size_t n1, const char *s2, size_t n2) {
- return mg_match(mg_str_n(s2, n2), mg_str_n(s1, n1), NULL);
- }
- static size_t mg_nce(const char *s, size_t n, size_t ofs, size_t *koff,
- size_t *klen, size_t *voff, size_t *vlen, char delim) {
- size_t kvlen, kl;
- for (kvlen = 0; ofs + kvlen < n && s[ofs + kvlen] != delim;) kvlen++;
- for (kl = 0; kl < kvlen && s[ofs + kl] != '=';) kl++;
- if (koff != NULL) *koff = ofs;
- if (klen != NULL) *klen = kl;
- if (voff != NULL) *voff = kl < kvlen ? ofs + kl + 1 : 0;
- if (vlen != NULL) *vlen = kl < kvlen ? kvlen - kl - 1 : 0;
- ofs += kvlen + 1;
- return ofs > n ? n : ofs;
- }
- bool mg_split(struct mg_str *s, struct mg_str *k, struct mg_str *v, char sep) {
- size_t koff = 0, klen = 0, voff = 0, vlen = 0, off = 0;
- if (s->ptr == NULL || s->len == 0) return 0;
- off = mg_nce(s->ptr, s->len, 0, &koff, &klen, &voff, &vlen, sep);
- if (k != NULL) *k = mg_str_n(s->ptr + koff, klen);
- if (v != NULL) *v = mg_str_n(s->ptr + voff, vlen);
- *s = mg_str_n(s->ptr + off, s->len - off);
- return off > 0;
- }
- bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v) {
- return mg_split(s, k, v, ',');
- }
- char *mg_hex(const void *buf, size_t len, char *to) {
- const unsigned char *p = (const unsigned char *) buf;
- const char *hex = "0123456789abcdef";
- size_t i = 0;
- for (; len--; p++) {
- to[i++] = hex[p[0] >> 4];
- to[i++] = hex[p[0] & 0x0f];
- }
- to[i] = '\0';
- return to;
- }
- static unsigned char mg_unhex_nimble(unsigned char c) {
- return (c >= '0' && c <= '9') ? (unsigned char) (c - '0')
- : (c >= 'A' && c <= 'F') ? (unsigned char) (c - '7')
- : (unsigned char) (c - 'W');
- }
- unsigned long mg_unhexn(const char *s, size_t len) {
- unsigned long i = 0, v = 0;
- for (i = 0; i < len; i++) v <<= 4, v |= mg_unhex_nimble(((uint8_t *) s)[i]);
- return v;
- }
- void mg_unhex(const char *buf, size_t len, unsigned char *to) {
- size_t i;
- for (i = 0; i < len; i += 2) {
- to[i >> 1] = (unsigned char) mg_unhexn(&buf[i], 2);
- }
- }
- uint64_t mg_tou64(struct mg_str str) {
- uint64_t result = 0;
- size_t i = 0;
- while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++;
- while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
- result *= 10;
- result += (unsigned) (str.ptr[i] - '0');
- i++;
- }
- return result;
- }
- int64_t mg_to64(struct mg_str str) {
- int64_t result = 0, neg = 1, max = 922337203685477570 /* INT64_MAX/10-10 */;
- size_t i = 0;
- while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++;
- if (i < str.len && str.ptr[i] == '-') neg = -1, i++;
- while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
- if (result > max) return 0;
- result *= 10;
- result += (str.ptr[i] - '0');
- i++;
- }
- return result * neg;
- }
- char *mg_remove_double_dots(char *s) {
- char *saved = s, *p = s;
- while (*s != '\0') {
- *p++ = *s++;
- if (s[-1] == '/' || s[-1] == '\\') {
- while (s[0] != '\0') {
- if (s[0] == '/' || s[0] == '\\') {
- s++;
- } else if (s[0] == '.' && s[1] == '.' &&
- (s[2] == '/' || s[2] == '\\')) {
- s += 2;
- } else {
- break;
- }
- }
- }
- }
- *p = '\0';
- return saved;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/timer.c"
- #endif
- #define MG_TIMER_CALLED 4
- void mg_timer_init(struct mg_timer **head, struct mg_timer *t, uint64_t ms,
- unsigned flags, void (*fn)(void *), void *arg) {
- t->id = 0, t->period_ms = ms, t->expire = 0;
- t->flags = flags, t->fn = fn, t->arg = arg, t->next = *head;
- *head = t;
- }
- void mg_timer_free(struct mg_timer **head, struct mg_timer *t) {
- while (*head && *head != t) head = &(*head)->next;
- if (*head) *head = t->next;
- }
- // t: expiration time, prd: period, now: current time. Return true if expired
- bool mg_timer_expired(uint64_t *t, uint64_t prd, uint64_t now) {
- if (now + prd < *t) *t = 0; // Time wrapped? Reset timer
- if (*t == 0) *t = now + prd; // Firt poll? Set expiration
- if (*t > now) return false; // Not expired yet, return
- *t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time
- return true; // Expired, return true
- }
- void mg_timer_poll(struct mg_timer **head, uint64_t now_ms) {
- struct mg_timer *t, *tmp;
- for (t = *head; t != NULL; t = tmp) {
- bool once = t->expire == 0 && (t->flags & MG_TIMER_RUN_NOW) &&
- !(t->flags & MG_TIMER_CALLED); // Handle MG_TIMER_NOW only once
- bool expired = mg_timer_expired(&t->expire, t->period_ms, now_ms);
- tmp = t->next;
- if (!once && !expired) continue;
- if ((t->flags & MG_TIMER_REPEAT) || !(t->flags & MG_TIMER_CALLED)) {
- t->fn(t->arg);
- }
- t->flags |= MG_TIMER_CALLED;
- }
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tls_dummy.c"
- #endif
- #if !MG_ENABLE_MBEDTLS && !MG_ENABLE_OPENSSL && !MG_ENABLE_CUSTOM_TLS
- void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
- (void) opts;
- mg_error(c, "TLS is not enabled");
- }
- void mg_tls_handshake(struct mg_connection *c) {
- (void) c;
- }
- void mg_tls_free(struct mg_connection *c) {
- (void) c;
- }
- long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
- return c == NULL || buf == NULL || len == 0 ? 0 : -1;
- }
- long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
- return c == NULL || buf == NULL || len == 0 ? 0 : -1;
- }
- size_t mg_tls_pending(struct mg_connection *c) {
- (void) c;
- return 0;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tls_mbed.c"
- #endif
- #if MG_ENABLE_MBEDTLS
- #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000
- #define MGRNG , rng_get, NULL
- #else
- #define MGRNG
- #endif
- void mg_tls_free(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- if (tls != NULL) {
- free(tls->cafile);
- mbedtls_ssl_free(&tls->ssl);
- mbedtls_pk_free(&tls->pk);
- mbedtls_x509_crt_free(&tls->ca);
- mbedtls_x509_crt_free(&tls->cert);
- mbedtls_ssl_config_free(&tls->conf);
- free(tls);
- c->tls = NULL;
- }
- }
- static int mg_net_send(void *ctx, const unsigned char *buf, size_t len) {
- long n = mg_io_send((struct mg_connection *) ctx, buf, len);
- MG_VERBOSE(("%lu n=%ld e=%d", ((struct mg_connection *) ctx)->id, n, errno));
- if (n == MG_IO_WAIT) return MBEDTLS_ERR_SSL_WANT_WRITE;
- if (n == MG_IO_RESET) return MBEDTLS_ERR_NET_CONN_RESET;
- if (n == MG_IO_ERR) return MBEDTLS_ERR_NET_SEND_FAILED;
- return (int) n;
- }
- static int mg_net_recv(void *ctx, unsigned char *buf, size_t len) {
- long n = mg_io_recv((struct mg_connection *) ctx, buf, len);
- MG_VERBOSE(("%lu n=%ld", ((struct mg_connection *) ctx)->id, n));
- if (n == MG_IO_WAIT) return MBEDTLS_ERR_SSL_WANT_WRITE;
- if (n == MG_IO_RESET) return MBEDTLS_ERR_NET_CONN_RESET;
- if (n == MG_IO_ERR) return MBEDTLS_ERR_NET_RECV_FAILED;
- return (int) n;
- }
- void mg_tls_handshake(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- int rc = mbedtls_ssl_handshake(&tls->ssl);
- if (rc == 0) { // Success
- MG_DEBUG(("%lu success", c->id));
- c->is_tls_hs = 0;
- mg_call(c, MG_EV_TLS_HS, NULL);
- } else if (rc == MBEDTLS_ERR_SSL_WANT_READ ||
- rc == MBEDTLS_ERR_SSL_WANT_WRITE) { // Still pending
- MG_VERBOSE(("%lu pending, %d%d %d (-%#x)", c->id, c->is_connecting,
- c->is_tls_hs, rc, -rc));
- } else {
- mg_error(c, "TLS handshake: -%#x", -rc); // Error
- }
- }
- static int mbed_rng(void *ctx, unsigned char *buf, size_t len) {
- mg_random(buf, len);
- (void) ctx;
- return 0;
- }
- static void debug_cb(void *c, int lev, const char *s, int n, const char *s2) {
- n = (int) strlen(s2) - 1;
- MG_INFO(("%lu %d %.*s", ((struct mg_connection *) c)->id, lev, n, s2));
- (void) s;
- }
- #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000
- static int rng_get(void *p_rng, unsigned char *buf, size_t len) {
- (void) p_rng;
- mg_random(buf, len);
- return 0;
- }
- #endif
- static struct mg_str mg_loadfile(struct mg_fs *fs, const char *path) {
- size_t n = 0;
- if (path[0] == '-') return mg_str(path);
- char *p = mg_file_read(fs, path, &n);
- return mg_str_n(p, n);
- }
- void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
- struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
- struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls));
- int rc = 0;
- c->tls = tls;
- if (c->tls == NULL) {
- mg_error(c, "TLS OOM");
- goto fail;
- }
- MG_DEBUG(("%lu Setting TLS", c->id));
- mbedtls_ssl_init(&tls->ssl);
- mbedtls_ssl_config_init(&tls->conf);
- mbedtls_x509_crt_init(&tls->ca);
- mbedtls_x509_crt_init(&tls->cert);
- mbedtls_pk_init(&tls->pk);
- mbedtls_ssl_conf_dbg(&tls->conf, debug_cb, c);
- #if defined(MG_MBEDTLS_DEBUG_LEVEL)
- mbedtls_debug_set_threshold(MG_MBEDTLS_DEBUG_LEVEL);
- #endif
- if ((rc = mbedtls_ssl_config_defaults(
- &tls->conf,
- c->is_client ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
- MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
- mg_error(c, "tls defaults %#x", -rc);
- goto fail;
- }
- mbedtls_ssl_conf_rng(&tls->conf, mbed_rng, c);
- if (opts->ca == NULL || strcmp(opts->ca, "*") == 0) {
- mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
- } else if (opts->ca != NULL && opts->ca[0] != '\0') {
- #if defined(MBEDTLS_X509_CA_CHAIN_ON_DISK)
- tls->cafile = strdup(opts->ca);
- rc = mbedtls_ssl_conf_ca_chain_file(&tls->conf, tls->cafile, NULL);
- if (rc != 0) {
- mg_error(c, "parse on-disk chain(%s) err %#x", tls->cafile, -rc);
- goto fail;
- }
- #else
- struct mg_str s = mg_loadfile(fs, opts->ca);
- rc = mbedtls_x509_crt_parse(&tls->ca, (uint8_t *) s.ptr, s.len + 1);
- if (opts->ca[0] != '-') free((char *) s.ptr);
- if (rc != 0) {
- mg_error(c, "parse(%s) err %#x", opts->ca, -rc);
- goto fail;
- }
- mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->ca, NULL);
- #endif
- if (opts->srvname.len > 0) {
- char *x = mg_mprintf("%.*s", (int) opts->srvname.len, opts->srvname.ptr);
- mbedtls_ssl_set_hostname(&tls->ssl, x);
- free(x);
- }
- mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
- }
- if (opts->cert != NULL && opts->cert[0] != '\0') {
- struct mg_str s = mg_loadfile(fs, opts->cert);
- const char *key = opts->certkey == NULL ? opts->cert : opts->certkey;
- rc = mbedtls_x509_crt_parse(&tls->cert, (uint8_t *) s.ptr, s.len + 1);
- if (opts->cert[0] != '-') free((char *) s.ptr);
- if (rc != 0) {
- mg_error(c, "parse(%s) err %#x", opts->cert, -rc);
- goto fail;
- }
- s = mg_loadfile(fs, key);
- rc = mbedtls_pk_parse_key(&tls->pk, (uint8_t *) s.ptr, s.len + 1, NULL,
- 0 MGRNG);
- if (key[0] != '-') free((char *) s.ptr);
- if (rc != 0) {
- mg_error(c, "tls key(%s) %#x", key, -rc);
- goto fail;
- }
- rc = mbedtls_ssl_conf_own_cert(&tls->conf, &tls->cert, &tls->pk);
- if (rc != 0) {
- mg_error(c, "own cert %#x", -rc);
- goto fail;
- }
- }
- if ((rc = mbedtls_ssl_setup(&tls->ssl, &tls->conf)) != 0) {
- mg_error(c, "setup err %#x", -rc);
- goto fail;
- }
- c->tls = tls;
- c->is_tls = 1;
- c->is_tls_hs = 1;
- mbedtls_ssl_set_bio(&tls->ssl, c, mg_net_send, mg_net_recv, 0);
- if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) {
- mg_tls_handshake(c);
- }
- return;
- fail:
- mg_tls_free(c);
- }
- size_t mg_tls_pending(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- return tls == NULL ? 0 : mbedtls_ssl_get_bytes_avail(&tls->ssl);
- }
- long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len);
- if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE)
- return MG_IO_WAIT;
- if (n <= 0) return MG_IO_ERR;
- return n;
- }
- long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- long n = mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, len);
- if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE)
- return MG_IO_WAIT;
- if (n <= 0) return MG_IO_ERR;
- return n;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tls_openssl.c"
- #endif
- #if MG_ENABLE_OPENSSL
- static int mg_tls_err(struct mg_tls *tls, int res) {
- int err = SSL_get_error(tls->ssl, res);
- // We've just fetched the last error from the queue.
- // Now we need to clear the error queue. If we do not, then the following
- // can happen (actually reported):
- // - A new connection is accept()-ed with cert error (e.g. self-signed cert)
- // - Since all accept()-ed connections share listener's context,
- // - *ALL* SSL accepted connection report read error on the next poll cycle.
- // Thus a single errored connection can close all the rest, unrelated ones.
- // Clearing the error keeps the shared SSL_CTX in an OK state.
- if (err != 0) ERR_print_errors_fp(stderr);
- ERR_clear_error();
- if (err == SSL_ERROR_WANT_READ) return 0;
- if (err == SSL_ERROR_WANT_WRITE) return 0;
- return err;
- }
- void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
- struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls));
- const char *id = "mongoose";
- static unsigned char s_initialised = 0;
- int rc;
- if (tls == NULL) {
- mg_error(c, "TLS OOM");
- goto fail;
- }
- if (!s_initialised) {
- SSL_library_init();
- s_initialised++;
- }
- MG_DEBUG(("%lu Setting TLS, CA: %s, cert: %s, key: %s", c->id,
- opts->ca == NULL ? "null" : opts->ca,
- opts->cert == NULL ? "null" : opts->cert,
- opts->certkey == NULL ? "null" : opts->certkey));
- tls->ctx = c->is_client ? SSL_CTX_new(SSLv23_client_method())
- : SSL_CTX_new(SSLv23_server_method());
- if ((tls->ssl = SSL_new(tls->ctx)) == NULL) {
- mg_error(c, "SSL_new");
- goto fail;
- }
- SSL_set_session_id_context(tls->ssl, (const uint8_t *) id,
- (unsigned) strlen(id));
- // Disable deprecated protocols
- SSL_set_options(tls->ssl, SSL_OP_NO_SSLv2);
- SSL_set_options(tls->ssl, SSL_OP_NO_SSLv3);
- SSL_set_options(tls->ssl, SSL_OP_NO_TLSv1);
- SSL_set_options(tls->ssl, SSL_OP_NO_TLSv1_1);
- #ifdef MG_ENABLE_OPENSSL_NO_COMPRESSION
- SSL_set_options(tls->ssl, SSL_OP_NO_COMPRESSION);
- #endif
- #ifdef MG_ENABLE_OPENSSL_CIPHER_SERVER_PREFERENCE
- SSL_set_options(tls->ssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
- #endif
- if (opts->ca != NULL && opts->ca[0] != '\0') {
- SSL_set_verify(tls->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
- NULL);
- if ((rc = SSL_CTX_load_verify_locations(tls->ctx, opts->ca, NULL)) != 1) {
- mg_error(c, "load('%s') %d err %d", opts->ca, rc, mg_tls_err(tls, rc));
- goto fail;
- }
- }
- if (opts->cert != NULL && opts->cert[0] != '\0') {
- const char *key = opts->certkey;
- if (key == NULL) key = opts->cert;
- if ((rc = SSL_use_certificate_file(tls->ssl, opts->cert, 1)) != 1) {
- mg_error(c, "Invalid SSL cert, err %d", mg_tls_err(tls, rc));
- goto fail;
- } else if ((rc = SSL_use_PrivateKey_file(tls->ssl, key, 1)) != 1) {
- mg_error(c, "Invalid SSL key, err %d", mg_tls_err(tls, rc));
- goto fail;
- #if OPENSSL_VERSION_NUMBER > 0x10100000L
- } else if ((rc = SSL_use_certificate_chain_file(tls->ssl, opts->cert)) !=
- 1) {
- mg_error(c, "Invalid chain, err %d", mg_tls_err(tls, rc));
- goto fail;
- #endif
- } else {
- SSL_set_mode(tls->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- #if OPENSSL_VERSION_NUMBER > 0x10002000L
- SSL_set_ecdh_auto(tls->ssl, 1);
- #endif
- }
- }
- if (opts->ciphers != NULL) SSL_set_cipher_list(tls->ssl, opts->ciphers);
- #if OPENSSL_VERSION_NUMBER >= 0x10100000L
- if (opts->srvname.len > 0) {
- char *s = mg_mprintf("%.*s", (int) opts->srvname.len, opts->srvname.ptr);
- SSL_set1_host(tls->ssl, s);
- SSL_set_tlsext_host_name(tls->ssl, s);
- free(s);
- }
- #endif
- c->tls = tls;
- c->is_tls = 1;
- c->is_tls_hs = 1;
- if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) {
- mg_tls_handshake(c);
- }
- MG_DEBUG(("%lu SSL %s OK", c->id, c->is_accepted ? "accept" : "client"));
- return;
- fail:
- c->is_closing = 1;
- free(tls);
- }
- void mg_tls_handshake(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- int rc;
- SSL_set_fd(tls->ssl, (int) (size_t) c->fd);
- rc = c->is_client ? SSL_connect(tls->ssl) : SSL_accept(tls->ssl);
- if (rc == 1) {
- MG_DEBUG(("%lu success", c->id));
- c->is_tls_hs = 0;
- mg_call(c, MG_EV_TLS_HS, NULL);
- } else {
- int code = mg_tls_err(tls, rc);
- if (code != 0) mg_error(c, "tls hs: rc %d, err %d", rc, code);
- }
- }
- void mg_tls_free(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- if (tls == NULL) return;
- SSL_free(tls->ssl);
- SSL_CTX_free(tls->ctx);
- free(tls);
- c->tls = NULL;
- }
- size_t mg_tls_pending(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- return tls == NULL ? 0 : (size_t) SSL_pending(tls->ssl);
- }
- long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- int n = SSL_read(tls->ssl, buf, (int) len);
- if (n < 0 && mg_tls_err(tls, n) == 0) return MG_IO_WAIT;
- if (n <= 0) return MG_IO_ERR;
- return n;
- }
- long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- int n = SSL_write(tls->ssl, buf, (int) len);
- if (n < 0 && mg_tls_err(tls, n) == 0) return MG_IO_WAIT;
- if (n <= 0) return MG_IO_ERR;
- return n;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/url.c"
- #endif
- struct url {
- size_t key, user, pass, host, port, uri, end;
- };
- int mg_url_is_ssl(const char *url) {
- return strncmp(url, "wss:", 4) == 0 || strncmp(url, "https:", 6) == 0 ||
- strncmp(url, "mqtts:", 6) == 0 || strncmp(url, "ssl:", 4) == 0 ||
- strncmp(url, "tls:", 4) == 0;
- }
- static struct url urlparse(const char *url) {
- size_t i;
- struct url u;
- memset(&u, 0, sizeof(u));
- for (i = 0; url[i] != '\0'; i++) {
- if (url[i] == '/' && i > 0 && u.host == 0 && url[i - 1] == '/') {
- u.host = i + 1;
- u.port = 0;
- } else if (url[i] == ']') {
- u.port = 0; // IPv6 URLs, like http://[::1]/bar
- } else if (url[i] == ':' && u.port == 0 && u.uri == 0) {
- u.port = i + 1;
- } else if (url[i] == '@' && u.user == 0 && u.pass == 0 && u.uri == 0) {
- u.user = u.host;
- u.pass = u.port;
- u.host = i + 1;
- u.port = 0;
- } else if (url[i] == '/' && u.host && u.uri == 0) {
- u.uri = i;
- }
- }
- u.end = i;
- #if 0
- printf("[%s] %d %d %d %d %d\n", url, u.user, u.pass, u.host, u.port, u.uri);
- #endif
- return u;
- }
- struct mg_str mg_url_host(const char *url) {
- struct url u = urlparse(url);
- size_t n = u.port ? u.port - u.host - 1
- : u.uri ? u.uri - u.host
- : u.end - u.host;
- struct mg_str s = mg_str_n(url + u.host, n);
- return s;
- }
- const char *mg_url_uri(const char *url) {
- struct url u = urlparse(url);
- return u.uri ? url + u.uri : "/";
- }
- unsigned short mg_url_port(const char *url) {
- struct url u = urlparse(url);
- unsigned short port = 0;
- if (strncmp(url, "http:", 5) == 0 || strncmp(url, "ws:", 3) == 0) port = 80;
- if (strncmp(url, "wss:", 4) == 0 || strncmp(url, "https:", 6) == 0)
- port = 443;
- if (strncmp(url, "mqtt:", 5) == 0) port = 1883;
- if (strncmp(url, "mqtts:", 6) == 0) port = 8883;
- if (u.port) port = (unsigned short) atoi(url + u.port);
- return port;
- }
- struct mg_str mg_url_user(const char *url) {
- struct url u = urlparse(url);
- struct mg_str s = mg_str("");
- if (u.user && (u.pass || u.host)) {
- size_t n = u.pass ? u.pass - u.user - 1 : u.host - u.user - 1;
- s = mg_str_n(url + u.user, n);
- }
- return s;
- }
- struct mg_str mg_url_pass(const char *url) {
- struct url u = urlparse(url);
- struct mg_str s = mg_str_n("", 0UL);
- if (u.pass && u.host) {
- size_t n = u.host - u.pass - 1;
- s = mg_str_n(url + u.pass, n);
- }
- return s;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/util.c"
- #endif
- #if MG_ENABLE_CUSTOM_RANDOM
- #else
- void mg_random(void *buf, size_t len) {
- bool done = false;
- unsigned char *p = (unsigned char *) buf;
- #if MG_ARCH == MG_ARCH_ESP32
- while (len--) *p++ = (unsigned char) (esp_random() & 255);
- done = true;
- #elif MG_ARCH == MG_ARCH_WIN32
- #elif MG_ARCH == MG_ARCH_UNIX
- FILE *fp = fopen("/dev/urandom", "rb");
- if (fp != NULL) {
- if (fread(buf, 1, len, fp) == len) done = true;
- fclose(fp);
- }
- #endif
- // If everything above did not work, fallback to a pseudo random generator
- while (!done && len--) *p++ = (unsigned char) (rand() & 255);
- }
- #endif
- char *mg_random_str(char *buf, size_t len) {
- size_t i;
- mg_random(buf, len);
- for (i = 0; i < len; i++) {
- uint8_t c = ((uint8_t *) buf)[i] % 62U;
- buf[i] = i == len - 1 ? (char) '\0' // 0-terminate last byte
- : c < 26 ? (char) ('a' + c) // lowercase
- : c < 52 ? (char) ('A' + c - 26) // uppercase
- : (char) ('0' + c - 52); // numeric
- }
- return buf;
- }
- uint32_t mg_ntohl(uint32_t net) {
- uint8_t data[4] = {0, 0, 0, 0};
- memcpy(&data, &net, sizeof(data));
- return (((uint32_t) data[3]) << 0) | (((uint32_t) data[2]) << 8) |
- (((uint32_t) data[1]) << 16) | (((uint32_t) data[0]) << 24);
- }
- uint16_t mg_ntohs(uint16_t net) {
- uint8_t data[2] = {0, 0};
- memcpy(&data, &net, sizeof(data));
- return (uint16_t) ((uint16_t) data[1] | (((uint16_t) data[0]) << 8));
- }
- uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len) {
- static const uint32_t crclut[16] = {
- // table for polynomial 0xEDB88320 (reflected)
- 0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC, 0x76DC4190, 0x6B6B51F4,
- 0x4DB26158, 0x5005713C, 0xEDB88320, 0xF00F9344, 0xD6D6A3E8, 0xCB61B38C,
- 0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, 0xBDBDF21C};
- crc = ~crc;
- while (len--) {
- uint8_t byte = *(uint8_t *)buf++;
- crc = crclut[(crc ^ byte) & 0x0F] ^ (crc >> 4);
- crc = crclut[(crc ^ (byte >> 4)) & 0x0F] ^ (crc >> 4);
- }
- return ~crc;
- }
- static int isbyte(int n) {
- return n >= 0 && n <= 255;
- }
- static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
- int n, a, b, c, d, slash = 32, len = 0;
- if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 ||
- sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) &&
- isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0 &&
- slash < 33) {
- len = n;
- *net = ((uint32_t) a << 24) | ((uint32_t) b << 16) | ((uint32_t) c << 8) |
- (uint32_t) d;
- *mask = slash ? (uint32_t) (0xffffffffU << (32 - slash)) : (uint32_t) 0;
- }
- return len;
- }
- int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip) {
- struct mg_str k, v;
- int allowed = acl.len == 0 ? '+' : '-'; // If any ACL is set, deny by default
- while (mg_commalist(&acl, &k, &v)) {
- uint32_t net, mask;
- if (k.ptr[0] != '+' && k.ptr[0] != '-') return -1;
- if (parse_net(&k.ptr[1], &net, &mask) == 0) return -2;
- if ((mg_ntohl(remote_ip) & mask) == net) allowed = k.ptr[0];
- }
- return allowed == '+';
- }
- #if MG_ENABLE_CUSTOM_MILLIS
- #else
- uint64_t mg_millis(void) {
- #if MG_ARCH == MG_ARCH_WIN32
- return GetTickCount();
- #elif MG_ARCH == MG_ARCH_RP2040
- return time_us_64() / 1000;
- #elif MG_ARCH == MG_ARCH_ESP32
- return esp_timer_get_time() / 1000;
- #elif MG_ARCH == MG_ARCH_ESP8266 || MG_ARCH == MG_ARCH_FREERTOS
- return xTaskGetTickCount() * portTICK_PERIOD_MS;
- #elif MG_ARCH == MG_ARCH_AZURERTOS
- return tx_time_get() * (1000 /* MS per SEC */ / TX_TIMER_TICKS_PER_SECOND);
- #elif MG_ARCH == MG_ARCH_TIRTOS
- return (uint64_t) Clock_getTicks();
- #elif MG_ARCH == MG_ARCH_ZEPHYR
- return (uint64_t) k_uptime_get();
- #elif MG_ARCH == MG_ARCH_CMSIS_RTOS1
- return (uint64_t)rt_time_get();
- #elif MG_ARCH == MG_ARCH_CMSIS_RTOS2
- return (uint64_t)((osKernelGetTickCount() * 1000) / osKernelGetTickFreq());
- #elif MG_ARCH == MG_ARCH_UNIX && defined(__APPLE__)
- // Apple CLOCK_MONOTONIC_RAW is equivalent to CLOCK_BOOTTIME on linux
- // Apple CLOCK_UPTIME_RAW is equivalent to CLOCK_MONOTONIC_RAW on linux
- return clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1000000;
- #elif MG_ARCH == MG_ARCH_UNIX
- struct timespec ts = {0, 0};
- // See #1615 - prefer monotonic clock
- #if defined(CLOCK_MONOTONIC_RAW)
- // Raw hardware-based time that is not subject to NTP adjustment
- clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
- #elif defined(CLOCK_MONOTONIC)
- // Affected by the incremental adjustments performed by adjtime and NTP
- clock_gettime(CLOCK_MONOTONIC, &ts);
- #else
- // Affected by discontinuous jumps in the system time and by the incremental
- // adjustments performed by adjtime and NTP
- clock_gettime(CLOCK_REALTIME, &ts);
- #endif
- return ((uint64_t) ts.tv_sec * 1000 + (uint64_t) ts.tv_nsec / 1000000);
- #elif defined(ARDUINO)
- return (uint64_t) millis();
- #else
- return (uint64_t) (time(NULL) * 1000);
- #endif
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/ws.c"
- #endif
- struct ws_msg {
- uint8_t flags;
- size_t header_len;
- size_t data_len;
- };
- size_t mg_ws_vprintf(struct mg_connection *c, int op, const char *fmt,
- va_list *ap) {
- size_t len = c->send.len;
- size_t n = mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
- mg_ws_wrap(c, c->send.len - len, op);
- return n;
- }
- size_t mg_ws_printf(struct mg_connection *c, int op, const char *fmt, ...) {
- size_t len = 0;
- va_list ap;
- va_start(ap, fmt);
- len = mg_ws_vprintf(c, op, fmt, &ap);
- va_end(ap);
- return len;
- }
- static void ws_handshake(struct mg_connection *c, const struct mg_str *wskey,
- const struct mg_str *wsproto, const char *fmt,
- va_list *ap) {
- const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
- unsigned char sha[20], b64_sha[30];
- mg_sha1_ctx sha_ctx;
- mg_sha1_init(&sha_ctx);
- mg_sha1_update(&sha_ctx, (unsigned char *) wskey->ptr, wskey->len);
- mg_sha1_update(&sha_ctx, (unsigned char *) magic, 36);
- mg_sha1_final(sha, &sha_ctx);
- mg_base64_encode(sha, sizeof(sha), (char *) b64_sha);
- mg_xprintf(mg_pfn_iobuf, &c->send,
- "HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: %s\r\n",
- b64_sha);
- if (fmt != NULL) mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
- if (wsproto != NULL) {
- mg_printf(c, "Sec-WebSocket-Protocol: %.*s\r\n", (int) wsproto->len,
- wsproto->ptr);
- }
- mg_send(c, "\r\n", 2);
- }
- static uint32_t be32(const uint8_t *p) {
- return (((uint32_t) p[3]) << 0) | (((uint32_t) p[2]) << 8) |
- (((uint32_t) p[1]) << 16) | (((uint32_t) p[0]) << 24);
- }
- static size_t ws_process(uint8_t *buf, size_t len, struct ws_msg *msg) {
- size_t i, n = 0, mask_len = 0;
- memset(msg, 0, sizeof(*msg));
- if (len >= 2) {
- n = buf[1] & 0x7f; // Frame length
- mask_len = buf[1] & 128 ? 4 : 0; // last bit is a mask bit
- msg->flags = buf[0];
- if (n < 126 && len >= mask_len) {
- msg->data_len = n;
- msg->header_len = 2 + mask_len;
- } else if (n == 126 && len >= 4 + mask_len) {
- msg->header_len = 4 + mask_len;
- msg->data_len = (((size_t) buf[2]) << 8) | buf[3];
- } else if (len >= 10 + mask_len) {
- msg->header_len = 10 + mask_len;
- msg->data_len =
- (size_t) (((uint64_t) be32(buf + 2) << 32) + be32(buf + 6));
- }
- }
- // Sanity check, and integer overflow protection for the boundary check below
- // data_len should not be larger than 1 Gb
- if (msg->data_len > 1024 * 1024 * 1024) return 0;
- if (msg->header_len + msg->data_len > len) return 0;
- if (mask_len > 0) {
- uint8_t *p = buf + msg->header_len, *m = p - mask_len;
- for (i = 0; i < msg->data_len; i++) p[i] ^= m[i & 3];
- }
- return msg->header_len + msg->data_len;
- }
- static size_t mkhdr(size_t len, int op, bool is_client, uint8_t *buf) {
- size_t n = 0;
- buf[0] = (uint8_t) (op | 128);
- if (len < 126) {
- buf[1] = (unsigned char) len;
- n = 2;
- } else if (len < 65536) {
- uint16_t tmp = mg_htons((uint16_t) len);
- buf[1] = 126;
- memcpy(&buf[2], &tmp, sizeof(tmp));
- n = 4;
- } else {
- uint32_t tmp;
- buf[1] = 127;
- tmp = mg_htonl((uint32_t) (((uint64_t) len) >> 32));
- memcpy(&buf[2], &tmp, sizeof(tmp));
- tmp = mg_htonl((uint32_t) (len & 0xffffffffU));
- memcpy(&buf[6], &tmp, sizeof(tmp));
- n = 10;
- }
- if (is_client) {
- buf[1] |= 1 << 7; // Set masking flag
- mg_random(&buf[n], 4);
- n += 4;
- }
- return n;
- }
- static void mg_ws_mask(struct mg_connection *c, size_t len) {
- if (c->is_client && c->send.buf != NULL) {
- size_t i;
- uint8_t *p = c->send.buf + c->send.len - len, *mask = p - 4;
- for (i = 0; i < len; i++) p[i] ^= mask[i & 3];
- }
- }
- size_t mg_ws_send(struct mg_connection *c, const void *buf, size_t len,
- int op) {
- uint8_t header[14];
- size_t header_len = mkhdr(len, op, c->is_client, header);
- mg_send(c, header, header_len);
- MG_VERBOSE(("WS out: %d [%.*s]", (int) len, (int) len, buf));
- mg_send(c, buf, len);
- mg_ws_mask(c, len);
- return header_len + len;
- }
- static bool mg_ws_client_handshake(struct mg_connection *c) {
- int n = mg_http_get_request_len(c->recv.buf, c->recv.len);
- if (n < 0) {
- mg_error(c, "not http"); // Some just, not an HTTP request
- } else if (n > 0) {
- if (n < 15 || memcmp(c->recv.buf + 9, "101", 3) != 0) {
- mg_error(c, "handshake error");
- } else {
- struct mg_http_message hm;
- mg_http_parse((char *) c->recv.buf, c->recv.len, &hm);
- c->is_websocket = 1;
- mg_call(c, MG_EV_WS_OPEN, &hm);
- }
- mg_iobuf_del(&c->recv, 0, (size_t) n);
- } else {
- return true; // Request is not yet received, quit event handler
- }
- return false; // Continue event handler
- }
- static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data,
- void *fn_data) {
- struct ws_msg msg;
- size_t ofs = (size_t) c->pfn_data;
- // assert(ofs < c->recv.len);
- if (ev == MG_EV_READ) {
- if (c->is_client && !c->is_websocket && mg_ws_client_handshake(c)) return;
- while (ws_process(c->recv.buf + ofs, c->recv.len - ofs, &msg) > 0) {
- char *s = (char *) c->recv.buf + ofs + msg.header_len;
- struct mg_ws_message m = {{s, msg.data_len}, msg.flags};
- size_t len = msg.header_len + msg.data_len;
- uint8_t final = msg.flags & 128, op = msg.flags & 15;
- // MG_VERBOSE ("fin %d op %d len %d [%.*s]", final, op,
- // (int) m.data.len, (int) m.data.len, m.data.ptr));
- switch (op) {
- case WEBSOCKET_OP_CONTINUE:
- mg_call(c, MG_EV_WS_CTL, &m);
- break;
- case WEBSOCKET_OP_PING:
- MG_DEBUG(("%s", "WS PONG"));
- mg_ws_send(c, s, msg.data_len, WEBSOCKET_OP_PONG);
- mg_call(c, MG_EV_WS_CTL, &m);
- break;
- case WEBSOCKET_OP_PONG:
- mg_call(c, MG_EV_WS_CTL, &m);
- break;
- case WEBSOCKET_OP_TEXT:
- case WEBSOCKET_OP_BINARY:
- if (final) mg_call(c, MG_EV_WS_MSG, &m);
- break;
- case WEBSOCKET_OP_CLOSE:
- MG_DEBUG(("%lu WS CLOSE", c->id));
- mg_call(c, MG_EV_WS_CTL, &m);
- // Echo the payload of the received CLOSE message back to the sender
- mg_ws_send(c, m.data.ptr, m.data.len, WEBSOCKET_OP_CLOSE);
- c->is_draining = 1;
- break;
- default:
- // Per RFC6455, close conn when an unknown op is recvd
- mg_error(c, "unknown WS op %d", op);
- break;
- }
- // Handle fragmented frames: strip header, keep in c->recv
- if (final == 0 || op == 0) {
- if (op) ofs++, len--, msg.header_len--; // First frame
- mg_iobuf_del(&c->recv, ofs, msg.header_len); // Strip header
- len -= msg.header_len;
- ofs += len;
- c->pfn_data = (void *) ofs;
- // MG_INFO(("FRAG %d [%.*s]", (int) ofs, (int) ofs, c->recv.buf));
- }
- // Remove non-fragmented frame
- if (final && op) mg_iobuf_del(&c->recv, ofs, len);
- // Last chunk of the fragmented frame
- if (final && !op) {
- m.flags = c->recv.buf[0];
- m.data = mg_str_n((char *) &c->recv.buf[1], (size_t) (ofs - 1));
- mg_call(c, MG_EV_WS_MSG, &m);
- mg_iobuf_del(&c->recv, 0, ofs);
- ofs = 0;
- c->pfn_data = NULL;
- }
- }
- }
- (void) fn_data;
- (void) ev_data;
- }
- struct mg_connection *mg_ws_connect(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data,
- const char *fmt, ...) {
- struct mg_connection *c = mg_connect(mgr, url, fn, fn_data);
- if (c != NULL) {
- char nonce[16], key[30];
- struct mg_str host = mg_url_host(url);
- mg_random(nonce, sizeof(nonce));
- mg_base64_encode((unsigned char *) nonce, sizeof(nonce), key);
- mg_xprintf(mg_pfn_iobuf, &c->send,
- "GET %s HTTP/1.1\r\n"
- "Upgrade: websocket\r\n"
- "Host: %.*s\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "Sec-WebSocket-Key: %s\r\n",
- mg_url_uri(url), (int) host.len, host.ptr, key);
- if (fmt != NULL) {
- va_list ap;
- va_start(ap, fmt);
- mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, &ap);
- va_end(ap);
- }
- mg_xprintf(mg_pfn_iobuf, &c->send, "\r\n");
- c->pfn = mg_ws_cb;
- c->pfn_data = NULL;
- }
- return c;
- }
- void mg_ws_upgrade(struct mg_connection *c, struct mg_http_message *hm,
- const char *fmt, ...) {
- struct mg_str *wskey = mg_http_get_header(hm, "Sec-WebSocket-Key");
- c->pfn = mg_ws_cb;
- c->pfn_data = NULL;
- if (wskey == NULL) {
- mg_http_reply(c, 426, "", "WS upgrade expected\n");
- c->is_draining = 1;
- } else {
- struct mg_str *wsproto = mg_http_get_header(hm, "Sec-WebSocket-Protocol");
- va_list ap;
- va_start(ap, fmt);
- ws_handshake(c, wskey, wsproto, fmt, &ap);
- va_end(ap);
- c->is_websocket = 1;
- c->is_resp = 0;
- mg_call(c, MG_EV_WS_OPEN, hm);
- }
- }
- size_t mg_ws_wrap(struct mg_connection *c, size_t len, int op) {
- uint8_t header[14], *p;
- size_t header_len = mkhdr(len, op, c->is_client, header);
- // NOTE: order of operations is important!
- mg_iobuf_add(&c->send, c->send.len, NULL, header_len);
- p = &c->send.buf[c->send.len - len]; // p points to data
- memmove(p, p - header_len, len); // Shift data
- memcpy(p - header_len, header, header_len); // Prepend header
- mg_ws_mask(c, len); // Mask data
- return c->send.len;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tcpip/driver_nxpimxrt1020.c"
- #endif
- /*
- * Todo
- * This driver doesn't support 10M line autoconfiguration yet.
- * Packets aren't sent if the link negociated 10M line.
- * todo: MAC back auto reconfiguration.
- */
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_IMXRT1020)
- struct imx_rt1020_enet {
- volatile uint32_t RESERVED0, EIR, EIMR, RESERVED1, RDAR, TDAR, RESERVED2[3], ECR, RESERVED3[6], MMFR, MSCR, RESERVED4[7], MIBC, RESERVED5[7], RCR, RESERVED6[15], TCR, RESERVED7[7], PALR, PAUR, OPD, TXIC0, TXIC1, TXIC2, RESERVED8, RXIC0, RXIC1, RXIC2, RESERVED9[3], IAUR, IALR, GAUR, GALR, RESERVED10[7], TFWR, RESERVED11[14], RDSR, TDSR, MRBR[2], RSFL, RSEM, RAEM, RAFL, TSEM, TAEM, TAFL, TIPG, FTRL, RESERVED12[3], TACC, RACC, RESERVED13[15], RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2048, RMON_T_GTE2048, RMON_T_OCTETS, IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, IEEE_T_FDXFC, IEEE_T_OCTETS_OK, RESERVED14[3], RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, RESERVED15, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, RMON_R_GTE2048, RMON_R_OCTETS, IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, IEEE_R_FDXFC, IEEE_R_OCTETS_OK, RESERVED16[71], ATCR, ATVR, ATOFF, ATPER, ATCOR, ATINC, ATSTMP, RESERVED17[122], TGSR, TCSR0, TCCR0, TCSR1, TCCR1, TCSR2, TCCR2, TCSR3;
- };
- #undef ENET
- #define ENET ((struct imx_rt1020_enet *) (uintptr_t) 0x402D8000u)
- #undef BIT
- #define BIT(x) ((uint32_t) 1 << (x))
- #define ENET_RXBUFF_SIZE 1536 // 1522 Buffer must be 64bits aligned
- #define ENET_TXBUFF_SIZE 1536 // 1522 hence set to 0x600 (1536)
- #define ENET_RXBD_NUM (4)
- #define ENET_TXBD_NUM (4)
- const uint32_t EIMR_RX_ERR = 0x2400000; // Intr mask RXF+EBERR
- void ETH_IRQHandler(void);
- static bool mg_tcpip_driver_imxrt1020_init(struct mg_tcpip_if *ifp);
- static void wait_phy_complete(void);
- static struct mg_tcpip_if *s_ifp; // MIP interface
- static size_t mg_tcpip_driver_imxrt1020_tx(const void *, size_t , struct mg_tcpip_if *);
- static bool mg_tcpip_driver_imxrt1020_up(struct mg_tcpip_if *ifp);
- enum { IMXRT1020_PHY_ADDR = 0x02, IMXRT1020_PHY_BCR = 0, IMXRT1020_PHY_BSR = 1 }; // PHY constants
- void delay(uint32_t);
- void delay (uint32_t di) {
- volatile int dno = 0; // Prevent optimization
- for (uint32_t i = 0; i < di; i++)
- for (int j=0; j<20; j++) // PLLx20 (500 MHz/24MHz)
- dno++;
- }
- static void wait_phy_complete(void) {
- delay(0x00010000);
- const uint32_t delay_max = 0x00100000;
- uint32_t delay_cnt = 0;
- while (!(ENET->EIR & BIT(23)) && (delay_cnt < delay_max))
- {delay_cnt++;}
- ENET->EIR |= BIT(23); // MII interrupt clear
- }
- static uint32_t imxrt1020_eth_read_phy(uint8_t addr, uint8_t reg) {
- ENET->EIR |= BIT(23); // MII interrupt clear
- uint32_t mask_phy_adr_reg = 0x1f; // 0b00011111: Ensure we write 5 bits (Phy address & register)
- uint32_t phy_transaction = 0x00;
- phy_transaction = (0x1 << 30) \
- | (0x2 << 28) \
- | ((uint32_t)(addr & mask_phy_adr_reg) << 23) \
- | ((uint32_t)(reg & mask_phy_adr_reg) << 18) \
- | (0x2 << 16);
- ENET->MMFR = phy_transaction;
- wait_phy_complete();
- return (ENET->MMFR & 0x0000ffff);
- }
- static void imxrt1020_eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
- ENET->EIR |= BIT(23); // MII interrupt clear
- uint8_t mask_phy_adr_reg = 0x1f; // 0b00011111: Ensure we write 5 bits (Phy address & register)
- uint32_t mask_phy_data = 0x0000ffff; // Ensure we write 16 bits (data)
- addr &= mask_phy_adr_reg;
- reg &= mask_phy_adr_reg;
- val &= mask_phy_data;
- uint32_t phy_transaction = 0x00;
- phy_transaction = (uint32_t)(0x1 << 30) \
- | (uint32_t)(0x1 << 28) \
- | (uint32_t)(addr << 23) \
- | (uint32_t)(reg << 18) \
- | (uint32_t)(0x2 << 16) \
- | (uint32_t)(val);
- ENET->MMFR = phy_transaction;
- wait_phy_complete();
- }
- // FEC RX/TX descriptors (Enhanced descriptor not enabled)
- // Descriptor buffer structure, little endian
- typedef struct enet_bd_struct_def
- {
- uint16_t length; // Data length
- uint16_t control; // Control and status
- uint32_t *buffer; // Data ptr
- } enet_bd_struct_t;
- // Descriptor and buffer globals, in non-cached area, 64 bits aligned.
- __attribute__((section("NonCacheable,\"aw\",%nobits @"))) enet_bd_struct_t rx_buffer_descriptor[(ENET_RXBD_NUM)] __attribute__((aligned((64U))));
- __attribute__((section("NonCacheable,\"aw\",%nobits @"))) enet_bd_struct_t tx_buffer_descriptor[(ENET_TXBD_NUM)] __attribute__((aligned((64U))));
- uint8_t rx_data_buffer[(ENET_RXBD_NUM)][((unsigned int)(((ENET_RXBUFF_SIZE)) + (((64U))-1U)) & (unsigned int)(~(unsigned int)(((64U))-1U)))] __attribute__((aligned((64U))));
- uint8_t tx_data_buffer[(ENET_TXBD_NUM)][((unsigned int)(((ENET_TXBUFF_SIZE)) + (((64U))-1U)) & (unsigned int)(~(unsigned int)(((64U))-1U)))] __attribute__((aligned((64U))));
- // Initialise driver imx_rt1020
- // static bool mg_tcpip_driver_imxrt1020_init(uint8_t *mac, void *data) { // VO
- static bool mg_tcpip_driver_imxrt1020_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_imxrt1020_data *d = (struct mg_tcpip_driver_imxrt1020_data *) ifp->driver_data;
- s_ifp = ifp;
- // ENET Reset, wait complete
- ENET->ECR |= BIT(0);
- while((ENET->ECR & BIT(0)) != 0) {}
- // Re-latches the pin strapping pin values
- ENET->ECR |= BIT(0);
- while((ENET->ECR & BIT(0)) != 0) {}
- // Setup MII/RMII MDC clock divider (<= 2.5MHz).
- ENET->MSCR = 0x130; // HOLDTIME 2 clk, Preamble enable, MDC MII_Speed Div 0x30
- imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR, 0x8000); // PHY W @0x00 D=0x8000 Soft reset
- while (imxrt1020_eth_read_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BSR) & BIT(15)) {delay(0x5000);} // Wait finished poll 10ms
- // PHY: Start Link
- {
- imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR, 0x1200); // PHY W @0x00 D=0x1200 Autonego enable + start
- imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, 0x1f, 0x8180); // PHY W @0x1f D=0x8180 Ref clock 50 MHz at XI input
- uint32_t bcr = imxrt1020_eth_read_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR);
- bcr &= ~BIT(10); // Isolation -> Normal
- imxrt1020_eth_write_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BCR, bcr);
- }
- // Disable ENET
- ENET->ECR = 0x0; // Disable before configuration
- // Configure ENET
- ENET->RCR = 0x05ee0104; // #CRCFWD=0 (CRC kept in frame) + RMII + MII Enable
-
- ENET->TCR = BIT(8) | BIT(2); // Addins (MAC address from PAUR+PALR) + Full duplex enable
- //ENET->TFWR = BIT(8); // Store And Forward Enable, 64 bytes (minimize tx latency)
- // Configure descriptors and buffers
- // RX
- for (int i = 0; i < ENET_RXBD_NUM; i++) {
- // Wrap last descriptor buffer ptr
- rx_buffer_descriptor[i].control = (BIT(15) | ((i<(ENET_RXBD_NUM-1))?0:BIT(13))); // E+(W*)
- rx_buffer_descriptor[i].buffer = (uint32_t *)rx_data_buffer[i];
- }
- // TX
- for (int i = 0; i < ENET_TXBD_NUM; i++) {
- // Wrap last descriptor buffer ptr
- tx_buffer_descriptor[i].control = ((i<(ENET_RXBD_NUM-1))?0:BIT(13)) | BIT(10); // (W*)+TC
- tx_buffer_descriptor[i].buffer = (uint32_t *)tx_data_buffer[i];
- }
- // Continue ENET configuration
- ENET->RDSR = (uint32_t)(uintptr_t)rx_buffer_descriptor;
- ENET->TDSR = (uint32_t)(uintptr_t)tx_buffer_descriptor;
- ENET->MRBR[0] = ENET_RXBUFF_SIZE; // Same size for RX/TX buffers
- // MAC address filtering (bytes in reversed order)
- ENET->PAUR = ((uint32_t) ifp->mac[4] << 24U) | (uint32_t) ifp->mac[5] << 16U;
- ENET->PALR = (uint32_t) (ifp->mac[0] << 24U) | ((uint32_t) ifp->mac[1] << 16U) |
- ((uint32_t) ifp->mac[2] << 8U) | ifp->mac[3];
- // Init Hash tables (mac filtering)
- ENET->IAUR = 0; // Unicast
- ENET->IALR = 0;
- ENET->GAUR = 0; // Multicast
- ENET->GALR = 0;
- // Set ENET Online
- ENET->ECR |= BIT(8); // ENET Set Little-endian + (FEC buffer desc.)
- ENET->ECR |= BIT(1); // Enable
- // Set interrupt mask
- ENET->EIMR = EIMR_RX_ERR;
- // RX Descriptor activation
- ENET->RDAR = BIT(24); // Activate Receive Descriptor
- return true;
- }
- // Transmit frame
- static uint32_t s_rt1020_txno;
- static size_t mg_tcpip_driver_imxrt1020_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
- if (len > sizeof(tx_data_buffer[ENET_TXBD_NUM])) {
- // MG_ERROR(("Frame too big, %ld", (long) len));
- len = 0; // Frame is too big
- } else if ((tx_buffer_descriptor[s_rt1020_txno].control & BIT(15))) {
- MG_ERROR(("No free descriptors"));
- // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) ETH->DMASR);
- len = 0; // All descriptors are busy, fail
- } else {
- memcpy(tx_data_buffer[s_rt1020_txno], buf, len); // Copy data
- tx_buffer_descriptor[s_rt1020_txno].length = (uint16_t) len; // Set data len
- tx_buffer_descriptor[s_rt1020_txno].control |= (uint16_t)(BIT(10)); // TC (transmit CRC)
- // tx_buffer_descriptor[s_rt1020_txno].control &= (uint16_t)(BIT(14) | BIT(12)); // Own doesn't affect HW
- tx_buffer_descriptor[s_rt1020_txno].control |= (uint16_t)(BIT(15) | BIT(11)); // R+L (ready+last)
- ENET->TDAR = BIT(24); // Descriptor updated. Hand over to DMA.
- // INFO
- // Relevant Descriptor bits: 15(R) Ready
- // 11(L) last in frame
- // 10(TC) transmis CRC
- // __DSB(); // ARM errata 838869 Cortex-M4, M4F, M7, M7F: "store immediate overlapping
- // exception" return might vector to incorrect interrupt.
- if (++s_rt1020_txno >= ENET_TXBD_NUM) s_rt1020_txno = 0;
- }
- (void) ifp;
- return len;
- }
- // IRQ (RX)
- static uint32_t s_rt1020_rxno;
- void ENET_IRQHandler(void) {
- ENET->EIMR = 0; // Mask interrupts.
- uint32_t eir = ENET->EIR; // Read EIR
- ENET->EIR = 0xffffffff; // Clear interrupts
- if (eir & EIMR_RX_ERR) // Global mask used
- {
- if (rx_buffer_descriptor[s_rt1020_rxno].control & BIT(15)) {
- ENET->EIMR = EIMR_RX_ERR; // Enable interrupts
- return; // Empty? -> exit.
- }
- // Read inframes
- else { // Frame received, loop
- for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
- if (rx_buffer_descriptor[s_rt1020_rxno].control & BIT(15)) break; // exit when done
- // Process if CRC OK and frame not truncated
- if (!(rx_buffer_descriptor[s_rt1020_rxno].control & (BIT(2) | BIT(0)))) {
- uint32_t len = (rx_buffer_descriptor[s_rt1020_rxno].length);
- mg_tcpip_qwrite(rx_buffer_descriptor[s_rt1020_rxno].buffer, len > 4 ? len - 4 : len, s_ifp);
- }
- rx_buffer_descriptor[s_rt1020_rxno].control |= BIT(15); // Inform DMA RX is empty
- if (++s_rt1020_rxno >= ENET_RXBD_NUM) s_rt1020_rxno = 0;
- }
- }
- }
- ENET->EIMR = EIMR_RX_ERR; // Enable interrupts
- }
- // Up/down status
- static bool mg_tcpip_driver_imxrt1020_up(struct mg_tcpip_if *ifp) {
- uint32_t bsr = imxrt1020_eth_read_phy(IMXRT1020_PHY_ADDR, IMXRT1020_PHY_BSR);
- (void) ifp;
- return bsr & BIT(2) ? 1 : 0;
- }
- // API
- struct mg_tcpip_driver mg_tcpip_driver_imxrt1020 = {
- mg_tcpip_driver_imxrt1020_init, mg_tcpip_driver_imxrt1020_tx, NULL,
- mg_tcpip_driver_imxrt1020_up};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tcpip/driver_stm32.c"
- #endif
- #if MG_ENABLE_TCPIP && MG_ENABLE_DRIVER_STM32
- struct stm32_eth {
- volatile uint32_t MACCR, MACFFR, MACHTHR, MACHTLR, MACMIIAR, MACMIIDR, MACFCR,
- MACVLANTR, RESERVED0[2], MACRWUFFR, MACPMTCSR, RESERVED1, MACDBGR, MACSR,
- MACIMR, MACA0HR, MACA0LR, MACA1HR, MACA1LR, MACA2HR, MACA2LR, MACA3HR,
- MACA3LR, RESERVED2[40], MMCCR, MMCRIR, MMCTIR, MMCRIMR, MMCTIMR,
- RESERVED3[14], MMCTGFSCCR, MMCTGFMSCCR, RESERVED4[5], MMCTGFCR,
- RESERVED5[10], MMCRFCECR, MMCRFAECR, RESERVED6[10], MMCRGUFCR,
- RESERVED7[334], PTPTSCR, PTPSSIR, PTPTSHR, PTPTSLR, PTPTSHUR, PTPTSLUR,
- PTPTSAR, PTPTTHR, PTPTTLR, RESERVED8, PTPTSSR, PTPPPSCR, RESERVED9[564],
- DMABMR, DMATPDR, DMARPDR, DMARDLAR, DMATDLAR, DMASR, DMAOMR, DMAIER,
- DMAMFBOCR, DMARSWTR, RESERVED10[8], DMACHTDR, DMACHRDR, DMACHTBAR,
- DMACHRBAR;
- };
- #undef ETH
- #define ETH ((struct stm32_eth *) (uintptr_t) 0x40028000)
- #undef BIT
- #define BIT(x) ((uint32_t) 1 << (x))
- #define ETH_PKT_SIZE 1540 // Max frame size
- #define ETH_DESC_CNT 4 // Descriptors count
- #define ETH_DS 4 // Descriptor size (words)
- static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
- static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
- static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
- static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
- static uint8_t s_txno; // Current TX descriptor
- static uint8_t s_rxno; // Current RX descriptor
- static struct mg_tcpip_if *s_ifp; // MIP interface
- enum { PHY_ADDR = 0, PHY_BCR = 0, PHY_BSR = 1, PHY_CSCR = 31 };
- static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
- ETH->MACMIIAR &= (7 << 2);
- ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6);
- ETH->MACMIIAR |= BIT(0);
- while (ETH->MACMIIAR & BIT(0)) (void) 0;
- return ETH->MACMIIDR;
- }
- static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
- ETH->MACMIIDR = val;
- ETH->MACMIIAR &= (7 << 2);
- ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | BIT(1);
- ETH->MACMIIAR |= BIT(0);
- while (ETH->MACMIIAR & BIT(0)) (void) 0;
- }
- static uint32_t get_hclk(void) {
- struct rcc {
- volatile uint32_t CR, PLLCFGR, CFGR;
- } *rcc = (struct rcc *) 0x40023800;
- uint32_t clk = 0, hsi = 16000000 /* 16 MHz */, hse = 8000000 /* 8MHz */;
- if (rcc->CFGR & (1 << 2)) {
- clk = hse;
- } else if (rcc->CFGR & (1 << 3)) {
- uint32_t vco, m, n, p;
- m = (rcc->PLLCFGR & (0x3f << 0)) >> 0;
- n = (rcc->PLLCFGR & (0x1ff << 6)) >> 6;
- p = (((rcc->PLLCFGR & (3 << 16)) >> 16) + 1) * 2;
- clk = (rcc->PLLCFGR & (1 << 22)) ? hse : hsi;
- vco = (uint32_t) ((uint64_t) clk * n / m);
- clk = vco / p;
- } else {
- clk = hsi;
- }
- uint32_t hpre = (rcc->CFGR & (15 << 4)) >> 4;
- if (hpre < 8) return clk;
- uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
- return ((uint32_t) clk) >> ahbptab[hpre - 8];
- }
- // Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3,
- // it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived
- // from the HSI (internal RC), and it can go above specs, the datasheets
- // specify a range of frequencies and activate one of a series of dividers to
- // keep the MDC clock safely below 2.5MHz. We guess a divider setting based on
- // HCLK with a +5% drift. If the user uses a different clock from our
- // defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx
- // (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift)
- static int guess_mdc_cr(void) {
- uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMIIAR::CR values
- uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers
- uint32_t hclk = get_hclk(); // Guess system HCLK
- int result = -1; // Invalid CR value
- if (hclk < 25000000) {
- MG_ERROR(("HCLK too low"));
- } else {
- for (int i = 0; i < 6; i++) {
- if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
- result = crs[i];
- break;
- }
- }
- if (result < 0) MG_ERROR(("HCLK too high"));
- }
- MG_DEBUG(("HCLK: %u, CR: %d", hclk, result));
- return result;
- }
- static bool mg_tcpip_driver_stm32_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_stm32_data *d =
- (struct mg_tcpip_driver_stm32_data *) ifp->driver_data;
- s_ifp = ifp;
- // Init RX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_rxdesc[i][0] = BIT(31); // Own
- s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | BIT(14); // 2nd address chained
- s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
- s_rxdesc[i][3] =
- (uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain
- }
- // Init TX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
- s_txdesc[i][3] =
- (uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain
- }
- ETH->DMABMR |= BIT(0); // Software reset
- while ((ETH->DMABMR & BIT(0)) != 0) (void) 0; // Wait until done
- // Set MDC clock divider. If user told us the value, use it. Otherwise, guess
- int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
- ETH->MACMIIAR = ((uint32_t) cr & 7) << 2;
- // NOTE(cpq): we do not use extended descriptor bit 7, and do not use
- // hardware checksum. Therefore, descriptor size is 4, not 8
- // ETH->DMABMR = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25);
- ETH->MACIMR = BIT(3) | BIT(9); // Mask timestamp & PMT IT
- ETH->MACFCR = BIT(7); // Disable zero quarta pause
- // ETH->MACFFR = BIT(31); // Receive all
- eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY
- eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation
- ETH->DMARDLAR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors
- ETH->DMATDLAR = (uint32_t) (uintptr_t) s_txdesc; // RX descriptors
- ETH->DMAIER = BIT(6) | BIT(16); // RIE, NISE
- ETH->MACCR = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast
- ETH->DMAOMR = BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF
- // MAC address filtering
- ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
- ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
- ((uint32_t) ifp->mac[2] << 16) |
- ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
- return true;
- }
- static size_t mg_tcpip_driver_stm32_tx(const void *buf, size_t len,
- struct mg_tcpip_if *ifp) {
- if (len > sizeof(s_txbuf[s_txno])) {
- MG_ERROR(("Frame too big, %ld", (long) len));
- len = 0; // Frame is too big
- } else if ((s_txdesc[s_txno][0] & BIT(31))) {
- ifp->nerr++;
- MG_ERROR(("No free descriptors"));
- // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) ETH->DMASR);
- len = 0; // All descriptors are busy, fail
- } else {
- memcpy(s_txbuf[s_txno], buf, len); // Copy data
- s_txdesc[s_txno][1] = (uint32_t) len; // Set data len
- s_txdesc[s_txno][0] = BIT(20) | BIT(28) | BIT(29) | BIT(30); // Chain,FS,LS
- s_txdesc[s_txno][0] |= BIT(31); // Set OWN bit - let DMA take over
- if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
- }
- ETH->DMASR = BIT(2) | BIT(5); // Clear any prior TBUS/TUS
- ETH->DMATPDR = 0; // and resume
- return len;
- }
- static bool mg_tcpip_driver_stm32_up(struct mg_tcpip_if *ifp) {
- uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
- bool up = bsr & BIT(2) ? 1 : 0;
- if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
- uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR);
- uint32_t maccr = ETH->MACCR | BIT(14) | BIT(11); // 100M, Full-duplex
- if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M
- if ((scsr & BIT(4)) == 0) maccr &= ~BIT(11); // Half-duplex
- ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
- MG_DEBUG(("Link is %uM %s-duplex", maccr & BIT(14) ? 100 : 10,
- maccr & BIT(11) ? "full" : "half"));
- }
- return up;
- }
- void ETH_IRQHandler(void);
- void ETH_IRQHandler(void) {
- if (ETH->DMASR & BIT(6)) { // Frame received, loop
- ETH->DMASR = BIT(16) | BIT(6); // Clear flag
- for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
- if (s_rxdesc[s_rxno][0] & BIT(31)) break; // exit when done
- if (((s_rxdesc[s_rxno][0] & (BIT(8) | BIT(9))) == (BIT(8) | BIT(9))) &&
- !(s_rxdesc[s_rxno][0] & BIT(15))) { // skip partial/errored frames
- uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1));
- // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
- // ETH->DMASR);
- mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
- }
- s_rxdesc[s_rxno][0] = BIT(31);
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
- }
- }
- ETH->DMASR = BIT(7); // Clear possible RBUS while processing
- ETH->DMARPDR = 0; // and resume RX
- }
- struct mg_tcpip_driver mg_tcpip_driver_stm32 = {mg_tcpip_driver_stm32_init,
- mg_tcpip_driver_stm32_tx, NULL,
- mg_tcpip_driver_stm32_up};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tcpip/driver_stm32h.c"
- #endif
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32H) && \
- MG_ENABLE_DRIVER_STM32H
- struct stm32h_eth {
- volatile uint32_t MACCR, MACECR, MACPFR, MACWTR, MACHT0R, MACHT1R,
- RESERVED1[14], MACVTR, RESERVED2, MACVHTR, RESERVED3, MACVIR, MACIVIR,
- RESERVED4[2], MACTFCR, RESERVED5[7], MACRFCR, RESERVED6[7], MACISR,
- MACIER, MACRXTXSR, RESERVED7, MACPCSR, MACRWKPFR, RESERVED8[2], MACLCSR,
- MACLTCR, MACLETR, MAC1USTCR, RESERVED9[12], MACVR, MACDR, RESERVED10,
- MACHWF0R, MACHWF1R, MACHWF2R, RESERVED11[54], MACMDIOAR, MACMDIODR,
- RESERVED12[2], MACARPAR, RESERVED13[59], MACA0HR, MACA0LR, MACA1HR,
- MACA1LR, MACA2HR, MACA2LR, MACA3HR, MACA3LR, RESERVED14[248], MMCCR,
- MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, RESERVED15[14], MMCTSCGPR, MMCTMCGPR,
- RESERVED16[5], MMCTPCGR, RESERVED17[10], MMCRCRCEPR, MMCRAEPR,
- RESERVED18[10], MMCRUPGR, RESERVED19[9], MMCTLPIMSTR, MMCTLPITCR,
- MMCRLPIMSTR, MMCRLPITCR, RESERVED20[65], MACL3L4C0R, MACL4A0R,
- RESERVED21[2], MACL3A0R0R, MACL3A1R0R, MACL3A2R0R, MACL3A3R0R,
- RESERVED22[4], MACL3L4C1R, MACL4A1R, RESERVED23[2], MACL3A0R1R,
- MACL3A1R1R, MACL3A2R1R, MACL3A3R1R, RESERVED24[108], MACTSCR, MACSSIR,
- MACSTSR, MACSTNR, MACSTSUR, MACSTNUR, MACTSAR, RESERVED25, MACTSSR,
- RESERVED26[3], MACTTSSNR, MACTTSSSR, RESERVED27[2], MACACR, RESERVED28,
- MACATSNR, MACATSSR, MACTSIACR, MACTSEACR, MACTSICNR, MACTSECNR,
- RESERVED29[4], MACPPSCR, RESERVED30[3], MACPPSTTSR, MACPPSTTNR, MACPPSIR,
- MACPPSWR, RESERVED31[12], MACPOCR, MACSPI0R, MACSPI1R, MACSPI2R, MACLMIR,
- RESERVED32[11], MTLOMR, RESERVED33[7], MTLISR, RESERVED34[55], MTLTQOMR,
- MTLTQUR, MTLTQDR, RESERVED35[8], MTLQICSR, MTLRQOMR, MTLRQMPOCR, MTLRQDR,
- RESERVED36[177], DMAMR, DMASBMR, DMAISR, DMADSR, RESERVED37[60], DMACCR,
- DMACTCR, DMACRCR, RESERVED38[2], DMACTDLAR, RESERVED39, DMACRDLAR,
- DMACTDTPR, RESERVED40, DMACRDTPR, DMACTDRLR, DMACRDRLR, DMACIER,
- DMACRIWTR, DMACSFCSR, RESERVED41, DMACCATDR, RESERVED42, DMACCARDR,
- RESERVED43, DMACCATBR, RESERVED44, DMACCARBR, DMACSR, RESERVED45[2],
- DMACMFCR;
- };
- #undef ETH
- #define ETH \
- ((struct stm32h_eth *) (uintptr_t) (0x40000000UL + 0x00020000UL + 0x8000UL))
- #undef BIT
- #define BIT(x) ((uint32_t) 1 << (x))
- #define ETH_PKT_SIZE 1540 // Max frame size
- #define ETH_DESC_CNT 4 // Descriptors count
- #define ETH_DS 4 // Descriptor size (words)
- static volatile uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
- static volatile uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
- static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
- static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
- static struct mg_tcpip_if *s_ifp; // MIP interface
- enum {
- PHY_ADDR = 0,
- PHY_BCR = 0,
- PHY_BSR = 1,
- PHY_CSCR = 31
- }; // PHY constants
- static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
- ETH->MACMDIOAR &= (0xF << 8);
- ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 3 << 2;
- ETH->MACMDIOAR |= BIT(0);
- while (ETH->MACMDIOAR & BIT(0)) (void) 0;
- return ETH->MACMDIODR;
- }
- static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
- ETH->MACMDIODR = val;
- ETH->MACMDIOAR &= (0xF << 8);
- ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 1 << 2;
- ETH->MACMDIOAR |= BIT(0);
- while (ETH->MACMDIOAR & BIT(0)) (void) 0;
- }
- static uint32_t get_hclk(void) {
- struct rcc {
- volatile uint32_t CR, HSICFGR, CRRCR, CSICFGR, CFGR, RESERVED1, D1CFGR,
- D2CFGR, D3CFGR, RESERVED2, PLLCKSELR, PLLCFGR, PLL1DIVR, PLL1FRACR,
- PLL2DIVR, PLL2FRACR, PLL3DIVR, PLL3FRACR, RESERVED3, D1CCIPR, D2CCIP1R,
- D2CCIP2R, D3CCIPR, RESERVED4, CIER, CIFR, CICR, RESERVED5, BDCR, CSR,
- RESERVED6, AHB3RSTR, AHB1RSTR, AHB2RSTR, AHB4RSTR, APB3RSTR, APB1LRSTR,
- APB1HRSTR, APB2RSTR, APB4RSTR, GCR, RESERVED8, D3AMR, RESERVED11[9],
- RSR, AHB3ENR, AHB1ENR, AHB2ENR, AHB4ENR, APB3ENR, APB1LENR, APB1HENR,
- APB2ENR, APB4ENR, RESERVED12, AHB3LPENR, AHB1LPENR, AHB2LPENR,
- AHB4LPENR, APB3LPENR, APB1LLPENR, APB1HLPENR, APB2LPENR, APB4LPENR,
- RESERVED13[4];
- } *rcc = ((struct rcc *) (0x40000000 + 0x18020000 + 0x4400));
- uint32_t clk = 0, hsi = 64000000 /* 64 MHz */, hse = 8000000 /* 8MHz */,
- csi = 4000000 /* 4MHz */;
- unsigned int sel = (rcc->CFGR & (7 << 3)) >> 3;
- if (sel == 1) {
- clk = csi;
- } else if (sel == 2) {
- clk = hse;
- } else if (sel == 3) {
- uint32_t vco, m, n, p;
- unsigned int src = (rcc->PLLCKSELR & (3 << 0)) >> 0;
- m = ((rcc->PLLCKSELR & (0x3F << 4)) >> 4);
- n = ((rcc->PLL1DIVR & (0x1FF << 0)) >> 0) + 1 +
- ((rcc->PLLCFGR & BIT(0)) ? 1 : 0); // round-up in fractional mode
- p = ((rcc->PLL1DIVR & (0x7F << 9)) >> 9) + 1;
- if (src == 1) {
- clk = csi;
- } else if (src == 2) {
- clk = hse;
- } else {
- clk = hsi;
- clk >>= ((rcc->CR & 3) >> 3);
- }
- vco = (uint32_t) ((uint64_t) clk * n / m);
- clk = vco / p;
- } else {
- clk = hsi;
- clk >>= ((rcc->CR & 3) >> 3);
- }
- const uint8_t cptab[12] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
- uint32_t d1cpre = (rcc->D1CFGR & (0x0F << 8)) >> 8;
- if (d1cpre >= 8) clk >>= cptab[d1cpre - 8];
- MG_DEBUG(("D1 CLK: %u", clk));
- uint32_t hpre = (rcc->D1CFGR & (0x0F << 0)) >> 0;
- if (hpre < 8) return clk;
- return ((uint32_t) clk) >> cptab[hpre - 8];
- }
- // Guess CR from AHB1 clock. MDC clock is generated from the ETH peripheral
- // clock (AHB1); as per 802.3, it must not exceed 2. As the AHB clock can
- // be derived from HSI or CSI (internal RC) clocks, and those can go above
- // specs, the datasheets specify a range of frequencies and activate one of a
- // series of dividers to keep the MDC clock safely below 2.5MHz. We guess a
- // divider setting based on HCLK with some drift. If the user uses a different
- // clock from our defaults, needs to set the macros on top. Valid for
- // STM32H74xxx/75xxx (58.11.4)(4.5% worst case drift)(CSI clock has a 7.5 %
- // worst case drift @ max temp)
- static int guess_mdc_cr(void) {
- const uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMDIOAR::CR values
- const uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers
- uint32_t hclk = get_hclk(); // Guess system HCLK
- int result = -1; // Invalid CR value
- for (int i = 0; i < 6; i++) {
- if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
- result = crs[i];
- break;
- }
- }
- if (result < 0) MG_ERROR(("HCLK too high"));
- MG_DEBUG(("HCLK: %u, CR: %d", hclk, result));
- return result;
- }
- static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_stm32h_data *d =
- (struct mg_tcpip_driver_stm32h_data *) ifp->driver_data;
- s_ifp = ifp;
- // Init RX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_rxdesc[i][0] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
- s_rxdesc[i][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V
- }
- // Init TX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
- }
- ETH->DMAMR |= BIT(0); // Software reset
- while ((ETH->DMAMR & BIT(0)) != 0) (void) 0; // Wait until done
- // Set MDC clock divider. If user told us the value, use it. Otherwise, guess
- int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
- ETH->MACMDIOAR = ((uint32_t) cr & 0xF) << 8;
- // NOTE(scaprile): We do not use timing facilities so the DMA engine does not
- // re-write buffer address
- ETH->DMAMR = 0 << 16; // use interrupt mode 0 (58.8.1) (reset value)
- ETH->DMASBMR |= BIT(12); // AAL NOTE(scaprile): is this actually needed
- ETH->MACIER = 0; // Do not enable additional irq sources (reset value)
- ETH->MACTFCR = BIT(7); // Disable zero-quanta pause
- // ETH->MACPFR = BIT(31); // Receive all
- eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY
- eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation
- ETH->DMACRDLAR =
- (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors start address
- ETH->DMACRDRLR = ETH_DESC_CNT - 1; // ring length
- ETH->DMACRDTPR =
- (uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT -
- 1]; // last valid descriptor address
- ETH->DMACTDLAR =
- (uint32_t) (uintptr_t) s_txdesc; // TX descriptors start address
- ETH->DMACTDRLR = ETH_DESC_CNT - 1; // ring length
- ETH->DMACTDTPR =
- (uint32_t) (uintptr_t) s_txdesc; // first available descriptor address
- ETH->DMACCR = 0; // DSL = 0 (contiguous descriptor table) (reset value)
- ETH->DMACIER = BIT(6) | BIT(15); // RIE, NIE
- ETH->MACCR = BIT(0) | BIT(1) | BIT(13) | BIT(14) |
- BIT(15); // RE, TE, Duplex, Fast, Reserved
- ETH->MTLTQOMR |= BIT(1); // TSF
- ETH->MTLRQOMR |= BIT(5); // RSF
- ETH->DMACTCR |= BIT(0); // ST
- ETH->DMACRCR |= BIT(0); // SR
- // MAC address filtering
- ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
- ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
- ((uint32_t) ifp->mac[2] << 16) |
- ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
- return true;
- }
- static uint32_t s_txno;
- static size_t mg_tcpip_driver_stm32h_tx(const void *buf, size_t len,
- struct mg_tcpip_if *ifp) {
- if (len > sizeof(s_txbuf[s_txno])) {
- MG_ERROR(("Frame too big, %ld", (long) len));
- len = 0; // Frame is too big
- } else if ((s_txdesc[s_txno][3] & BIT(31))) {
- MG_ERROR(("No free descriptors: %u %08X %08X %08X", s_txno,
- s_txdesc[s_txno][3], ETH->DMACSR, ETH->DMACTCR));
- for (int i = 0; i < ETH_DESC_CNT; i++) MG_ERROR(("%08X", s_txdesc[i][3]));
- len = 0; // All descriptors are busy, fail
- } else {
- memcpy(s_txbuf[s_txno], buf, len); // Copy data
- s_txdesc[s_txno][2] = (uint32_t) len; // Set data len
- s_txdesc[s_txno][3] = BIT(28) | BIT(29); // FD, LD
- s_txdesc[s_txno][3] |= BIT(31); // Set OWN bit - let DMA take over
- if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
- }
- ETH->DMACSR |= BIT(2) | BIT(1); // Clear any prior TBU, TPS
- ETH->DMACTDTPR = (uint32_t) (uintptr_t) &s_txdesc[s_txno]; // and resume
- return len;
- (void) ifp;
- }
- static bool mg_tcpip_driver_stm32h_up(struct mg_tcpip_if *ifp) {
- uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
- bool up = bsr & BIT(2) ? 1 : 0;
- if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
- uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR);
- uint32_t maccr = ETH->MACCR | BIT(14) | BIT(13); // 100M, Full-duplex
- if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M
- if ((scsr & BIT(4)) == 0) maccr &= ~BIT(13); // Half-duplex
- ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
- MG_DEBUG(("Link is %uM %s-duplex", maccr & BIT(14) ? 100 : 10,
- maccr & BIT(13) ? "full" : "half"));
- }
- return up;
- }
- void ETH_IRQHandler(void);
- static uint32_t s_rxno;
- void ETH_IRQHandler(void) {
- if (ETH->DMACSR & BIT(6)) { // Frame received, loop
- ETH->DMACSR = BIT(15) | BIT(6); // Clear flag
- for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
- if (s_rxdesc[s_rxno][3] & BIT(31)) break; // exit when done
- if (((s_rxdesc[s_rxno][3] & (BIT(28) | BIT(29))) ==
- (BIT(28) | BIT(29))) &&
- !(s_rxdesc[s_rxno][3] & BIT(15))) { // skip partial/errored frames
- uint32_t len = s_rxdesc[s_rxno][3] & (BIT(15) - 1);
- // MG_DEBUG(("%lx %lu %lx %08lx", s_rxno, len, s_rxdesc[s_rxno][3],
- // ETH->DMACSR));
- mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
- }
- s_rxdesc[s_rxno][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
- }
- }
- ETH->DMACSR = BIT(7) | BIT(8); // Clear possible RBU RPS while processing
- ETH->DMACRDTPR =
- (uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX
- }
- struct mg_tcpip_driver mg_tcpip_driver_stm32h = {
- mg_tcpip_driver_stm32h_init, mg_tcpip_driver_stm32h_tx, NULL,
- mg_tcpip_driver_stm32h_up};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tcpip/driver_tm4c.c"
- #endif
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C
- struct tm4c_emac {
- volatile uint32_t EMACCFG, EMACFRAMEFLTR, EMACHASHTBLH, EMACHASHTBLL,
- EMACMIIADDR, EMACMIIDATA, EMACFLOWCTL, EMACVLANTG, RESERVED0, EMACSTATUS,
- EMACRWUFF, EMACPMTCTLSTAT, RESERVED1[2], EMACRIS, EMACIM, EMACADDR0H,
- EMACADDR0L, EMACADDR1H, EMACADDR1L, EMACADDR2H, EMACADDR2L, EMACADDR3H,
- EMACADDR3L, RESERVED2[31], EMACWDOGTO, RESERVED3[8], EMACMMCCTRL,
- EMACMMCRXRIS, EMACMMCTXRIS, EMACMMCRXIM, EMACMMCTXIM, RESERVED4,
- EMACTXCNTGB, RESERVED5[12], EMACTXCNTSCOL, EMACTXCNTMCOL, RESERVED6[4],
- EMACTXOCTCNTG, RESERVED7[6], EMACRXCNTGB, RESERVED8[4], EMACRXCNTCRCERR,
- EMACRXCNTALGNERR, RESERVED9[10], EMACRXCNTGUNI, RESERVED10[239],
- EMACVLNINCREP, EMACVLANHASH, RESERVED11[93], EMACTIMSTCTRL, EMACSUBSECINC,
- EMACTIMSEC, EMACTIMNANO, EMACTIMSECU, EMACTIMNANOU, EMACTIMADD,
- EMACTARGSEC, EMACTARGNANO, EMACHWORDSEC, EMACTIMSTAT, EMACPPSCTRL,
- RESERVED12[12], EMACPPS0INTVL, EMACPPS0WIDTH, RESERVED13[294],
- EMACDMABUSMOD, EMACTXPOLLD, EMACRXPOLLD, EMACRXDLADDR, EMACTXDLADDR,
- EMACDMARIS, EMACDMAOPMODE, EMACDMAIM, EMACMFBOC, EMACRXINTWDT,
- RESERVED14[8], EMACHOSTXDESC, EMACHOSRXDESC, EMACHOSTXBA, EMACHOSRXBA,
- RESERVED15[218], EMACPP, EMACPC, EMACCC, RESERVED16, EMACEPHYRIS,
- EMACEPHYIM, EMACEPHYIMSC;
- };
- #undef EMAC
- #define EMAC ((struct tm4c_emac *) (uintptr_t) 0x400EC000)
- #undef BIT
- #define BIT(x) ((uint32_t) 1 << (x))
- #define ETH_PKT_SIZE 1540 // Max frame size
- #define ETH_DESC_CNT 4 // Descriptors count
- #define ETH_DS 4 // Descriptor size (words)
- static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
- static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
- static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
- static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
- static struct mg_tcpip_if *s_ifp; // MIP interface
- enum {
- EPHY_ADDR = 0,
- EPHYBMCR = 0,
- EPHYBMSR = 1,
- EPHYSTS = 16
- }; // PHY constants
- static inline void tm4cspin(volatile uint32_t count) {
- while (count--) (void) 0;
- }
- static uint32_t emac_read_phy(uint8_t addr, uint8_t reg) {
- EMAC->EMACMIIADDR &= (0xf << 2);
- EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6);
- EMAC->EMACMIIADDR |= BIT(0);
- while (EMAC->EMACMIIADDR & BIT(0)) tm4cspin(1);
- return EMAC->EMACMIIDATA;
- }
- static void emac_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
- EMAC->EMACMIIDATA = val;
- EMAC->EMACMIIADDR &= (0xf << 2);
- EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | BIT(1);
- EMAC->EMACMIIADDR |= BIT(0);
- while (EMAC->EMACMIIADDR & BIT(0)) tm4cspin(1);
- }
- static uint32_t get_sysclk(void) {
- struct sysctl {
- volatile uint32_t DONTCARE0[44], RSCLKCFG, DONTCARE1[43], PLLFREQ0,
- PLLFREQ1;
- } *sysctl = (struct sysctl *) 0x400FE000;
- uint32_t clk = 0, piosc = 16000000 /* 16 MHz */, mosc = 25000000 /* 25MHz */;
- if (sysctl->RSCLKCFG & (1 << 28)) { // USEPLL
- uint32_t fin, vco, mdiv, n, q, psysdiv;
- uint32_t pllsrc = (sysctl->RSCLKCFG & (0xf << 24)) >> 24;
- if (pllsrc == 0) {
- clk = piosc;
- } else if (pllsrc == 3) {
- clk = mosc;
- } else {
- MG_ERROR(("Unsupported clock source"));
- }
- q = (sysctl->PLLFREQ1 & (0x1f << 8)) >> 8;
- n = (sysctl->PLLFREQ1 & (0x1f << 0)) >> 0;
- fin = clk / ((q + 1) * (n + 1));
- mdiv = (sysctl->PLLFREQ0 & (0x3ff << 0)) >>
- 0; // mint + (mfrac / 1024); MFRAC not supported
- psysdiv = (sysctl->RSCLKCFG & (0x3f << 0)) >> 0;
- vco = (uint32_t) ((uint64_t) fin * mdiv);
- return vco / (psysdiv + 1);
- }
- uint32_t oscsrc = (sysctl->RSCLKCFG & (0xf << 20)) >> 20;
- if (oscsrc == 0) {
- clk = piosc;
- } else if (oscsrc == 3) {
- clk = mosc;
- } else {
- MG_ERROR(("Unsupported clock source"));
- }
- uint32_t osysdiv = (sysctl->RSCLKCFG & (0xf << 16)) >> 16;
- return clk / (osysdiv + 1);
- }
- // Guess CR from SYSCLK. MDC clock is generated from SYSCLK (AHB); as per
- // 802.3, it must not exceed 2.5MHz (also 20.4.2.6) As the AHB clock can be
- // derived from the PIOSC (internal RC), and it can go above specs, the
- // datasheets specify a range of frequencies and activate one of a series of
- // dividers to keep the MDC clock safely below 2.5MHz. We guess a divider
- // setting based on SYSCLK with a +5% drift. If the user uses a different clock
- // from our defaults, needs to set the macros on top Valid for TM4C129x (20.7)
- // (4.5% worst case drift)
- // The PHY receives the main oscillator (MOSC) (20.3.1)
- static int guess_mdc_cr(void) {
- uint8_t crs[] = {2, 3, 0, 1}; // EMAC->MACMIIAR::CR values
- uint8_t div[] = {16, 26, 42, 62}; // Respective HCLK dividers
- uint32_t sysclk = get_sysclk(); // Guess system SYSCLK
- int result = -1; // Invalid CR value
- if (sysclk < 25000000) {
- MG_ERROR(("SYSCLK too low"));
- } else {
- for (int i = 0; i < 4; i++) {
- if (sysclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
- result = crs[i];
- break;
- }
- }
- if (result < 0) MG_ERROR(("SYSCLK too high"));
- }
- MG_DEBUG(("SYSCLK: %u, CR: %d", sysclk, result));
- return result;
- }
- static bool mg_tcpip_driver_tm4c_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_tm4c_data *d =
- (struct mg_tcpip_driver_tm4c_data *) ifp->driver_data;
- s_ifp = ifp;
- // Init RX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_rxdesc[i][0] = BIT(31); // Own
- s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | BIT(14); // 2nd address chained
- s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
- s_rxdesc[i][3] =
- (uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain
- // MG_DEBUG(("%d %p", i, s_rxdesc[i]));
- }
- // Init TX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
- s_txdesc[i][3] =
- (uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain
- }
- EMAC->EMACDMABUSMOD |= BIT(0); // Software reset
- while ((EMAC->EMACDMABUSMOD & BIT(0)) != 0) tm4cspin(1); // Wait until done
- // Set MDC clock divider. If user told us the value, use it. Otherwise, guess
- int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
- EMAC->EMACMIIADDR = ((uint32_t) cr & 0xf) << 2;
- // NOTE(cpq): we do not use extended descriptor bit 7, and do not use
- // hardware checksum. Therefore, descriptor size is 4, not 8
- // EMAC->EMACDMABUSMOD = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25);
- EMAC->EMACIM = BIT(3) | BIT(9); // Mask timestamp & PMT IT
- EMAC->EMACFLOWCTL = BIT(7); // Disable zero-quanta pause
- // EMAC->EMACFRAMEFLTR = BIT(31); // Receive all
- // EMAC->EMACPC defaults to internal PHY (EPHY) in MMI mode
- emac_write_phy(EPHY_ADDR, EPHYBMCR, BIT(15)); // Reset internal PHY (EPHY)
- emac_write_phy(EPHY_ADDR, EPHYBMCR, BIT(12)); // Set autonegotiation
- EMAC->EMACRXDLADDR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors
- EMAC->EMACTXDLADDR = (uint32_t) (uintptr_t) s_txdesc; // TX descriptors
- EMAC->EMACDMAIM = BIT(6) | BIT(16); // RIE, NIE
- EMAC->EMACCFG = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast
- EMAC->EMACDMAOPMODE =
- BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF
- EMAC->EMACADDR0H = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
- EMAC->EMACADDR0L = (uint32_t) (ifp->mac[3] << 24) |
- ((uint32_t) ifp->mac[2] << 16) |
- ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
- // NOTE(scaprile) There are 3 additional slots for filtering, disabled by
- // default. This also applies to the STM32 driver (at least for F7)
- return true;
- }
- static uint32_t s_txno;
- static size_t mg_tcpip_driver_tm4c_tx(const void *buf, size_t len,
- struct mg_tcpip_if *ifp) {
- if (len > sizeof(s_txbuf[s_txno])) {
- MG_ERROR(("Frame too big, %ld", (long) len));
- len = 0; // fail
- } else if ((s_txdesc[s_txno][0] & BIT(31))) {
- MG_ERROR(("No descriptors available"));
- // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long)
- // EMAC->EMACDMARIS);
- len = 0; // fail
- } else {
- memcpy(s_txbuf[s_txno], buf, len); // Copy data
- s_txdesc[s_txno][1] = (uint32_t) len; // Set data len
- s_txdesc[s_txno][0] =
- BIT(20) | BIT(28) | BIT(29) | BIT(30); // Chain,FS,LS,IC
- s_txdesc[s_txno][0] |= BIT(31); // Set OWN bit - let DMA take over
- if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
- }
- EMAC->EMACDMARIS = BIT(2) | BIT(5); // Clear any prior TU/UNF
- EMAC->EMACTXPOLLD = 0; // and resume
- return len;
- (void) ifp;
- }
- static bool mg_tcpip_driver_tm4c_up(struct mg_tcpip_if *ifp) {
- uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR);
- bool up = (bmsr & BIT(2)) ? 1 : 0;
- if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
- uint32_t sts = emac_read_phy(EPHY_ADDR, EPHYSTS);
- uint32_t emaccfg = EMAC->EMACCFG | BIT(14) | BIT(11); // 100M, Full-duplex
- if (sts & BIT(1)) emaccfg &= ~BIT(14); // 10M
- if ((sts & BIT(2)) == 0) emaccfg &= ~BIT(11); // Half-duplex
- EMAC->EMACCFG = emaccfg; // IRQ handler does not fiddle with this register
- MG_DEBUG(("Link is %uM %s-duplex", emaccfg & BIT(14) ? 100 : 10,
- emaccfg & BIT(11) ? "full" : "half"));
- }
- return up;
- }
- void EMAC0_IRQHandler(void);
- static uint32_t s_rxno;
- void EMAC0_IRQHandler(void) {
- if (EMAC->EMACDMARIS & BIT(6)) { // Frame received, loop
- EMAC->EMACDMARIS = BIT(16) | BIT(6); // Clear flag
- for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
- if (s_rxdesc[s_rxno][0] & BIT(31)) break; // exit when done
- if (((s_rxdesc[s_rxno][0] & (BIT(8) | BIT(9))) == (BIT(8) | BIT(9))) &&
- !(s_rxdesc[s_rxno][0] & BIT(15))) { // skip partial/errored frames
- uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1));
- // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
- // EMAC->EMACDMARIS);
- mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
- }
- s_rxdesc[s_rxno][0] = BIT(31);
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
- }
- }
- EMAC->EMACDMARIS = BIT(7); // Clear possible RU while processing
- EMAC->EMACRXPOLLD = 0; // and resume RX
- }
- struct mg_tcpip_driver mg_tcpip_driver_tm4c = {mg_tcpip_driver_tm4c_init,
- mg_tcpip_driver_tm4c_tx, NULL,
- mg_tcpip_driver_tm4c_up};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tcpip/driver_w5500.c"
- #endif
- #if MG_ENABLE_TCPIP
- enum { W5500_CR = 0, W5500_S0 = 1, W5500_TX0 = 2, W5500_RX0 = 3 };
- static void w5500_txn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, bool wr,
- void *buf, size_t len) {
- uint8_t *p = (uint8_t *) buf;
- uint8_t cmd[] = {(uint8_t) (addr >> 8), (uint8_t) (addr & 255),
- (uint8_t) ((block << 3) | (wr ? 4 : 0))};
- s->begin(s->spi);
- for (size_t i = 0; i < sizeof(cmd); i++) s->txn(s->spi, cmd[i]);
- for (size_t i = 0; i < len; i++) {
- uint8_t r = s->txn(s->spi, p[i]);
- if (!wr) p[i] = r;
- }
- s->end(s->spi);
- }
- // clang-format off
- static void w5500_wn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, true, buf, len); }
- static void w5500_w1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint8_t val) { w5500_wn(s, block, addr, &val, 1); }
- static void w5500_w2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint16_t val) { uint8_t buf[2] = {(uint8_t) (val >> 8), (uint8_t) (val & 255)}; w5500_wn(s, block, addr, buf, sizeof(buf)); }
- static void w5500_rn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, false, buf, len); }
- static uint8_t w5500_r1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t r = 0; w5500_rn(s, block, addr, &r, 1); return r; }
- static uint16_t w5500_r2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5500_rn(s, block, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); }
- // clang-format on
- static size_t w5500_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) {
- struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
- uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len
- while ((n2 = w5500_r2(s, W5500_S0, 0x26)) > n) n = n2; // Until it is stable
- // printf("RSR: %d\n", (int) n);
- if (n > 0) {
- uint16_t ptr = w5500_r2(s, W5500_S0, 0x28); // Get read pointer
- n = w5500_r2(s, W5500_RX0, ptr); // Read frame length
- if (n <= len + 2 && n > 1) {
- r = (uint16_t) (n - 2);
- w5500_rn(s, W5500_RX0, (uint16_t) (ptr + 2), buf, r);
- }
- w5500_w2(s, W5500_S0, 0x28, (uint16_t) (ptr + n)); // Advance read pointer
- w5500_w1(s, W5500_S0, 1, 0x40); // Sock0 CR -> RECV
- // printf(" RX_RD: tot=%u n=%u r=%u\n", n2, n, r);
- }
- return r;
- }
- static size_t w5500_tx(const void *buf, size_t buflen, struct mg_tcpip_if *ifp) {
- struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
- uint16_t n = 0, len = (uint16_t) buflen;
- while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space
- uint16_t ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer
- w5500_wn(s, W5500_TX0, ptr, (void *) buf, len); // Write data
- w5500_w2(s, W5500_S0, 0x24, (uint16_t) (ptr + len)); // Advance write pointer
- w5500_w1(s, W5500_S0, 1, 0x20); // Sock0 CR -> SEND
- for (int i = 0; i < 40; i++) {
- uint8_t ir = w5500_r1(s, W5500_S0, 2); // Read S0 IR
- if (ir == 0) continue;
- // printf("IR %d, len=%d, free=%d, ptr %d\n", ir, (int) len, (int) n, ptr);
- w5500_w1(s, W5500_S0, 2, ir); // Write S0 IR: clear it!
- if (ir & 8) len = 0; // Timeout. Report error
- if (ir & (16 | 8)) break; // Stop on SEND_OK or timeout
- }
- return len;
- }
- static bool w5500_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
- s->end(s->spi);
- w5500_w1(s, W5500_CR, 0, 0x80); // Reset chip: CR -> 0x80
- w5500_w1(s, W5500_CR, 0x2e, 0); // CR PHYCFGR -> reset
- w5500_w1(s, W5500_CR, 0x2e, 0xf8); // CR PHYCFGR -> set
- // w5500_wn(s, W5500_CR, 9, s->mac, 6); // Set source MAC
- w5500_w1(s, W5500_S0, 0x1e, 16); // Sock0 RX buf size
- w5500_w1(s, W5500_S0, 0x1f, 16); // Sock0 TX buf size
- w5500_w1(s, W5500_S0, 0, 4); // Sock0 MR -> MACRAW
- w5500_w1(s, W5500_S0, 1, 1); // Sock0 CR -> OPEN
- return w5500_r1(s, W5500_S0, 3) == 0x42; // Sock0 SR == MACRAW
- }
- static bool w5500_up(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_spi *spi = (struct mg_tcpip_spi *) ifp->driver_data;
- uint8_t phycfgr = w5500_r1(spi, W5500_CR, 0x2e);
- return phycfgr & 1; // Bit 0 of PHYCFGR is LNK (0 - down, 1 - up)
- }
- struct mg_tcpip_driver mg_tcpip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx, w5500_up};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tcpip/tcpip.c"
- #endif
- #if MG_ENABLE_TCPIP
- #define MG_EPHEMERAL_PORT_BASE 32768
- #define PDIFF(a, b) ((size_t) (((char *) (b)) - ((char *) (a))))
- #ifndef MIP_TCP_KEEPALIVE_MS
- #define MIP_TCP_KEEPALIVE_MS 45000 // TCP keep-alive period, ms
- #endif
- #define MIP_TCP_ACK_MS 150 // Timeout for ACKing
- struct connstate {
- uint32_t seq, ack; // TCP seq/ack counters
- uint64_t timer; // TCP keep-alive / ACK timer
- uint8_t mac[6]; // Peer MAC address
- uint8_t ttype; // Timer type. 0: ack, 1: keep-alive
- #define MIP_TTYPE_KEEPALIVE 0 // Connection is idle for long, send keepalive
- #define MIP_TTYPE_ACK 1 // Peer sent us data, we have to ack it soon
- uint8_t tmiss; // Number of keep-alive misses
- struct mg_iobuf raw; // For TLS only. Incoming raw data
- };
- #pragma pack(push, 1)
- struct lcp {
- uint8_t addr, ctrl, proto[2], code, id, len[2];
- };
- struct eth {
- uint8_t dst[6]; // Destination MAC address
- uint8_t src[6]; // Source MAC address
- uint16_t type; // Ethernet type
- };
- struct ip {
- uint8_t ver; // Version
- uint8_t tos; // Unused
- uint16_t len; // Length
- uint16_t id; // Unused
- uint16_t frag; // Fragmentation
- uint8_t ttl; // Time to live
- uint8_t proto; // Upper level protocol
- uint16_t csum; // Checksum
- uint32_t src; // Source IP
- uint32_t dst; // Destination IP
- };
- struct ip6 {
- uint8_t ver; // Version
- uint8_t opts[3]; // Options
- uint16_t len; // Length
- uint8_t proto; // Upper level protocol
- uint8_t ttl; // Time to live
- uint8_t src[16]; // Source IP
- uint8_t dst[16]; // Destination IP
- };
- struct icmp {
- uint8_t type;
- uint8_t code;
- uint16_t csum;
- };
- struct arp {
- uint16_t fmt; // Format of hardware address
- uint16_t pro; // Format of protocol address
- uint8_t hlen; // Length of hardware address
- uint8_t plen; // Length of protocol address
- uint16_t op; // Operation
- uint8_t sha[6]; // Sender hardware address
- uint32_t spa; // Sender protocol address
- uint8_t tha[6]; // Target hardware address
- uint32_t tpa; // Target protocol address
- };
- struct tcp {
- uint16_t sport; // Source port
- uint16_t dport; // Destination port
- uint32_t seq; // Sequence number
- uint32_t ack; // Acknowledgement number
- uint8_t off; // Data offset
- uint8_t flags; // TCP flags
- #define TH_FIN 0x01
- #define TH_SYN 0x02
- #define TH_RST 0x04
- #define TH_PUSH 0x08
- #define TH_ACK 0x10
- #define TH_URG 0x20
- #define TH_ECE 0x40
- #define TH_CWR 0x80
- uint16_t win; // Window
- uint16_t csum; // Checksum
- uint16_t urp; // Urgent pointer
- };
- struct udp {
- uint16_t sport; // Source port
- uint16_t dport; // Destination port
- uint16_t len; // UDP length
- uint16_t csum; // UDP checksum
- };
- struct dhcp {
- uint8_t op, htype, hlen, hops;
- uint32_t xid;
- uint16_t secs, flags;
- uint32_t ciaddr, yiaddr, siaddr, giaddr;
- uint8_t hwaddr[208];
- uint32_t magic;
- uint8_t options[32];
- };
- #pragma pack(pop)
- struct pkt {
- struct mg_str raw; // Raw packet data
- struct mg_str pay; // Payload data
- struct eth *eth;
- struct llc *llc;
- struct arp *arp;
- struct ip *ip;
- struct ip6 *ip6;
- struct icmp *icmp;
- struct tcp *tcp;
- struct udp *udp;
- struct dhcp *dhcp;
- };
- static void mkpay(struct pkt *pkt, void *p) {
- pkt->pay =
- mg_str_n((char *) p, (size_t) (&pkt->raw.ptr[pkt->raw.len] - (char *) p));
- }
- static uint32_t csumup(uint32_t sum, const void *buf, size_t len) {
- const uint8_t *p = (const uint8_t *) buf;
- for (size_t i = 0; i < len; i++) sum += i & 1 ? p[i] : (uint32_t) (p[i] << 8);
- return sum;
- }
- static uint16_t csumfin(uint32_t sum) {
- while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16);
- return mg_htons(~sum & 0xffff);
- }
- static uint16_t ipcsum(const void *buf, size_t len) {
- uint32_t sum = csumup(0, buf, len);
- return csumfin(sum);
- }
- static size_t ether_output(struct mg_tcpip_if *ifp, size_t len) {
- // size_t min = 64; // Pad short frames to 64 bytes (minimum Ethernet size)
- // if (len < min) memset(ifp->tx.ptr + len, 0, min - len), len = min;
- // mg_hexdump(ifp->tx.ptr, len);
- size_t n = ifp->driver->tx(ifp->tx.ptr, len, ifp);
- if (n == len) ifp->nsent++;
- return n;
- }
- static void arp_ask(struct mg_tcpip_if *ifp, uint32_t ip) {
- struct eth *eth = (struct eth *) ifp->tx.ptr;
- struct arp *arp = (struct arp *) (eth + 1);
- memset(eth->dst, 255, sizeof(eth->dst));
- memcpy(eth->src, ifp->mac, sizeof(eth->src));
- eth->type = mg_htons(0x806);
- memset(arp, 0, sizeof(*arp));
- arp->fmt = mg_htons(1), arp->pro = mg_htons(0x800), arp->hlen = 6,
- arp->plen = 4;
- arp->op = mg_htons(1), arp->tpa = ip, arp->spa = ifp->ip;
- memcpy(arp->sha, ifp->mac, sizeof(arp->sha));
- ether_output(ifp, PDIFF(eth, arp + 1));
- }
- static void onstatechange(struct mg_tcpip_if *ifp) {
- if (ifp->state == MG_TCPIP_STATE_READY) {
- MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip));
- MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw));
- if (ifp->lease_expire > ifp->now) {
- MG_INFO(
- (" Lease: %lld sec", (ifp->lease_expire - ifp->now) / 1000));
- }
- arp_ask(ifp, ifp->gw);
- } else if (ifp->state == MG_TCPIP_STATE_UP) {
- MG_ERROR(("Link up"));
- srand((unsigned int) mg_millis());
- } else if (ifp->state == MG_TCPIP_STATE_DOWN) {
- MG_ERROR(("Link down"));
- }
- }
- static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
- uint8_t proto, uint32_t ip_src, uint32_t ip_dst,
- size_t plen) {
- struct eth *eth = (struct eth *) ifp->tx.ptr;
- struct ip *ip = (struct ip *) (eth + 1);
- memcpy(eth->dst, mac_dst, sizeof(eth->dst));
- memcpy(eth->src, ifp->mac, sizeof(eth->src)); // Use our MAC
- eth->type = mg_htons(0x800);
- memset(ip, 0, sizeof(*ip));
- ip->ver = 0x45; // Version 4, header length 5 words
- ip->frag = 0x40; // Don't fragment
- ip->len = mg_htons((uint16_t) (sizeof(*ip) + plen));
- ip->ttl = 64;
- ip->proto = proto;
- ip->src = ip_src;
- ip->dst = ip_dst;
- ip->csum = ipcsum(ip, sizeof(*ip));
- return ip;
- }
- static void tx_udp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src,
- uint16_t sport, uint32_t ip_dst, uint16_t dport,
- const void *buf, size_t len) {
- struct ip *ip =
- tx_ip(ifp, mac_dst, 17, ip_src, ip_dst, len + sizeof(struct udp));
- struct udp *udp = (struct udp *) (ip + 1);
- // MG_DEBUG(("UDP XX LEN %d %d", (int) len, (int) ifp->tx.len));
- udp->sport = sport;
- udp->dport = dport;
- udp->len = mg_htons((uint16_t) (sizeof(*udp) + len));
- udp->csum = 0;
- uint32_t cs = csumup(0, udp, sizeof(*udp));
- cs = csumup(cs, buf, len);
- cs = csumup(cs, &ip->src, sizeof(ip->src));
- cs = csumup(cs, &ip->dst, sizeof(ip->dst));
- cs += (uint32_t) (ip->proto + sizeof(*udp) + len);
- udp->csum = csumfin(cs);
- memmove(udp + 1, buf, len);
- // MG_DEBUG(("UDP LEN %d %d", (int) len, (int) ifp->frame_len));
- ether_output(ifp, sizeof(struct eth) + sizeof(*ip) + sizeof(*udp) + len);
- }
- static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src,
- uint32_t ip_dst, uint8_t *opts, size_t optslen) {
- struct dhcp dhcp = {1, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
- dhcp.magic = mg_htonl(0x63825363);
- memcpy(&dhcp.hwaddr, ifp->mac, sizeof(ifp->mac));
- memcpy(&dhcp.xid, ifp->mac + 2, sizeof(dhcp.xid));
- memcpy(&dhcp.options, opts, optslen);
- tx_udp(ifp, mac_dst, ip_src, mg_htons(68), ip_dst, mg_htons(67), &dhcp,
- sizeof(dhcp));
- }
- static void tx_dhcp_request(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
- uint32_t ip_src, uint32_t ip_dst) {
- uint8_t opts[] = {
- 53, 1, 3, // Type: DHCP request
- 55, 2, 1, 3, // GW and mask
- 12, 3, 'm', 'i', 'p', // Host name: "mip"
- 54, 4, 0, 0, 0, 0, // DHCP server ID
- 50, 4, 0, 0, 0, 0, // Requested IP
- 255 // End of options
- };
- memcpy(opts + 14, &ip_dst, sizeof(ip_dst));
- memcpy(opts + 20, &ip_src, sizeof(ip_src));
- tx_dhcp(ifp, mac_dst, ip_src, ip_dst, opts, sizeof(opts));
- }
- static void tx_dhcp_discover(struct mg_tcpip_if *ifp) {
- uint8_t mac[6] = {255, 255, 255, 255, 255, 255};
- uint8_t opts[] = {
- 53, 1, 1, // Type: DHCP discover
- 55, 2, 1, 3, // Parameters: ip, mask
- 255 // End of options
- };
- tx_dhcp(ifp, mac, 0, 0xffffffff, opts, sizeof(opts));
- MG_DEBUG(("DHCP discover sent"));
- }
- static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt,
- bool lsn) {
- struct mg_connection *c = NULL;
- for (c = mgr->conns; c != NULL; c = c->next) {
- if (c->is_udp && pkt->udp && c->loc.port == pkt->udp->dport) break;
- if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport &&
- lsn == c->is_listening && (lsn || c->rem.port == pkt->tcp->sport))
- break;
- }
- return c;
- }
- static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
- // ARP request. Make a response, then send
- // MG_DEBUG(("ARP op %d %M: %M", mg_ntohs(pkt->arp->op), mg_print_ip4,
- // &pkt->arp->spa, mg_print_ip4, &pkt->arp->tpa));
- struct eth *eth = (struct eth *) ifp->tx.ptr;
- struct arp *arp = (struct arp *) (eth + 1);
- memcpy(eth->dst, pkt->eth->src, sizeof(eth->dst));
- memcpy(eth->src, ifp->mac, sizeof(eth->src));
- eth->type = mg_htons(0x806);
- *arp = *pkt->arp;
- arp->op = mg_htons(2);
- memcpy(arp->tha, pkt->arp->sha, sizeof(pkt->arp->tha));
- memcpy(arp->sha, ifp->mac, sizeof(pkt->arp->sha));
- arp->tpa = pkt->arp->spa;
- arp->spa = ifp->ip;
- MG_DEBUG(("ARP: tell %M we're %M", mg_print_ip4, &arp->tpa, mg_print_ip4,
- &ifp->ip));
- ether_output(ifp, PDIFF(eth, arp + 1));
- } else if (pkt->arp->op == mg_htons(2)) {
- if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return;
- if (pkt->arp->spa == ifp->gw) {
- // Got response for the GW ARP request. Set ifp->gwmac
- memcpy(ifp->gwmac, pkt->arp->sha, sizeof(ifp->gwmac));
- } else {
- struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
- if (c != NULL && c->is_arplooking) {
- struct connstate *s = (struct connstate *) (c + 1);
- memcpy(s->mac, pkt->arp->sha, sizeof(s->mac));
- MG_DEBUG(("%lu ARP resolved %M -> %M", c->id, mg_print_ip4, &c->rem.ip,
- mg_print_mac, s->mac));
- c->is_arplooking = 0;
- }
- }
- }
- }
- static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- // MG_DEBUG(("ICMP %d", (int) len));
- if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) {
- size_t hlen = sizeof(struct eth) + sizeof(struct ip) + sizeof(struct icmp);
- size_t space = ifp->tx.len - hlen, plen = pkt->pay.len;
- if (plen > space) plen = space;
- struct ip *ip = tx_ip(ifp, pkt->eth->src, 1, ifp->ip, pkt->ip->src,
- sizeof(struct icmp) + plen);
- struct icmp *icmp = (struct icmp *) (ip + 1);
- memset(icmp, 0, sizeof(*icmp)); // Set csum to 0
- memcpy(icmp + 1, pkt->pay.ptr, plen); // Copy RX payload to TX
- icmp->csum = ipcsum(icmp, sizeof(*icmp) + plen);
- ether_output(ifp, hlen + plen);
- }
- }
- static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- uint32_t ip = 0, gw = 0, mask = 0;
- uint8_t *p = pkt->dhcp->options,
- *end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len];
- if (end < (uint8_t *) (pkt->dhcp + 1)) return;
- while (p + 1 < end && p[0] != 255) { // Parse options
- if (p[0] == 1 && p[1] == sizeof(ifp->mask) && p + 6 < end) { // Mask
- memcpy(&mask, p + 2, sizeof(mask));
- } else if (p[0] == 3 && p[1] == sizeof(ifp->gw) && p + 6 < end) { // GW
- memcpy(&gw, p + 2, sizeof(gw));
- ip = pkt->dhcp->yiaddr;
- } else if (p[0] == 51 && p[1] == 4 && p + 6 < end) { // Lease
- uint32_t lease = 0;
- memcpy(&lease, p + 2, sizeof(lease));
- ifp->lease_expire = ifp->now + mg_ntohl(lease) * 1000;
- }
- p += p[1] + 2;
- }
- if (ip && mask && gw && ifp->ip == 0) {
- memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac));
- ifp->ip = ip, ifp->gw = gw, ifp->mask = mask;
- ifp->state = MG_TCPIP_STATE_READY;
- onstatechange(ifp);
- tx_dhcp_request(ifp, pkt->eth->src, ip, pkt->dhcp->siaddr);
- uint64_t rand;
- mg_random(&rand, sizeof(rand));
- srand((unsigned int) (rand + mg_millis()));
- }
- }
- // Simple DHCP server that assigns a next IP address: ifp->ip + 1
- static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- uint8_t op = 0, *p = pkt->dhcp->options,
- *end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len];
- if (end < (uint8_t *) (pkt->dhcp + 1)) return;
- // struct dhcp *req = pkt->dhcp;
- struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
- res.yiaddr = ifp->ip;
- ((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1
- while (p + 1 < end && p[0] != 255) { // Parse options
- if (p[0] == 53 && p[1] == 1 && p + 2 < end) { // Message type
- op = p[2];
- }
- p += p[1] + 2;
- }
- if (op == 1 || op == 3) { // DHCP Discover or DHCP Request
- uint8_t msg = op == 1 ? 2 : 5; // Message type: DHCP OFFER or DHCP ACK
- uint8_t opts[] = {
- 53, 1, msg, // Message type
- 1, 4, 0, 0, 0, 0, // Subnet mask
- 54, 4, 0, 0, 0, 0, // Server ID
- 12, 3, 'm', 'i', 'p', // Host name: "mip"
- 51, 4, 255, 255, 255, 255, // Lease time
- 255 // End of options
- };
- memcpy(&res.hwaddr, pkt->dhcp->hwaddr, 6);
- memcpy(opts + 5, &ifp->mask, sizeof(ifp->mask));
- memcpy(opts + 11, &ifp->ip, sizeof(ifp->ip));
- memcpy(&res.options, opts, sizeof(opts));
- res.magic = pkt->dhcp->magic;
- res.xid = pkt->dhcp->xid;
- // memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac));
- tx_udp(ifp, pkt->eth->src, ifp->ip, mg_htons(67),
- op == 1 ? ~0U : res.yiaddr, mg_htons(68), &res, sizeof(res));
- }
- }
- static void rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
- if (c == NULL) {
- // No UDP listener on this port. Should send ICMP, but keep silent.
- } else if (c != NULL) {
- c->rem.port = pkt->udp->sport;
- c->rem.ip = pkt->ip->src;
- if (c->recv.len >= MG_MAX_RECV_SIZE) {
- mg_error(c, "max_recv_buf_size reached");
- } else if (c->recv.size - c->recv.len < pkt->pay.len &&
- !mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
- mg_error(c, "oom");
- } else {
- memcpy(&c->recv.buf[c->recv.len], pkt->pay.ptr, pkt->pay.len);
- c->recv.len += pkt->pay.len;
- mg_call(c, MG_EV_READ, &pkt->pay.len);
- }
- }
- }
- static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip,
- uint8_t flags, uint16_t sport, uint16_t dport,
- uint32_t seq, uint32_t ack, const void *buf, size_t len) {
- struct ip *ip =
- tx_ip(ifp, dst_mac, 6, ifp->ip, dst_ip, sizeof(struct tcp) + len);
- struct tcp *tcp = (struct tcp *) (ip + 1);
- memset(tcp, 0, sizeof(*tcp));
- if (buf != NULL && len) memmove(tcp + 1, buf, len);
- tcp->sport = sport;
- tcp->dport = dport;
- tcp->seq = seq;
- tcp->ack = ack;
- tcp->flags = flags;
- tcp->win = mg_htons(8192);
- tcp->off = (uint8_t) (sizeof(*tcp) / 4 << 4);
- uint32_t cs = 0;
- uint16_t n = (uint16_t) (sizeof(*tcp) + len);
- uint8_t pseudo[] = {0, ip->proto, (uint8_t) (n >> 8), (uint8_t) (n & 255)};
- cs = csumup(cs, tcp, n);
- cs = csumup(cs, &ip->src, sizeof(ip->src));
- cs = csumup(cs, &ip->dst, sizeof(ip->dst));
- cs = csumup(cs, pseudo, sizeof(pseudo));
- tcp->csum = csumfin(cs);
- MG_DEBUG(("TCP %M:%hu -> %M:%hu fl %x len %u", mg_print_ip4, &ip->src,
- mg_ntohs(tcp->sport), mg_print_ip4, &ip->dst, mg_ntohs(tcp->dport),
- tcp->flags, (int) len));
- return ether_output(ifp, PDIFF(ifp->tx.ptr, tcp + 1) + len);
- }
- static size_t tx_tcp_pkt(struct mg_tcpip_if *ifp, struct pkt *pkt,
- uint8_t flags, uint32_t seq, const void *buf,
- size_t len) {
- uint32_t delta = (pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0;
- return tx_tcp(ifp, pkt->eth->src, pkt->ip->src, flags, pkt->tcp->dport,
- pkt->tcp->sport, seq, mg_htonl(mg_ntohl(pkt->tcp->seq) + delta),
- buf, len);
- }
- static void settmout(struct mg_connection *c, uint8_t type) {
- struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
- struct connstate *s = (struct connstate *) (c + 1);
- unsigned n = type == MIP_TTYPE_ACK ? MIP_TCP_ACK_MS : MIP_TCP_KEEPALIVE_MS;
- s->timer = ifp->now + n;
- s->ttype = type;
- MG_VERBOSE(("%lu %d -> %llx", c->id, type, s->timer));
- }
- static struct mg_connection *accept_conn(struct mg_connection *lsn,
- struct pkt *pkt) {
- struct mg_connection *c = mg_alloc_conn(lsn->mgr);
- struct connstate *s = (struct connstate *) (c + 1);
- s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
- memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
- settmout(c, MIP_TTYPE_KEEPALIVE);
- c->rem.ip = pkt->ip->src;
- c->rem.port = pkt->tcp->sport;
- MG_DEBUG(("%lu accepted %M", c->id, mg_print_ip_port, &c->rem));
- LIST_ADD_HEAD(struct mg_connection, &lsn->mgr->conns, c);
- c->is_accepted = 1;
- c->is_hexdumping = lsn->is_hexdumping;
- c->pfn = lsn->pfn;
- c->loc = lsn->loc;
- c->pfn_data = lsn->pfn_data;
- c->fn = lsn->fn;
- c->fn_data = lsn->fn_data;
- mg_call(c, MG_EV_OPEN, NULL);
- mg_call(c, MG_EV_ACCEPT, NULL);
- return c;
- }
- long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
- struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
- struct connstate *s = (struct connstate *) (c + 1);
- size_t max_headers_len = 14 + 24 /* max IP */ + 60 /* max TCP */;
- if (len + max_headers_len > ifp->tx.len) len = ifp->tx.len - max_headers_len;
- if (tx_tcp(ifp, s->mac, c->rem.ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port,
- mg_htonl(s->seq), mg_htonl(s->ack), buf, len) > 0) {
- s->seq += (uint32_t) len;
- if (s->ttype == MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_KEEPALIVE);
- } else {
- return MG_IO_ERR;
- }
- return (long) len;
- }
- long mg_io_recv(struct mg_connection *c, void *buf, size_t len) {
- struct connstate *s = (struct connstate *) (c + 1);
- if (s->raw.len == 0) return MG_IO_WAIT;
- if (len > s->raw.len) len = s->raw.len;
- memcpy(buf, s->raw.buf, len);
- mg_iobuf_del(&s->raw, 0, len);
- MG_DEBUG(("%lu", len));
- return (long) len;
- }
- static void read_conn(struct mg_connection *c, struct pkt *pkt) {
- struct connstate *s = (struct connstate *) (c + 1);
- struct mg_iobuf *io = c->is_tls ? &s->raw : &c->recv;
- uint32_t seq = mg_ntohl(pkt->tcp->seq);
- s->raw.align = c->recv.align;
- if (pkt->tcp->flags & TH_FIN) {
- s->ack = mg_htonl(pkt->tcp->seq) + 1, s->seq = mg_htonl(pkt->tcp->ack);
- c->is_closing = 1;
- } else if (pkt->pay.len == 0) {
- // TODO(cpq): handle this peer's ACK
- } else if (seq != s->ack) {
- uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
- if (s->ack == ack) {
- MG_VERBOSE(("ignoring duplicate pkt"));
- } else {
- // TODO(cpq): peer sent us SEQ which we don't expect. Retransmit rather
- // than close this connection
- mg_error(c, "SEQ != ACK: %x %x %x", seq, s->ack, ack);
- }
- } else if (io->size - io->len < pkt->pay.len &&
- !mg_iobuf_resize(io, io->len + pkt->pay.len)) {
- mg_error(c, "oom");
- } else {
- // Copy TCP payload into the IO buffer. If the connection is plain text, we
- // copy to c->recv. If the connection is TLS, this data is encrypted,
- // therefore we copy that encrypted data to the s->raw iobuffer instead,
- // and then call mg_tls_recv() to decrypt it. NOTE: mg_tls_recv() will
- // call back mg_io_recv() which grabs raw data from s->raw
- memcpy(&io->buf[io->len], pkt->pay.ptr, pkt->pay.len);
- io->len += pkt->pay.len;
- MG_DEBUG(("%lu SEQ %x -> %x", c->id, mg_htonl(pkt->tcp->seq), s->ack));
- // Advance ACK counter
- s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
- #if 0
- // Send ACK immediately
- MG_DEBUG((" imm ACK", c->id, mg_htonl(pkt->tcp->seq), s->ack));
- tx_tcp((struct mg_tcpip_if *) c->mgr->priv, c->rem.ip, TH_ACK, c->loc.port,
- c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
- #else
- // if not already running, setup a timer to send an ACK later
- if (s->ttype != MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_ACK);
- #endif
- if (c->is_tls) {
- // TLS connection. Make room for decrypted data in c->recv
- io = &c->recv;
- if (io->size - io->len < pkt->pay.len &&
- !mg_iobuf_resize(io, io->len + pkt->pay.len)) {
- mg_error(c, "oom");
- } else {
- // Decrypt data directly into c->recv
- long n = mg_tls_recv(c, &io->buf[io->len], io->size - io->len);
- if (n == MG_IO_ERR) {
- mg_error(c, "TLS recv error");
- } else if (n > 0) {
- // Decrypted successfully - trigger MG_EV_READ
- io->len += (size_t) n;
- mg_call(c, MG_EV_READ, &n);
- }
- }
- } else {
- // Plain text connection, data is already in c->recv, trigger MG_EV_READ
- mg_call(c, MG_EV_READ, &pkt->pay.len);
- }
- }
- }
- static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
- struct connstate *s = c == NULL ? NULL : (struct connstate *) (c + 1);
- #if 0
- MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len));
- #endif
- if (c != NULL && c->is_connecting && pkt->tcp->flags & (TH_SYN | TH_ACK)) {
- s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1;
- tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0);
- c->is_connecting = 0; // Client connected
- settmout(c, MIP_TTYPE_KEEPALIVE);
- mg_call(c, MG_EV_CONNECT, NULL); // Let user know
- } else if (c != NULL && c->is_connecting) {
- tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
- } else if (c != NULL && pkt->tcp->flags & TH_RST) {
- mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
- } else if (c != NULL) {
- #if 0
- MG_DEBUG(("%lu %d %M:%hu -> %M:%hu", c->id, (int) pkt->raw.len,
- mg_print_ip4, &pkt->ip->src, mg_ntohs(pkt->tcp->sport),
- mg_print_ip4, &pkt->ip->dst, mg_ntohs(pkt->tcp->dport)));
- mg_hexdump(pkt->pay.buf, pkt->pay.len);
- #endif
- s->tmiss = 0; // Reset missed keep-alive counter
- if (s->ttype == MIP_TTYPE_KEEPALIVE) // Advance keep-alive timer
- settmout(c,
- MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending
- read_conn(c, pkt); // Override timer with ACK timeout if needed
- } else if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) {
- tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
- } else if (pkt->tcp->flags & TH_RST) {
- if (c->is_accepted) mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
- // ignore RST if not connected
- } else if (pkt->tcp->flags & TH_SYN) {
- // Use peer's source port as ISN, in order to recognise the handshake
- uint32_t isn = mg_htonl((uint32_t) mg_ntohs(pkt->tcp->sport));
- tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0);
- } else if (pkt->tcp->flags & TH_FIN) {
- tx_tcp_pkt(ifp, pkt, TH_FIN | TH_ACK, pkt->tcp->ack, NULL, 0);
- } else if (mg_htonl(pkt->tcp->ack) == mg_htons(pkt->tcp->sport) + 1U) {
- accept_conn(c, pkt);
- } else if (!c->is_accepted) { // no peer
- tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
- } else {
- // MG_DEBUG(("dropped silently.."));
- }
- }
- static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- if (pkt->ip->proto == 1) {
- pkt->icmp = (struct icmp *) (pkt->ip + 1);
- if (pkt->pay.len < sizeof(*pkt->icmp)) return;
- mkpay(pkt, pkt->icmp + 1);
- rx_icmp(ifp, pkt);
- } else if (pkt->ip->proto == 17) {
- pkt->udp = (struct udp *) (pkt->ip + 1);
- if (pkt->pay.len < sizeof(*pkt->udp)) return;
- mkpay(pkt, pkt->udp + 1);
- MG_DEBUG(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
- mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst,
- mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
- if (pkt->udp->dport == mg_htons(68)) {
- pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
- mkpay(pkt, pkt->dhcp + 1);
- rx_dhcp_client(ifp, pkt);
- } else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(67)) {
- pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
- mkpay(pkt, pkt->dhcp + 1);
- rx_dhcp_server(ifp, pkt);
- } else {
- rx_udp(ifp, pkt);
- }
- } else if (pkt->ip->proto == 6) {
- pkt->tcp = (struct tcp *) (pkt->ip + 1);
- if (pkt->pay.len < sizeof(*pkt->tcp)) return;
- mkpay(pkt, pkt->tcp + 1);
- uint16_t iplen = mg_ntohs(pkt->ip->len);
- uint16_t off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U));
- if (iplen >= off) pkt->pay.len = (size_t) (iplen - off);
- MG_DEBUG(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
- mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst,
- mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
- rx_tcp(ifp, pkt);
- }
- }
- static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- // MG_DEBUG(("IP %d", (int) len));
- if (pkt->ip6->proto == 1 || pkt->ip6->proto == 58) {
- pkt->icmp = (struct icmp *) (pkt->ip6 + 1);
- if (pkt->pay.len < sizeof(*pkt->icmp)) return;
- mkpay(pkt, pkt->icmp + 1);
- rx_icmp(ifp, pkt);
- } else if (pkt->ip6->proto == 17) {
- pkt->udp = (struct udp *) (pkt->ip6 + 1);
- if (pkt->pay.len < sizeof(*pkt->udp)) return;
- // MG_DEBUG((" UDP %u %u -> %u", len, mg_htons(udp->sport),
- // mg_htons(udp->dport)));
- mkpay(pkt, pkt->udp + 1);
- }
- }
- static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) {
- const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255};
- struct pkt pkt;
- memset(&pkt, 0, sizeof(pkt));
- pkt.raw.ptr = (char *) buf;
- pkt.raw.len = len;
- pkt.eth = (struct eth *) buf;
- if (pkt.raw.len < sizeof(*pkt.eth)) return; // Truncated - runt?
- if (ifp->enable_mac_check &&
- memcmp(pkt.eth->dst, ifp->mac, sizeof(pkt.eth->dst)) != 0 &&
- memcmp(pkt.eth->dst, broadcast, sizeof(pkt.eth->dst)) != 0)
- return;
- if (ifp->enable_crc32_check && len > 4) {
- len -= 4; // TODO(scaprile): check on bigendian
- uint32_t crc = mg_crc32(0, (const char *) buf, len);
- if (memcmp((void *) ((size_t) buf + len), &crc, sizeof(crc))) return;
- }
- if (pkt.eth->type == mg_htons(0x806)) {
- pkt.arp = (struct arp *) (pkt.eth + 1);
- if (sizeof(*pkt.eth) + sizeof(*pkt.arp) > pkt.raw.len) return; // Truncated
- rx_arp(ifp, &pkt);
- } else if (pkt.eth->type == mg_htons(0x86dd)) {
- pkt.ip6 = (struct ip6 *) (pkt.eth + 1);
- if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip6)) return; // Truncated
- if ((pkt.ip6->ver >> 4) != 0x6) return; // Not IP
- mkpay(&pkt, pkt.ip6 + 1);
- rx_ip6(ifp, &pkt);
- } else if (pkt.eth->type == mg_htons(0x800)) {
- pkt.ip = (struct ip *) (pkt.eth + 1);
- if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated
- // Truncate frame to what IP header tells us
- if ((size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth) < pkt.raw.len) {
- pkt.raw.len = (size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth);
- }
- if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated
- if ((pkt.ip->ver >> 4) != 4) return; // Not IP
- mkpay(&pkt, pkt.ip + 1);
- rx_ip(ifp, &pkt);
- } else {
- MG_DEBUG((" Unknown eth type %x", mg_htons(pkt.eth->type)));
- mg_hexdump(buf, len >= 16 ? 16 : len);
- }
- }
- static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t uptime_ms) {
- if (ifp == NULL || ifp->driver == NULL) return;
- bool expired_1000ms = mg_timer_expired(&ifp->timer_1000ms, 1000, uptime_ms);
- ifp->now = uptime_ms;
- // Handle physical interface up/down status
- if (expired_1000ms && ifp->driver->up) {
- bool up = ifp->driver->up(ifp);
- bool current = ifp->state != MG_TCPIP_STATE_DOWN;
- if (up != current) {
- ifp->state = up == false ? MG_TCPIP_STATE_DOWN
- : ifp->enable_dhcp_client ? MG_TCPIP_STATE_UP
- : MG_TCPIP_STATE_READY;
- if (!up && ifp->enable_dhcp_client) ifp->ip = 0;
- onstatechange(ifp);
- }
- }
- if (ifp->state == MG_TCPIP_STATE_DOWN) return;
- // If IP not configured, send DHCP
- if (ifp->ip == 0 && expired_1000ms) tx_dhcp_discover(ifp);
- // Read data from the network
- if (ifp->driver->rx != NULL) { // Polling driver. We must call it
- size_t len =
- ifp->driver->rx(ifp->recv_queue.buf, ifp->recv_queue.size, ifp);
- if (len > 0) mg_tcpip_rx(ifp, ifp->recv_queue.buf, len);
- } else { // Interrupt-based driver. Fills recv queue itself
- char *buf;
- size_t len = mg_queue_next(&ifp->recv_queue, &buf);
- if (len > 0) {
- mg_tcpip_rx(ifp, buf, len);
- mg_queue_del(&ifp->recv_queue, len);
- }
- }
- // Process timeouts
- for (struct mg_connection *c = ifp->mgr->conns; c != NULL; c = c->next) {
- if (c->is_udp || c->is_listening) continue;
- if (c->is_connecting || c->is_resolving) continue;
- struct connstate *s = (struct connstate *) (c + 1);
- if (uptime_ms > s->timer) {
- if (s->ttype == MIP_TTYPE_ACK) {
- MG_DEBUG(("%lu ack %x %x", c->id, s->seq, s->ack));
- tx_tcp(ifp, s->mac, c->rem.ip, TH_ACK, c->loc.port, c->rem.port,
- mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
- } else {
- if (s->tmiss++ > 2) {
- mg_error(c, "keepalive");
- } else {
- MG_DEBUG(("%lu keepalive", c->id));
- tx_tcp(ifp, s->mac, c->rem.ip, TH_ACK, c->loc.port, c->rem.port,
- mg_htonl(s->seq - 1), mg_htonl(s->ack), "", 0);
- }
- }
- settmout(c, MIP_TTYPE_KEEPALIVE);
- }
- }
- }
- // This function executes in interrupt context, thus it should copy data
- // somewhere fast. Note that newlib's malloc is not thread safe, thus use
- // our lock-free queue with preallocated buffer to copy data and return asap
- void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp) {
- char *p;
- if (mg_queue_book(&ifp->recv_queue, &p, len) >= len) {
- memcpy(p, buf, len);
- mg_queue_add(&ifp->recv_queue, len);
- ifp->nrecv++;
- } else {
- ifp->ndrop++;
- }
- }
- void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
- // If MAC address is not set, make a random one
- if (ifp->mac[0] == 0 && ifp->mac[1] == 0 && ifp->mac[2] == 0 &&
- ifp->mac[3] == 0 && ifp->mac[4] == 0 && ifp->mac[5] == 0) {
- ifp->mac[0] = 0x02; // Locally administered, unicast
- mg_random(&ifp->mac[1], sizeof(ifp->mac) - 1);
- MG_INFO(("MAC not set. Generated random: %M", mg_print_mac, ifp->mac));
- }
- if (ifp->driver->init && !ifp->driver->init(ifp)) {
- MG_ERROR(("driver init failed"));
- } else {
- size_t framesize = 1540;
- ifp->tx.ptr = (char *) calloc(1, framesize), ifp->tx.len = framesize;
- if (ifp->recv_queue.size == 0)
- ifp->recv_queue.size = ifp->driver->rx ? framesize : 8192;
- ifp->recv_queue.buf = (char *) calloc(1, ifp->recv_queue.size);
- ifp->timer_1000ms = mg_millis();
- mgr->priv = ifp;
- ifp->mgr = mgr;
- mgr->extraconnsize = sizeof(struct connstate);
- if (ifp->ip == 0) ifp->enable_dhcp_client = true;
- memset(ifp->gwmac, 255, sizeof(ifp->gwmac)); // Set to broadcast
- mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535
- ifp->eport |=
- MG_EPHEMERAL_PORT_BASE; // Random from MG_EPHEMERAL_PORT_BASE to 65535
- }
- }
- void mg_tcpip_free(struct mg_tcpip_if *ifp) {
- free(ifp->recv_queue.buf);
- free((char *) ifp->tx.ptr);
- }
- int mg_mkpipe(struct mg_mgr *m, mg_event_handler_t fn, void *d, bool udp) {
- (void) m, (void) fn, (void) d, (void) udp;
- MG_ERROR(("Not implemented"));
- return -1;
- }
- static void send_syn(struct mg_connection *c) {
- struct connstate *s = (struct connstate *) (c + 1);
- uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port));
- struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
- tx_tcp(ifp, s->mac, c->rem.ip, TH_SYN, c->loc.port, c->rem.port, isn, 0, NULL,
- 0);
- }
- void mg_connect_resolved(struct mg_connection *c) {
- struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
- c->is_resolving = 0;
- if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE;
- c->loc.ip = ifp->ip;
- c->loc.port = mg_htons(ifp->eport++);
- MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port,
- &c->rem));
- mg_call(c, MG_EV_RESOLVE, NULL);
- if (((c->rem.ip & ifp->mask) == (ifp->ip & ifp->mask))) {
- // If we're in the same LAN, fire an ARP lookup. TODO(cpq): handle this!
- MG_DEBUG(("%lu ARP lookup...", c->id));
- arp_ask(ifp, c->rem.ip);
- c->is_arplooking = 1;
- } else {
- struct connstate *s = (struct connstate *) (c + 1);
- memcpy(s->mac, ifp->gwmac, sizeof(ifp->gwmac));
- if (c->is_udp) {
- mg_call(c, MG_EV_CONNECT, NULL);
- } else {
- send_syn(c);
- c->is_connecting = 1;
- }
- }
- }
- bool mg_open_listener(struct mg_connection *c, const char *url) {
- c->loc.port = mg_htons(mg_url_port(url));
- return true;
- }
- static void write_conn(struct mg_connection *c) {
- long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
- : mg_io_send(c, c->send.buf, c->send.len);
- if (len > 0) {
- mg_iobuf_del(&c->send, 0, (size_t) len);
- mg_call(c, MG_EV_WRITE, &len);
- }
- }
- static void close_conn(struct mg_connection *c) {
- struct connstate *s = (struct connstate *) (c + 1);
- mg_iobuf_free(&s->raw); // For TLS connections, release raw data
- if (c->is_udp == false && c->is_listening == false) { // For TCP conns,
- struct mg_tcpip_if *ifp =
- (struct mg_tcpip_if *) c->mgr->priv; // send TCP FIN
- tx_tcp(ifp, s->mac, c->rem.ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port,
- mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
- }
- mg_close_conn(c);
- }
- static bool can_write(struct mg_connection *c) {
- return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 &&
- c->is_tls_hs == 0 && c->is_arplooking == 0;
- }
- void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
- struct mg_connection *c, *tmp;
- uint64_t now = mg_millis();
- mg_tcpip_poll((struct mg_tcpip_if *) mgr->priv, now);
- mg_timer_poll(&mgr->timers, now);
- for (c = mgr->conns; c != NULL; c = tmp) {
- tmp = c->next;
- mg_call(c, MG_EV_POLL, &now);
- MG_VERBOSE(("%lu .. %c%c%c%c%c", c->id, c->is_tls ? 'T' : 't',
- c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h',
- c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c'));
- if (c->is_tls_hs) mg_tls_handshake(c);
- if (can_write(c)) write_conn(c);
- if (c->is_draining && c->send.len == 0) c->is_closing = 1;
- if (c->is_closing) close_conn(c);
- }
- (void) ms;
- }
- bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
- struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
- bool res = false;
- if (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY) {
- mg_error(c, "net down");
- } else if (c->is_udp) {
- struct connstate *s = (struct connstate *) (c + 1);
- tx_udp(ifp, s->mac, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len);
- res = true;
- } else {
- res = mg_iobuf_add(&c->send, c->send.len, buf, len);
- }
- return res;
- }
- #endif // MG_ENABLE_TCPIP
|