| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115321163211732118321193212032121321223212332124321253212632127321283212932130321313213232133321343213532136321373213832139321403214132142321433214432145321463214732148321493215032151321523215332154321553215632157321583215932160321613216232163321643216532166321673216832169321703217132172321733217432175321763217732178321793218032181321823218332184321853218632187321883218932190321913219232193321943219532196321973219832199322003220132202322033220432205322063220732208322093221032211322123221332214322153221632217322183221932220322213222232223322243222532226322273222832229322303223132232322333223432235322363223732238322393224032241322423224332244322453224632247322483224932250322513225232253322543225532256322573225832259322603226132262322633226432265322663226732268322693227032271322723227332274322753227632277322783227932280322813228232283322843228532286322873228832289322903229132292322933229432295322963229732298322993230032301323023230332304323053230632307323083230932310323113231232313323143231532316323173231832319323203232132322323233232432325323263232732328323293233032331323323233332334323353233632337323383233932340323413234232343323443234532346 |
- // Copyright (c) 2004-2013 Sergey Lyubka
- // Copyright (c) 2013-2025 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_base64_encode_single(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 base64_decode_single(int c, char plus, char slash) {
- 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 == plus) {
- return 62;
- } else if (c == slash) {
- return 63;
- } else if (c == '=') {
- return 64;
- } else {
- return -1;
- }
- }
- static int mg_base64_decode_single(int c) {
- return base64_decode_single(c, '+', '/');
- }
- static int mg_base64url_decode_single(int c) {
- return base64_decode_single(c, '-', '_');
- }
- size_t mg_base64_update(unsigned char ch, char *to, size_t n) {
- unsigned long rem = (n & 3) % 3;
- if (rem == 0) {
- to[n] = (char) mg_base64_encode_single(ch >> 2);
- to[++n] = (char) ((ch & 3) << 4);
- } else if (rem == 1) {
- to[n] = (char) mg_base64_encode_single(to[n] | (ch >> 4));
- to[++n] = (char) ((ch & 15) << 2);
- } else {
- to[n] = (char) mg_base64_encode_single(to[n] | (ch >> 6));
- to[++n] = (char) mg_base64_encode_single(ch & 63);
- n++;
- }
- return n;
- }
- size_t mg_base64_final(char *to, size_t n) {
- size_t 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;
- }
- size_t mg_base64_encode(const unsigned char *p, size_t n, char *to, size_t dl) {
- size_t i, len = 0;
- if (dl > 0) to[0] = '\0';
- if (dl < ((n / 3) + (n % 3 ? 1 : 0)) * 4 + 1) return 0;
- for (i = 0; i < n; i++) len = mg_base64_update(p[i], to, len);
- len = mg_base64_final(to, len);
- return len;
- }
- size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t dl) {
- const char *end = src == NULL ? NULL : src + n; // Cannot add to NULL
- size_t len = 0;
- if (dl < n / 4 * 3 + 1) goto fail;
- while (src != NULL && src + 3 < end) {
- int a = mg_base64_decode_single(src[0]),
- b = mg_base64_decode_single(src[1]),
- c = mg_base64_decode_single(src[2]),
- d = mg_base64_decode_single(src[3]);
- if (a == 64 || a < 0 || b == 64 || b < 0 || c < 0 || d < 0) {
- goto fail;
- }
- 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;
- fail:
- if (dl > 0) dst[0] = '\0';
- return 0;
- }
- size_t mg_base64url_encode(const unsigned char *p, size_t n, char *to,
- size_t dl) {
- size_t i, len = mg_base64_encode(p, n, to, dl);
- if (len == 0) return 0;
- for (i = 0; i < len; i++) {
- if (to[i] == '+') {
- to[i] = '-';
- } else if (to[i] == '/') {
- to[i] = '_';
- }
- }
- while (len > 0 && to[len - 1] == '=') to[--len] = '\0';
- return len;
- }
- size_t mg_base64url_decode(const char *src, size_t n, char *dst, size_t dl) {
- size_t i, len = 0;
- unsigned int bits = 0, v = 0;
- if (dl == 0 || (n & 3) == 1) goto fail;
- for (i = 0; src != NULL && i < n; i++) {
- int c = mg_base64url_decode_single(src[i]);
- if (c == 64) break;
- if (c < 0) goto fail;
- v = (v << 6) | (unsigned int) c;
- bits += 6;
- if (bits >= 8) {
- bits -= 8;
- if (len + 1 >= dl) goto fail;
- dst[len++] = (char) ((v >> bits) & 255U);
- }
- }
- dst[len] = '\0';
- return len;
- fail:
- if (dl > 0) dst[0] = '\0';
- return 0;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/bsd.c"
- #endif
- #if MG_ENABLE_BSD_SOCKETS
- // Queue-based BSD shim is currently TCP/SOCK_STREAM only. UDP/SOCK_DGRAM would
- // need datagram boundaries, e.g. datagram queues or SOD/EOD framing.
- // Unconnected UDP also needs per-packet peer addresses for sendto()/recvfrom().
- // It is also IPv4-only: transports store sockaddr_in and build IPv4 URLs.
- #ifndef MG_ENABLE_BSD_LOG
- #define MG_ENABLE_BSD_LOG 0
- #define bsd_log(type, tag, a, b, c, n)
- #else
- #define MG_BSD_LOG_SOCK 1
- #define MG_BSD_LOG_ACCEPT 2
- #define MG_BSD_LOG_CLOSE 3
- #define MG_BSD_LOG_TRANSPORT 4
- #define MG_BSD_LOG_RESULT 5
- #define MG_BSD_LOG_CONNECT 6
- // Type is always a constant, so optimised builds fold the switch to one log.
- #define bsd_log(type, tag, a, b, c, n) \
- do { \
- switch (type) { \
- case MG_BSD_LOG_SOCK: { \
- struct mg_bsd_sock *log_s = (struct mg_bsd_sock *) (a); \
- MG_DEBUG(("SOCK %s %ld %p", tag, (long) (n), log_s->t)); \
- break; \
- } \
- case MG_BSD_LOG_ACCEPT: { \
- struct mg_connection *log_mc = (struct mg_connection *) (a); \
- if ((n) >= 0) { \
- MG_DEBUG(("ACCEPT %lu %p %u %ld", log_mc->id, c, \
- (unsigned) mg_ntohs(log_mc->rem.port), (long) (n))); \
- } else { \
- MG_DEBUG(("ACCEPT %lu %p %u", log_mc->id, c, \
- (unsigned) mg_ntohs(log_mc->rem.port))); \
- } \
- break; \
- } \
- case MG_BSD_LOG_CLOSE: { \
- struct mg_connection *log_mc = (struct mg_connection *) (a); \
- if ((n) >= 0) MG_INFO(("CLOSE %s %lu %p %ld", tag, log_mc->id, b, \
- (long) (n))); \
- else MG_DEBUG(("CLOSE %s %lu %p", tag, log_mc->id, b)); \
- break; \
- } \
- case MG_BSD_LOG_TRANSPORT: \
- if ((n) >= 0) MG_INFO(("TRANSP %s %p %ld", tag, a, (long) (n))); \
- else MG_DEBUG(("TRANSP %s %p", tag, a)); \
- break; \
- case MG_BSD_LOG_RESULT: \
- MG_DEBUG(("RESULT %s %p %ld", tag, a, (long) (n))); \
- break; \
- case MG_BSD_LOG_CONNECT: \
- if ((c) != NULL) { \
- MG_DEBUG(("CONNECT %s %p %p %s %ld", tag, a, b, \
- (const char *) (c), (long) (n))); \
- } else { \
- MG_DEBUG(("CONNECT %s %p %p %ld", tag, a, b, (long) (n))); \
- } \
- break; \
- } \
- } while (0)
- #endif
- struct mg_bsd_sock {
- void *t; // opaque transport handle
- int fd;
- int domain, type, proto;
- bool nonblock;
- struct sockaddr_in addr; // bind address
- struct sockaddr_in peer; // peer address (after accept/connect)
- struct mg_bsd_sock *next;
- };
- #define MG_BSD_FD_BASE 17777
- // static struct mg_mgr *s_mgr;
- static struct mg_bsd_sock *s_socks;
- static struct mg_bsd_sock *get(int fd) {
- if (fd < MG_BSD_FD_BASE) return NULL;
- for (struct mg_bsd_sock *s = s_socks; s; s = s->next)
- if (s->fd == fd) return s;
- return NULL;
- }
- static int alloc_sock(struct mg_bsd_sock *s) {
- for (int fd = MG_BSD_FD_BASE; ; fd++) {
- if (get(fd) == NULL) { s->fd = fd; break; }
- }
- s->next = s_socks;
- s_socks = s;
- return s->fd;
- }
- static void release_sock(int fd) {
- struct mg_bsd_sock **p = &s_socks;
- while (*p && (*p)->fd != fd) p = &(*p)->next;
- if (*p) *p = (*p)->next;
- }
- int socket(int domain, int type, int proto) {
- struct mg_bsd_sock *s = (struct mg_bsd_sock *) calloc(1, sizeof(*s));
- if (!s) { errno = ENOMEM; return -1; }
- s->t = mg_bsd_transport_new(domain, type, proto);
- if (!s->t) { free(s); return -1; }
- if (alloc_sock(s) < 0) { mg_bsd_transport_free(s->t); free(s); errno = ENOMEM; return -1; }
- s->domain = domain; s->type = type; s->proto = proto;
- return s->fd;
- }
- int bind(int fd, const struct sockaddr *addr, socklen_t len) {
- struct mg_bsd_sock *s = get(fd);
- if (!s) return -1;
- memcpy(&s->addr, addr, len < sizeof(s->addr) ? len : sizeof(s->addr));
- return 0;
- }
- int listen(int fd, int backlog) {
- struct mg_bsd_sock *s = get(fd);
- if (!s) return -1;
- (void) backlog;
- return mg_bsd_transport_listen(s->t, &s->addr);
- }
- int accept(int fd, struct sockaddr *addr, socklen_t *addrlen) {
- struct mg_bsd_sock *ls = get(fd);
- if (!ls) return -1;
- struct sockaddr_in peer;
- memset(&peer, 0, sizeof(peer));
- void *t = mg_bsd_transport_accept(ls->t, &peer, ls->nonblock);
- if (!t) return -1; // errno was set by transport_accept()
- struct mg_bsd_sock *ns = (struct mg_bsd_sock *) calloc(1, sizeof(*ns));
- if (!ns || alloc_sock(ns) < 0) { mg_bsd_transport_close(t); free(ns); errno = ENOMEM; return -1; }
- ns->t = t; ns->domain = ls->domain; ns->type = ls->type; ns->peer = peer;
- if (addr && addrlen) {
- size_t sz = sizeof(peer) < *addrlen ? sizeof(peer) : *addrlen;
- memcpy(addr, &peer, sz);
- *addrlen = (socklen_t) sizeof(peer);
- }
- return ns->fd;
- }
- int connect(int fd, const struct sockaddr *addr, socklen_t len) {
- struct mg_bsd_sock *s = get(fd);
- if (!s) return -1;
- (void) len;
- return mg_bsd_transport_connect(s->t, (const struct sockaddr_in *) addr, s->nonblock);
- }
- ssize_t send(int fd, const void *buf, size_t len, int flags) {
- struct mg_bsd_sock *s = get(fd);
- if (!s) return -1;
- return mg_bsd_transport_send(s->t, buf, len, s->nonblock || (flags & MSG_DONTWAIT));
- }
- ssize_t recv(int fd, void *buf, size_t len, int flags) {
- struct mg_bsd_sock *s = get(fd);
- if (!s) return -1;
- return mg_bsd_transport_recv(s->t, buf, len, s->nonblock || (flags & MSG_DONTWAIT));
- }
- ssize_t sendto(int fd, const void *buf, size_t len, int flags,
- const struct sockaddr *dest, socklen_t addrlen) {
- (void) fd; (void) buf; (void) len; (void) flags; (void) dest; (void) addrlen;
- errno = EPROTONOSUPPORT;
- return -1;
- }
- ssize_t recvfrom(int fd, void *buf, size_t len, int flags,
- struct sockaddr *src, socklen_t *addrlen) {
- (void) fd; (void) buf; (void) len; (void) flags; (void) src; (void) addrlen;
- errno = EPROTONOSUPPORT;
- return -1;
- }
- ssize_t write(int fd, const void *buf, size_t len) { return send(fd, buf, len, 0); }
- ssize_t read(int fd, void *buf, size_t len) { return recv(fd, buf, len, 0); }
- int close(int fd) {
- struct mg_bsd_sock *s = get(fd);
- if (!s) return -1;
- bsd_log(MG_BSD_LOG_SOCK, "CLOSE", s, NULL, NULL, (long) fd);
- mg_bsd_transport_close(s->t);
- bsd_log(MG_BSD_LOG_SOCK, "FREE", s, NULL, NULL, (long) fd);
- release_sock(fd);
- free(s);
- return 0;
- }
- int shutdown(int fd, int how) { (void) how; return close(fd); }
- int fcntl(int fd, int cmd, int arg) {
- struct mg_bsd_sock *s = get(fd);
- if (!s) return -1;
- if (cmd == F_GETFL) return s->nonblock ? O_NONBLOCK : 0;
- if (cmd == F_SETFL) { s->nonblock = (arg & O_NONBLOCK) != 0; return 0; }
- return -1;
- }
- int setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) {
- (void) fd; (void) level; (void) optname; (void) optval; (void) optlen;
- return 0;
- }
- int getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen) {
- (void) fd; (void) level; (void) optname;
- if (optval && optlen && *optlen >= sizeof(int)) { *(int *) optval = 0; *optlen = sizeof(int); }
- return 0;
- }
- int getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen) {
- struct mg_bsd_sock *s = get(fd);
- if (!s) return -1;
- size_t sz = sizeof(s->addr) < *addrlen ? sizeof(s->addr) : *addrlen;
- memcpy(addr, &s->addr, sz);
- *addrlen = (socklen_t) sz;
- return 0;
- }
- int getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen) {
- struct mg_bsd_sock *s = get(fd);
- if (!s) { errno = ENOTCONN; return -1; }
- size_t sz = sizeof(s->peer) < *addrlen ? sizeof(s->peer) : *addrlen;
- memcpy(addr, &s->peer, sz);
- *addrlen = (socklen_t) sz;
- return 0;
- }
- // select/poll: not implemented for queue-based backend
- int select(int nfds, fd_set *r, fd_set *w, fd_set *e, struct timeval *tv) {
- (void) nfds; (void) r; (void) w; (void) e; (void) tv;
- return 0;
- }
- int poll(struct pollfd *fds, unsigned int nfds, int timeout) {
- (void) fds; (void) nfds; (void) timeout;
- return 0;
- }
- // DNS stubs (overridden in the FreeRTOS backend below)
- #if !MG_ENABLE_FREERTOS
- struct hostent *gethostbyname(const char *name) { (void) name; return NULL; }
- int getaddrinfo(const char *node, const char *service,
- const struct addrinfo *hints, struct addrinfo **res) {
- (void) node; (void) service; (void) hints; (void) res;
- return -1;
- }
- void freeaddrinfo(struct addrinfo *res) { (void) res; }
- #endif
- int inet_pton(int af, const char *src, void *dst) {
- struct mg_addr a;
- memset(&a, 0, sizeof(a));
- if (af == AF_INET && mg_aton(mg_str_s(src), &a)) { memcpy(dst, &a.addr.ip4, 4); return 1; }
- return 0;
- }
- const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) {
- if (af == AF_INET && size >= 16) {
- const uint8_t *ip = (const uint8_t *) src;
- snprintf(dst, size, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
- return dst;
- }
- return NULL;
- }
- in_addr_t inet_addr(const char *cp) {
- struct mg_addr a;
- memset(&a, 0, sizeof(a));
- return mg_aton(mg_str_s(cp), &a) ? a.addr.ip4 : (in_addr_t) -1;
- }
- static char s_ntoa_buf[16];
- char *inet_ntoa(struct in_addr in) {
- const uint8_t *ip = (const uint8_t *) &in.s_addr;
- snprintf(s_ntoa_buf, sizeof(s_ntoa_buf), "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
- return s_ntoa_buf;
- }
- #ifdef MG_ENABLE_BSD_PROTOTYPES
- uint16_t htons(uint16_t n) { return mg_htons(n); }
- uint16_t ntohs(uint16_t n) { return mg_htons(n); }
- uint32_t htonl(uint32_t n) { return mg_htonl(n); }
- uint32_t ntohl(uint32_t n) { return mg_htonl(n); }
- #endif
- // ============================================================
- // FreeRTOS + Mongoose transport backend
- // ============================================================
- #if MG_ENABLE_FREERTOS
- #include <queue.h>
- #ifndef MG_BSD_CHUNK_SIZE
- #define MG_BSD_CHUNK_SIZE 256
- #endif
- #ifndef MG_BSD_Q_DEPTH
- #define MG_BSD_Q_DEPTH 4
- #endif
- #ifndef MG_BSD_ACCEPT_MS
- #define MG_BSD_ACCEPT_MS 3000 // Timeout for accept queue handoff
- #endif
- struct mg_bsd_chunk { uint8_t data[MG_BSD_CHUNK_SIZE]; uint16_t len; };
- struct mg_xport {
- struct mg_connection *c; // Mongoose connection, task1-only
- QueueHandle_t recv_q; // task1 writes on MG_EV_READ, task2 reads in recv()
- QueueHandle_t send_q; // task2 writes in send(), task1 drains on MG_EV_POLL
- QueueHandle_t accept_q; // task1 writes on MG_EV_ACCEPT, task2 reads in accept()
- struct sockaddr_in peer;
- uint16_t rx_off;
- uint64_t accept_expire;
- int err;
- bool closed;
- bool orphan; // accepted connection not handed to socket owner
- TaskHandle_t connect_waiter; // task blocked in connect(), woken by MG_EV_CONNECT
- int *connect_result; // where to store 0/−1 connect outcome
- };
- enum mg_bsd_cmd_op { BSD_CMD_LISTEN, BSD_CMD_CLOSE, BSD_CMD_CONNECT, BSD_CMD_RESOLVE };
- struct mg_bsd_cmd {
- enum mg_bsd_cmd_op type;
- struct mg_xport *x;
- char url[64];
- TaskHandle_t caller;
- int *result;
- };
- static QueueHandle_t s_cmd_q;
- static bool bsd_qsend(QueueHandle_t q, const void *item, TickType_t ticks,
- bool reserve) {
- BaseType_t ok = pdFALSE;
- if (!reserve || uxQueueSpacesAvailable(q) > 1) ok = xQueueSend(q, item, ticks);
- if (ok != pdTRUE) MG_ERROR(("%p", q));
- return ok == pdTRUE;
- }
- static bool xport_accept(struct mg_xport *x) {
- if (bsd_qsend(x->accept_q, &x, 0, true)) {
- x->orphan = false; // not an orphan anymore
- x->accept_q = NULL;
- return true;
- }
- return false; // still an orphan, retry later
- }
- // static DNS resolve state; not reentrant (see below)
- static struct { struct mg_addr addr; bool error; TaskHandle_t caller; } s_resolve;
- // gethostbyname statics (official isn't reentrant anyway, and is obsolete)
- static struct hostent s_hostent;
- static char *s_h_aliases[1];
- static char *s_h_addr_list[2];
- static uint32_t s_h_addr;
- static char s_h_name[64];
- static void resolve_cb(struct mg_connection *c, int ev, void *ev_data) {
- bool notify = false;
- if (ev == MG_EV_RESOLVE) {
- s_resolve.addr = c->rem;
- c->is_closing = 1;
- MG_DEBUG(("%lu resolved", c->id));
- notify = true;
- } else if (ev == MG_EV_ERROR) {
- s_resolve.error = true;
- MG_DEBUG(("%lu failed", c->id));
- notify = true;
- } else if (ev == MG_EV_CLOSE) {
- s_resolve.caller = NULL; // The resolver connection has fully unwound.
- MG_DEBUG(("%lu done", c->id));
- }
- if (notify) xTaskNotifyGive(s_resolve.caller);
- (void) ev_data;
- }
- // Allocate transport for an accepted connection (recv+send queues only)
- static struct mg_xport *xport_alloc(void) {
- struct mg_xport *x = (struct mg_xport *) calloc(1, sizeof(*x));
- if (!x) return NULL;
- // +1 keeps a terminal EOF/error slot for MG_EV_CLOSE
- x->recv_q = xQueueCreate(MG_BSD_Q_DEPTH + 1, sizeof(struct mg_bsd_chunk));
- x->send_q = xQueueCreate(MG_BSD_Q_DEPTH, sizeof(struct mg_bsd_chunk));
- if (!x->recv_q || !x->send_q) { mg_bsd_transport_free(x); return NULL; }
- x->orphan = true; // haven't attached this connection to its socket
- return x;
- }
- static void xport_ev(struct mg_connection *c, int ev, void *ev_data) {
- struct mg_xport *x = (struct mg_xport *) c->fn_data;
- if (!x) return;
- if (ev == MG_EV_ACCEPT) {
- // c is the new accepted connection; x is the listening transport
- bool ok;
- struct mg_xport *nx = xport_alloc();
- if (!nx) { c->fn_data = NULL; mg_error(c, "accept OOM"); return; }
- nx->c = c;
- nx->accept_q = x->accept_q;
- nx->accept_expire = mg_millis() + MG_BSD_ACCEPT_MS;
- nx->peer.sin_family = AF_INET;
- nx->peer.sin_port = c->rem.port;
- memcpy(&nx->peer.sin_addr, &c->rem.addr.ip4, 4);
- c->fn_data = nx;
- ok = xport_accept(nx); // leaves orphaned on failure, retry on POLL
- bsd_log(MG_BSD_LOG_ACCEPT, NULL, c, x, nx, ok ? 1 : 0);
- } else if (ev == MG_EV_POLL && x->orphan && x->accept_q) {
- if (uxQueueSpacesAvailable(x->accept_q) > 1 && xport_accept(x)) {
- bsd_log(MG_BSD_LOG_ACCEPT, NULL, c, NULL, x, 1);
- } else if (mg_millis() > x->accept_expire) { // retried enough, give up
- x->err = EIO;
- mg_error(c, "accept_q");
- }
- } else if (ev == MG_EV_READ || ev == MG_EV_POLL) {
- if (x->recv_q) { // let POLL resume abandoned READ processing on full queue
- // Drain c->recv into recv_q in fixed-size chunks; task1 owns c->recv
- size_t off = 0;
- while (off < c->recv.len) {
- struct mg_bsd_chunk chunk;
- size_t n = c->recv.len - off;
- if (n > MG_BSD_CHUNK_SIZE) n = MG_BSD_CHUNK_SIZE;
- memcpy(chunk.data, c->recv.buf + off, n);
- chunk.len = (uint16_t) n;
- if (uxQueueSpacesAvailable(x->recv_q) <= 1 ||
- !bsd_qsend(x->recv_q, &chunk, 0, false)) break; // retry later
- off += n;
- }
- mg_iobuf_del(&c->recv, 0, off);
- }
- if (ev == MG_EV_POLL && x->send_q) {
- // Drain send_q → mg_send(); task1 owns c
- struct mg_bsd_chunk chunk;
- while (xQueuePeek(x->send_q, &chunk, 0) == pdTRUE) {
- if (!mg_send(c, chunk.data, chunk.len)) break; // retry later
- xQueueReceive(x->send_q, &chunk, 0);
- }
- }
- } else if (ev == MG_EV_CONNECT) {
- // Outgoing connection established: wake the task blocked in connect()
- if (x->connect_waiter) {
- bsd_log(MG_BSD_LOG_CONNECT, "OK", x, c, NULL, 0);
- if (x->connect_result) *x->connect_result = 0;
- TaskHandle_t h = x->connect_waiter;
- x->connect_waiter = NULL; x->connect_result = NULL;
- xTaskNotifyGive(h);
- }
- } else if (ev == MG_EV_ERROR) { // remember error condition
- if (x->err == 0) x->err = EIO; // and let CLOSE handle it
- bsd_log(MG_BSD_LOG_CLOSE, "ERR", c, x, NULL, (long) x->err);
- } else if (ev == MG_EV_CLOSE) {
- bsd_log(MG_BSD_LOG_CLOSE, "IN", c, x, NULL, -1);
- x->c = NULL; x->closed = true; c->fn_data = NULL;
- // If connect() is still waiting, signal failure
- if (x->connect_waiter) {
- bsd_log(MG_BSD_LOG_CONNECT, "FAIL", x, c, NULL, -1);
- if (x->connect_result) *x->connect_result = -1;
- TaskHandle_t h = x->connect_waiter;
- x->connect_waiter = NULL; x->connect_result = NULL;
- xTaskNotifyGive(h);
- // The notified task owns the transport and can close/free it immediately.
- return;
- }
- if (x->orphan) { // connection --> socket attachment failed
- x->accept_q = NULL; // borrowed from listener; do not delete it here
- bsd_log(MG_BSD_LOG_CLOSE, "FREE", c, x, NULL, -1);
- mg_bsd_transport_free(x); // connection closed, release resources
- return;
- }
- if (x->recv_q) {
- struct mg_bsd_chunk eof;
- bool ok;
- memset(&eof, 0, sizeof(eof));
- eof.len = 0;
- ok = bsd_qsend(x->recv_q, &eof, 0, false);
- bsd_log(MG_BSD_LOG_CLOSE, "EOF>", c, x, NULL, ok ? 1 : 0);
- if (!ok) MG_ERROR(("recv_q close notification failed"));
- // recv() wakeup transfers control to the transport owner.
- return;
- }
- if (x->accept_q) {
- struct mg_xport *nil = NULL;
- bool ok;
- ok = bsd_qsend(x->accept_q, &nil, 0, false);
- bsd_log(MG_BSD_LOG_CLOSE, "ACCEPT>", c, x, NULL, ok ? 1 : 0);
- if (!ok) MG_ERROR(("accept_q close notification failed"));
- // accept() wakeup transfers control to the transport owner.
- return;
- }
- bsd_log(MG_BSD_LOG_CLOSE, "OUT", c, x, NULL, -1);
- }
- (void) ev_data;
- }
- void mg_bsd_init(void) {
- s_cmd_q = xQueueCreate(8, sizeof(struct mg_bsd_cmd));
- }
- void mg_bsd_poll(struct mg_mgr *mgr) {
- struct mg_bsd_cmd cmd;
- if (s_cmd_q == NULL) return;
- while (xQueueReceive(s_cmd_q, &cmd, 0) == pdTRUE) {
- bool notify = true;
- if (cmd.type == BSD_CMD_LISTEN) {
- struct mg_connection *c = mg_listen(mgr, cmd.url, xport_ev, cmd.x);
- cmd.x->c = c;
- *cmd.result = c ? 0 : -1;
- } else if (cmd.type == BSD_CMD_CLOSE) {
- bsd_log(MG_BSD_LOG_TRANSPORT, "CMD", cmd.x, NULL, NULL, -1);
- if (cmd.x->c) {
- cmd.x->c->fn_data = NULL;
- cmd.x->c->is_draining = 1;
- cmd.x->c = NULL;
- }
- cmd.x->closed = true;
- *cmd.result = 0;
- } else if (cmd.type == BSD_CMD_CONNECT) {
- cmd.x->connect_waiter = cmd.caller;
- cmd.x->connect_result = cmd.result;
- struct mg_connection *c = mg_connect(mgr, cmd.url, xport_ev, cmd.x);
- bsd_log(MG_BSD_LOG_CONNECT, "NEW", cmd.x, c, cmd.url, c ? 1 : 0);
- cmd.x->c = c;
- if (!c) { *cmd.result = -1; cmd.x->connect_waiter = NULL; cmd.x->connect_result = NULL; }
- else notify = false; // xport_ev notifies when connected or on error
- } else if (cmd.type == BSD_CMD_RESOLVE) {
- s_resolve.error = false;
- s_resolve.caller = cmd.caller;
- struct mg_connection *c;
- c = mg_alloc_conn(mgr);
- if (c == NULL) {
- } else {
- c->fn = resolve_cb;
- LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
- mg_call(c, MG_EV_OPEN, NULL);
- MG_DEBUG(("%lu resolve %s", c->id, cmd.url));
- mg_resolve(c, cmd.url);
- notify = false; // resolve_cb notifies when done
- }
- }
- if (notify) xTaskNotifyGive(cmd.caller);
- }
- }
- void *mg_bsd_transport_new(int domain, int type, int proto) {
- if (domain != AF_INET || type != SOCK_STREAM ||
- (proto != 0 && proto != IPPROTO_TCP)) {
- errno = EPROTONOSUPPORT;
- return NULL;
- }
- // For socket() calls: allocate accept_q only; recv/send added when needed.
- // +1 keeps a terminal slot for MG_EV_CLOSE without blocking Mongoose.
- struct mg_xport *x = (struct mg_xport *) calloc(1, sizeof(*x));
- if (!x) return NULL;
- x->accept_q = xQueueCreate(MG_BSD_BACKLOG + 1, sizeof(struct mg_xport *));
- if (!x->accept_q) { free(x); return NULL; }
- return x;
- }
- void mg_bsd_transport_free(void *t) {
- struct mg_xport *x = (struct mg_xport *) t;
- if (!x) return;
- bsd_log(MG_BSD_LOG_TRANSPORT, "FREE", x, NULL, NULL, -1);
- if (x->recv_q) vQueueDelete(x->recv_q);
- if (x->send_q) vQueueDelete(x->send_q);
- if (x->accept_q) vQueueDelete(x->accept_q);
- free(x);
- }
- int mg_bsd_transport_listen(void *t, const struct sockaddr_in *addr) {
- struct mg_xport *x = (struct mg_xport *) t;
- int result = -1;
- struct mg_bsd_cmd cmd = {BSD_CMD_LISTEN, x, {0}, xTaskGetCurrentTaskHandle(), &result};
- snprintf(cmd.url, sizeof(cmd.url), "tcp://0.0.0.0:%d", mg_ntohs(addr->sin_port));
- if (!bsd_qsend(s_cmd_q, &cmd, portMAX_DELAY, false)) return -1;
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
- return result;
- }
- void *mg_bsd_transport_accept(void *t, struct sockaddr_in *peer, bool nonblock) {
- struct mg_xport *x = (struct mg_xport *) t;
- struct mg_xport *nx = NULL;
- TickType_t ticks = nonblock ? 0 : portMAX_DELAY;
- if (xQueueReceive(x->accept_q, &nx, ticks) != pdTRUE) {
- errno = x->closed ? (x->err ? x->err : EIO) : (nonblock ? EAGAIN : EIO);
- return NULL;
- }
- if (!nx) { errno = x->err ? x->err : EIO; return NULL; }
- if (peer) *peer = nx->peer;
- return nx;
- }
- ssize_t mg_bsd_transport_recv(void *t, void *buf, size_t len, bool nonblock) {
- struct mg_xport *x = (struct mg_xport *) t;
- uint8_t *p = (uint8_t *) buf;
- size_t recvd = 0;
- TickType_t ticks = nonblock ? 0 : portMAX_DELAY;
- while (recvd < len) {
- struct mg_bsd_chunk chunk;
- if (xQueuePeek(x->recv_q, &chunk, recvd == 0 ? ticks : 0) != pdTRUE) {
- if (recvd > 0) break;
- errno = x->closed ? (x->err ? x->err : EIO) : (nonblock ? EAGAIN : EIO);
- return -1;
- }
- if (chunk.len == 0) {
- if (recvd > 0) break;
- xQueueReceive(x->recv_q, &chunk, 0);
- if (x->err) { errno = x->err; return -1; }
- return 0; // EOF
- } else {
- size_t n = chunk.len - x->rx_off;
- if (n > len - recvd) n = len - recvd;
- memcpy(p + recvd, chunk.data + x->rx_off, n);
- recvd += n;
- x->rx_off = (uint16_t) (x->rx_off + n);
- if (x->rx_off >= chunk.len) {
- xQueueReceive(x->recv_q, &chunk, 0);
- x->rx_off = 0;
- }
- }
- }
- return (ssize_t) recvd;
- }
- ssize_t mg_bsd_transport_send(void *t, const void *buf, size_t len, bool nonblock) {
- struct mg_xport *x = (struct mg_xport *) t;
- if (x->closed) { errno = x->err ? x->err : EPIPE; return -1; }
- size_t sent = 0;
- TickType_t ticks = nonblock ? 0 : portMAX_DELAY;
- while (sent < len) {
- struct mg_bsd_chunk chunk;
- size_t n = len - sent;
- if (n > MG_BSD_CHUNK_SIZE) n = MG_BSD_CHUNK_SIZE;
- memcpy(chunk.data, (const uint8_t *) buf + sent, n);
- chunk.len = (uint16_t) n;
- if (!bsd_qsend(x->send_q, &chunk, ticks, false)) {
- errno = x->closed ? (x->err ? x->err : EPIPE) : (nonblock ? EAGAIN : EIO);
- return sent > 0 ? (ssize_t) sent : -1;
- }
- sent += n;
- }
- return sent > 0 ? (ssize_t) sent : (errno = EAGAIN, -1);
- }
- int mg_bsd_transport_connect(void *t, const struct sockaddr_in *addr, bool nonblock) {
- struct mg_xport *x = (struct mg_xport *) t;
- (void) nonblock;
- if (!x->recv_q) x->recv_q = xQueueCreate(MG_BSD_Q_DEPTH + 1, sizeof(struct mg_bsd_chunk));
- if (!x->send_q) x->send_q = xQueueCreate(MG_BSD_Q_DEPTH, sizeof(struct mg_bsd_chunk));
- if (!x->recv_q || !x->send_q) { errno = ENOMEM; return -1; }
- int result = -1;
- struct mg_bsd_cmd cmd = {BSD_CMD_CONNECT, x, {0}, xTaskGetCurrentTaskHandle(), &result};
- uint8_t *ip = (uint8_t *) &addr->sin_addr.s_addr;
- snprintf(cmd.url, sizeof(cmd.url), "tcp://%d.%d.%d.%d:%d",
- ip[0], ip[1], ip[2], ip[3], mg_ntohs(addr->sin_port));
- bsd_log(MG_BSD_LOG_CONNECT, "REQ", x, NULL, cmd.url, -1);
- if (!bsd_qsend(s_cmd_q, &cmd, portMAX_DELAY, false)) return -1;
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
- if (result != 0) errno = x->err ? x->err : EIO;
- return result;
- }
- // gethostbyname: resolve via Mongoose DNS (not reentrant)
- struct hostent *gethostbyname(const char *name) {
- struct mg_bsd_cmd cmd = {BSD_CMD_RESOLVE, NULL, {0}, xTaskGetCurrentTaskHandle(), NULL};
- snprintf(cmd.url, sizeof(cmd.url), "%s", name);
- if (!bsd_qsend(s_cmd_q, &cmd, portMAX_DELAY, false)) return NULL;
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
- if (s_resolve.error) return NULL;
- s_h_addr = s_resolve.addr.addr.ip4;
- s_h_addr_list[0] = (char *) &s_h_addr;
- s_h_addr_list[1] = NULL;
- s_h_aliases[0] = NULL;
- snprintf(s_h_name, sizeof(s_h_name), "%s", name);
- s_hostent.h_name = s_h_name;
- s_hostent.h_aliases = s_h_aliases;
- s_hostent.h_addrtype = AF_INET;
- s_hostent.h_length = 4;
- s_hostent.h_addr_list = s_h_addr_list;
- return &s_hostent;
- }
- int getaddrinfo(const char *node, const char *service,
- const struct addrinfo *hints, struct addrinfo **res) {
- struct hostent *h = gethostbyname(node);
- if (!h) return -1;
- struct addrinfo *ai = (struct addrinfo *) calloc(1, sizeof(*ai));
- struct sockaddr_in *sa = (struct sockaddr_in *) calloc(1, sizeof(*sa));
- if (!ai || !sa) { free(ai); free(sa); return -1; }
- sa->sin_family = AF_INET;
- memcpy(&sa->sin_addr, h->h_addr, 4);
- if (service) sa->sin_port = htons((uint16_t) atoi(service));
- ai->ai_family = AF_INET;
- ai->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;
- ai->ai_addrlen = sizeof(*sa);
- ai->ai_addr = (struct sockaddr *) sa;
- *res = ai;
- return 0;
- }
- void freeaddrinfo(struct addrinfo *res) {
- while (res) {
- struct addrinfo *next = res->ai_next;
- free(res->ai_addr);
- free(res);
- res = next;
- }
- }
- void mg_bsd_transport_close(void *t) {
- struct mg_xport *x = (struct mg_xport *) t;
- bsd_log(MG_BSD_LOG_TRANSPORT, "CLOSE", x, NULL, NULL, -1);
- if (!x->closed && x->c) {
- int result = 0;
- struct mg_bsd_cmd cmd = {BSD_CMD_CLOSE, x, {0}, xTaskGetCurrentTaskHandle(), &result};
- bool ok = bsd_qsend(s_cmd_q, &cmd, portMAX_DELAY, false);
- bsd_log(MG_BSD_LOG_TRANSPORT, "CMD>", x, NULL, NULL, ok ? 1 : 0);
- if (ok) {
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
- bsd_log(MG_BSD_LOG_RESULT, "CMD", x, NULL, NULL, (long) result);
- }
- }
- bsd_log(MG_BSD_LOG_TRANSPORT, "FREE_REQ", x, NULL, NULL, -1);
- mg_bsd_transport_free(x);
- }
- #ifndef MG_WAKEUP_QUEUE_DEPTH
- #define MG_WAKEUP_QUEUE_DEPTH 4
- #endif
- struct wumsg {
- unsigned long id;
- size_t len;
- uint8_t data[];
- };
- static void wufn(struct mg_connection *c, int ev, void *ev_data) {
- QueueHandle_t q = (QueueHandle_t) c->mgr->pipe.q;
- if (ev == MG_EV_POLL) {
- struct wumsg *m;
- if (xQueueReceive(q, &m, 0) == pdTRUE) {
- struct mg_connection *t;
- for (t = c->mgr->conns; t != NULL; t = t->next) {
- if (t->id == m->id) {
- struct mg_str data = mg_str_n((char *) m->data, m->len);
- mg_call(t, MG_EV_WAKEUP, &data);
- break;
- }
- }
- free(m);
- }
- } else if (ev == MG_EV_CLOSE) {
- struct wumsg *m;
- while (xQueueReceive(q, &m, 0) == pdTRUE) free(m);
- vQueueDelete(q);
- c->mgr->pipe.q = NULL;
- }
- (void) ev_data;
- }
- bool mg_wakeup_init(struct mg_mgr *mgr) {
- struct mg_connection *c;
- if (mgr->pipe.q != NULL) return true;
- mgr->pipe.q = xQueueCreate(MG_WAKEUP_QUEUE_DEPTH, sizeof(void *));
- if (mgr->pipe.q == NULL) {
- MG_ERROR(("Cannot create queue"));
- return false;
- }
- c = mg_alloc_conn(mgr);
- if (c == NULL) {
- vQueueDelete((QueueHandle_t) mgr->pipe.q);
- mgr->pipe.q = NULL;
- return false;
- }
- c->fd = (void *) (size_t) MG_INVALID_SOCKET;
- c->fn = wufn;
- LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
- MG_DEBUG(("%lu queue %p", c->id, mgr->pipe.q));
- mg_call(c, MG_EV_OPEN, NULL);
- return true;
- }
- bool mg_wakeup(struct mg_mgr *mgr, unsigned long conn_id, const void *buf,
- size_t len) {
- struct wumsg *m;
- if (mgr->pipe.q == NULL || conn_id == 0) return false;
- m = (struct wumsg *) calloc(1, sizeof(*m) + len);
- if (m == NULL) {
- MG_ERROR(("OOM"));
- return false;
- }
- m->id = conn_id;
- m->len = len;
- memcpy(m->data, buf, len);
- if (!bsd_qsend((QueueHandle_t) mgr->pipe.q, &m, 0, false)) {
- free(m);
- return false;
- }
- return true;
- }
- #endif // MG_ENABLE_FREERTOS
- #endif // MG_ENABLE_BSD_SOCKETS
- #ifdef MG_ENABLE_LINES
- #line 1 "src/dash.c"
- #endif
- #define MG_NO_CACHE_HEADERS "Cache-Control: no-cache\r\n"
- #define MG_JSON_HEADERS "Content-Type: application/json\r\n" MG_NO_CACHE_HEADERS
- #define CONN_HANDLED 'Z'
- struct mg_dash_cdata {
- char marker;
- struct mg_dash_user *u;
- struct mg_dash *dash;
- };
- static struct mg_dash_user s_guest;
- static struct mg_dash_user *s_users; // List of authenticated users
- static struct mg_str trimq(struct mg_str s) { // Trim double quotes
- if (s.len > 1 && s.buf[0] == '"') s.len -= 2, s.buf++;
- return s;
- }
- static struct mg_field_set *mg_dash_find_field_set(struct mg_dash *dash,
- struct mg_str name) {
- struct mg_field_set *fs;
- for (fs = dash->sets; fs != NULL; fs = fs->next) {
- if (mg_strcmp(name, mg_str(fs->name)) == 0) return fs;
- }
- return NULL;
- }
- // static struct mg_field *mg_dash_find_field(struct mg_field *fields,
- // struct mg_str name) {
- // size_t i;
- // for (i = 0; fields != NULL && fields[i].name != NULL; i++) {
- // if (mg_strcmp(name, mg_str(fields[i].name)) == 0) return &fields[i];
- // }
- // return NULL;
- // }
- static size_t mg_print_field(mg_pfn_t fn, void *arg, va_list *ap) {
- struct mg_field *f = va_arg(*ap, struct mg_field *);
- size_t n = 0;
- n += mg_xprintf(fn, arg, "%m:", MG_ESC(f->name));
- if (f->type == MG_VAL_BOOL) {
- n += mg_xprintf(fn, arg, "%s", *(bool *) f->value ? "true" : "false");
- } else if (f->type == MG_VAL_INT) {
- n += mg_xprintf(fn, arg, "%d", *(int *) f->value);
- } else if (f->type == MG_VAL_UINT64) {
- n += mg_xprintf(fn, arg, "%llu", (uint64_t) *(uint64_t *) f->value);
- } else if (f->type == MG_VAL_DBL) {
- n += mg_xprintf(fn, arg, "%.2f", *(double *) f->value);
- } else if (f->type == MG_VAL_STR) {
- n += mg_xprintf(fn, arg, "%m", MG_ESC(f->value));
- } else if (f->type == MG_VAL_RAW) {
- n += mg_xprintf(fn, arg, "%s", f->value);
- } else {
- n += mg_xprintf(fn, arg, "null");
- }
- return n;
- }
- static size_t mg_print_field_set(mg_pfn_t fn, void *arg, va_list *ap) {
- struct mg_field_set *set = va_arg(*ap, struct mg_field_set *);
- size_t i, n = 0;
- n += mg_xprintf(fn, arg, "{");
- for (i = 0; set != NULL && set->fields[i].name != NULL; i++) {
- if (i > 0) n += mg_xprintf(fn, arg, ",");
- n += mg_xprintf(fn, arg, "%M", mg_print_field, &set->fields[i]);
- }
- n += mg_xprintf(fn, arg, "}");
- return n;
- }
- static int mg_dash_array_size(struct mg_field_set *set,
- struct mg_dash_user *u) {
- int saved = *set->index, sz = -1;
- *set->index = -1;
- if (set->fn) {
- if (set->fn(MG_DASH_READ, u)) sz = *set->index;
- } else if (set->get_dir) {
- mg_dash_dir_read(set, u);
- sz = *set->index;
- }
- *set->index = saved;
- return sz;
- }
- static size_t mg_dash_print_array(mg_pfn_t fn, void *arg, va_list *ap) {
- struct mg_field_set *set = va_arg(*ap, struct mg_field_set *);
- int from = va_arg(*ap, int);
- int to = va_arg(*ap, int);
- struct mg_dash_user *u = va_arg(*ap, struct mg_dash_user *);
- bool started = false;
- int saved = *set->index;
- size_t n = 0;
- *set->index = from;
- n += mg_xprintf(fn, arg, "[");
- for (;;) {
- bool done = to >= 0 && *set->index > to;
- if (!done) {
- if (set->fn)
- set->fn(MG_DASH_READ, u);
- else if (set->get_dir)
- mg_dash_dir_read(set, u);
- done = *set->index < 0;
- }
- if (done) break;
- n += mg_xprintf(fn, arg, "%s%M", started ? "," : "", mg_print_field_set,
- set);
- started = true;
- (*set->index)++;
- }
- n += mg_xprintf(fn, arg, "]");
- *set->index = saved;
- return n;
- }
- static size_t mg_dash_print_endpoint(mg_pfn_t fn, void *arg, va_list *ap) {
- struct mg_dash *dash = va_arg(*ap, struct mg_dash *);
- struct mg_dash_user *u = va_arg(*ap, struct mg_dash_user *);
- struct mg_str *name = va_arg(*ap, struct mg_str *);
- struct mg_str *from_str = va_arg(*ap, struct mg_str *);
- struct mg_str *to_str = va_arg(*ap, struct mg_str *);
- struct mg_field_set *set = mg_dash_find_field_set(dash, *name);
- size_t n = 0;
- if (name->len == 0) {
- struct mg_field_set *fs;
- const char *comma = "";
- n += mg_xprintf(fn, arg, "{");
- for (fs = dash->sets; fs != NULL; fs = fs->next) {
- if (fs->index != NULL) {
- int sz = mg_dash_array_size(fs, u);
- if (sz < 0) continue;
- n += mg_xprintf(fn, arg, comma);
- n += mg_xprintf(fn, arg, "%m:%d", MG_ESC(fs->name), sz);
- } else {
- if (fs->fn && !fs->fn(MG_DASH_READ, u)) continue;
- n += mg_xprintf(fn, arg, comma);
- n += mg_xprintf(fn, arg, "%m:%M", MG_ESC(fs->name), mg_print_field_set,
- fs);
- }
- comma = ",";
- }
- n += mg_xprintf(fn, arg, "}");
- } else if (set != NULL && (set->fn == NULL || set->fn(MG_DASH_READ, u))) {
- if (set->index != NULL && from_str != NULL && from_str->len > 0) {
- int from = 0, to = 0;
- mg_str_to_num(*from_str, 10, &from, sizeof(from));
- to = from;
- if (to_str != NULL && to_str->len > 0) {
- mg_str_to_num(*to_str, 10, &to, sizeof(to));
- }
- n += mg_xprintf(fn, arg, "%M", mg_dash_print_array, set, from, to, u);
- } else if (set->index != NULL) {
- n += mg_xprintf(fn, arg, "%d", mg_dash_array_size(set, u));
- } else {
- n += mg_xprintf(fn, arg, "%M", mg_print_field_set, set);
- }
- } else {
- n += mg_xprintf(fn, arg, "null");
- }
- return n;
- }
- void mg_dash_send_change(struct mg_mgr *mgr, struct mg_field_set *set) {
- struct mg_connection *c;
- for (c = mgr->conns; c != NULL; c = c->next) {
- struct mg_dash_cdata *d;
- struct mg_dash_user *u;
- if (!c->is_websocket) continue;
- d = (struct mg_dash_cdata *) c->data;
- u = d->u;
- if (u == NULL) continue;
- if (set->index != NULL && *set->index < 0) {
- int sz = mg_dash_array_size(set, u);
- if (sz < 0) continue;
- mg_ws_printf(c, WEBSOCKET_OP_TEXT, "{%m:%m,%m:{%m:%d}}", MG_ESC("method"),
- MG_ESC("change"), MG_ESC("params"), MG_ESC(set->name), sz);
- } else {
- int saved_idx = set->index != NULL ? *set->index : 0;
- bool ok = set->fn ? set->fn(MG_DASH_READ, u)
- : (set->get_dir ? mg_dash_dir_read(set, u) : true);
- if (!ok) {
- if (set->index != NULL) *set->index = saved_idx;
- continue;
- }
- if (set->index != NULL) {
- char key[64];
- mg_snprintf(key, sizeof(key), "%s/%d", set->name, *set->index);
- mg_ws_printf(c, WEBSOCKET_OP_TEXT, "{%m:%m,%m:{%m:%M}}",
- MG_ESC("method"), MG_ESC("change"), MG_ESC("params"),
- MG_ESC(key), mg_print_field_set, set);
- *set->index = saved_idx;
- } else {
- mg_ws_printf(c, WEBSOCKET_OP_TEXT, "{%m:%m,%m:{%m:%M}}",
- MG_ESC("method"), MG_ESC("change"), MG_ESC("params"),
- MG_ESC(set->name), mg_print_field_set, set);
- }
- }
- }
- }
- static int mg_dash_parse_field(struct mg_str json, struct mg_field *f) {
- char json_path[128];
- bool ok = false;
- mg_snprintf(json_path, sizeof(json_path), "$.%s", f->name);
- if (f->type == MG_VAL_BOOL) {
- ok = f->value_size == sizeof(bool) &&
- mg_json_get_bool(json, json_path, (bool *) f->value);
- } else if (f->type == MG_VAL_INT) {
- double d;
- if (f->value_size == sizeof(int) && mg_json_get_num(json, json_path, &d) &&
- d == (int) d) {
- *(int *) f->value = (int) d;
- ok = true;
- }
- } else if (f->type == MG_VAL_UINT64) {
- double d;
- if (f->value_size == sizeof(uint64_t) &&
- mg_json_get_num(json, json_path, &d) && d == (double) (int64_t) d) {
- *(uint64_t *) f->value = (uint64_t) d;
- ok = true;
- }
- } else if (f->type == MG_VAL_DBL) {
- ok = f->value_size == sizeof(double) &&
- mg_json_get_num(json, json_path, (double *) f->value);
- } else if (f->type == MG_VAL_STR && f->value_size > 0) {
- struct mg_str tok = mg_json_get_tok(json, json_path);
- ok = tok.len >= 2 && tok.buf[0] == '"' &&
- tok.buf[tok.len - 1] == '"';
- if (ok) mg_json_unescape(json, json_path, (char *) f->value, f->value_size);
- } else if (f->type == MG_VAL_RAW && f->value_size > 0) {
- ok = mg_snprintf((char *) f->value, f->value_size, "%.*s", json.len,
- json.buf) == json.len;
- }
- return ok;
- }
- static int mg_dash_apply(struct mg_connection *c, struct mg_dash *dash,
- struct mg_str json, struct mg_dash_user *u) {
- struct mg_str key, val;
- size_t ofs = 0;
- int total_count = 0;
- while ((ofs = mg_json_next(json, ofs, &key, &val)) > 0) {
- struct mg_field_set *set = mg_dash_find_field_set(dash, trimq(key));
- int count = 0;
- if (set == NULL) {
- MG_ERROR(("UNKNOWN SET: [%.*s]", key.len, key.buf));
- continue;
- }
- if (set->fn != NULL && !set->fn(MG_DASH_WRITE, u)) continue; // auth check
- {
- size_t i;
- for (i = 0; set->fields[i].name != NULL; i++) {
- if (mg_dash_parse_field(val, &set->fields[i])) count++;
- }
- }
- if (count) {
- if (set->fn) set->fn(MG_DASH_WRITE, u); // apply side effects
- mg_dash_send_change(c->mgr, set);
- total_count += count;
- }
- }
- return total_count;
- }
- bool mg_dash_dir_read(struct mg_field_set *set, struct mg_dash_user *u) {
- char dir[256], fname[128] = "";
- struct mg_fs *fs = u->dash->upload_fs ? u->dash->upload_fs : &mg_fs_posix;
- struct mg_field *name_field = NULL, *size_field = NULL;
- size_t i;
- if (!set->get_dir(u, dir, sizeof(dir))) return false;
- for (i = 0; set->fields[i].name != NULL; i++) {
- if (name_field == NULL && set->fields[i].type == MG_VAL_STR &&
- strcmp(set->fields[i].name, "name") == 0)
- name_field = &set->fields[i];
- if (size_field == NULL && strcmp(set->fields[i].name, "size") == 0)
- size_field = &set->fields[i];
- }
- if (name_field == NULL) return false;
- if (*set->index == -1) { // Size query: count all files
- int count = 0;
- while (mg_fs_ls(fs, dir, fname, sizeof(fname))) count++;
- *set->index = count;
- return true;
- }
- { // Regular read: scan to *set->index
- int target = *set->index, cur = 0;
- while (mg_fs_ls(fs, dir, fname, sizeof(fname))) {
- if (cur++ == target) {
- mg_snprintf((char *) name_field->value, name_field->value_size, "%s",
- fname);
- if (size_field != NULL) {
- char path[512];
- size_t sz = 0;
- mg_snprintf(path, sizeof(path), "%s/%s", dir, fname);
- fs->st(path, &sz, NULL);
- if (size_field->type == MG_VAL_UINT64)
- *(uint64_t *) size_field->value = (uint64_t) sz;
- else if (size_field->type == MG_VAL_INT)
- *(int *) size_field->value = (int) sz;
- }
- return true;
- }
- }
- *set->index = -1; // No more entries
- return true;
- }
- }
- static bool mg_dash_set_file_name(struct mg_field_set *set,
- struct mg_str name) {
- size_t i;
- for (i = 0; set->fields[i].name != NULL; i++) {
- struct mg_field *f = &set->fields[i];
- if (f->type == MG_VAL_STR && strcmp(f->name, "name") == 0) {
- mg_snprintf((char *) f->value, f->value_size, "%.*s", (int) name.len,
- name.buf);
- return true;
- }
- }
- return false;
- }
- static inline void mg_log_http_req(struct mg_connection *c,
- struct mg_http_message *hm) {
- int len = 0;
- size_t n, spaces = 0, body_n = hm->body.len;
- struct mg_http_message tmp;
- memset(&tmp, 0, sizeof(tmp));
- len = mg_http_parse((char *) c->send.buf, c->send.len, &tmp);
- n = (len < 0 || (size_t) len > c->send.len) ? c->send.len : (size_t) len;
- while ((n + spaces) < c->send.len && spaces < c->send.len &&
- (c->send.buf[c->send.len - spaces - 1] == '\r' ||
- c->send.buf[c->send.len - spaces - 1] == '\n'))
- spaces++;
- // hm->body.len comes from Content-Length and can be larger than the bytes
- // actually buffered so far (e.g. mid-stream uploads); cap the preview to
- // what's actually present in c->recv, or we'd read past its end
- {
- char *recv_end = (char *) c->recv.buf + c->recv.len;
- if (hm->body.buf >= (char *) c->recv.buf && hm->body.buf <= recv_end) {
- size_t avail = (size_t) (recv_end - hm->body.buf);
- if (body_n > avail) body_n = avail;
- } else {
- body_n = 0;
- }
- }
- MG_DEBUG(("%lu %.*s %.*s%s%.*s %.*s: %lu %.*s -> %lu %.*s", c->id,
- hm->method.len, hm->method.buf, hm->uri.len, hm->uri.buf,
- hm->query.len > 0 ? "?" : "", hm->query.len, hm->query.buf,
- c->send.len > 15 ? 3 : 0, &c->send.buf[9], hm->body.len, body_n,
- hm->body.buf, c->send.len - n, c->send.len - n - spaces,
- c->send.buf + n));
- }
- static void mg_dash_ota_cb(struct mg_connection *c, const char *errmsg) {
- mg_http_reply(c, errmsg ? 500 : 200, NULL, errmsg ? errmsg : "ok\n");
- c->is_draining = 1;
- }
- static void mg_dash_upload_cb(struct mg_connection *c, const char *errmsg) {
- if (errmsg) {
- mg_http_reply(c, 500, NULL, "%s\n", errmsg);
- } else {
- // mg_http_start_upload() repurposes c->data for its own bookkeeping,
- // so the field set can't be cached there. Re-derive the dashboard from
- // c->fn_data instead, and notify every file-backed array: the upload
- // could belong to any of them, and re-querying get_dir() per recipient
- // is what mg_dash_send_change() does anyway (directories can be
- // user-specific)
- struct mg_dash *dash = (struct mg_dash *) c->fn_data;
- struct mg_field_set *fs;
- mg_http_reply(c, 200, NULL, "ok\n");
- for (fs = dash->sets; fs != NULL; fs = fs->next) {
- if (fs->get_dir == NULL) continue;
- *fs->index = -1; // Signal mg_dash_send_change() to broadcast new size
- mg_dash_send_change(c->mgr, fs);
- }
- }
- c->is_draining = 1;
- }
- static uint64_t mg_dash_make_expiration_time(struct mg_dash *dash) {
- unsigned t = (unsigned) dash->session_auto_expiration_seconds;
- if (t == 0) t = 3600; // Default expiration time in seconds
- return mg_millis() + t * 1000;
- }
- static struct mg_dash_user *mg_dash_add_user(struct mg_dash_user **users,
- struct mg_dash *dash,
- const char *name,
- const char *token, int level) {
- struct mg_dash_user *u = (struct mg_dash_user *) mg_calloc(1, sizeof(*u));
- if (u != NULL) {
- mg_snprintf(u->name, sizeof(u->name), "%s", name);
- if (token == NULL) {
- mg_random_str(u->token, sizeof(u->token) - 1);
- } else {
- mg_snprintf(u->token, sizeof(u->token), "%s", token);
- }
- u->level = level;
- u->expire = mg_dash_make_expiration_time(dash);
- u->dash = dash;
- u->next = *users;
- *users = u;
- }
- return u;
- }
- static struct mg_dash_user *mg_dash_find_user(struct mg_dash_user *users,
- struct mg_dash *dash,
- const char *name) {
- struct mg_dash_user *u;
- for (u = users; u != NULL; u = u->next) {
- if (u->dash == dash && strcmp(u->name, name) == 0) return u;
- }
- return NULL;
- }
- static struct mg_dash_user *mg_dash_find_token(struct mg_dash_user *users,
- struct mg_dash *dash,
- const char *token) {
- struct mg_dash_user *u;
- for (u = users; u != NULL; u = u->next) {
- if (u->dash == dash && strcmp(u->token, token) == 0) return u;
- }
- return NULL;
- }
- static void mg_dash_refresh_user(struct mg_dash *dash,
- struct mg_dash_user *user) {
- user->expire = mg_dash_make_expiration_time(dash);
- }
- static void mg_dash_delete_user(struct mg_mgr *mgr, struct mg_dash_user *u) {
- struct mg_connection *conn;
- for (conn = mgr->conns; conn != NULL; conn = conn->next) {
- struct mg_dash_cdata *d = (struct mg_dash_cdata *) conn->data;
- if (conn->is_websocket && d->dash == u->dash && d->u == u) {
- d->u = NULL;
- conn->is_closing = 1;
- }
- }
- LIST_DELETE(struct mg_dash_user, &s_users, u);
- mg_free(u);
- }
- static void mg_dash_delete_users(struct mg_mgr *mgr, struct mg_dash *dash) {
- struct mg_dash_user *u, *tmp;
- for (u = s_users; u != NULL; u = tmp) {
- tmp = u->next;
- if (u->dash == dash) mg_dash_delete_user(mgr, u);
- }
- }
- // Parse HTTP requests, return authenticated user or NULL
- static struct mg_dash_user *mg_dash_authenticate(struct mg_connection *c,
- struct mg_http_message *hm,
- struct mg_dash *dash) {
- char user[100], pass[100];
- struct mg_dash_user *u, *tmp;
- struct mg_str *ah;
- int level = 0, num_users = 0;
- if (dash->authenticate == NULL) {
- mg_snprintf(s_guest.name, sizeof(s_guest.name), "%s", "guest");
- s_guest.level = 9;
- s_guest.dash = dash;
- dash->guest = &s_guest;
- return dash->guest;
- }
- mg_http_creds(hm, user, sizeof(user), pass, sizeof(pass));
- ah = mg_http_get_header(hm, "Authorization");
- // MG_DEBUG(("user [%s], pass: [%s], h: %.*s", user, pass, hm->head.len,
- // hm->head.buf));
- // Remove expired users
- for (u = s_users; u != NULL; u = tmp) {
- tmp = u->next;
- if (u->expire < mg_millis()) {
- MG_DEBUG(("Deleting expired auth %s/%d %llu %u", u->name, u->level,
- u->expire, mg_millis() - u->expire));
- mg_dash_delete_user(c->mgr, u);
- }
- }
- if (pass[0] == '\0') return NULL;
- for (u = s_users; u != NULL; u = u->next) {
- if (u->dash == dash) num_users++;
- }
- if (ah == NULL) {
- u = mg_dash_find_token(s_users, dash, pass);
- if (u != NULL) {
- mg_dash_refresh_user(dash, u);
- return u;
- }
- }
- level = dash->authenticate(user, sizeof(user), pass);
- MG_DEBUG(("user %s, level: %d", user, level));
- if (level <= 0) return NULL;
- u = mg_dash_find_user(s_users, dash, user);
- if (u != NULL) {
- if (ah == NULL) mg_snprintf(u->token, sizeof(u->token), "%s", pass);
- mg_dash_refresh_user(dash, u);
- return u;
- }
- if (num_users < 10)
- return mg_dash_add_user(&s_users, dash, user, ah == NULL ? pass : NULL,
- level);
- // MG_DEBUG(("[%s/%s] -> %s", user, pass, result ? "OK" : "FAIL"));
- return NULL;
- }
- static void mg_handle_login(struct mg_connection *c, struct mg_dash_user *u) {
- char cookie[256];
- mg_snprintf(cookie, sizeof(cookie),
- "Set-Cookie: access_token=%s; Path=/; "
- "%sHttpOnly; SameSite=Lax; Max-Age=%d\r\n%s",
- u->token, c->is_tls ? "Secure; " : "", 3600 * 24,
- MG_JSON_HEADERS);
- mg_http_reply(c, 200, cookie, "{%m:%m,%m:%d}\n", //
- MG_ESC("user"), MG_ESC(u->name), //
- MG_ESC("level"), u->level);
- }
- static void mg_handle_logout(struct mg_connection *c) {
- char cookie[256];
- mg_snprintf(cookie, sizeof(cookie),
- "Set-Cookie: access_token=; Path=/; "
- "Expires=Thu, 01 Jan 1970 00:00:00 UTC; "
- "%sHttpOnly; Max-Age=0; \r\n",
- c->is_tls ? "Secure; " : "");
- mg_http_reply(c, 401, cookie, "Unauthorized\n");
- }
- static void mg_dash_handle_del(struct mg_connection *c, struct mg_dash *dash,
- struct mg_dash_user *u, struct mg_str *parts) {
- struct mg_field_set *set = mg_dash_find_field_set(dash, parts[0]);
- if (set == NULL || set->index == NULL) {
- mg_http_reply(c, 404, MG_JSON_HEADERS, "null\n");
- } else {
- int from = 0, to = 0, count = 0;
- if (parts[1].len) mg_str_to_num(parts[1], 10, &from, sizeof(from));
- to = parts[2].len ? 0 : from;
- if (parts[2].len) mg_str_to_num(parts[2], 10, &to, sizeof(to));
- for (*set->index = from; *set->index <= to; (*set->index)++) {
- if (set->fn && set->fn(MG_DASH_DELETE, u))
- count++;
- else
- break;
- }
- if (count) {
- *set->index = -1;
- mg_dash_send_change(c->mgr, set);
- mg_http_reply(c, 200, MG_JSON_HEADERS, "%d\n", count);
- } else {
- mg_http_reply(c, 403, MG_JSON_HEADERS, "false\n");
- }
- }
- }
- // Handle "POST /api/get/<set>/<index>": modify one array element. Loads the
- // element at <index> first - that doubles as a read-access check and as a
- // pre-fill, so that JSON keys absent from the body keep their old values -
- // then overlays the values from the body and asks fn to persist them
- static void mg_dash_handle_mod(struct mg_connection *c, struct mg_dash *dash,
- struct mg_dash_user *u, struct mg_str *parts,
- struct mg_str body) {
- struct mg_field_set *set = mg_dash_find_field_set(dash, parts[0]);
- int index = 0, count = 0;
- size_t i;
- if (set == NULL || set->index == NULL || set->fn == NULL) {
- mg_http_reply(c, 404, MG_JSON_HEADERS, "null\n");
- return;
- }
- mg_str_to_num(parts[1], 10, &index, sizeof(index));
- *set->index = index;
- if (!set->fn(MG_DASH_READ, u) || *set->index != index) {
- mg_http_reply(c, 404, MG_JSON_HEADERS, "null\n");
- return;
- }
- for (i = 0; set->fields[i].name != NULL; i++) {
- if (mg_dash_parse_field(body, &set->fields[i])) count++;
- }
- if (count && set->fn(MG_DASH_WRITE, u)) {
- mg_dash_send_change(c->mgr, set);
- mg_http_reply(c, 200, MG_JSON_HEADERS, "%d\n", count);
- } else {
- mg_http_reply(c, 403, MG_JSON_HEADERS, "false\n");
- }
- }
- // Handle "POST /api/add/<set>": append a new array element. Parses the body
- // straight into the bound fields, then asks fn to accept and persist them as
- // a new element - fn returns false to reject, e.g. when a cap is reached
- static void mg_dash_handle_add(struct mg_connection *c, struct mg_dash *dash,
- struct mg_dash_user *u, struct mg_str *parts,
- struct mg_str body) {
- struct mg_field_set *set = mg_dash_find_field_set(dash, parts[0]);
- int count = 0;
- size_t i;
- if (set == NULL || set->index == NULL || set->fn == NULL) {
- mg_http_reply(c, 404, MG_JSON_HEADERS, "null\n");
- return;
- }
- for (i = 0; set->fields[i].name != NULL; i++) {
- if (mg_dash_parse_field(body, &set->fields[i])) count++;
- }
- if (count && set->fn(MG_DASH_ADD, u)) {
- *set->index = -1; // Signal mg_dash_send_change() to send new size
- mg_dash_send_change(c->mgr, set);
- mg_http_reply(c, 200, MG_JSON_HEADERS, "true\n");
- } else {
- mg_http_reply(c, 403, MG_JSON_HEADERS, "false\n");
- }
- }
- void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
- struct mg_dash *dash = (struct mg_dash *) c->fn_data;
- struct mg_dash_cdata *d = (struct mg_dash_cdata *) c->data;
- if (ev == MG_EV_OPEN) {
- d->dash = dash;
- // c->is_hexdumping = 1;
- } else if (ev == MG_EV_CLOSE && c->is_listening) {
- mg_dash_delete_users(c->mgr, dash);
- } else if (ev == MG_EV_HTTP_HDRS && d->marker == 0) {
- // Received headers - check authentication and possibly start uploads/ota
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- struct mg_dash_user *u = mg_dash_authenticate(c, hm, dash);
- struct mg_str parts[3];
- memset(parts, 0, sizeof(parts));
- if (mg_match(hm->uri, mg_str("/api/hi"), NULL) ||
- mg_match(hm->uri, mg_str("/api/logout"), NULL)) {
- // Do nothing, handle them MG_EV_HTTP_MSG. We bypass auth for those
- } else if (u == NULL && mg_match(hm->uri, mg_str("/api/#"), NULL)) {
- mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
- d->marker = CONN_HANDLED;
- } else if (mg_match(hm->uri, mg_str("/api/login"), NULL) && u != NULL) {
- mg_handle_login(c, u);
- d->marker = CONN_HANDLED;
- } else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) {
- mg_http_start_ota(c, hm, mg_dash_ota_cb);
- } else if (mg_match(hm->uri, mg_str("/fs/*/*"), parts) &&
- (mg_strcasecmp(hm->method, mg_str("POST")) == 0 ||
- mg_strcasecmp(hm->method, mg_str("PUT")) == 0)) {
- struct mg_field_set *set = mg_dash_find_field_set(dash, parts[0]);
- struct mg_str name = parts[1];
- int len =
- mg_url_decode(name.buf, name.len, (char *) name.buf, name.len + 1, 0);
- if (len > 0 && (size_t) len <= name.len) name.len = (size_t) len;
- if (set == NULL || set->get_dir == NULL) {
- mg_http_reply(c, 404, MG_JSON_HEADERS, "Not Found\n");
- d->marker = CONN_HANDLED;
- } else if (u == NULL) {
- mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
- d->marker = CONN_HANDLED;
- } else if (!mg_path_is_sane(name)) {
- mg_http_reply(c, 400, MG_JSON_HEADERS, "Bad file name\n");
- d->marker = CONN_HANDLED;
- } else {
- mg_dash_set_file_name(set, name);
- if (set->fn != NULL && !set->fn(MG_DASH_WRITE, u)) {
- mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
- d->marker = CONN_HANDLED;
- } else {
- char dir[256];
- struct mg_fs *fs = dash->upload_fs ? dash->upload_fs : &mg_fs_posix;
- if (!set->get_dir(u, dir, sizeof(dir))) {
- mg_http_reply(c, 500, MG_JSON_HEADERS, "Upload dir error\n");
- d->marker = CONN_HANDLED;
- } else {
- mg_http_start_upload(c, hm, name, mg_str(dir), fs,
- mg_dash_upload_cb);
- }
- }
- }
- }
- if (d->marker != '\0') mg_log_http_req(c, hm);
- } else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') {
- // The response has been send in EV_HDRS path, so we're not reponding
- // anything but clearing the marker for the next request.
- d->marker = 0;
- c->is_resp = 0;
- } else if (ev == MG_EV_HTTP_MSG && d->marker == '\0') {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- struct mg_dash_user *u = mg_dash_authenticate(c, hm, dash);
- struct mg_str parts[5];
- memset(parts, 0, sizeof(parts));
- if (mg_match(hm->uri, mg_str("/api/hi"), NULL)) {
- mg_http_reply(c, 200, MG_JSON_HEADERS, "hi\n");
- } else if (mg_match(hm->uri, mg_str("/api/logout"), NULL)) {
- mg_handle_logout(c);
- mg_ws_printf(c, WEBSOCKET_OP_TEXT, "{%m:%m}", MG_ESC("method"),
- MG_ESC("logout"));
- } else if (mg_match(hm->uri, mg_str("/api/websocket"), NULL)) {
- d->u = u;
- mg_ws_upgrade(c, hm, NULL);
- } else if (mg_match(hm->uri, mg_str("/fs/*/*"), parts)) {
- struct mg_field_set *set = mg_dash_find_field_set(dash, parts[0]);
- if (set == NULL || set->get_dir == NULL) {
- mg_http_reply(c, 404, MG_JSON_HEADERS, "Not Found");
- } else if (u == NULL) {
- mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
- } else {
- char dir[256], path[512];
- struct mg_fs *fs = dash->upload_fs ? dash->upload_fs : &mg_fs_posix;
- struct mg_str name = parts[1];
- int len = mg_url_decode(name.buf, name.len, (char *) name.buf,
- name.len + 1, 0);
- if (len > 0 && (size_t) len <= name.len) name.len = (size_t) len;
- if (!mg_path_is_sane(name)) {
- mg_http_reply(c, 400, MG_JSON_HEADERS, "Bad file name\n");
- return;
- }
- if (!set->get_dir(u, dir, sizeof(dir))) {
- mg_http_reply(c, 500, MG_JSON_HEADERS, "Upload dir error\n");
- return;
- }
- mg_snprintf(path, sizeof(path), "%s/%.*s", dir, name.len, name.buf);
- if (mg_strcasecmp(hm->method, mg_str("DELETE")) == 0) {
- mg_dash_set_file_name(set, name);
- if (set->fn != NULL && !set->fn(MG_DASH_DELETE, u)) {
- mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
- } else {
- fs->rm(path);
- *set->index = -1; // Signal mg_dash_send_change() to send new size
- mg_dash_send_change(c->mgr, set);
- mg_http_reply(c, 200, NULL, "true");
- }
- } else {
- mg_http_serve_file(c, hm, path, NULL);
- }
- }
- } else if (mg_match(hm->uri, mg_str("/api/del/*/*/*"), parts) ||
- mg_match(hm->uri, mg_str("/api/del/*/*"), parts)) {
- mg_dash_handle_del(c, dash, u, parts);
- } else if (mg_match(hm->uri, mg_str("/api/add/*"), parts)) {
- mg_dash_handle_add(c, dash, u, parts, hm->body);
- } else if (mg_match(hm->uri, mg_str("/api/get/*/*"), parts) &&
- mg_strcasecmp(hm->method, mg_str("POST")) == 0) {
- mg_dash_handle_mod(c, dash, u, parts, hm->body);
- } else if (mg_match(hm->uri, mg_str("/api/get/*/*/*"), parts) ||
- mg_match(hm->uri, mg_str("/api/get/*/*"), parts) ||
- mg_match(hm->uri, mg_str("/api/get/*"), parts) ||
- mg_match(hm->uri, mg_str("/api/get"), NULL)) {
- mg_http_reply(c, 200, MG_JSON_HEADERS, "%M\n", mg_dash_print_endpoint,
- dash, u, &parts[0], &parts[1], &parts[2]);
- } else if (mg_match(hm->uri, mg_str("/api/set"), NULL)) {
- int count = mg_dash_apply(c, dash, hm->body, u);
- mg_http_reply(c, 200, MG_JSON_HEADERS, "%d\n", count);
- } else if (mg_match(hm->uri, mg_str("/"), NULL)) {
- struct mg_http_serve_opts opts;
- memset(&opts, 0, sizeof(opts));
- opts.fs = &mg_fs_packed;
- mg_http_serve_file(c, hm, "/dashboard.html", &opts);
- } else {
- struct mg_dash_custom_handler *ch = dash->custom_handlers;
- for (ch = dash->custom_handlers; ch != NULL; ch = ch->next) {
- if (mg_match(hm->uri, ch->uri_pattern, NULL)) {
- ch->handler(c, ev, ev_data);
- break;
- }
- }
- if (ch == NULL) mg_http_reply(c, 404, MG_JSON_HEADERS, "Not Found");
- mg_log_http_req(c, hm);
- }
- }
- }
- #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 sendnsreq(struct mg_connection *, struct mg_str *, int,
- struct mg_dns *, bool);
- struct mdns_data {
- struct mdns_data *next;
- struct mg_connection *c;
- uint64_t expire;
- struct mg_str name;
- };
- static void sendmdnsreq(struct mg_connection *, struct mg_str *, int,
- struct mg_connection *, bool);
- static void dns_free(struct dns_data **head, struct dns_data *d) {
- LIST_DELETE(struct dns_data, head, d);
- mg_free(d);
- }
- static void mdns_free(struct mdns_data **head, struct mdns_data *d) {
- LIST_DELETE(struct mdns_data, head, d);
- mg_free((void *) d->name.buf);
- mg_free(d);
- }
- void mg_resolve_cancel(struct mg_connection *c) {
- struct dns_data *tmp, *d;
- struct mdns_data *mtmp, *md;
- struct dns_data **head = (struct dns_data **) &c->mgr->active_dns_requests;
- struct mdns_data **mhead =
- (struct mdns_data **) &c->mgr->active_mdns_requests;
- for (d = *head; d != NULL; d = tmp) {
- tmp = d->next;
- if (d->c == c) dns_free(head, d);
- }
- for (md = *mhead; md != NULL; md = mtmp) {
- mtmp = md->next;
- if (md->c == c) mdns_free(mhead, md);
- }
- }
- 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 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, num_answers, ofs = sizeof(*h);
- bool is_response;
- 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
- num_answers = mg_ntohs(h->num_answers);
- if (num_answers > 10) {
- MG_DEBUG(("Got %u answers, ignoring beyond 10th one", num_answers));
- num_answers = 10; // Sanity cap
- }
- dm->txnid = mg_ntohs(h->txnid);
- is_response = mg_ntohs(h->flags) & 0x8000;
- 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));
- mg_dns_parse_name(buf, len, ofs, dm->name, sizeof(dm->name));
- ofs += n;
- }
- if (!is_response) {
- // For queries, there is no need to parse the answers. In this way,
- // we also ensure the domain name (dm->name) is parsed from
- // the question field.
- return true;
- }
- for (i = 0; i < 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 == MG_DNS_RTYPE_A && rr.aclass == 1) {
- dm->addr.is_ip6 = false;
- memcpy(&dm->addr.addr.ip, &buf[ofs - 4], 4);
- dm->resolved = true;
- break; // Return success
- } else if (rr.alen == 16 && rr.atype == MG_DNS_RTYPE_AAAA &&
- rr.aclass == 1) {
- dm->addr.is_ip6 = true;
- memcpy(&dm->addr.addr.ip, &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) {
- struct dns_data *d, *tmp;
- struct dns_data **head = (struct dns_data **) &c->mgr->active_dns_requests;
- if (ev == MG_EV_POLL) {
- uint64_t now = *(uint64_t *) ev_data;
- for (d = *head; 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"); // will remove entry
- }
- } 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 = *head; 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);
- 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));
- }
- dns_free(head, d);
- resolved = 1;
- }
- }
- if (!resolved) MG_ERROR(("stray DNS reply"));
- c->recv.len = 0;
- } else if (ev == MG_EV_CLOSE) {
- for (d = *head; d != NULL; d = tmp) {
- tmp = d->next;
- mg_error(d->c, "DNS error"); // will remove entry
- }
- }
- }
- static bool dns_send(struct mg_connection *c, const struct mg_str *name,
- unsigned int rtype, uint16_t txnid, uint16_t flags) {
- struct { // RFC-1035 4.1.2
- 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(flags);
- pkt.header.num_questions = mg_htons(1);
- for (i = n = 0; i < sizeof(pkt.data) - 5; i++) {
- if (name->buf[i] == '.' || i >= name->len) {
- pkt.data[n] = (uint8_t) (i - n);
- memcpy(&pkt.data[n + 1], name->buf + n, i - n);
- n = i + 1;
- }
- if (i >= name->len) break;
- }
- memset(&pkt.data[n], 0, 5); // nul, QTYPE, QCLASS
- pkt.data[n + 2] = (uint8_t) rtype; // QTYPE = rtype, only 0-255 supported
- pkt.data[n + 4] = 1; // QCLASS = IN
- n += 5;
- return mg_send(c, &pkt, sizeof(pkt.header) + n);
- }
- static bool mg_dns_send(struct mg_connection *c, const struct mg_str *name,
- uint16_t txnid, bool ipv6) {
- return dns_send(c, name, ipv6 ? MG_DNS_RTYPE_AAAA : MG_DNS_RTYPE_A, txnid,
- 0x100); // RD
- }
- bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc);
- bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc) {
- if (dnsc->url == NULL) {
- mg_error(0, "DNS server URL is NULL. Call mg_mgr_init()");
- return false;
- }
- if (dnsc->c == NULL) {
- dnsc->c = mg_connect(mgr, dnsc->url, NULL, NULL);
- if (dnsc->c == NULL) return false;
- dnsc->c->pfn = dns_cb;
- }
- return true;
- }
- static void sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
- struct mg_dns *dnsc, bool ipv6) {
- struct dns_data *d = NULL;
- if (!mg_dnsc_init(c->mgr, dnsc)) {
- mg_error(c, "resolver");
- } else if ((d = (struct dns_data *) mg_calloc(1, sizeof(*d))) == NULL) {
- mg_error(c, "resolve OOM");
- } else {
- struct dns_data *reqs = (struct dns_data *) c->mgr->active_dns_requests;
- uint16_t id;
- mg_random(&id, sizeof(uint16_t));
- if (reqs != NULL) // no seq, no collision for upto 256 in-flight requests
- id = (uint16_t) (reqs->txnid + (id & 0xFF) + 1);
- d->txnid = id;
- d->next = reqs;
- 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->buf, 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 if (host.len > 6 &&
- strncmp(".local", &host.buf[host.len - 6], 6) == 0) {
- // this is a request for a .local name (mDNS)
- sendmdnsreq(c, &host, 500, c->mgr->mdns, c->mgr->use_dns6); // 500ms tmout
- } else {
- // host is not an IP nor a .local, send DNS resolution request
- struct mg_dns *dns = c->mgr->use_dns6 ? &c->mgr->dns6 : &c->mgr->dns4;
- sendnsreq(c, &host, c->mgr->dnstimeout, dns, c->mgr->use_dns6);
- }
- }
- // Response header length is 10 bytes
- static const uint8_t mdns_answer[] = {
- 0, 1, // 2 bytes - record type, A
- 0, 1, // 2 bytes - address class, INET
- 0, 0, 0, 120, // 4 bytes - TTL
- 0, 4 // 2 bytes - address length
- };
- // A name length is name->len + '.local' + 2 = name->len + 8
- static uint8_t *build_name(struct mg_str *name, uint8_t *p) {
- *p++ = (uint8_t) name->len; // label 1
- memcpy(p, name->buf, name->len), p += name->len;
- *p++ = 5; // label 2
- memcpy(p, "local", 5), p += 5;
- *p++ = 0; // no more labels
- return p;
- }
- void mg_getlocaddr(struct mg_connection *, struct mg_addr *, struct mg_addr *);
- // An A record length is 10 + 4 = 14 bytes
- static uint8_t *build_a_record(struct mg_connection *c, uint8_t *p,
- struct mg_addr *addr) {
- memcpy(p, mdns_answer, sizeof(mdns_answer)), p += sizeof(mdns_answer);
- if (addr != NULL && !addr->is_ip6) {
- memcpy(p, &addr->addr.ip4, 4), p += 4;
- } else {
- #if MG_ENABLE_TCPIP
- memcpy(p, &c->mgr->ifp->ip, 4), p += 4;
- #else
- struct mg_addr loc, to;
- memset(&loc, 0, sizeof(loc));
- to.is_ip6 = false;
- to.port = mg_htons(5353);
- to.addr.ip4 = MG_IPV4(224, 0, 0, 51);
- mg_getlocaddr(c, &to, &loc);
- memcpy(p, &loc.addr.ip4, 4), p += 4;
- #endif
- }
- return p;
- }
- // A srv name length is r->srvcproto.len + '.local' + 2 = r->srvcproto.len + 8
- static uint8_t *build_srv_name(uint8_t *p, struct mg_dnssd_record *r) {
- *p++ = (uint8_t) r->srvcproto.len - 5; // label 1, up to '._tcp'
- memcpy(p, r->srvcproto.buf, r->srvcproto.len), p += r->srvcproto.len;
- p[-5] = 4; // label 2, '_tcp', overwrite '.'
- *p++ = 5; // label 3
- memcpy(p, "local", 5), p += 5;
- *p++ = 0; // no more labels
- return p;
- }
- #if 0
- // TODO(): for listing
- static uint8_t *build_mysrv_name(struct mg_str *name, uint8_t *p,
- struct mg_dnssd_record *r) {
- *p++ = name->len; // label 1
- memcpy(p, name->buf, name->len), p += name->len;
- return build_srv_name(p, r);
- }
- #endif
- // A PTR record length is 10 + name->len + 3 = name->len + 13
- static uint8_t *build_ptr_record(struct mg_str *name, uint8_t *p, uint16_t o) {
- uint16_t offset = mg_htons(o);
- memcpy(p, mdns_answer, sizeof(mdns_answer));
- p[1] = MG_DNS_RTYPE_PTR; // overwrite record type
- p += sizeof(mdns_answer);
- p[-1] = (uint8_t) name->len +
- 3; // overwrite response length, label length + label + offset
- *p++ = (uint8_t) name->len; // response: label 1
- memcpy(p, name->buf, name->len), p += name->len; // copy label
- memcpy(p, &offset, 2);
- *p |= 0xC0, p += 2;
- return p;
- }
- // An SRV record length is 10 + name->len + 9 = name->len + 19
- static uint8_t *build_srv_record(struct mg_str *name, uint8_t *p,
- struct mg_dnssd_record *r, uint16_t o) {
- uint16_t port = mg_htons(r->port);
- uint16_t offset = mg_htons(o);
- memcpy(p, mdns_answer, sizeof(mdns_answer));
- p[1] = MG_DNS_RTYPE_SRV; // overwrite record type
- p += sizeof(mdns_answer);
- p[-1] = (uint8_t) name->len + 9; // overwrite response length (4+2+1+2)
- *p++ = 0; // priority
- *p++ = 0;
- *p++ = 0; // weight
- *p++ = 0;
- memcpy(p, &port, 2), p += 2; // port
- *p++ = (uint8_t) name->len; // label 1
- memcpy(p, name->buf, name->len), p += name->len;
- memcpy(p, &offset, 2);
- *p |= 0xC0, p += 2;
- return p;
- }
- // A TXT record length is r->txt.len (txt contents) + 10
- static uint8_t *build_txt_record(uint8_t *p, struct mg_dnssd_record *r) {
- uint16_t len = mg_htons((uint16_t) r->txt.len);
- memcpy(p, mdns_answer, sizeof(mdns_answer));
- p[1] = MG_DNS_RTYPE_TXT; // overwrite record type
- p += sizeof(mdns_answer);
- memcpy(p - 2, &len, 2); // overwrite response length
- memcpy(p, r->txt.buf, r->txt.len), p += r->txt.len; // copy record verbatim
- return p;
- }
- // Each additional record has a 2-byte field pointing to the name label
- // RFC-6762 16: case-insensitivity --> RFC-1034, 1035
- static void handle_mdns_query(struct mg_connection *c) {
- struct mg_dns_header *qh = (struct mg_dns_header *) c->recv.buf;
- struct mg_dns_rr rr;
- size_t n;
- // Parse first question, offset 12 is header size
- n = mg_dns_parse_rr(c->recv.buf, c->recv.len, 12, true, &rr);
- MG_VERBOSE(("mDNS request parsed, result=%d", (int) n));
- if (n > 0) {
- // RFC-6762 Appendix C, RFC2181 11: m(n + 1-63), max 255 + 0x0
- uint8_t buf[sizeof(struct mg_dns_header) + 256 + sizeof(mdns_answer) + 4];
- struct mg_dns_header *h = (struct mg_dns_header *) buf;
- uint8_t *p = &buf[sizeof(*h)];
- char name[256];
- uint8_t name_len;
- // uint16_t q = mg_ntohs(qh->num_questions);
- struct mg_str defname = mg_str((const char *) c->fn_data);
- struct mg_str *respname;
- struct mg_mdns_req req;
- memset(&req, 0, sizeof(req));
- req.is_unicast = (rr.aclass & MG_BIT(15)) != 0; // QU
- rr.aclass &= (uint16_t) ~MG_BIT(15); // remove "QU" (unicast response)
- qh->num_questions = mg_htons(1); // parser sanity
- mg_dns_parse_name(c->recv.buf, c->recv.len, 12, name, sizeof(name));
- name_len = (uint8_t) strlen(name); // verify it ends in .local
- if (name_len <= 6 || strcmp(".local", &name[name_len - 6]) != 0 ||
- (rr.aclass != 1 && rr.aclass != 0xff))
- return;
- name[name_len -= 6] = '\0'; // remove .local
- MG_VERBOSE(("RR %u %u %s", (unsigned int) rr.atype,
- (unsigned int) rr.aclass, name));
- if (rr.atype == MG_DNS_RTYPE_A) {
- // if we have a name to match, go; otherwise users will match and fill
- // req.r.name and set req.is_resp
- if (c->fn_data != NULL && mg_casecmp((char *) c->fn_data, name) != 0)
- return;
- req.is_resp = (c->fn_data != NULL);
- req.reqname = mg_str_n(name, name_len);
- } else // users have to match the request to something in their db, then
- // fill req.r and set req.is_resp
- if (rr.atype == MG_DNS_RTYPE_PTR) {
- if (strcmp("_services._dns-sd._udp", name) == 0) req.is_listing = true;
- MG_DEBUG(
- ("PTR request for %s", req.is_listing ? "services listing" : name));
- req.reqname = mg_str_n(name, name_len);
- } else if (rr.atype == MG_DNS_RTYPE_SRV || rr.atype == MG_DNS_RTYPE_TXT) {
- MG_DEBUG(("%s request for %s",
- rr.atype == MG_DNS_RTYPE_SRV ? "SRV" : "TXT", name));
- // if possible, check it starts with our name, users will check it ends
- // in a service name they handle
- if (c->fn_data != NULL) {
- if (mg_strcasecmp(defname, mg_str_n(name, defname.len)) != 0 ||
- name[defname.len] != '.')
- return;
- req.reqname =
- mg_str_n(name + defname.len + 1, name_len - defname.len - 1);
- MG_DEBUG(
- ("That's us, handing %.*s", req.reqname.len, req.reqname.buf));
- } else {
- req.reqname = mg_str_n(name, name_len);
- }
- } else { // unhandled record
- return;
- }
- req.rr = &rr;
- mg_call(c, MG_EV_MDNS_REQ, &req);
- if (!req.is_resp) return;
- respname = req.respname.buf != NULL ? &req.respname : &defname;
- memset(h, 0, sizeof(*h)); // clear header
- h->txnid = req.is_unicast ? qh->txnid : 0; // RFC-6762 18.1
- h->num_answers = mg_htons(1); // RFC-6762 6: 0 questions, 1 Answer
- h->flags = mg_htons(0x8400); // Authoritative response
- if (req.is_listing) {
- // TODO(): RFC-6762 6: each responder SHOULD delay its response by a
- // random amount of time selected with uniform random distribution in the
- // range 20-120 ms.
- // TODO():
- return;
- } else if (rr.atype == MG_DNS_RTYPE_PTR) { // serve PTR + SRV + TXT + A
- // TODO(): RFC-6762 6: each responder SHOULD delay its response by a
- // random amount of time selected with uniform random distribution in the
- // range 20-120 ms. Response to PTR is local_name._myservice._tcp.local
- uint8_t *o = p, *aux;
- uint16_t offset;
- if (respname->buf == NULL || respname->len == 0) return;
- if ((sizeof(*h) + req.r->srvcproto.len + 8 + respname->len + 13 + 2 +
- respname->len + 19 + 2 + req.r->txt.len + 10 + 2 + 14) >
- sizeof(buf)) // srv name + PTR + 2 + SRV + 2 + TXT + 2 + A
- return;
- h->num_other_prs = mg_htons(3); // 3 additional records
- p = build_srv_name(p, req.r);
- aux = build_ptr_record(respname, p, (uint16_t) (o - buf));
- o = p + sizeof(mdns_answer); // point to PTR response (full srvc name)
- offset = mg_htons((uint16_t) (o - buf));
- o = p - 7; // point to '.local' label (\x05local\x00)
- p = aux;
- memcpy(p, &offset, 2); // point to full srvc name, in record
- *p |= 0xC0, p += 2;
- aux = p;
- p = build_srv_record(respname, p, req.r, (uint16_t) (o - buf));
- o = aux + sizeof(mdns_answer) + 6; // point to target in SRV
- memcpy(p, &offset, 2); // point to full srvc name, in record
- *p |= 0xC0, p += 2;
- p = build_txt_record(p, req.r);
- offset = mg_htons((uint16_t) (o - buf));
- memcpy(p, &offset, 2); // point to target name, in record
- *p |= 0xC0, p += 2;
- p = build_a_record(c, p, req.addr);
- } else if (rr.atype == MG_DNS_RTYPE_TXT) {
- if ((sizeof(*h) + req.r->srvcproto.len + 8 + req.r->txt.len + 10) >
- sizeof(buf)) // srv name + TXT
- return;
- p = build_srv_name(p, req.r);
- p = build_txt_record(p, req.r);
- } else if (rr.atype == MG_DNS_RTYPE_SRV) { // serve SRV + A
- uint8_t *o, *aux;
- uint16_t offset;
- if (respname->buf == NULL || respname->len == 0) return;
- if ((sizeof(*h) + req.r->srvcproto.len + 8 + respname->len + 19 + 2 +
- 14) > sizeof(buf)) // srv name + SRV + 2 + A
- return;
- h->num_other_prs = mg_htons(1); // 1 additional record
- p = build_srv_name(p, req.r);
- o = p - 7; // point to '.local' label (\x05local\x00)
- aux = p;
- p = build_srv_record(respname, p, req.r, (uint16_t) (o - buf));
- o = aux + sizeof(mdns_answer) + 6; // point to target in SRV
- offset = mg_htons((uint16_t) (o - buf));
- memcpy(p, &offset, 2); // point to target name, in record
- *p |= 0xC0, p += 2;
- p = build_a_record(c, p, req.addr);
- } else { // A requested
- // RFC-6762 6: 0 Auth, 0 Additional RRs
- if (respname->buf == NULL || respname->len == 0) return;
- if ((sizeof(*h) + respname->len + 8 + 14) > sizeof(buf)) // name + A
- return;
- p = build_name(respname, p);
- p = build_a_record(c, p, req.addr);
- }
- if (!req.is_unicast) mg_multicast_restore(c, (uint8_t *) &c->loc);
- mg_send(c, buf, (size_t) (p - buf)); // And send it!
- MG_DEBUG(("%M > %M", mg_print_ip_port, &c->loc, mg_print_ip_port, &c->rem));
- MG_DEBUG(("mDNS %s response sent", req.is_unicast ? "unicast" : "mcast"));
- }
- }
- static size_t srvtoname(char *svc) {
- char *dot = strchr(svc, '.');
- if (dot == NULL) return 0;
- *dot = '\0';
- return (size_t) (dot - svc);
- }
- #define MG_MAX_MDNS_RECORDS 8
- // Condense one useful mDNS/DNS-SD chain from a response datagram into a single
- // MG_EV_MDNS_RESP event. A/AAAA as the first supported record are terminal
- // address answers (AAAA currently ignored). For PTR/SRV/TXT, subsequent
- // records are attached only when their label matches the service instance, or
- // the SRV target for address records.
- // Some devices send SRV/TXT as extra Answer RRs instead of Additional RRs, so
- // both sections are parsed as one chain.
- static void handle_mdns_response(struct mg_connection *c) {
- struct mg_dns_header *rh = (struct mg_dns_header *) c->recv.buf;
- struct mg_dns_rr rr, rr_[5]; // A, PTR, SRV, TXT, AAAA, in that order
- struct mg_mdns_resp resp;
- size_t n, i, num, answers, roff_[5], roff = 12; // offset 12 is header size
- // RFC-6762 Appendix C, RFC2181 11: m(n + 1-63), max 255 + 0x0
- char name[256], srvcproto[256], instance[256], host[256];
- uint16_t atype = 0;
- answers = mg_ntohs(rh->num_answers);
- num = answers + mg_ntohs(rh->num_other_prs);
- if (answers == 0) return;
- MG_VERBOSE(("mDNS response: %u answer, %u additional", (unsigned) answers,
- (unsigned) (num - answers)));
- if (num > MG_MAX_MDNS_RECORDS) {
- num = MG_MAX_MDNS_RECORDS;
- MG_DEBUG(("ignoring > %u records", MG_MAX_MDNS_RECORDS));
- }
- memset(roff_, 0, sizeof(roff_));
- memset(&rr_, 0, sizeof(rr_));
- memset(&resp, 0, sizeof(resp));
- instance[0] = host[0] = '\0';
- for (i = 0; i < num; i++) { // First Answer RR is primary; the rest must match it
- n = mg_dns_parse_rr(c->recv.buf, c->recv.len, roff, false, &rr);
- MG_VERBOSE(("mDNS record parsed, result=%u", n));
- if (n == 0) return;
- if (mg_dns_parse_name(c->recv.buf, c->recv.len, roff, name, sizeof(name)) == 0) return;
- MG_VERBOSE(("RR %u %u %s", (unsigned int) rr.atype, (unsigned int) rr.aclass, name));
- if (rr.alen == 4 && rr.atype == MG_DNS_RTYPE_A && (rr.aclass & 0x7FFF) == 1 &&
- (i == 0 || (host[0] != '\0' && strcmp(name, host) == 0) ||
- (host[0] == '\0' && instance[0] != '\0' && strcmp(name, instance) == 0))) {
- if (rr_[0].atype != 0) break;
- if (i == 0) atype = rr.atype;
- resp.name = mg_str(name);
- resp.addr.is_ip6 = false;
- memcpy(resp.addr.addr.ip, c->recv.buf + roff + n - 4, 4);
- MG_VERBOSE(("A record"));
- roff_[0] = roff, rr_[0] = rr;
- if (atype == MG_DNS_RTYPE_A) break;
- } else if (rr.alen == 16 && rr.atype == MG_DNS_RTYPE_AAAA && (rr.aclass & 0x7FFF) == 1 &&
- (i == 0 || (host[0] != '\0' && strcmp(name, host) == 0) ||
- (host[0] == '\0' && instance[0] != '\0' && strcmp(name, instance) == 0))) {
- # if 0
- if (rr_[4].atype != 0) break;
- resp.addr.is_ip6 = true;
- if (i == 0) atype = rr.atype;
- resp.name = mg_str(name);
- memcpy(resp.addr.addr.ip, c->recv.buf + roff + n - 16, 16);
- MG_VERBOSE(("AAAA record"));
- roff_[4] = roff, rr_[4] = rr;
- if (atype == MG_DNS_RTYPE_AAAA) break;
- #else
- MG_VERBOSE(("ignored AAAA record"));
- #endif
- } else if (rr.atype == MG_DNS_RTYPE_PTR && (rr.aclass & 0x7FFF) == 1 && i == 0) {
- if (rr_[1].atype != 0) break;
- atype = rr.atype;
- if (mg_dns_parse_name(c->recv.buf, c->recv.len, roff + rr.nlen + 10,
- instance, sizeof(instance)) == 0) return;
- MG_VERBOSE(("PTR record"));
- roff_[1] = roff, rr_[1] = rr;
- } else if (rr.atype == MG_DNS_RTYPE_SRV && rr.alen >= 6 && (rr.aclass & 0x7FFF) == 1 &&
- (i == 0 || (instance[0] != '\0' && strcmp(name, instance) == 0))) {
- if (rr_[2].atype != 0) break;
- if (i == 0) atype = rr.atype;
- resp.sd.port = MG_LOAD_BE16(c->recv.buf + roff + rr.nlen + 14);
- resp.addr.port = mg_htons(resp.sd.port);
- if (instance[0] == '\0') mg_snprintf(instance, sizeof(instance), "%s", name);
- if (mg_dns_parse_name(c->recv.buf, c->recv.len, roff + rr.nlen + 16,
- host, sizeof(host)) == 0) return;
- MG_VERBOSE(("SRV record"));
- roff_[2] = roff, rr_[2] = rr;
- } else if (rr.atype == MG_DNS_RTYPE_TXT && (rr.aclass & 0x7FFF) == 1 &&
- (i == 0 || (instance[0] != '\0' && strcmp(name, instance) == 0))) {
- if (rr_[3].atype != 0) break;
- if (i == 0) atype = rr.atype;
- if (instance[0] == '\0') mg_snprintf(instance, sizeof(instance), "%s", name);
- resp.sd.txt = mg_str_n((char *) c->recv.buf + roff + rr.nlen + 10, rr.alen);
- MG_VERBOSE(("TXT record"));
- roff_[3] = roff, rr_[3] = rr;
- }
- roff += n;
- if (i == 0 && atype == 0) return;
- }
- if (atype == MG_DNS_RTYPE_A) {
- resp.rr = &rr_[0];
- MG_DEBUG(("A response from %s = %M", name, mg_print_ip, &resp.addr));
- #if 0
- } else if (atype == MG_DNS_RTYPE_AAAA) {
- resp.rr = &rr_[4];
- MG_DEBUG(("AAAA response from %s = %M", name, mg_print_ip6, resp.addr.addr.ip));
- #endif
- } else if (atype == MG_DNS_RTYPE_PTR) {
- size_t len;
- resp.rr = &rr_[1];
- if (mg_dns_parse_name(c->recv.buf, c->recv.len, roff_[1], srvcproto,
- sizeof(srvcproto)) == 0) return;
- len = strlen(srvcproto);
- if (len <= 6) return;
- resp.sd.srvcproto = mg_str_n(srvcproto, len - 6); // remove .local
- if (host[0] != '\0') {
- resp.name = mg_str(host);
- } else if (rr_[0].atype == 0) {
- mg_snprintf(name, sizeof(name), "%s", instance);
- resp.name = mg_str_n(name, srvtoname(name));
- } else {
- if (mg_dns_parse_name(c->recv.buf, c->recv.len, roff_[0], name,
- sizeof(name)) == 0) return;
- resp.name = mg_str(name);
- }
- MG_DEBUG(("PTR response for %s from %s", srvcproto, name));
- } else if (atype == MG_DNS_RTYPE_SRV || atype == MG_DNS_RTYPE_TXT) {
- size_t len, name_len;
- resp.rr = atype == MG_DNS_RTYPE_SRV ? &rr_[2] : &rr_[3];
- if (mg_dns_parse_name(c->recv.buf, c->recv.len,
- atype == MG_DNS_RTYPE_SRV ? roff_[2] : roff_[3],
- srvcproto, sizeof(srvcproto)) == 0)
- return;
- len = strlen(srvcproto);
- name_len = srvtoname(srvcproto);
- if (name_len == 0 || len <= name_len + 7) return; // remove .local
- resp.sd.srvcproto = mg_str_n(srvcproto + name_len + 1, len - name_len - 7);
- resp.name = mg_str_n(srvcproto, name_len);
- MG_DEBUG(("%s response for %s from %s", atype == MG_DNS_RTYPE_SRV ? "SRV" : "TXT", srvcproto, name));
- } else {
- return;
- }
- mg_call(c, MG_EV_MDNS_RESP, &resp);
- }
- static void handle_mdns_record(struct mg_connection *c) {
- struct mg_dns_header *h = (struct mg_dns_header *) c->recv.buf;
- if (c->recv.len <= 12) return;
- if ((h->flags & mg_htons(0xF800)) == 0) {
- // flags -> !resp, opcode=0 => query; ignore other opcodes
- handle_mdns_query(c);
- } else if ((h->flags & mg_htons(0xF800)) == mg_htons(0x8000)) {
- // flags -> resp, opcode=0 => response; ignore other opcodes
- handle_mdns_response(c);
- }
- }
- static void mdns_cb(struct mg_connection *c, int ev, void *ev_data) {
- struct mdns_data *d, *tmp;
- struct mdns_data **head = (struct mdns_data **) &c->mgr->active_mdns_requests;
- // mDNS resolver
- if (ev == MG_EV_POLL) {
- uint64_t now = *(uint64_t *) ev_data;
- for (d = *head; d != NULL; d = tmp) {
- tmp = d->next;
- // MG_DEBUG(("%lu %lu mdns poll", d->expire, now));
- if (now > d->expire) mg_error(d->c, "mDNS timeout"); // will remove entry
- }
- } else if (ev == MG_EV_CLOSE) {
- for (d = *head; d != NULL; d = tmp) {
- tmp = d->next;
- mg_error(d->c, "mDNS listener error"); // this will remove entry
- }
- } else if (ev == MG_EV_MDNS_RESP) {
- struct mg_mdns_resp *resp = (struct mg_mdns_resp *) ev_data;
- if (resp->rr->atype == MG_DNS_RTYPE_A) {
- for (d = *head; d != NULL; d = tmp) {
- tmp = d->next;
- if (mg_strcasecmp(d->name, resp->name) != 0) continue;
- if (d->c->is_resolving) {
- resp->addr.port = d->c->rem.port; // Save port
- d->c->rem = resp->addr; // Copy resolved address
- MG_DEBUG(("%lu %.*s is %M", d->c->id, resp->name.len, resp->name.buf,
- mg_print_ip, &d->c->rem));
- mg_connect_resolved(d->c);
- } else {
- // this should not happen, unless above does not clear c->is_resolving
- MG_ERROR(("%lu already resolved", d->c->id));
- }
- mdns_free(head, d);
- }
- }
- } else if (ev == MG_EV_READ) {
- // generic mDNS[-SD] handling
- handle_mdns_record(c); // this will call us back with MG_EV_MDNS_RESP
- mg_iobuf_del(&c->recv, 0, c->recv.len);
- }
- (void) ev_data;
- }
- void mg_multicast_add(struct mg_connection *c, char *ip);
- struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, mg_event_handler_t fn,
- void *fn_data) {
- struct mg_connection *c =
- mg_listen(mgr, "udp://224.0.0.251:5353", fn, fn_data);
- if (c == NULL) return NULL;
- c->mgr->mdns = c; // Add mDNS entry to enable resolver to use it
- c->pfn = mdns_cb, c->pfn_data = fn_data;
- mg_multicast_add(c, (char *) "224.0.0.251");
- return c;
- }
- static bool mdns_query(struct mg_connection *c, struct mg_str *name,
- unsigned int rtype) {
- mg_multicast_restore(c, (uint8_t *) &c->loc);
- return dns_send(c, name, rtype, 0, 0); // RFC-6762 18.1 id = 0, 18.6 RD = 0
- }
- bool mg_mdns_query(struct mg_connection *c, const char *name,
- unsigned int rtype) {
- struct mg_str name_;
- name_.buf = (char *) name, name_.len = strlen(name);
- return mdns_query(c, &name_, rtype);
- }
- static void sendmdnsreq(struct mg_connection *c, struct mg_str *name, int ms,
- struct mg_connection *mdnsc, bool ipv6) {
- struct mdns_data *d = NULL;
- if (mdnsc == NULL) {
- mg_error(c, "no mDNS listener, see mg_mdns_listen()");
- } else if ((d = (struct mdns_data *) mg_calloc(1, sizeof(*d))) == NULL) {
- mg_error(c, "resolve OOM");
- } else {
- struct mdns_data *reqs = (struct mdns_data *) c->mgr->active_mdns_requests;
- d->next = reqs;
- c->mgr->active_mdns_requests = d;
- d->expire = mg_millis() + (uint64_t) ms;
- d->name = mg_strdup(*name);
- d->c = c;
- c->is_resolving = 1;
- MG_VERBOSE(
- ("%lu resolving %.*s via mDNS", c->id, (int) name->len, name->buf));
- if (!mdns_query(mdnsc, name, MG_DNS_RTYPE_A)) {
- mg_error(c, "mDNS send"); // will remove newly created entry
- }
- }
- (void) ipv6;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/event.c"
- #endif
- void mg_call(struct mg_connection *c, int ev, void *ev_data) {
- #if MG_ENABLE_PROFILE
- const char *names[] = {
- "EV_ERROR", "EV_OPEN", "EV_POLL", "EV_RESOLVE",
- "EV_CONNECT", "EV_ACCEPT", "EV_TLS_HS", "EV_READ",
- "EV_WRITE", "EV_CLOSE", "EV_HTTP_MSG", "EV_HTTP_CHUNK",
- "EV_WS_OPEN", "EV_WS_MSG", "EV_WS_CTL", "EV_MQTT_CMD",
- "EV_MQTT_MSG", "EV_MQTT_OPEN", "EV_SNTP_TIME", "EV_USER"};
- if (ev != MG_EV_POLL && ev < (int) (sizeof(names) / sizeof(names[0]))) {
- MG_PROF_ADD(c, names[ev]);
- }
- #endif
- // Fire protocol handler first, user handler second. See #2559
- if (c->pfn != NULL) c->pfn(c, ev, ev_data);
- if (c->fn != NULL) c->fn(c, ev, ev_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 %ld %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 override it
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/flash.c"
- #endif
- #ifdef MG_OTA_PUBLIC_KEY
- #endif
- #if MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM
- static char *s_addr; // Current address to write to
- static size_t s_size; // Firmware size to flash. In-progress indicator
- static uint32_t s_crc32; // Firmware checksum
- bool mg_ota_flash_begin(size_t new_firmware_size, struct mg_flash *flash) {
- bool ok = false;
- if (s_size) {
- MG_ERROR(("OTA already in progress. Call mg_ota_end()"));
- } else {
- size_t half = flash->size / 2;
- s_crc32 = 0;
- s_addr = (char *) flash->start + half;
- MG_DEBUG(("FW %lu bytes, max %lu", new_firmware_size, half));
- if (new_firmware_size < half) {
- ok = true;
- s_size = new_firmware_size;
- MG_INFO(("Starting OTA, firmware size %lu", s_size));
- } else {
- MG_ERROR(("Firmware %lu is too big to fit %lu", new_firmware_size, half - flash->align));
- }
- }
- return ok;
- }
- bool mg_ota_flash_write(const void *buf, size_t len, struct mg_flash *flash) {
- bool ok = false;
- if (s_size == 0) {
- MG_ERROR(("OTA is not started, call mg_ota_begin()"));
- } else if (s_addr + MG_ROUND_UP(len, flash->align) >
- (char *) flash->start + flash->size) {
- MG_ERROR(("Flash overflow: attempting to write past the flash boundary"));
- } else {
- size_t len_aligned_down = MG_ROUND_DOWN(len, flash->align);
- if (len_aligned_down) ok = flash->write_fn(s_addr, buf, len_aligned_down);
- if (len_aligned_down < len) {
- size_t left = len - len_aligned_down;
- char tmp[flash->align];
- memset(tmp, 0xff, sizeof(tmp));
- memcpy(tmp, (char *) buf + len_aligned_down, left);
- ok = flash->write_fn(s_addr + len_aligned_down, tmp, sizeof(tmp));
- }
- s_crc32 = mg_crc32(s_crc32, (char *) buf, len); // Update CRC
- MG_DEBUG(("%#x %p %lu -> %d", s_addr - len, buf, len, ok));
- s_addr += len;
- }
- return ok;
- }
- bool mg_ota_flash_end(struct mg_flash *flash) {
- char *base = (char *) flash->start + flash->size / 2;
- bool ok = false;
- int prev_state = MG_OTA_STATE_GET();
- if (s_size) {
- size_t size = (size_t) (s_addr - base);
- uint32_t crc32 = mg_crc32(0, base, s_size);
- if (size == s_size && crc32 == s_crc32) ok = true;
- MG_DEBUG(("CRC: %x/%x, size: %lu/%lu, status: %s", s_crc32, crc32, s_size,
- size, ok ? "ok" : "fail"));
- #ifdef MG_OTA_PUBLIC_KEY
- if (ok) {
- bool signed_fw = s_size > 68 &&
- memcmp((uint8_t *) base + s_size - 4, "MGSG", 4) == 0;
- if (signed_fw) {
- static const uint8_t s_pubkey[] = MG_OTA_PUBLIC_KEY;
- uint8_t hash[32];
- size_t fw_size = s_size - 68; // strip 64-byte sig + 4-byte magic
- mg_sha256(hash, (uint8_t *) base, fw_size);
- ok = mg_uecc_verify(s_pubkey, hash, sizeof(hash),
- (uint8_t *) base + fw_size,
- mg_uecc_secp256r1()) == 1;
- MG_INFO(("Signature: %s", ok ? "ok" : "fail"));
- } else {
- ok = false;
- MG_ERROR(("Unsigned firmware rejected"));
- }
- }
- #endif
- s_size = 0;
- if (ok) MG_OTA_STATE_SET(MG_OTA_TESTING);
- if (ok) ok = flash->swap_fn();
- if (!ok) MG_OTA_STATE_SET(prev_state); // undo state in case of failure
- }
- MG_INFO(("Finishing OTA: %s", ok ? "ok" : "fail"));
- return ok;
- }
- #endif
- #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;
- ieee754.f = x;
- return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 &&
- ((unsigned) ieee754.u == 0);
- }
- static int xisnan(double x) {
- union {
- double f;
- uint64_t u;
- } ieee754;
- ieee754.f = 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;
- if (tz) {
- mul = 1.0;
- while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0;
- } else {
- mul = 0.1;
- }
- 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 (tz && (e >= width || e <= -width) && width > 1) {
- char exp[6];
- int ne;
- n = (int) mg_dtoa(buf + s, sizeof(buf) - (size_t) s, saved / mul, width, tz);
- // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, n, buf);
- ne = addexp(exp, e < 0 ? -e : e, e < 0 ? '-' : '+');
- if (s + n + ne >= (int) sizeof(buf))
- n = (int) sizeof(buf) - s - ne - 1;
- memcpy(buf + s + n, exp, (size_t) ne);
- n += ne;
- return mg_snprintf(dst, dstlen, "%.*s", s + n, buf);
- } else {
- int targ_width = width;
- 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);
- if (!tz && n > 0) targ_width = width + n;
- for (i = 0, t = 0.1; s + n < (int) sizeof(buf) && n < targ_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 (tz && 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 == 'z') is_long = 1, 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 *) mg_calloc(1, sizeof(*fd));
- if (fd != NULL) {
- fd->fd = fs->op(path, flags);
- fd->fs = fs;
- if (fd->fd == NULL) {
- mg_free(fd);
- fd = NULL;
- }
- }
- return fd;
- }
- void mg_fs_close(struct mg_fd *fd) {
- if (fd != NULL) {
- fd->fs->cl(fd->fd);
- mg_free(fd);
- }
- }
- struct mg_str mg_file_read(struct mg_fs *fs, const char *path) {
- struct mg_str result = {NULL, 0};
- void *fp;
- fs->st(path, &result.len, NULL);
- if ((fp = fs->op(path, MG_FS_READ)) != NULL) {
- result.buf = (char *) mg_calloc(1, result.len + 1);
- if (result.buf != NULL &&
- fs->rd(fp, (void *) result.buf, result.len) != result.len) {
- mg_free((void *) result.buf);
- result.buf = NULL;
- }
- fs->cl(fp);
- }
- if (result.buf == NULL) result.len = 0;
- return result;
- }
- 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], rnd[10];
- size_t path_len = mg_snprintf(tmp, sizeof(tmp), "%s..%s", path,
- mg_random_str(rnd, sizeof(rnd)));
- if (path_len < sizeof(tmp) &&
- (fd = mg_fs_open(fs, tmp, MG_FS_WRITE | MG_FS_EXCL)) != NULL) {
- result = fs->wr(fd->fd, buf, len) == len;
- mg_fs_close(fd);
- if (result) {
- fs->rm(path);
- result = fs->mv(tmp, path);
- }
- 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));
- mg_free(data);
- return result;
- }
- // This helper function allows to scan a filesystem in a sequential way,
- // without using callback function:
- // char buf[100] = "";
- // while (mg_fs_ls(&mg_fs_posix, "./", buf, sizeof(buf))) {
- // ...
- static void mg_fs_ls_fn(const char *filename, void *param) {
- struct mg_str *s = (struct mg_str *) param;
- if (s->buf[0] == '\0') {
- mg_snprintf((char *) s->buf, s->len, "%s", filename);
- } else if (strcmp(s->buf, filename) == 0) {
- ((char *) s->buf)[0] = '\0'; // Fetch next file
- }
- }
- bool mg_fs_ls(struct mg_fs *fs, const char *path, char *buf, size_t len) {
- struct mg_str s;
- s.buf = buf, s.len = len;
- fs->ls(path, mg_fs_ls_fn, &s);
- return buf[0] != '\0';
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/fs_fat.c"
- #endif
- #if MG_ENABLE_FATFS
- #include <ff.h>
- static time_t ff_time_to_epoch(uint16_t fdate, uint16_t ftime) {
- unsigned int sec = (ftime << 1) & 0x3e;
- unsigned int min = (ftime >> 5) & 0x3f;
- unsigned int hour = (ftime >> 11) & 0x1f;
- unsigned int day = fdate & 0x1f;
- unsigned int month = (fdate >> 5) & 0x0f;
- unsigned int year = ((fdate >> 9) & 0x7f) + 1980;
- return (time_t) mg_timegm(year, month, day, hour, min, sec);
- }
- 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 *fp = NULL;
- unsigned char mode = FA_READ;
- if (flags & MG_FS_WRITE) {
- mode |= FA_WRITE;
- if (flags & MG_FS_EXCL) {
- mode |= FA_OPEN_ALWAYS | FA_OPEN_APPEND;
- } else {
- mode |= FA_CREATE_NEW;
- }
- }
- if ((fp = mg_calloc(1, sizeof(*fp))) != NULL &&
- f_open(fp, path, mode) != FR_OK) {
- mg_free(fp);
- fp = NULL;
- }
- return fp;
- }
- static void ff_close(void *fp) {
- if (fp != NULL) {
- f_close((FIL *) fp);
- mg_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 struct mg_mem_file *mg_mem_files;
- static int mg_scmp(const char *a, const char *b) {
- while (*a && (*a == *b)) a++, b++;
- return *(const unsigned char *) a - *(const unsigned char *) b;
- }
- static const char *mg_unlist(size_t no) {
- return mg_mem_files == NULL ? NULL : mg_mem_files[no].path;
- }
- static const char *mg_unpack(const char *path, size_t *size, time_t *mtime) {
- const struct mg_mem_file *p;
- for (p = mg_mem_files; p != NULL && p->path != NULL; p++) {
- if (mg_scmp(p->path, path) != 0) continue;
- if (size != NULL) *size = p->size;
- if (mtime != NULL) *mtime = p->mtime;
- return (const char *) p->data;
- }
- return NULL;
- }
- struct mg_str mg_unpacked(const char *path) {
- size_t len = 0;
- const char *buf = mg_unpack(path, &len, NULL);
- return mg_str_n(buf, len);
- }
- 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;
- if ((fp = (struct packed_file *) mg_calloc(1, sizeof(*fp))) != NULL) {
- fp->size = size;
- fp->data = data;
- }
- return (void *) fp;
- }
- static void packed_close(void *fp) {
- if (fp != NULL) mg_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_POSIX_FS
- #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;
- #if 0
- 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;
- }
- #endif
- 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;
- size_t n;
- if (name == NULL) {
- SetLastError(ERROR_BAD_ARGUMENTS);
- } else if ((d = (DIR *) mg_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)) {
- n = wcslen(wpath);
- if (n <= (sizeof(wpath) / sizeof(wpath[0])) - 3) {
- (void) wcscat(wpath, L"\\*");
- d->handle = FindFirstFileW(wpath, &d->info);
- d->result.d_name[0] = '\0';
- } else {
- mg_free(d);
- d = NULL;
- SetLastError(ERROR_BUFFER_OVERFLOW);
- }
- } else {
- mg_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;
- mg_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) {
- #if MG_ARCH == MG_ARCH_WIN32
- const char *mode = flags == MG_FS_READ ? "rb"
- : (flags & MG_FS_EXCL) ? "wxb"
- : "a+b";
- 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
- const char *mode = flags == MG_FS_READ ? "rbe"
- : (flags & MG_FS_EXCL) ? "wxbe"
- : "a+be"; // e for CLOSEXEC
- 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
- static int mg_ncasecmp(const char *s1, const char *s2, size_t len) {
- int diff = 0;
- if (len > 0) do {
- int c = *s1++, d = *s2++;
- if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
- if (d >= 'A' && d <= 'Z') d += 'a' - 'A';
- diff = c - d;
- } while (diff == 0 && s1[-1] != '\0' && --len > 0);
- return diff;
- }
- bool mg_to_size_t(struct mg_str str, size_t *val);
- bool mg_to_size_t(struct mg_str str, size_t *val) {
- size_t i = 0, max = (size_t) -1, max2 = max / 10, result = 0, ndigits = 0;
- while (i < str.len && (str.buf[i] == ' ' || str.buf[i] == '\t')) i++;
- if (i < str.len && str.buf[i] == '-') return false;
- while (i < str.len && str.buf[i] >= '0' && str.buf[i] <= '9') {
- size_t digit = (size_t) (str.buf[i] - '0');
- if (result > max2) return false; // Overflow
- result *= 10;
- if (result > max - digit) return false; // Overflow
- result += digit;
- i++, ndigits++;
- }
- while (i < str.len && (str.buf[i] == ' ' || str.buf[i] == '\t')) i++;
- if (ndigits == 0) return false; // #2322: Content-Length = 1 * DIGIT
- if (i != str.len) return false; // Ditto
- *val = (size_t) result;
- return true;
- }
- // 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.buf;
- 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.buf, 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) {
- size_t 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 < u.len; i++) {
- n = mg_base64_update(((unsigned char *) u.buf)[i], buf + 21, n);
- }
- if (p.len > 0) {
- n = mg_base64_update(':', buf + 21, n);
- for (i = 0; i < p.len; i++) {
- n = mg_base64_update(((unsigned char *) p.buf)[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 entry, k, v, result = mg_str_n(NULL, 0);
- while (mg_span(buf, &entry, &buf, '&')) {
- if (mg_span(entry, &k, &v, '=') && name.len == k.len &&
- mg_ncasecmp(name.buf, k.buf, 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) {
- dst[0] = '\0'; // If destination buffer is valid, always nul-terminate it
- }
- if (dst == NULL || dst_len == 0) {
- len = -2; // Bad destination
- } else if (buf->buf == NULL || name == NULL || buf->len == 0) {
- len = -1; // Bad source
- } else {
- struct mg_str v = mg_http_var(*buf, mg_str(name));
- if (v.buf == NULL) {
- len = -4; // Name does not exist
- } else {
- len = mg_url_decode(v.buf, 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_str_to_num(mg_str_n(src + i + 1, 2), 16, &dst[j], sizeof(uint8_t));
- 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 == '\t' || 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;
- }
- 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->buf, name, n) == 0) return v;
- }
- return NULL;
- }
- // Is it a valid utf-8 continuation byte
- static bool vcb(uint8_t c) {
- return (c & 0xc0) == 0x80;
- }
- // Get character length (valid utf-8). Used to parse method, URI, headers
- static size_t clen(const char *s, const char *end) {
- const unsigned char *u = (unsigned char *) s, c = *u;
- long n = (long) (end - s);
- if (c > ' ' && c <= '~') return 1; // Usual ascii printed char
- if ((c & 0xe0) == 0xc0 && n > 1 && vcb(u[1])) return 2; // 2-byte UTF8
- if ((c & 0xf0) == 0xe0 && n > 2 && vcb(u[1]) && vcb(u[2])) return 3;
- if ((c & 0xf8) == 0xf0 && n > 3 && vcb(u[1]) && vcb(u[2]) && vcb(u[3]))
- return 4;
- return 0;
- }
- // Skip until the newline. Return advanced `s`, or NULL on error
- static const char *skiptorn(const char *s, const char *end, struct mg_str *v) {
- v->buf = (char *) s;
- while (s < end && s[0] != '\n' && s[0] != '\r') s++, v->len++; // To newline
- if (s >= end || (s[0] == '\r' && s[1] != '\n')) return NULL; // Stray \r
- if (s < end && s[0] == '\r') s++; // Skip \r
- if (s >= end || *s++ != '\n') return NULL; // Skip \n
- return s;
- }
- static bool mg_http_parse_headers(const char *s, const char *end,
- struct mg_http_header *h, size_t max_hdrs) {
- size_t i, n;
- int cl_count = 0, te_count = 0, auth_count = 0;
- int conn_count = 0, cookie_count = 0;
- for (i = 0; i < max_hdrs; i++) {
- struct mg_str k = {NULL, 0}, v = {NULL, 0};
- if (s >= end) return false;
- if (s[0] == '\n' || (s[0] == '\r' && s[1] == '\n')) break;
- k.buf = (char *) s;
- while (s < end && s[0] != ':' && (n = clen(s, end)) > 0) s += n, k.len += n;
- if (k.len == 0) return false; // Empty name
- if (s >= end || clen(s, end) == 0) return false; // Invalid UTF-8
- if (*s++ != ':') return false; // Invalid, not followed by :
- // if (clen(s, end) == 0) return false; // Invalid UTF-8
- while (s < end && (s[0] == ' ' || s[0] == '\t')) s++; // Skip spaces
- if ((s = skiptorn(s, end, &v)) == NULL) return false;
- while (v.len > 0 && (v.buf[v.len - 1] == ' ' || v.buf[v.len - 1] == '\t')) {
- v.len--; // Trim spaces
- }
- // detect duplicated headers -> discard
- if (((mg_strcasecmp(k, mg_str("Content-Length")) == 0) &&
- (++cl_count > 1)) ||
- ((mg_strcasecmp(k, mg_str("Transfer-Encoding")) == 0) &&
- (++te_count > 1)) ||
- ((mg_strcasecmp(k, mg_str("Authorization")) == 0) &&
- (++auth_count > 1)) ||
- ((mg_strcasecmp(k, mg_str("Cookie")) == 0) && (++cookie_count > 1)) ||
- ((mg_strcasecmp(k, mg_str("Connection")) == 0) && (++conn_count > 1)))
- return false;
- // MG_INFO(("--HH [%.*s] [%.*s]", (int) k.len, k.buf, (int) v.len, v.buf));
- h[i].name = k, h[i].value = v; // Success. Assign values
- }
- return true;
- }
- 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
- const struct mg_str *cl;
- size_t n;
- bool version_prefix_valid;
- memset(hm, 0, sizeof(*hm));
- if (req_len <= 0) return req_len;
- hm->message.buf = hm->head.buf = (char *) s;
- hm->body.buf = (char *) end;
- hm->head.len = (size_t) req_len;
- hm->message.len = hm->body.len = (size_t) -1; // Set body length to infinite
- // Parse request line
- hm->method.buf = (char *) s;
- while (s < end && (n = clen(s, end)) > 0) s += n, hm->method.len += n;
- while (s < end && s[0] == ' ') s++; // Skip spaces
- hm->uri.buf = (char *) s;
- while (s < end && (n = clen(s, end)) > 0) s += n, hm->uri.len += n;
- while (s < end && s[0] == ' ') s++; // Skip spaces
- is_response =
- hm->method.len > 5 && (mg_ncasecmp(hm->method.buf, "HTTP/", 5) == 0);
- if ((s = skiptorn(s, end, &hm->proto)) == NULL) return false;
- // If we're given a version, check that it is HTTP/x.x
- version_prefix_valid =
- hm->proto.len > 5 && (mg_ncasecmp(hm->proto.buf, "HTTP/", 5) == 0);
- if (!is_response && !version_prefix_valid)
- return -1; // no version detected in request
- if (!is_response && hm->proto.len > 0 &&
- (!version_prefix_valid || hm->proto.len != 8 ||
- (hm->proto.buf[5] < '0' || hm->proto.buf[5] > '9') ||
- (hm->proto.buf[6] != '.') ||
- (hm->proto.buf[7] < '0' || hm->proto.buf[7] > '9'))) {
- return -1;
- }
- // If URI contains '?' character, setup query string
- if ((qs = (const char *) memchr(hm->uri.buf, '?', hm->uri.len)) != NULL) {
- hm->query.buf = (char *) qs + 1;
- hm->query.len = (size_t) (&hm->uri.buf[hm->uri.len] - (qs + 1));
- hm->uri.len = (size_t) (qs - hm->uri.buf);
- }
- // Sanity check. Allow protocol/reason to be empty
- // Do this check after hm->method.len and hm->uri.len are finalised
- if (hm->method.len == 0 || hm->uri.len == 0) return -1;
- if (!mg_http_parse_headers(s, end, hm->headers,
- sizeof(hm->headers) / sizeof(hm->headers[0])))
- return -1; // error when parsing
- cl = mg_http_get_header(hm, "Content-Length");
- if (cl != NULL && mg_http_get_header(hm, "Transfer-Encoding") != NULL)
- return -1; // cannot contain both CL and TE
- if (cl != NULL) {
- if (mg_to_size_t(*cl, &hm->body.len) == false) return -1;
- 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, if Content-Length is not specified
- // set body length to 0.
- if (hm->body.len == (size_t) ~0 && !is_response) {
- 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_strcasecmp(hm->uri, mg_str("204")) == 0) {
- hm->body.len = 0;
- hm->message.len = (size_t) req_len;
- }
- if (hm->message.len < (size_t) req_len) return -1; // Overflow protection
- 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;
- if (!mg_send(c, " \r\n", 10)) mg_error(c, "OOM");
- 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
- }
- if (!mg_send(c, "\r\n", 2)) mg_error(c, "OOM");
- }
- 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);
- if (!mg_send(c, buf, len) || !mg_send(c, "\r\n", 2)) mg_error(c, "OOM");
- 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 101: return "Switching Protocols";
- case 102: return "Processing";
- case 200: return "OK";
- case 201: return "Created";
- case 202: return "Accepted";
- case 203: return "Non-authoritative Information";
- case 204: return "No Content";
- case 205: return "Reset Content";
- case 206: return "Partial Content";
- case 207: return "Multi-Status";
- case 208: return "Already Reported";
- case 226: return "IM Used";
- case 300: return "Multiple Choices";
- case 301: return "Moved Permanently";
- case 302: return "Found";
- case 303: return "See Other";
- case 304: return "Not Modified";
- case 305: return "Use Proxy";
- case 307: return "Temporary Redirect";
- case 308: return "Permanent Redirect";
- case 400: return "Bad Request";
- case 401: return "Unauthorized";
- case 402: return "Payment Required";
- case 403: return "Forbidden";
- case 404: return "Not Found";
- case 405: return "Method Not Allowed";
- case 406: return "Not Acceptable";
- case 407: return "Proxy Authentication Required";
- case 408: return "Request Timeout";
- case 409: return "Conflict";
- case 410: return "Gone";
- case 411: return "Length Required";
- case 412: return "Precondition Failed";
- case 413: return "Payload Too Large";
- case 414: return "Request-URI Too Long";
- case 415: return "Unsupported Media Type";
- case 416: return "Requested Range Not Satisfiable";
- case 417: return "Expectation Failed";
- case 418: return "I'm a teapot";
- case 421: return "Misdirected Request";
- case 422: return "Unprocessable Entity";
- case 423: return "Locked";
- case 424: return "Failed Dependency";
- case 426: return "Upgrade Required";
- case 428: return "Precondition Required";
- case 429: return "Too Many Requests";
- case 431: return "Request Header Fields Too Large";
- case 444: return "Connection Closed Without Response";
- case 451: return "Unavailable For Legal Reasons";
- case 499: return "Client Closed Request";
- case 500: return "Internal Server Error";
- case 501: return "Not Implemented";
- case 502: return "Bad Gateway";
- case 503: return "Service Unavailable";
- case 504: return "Gateway Timeout";
- case 505: return "HTTP Version Not Supported";
- case 506: return "Variant Also Negotiates";
- case 507: return "Insufficient Storage";
- case 508: return "Loop Detected";
- case 510: return "Not Extended";
- case 511: return "Network Authentication Required";
- case 599: return "Network Connect Timeout Error";
- default: return "";
- }
- }
- // 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 *);
- 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) {
- if (ev == MG_EV_WRITE || ev == MG_EV_POLL) {
- struct mg_fd *fd = (struct mg_fd *) c->pfn_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
- #define MG_C_STR(a) { (char *) (a), sizeof(a) - 1 }
- 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("mjs"), 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 entry, k, v, s = mg_str(extra), asterisk = mg_str_n("*", 1);
- size_t i = 0;
- // Shrink path to its extension only
- while (i < path.len && path.buf[path.len - i - 1] != '.') i++;
- path.buf += path.len - i;
- path.len = i;
- // Process user-provided mime type overrides, if any
- while (mg_span(s, &entry, &s, ',')) {
- if (mg_span(entry, &k, &v, '=') &&
- (mg_strcmp(asterisk, k) == 0 || mg_strcmp(path, k) == 0))
- return v;
- }
- // Process built-in mime types
- for (i = 0; s_known_types[i].buf != 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, size_t *a, size_t *b) {
- size_t i, numparsed = 0;
- for (i = 0; i + 6 < s->len; i++) {
- struct mg_str k, v = mg_str_n(s->buf + i + 6, s->len - i - 6);
- if (memcmp(&s->buf[i], "bytes=", 6) != 0) continue;
- if (mg_span(v, &k, &v, '-')) {
- if (mg_to_size_t(k, a)) numparsed++;
- if (v.len > 0 && mg_to_size_t(v, b)) numparsed++;
- } else {
- if (mg_to_size_t(v, a)) 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 && opts->fs ? opts->fs : &mg_fs_posix;
- struct mg_fd *fd = NULL;
- size_t size = 0;
- time_t mtime = 0;
- const char *mime_types = opts && opts->mime_types ? opts->mime_types : NULL;
- const char *hdrs = opts && opts->extra_headers ? opts->extra_headers : "";
- struct mg_str *inm = NULL;
- struct mg_str mime = guess_content_type(mg_str(path), mime_types);
- bool gzip = false;
- if (path != NULL) {
- // If a browser sends us "Accept-Encoding: gzip", try to open .gz first
- struct mg_str *ae = mg_http_get_header(hm, "Accept-Encoding");
- if (ae != NULL) {
- if (mg_match(*ae, mg_str("*gzip*"), NULL)) {
- mg_snprintf(tmp, sizeof(tmp), "%s.gz", path);
- fd = mg_fs_open(fs, tmp, MG_FS_READ);
- if (fd != NULL) gzip = true, path = tmp;
- }
- }
- // No luck opening .gz? Open what we've told to open
- if (fd == NULL) fd = mg_fs_open(fs, path, MG_FS_READ);
- }
- // Failed to open, and page404 is configured? Open it, then
- if (fd == NULL && opts && opts->page404) {
- fd = mg_fs_open(fs, opts->page404, MG_FS_READ);
- path = opts->page404;
- mime = guess_content_type(mg_str(path), mime_types);
- }
- if (fd == NULL || fs->st(path, &size, &mtime) == 0) {
- mg_http_reply(c, 404, hdrs, "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_strcasecmp(*inm, mg_str(etag)) == 0) {
- mg_fs_close(fd);
- mg_http_reply(c, 304, hdrs, "");
- } else {
- int n, status = 200;
- char range[100];
- size_t r1 = 0, r2 = 0, cl = 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) {
- // 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 %llu-%llu/%llu\r\n", (uint64_t) r1,
- (uint64_t) (r1 + cl - 1), (uint64_t) size);
- fs->sk(fd->fd, 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.buf,
- etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "",
- range, hdrs);
- if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0 || c->is_closing) {
- c->is_resp = 0;
- mg_fs_close(fd);
- } else { // start serving static content only if not closing, see #3354
- // 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 = cl;
- }
- }
- }
- struct printdirentrydata {
- struct mg_connection *c;
- struct mg_http_message *hm;
- const struct mg_http_serve_opts *opts;
- const char *dir;
- };
- #if MG_ENABLE_DIRLIST
- // Print file name, escaping HTML chars
- static size_t html_esc(void (*fn)(char, void *), void *arg, va_list *ap) {
- const char *s = va_arg(*ap, const char *);
- size_t i, len = 0;
- for (i = 0; s[i] != '\0'; i++) {
- if (s[i] == '<') {
- len += mg_xprintf(fn, arg, "%s", "<");
- } else if (s[i] == '>') {
- len += mg_xprintf(fn, arg, "%s", ">");
- } else if (s[i] == '&') {
- len += mg_xprintf(fn, arg, "%s", "&");
- } else {
- len += mg_xprintf(fn, arg, "%c", s[i]);
- }
- }
- return len;
- }
- 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->c->id, path));
- } 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\">%M%s</a></td>"
- "<td name=%lu>%s</td><td name=%lld>%s</td></tr>\n",
- n, path, slash, html_esc, 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.buf, 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 %M</title>%s%s"
- "<style>th,td {text-align: left; padding-right: 1em; "
- "font-family: monospace; }</style></head>"
- "<body><h1>Index of %M</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",
- mg_print_html_esc, (int) uri.len, uri.buf, sort_js_code, sort_js_code2,
- mg_print_html_esc, (int) uri.len, uri.buf);
- 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
- }
- #endif
- // 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.buf);
- if (n + 2 >= path_size) {
- mg_http_reply(c, 400, "", "Exceeded path size");
- return -1;
- }
- path[path_size - 1] = '\0';
- // Terminate root dir with slash
- if (n > 0 && path[n - 1] != '/') path[n++] = '/', path[n] = '\0';
- if (url.len < hm->uri.len) {
- mg_url_decode(hm->uri.buf + url.len, hm->uri.len - url.len, path + n,
- path_size - n, 0);
- }
- path[path_size - 1] = '\0'; // Double-check
- n = strlen(path);
- if (!mg_path_is_sane(mg_str_n(path, n))) {
- mg_http_reply(c, 400, "", "Invalid path");
- return -1;
- }
- while (n > 1 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes
- flags = mg_strcmp(hm->uri, mg_str("/")) == 0 ? MG_FS_DIR
- : fs->st(path, NULL, NULL);
- MG_VERBOSE(("%lu %.*s -> %s %d", c->id, (int) hm->uri.len, hm->uri.buf, path,
- flags));
- if (flags == 0) {
- // Do nothing - let's caller decide
- } else if ((flags & MG_FS_DIR) && hm->uri.len > 0 &&
- hm->uri.buf[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.buf);
- 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, part, s = mg_str(opts->root_dir), u = {NULL, 0}, p = u;
- while (mg_span(s, &part, &s, ',')) {
- if (!mg_span(part, &k, &v, '=')) k = part, v = mg_str_n(NULL, 0);
- if (v.len == 0) v = k, k = mg_str("/"), u = k, p = v;
- if (hm->uri.len < k.len) continue;
- if (mg_strcmp(k, mg_str_n(hm->uri.buf, 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) {
- #if MG_ENABLE_DIRLIST
- listdir(c, hm, opts, path);
- #else
- mg_http_reply(c, 403, "", "Forbidden\n");
- #endif
- } else if (flags && sp != NULL && mg_match(mg_str(path), mg_str(sp), NULL)) {
- 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 {
- mg_snprintf(&buf[n], 4, "%%%M", mg_print_hex, 1, &s[i]);
- n += 3;
- }
- }
- 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->buf, "Basic ", 6) == 0) {
- char buf[256];
- size_t n = mg_base64_decode(v->buf + 6, v->len - 6, buf, sizeof(buf));
- const char *p = (const char *) memchr(buf, ':', n > 0 ? n : 0);
- if (p != NULL) {
- mg_snprintf(user, userlen, "%.*s", p - buf, buf);
- mg_snprintf(pass, passlen, "%.*s", n - (size_t) (p - buf) - 1, p + 1);
- }
- } else if (v != NULL && v->len > 7 && memcmp(v->buf, "Bearer ", 7) == 0) {
- mg_snprintf(pass, passlen, "%.*s", (int) v->len - 7, v->buf + 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.buf);
- } 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.buf[0] == '"' && s.buf[s.len - 1] == '"'
- ? mg_str_n(s.buf + 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.buf[i + v.len] == '=' && memcmp(&s.buf[i], v.buf, v.len) == 0) {
- const char *p = &s.buf[i + v.len + 1], *b = p, *x = &s.buf[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.buf, (int) v.len,
- // v.buf, (int) (p - b), b));
- return stripquotes(mg_str_n(b,
- (size_t) (p - b + (q && p < x && *p == '"' ? 1 : 0))));
- }
- }
- return mg_str_n(NULL, 0);
- }
- long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
- struct mg_fs *fs, const char *dir, size_t max_size) {
- char buf[20] = "0", file[MG_PATH_MAX], path[MG_PATH_MAX];
- long res = 0, offset;
- mg_http_get_var(&hm->query, "offset", buf, sizeof(buf));
- mg_http_get_var(&hm->query, "file", file, sizeof(file));
- offset = strtol(buf, NULL, 0);
- mg_snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, file);
- if (hm->body.len == 0) {
- mg_http_reply(c, 200, "", "%ld", res); // Nothing to write
- } else if (file[0] == '\0') {
- mg_http_reply(c, 400, "", "file required");
- res = -1;
- } else if (mg_path_is_sane(mg_str(file)) == false) {
- mg_http_reply(c, 400, "", "%s: invalid file", file);
- res = -2;
- } else if (offset < 0) {
- mg_http_reply(c, 400, "", "offset required");
- res = -3;
- } 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 = -4;
- } else {
- struct mg_fd *fd;
- size_t current_size = 0;
- MG_DEBUG(("%s -> %lu bytes @ %ld", path, 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 && current_size != (size_t) offset) {
- mg_http_reply(c, 400, "", "%s: offset mismatch", path);
- res = -5;
- } else if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
- mg_http_reply(c, 400, "", "open(%s)", path);
- res = -6;
- } else {
- res = offset + (long) fs->wr(fd->fd, hm->body.buf, 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.buf);
- }
- struct mg_upload_priv {
- size_t expected;
- size_t received;
- struct mg_fd *fd; // non-NULL: file upload; NULL: OTA
- void (*fn)(struct mg_connection *, const char *);
- };
- static void mg_upload_handler(struct mg_connection *c, int ev, void *ev_data) {
- struct mg_upload_priv *p = (struct mg_upload_priv *) c->data;
- if (p->fn == NULL) return;
- if (ev == MG_EV_READ && c->recv.len > 0) {
- size_t alignment = 512;
- size_t left = p->expected > p->received ? p->expected - p->received : 0;
- size_t aligned = c->recv.len < left ? MG_ROUND_DOWN(c->recv.len, alignment)
- : left;
- bool ok = true;
- if (aligned > 0) {
- if (p->fd != NULL) {
- ok = p->fd->fs->wr(p->fd->fd, c->recv.buf, aligned) == aligned;
- } else {
- ok = mg_ota_write(c->recv.buf, aligned);
- }
- }
- p->received += aligned;
- mg_iobuf_del(&c->recv, 0, aligned);
- if (!ok) {
- if (p->fd != NULL) { mg_fs_close(p->fd); p->fd = NULL; }
- else mg_ota_end();
- p->fn(c, "write error");
- mg_free(c->pfn_data); c->pfn_data = NULL; p->fn = NULL;
- } else if (p->received >= p->expected) {
- const char *errmsg = NULL;
- if (p->fd != NULL) { mg_fs_close(p->fd); p->fd = NULL; }
- else if (!mg_ota_end()) errmsg = "OTA finalize failed";
- p->fn(c, errmsg);
- mg_free(c->pfn_data); c->pfn_data = NULL; p->fn = NULL;
- }
- } else if (ev == MG_EV_ERROR || ev == MG_EV_CLOSE) {
- if (p->fd != NULL) { mg_fs_close(p->fd); p->fd = NULL; }
- else mg_ota_end();
- p->fn(c, ev == MG_EV_ERROR ? (const char *) ev_data : "connection closed");
- mg_free(c->pfn_data); c->pfn_data = NULL; p->fn = NULL;
- }
- (void) ev_data;
- }
- static void mg_upload_default_cb(struct mg_connection *c, const char *status) {
- MG_INFO(("%lu %s", c->id, status ? status : "ok"));
- mg_http_reply(c, status ? 500 : 200, "", "%s\n", status ? status : "ok");
- }
- void mg_http_start_upload(struct mg_connection *c, struct mg_http_message *hm,
- struct mg_str name, struct mg_str dir,
- struct mg_fs *fs,
- void (*fn)(struct mg_connection *, const char *)) {
- struct mg_upload_priv *p = (struct mg_upload_priv *) c->data;
- char path[MG_PATH_MAX];
- struct mg_fd *fd;
- if (fn == NULL) fn = mg_upload_default_cb;
- if (sizeof(*p) > sizeof(c->data)) { fn(c, "data too small"); return; }
- if (!mg_path_is_sane(name)) { fn(c, "bad name"); return; }
- mg_snprintf(path, sizeof(path), "%.*s%c%.*s", (int) dir.len, dir.buf,
- MG_DIRSEP, (int) name.len, name.buf);
- fd = mg_fs_open(fs, path, MG_FS_WRITE);
- if (fd == NULL) { fn(c, "open failed"); return; }
- p->expected = hm->body.len;
- p->received = 0;
- p->fd = fd;
- p->fn = fn;
- c->fn = mg_upload_handler;
- c->pfn_data = strdup(path);
- c->pfn = NULL;
- mg_iobuf_del(&c->recv, 0, hm->head.len);
- mg_call(c, MG_EV_READ, &c->recv.len);
- }
- void mg_http_start_ota(struct mg_connection *c, struct mg_http_message *hm,
- void (*fn)(struct mg_connection *, const char *)) {
- struct mg_upload_priv *p = (struct mg_upload_priv *) c->data;
- if (fn == NULL) fn = mg_upload_default_cb;
- if (sizeof(*p) > sizeof(c->data)) { fn(c, "data too small"); return; }
- if (!mg_ota_begin(hm->body.len)) { fn(c, "ota begin failed"); return; }
- p->expected = hm->body.len;
- p->received = 0;
- p->fd = NULL;
- p->fn = fn;
- c->fn = mg_upload_handler;
- c->pfn = NULL;
- mg_iobuf_del(&c->recv, 0, hm->head.len);
- mg_call(c, MG_EV_READ, &c->recv.len);
- }
- static bool is_hex_digit(int c) {
- return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
- (c >= 'A' && c <= 'F');
- }
- static int skip_chunk(const char *buf, int len, int *pl, int *dl) {
- int i = 0, n = 0; // pass int to mg_str_to_num, treats as unsigned
- if (len < 3) return 0;
- while (i < len && is_hex_digit(buf[i])) i++;
- if (i == 0) return -1; // Error, no length specified
- if (len < i + 2 || buf[i] != '\r' || buf[i + 1] != '\n') return -1; // Error
- if (mg_str_to_num(mg_str_n(buf, (size_t) i), 16, &n, sizeof(n)) == false)
- return -1; // Decode chunk length, overflow
- if (n < 0) return -1; // Chunk length is too big <0 = >0x80000000
- if (n > len - i - 4) return 0; // Chunk not yet fully buffered
- if (buf[i + n + 2] != '\r' || buf[i + n + 3] != '\n') return -1; // Error
- *pl = i + 2, *dl = n;
- return i + 2 + n + 2;
- }
- static void http_cb(struct mg_connection *c, int ev, void *ev_data) {
- if (ev == MG_EV_READ || ev == MG_EV_CLOSE ||
- (ev == MG_EV_POLL && c->is_accepted && !c->is_draining &&
- c->recv.len > 0)) { // see #2796
- struct mg_http_message hm;
- size_t ofs = 0; // Parsing offset
- while (c->is_resp == 0 && ofs < c->recv.len) {
- const char *buf = (char *) c->recv.buf + ofs;
- int n = mg_http_parse(buf, c->recv.len - ofs, &hm);
- struct mg_str *te; // Transfer - encoding header
- bool is_chunked = false, is_http_1_0 = false;
- size_t old_len = c->recv.len;
- if (n < 0) {
- // We don't use mg_error() here, to avoid closing pipelined requests
- // prematurely, see #2592
- MG_ERROR(("HTTP parse, %lu bytes", c->recv.len));
- c->is_draining = 1;
- mg_hexdump(buf, c->recv.len - ofs > 16 ? 16 : c->recv.len - ofs);
- c->recv.len = 0;
- return;
- }
- if (n == 0) break; // Request is not buffered yet
- mg_call(c, MG_EV_HTTP_HDRS, &hm); // Got all HTTP headers
- if (c->recv.len != old_len) {
- // User manipulated received data. Wash our hands
- MG_DEBUG(("%lu detaching HTTP handler", c->id));
- c->pfn = NULL;
- return;
- }
- if (ev == MG_EV_CLOSE) { // If client did not set Content-Length
- hm.message.len = c->recv.len - ofs; // and closes now, deliver MSG
- hm.body.len = hm.message.len - (size_t) (hm.body.buf - hm.message.buf);
- }
- is_http_1_0 =
- hm.proto.len == 8 && mg_ncasecmp(hm.proto.buf, "HTTP/1.0", 8) == 0;
- // HTTP/1.0 does not use "Transfer-Encoding: chunked"
- if (!is_http_1_0 &&
- (te = mg_http_get_header(&hm, "Transfer-Encoding")) != NULL) {
- if (mg_strcasecmp(*te, mg_str("chunked")) == 0) {
- is_chunked = true;
- } else {
- mg_error(c, "Invalid Transfer-Encoding"); // See #2460
- return;
- }
- } else if (mg_http_get_header(&hm, "Content-length") == NULL) {
- // #2593: HTTP packets must contain either Transfer-Encoding or
- // Content-length
- bool is_response = mg_ncasecmp(hm.method.buf, "HTTP/", 5) == 0;
- bool require_content_len = false;
- if (!is_response && (mg_strcasecmp(hm.method, mg_str("POST")) == 0 ||
- mg_strcasecmp(hm.method, mg_str("PUT")) == 0)) {
- // POST and PUT should include an entity body. Therefore, they should
- // contain a Content-length header (unless the body length is 0, in
- // which case it can be omitted). Other requests can also contain a
- // body, but their content has no defined semantics (RFC 7231)
- if (hm.body.len != 0) require_content_len = true;
- ofs += (size_t) n; // this request has been processed
- } else if (is_response) {
- // HTTP spec 7.2 Entity body: All other responses must include a body
- // or Content-Length header field defined with a value of 0.
- int status = mg_http_status(&hm);
- require_content_len = status >= 200 && status != 204 && status != 304;
- }
- if (require_content_len) {
- if (!c->is_client) mg_http_reply(c, 411, "", "");
- MG_ERROR(("Content length missing from %s",
- is_response ? "response" : "request"));
- }
- }
- if (is_chunked) {
- // For chunked data, strip off prefixes and suffixes from chunks
- // and relocate them right after the headers, then report a message
- char *s = (char *) c->recv.buf + ofs + n;
- int o = 0, pl, dl, cl, len = (int) (c->recv.len - ofs - (size_t) n);
- // Find zero-length chunk (the end of the body)
- while ((cl = skip_chunk(s + o, len - o, &pl, &dl)) > 0 && dl) o += cl;
- if (cl == 0) break; // No zero-len chunk, buffer more data
- if (cl < 0) {
- mg_error(c, "Invalid chunk");
- break;
- }
- // Zero chunk found. Second pass: strip + relocate
- o = 0, hm.body.len = 0, hm.message.len = (size_t) n;
- while ((cl = skip_chunk(s + o, len - o, &pl, &dl)) > 0) {
- memmove(s + hm.body.len, s + o + pl, (size_t) dl);
- o += cl, hm.body.len += (size_t) dl, hm.message.len += (size_t) dl;
- if (dl == 0) break;
- }
- ofs += (size_t) (n + o);
- } else { // Normal, non-chunked data
- size_t len = c->recv.len - ofs - (size_t) n;
- if (hm.body.len > len) break; // Buffer more data
- ofs += (size_t) n + hm.body.len;
- }
- 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
- if (c->is_accepted && !c->is_resp) {
- struct mg_str *cc = mg_http_get_header(&hm, "Connection");
- if (cc != NULL && mg_strcasecmp(*cc, mg_str("close")) == 0) {
- c->is_draining = 1; // honor "Connection: close"
- break;
- }
- }
- }
- if (ofs > 0) mg_iobuf_del(&c->recv, 0, ofs); // Delete processed data
- }
- (void) ev_data;
- }
- struct mg_connection *mg_http_connect(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data) {
- return mg_connect_svc(mgr, url, fn, fn_data, http_cb, NULL);
- }
- 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
- static size_t roundup(size_t size, size_t align) {
- return align == 0 ? size : (size + align - 1) / align * align;
- }
- bool mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) {
- bool ok = true;
- new_size = roundup(new_size, io->align);
- if (new_size == 0) {
- mg_bzero(io->buf, io->size);
- mg_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 mg_calloc/mg_free only
- void *p = mg_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);
- mg_bzero(io->buf, io->size);
- mg_free(io->buf);
- io->buf = (unsigned char *) p;
- io->size = new_size;
- io->len = len;
- } else {
- ok = false;
- MG_ERROR(("%lld->%lld", (uint64_t) io->size, (uint64_t) new_size));
- }
- }
- return ok;
- }
- bool 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) mg_bzero(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 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');
- // use fast exponentiation
- // https://en.wikipedia.org/wiki/Exponentiation_by_squaring
- if (exp != 0) {
- double x = 10, y = 1;
- if (exp > 308) exp = 308;
- if (minus) x = 0.1;
- while (exp > 1) {
- if (exp & 1) {
- y *= x;
- --exp;
- }
- x *= x;
- exp >>= 1;
- }
- d *= x * y;
- }
- }
- if (numlen != NULL) *numlen = i;
- return d;
- }
- // Iterate over object or array elements
- size_t mg_json_next(struct mg_str obj, size_t ofs, struct mg_str *key,
- struct mg_str *val) {
- if (ofs >= obj.len) {
- ofs = 0; // Out of boundaries, stop scanning
- } else if (obj.len < 2 || (*obj.buf != '{' && *obj.buf != '[')) {
- ofs = 0; // Not an array or object, stop
- } else {
- struct mg_str sub = mg_str_n(obj.buf + ofs, obj.len - ofs);
- if (ofs == 0) ofs++, sub.buf++, sub.len--;
- if (*obj.buf == '[') { // Iterate over an array
- int n = 0, o = mg_json_get(sub, "$", &n);
- if (n < 0 || o < 0 || (size_t) (o + n) > sub.len) {
- ofs = 0; // Error parsing key, stop scanning
- } else {
- if (key) *key = mg_str_n(NULL, 0);
- if (val) *val = mg_str_n(sub.buf + o, (size_t) n);
- ofs = (size_t) (&sub.buf[o + n] - obj.buf);
- }
- } else { // Iterate over an object
- int n = 0, o = mg_json_get(sub, "$", &n);
- if (n < 0 || o < 0 || (size_t) (o + n) > sub.len) {
- ofs = 0; // Error parsing key, stop scanning
- } else {
- if (key) *key = mg_str_n(sub.buf + o, (size_t) n);
- sub.buf += o + n, sub.len -= (size_t) (o + n);
- while (sub.len > 0 && *sub.buf != ':') sub.len--, sub.buf++;
- if (sub.len > 0 && *sub.buf == ':') sub.len--, sub.buf++;
- n = 0, o = mg_json_get(sub, "$", &n);
- if (n < 0 || o < 0 || (size_t) (o + n) > sub.len) {
- ofs = 0; // Error parsing value, stop scanning
- } else {
- if (val) *val = mg_str_n(sub.buf + o, (size_t) n);
- ofs = (size_t) (&sub.buf[o + n] - obj.buf);
- }
- }
- }
- // MG_INFO(("SUB ofs %u %.*s", ofs, sub.len, sub.buf));
- while (ofs && ofs < obj.len &&
- (obj.buf[ofs] == ' ' || obj.buf[ofs] == '\t' ||
- obj.buf[ofs] == '\n' || obj.buf[ofs] == '\r')) {
- ofs++;
- }
- if (ofs && ofs < obj.len && obj.buf[ofs] == ',') ofs++;
- if (ofs > obj.len) ofs = 0;
- }
- return ofs;
- }
- int mg_json_get(struct mg_str json, const char *path, int *toklen) {
- const char *s = json.buf;
- 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 %d %d\n", path, pos, path, n,
- // &s[i + 1], n, depth, ed, ci, ei);
- // 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;
- if (depth == ed && ei >= 0) ci++;
- } 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 == '}') {
- if (depth == ed && c == '}' && path[pos - 1] == '.')
- return MG_JSON_NOT_FOUND;
- if (depth == ed && c == ']' && path[pos - 1] == ',')
- return MG_JSON_NOT_FOUND;
- MG_EOO('O');
- if (depth == ed && ei >= 0) ci++;
- } else {
- return MG_JSON_INVALID;
- }
- break;
- }
- }
- return MG_JSON_NOT_FOUND;
- }
- struct mg_str mg_json_get_tok(struct mg_str json, const char *path) {
- int len = 0, ofs = mg_json_get(json, path, &len);
- return mg_str_n(ofs < 0 ? NULL : json.buf + ofs,
- (size_t) (len < 0 ? 0 : len));
- }
- 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.buf[n] == '-' || (json.buf[n] >= '0' && json.buf[n] <= '9'))) {
- if (v != NULL) *v = mg_atod(json.buf + 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.buf[off] == 't' || json.buf[off] == 'f')) {
- if (v != NULL) *v = json.buf[off] == 't';
- found = 1;
- }
- return found;
- }
- size_t mg_json_unescape(struct mg_str json, const char *path, char *to,
- size_t n) {
- struct mg_str s = mg_json_get_tok(json, path);
- size_t res = 0, i, j;
- if (s.len > 1 && s.buf[0] == '"') { // Is is a string?
- s.len -= 2, s.buf++; // Trim surrounding double-quotes
- for (i = 0, j = 0; i < s.len && j < n; i++, j++) {
- if (s.buf[i] == '\\' && i + 5 < s.len && s.buf[i + 1] == 'u') {
- // \uXXXX escape. We process simple one-byte chars \u00xx within ASCII
- // range. More complex chars would require dragging in a UTF8 library,
- // which is too much for us
- if (mg_str_to_num(mg_str_n(s.buf + i + 2, 4), 16, &to[j],
- sizeof(uint8_t)) == false)
- break;
- i += 5;
- } else if (s.buf[i] == '\\' && i + 1 < s.len) {
- char c = json_esc(s.buf[i + 1], 0);
- if (c == 0) return false;
- to[j] = c;
- i++;
- } else {
- to[j] = s.buf[i];
- }
- }
- if (j < n) res = j;
- if (n > 0) to[j < n ? j : n - 1] = '\0';
- }
- return res;
- }
- 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 >= 2 && json.buf[off] == '"') {
- if ((result = (char *) mg_calloc(1, (size_t) len)) != NULL &&
- len > 2 && mg_json_unescape(json, path, result, (size_t) len) == 0) {
- mg_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.buf[off] == '"' && len > 1 &&
- (result = (char *) mg_calloc(1, (size_t) len)) != NULL) {
- size_t k = mg_base64_decode(json.buf + off + 1, (size_t) (len - 2), result,
- (size_t) len);
- if (slen != NULL) *slen = (int) 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.buf[off] == '"' && len > 1 && (len - 2) % 2 == 0 &&
- (result = (char *) mg_calloc(1, (size_t) len / 2)) != NULL) {
- int i;
- for (i = 0; i < len - 2; i += 2) {
- if (!mg_str_to_num(mg_str_n(json.buf + off + 1 + i, 2), 16,
- &result[i >> 1], sizeof(uint8_t))) break;
- }
- if (i < len - 2) mg_free(result), result = NULL;
- if (result != NULL) {
- 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/jwt.c"
- #endif
- #if MG_TLS == MG_TLS_BUILTIN
- #endif
- static size_t jwt_split(struct mg_str jwt, struct mg_str *h, struct mg_str *p,
- struct mg_str *s) {
- size_t i, a = jwt.len, b = jwt.len;
- for (i = 0; i < jwt.len; i++) {
- if (jwt.buf[i] == '.') {
- if (a == jwt.len) a = i;
- if (a != i) b = i;
- if (b != jwt.len) break;
- }
- }
- if (a == jwt.len || b == jwt.len || b + 1 >= jwt.len) return 0;
- *h = mg_str_n(jwt.buf, a);
- *p = mg_str_n(jwt.buf + a + 1, b - a - 1);
- *s = mg_str_n(jwt.buf + b + 1, jwt.len - b - 1);
- return b;
- }
- static bool jwt_alg(struct mg_str h, const char *alg) {
- char got[16];
- size_t len = h.len * 3 / 4 + 2;
- char *buf = (char *) mg_calloc(1, len);
- bool ok;
- if (buf == NULL) return false;
- ok = mg_base64url_decode(h.buf, h.len, buf, len) > 0 &&
- mg_json_unescape(mg_str(buf), "$.alg", got, sizeof(got)) > 0 &&
- strcmp(got, alg) == 0;
- mg_free(buf);
- return ok;
- }
- static size_t jwt_header_payload(const char *alg, const struct mg_jwt_opts *opts,
- char *buf, size_t len) {
- char *header, *kid = NULL;
- size_t n, m;
- if (opts->kid.len > 0) {
- kid = mg_mprintf(",%m:%m", MG_ESC("kid"), mg_print_esc,
- (int) opts->kid.len, opts->kid.buf);
- if (kid == NULL) return 0;
- }
- header = mg_mprintf("{%m:%m,%m:%m%.*s%.*s%.*s}", MG_ESC("alg"),
- MG_ESC(alg), MG_ESC("typ"), MG_ESC("JWT"),
- kid == NULL ? 0 : (int) strlen(kid),
- kid == NULL ? "" : kid,
- opts->header.len > 0 ? 1 : 0, ",",
- (int) opts->header.len, opts->header.buf);
- mg_free(kid);
- if (header == NULL) return 0;
- n = mg_base64url_encode((uint8_t *) header, strlen(header), buf, len);
- mg_free(header);
- if (n == 0 || n + 1 >= len) return 0;
- buf[n++] = '.';
- m = mg_base64url_encode((uint8_t *) opts->claims.buf, opts->claims.len,
- buf + n, len - n);
- return m == 0 ? 0 : n + m;
- }
- size_t mg_jwt_sign_hs256(const struct mg_jwt_opts *opts, char *buf,
- size_t len) {
- uint8_t sig[32];
- size_t n = jwt_header_payload("HS256", opts, buf, len);
- size_t m;
- if (n == 0 || n + 1 >= len) return 0;
- mg_hmac_sha256(sig, (uint8_t *) opts->secret.buf, opts->secret.len,
- (uint8_t *) buf, n);
- buf[n++] = '.';
- m = mg_base64url_encode(sig, sizeof(sig), buf + n, len - n);
- return m == 0 ? 0 : n + m;
- }
- size_t mg_jwt_verify_hs256(struct mg_str jwt, const struct mg_jwt_opts *opts,
- char *buf, size_t len) {
- struct mg_str h, p, s;
- uint8_t sig[33], digest[32];
- size_t n = jwt_split(jwt, &h, &p, &s);
- if (n == 0 || !jwt_alg(h, "HS256")) return 0;
- if (mg_base64url_decode(s.buf, s.len, (char *) sig, sizeof(sig)) !=
- sizeof(digest))
- return 0;
- mg_hmac_sha256(digest, (uint8_t *) opts->secret.buf, opts->secret.len,
- (uint8_t *) jwt.buf, n);
- if (!mg_memeq(sig, digest, sizeof(digest))) return 0;
- return mg_base64url_decode(p.buf, p.len, buf, len);
- }
- #if MG_TLS == MG_TLS_BUILTIN
- typedef struct {
- MG_UECC_HashContext uECC;
- mg_sha256_ctx sha256;
- } jwt_hash_ctx;
- static void jwt_hash_init(const MG_UECC_HashContext *ctx) {
- jwt_hash_ctx *c = (jwt_hash_ctx *) ctx;
- mg_sha256_init(&c->sha256);
- }
- static void jwt_hash_update(const MG_UECC_HashContext *ctx, const uint8_t *msg,
- unsigned len) {
- jwt_hash_ctx *c = (jwt_hash_ctx *) ctx;
- mg_sha256_update(&c->sha256, msg, len);
- }
- static void jwt_hash_finish(const MG_UECC_HashContext *ctx, uint8_t *hash) {
- jwt_hash_ctx *c = (jwt_hash_ctx *) ctx;
- mg_sha256_final(hash, &c->sha256);
- }
- size_t mg_jwt_sign_es256(const struct mg_jwt_opts *opts, char *buf,
- size_t len) {
- uint8_t hash[32], sig[64], tmp[2 * 32 + 64];
- jwt_hash_ctx ctx = {
- {jwt_hash_init, jwt_hash_update, jwt_hash_finish, 64, 32, tmp},
- {{0}, 0, 0, {0}}};
- size_t n = jwt_header_payload("ES256", opts, buf, len);
- size_t m;
- if (n == 0 || n + 1 >= len) return 0;
- mg_sha256(hash, (uint8_t *) buf, n);
- if (mg_uecc_sign_deterministic(opts->private_key, hash, sizeof(hash),
- &ctx.uECC, sig, mg_uecc_secp256r1()) != 1)
- return 0;
- buf[n++] = '.';
- m = mg_base64url_encode(sig, sizeof(sig), buf + n, len - n);
- return m == 0 ? 0 : n + m;
- }
- size_t mg_jwt_verify_es256(struct mg_str jwt, const struct mg_jwt_opts *opts,
- char *buf, size_t len) {
- struct mg_str h, p, s;
- uint8_t sig[65], hash[32];
- size_t n = jwt_split(jwt, &h, &p, &s);
- if (n == 0 || !jwt_alg(h, "ES256")) return 0;
- if (mg_base64url_decode(s.buf, s.len, (char *) sig, sizeof(sig)) != 64)
- return 0;
- mg_sha256(hash, (uint8_t *) jwt.buf, n);
- if (mg_uecc_verify(opts->public_key, hash, sizeof(hash), sig,
- mg_uecc_secp256r1()) != 1)
- return 0;
- return mg_base64url_decode(p.buf, p.len, buf, len);
- }
- #else
- size_t mg_jwt_sign_es256(const struct mg_jwt_opts *opts, char *buf,
- size_t len) {
- (void) opts, (void) buf, (void) len;
- MG_ERROR(("JWT ES256 requires built-in TLS"));
- return 0;
- }
- size_t mg_jwt_verify_es256(struct mg_str jwt, const struct mg_jwt_opts *opts,
- char *buf, size_t len) {
- (void) jwt, (void) opts, (void) buf, (void) len;
- return mg_jwt_sign_es256(NULL, NULL, 0);
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/l2.c"
- #endif
- #if MG_ENABLE_TCPIP
- // L2 API
- void mg_l2_init(struct mg_tcpip_if *ifp);
- bool mg_l2_poll(struct mg_tcpip_if *ifp, bool expired_1000ms);
- uint8_t *mg_l2_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
- uint8_t *src, uint8_t *dst, uint8_t *frame);
- size_t mg_l2_trailer(struct mg_tcpip_if *ifp, size_t len, uint8_t *cur);
- bool mg_l2_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
- struct mg_str *pay, struct mg_str *raw);
- uint8_t *mg_l2_getaddr(struct mg_tcpip_if *ifp, uint8_t *frame);
- uint8_t *mg_l2_mapip(enum mg_l2type type, enum mg_l2addrtype addrtype,
- struct mg_addr *ip);
- #if MG_ENABLE_IPV6
- bool mg_l2_genip6(enum mg_l2type type, uint64_t *ip6, uint8_t prefix_len,
- uint8_t *addr);
- bool mg_l2_ip6get(enum mg_l2type type, uint8_t *addr, uint8_t *opts,
- uint8_t len);
- uint8_t mg_l2_ip6put(enum mg_l2type type, uint8_t *addr, uint8_t *opts);
- #endif
- size_t mg_l2_driver_output(struct mg_tcpip_if *ifp, size_t len);
- // clang-format off
- extern void mg_l2_eth_init(struct mg_tcpip_if *);
- extern bool mg_l2_eth_poll(struct mg_tcpip_if *, bool);
- extern uint8_t *mg_l2_eth_header(struct mg_tcpip_if *, enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *);
- extern size_t mg_l2_eth_trailer(struct mg_tcpip_if *, size_t, uint8_t *);
- extern bool mg_l2_eth_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
- extern struct mg_l2addr *mg_l2_eth_getaddr(struct mg_tcpip_if *, uint8_t *);
- extern struct mg_l2addr *mg_l2_eth_mapip(enum mg_l2addrtype, struct mg_addr *);
- #if MG_ENABLE_IPV6
- extern bool mg_l2_eth_genip6(uint64_t *, uint8_t, struct mg_l2addr *);
- extern bool mg_l2_eth_ip6get(struct mg_l2addr *, uint8_t *, uint8_t);
- extern uint8_t mg_l2_eth_ip6put(struct mg_l2addr *, uint8_t *);
- #endif
- extern void mg_l2_ppp_init(struct mg_tcpip_if *);
- extern bool mg_l2_ppp_poll(struct mg_tcpip_if *, bool);
- extern uint8_t *mg_l2_ppp_header(struct mg_tcpip_if *, enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *);
- extern size_t mg_l2_ppp_trailer(struct mg_tcpip_if *, size_t, uint8_t *);
- extern bool mg_l2_ppp_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
- extern struct mg_l2addr *mg_l2_ppp_getaddr(struct mg_tcpip_if *, uint8_t *);
- extern struct mg_l2addr *mg_l2_ppp_mapip(enum mg_l2addrtype, struct mg_addr *);
- #if MG_ENABLE_IPV6
- extern bool mg_l2_ppp_genip6(uint64_t *, uint8_t, struct mg_l2addr *);
- extern bool mg_l2_ppp_ip6get(struct mg_l2addr *, uint8_t *, uint8_t);
- extern uint8_t mg_l2_ppp_ip6put(struct mg_l2addr *, uint8_t *);
- #endif
- extern void mg_l2_pppoe_init(struct mg_tcpip_if *);
- extern bool mg_l2_pppoe_poll(struct mg_tcpip_if *, bool);
- extern uint8_t *mg_l2_pppoe_header(struct mg_tcpip_if *, enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *);
- extern size_t mg_l2_pppoe_trailer(struct mg_tcpip_if *, size_t, uint8_t *);
- extern bool mg_l2_pppoe_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
- typedef void (*l2_init_fn)(struct mg_tcpip_if *);
- typedef bool (*l2_poll_fn)(struct mg_tcpip_if *, bool);
- typedef uint8_t *((*l2_header_fn)(struct mg_tcpip_if *, enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *));
- typedef size_t (*l2_trailer_fn)(struct mg_tcpip_if *, size_t, uint8_t *);
- typedef bool (*l2_rx_fn)(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
- typedef struct mg_l2addr (*(*l2_getaddr_fn)(struct mg_tcpip_if *, uint8_t *));
- typedef struct mg_l2addr (*(*l2_mapip_fn)(enum mg_l2addrtype, struct mg_addr *));
- #if MG_ENABLE_IPV6
- typedef bool (*l2_genip6_fn)(uint64_t *, uint8_t, struct mg_l2addr *);
- typedef bool (*l2_ip6get_fn)(struct mg_l2addr *, uint8_t *, uint8_t);
- typedef uint8_t (*l2_ip6put_fn)(struct mg_l2addr *, uint8_t *);
- #endif
- // clang-format on
- static const l2_init_fn l2_init[] = {mg_l2_eth_init, mg_l2_ppp_init,
- mg_l2_pppoe_init};
- static const l2_poll_fn l2_poll[] = {mg_l2_eth_poll, mg_l2_ppp_poll,
- mg_l2_pppoe_poll};
- static const l2_header_fn l2_header[] = {mg_l2_eth_header, mg_l2_ppp_header,
- mg_l2_pppoe_header};
- static const l2_trailer_fn l2_trailer[] = {mg_l2_eth_trailer, mg_l2_ppp_trailer,
- mg_l2_pppoe_trailer};
- static const l2_rx_fn l2_rx[] = {mg_l2_eth_rx, mg_l2_ppp_rx, mg_l2_pppoe_rx};
- static const l2_getaddr_fn l2_getaddr[] = {mg_l2_eth_getaddr, mg_l2_ppp_getaddr,
- mg_l2_ppp_getaddr};
- static const l2_mapip_fn l2_mapip[] = {mg_l2_eth_mapip, mg_l2_ppp_mapip,
- mg_l2_ppp_mapip};
- #if MG_ENABLE_IPV6
- static const l2_genip6_fn l2_genip6[] = {mg_l2_eth_genip6, mg_l2_ppp_genip6,
- mg_l2_ppp_genip6};
- static const l2_ip6get_fn l2_ip6get[] = {mg_l2_eth_ip6get, mg_l2_ppp_ip6get,
- mg_l2_ppp_ip6get};
- static const l2_ip6put_fn l2_ip6put[] = {mg_l2_eth_ip6put, mg_l2_ppp_ip6put,
- mg_l2_ppp_ip6put};
- #endif
- void mg_l2_init(struct mg_tcpip_if *ifp) {
- l2_init[ifp->l2type](ifp);
- }
- bool mg_l2_poll(struct mg_tcpip_if *ifp, bool expired_1000ms) {
- return l2_poll[ifp->l2type](ifp, expired_1000ms);
- }
- uint8_t *mg_l2_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
- uint8_t *src, uint8_t *dst, uint8_t *frame) {
- return l2_header[ifp->l2type](ifp, proto, (struct mg_l2addr *) src,
- (struct mg_l2addr *) dst, frame);
- }
- size_t mg_l2_trailer(struct mg_tcpip_if *ifp, size_t len, uint8_t *frame) {
- return l2_trailer[ifp->l2type](ifp, len, frame);
- }
- bool mg_l2_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
- struct mg_str *pay, struct mg_str *raw) {
- return l2_rx[ifp->l2type](ifp, proto, pay, raw);
- }
- uint8_t *mg_l2_getaddr(struct mg_tcpip_if *ifp, uint8_t *frame) {
- return (uint8_t *) l2_getaddr[ifp->l2type](ifp, frame);
- }
- struct mg_l2addr s_mapip;
- uint8_t *mg_l2_mapip(enum mg_l2type type, enum mg_l2addrtype addrtype,
- struct mg_addr *ip) {
- return (uint8_t *) l2_mapip[type](addrtype, ip);
- }
- #if MG_ENABLE_IPV6
- bool mg_l2_genip6(enum mg_l2type type, uint64_t *ip6, uint8_t prefix_len,
- uint8_t *addr) {
- return l2_genip6[type](ip6, prefix_len, (struct mg_l2addr *) addr);
- }
- bool mg_l2_ip6get(enum mg_l2type type, uint8_t *addr, uint8_t *opts,
- uint8_t len) {
- return l2_ip6get[type]((struct mg_l2addr *) addr, opts, len);
- }
- uint8_t mg_l2_ip6put(enum mg_l2type type, uint8_t *addr, uint8_t *opts) {
- return l2_ip6put[type]((struct mg_l2addr *) addr, opts);
- }
- #endif
- size_t mg_l2_driver_output(struct mg_tcpip_if *ifp, size_t len) {
- size_t n = ifp->driver->tx(ifp->tx.buf, len, ifp);
- if (n == len) ifp->nsent++;
- return n;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/l2_eth.c"
- #endif
- #if MG_ENABLE_TCPIP
- #if defined(__DCC__)
- #pragma pack(1)
- #else
- #pragma pack(push, 1)
- #endif
- struct eth {
- uint8_t dst[6]; // Destination MAC address
- uint8_t src[6]; // Source MAC address
- uint16_t type; // Ethernet type
- };
- struct qtag {
- uint16_t tpid; // Ethernet 802.1Q type
- uint16_t tci; // PCP, DEI, VLAN id
- #define VLAN_ID(x) ((uint16_t) ((x) &0x0FFF))
- };
- #if defined(__DCC__)
- #pragma pack(0)
- #else
- #pragma pack(pop)
- #endif
- static const uint16_t eth_types[] = {
- // order is vital, see l2.h
- 0x800, // IPv4
- 0x86dd, // IPv6
- 0x806, // ARP
- 0x8863, // PPPoE Discovery Stage
- 0x8864 // PPPoE Session Stage
- };
- void mg_l2_eth_init(struct mg_tcpip_if *ifp) {
- struct mg_l2addr *l2addr = (struct mg_l2addr *) ifp->mac;
- // If MAC is not set, make a random one
- if (l2addr->addr.mac[0] == 0 && l2addr->addr.mac[1] == 0 &&
- l2addr->addr.mac[2] == 0 && l2addr->addr.mac[3] == 0 &&
- l2addr->addr.mac[4] == 0 && l2addr->addr.mac[5] == 0) {
- l2addr->addr.mac[0] = 0x02; // Locally administered, unicast
- mg_random(&l2addr->addr.mac[1], sizeof(l2addr->addr.mac) - 1);
- MG_INFO(
- ("MAC not set. Generated random: %M", mg_print_mac, l2addr->addr.mac));
- }
- ifp->l2mtu = 1500;
- ifp->framesize = 1540;
- }
- bool mg_l2_eth_poll(struct mg_tcpip_if *ifp, bool expired_1000ms) {
- (void) ifp;
- (void) expired_1000ms;
- return true;
- }
- uint8_t *mg_l2_eth_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
- struct mg_l2addr *src, struct mg_l2addr *dst,
- uint8_t *frame) {
- struct eth_data *d = &ifp->l2data.eth;
- struct eth *eth = (struct eth *) frame;
- uint8_t *hlp;
- hlp = (uint8_t *) (eth + 1);
- memcpy(eth->src, src->addr.mac, sizeof(eth->dst));
- memcpy(eth->dst, dst->addr.mac, sizeof(eth->dst));
- if (d->vlan_id == 0) { // Traditional plain frame
- eth->type = mg_htons(eth_types[(unsigned int) proto]);
- } else { // Add 802.1Q tag
- struct qtag *qtag = (struct qtag *) ð->type;
- qtag->tpid = mg_htons(0x8100);
- qtag->tci = mg_htons(d->vlan_id); // PCP = default (best-effort)
- hlp += sizeof(*qtag);
- MG_STORE_BE16(qtag + 1, eth_types[(unsigned int) proto]);
- }
- return hlp;
- }
- size_t mg_l2_eth_trailer(struct mg_tcpip_if *ifp, size_t len, uint8_t *cur) {
- struct eth_data *d = &ifp->l2data.eth;
- // there is no len field in Ethernet, CRC is hw-calculated; pad to 64 - CRC
- size_t ethlen =
- len + sizeof(struct eth) + (d->vlan_id != 0 ? sizeof(struct qtag) : 0);
- (void) cur;
- return ethlen >= 60 ? ethlen : 60;
- }
- struct mg_l2addr *mg_l2_eth_mapip(enum mg_l2addrtype addrtype,
- struct mg_addr *addr);
- // Read an unaligned little-endian value from byte pointer p into a native integer.
- // Safe on architectures that forbid unaligned access (e.g. Cortex-M0).
- #define MG_LOAD_LE32(p) \
- ((uint32_t) (((uint32_t) MG_U8P(p)[3] << 24U) | \
- ((uint32_t) MG_U8P(p)[2] << 16U) | \
- ((uint32_t) MG_U8P(p)[1] << 8U) | MG_U8P(p)[0]))
- bool mg_l2_eth_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
- struct mg_str *pay, struct mg_str *raw) {
- struct eth *eth = (struct eth *) raw->buf;
- struct eth_data *d = &ifp->l2data.eth;
- uint16_t type, len;
- unsigned int i;
- size_t hdrlen =
- sizeof(struct eth) + (d->vlan_id != 0 ? sizeof(struct qtag) : 0);
- if (raw->len < hdrlen) return false; // Truncated - runt?
- len = (uint16_t) raw->len;
- if (d->vlan_id == 0) { // We don't handle VLANs
- type = mg_ntohs(eth->type);
- } else { // We do, check 802.1Q tag
- struct qtag *qtag = (struct qtag *) ð->type;
- if (qtag->tpid != mg_htons(0x8100)) return false; // Untagged frame
- if (VLAN_ID(mg_ntohs(qtag->tci)) != VLAN_ID(d->vlan_id))
- return false; // Not our VLAN
- type = MG_LOAD_BE16(qtag + 1);
- }
- if (ifp->enable_mac_check &&
- memcmp(eth->dst, ifp->mac, sizeof(eth->dst)) != 0 &&
- memcmp(eth->dst, mg_l2_eth_mapip(MG_TCPIP_L2ADDR_BCAST, NULL),
- sizeof(eth->dst)) != 0)
- return false; // TODO(): add multicast addresses
- if (ifp->enable_fcs_check && len > hdrlen + 4) {
- uint32_t crc, crc_rx;
- len -= 4;
- crc = mg_crc32(0, (const char *) raw->buf, len);
- crc_rx = MG_LOAD_LE32(raw->buf + len);
- if (crc_rx != crc)
- return false;
- }
- pay->buf = ((char *) eth) + hdrlen;
- pay->len = len - hdrlen;
- for (i = 0; i < sizeof(eth_types) / sizeof(uint16_t); i++) {
- if (type == eth_types[i]) break;
- }
- if (i == sizeof(eth_types) / sizeof(eth_types[0])) {
- MG_DEBUG(("Unknown eth type %x", type));
- if (mg_log_level >= MG_LL_VERBOSE)
- mg_hexdump(raw->buf, raw->len >= 32 ? 32 : raw->len);
- return false;
- }
- *proto = (enum mg_l2proto) i;
- return true;
- }
- struct mg_l2addr *mg_l2_eth_getaddr(struct mg_tcpip_if *ifp, uint8_t *frame) {
- struct eth *eth = (struct eth *) frame;
- (void) ifp; // address field is before a possible 802.1Q tag
- return (struct mg_l2addr *) ð->src;
- }
- extern struct mg_l2addr s_mapip;
- struct mg_l2addr *mg_l2_eth_mapip(enum mg_l2addrtype addrtype,
- struct mg_addr *addr) {
- switch (addrtype) {
- case MG_TCPIP_L2ADDR_BCAST:
- memset(s_mapip.addr.mac, 0xff, sizeof(s_mapip.addr.mac));
- break;
- case MG_TCPIP_L2ADDR_MCAST: {
- uint8_t *ip = (uint8_t *) &addr->addr.ip4;
- // IP multicast group MAC, RFC-1112 6.4
- s_mapip.addr.mac[0] = 0x01, s_mapip.addr.mac[1] = 0x00,
- s_mapip.addr.mac[2] = 0x5E;
- s_mapip.addr.mac[3] = ip[1] & 0x7F; // 23 LSb
- s_mapip.addr.mac[4] = ip[2];
- s_mapip.addr.mac[5] = ip[3];
- break;
- }
- case MG_TCPIP_L2ADDR_MCAST6: {
- // IPv6 multicast address mapping, RFC-2464 7
- uint8_t *ip = (uint8_t *) &addr->addr.ip6;
- s_mapip.addr.mac[0] = 0x33, s_mapip.addr.mac[1] = 0x33;
- s_mapip.addr.mac[2] = ip[12], s_mapip.addr.mac[3] = ip[13],
- s_mapip.addr.mac[4] = ip[14], s_mapip.addr.mac[5] = ip[15];
- break;
- }
- }
- return &s_mapip;
- }
- #if MG_ENABLE_IPV6
- static void meui64(uint8_t *addr, uint8_t *mac) {
- *addr++ = *mac++ ^ (uint8_t) 0x02, *addr++ = *mac++, *addr++ = *mac++;
- *addr++ = 0xff, *addr++ = 0xfe;
- *addr++ = *mac++, *addr++ = *mac++, *addr = *mac;
- }
- bool mg_l2_eth_genip6(uint64_t *ip6, uint8_t prefix_len,
- struct mg_l2addr *l2addr) {
- if (prefix_len > 64) {
- MG_ERROR(("Prefix length > 64, UNSUPPORTED"));
- return false;
- }
- ip6[0] = 0;
- meui64(((uint8_t *) &ip6[1]), l2addr->addr.mac); // RFC-4291 2.5.4, 2.5.1
- return true;
- }
- bool mg_l2_eth_ip6get(struct mg_l2addr *l2addr, uint8_t *opts, uint8_t len) {
- if (len != 1) return false;
- memcpy(l2addr->addr.mac, opts, 6);
- return true;
- }
- uint8_t mg_l2_eth_ip6put(struct mg_l2addr *l2addr, uint8_t *opts) {
- memcpy(opts, l2addr->addr.mac, 6);
- return 1;
- }
- #endif
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/l2_ppp.c"
- #endif
- #if MG_ENABLE_TCPIP
- #if defined(__DCC__)
- #pragma pack(1)
- #else
- #pragma pack(push, 1)
- #endif
- // all in network order
- struct ppp { // RFC-1661
- uint16_t proto;
- };
- struct lcp { // RFC-1661
- uint8_t code, id;
- uint16_t len;
- };
- struct ipcp { // RFC-1332
- uint8_t code, id;
- uint16_t len;
- };
- struct ipv6cp { // RFC-5072
- uint8_t code, id;
- uint16_t len;
- };
- struct hdlc_ { // RFC-1662, "PPP in HDLC-like Framing"
- uint8_t addr, ctrl;
- };
- struct pppoe { // RFC-2516, "A Method for Transmitting PPP Over Ethernet
- // (PPPoE)"
- uint8_t vertype, code;
- uint16_t id, len;
- };
- #if defined(__DCC__)
- #pragma pack(0)
- #else
- #pragma pack(pop)
- #endif
- #define MG_PPP_ADDR 0xff
- #define MG_PPP_CTRL 0x03
- #define MG_PPP_PROTO_IP 0x0021
- #define MG_PPP_PROTO_IPV6 0x0057
- #define MG_PPP_PROTO_LCP 0xc021
- #define MG_PPP_PROTO_IPCP 0x8021
- #define MG_PPP_PROTO_IPV6CP 0x8057
- #define MG_PPP_PROTO_PAP 0xc023
- #define MG_PPP_PROTO_CHAP 0xc223
- #define MG_PPP_LCP_CFG_REQ 1
- #define MG_PPP_LCP_CFG_ACK 2
- #define MG_PPP_LCP_CFG_NACK 3
- #define MG_PPP_LCP_CFG_REJECT 4
- #define MG_PPP_LCP_CFG_TERM_REQ 5
- #define MG_PPP_LCP_CFG_TERM_ACK 6
- #define MG_PPP_LCP_REJECT 8
- #define MG_PPP_LCP_ECHO_REQ 9
- #define MG_PPP_LCP_ECHO_REPLY 10
- #define MG_PPP_IPCP_CFG_REQ 1
- #define MG_PPP_IPCP_CFG_ACK 2
- #define MG_PPP_IPCP_CFG_NACK 3
- #define MG_PPP_IPCP_CFG_REJECT 4
- #define MG_PPP_IPCP_OPT_IPADDR 3
- #define MG_PPP_IPV6CP_CFG_REQ 1
- #define MG_PPP_IPV6CP_CFG_ACK 2
- #define MG_PPP_IPV6CP_CFG_NACK 3
- #define MG_PPP_IPV6CP_CFG_REJECT 4
- #define MG_PPP_IPV6CP_OPT_IFCID 1
- #define MG_PPPoE_PADI 0x09
- #define MG_PPPoE_PADO 0x07
- #define MG_PPPoE_PADR 0x19
- #define MG_PPPoE_PADS 0x65
- #define MG_PPPoE_PADT 0xa7
- #define MG_PPPoE_ST_DISC 0 // Discovery phase, see what servers are out there
- #define MG_PPPoE_ST_REQ 1 // Chose a server, request a session and wait
- #define MG_PPPoE_ST_SESS 2 // Session established, PPP traffic is exchanged
- #define PDIFF(a, b) ((size_t) (((char *) (b)) - ((char *) (a))))
- static bool s_lcpup = false; // ************ THESE SHOULD MOVE TO A struct
- // mg_l2data *******************************
- static uint8_t s_state = MG_PPPoE_ST_DISC;
- static uint16_t s_id;
- void mg_l2_ppp_init(struct mg_tcpip_if *ifp) {
- ifp->l2mtu = 1500;
- ifp->framesize = 1500 + sizeof(struct ppp) + sizeof(struct hdlc_);
- }
- extern void mg_l2_eth_init(struct mg_tcpip_if *);
- void mg_l2_pppoe_init(struct mg_tcpip_if *ifp) {
- mg_l2_eth_init(ifp);
- ifp->l2mtu = ifp->l2mtu - (uint16_t) (sizeof(struct pppoe) +
- sizeof(struct ppp)); // 1500 --> 1492
- }
- bool mg_l2_ppp_poll(struct mg_tcpip_if *ifp, bool expired_1000ms) {
- if (expired_1000ms && ifp->state == MG_TCPIP_STATE_DOWN) s_lcpup = false;
- return s_lcpup;
- }
- static uint8_t *hdlc_header(uint8_t *p) {
- struct hdlc_ *hdlc = (struct hdlc_ *) p;
- hdlc->addr = MG_PPP_ADDR;
- hdlc->ctrl = MG_PPP_CTRL;
- return (uint8_t *) (hdlc + 1);
- }
- static uint8_t *ppp_header(uint16_t proto, uint8_t *p) {
- struct ppp *ppp = (struct ppp *) p;
- ppp->proto = mg_htons(proto);
- return (uint8_t *) (ppp + 1);
- }
- static uint8_t *l2_ppp_header(enum mg_l2proto proto, uint8_t *p) {
- uint16_t ppp_proto = proto == MG_TCPIP_L2PROTO_IPV4 ? MG_PPP_PROTO_IP
- : proto == MG_TCPIP_L2PROTO_IPV4 ? MG_PPP_PROTO_IPV6
- : 0;
- return ppp_header(ppp_proto, p);
- }
- uint8_t *mg_l2_ppp_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
- struct mg_l2addr *src, struct mg_l2addr *dst,
- uint8_t *frame) {
- (void) ifp;
- (void) src;
- (void) dst;
- return l2_ppp_header(proto, hdlc_header(frame));
- }
- extern uint8_t *mg_l2_eth_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
- struct mg_l2addr *src, struct mg_l2addr *dst,
- uint8_t *frame);
- static uint8_t *pppoe_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
- uint8_t code, uint16_t id, struct mg_l2addr *src,
- struct mg_l2addr *dst, uint8_t *frame) {
- struct pppoe *pppoe =
- (struct pppoe *) mg_l2_eth_header(ifp, proto, src, dst, frame);
- pppoe->vertype = 0x11;
- pppoe->code = code;
- pppoe->id = id;
- return (uint8_t *) (pppoe + 1);
- }
- uint8_t *mg_l2_pppoe_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
- struct mg_l2addr *src, struct mg_l2addr *dst,
- uint8_t *frame) {
- (void) dst;
- return l2_ppp_header(proto, pppoe_header(ifp, MG_TCPIP_L2PROTO_PPPoE_SESS, 0,
- s_id, src, dst, frame));
- }
- size_t mg_l2_ppp_trailer(struct mg_tcpip_if *ifp, size_t len, uint8_t *cur) {
- uint16_t crc;
- uint8_t *frame;
- len += sizeof(struct ppp) + sizeof(struct hdlc_);
- frame = cur - len;
- crc = mg_crc16(0, (const char *) frame, len);
- *cur++ = (uint8_t) crc; // add CRC, note the byte order
- *cur++ = (uint8_t) (crc >> 8);
- // there is no len field in PPP
- (void) ifp;
- return len + 2;
- }
- extern size_t mg_l2_eth_trailer(struct mg_tcpip_if *, size_t, uint8_t *);
- static size_t pppoe_trailer(struct mg_tcpip_if *ifp, size_t len, uint8_t *cur) {
- struct pppoe *pppoe = (struct pppoe *) (cur - len - sizeof(struct pppoe));
- pppoe->len = mg_htons((uint16_t) len);
- return mg_l2_eth_trailer(ifp, PDIFF(pppoe, cur), cur);
- }
- size_t mg_l2_pppoe_trailer(struct mg_tcpip_if *ifp, size_t len, uint8_t *cur) {
- return pppoe_trailer(ifp, len + sizeof(struct ppp), cur);
- }
- size_t mg_l2_driver_output(struct mg_tcpip_if *ifp, size_t len);
- static uint8_t *ppp_tx_frame_header(struct mg_tcpip_if *ifp, uint16_t proto) {
- uint8_t *l2p = (uint8_t *) ifp->tx.buf;
- if (ifp->l2type == MG_TCPIP_L2_PPP)
- return ppp_header(proto, hdlc_header(l2p));
- return ppp_header(proto, pppoe_header(ifp, MG_TCPIP_L2PROTO_PPPoE_SESS, 0,
- s_id, (struct mg_l2addr *) ifp->mac,
- (struct mg_l2addr *) ifp->gwmac, l2p));
- }
- static size_t ppp_tx_frame_trailer(struct mg_tcpip_if *ifp, size_t len,
- uint8_t *cur) {
- return mg_l2_driver_output(ifp, ifp->l2type == MG_TCPIP_L2_PPPoE
- ? mg_l2_pppoe_trailer(ifp, len, cur)
- : mg_l2_ppp_trailer(ifp, len, cur));
- }
- // Transmit a single PPP frame for the given protocol
- static size_t ppp_tx_frame(struct mg_tcpip_if *ifp, uint16_t proto,
- uint8_t *data, size_t datasz) {
- uint8_t *pay = ppp_tx_frame_header(ifp, proto);
- memcpy(pay, data, datasz);
- return ppp_tx_frame_trailer(ifp, datasz, pay + datasz);
- }
- static void ppp_handle_lcp(struct mg_tcpip_if *ifp, uint8_t *lcpp,
- size_t lcpsz) {
- uint8_t id;
- uint16_t len;
- struct lcp *lcp = (struct lcp *) lcpp;
- if (lcpsz < sizeof(*lcp)) return;
- id = lcp->id;
- len = mg_ntohs(lcp->len);
- if (len < sizeof(*lcp) || len > lcpsz) return;
- switch (lcp->code) {
- case MG_PPP_LCP_CFG_REQ: {
- if (len == sizeof(*lcp)) {
- MG_DEBUG(("LCP config request of %d bytes, acknowledging...", len));
- lcp->code = MG_PPP_LCP_CFG_ACK;
- ppp_tx_frame(ifp, MG_PPP_PROTO_LCP, lcpp, len);
- lcp->code = MG_PPP_LCP_CFG_REQ;
- ppp_tx_frame(ifp, MG_PPP_PROTO_LCP, lcpp, len);
- } else {
- MG_DEBUG(("LCP config request of %d bytes, rejecting...", len));
- lcp->code = MG_PPP_LCP_CFG_REJECT;
- ppp_tx_frame(ifp, MG_PPP_PROTO_LCP, lcpp, len);
- }
- } break;
- case MG_PPP_LCP_CFG_ACK:
- s_lcpup = true;
- break;
- case MG_PPP_LCP_CFG_TERM_REQ: {
- uint8_t ack[4] = {MG_PPP_LCP_CFG_TERM_ACK, id, 0, 4};
- MG_DEBUG(("LCP termination request, acknowledging..."));
- ppp_tx_frame(ifp, MG_PPP_PROTO_LCP, ack, sizeof(ack));
- s_lcpup = false;
- } break;
- case MG_PPP_LCP_ECHO_REQ: // RFC-1661 5.8: must respond
- MG_DEBUG(("LCP echo request of %d bytes, replying...", len));
- lcp->code = MG_PPP_LCP_ECHO_REPLY;
- ppp_tx_frame(ifp, MG_PPP_PROTO_LCP, lcpp, len);
- break;
- }
- }
- static bool find_opt(const uint8_t opt, const uint8_t optlen,
- const uint8_t *opts, size_t optslen, uint8_t *dest) {
- uint8_t *p = (uint8_t *) opts;
- while (optslen >= 2) { // parse options for requested one
- if (p[1] > optslen || p[1] < 2) return false; // truncated / malformed
- if (p[0] == opt && p[1] == optlen) {
- memcpy(dest, p + 2, optlen - 2);
- return true;
- }
- optslen -= p[1];
- p += p[1];
- }
- return false;
- }
- static void ppp_handle_ipcp(struct mg_tcpip_if *ifp, uint8_t *ipcpp,
- size_t ipcpsz) {
- uint16_t len;
- uint8_t id;
- struct ipcp *ipcp = (struct ipcp *) ipcpp;
- uint8_t req[] = {
- MG_PPP_IPCP_CFG_REQ, 0, 0, 10, MG_PPP_IPCP_OPT_IPADDR, 6, 0, 0, 0, 0};
- if (ipcpsz < sizeof(*ipcp)) return;
- id = ipcp->id;
- len = mg_ntohs(ipcp->len);
- if (len < sizeof(*ipcp) || len > ipcpsz) return;
- switch (ipcp->code) {
- case MG_PPP_IPCP_CFG_REQ:
- MG_VERBOSE(("got IPCP config request, acknowledging..."));
- if (len >= 10 &&
- find_opt(MG_PPP_IPCP_OPT_IPADDR, 6, (const uint8_t *) (ipcp + 1),
- len - sizeof(*ipcp), (uint8_t *) &ifp->gw)) {
- MG_DEBUG(("IPCP cfg, GW IP: %M", mg_print_ip4, &ifp->gw));
- ipcp->code = MG_PPP_IPCP_CFG_ACK;
- } else if (ifp->gw == 0) {
- MG_ERROR(("Peer did not provide its IP address"));
- // NOTE: We should NACK with an added option, probably we can just store
- // the incoming address and offer a statically configured ifp->gw when
- // theirs == 0, but it is unlikely to find such a dumb PPP server and it
- // will complicate our config and this protocol state machine
- ipcp->code = MG_PPP_IPCP_CFG_REJECT;
- ppp_tx_frame(ifp, MG_PPP_PROTO_IPCP, ipcpp, len);
- }
- ppp_tx_frame(ifp, MG_PPP_PROTO_IPCP, ipcpp, len);
- req[1] = id;
- memcpy(req + 6, &ifp->ip, 4); // Request config IP address or 0.0.0.0
- ppp_tx_frame(ifp, MG_PPP_PROTO_IPCP, req, sizeof(req));
- break;
- case MG_PPP_IPCP_CFG_ACK:
- // Our peer accepted our IP address
- MG_VERBOSE(("got IPCP config ack"));
- ifp->mask = 0xffffffff; // send to gw
- ifp->state = MG_TCPIP_STATE_IP;
- ifp->gw_ready = true;
- break;
- case MG_PPP_IPCP_CFG_NACK:
- MG_VERBOSE(("got IPCP config nack"));
- // NACK contains our "suggested" IP address, use it
- if (len >= 10 &&
- find_opt(MG_PPP_IPCP_OPT_IPADDR, 6, (const uint8_t *) (ipcp + 1),
- len - sizeof(*ipcp), (uint8_t *) &ifp->ip)) {
- MG_DEBUG(("IPCP cfg, IP: %M", mg_print_ip4, &ifp->ip));
- ipcp->code = MG_PPP_IPCP_CFG_REQ;
- ppp_tx_frame(ifp, MG_PPP_PROTO_IPCP, ipcpp, len);
- }
- break;
- case MG_PPP_IPCP_CFG_REJECT:
- MG_ERROR(("Peer rejected our IP address, need to properly set ifp->ip"));
- break;
- }
- }
- #if MG_ENABLE_IPV6
- static void ppp_handle_ipv6cp(struct mg_tcpip_if *ifp, uint8_t *ipv6cpp,
- size_t ipv6cpsz) {
- uint16_t len;
- uint8_t id;
- struct ipv6cp *ipv6cp = (struct ipv6cp *) ipv6cpp;
- uint8_t req[14];
- memset(req, 0, sizeof(req));
- req[0] = MG_PPP_IPV6CP_CFG_REQ, req[3] = 14, req[4] = MG_PPP_IPV6CP_OPT_IFCID,
- req[5] = 10;
- if (ipv6cpsz < sizeof(*ipv6cp)) return;
- id = ipv6cp->id;
- len = mg_ntohs(ipv6cp->len);
- if (len < sizeof(*ipv6cp) || len > ipv6cpsz) return;
- switch (ipv6cp->code) {
- case MG_PPP_IPV6CP_CFG_REQ:
- MG_VERBOSE(("got IPV6CP config request..."));
- if (len >= 10 &&
- find_opt(
- MG_PPP_IPV6CP_OPT_IFCID, 10, (const uint8_t *) (ipv6cp + 1),
- len - sizeof(*ipv6cp),
- (uint8_t *) &((struct mg_l2addr *) (ifp->gw6mac))->addr.ieee64)) {
- if (((struct mg_l2addr *) (ifp->gw6mac))->addr.ieee64 != 0) {
- MG_DEBUG(("IPV6CP cfg, GW IFCID: %M", mg_print_ieee64,
- &((struct mg_l2addr *) (ifp->gw6mac))->addr.ieee64));
- ipv6cp->code = MG_PPP_IPV6CP_CFG_ACK;
- ppp_tx_frame(ifp, MG_PPP_PROTO_IPV6CP, ipv6cpp, len);
- req[1] = id;
- ifp->ip6ll[0] = 0, ifp->ip6ll[1] = 0; // clear any former ll address
- memset(req + 6, 0, 8); // Inform ifc id 0
- ppp_tx_frame(ifp, MG_PPP_PROTO_IPV6CP, req, sizeof(req));
- } else {
- MG_ERROR(("Peer is not able to provide its interface id"));
- ipv6cp->code = MG_PPP_IPV6CP_CFG_REJECT;
- ppp_tx_frame(ifp, MG_PPP_PROTO_IPV6CP, ipv6cpp, len);
- }
- } else {
- MG_ERROR(("Peer did not provide its interface id"));
- // We should NACK with an added option, but we can't provide one for
- // them
- ipv6cp->code = MG_PPP_IPV6CP_CFG_REJECT;
- ppp_tx_frame(ifp, MG_PPP_PROTO_IPV6CP, ipv6cpp, len);
- }
- break;
- case MG_PPP_IPV6CP_CFG_ACK:
- // Our peer accepted our ifc id
- MG_VERBOSE(("got IPV6CP config ack"));
- break;
- case MG_PPP_IPV6CP_CFG_NACK: {
- struct mg_l2addr l2;
- MG_VERBOSE(("got IPV6CP config nack"));
- // NACK contains our "suggested" IFC id, use it
- if (len >= 10 &&
- find_opt(MG_PPP_IPV6CP_OPT_IFCID, 10, (const uint8_t *) (ipv6cp + 1),
- len - sizeof(*ipv6cp), (uint8_t *) &l2.addr.ieee64)) {
- MG_DEBUG(("IPV6CP cfg, IFCID: %M", mg_print_ieee64, &l2.addr.ieee64));
- ifp->ip6ll[1] = l2.addr.ieee64; // RFC-5072 5, signal L3 we're ready
- ipv6cp->code = MG_PPP_IPV6CP_CFG_REQ;
- ppp_tx_frame(ifp, MG_PPP_PROTO_IPV6CP, ipv6cpp, len);
- } else {
- MG_ERROR(("Peer is not able to offer an interface id"));
- }
- } break;
- case MG_PPP_IPV6CP_CFG_REJECT:
- MG_ERROR(("Peer rejected our interface id"));
- break;
- }
- }
- #endif
- static bool ppp_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
- struct mg_str *pay, struct mg_str *raw) {
- struct ppp *ppp = (struct ppp *) pay->buf;
- if (pay->len < sizeof(*ppp)) return false; // Truncated
- pay->buf = (char *) (ppp + 1);
- pay->len = pay->len - sizeof(*ppp);
- switch (mg_ntohs(ppp->proto)) {
- case MG_PPP_PROTO_LCP:
- ppp_handle_lcp(ifp, (uint8_t *) pay->buf, pay->len);
- return false;
- case MG_PPP_PROTO_IPCP:
- if (s_lcpup) ppp_handle_ipcp(ifp, (uint8_t *) pay->buf, pay->len);
- return false;
- case MG_PPP_PROTO_IP:
- if (!s_lcpup) return false;
- MG_VERBOSE(("got IP packet of %d bytes", pay->len));
- *proto = MG_TCPIP_L2PROTO_IPV4;
- break;
- #if MG_ENABLE_IPV6
- case MG_PPP_PROTO_IPV6CP:
- if (s_lcpup) ppp_handle_ipv6cp(ifp, (uint8_t *) pay->buf, pay->len);
- return false;
- case MG_PPP_PROTO_IPV6:
- if (!s_lcpup) return false;
- MG_VERBOSE(("got IPv6 packet of %d bytes", pay->len));
- *proto = MG_TCPIP_L2PROTO_IPV6;
- break;
- #endif
- default: {
- struct lcp rej;
- uint8_t *p;
- size_t msglen;
- MG_DEBUG(("unknown %u-byte PPP frame with proto 0x%04x:",
- pay->len + sizeof(*ppp), mg_ntohs(ppp->proto)));
- if (mg_log_level >= MG_LL_DEBUG)
- mg_hexdump(ppp, pay->len > 14 ? 16 : pay->len + sizeof(*ppp));
- if (!s_lcpup) return false; // RFC-1661 5.7: must reject on link down
- if (pay->len > (size_t) (ifp->mtu - 20))
- pay->len = (size_t) (ifp->mtu - 20); // truncate to some safe limit
- rej.code = MG_PPP_LCP_REJECT;
- mg_random(&rej.id, sizeof(rej.id));
- msglen = pay->len + sizeof(*ppp);
- rej.len = mg_htons((uint16_t) (msglen + sizeof(rej)));
- p = ppp_tx_frame_header(ifp, MG_PPP_PROTO_LCP);
- memmove(p, &rej, sizeof(rej)); // LCP reject
- memmove(p + sizeof(rej), ppp, msglen); // rejected PPP message
- ppp_tx_frame_trailer(ifp, msglen + sizeof(rej), p + msglen + sizeof(rej));
- }
- return false;
- }
- (void) raw;
- return true;
- }
- bool mg_l2_ppp_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
- struct mg_str *pay, struct mg_str *raw) {
- struct hdlc_ *hdlc = (struct hdlc_ *) raw->buf;
- if (raw->len < sizeof(*hdlc) + 2) return false; // Truncated
- if (hdlc->addr == MG_PPP_ADDR && hdlc->ctrl == MG_PPP_CTRL) {
- pay->buf = (char *) (hdlc + 1);
- pay->len = raw->len - sizeof(*hdlc) - 2;
- } else { // Address-and-Control-Field-Compressed PPP header
- pay->buf = (char *) raw->buf;
- pay->len = raw->len - 2;
- }
- return ppp_rx(ifp, proto, pay, raw);
- }
- extern struct mg_l2addr s_mapip;
- struct mg_l2addr *mg_l2_ppp_getaddr(struct mg_tcpip_if *ifp, uint8_t *frame) {
- (void) ifp;
- (void) frame;
- return &s_mapip; // bogus
- }
- extern struct mg_l2addr *mg_l2_eth_mapip(enum mg_l2addrtype, struct mg_addr *);
- struct mg_l2addr *mg_l2_ppp_mapip(enum mg_l2addrtype addrtype,
- struct mg_addr *addr) {
- return mg_l2_eth_mapip(addrtype, addr);
- }
- #if MG_ENABLE_IPV6
- bool mg_l2_ppp_genip6(uint64_t *ip6, uint8_t prefix_len,
- struct mg_l2addr *l2addr) {
- if (prefix_len > 64) {
- MG_ERROR(("Prefix length > 64, UNSUPPORTED"));
- return false;
- }
- ip6[0] = 0;
- ip6[1] = l2addr->addr.ieee64;
- return false;
- }
- bool mg_l2_ppp_ip6get(struct mg_l2addr *l2addr, uint8_t *opts, uint8_t len) {
- (void) l2addr;
- (void) opts;
- (void) len;
- return true;
- }
- uint8_t mg_l2_ppp_ip6put(struct mg_l2addr *l2addr, uint8_t *opts) {
- (void) l2addr;
- (void) opts;
- return 0;
- }
- #endif
- // Transmit a single PPPoE frame for the discovery phase
- static size_t pppoe_tx_frame(struct mg_tcpip_if *ifp, uint8_t code, uint16_t id,
- uint8_t *data, size_t datasz,
- struct mg_l2addr *dst) {
- uint8_t *l2p = (uint8_t *) ifp->tx.buf;
- uint8_t *p = pppoe_header(ifp, MG_TCPIP_L2PROTO_PPPoE_DISC, code, id,
- (struct mg_l2addr *) ifp->mac, dst, l2p);
- memmove(p, data, datasz);
- return mg_l2_driver_output(ifp, pppoe_trailer(ifp, datasz, p + datasz));
- }
- bool mg_l2_pppoe_poll(struct mg_tcpip_if *ifp, bool expired_1000ms) {
- if (expired_1000ms && s_state == MG_PPPoE_ST_DISC &&
- ifp->state == MG_TCPIP_STATE_LINK_UP) {
- uint16_t tags[2];
- tags[0] = mg_htons(0x0101); // Service Request
- tags[1] = mg_htons(0x0000); // Any
- pppoe_tx_frame(ifp, MG_PPPoE_PADI, 0, (uint8_t *) tags, sizeof(tags),
- mg_l2_eth_mapip(MG_TCPIP_L2ADDR_BCAST, NULL));
- MG_DEBUG(("Sent PADI"));
- } else if (expired_1000ms && (s_state != MG_PPPoE_ST_SESS ||
- ifp->state == MG_TCPIP_STATE_DOWN)) {
- s_state = MG_PPPoE_ST_DISC;
- }
- return mg_l2_ppp_poll(ifp, expired_1000ms);
- }
- extern bool mg_l2_eth_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
- struct mg_str *pay, struct mg_str *raw);
- extern struct mg_l2addr *mg_l2_eth_getaddr(struct mg_tcpip_if *, uint8_t *);
- bool mg_l2_pppoe_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
- struct mg_str *pay, struct mg_str *raw) {
- enum mg_l2proto eth_proto; // raw is handled by eth
- struct pppoe *pppoe;
- if (!mg_l2_eth_rx(ifp, ð_proto, pay, raw)) return false;
- pppoe = (struct pppoe *) pay->buf; // here we handle pay, not raw
- if (pay->len < sizeof(*pppoe)) return false; // Truncated
- if (eth_proto == MG_TCPIP_L2PROTO_PPPoE_DISC) {
- MG_VERBOSE(("PPPoE_DISC"));
- if (s_state == MG_PPPoE_ST_DISC && pppoe->code == MG_PPPoE_PADO &&
- pppoe->id == 0) {
- uint16_t tags[2];
- bool has_cookie = false;
- size_t len = pay->len - sizeof(*pppoe);
- uint8_t *p = (uint8_t *) (pppoe + 1);
- uint16_t taglen;
- while (len >= 4) { // parse tags for a possible AC-Cookie
- uint16_t curtag = MG_LOAD_BE16(p);
- taglen = MG_LOAD_BE16(p + 2);
- if (taglen > len - 4) return false; // truncated / malformed
- if (curtag == 0x0104) {
- has_cookie = true;
- break;
- }
- len -= 4 + taglen;
- p += 4 + taglen;
- }
- tags[0] = mg_htons(0x0101); // Service Request
- tags[1] = mg_htons(0x0000); // Any
- if (has_cookie) { // copy tags to before AC-Cookie tag in rx buffer
- memcpy(p -= sizeof(tags), tags, sizeof(tags)); // point to all tags
- taglen += 4 + sizeof(tags); // account for tags
- } else {
- p = (uint8_t *) tags;
- taglen = sizeof(tags);
- }
- pppoe_tx_frame(ifp, MG_PPPoE_PADR, 0, p, taglen,
- mg_l2_eth_getaddr(ifp, (uint8_t *) raw->buf));
- MG_DEBUG(("Sent PADR"));
- s_state = MG_PPPoE_ST_REQ;
- } else if (s_state == MG_PPPoE_ST_REQ && pppoe->code == MG_PPPoE_PADS) {
- s_id = pppoe->id;
- memcpy(&ifp->gwmac,
- mg_l2_eth_getaddr(ifp, (uint8_t *) raw->buf)->addr.mac, 6);
- MG_DEBUG(("PPPoE session 0x%04x started", mg_ntohs(s_id)));
- s_state = MG_PPPoE_ST_SESS;
- } else if (s_state == MG_PPPoE_ST_SESS && pppoe->code == MG_PPPoE_PADT &&
- pppoe->id == s_id) {
- MG_ERROR(("Got PADT"));
- s_id = 0;
- s_lcpup = false;
- s_state = MG_PPPoE_ST_DISC;
- }
- } else if (eth_proto == MG_TCPIP_L2PROTO_PPPoE_SESS &&
- s_state == MG_PPPoE_ST_SESS) {
- pay->buf = (char *) (pppoe + 1);
- pay->len = pay->len - sizeof(*pppoe);
- return ppp_rx(ifp, proto, pay, raw);
- }
- return false;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/lfs.c"
- #endif
- // Copyright (c) 2023 Cesanta Software Limited
- // SPDX-License-Identifier: GPL-2.0-only or commercial
- #if MG_ENABLE_LFS
- #include <fcntl.h>
- #include <littlefs/lfs.h>
- #include <string.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #ifndef LFS_USE_RAM
- #define LFS_USE_RAM 0 // If 1, use RAM FS. If 0, use flash
- #endif
- #ifndef MG_LFS_BUF_SIZE
- #define MG_LFS_BUF_SIZE 64 // Buffer size used for reads, writes, and cache
- #endif
- #ifndef MG_LFS_FD_BASE
- #define MG_LFS_FD_BASE 12000 // Starting file descriptor number
- #endif
- #if LFS_USE_RAM && !defined(MG_LFS_BLOCK_SIZE)
- #define MG_LFS_BLOCK_SIZE 8192
- #endif
- #ifndef DT_DIR
- #define DT_DIR 4
- #endif
- #ifndef DT_REG
- #define DT_REG 8
- #endif
- typedef struct mg_lfs_fd DIR;
- struct dirent {
- char d_name[LFS_NAME_MAX + 1];
- unsigned char d_type;
- };
- struct mg_lfs_fd {
- struct mg_lfs_fd *next;
- bool isopen;
- lfs_file_t file;
- lfs_dir_t dir;
- int fd;
- };
- static lfs_t s_lfs;
- static struct mg_lfs_fd *s_fds;
- static uint8_t *s_fs;
- static int s_next_fd = MG_LFS_FD_BASE;
- static bool s_lfs_ready;
- #if LFS_USE_RAM
- static uint8_t *s_ram_fs;
- #endif
- static int lfs_driver_read(const struct lfs_config *cfg, lfs_block_t block,
- lfs_off_t off, void *buf, lfs_size_t len);
- static int lfs_driver_prog(const struct lfs_config *cfg, lfs_block_t block,
- lfs_off_t off, const void *buf, lfs_size_t len);
- static int lfs_driver_erase(const struct lfs_config *cfg, lfs_block_t block);
- static int lfs_driver_sync(const struct lfs_config *cfg);
- static struct lfs_config s_cfg = {
- .read = lfs_driver_read,
- .prog = lfs_driver_prog,
- .erase = lfs_driver_erase,
- .sync = lfs_driver_sync,
- .block_cycles = 200,
- .cache_size = MG_LFS_BUF_SIZE,
- .read_size = MG_LFS_BUF_SIZE,
- .prog_size = MG_LFS_BUF_SIZE,
- .lookahead_size = MG_LFS_BUF_SIZE / 8,
- };
- static bool flash_write_buf(void *addr, const void *buf, size_t len) {
- #if LFS_USE_RAM
- memmove(addr, buf, len);
- return true;
- #else
- return mg_flash != NULL && mg_flash->write_fn(addr, buf, len);
- #endif
- }
- static int lfs_driver_read(const struct lfs_config *cfg, lfs_block_t block,
- lfs_off_t off, void *buf, lfs_size_t len) {
- memmove(buf, &s_fs[block * cfg->block_size + off], len);
- return 0;
- }
- static int lfs_driver_prog(const struct lfs_config *cfg, lfs_block_t block,
- lfs_off_t off, const void *buf, lfs_size_t len) {
- uint8_t *dst = &s_fs[block * cfg->block_size + off];
- if (!flash_write_buf(dst, buf, len)) return LFS_ERR_IO;
- return 0;
- }
- static int lfs_driver_erase(const struct lfs_config *cfg, lfs_block_t block) {
- (void) cfg, (void) block;
- return 0;
- }
- static int lfs_driver_sync(const struct lfs_config *cfg) {
- (void) cfg;
- return 0;
- }
- bool mg_lfs_init(size_t size) {
- int result = 0;
- if (s_lfs_ready) return true;
- if (size == 0) return false;
- #if LFS_USE_RAM
- s_cfg.block_size = MG_LFS_BLOCK_SIZE;
- if (s_ram_fs == NULL) s_ram_fs = (uint8_t *) mg_calloc(1, size);
- s_fs = s_ram_fs;
- #else
- if (mg_flash == NULL || mg_flash->secsz == 0 || mg_flash->size < size) return false;
- if (mg_flash->write_fn == NULL) return false;
- s_cfg.block_size = mg_flash->secsz;
- s_fs = (uint8_t *) mg_flash->start + mg_flash->size - size;
- #endif
- if (s_fs == NULL || s_cfg.block_size == 0 || size % s_cfg.block_size != 0) {
- return false;
- }
- s_cfg.block_count = (lfs_size_t) (size / s_cfg.block_size);
- if (s_cfg.block_count == 0) return false;
- if (lfs_mount(&s_lfs, &s_cfg) != 0) {
- lfs_format(&s_lfs, &s_cfg);
- if (lfs_mount(&s_lfs, &s_cfg) != 0) result = -1;
- }
- s_lfs_ready = result == 0;
- return s_lfs_ready;
- }
- static struct mg_lfs_fd *find_fd(int fd) {
- struct mg_lfs_fd *f;
- for (f = s_fds; f != NULL; f = f->next) {
- if (f->isopen && f->fd == fd) return f;
- }
- return NULL;
- }
- static struct mg_lfs_fd *open_fd(void) {
- struct mg_lfs_fd *f = NULL;
- if (s_lfs_ready) {
- f = (struct mg_lfs_fd *) mg_calloc(1, sizeof(*f));
- if (f != NULL) {
- f->isopen = true;
- f->fd = s_next_fd++;
- f->next = s_fds;
- s_fds = f;
- if (s_next_fd < MG_LFS_FD_BASE) s_next_fd = MG_LFS_FD_BASE;
- }
- }
- return f;
- }
- static int close_fd(struct mg_lfs_fd *fd) {
- struct mg_lfs_fd **f = &s_fds;
- while (*f != NULL && *f != fd) f = &(*f)->next;
- if (*f == NULL) return -1;
- *f = fd->next;
- fd->isopen = false;
- mg_free(fd);
- return 0;
- }
- int _open(const char *path, int flags, mode_t mode) {
- int err, lfs_flags = 0, fd = -1;
- struct mg_lfs_fd *f = open_fd();
- (void) mode;
- if (f == NULL) return -1;
- fd = f->fd;
- if ((flags & 3) == O_RDONLY) lfs_flags |= LFS_O_RDONLY;
- if ((flags & 3) == O_WRONLY) lfs_flags |= LFS_O_WRONLY;
- if ((flags & 3) == O_RDWR) lfs_flags |= LFS_O_RDWR;
- if (flags & O_CREAT) lfs_flags |= LFS_O_CREAT;
- if (flags & O_TRUNC) lfs_flags |= LFS_O_TRUNC;
- if (flags & O_APPEND) lfs_flags |= LFS_O_APPEND;
- err = lfs_file_open(&s_lfs, &f->file, path, lfs_flags);
- if (err < 0) close_fd(f), fd = -1;
- return fd;
- }
- int _close(int fd) {
- struct mg_lfs_fd *f = find_fd(fd);
- if (fd < 3) return 0;
- if (f == NULL) return -1;
- lfs_file_close(&s_lfs, &f->file);
- close_fd(f);
- return 0;
- }
- int _write(int fd, char *ptr, int len) {
- struct mg_lfs_fd *f = find_fd(fd);
- return fd < 3 ? len
- : f == NULL ? -1 : lfs_file_write(&s_lfs, &f->file, ptr, len);
- }
- int _read(int fd, char *ptr, int len) {
- struct mg_lfs_fd *f = find_fd(fd);
- return fd < 3 ? 0 : f == NULL ? -1 : lfs_file_read(&s_lfs, &f->file, ptr, len);
- }
- int _lseek(int fd, int offset, int whence) {
- struct mg_lfs_fd *f = find_fd(fd);
- return fd < 3 ? 0
- : f == NULL ? -1 : lfs_file_seek(&s_lfs, &f->file, offset,
- whence);
- }
- int _rename(const char *oldname, const char *newname) {
- return s_lfs_ready ? lfs_rename(&s_lfs, oldname, newname) : -1;
- }
- int _unlink_r(void *r, const char *a) {
- (void) r;
- return s_lfs_ready ? lfs_remove(&s_lfs, a) : -1;
- }
- DIR *opendir(const char *name) {
- struct mg_lfs_fd *f = open_fd();
- if (f == NULL) return NULL;
- if (lfs_dir_open(&s_lfs, &f->dir, name) != 0) {
- close_fd(f);
- return NULL;
- }
- return (DIR *) f;
- }
- int closedir(DIR *dir) {
- struct mg_lfs_fd *f = (struct mg_lfs_fd *) dir;
- if (f == NULL || find_fd(f->fd) != f) return -1;
- lfs_dir_close(&s_lfs, &f->dir);
- return close_fd(f);
- }
- struct dirent *readdir(DIR *dir) {
- static struct dirent dirent;
- struct mg_lfs_fd *f = (struct mg_lfs_fd *) dir;
- struct lfs_info info;
- if (f == NULL || !f->isopen) return NULL;
- if (lfs_dir_read(&s_lfs, &f->dir, &info) < 1) return NULL;
- memset(&dirent, 0, sizeof(dirent));
- strncpy(dirent.d_name, info.name, sizeof(dirent.d_name) - 1);
- if (info.type == LFS_TYPE_DIR) dirent.d_type |= DT_DIR;
- if (info.type == LFS_TYPE_REG) dirent.d_type |= DT_REG;
- return &dirent;
- }
- int _stat(const char *path, struct stat *st) {
- struct lfs_info info;
- if (!s_lfs_ready || lfs_stat(&s_lfs, path, &info) != 0) return -1;
- st->st_mode = info.type == LFS_TYPE_DIR ? S_IFDIR : S_IFREG;
- st->st_size = info.size;
- return 0;
- }
- int _fstat(int fd, struct stat *st) {
- if (fd >= 3 && find_fd(fd) == NULL) return -1;
- st->st_mode = S_IFCHR;
- return 0;
- }
- int mkdir(const char *path, mode_t mode) {
- (void) mode;
- return s_lfs_ready ? lfs_mkdir(&s_lfs, path) : -1;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/log.c"
- #endif
- int mg_log_level = MG_LL_DEBUG;
- 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]);
- }
- #if MG_ENABLE_CUSTOM_LOG
- // Let user define their own mg_log_prefix() and mg_log()
- #else
- void mg_log_prefix(int level, const char *file, int line, const char *fname) {
- 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);
- }
- 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);
- logs("\r\n", 2);
- }
- #endif
- 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), logs("\r\n", 2), 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), logs("\r\n", 2);
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/md5.c"
- #endif
- // This code implements the MD5 message-digest algorithm.
- // The algorithm is due to Ron Rivest. This code was
- // written by Colin Plumb in 1993, no copyright is claimed.
- // This code is in the public domain; do with it what you wish.
- //
- // Equivalent code is available from RSA Data Security, Inc.
- // This code has been tested against that, and is equivalent,
- // except that you don't need to include two pages of legalese
- // with every copy.
- //
- // To compute the message digest of a chunk of bytes, declare an
- // MD5Context structure, pass it to MD5Init, call MD5Update as
- // needed on buffers full of bytes, and then call MD5Final, which
- // will fill a supplied 16-byte array with the digest.
- #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/modbus.c"
- #endif
- #define MG_MODBUS_MIN_PAYLOAD_LEN 12
- #define MG_MODBUS_HEADER_LEN 7
- static void mg_modbus_send_mbap(struct mg_connection *c, uint16_t txid,
- uint16_t pdu_len, uint8_t unit_id) {
- uint8_t hdr[7];
- memset(hdr, 0, sizeof(hdr));
- MG_STORE_BE16(&hdr[0], txid & 0xFFFFU);
- MG_STORE_BE16(&hdr[4], (pdu_len + 1) & 0xFFFFU);
- hdr[6] = unit_id;
- mg_send(c, hdr, sizeof(hdr));
- }
- static void mg_modbus_send_response(struct mg_connection *c,
- struct mg_modbus_req *req,
- uint16_t txid, uint8_t unit_id) {
- uint8_t buf[5];
- uint8_t out;
- uint16_t reg;
- uint16_t i, nbytes;
- memset(buf, 0, sizeof(buf));
- if (req == NULL) return;
- if (req->error != MG_MODBUS_ERR_NONE) {
- mg_modbus_send_mbap(c, txid, 2, unit_id);
- buf[0] = (uint8_t) (req->func | 0x80);
- buf[1] = req->error;
- mg_send(c, buf, 2);
- return;
- }
- switch (req->func) {
- case MG_MODBUS_FUNC_READ_COILS:
- case MG_MODBUS_FUNC_READ_DISCRETE_INPUTS:
- if (req->u.bits == NULL) return;
- nbytes = (uint16_t) ((req->len + 7) / 8);
- mg_modbus_send_mbap(c, txid, (uint16_t) (nbytes + 2), unit_id);
- buf[0] = req->func;
- buf[1] = (uint8_t) nbytes;
- mg_send(c, buf, 2);
- memset(buf, 0, sizeof(buf));
- for (i = 0; i < req->len; i++) {
- if (i % 8 == 0) out = 0;
- if (req->u.bits[i]) out |= (uint8_t) (1 << (i % 8));
- if ((i % 8) == 7 || i + 1 == req->len) {
- mg_send(c, &out, 1);
- }
- }
- break;
- case MG_MODBUS_FUNC_READ_HOLDING_REGISTERS:
- case MG_MODBUS_FUNC_READ_INPUT_REGISTERS:
- if (req->u.regs == NULL) return;
- nbytes = (uint16_t) (req->len * 2);
- mg_modbus_send_mbap(c, txid, (uint16_t) (nbytes + 2), unit_id);
- buf[0] = req->func;
- buf[1] = (uint8_t) nbytes;
- mg_send(c, buf, 2);
- for (i = 0; i < req->len; i++) {
- MG_STORE_BE16(®, req->u.regs[i] & 0xFFFFU);
- mg_send(c, ®, 2);
- }
- break;
- case MG_MODBUS_FUNC_WRITE_SINGLE_COIL:
- if (req->len != 1 || req->u.bits == NULL) return;
- mg_modbus_send_mbap(c, txid, 5, unit_id);
- buf[0] = req->func;
- MG_STORE_BE16(&buf[1], req->addr & 0xFFFFu);
- MG_STORE_BE16(&buf[3], req->u.bits[0] ? 0xFF00U : 0x0000U);
- mg_send(c, buf, 5);
- break;
- case MG_MODBUS_FUNC_WRITE_SINGLE_REGISTER:
- if (req->len != 1 || req->u.regs == NULL) return;
- mg_modbus_send_mbap(c, txid, 5, unit_id);
- buf[0] = req->func;
- MG_STORE_BE16(&buf[1], req->addr & 0xFFFFU);
- MG_STORE_BE16(&buf[3], req->u.regs[0] & 0xFFFFU);
- mg_send(c, buf, 5);
- break;
- case MG_MODBUS_FUNC_WRITE_MULTIPLE_COILS:
- case MG_MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS:
- mg_modbus_send_mbap(c, txid, 5, unit_id);
- buf[0] = req->func;
- MG_STORE_BE16(&buf[1], req->addr & 0xFFFFU);
- MG_STORE_BE16(&buf[3], req->len & 0xFFFFU);
- mg_send(c, buf, 5);
- break;
- default:
- break;
- }
- }
- static void handle_pdu(struct mg_connection *c, uint8_t *buf, size_t len) {
- uint16_t max_len, val, txid, addr, quantity;
- uint8_t func, unit_id, byte_count, packed;
- int i;
- size_t pdu_len = len - MG_MODBUS_HEADER_LEN, len_check;
- struct mg_modbus_req mr;
- if (len < MG_MODBUS_MIN_PAYLOAD_LEN) return;
- memset(&mr, 0, sizeof(mr));
- txid = MG_LOAD_BE16(&buf[0]);
- unit_id = buf[6];
- func = buf[7];
- addr = MG_LOAD_BE16(&buf[8]);
- mr.func = func;
- mr.addr = addr;
- mr.error = MG_MODBUS_ERR_NONE;
- mr.len = 0;
- mr.u.bits = NULL;
- switch (func) {
- case MG_MODBUS_FUNC_READ_COILS:
- case MG_MODBUS_FUNC_READ_DISCRETE_INPUTS:
- if (pdu_len != 5) goto modbus_illegal_value;
- quantity = MG_LOAD_BE16(&buf[10]);
- max_len = 0x07D0; // 2000 bits
- if (quantity == 0 || quantity > max_len) goto modbus_illegal_value;
- mr.len = quantity;
- mr.u.bits = (bool *) mg_calloc((size_t) mr.len, sizeof(bool));
- if (mr.u.bits == NULL) goto modbus_oom;
- break;
- case MG_MODBUS_FUNC_READ_HOLDING_REGISTERS:
- case MG_MODBUS_FUNC_READ_INPUT_REGISTERS:
- if (pdu_len != 5) goto modbus_illegal_value;
- quantity = MG_LOAD_BE16(&buf[10]);
- max_len = 0x007D; // 125 registers
- if (quantity == 0 || quantity > max_len) goto modbus_illegal_value;
- mr.len = quantity;
- mr.u.regs = (uint16_t *) mg_calloc((size_t) mr.len, sizeof(uint16_t));
- if (mr.u.regs == NULL) goto modbus_oom;
- break;
- case MG_MODBUS_FUNC_WRITE_SINGLE_COIL:
- if (pdu_len != 5) goto modbus_illegal_value;
- mr.len = 1;
- mr.u.bits = (bool *) mg_calloc(1, sizeof(bool));
- if (mr.u.bits == NULL) goto modbus_oom;
- val = MG_LOAD_BE16(&buf[10]);
- if (val != 0x0000 && val != 0xFF00) goto modbus_illegal_value;
- mr.u.bits[0] = (val == 0xFF00);
- break;
- case MG_MODBUS_FUNC_WRITE_SINGLE_REGISTER:
- if (pdu_len != 5) goto modbus_illegal_value;
- mr.len = 1;
- mr.u.regs = (uint16_t *) mg_calloc(1, sizeof(uint16_t));
- if (mr.u.regs == NULL) goto modbus_oom;
- mr.u.regs[0] = MG_LOAD_BE16(&buf[10]);
- break;
- case MG_MODBUS_FUNC_WRITE_MULTIPLE_COILS: {
- quantity = MG_LOAD_BE16(&buf[10]);
- if (quantity == 0 || quantity > 0x07B0) goto modbus_illegal_value;
- if (pdu_len < 6) goto modbus_illegal_value;
- byte_count = buf[12];
- if (byte_count != (uint8_t) ((quantity + 7) / 8)) goto modbus_illegal_value;
- len_check = (size_t) (6 + byte_count);
- if (len_check != pdu_len) goto modbus_illegal_value;
- mr.len = quantity;
- mr.u.bits = (bool *) mg_calloc((size_t) mr.len, sizeof(bool));
- if (mr.u.bits == NULL) goto modbus_oom;
- for (i = 0; i < mr.len; i++) {
- packed = buf[13 + (i / 8)];
- mr.u.bits[i] = ((packed >> (i % 8)) & 1) != 0;
- }
- break;
- }
- case MG_MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS: {
- quantity = MG_LOAD_BE16(&buf[10]);
- if (quantity == 0 || quantity > 0x007B) goto modbus_illegal_value;
- if (pdu_len < 6) goto modbus_illegal_value;
- byte_count = buf[12];
- if (byte_count != (uint8_t) (quantity * 2)) goto modbus_illegal_value;
- len_check = (size_t) (6 + quantity * 2);
- if (len_check != pdu_len) goto modbus_illegal_value;
- mr.len = quantity;
- mr.u.regs = (uint16_t *) mg_calloc((size_t) mr.len, sizeof(uint16_t));
- if (mr.u.regs == NULL) goto modbus_oom;
- for (i = 0; i < mr.len; i++) {
- mr.u.regs[i] = MG_LOAD_BE16(&buf[13 + i * 2]);
- }
- break;
- }
- default:
- MG_ERROR(("Unsupported modbus function"));
- mr.error = MG_MODBUS_ERR_ILLEGAL_FUNCTION;
- mg_modbus_send_response(c, &mr, txid, unit_id);
- return;
- }
- mg_call(c, MG_EV_MODBUS_REQ, &mr);
- goto modbus_exit;
- modbus_illegal_value:
- MG_ERROR(("Invalid data"));
- mr.error = MG_MODBUS_ERR_ILLEGAL_VALUE;
- goto modbus_exit;
- modbus_oom:
- MG_ERROR(("OOM"));
- mr.error = MG_MODBUS_ERR_DEVICE_FAILURE;
- modbus_exit:
- mg_modbus_send_response(c, &mr, txid, unit_id);
- if (mr.u.bits != NULL) mg_free(mr.u.bits);
- }
- static void modbus_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
- if (ev == MG_EV_READ) {
- while (c->recv.len >= MG_MODBUS_MIN_PAYLOAD_LEN) {
- uint16_t len = MG_LOAD_BE16(&c->recv.buf[4]); // PDU length
- uint16_t proto_id = MG_LOAD_BE16(&c->recv.buf[2]);
- if (len < 6 || proto_id != 0) {
- mg_error(c, "invalid pdu");
- break;
- } else if (c->recv.len < len + 6U) {
- break; // Partial frame, buffer more
- } else {
- handle_pdu(c, c->recv.buf, len + 6); // Parse PDU and call user
- mg_iobuf_del(&c->recv, 0, len + 6); // Delete received PDU
- }
- }
- }
- (void) ev_data;
- }
- struct mg_connection *mg_modbus_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 = modbus_ev_handler;
- return c;
- }
- #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
- struct mg_mqtt_pmap {
- uint8_t id;
- uint8_t type;
- };
- static const struct mg_mqtt_pmap s_prop_map[] = {
- {MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, MQTT_PROP_TYPE_BYTE},
- {MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT},
- {MQTT_PROP_CONTENT_TYPE, MQTT_PROP_TYPE_STRING},
- {MQTT_PROP_RESPONSE_TOPIC, MQTT_PROP_TYPE_STRING},
- {MQTT_PROP_CORRELATION_DATA, MQTT_PROP_TYPE_BINARY_DATA},
- {MQTT_PROP_SUBSCRIPTION_IDENTIFIER, MQTT_PROP_TYPE_VARIABLE_INT},
- {MQTT_PROP_SESSION_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT},
- {MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, MQTT_PROP_TYPE_STRING},
- {MQTT_PROP_SERVER_KEEP_ALIVE, MQTT_PROP_TYPE_SHORT},
- {MQTT_PROP_AUTHENTICATION_METHOD, MQTT_PROP_TYPE_STRING},
- {MQTT_PROP_AUTHENTICATION_DATA, MQTT_PROP_TYPE_BINARY_DATA},
- {MQTT_PROP_REQUEST_PROBLEM_INFORMATION, MQTT_PROP_TYPE_BYTE},
- {MQTT_PROP_WILL_DELAY_INTERVAL, MQTT_PROP_TYPE_INT},
- {MQTT_PROP_REQUEST_RESPONSE_INFORMATION, MQTT_PROP_TYPE_BYTE},
- {MQTT_PROP_RESPONSE_INFORMATION, MQTT_PROP_TYPE_STRING},
- {MQTT_PROP_SERVER_REFERENCE, MQTT_PROP_TYPE_STRING},
- {MQTT_PROP_REASON_STRING, MQTT_PROP_TYPE_STRING},
- {MQTT_PROP_RECEIVE_MAXIMUM, MQTT_PROP_TYPE_SHORT},
- {MQTT_PROP_TOPIC_ALIAS_MAXIMUM, MQTT_PROP_TYPE_SHORT},
- {MQTT_PROP_TOPIC_ALIAS, MQTT_PROP_TYPE_SHORT},
- {MQTT_PROP_MAXIMUM_QOS, MQTT_PROP_TYPE_BYTE},
- {MQTT_PROP_RETAIN_AVAILABLE, MQTT_PROP_TYPE_BYTE},
- {MQTT_PROP_USER_PROPERTY, MQTT_PROP_TYPE_STRING_PAIR},
- {MQTT_PROP_MAXIMUM_PACKET_SIZE, MQTT_PROP_TYPE_INT},
- {MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE},
- {MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE, MQTT_PROP_TYPE_BYTE},
- {MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE}};
- static bool 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)]);
- return mg_send(c, buf, (size_t) (vlen - buf));
- }
- void mg_mqtt_send_header(struct mg_connection *c, uint8_t cmd, uint8_t flags,
- uint32_t len) {
- if (!mqtt_send_header(c, cmd, flags, len)) mg_error(c, "OOM");
- }
- static bool mg_send_u16(struct mg_connection *c, uint16_t value) {
- return mg_send(c, &value, sizeof(value));
- }
- static bool mg_send_u32(struct mg_connection *c, uint32_t value) {
- return mg_send(c, &value, sizeof(value));
- }
- static uint8_t varint_size(size_t length) {
- uint8_t bytes_needed = 0;
- do {
- bytes_needed++;
- length /= 0x80;
- } while (length > 0);
- return bytes_needed;
- }
- static size_t encode_varint(uint8_t *buf, size_t value) {
- size_t len = 0;
- do {
- uint8_t b = (uint8_t) (value % 128);
- value /= 128;
- if (value > 0) b |= 0x80;
- buf[len++] = b;
- } while (value > 0);
- return len;
- }
- static size_t decode_varint(const uint8_t *buf, size_t len, uint32_t *value) {
- uint32_t mul = 1, ofs;
- *value = 0;
- for (ofs = 0; ofs < 4 && ofs < len; ofs++) {
- uint8_t enc_byte = buf[ofs];
- *value += (enc_byte & 0x7f) * mul;
- mul *= 128;
- if ((enc_byte & 0x80) == 0) return ofs + 1;
- }
- return 0;
- }
- static int mqtt_prop_type_by_id(uint8_t prop_id) {
- size_t i, num_properties = sizeof(s_prop_map) / sizeof(s_prop_map[0]);
- for (i = 0; i < num_properties; ++i) {
- if (s_prop_map[i].id == prop_id) return s_prop_map[i].type;
- }
- return -1; // Property ID not found
- }
- // Returns the size of the properties section, without the
- // size of the content's length
- static size_t get_properties_length(struct mg_mqtt_prop *props, size_t count) {
- size_t i, size = 0;
- for (i = 0; i < count; i++) {
- size++; // identifier
- switch (mqtt_prop_type_by_id(props[i].id)) {
- case MQTT_PROP_TYPE_STRING_PAIR:
- size += (uint32_t) (props[i].val.len + props[i].key.len +
- 2 * sizeof(uint16_t));
- break;
- case MQTT_PROP_TYPE_STRING:
- size += (uint32_t) (props[i].val.len + sizeof(uint16_t));
- break;
- case MQTT_PROP_TYPE_BINARY_DATA:
- size += (uint32_t) (props[i].val.len + sizeof(uint16_t));
- break;
- case MQTT_PROP_TYPE_VARIABLE_INT:
- size += varint_size((uint32_t) props[i].iv);
- break;
- case MQTT_PROP_TYPE_INT: size += (uint32_t) sizeof(uint32_t); break;
- case MQTT_PROP_TYPE_SHORT: size += (uint32_t) sizeof(uint16_t); break;
- case MQTT_PROP_TYPE_BYTE: size += (uint32_t) sizeof(uint8_t); break;
- default: return size; // cannot parse further down
- }
- }
- return size;
- }
- // returns the entire size of the properties section, including the
- // size of the variable length of the content
- static size_t get_props_size(struct mg_mqtt_prop *props, size_t count) {
- size_t size = get_properties_length(props, count);
- size += varint_size(size);
- return size;
- }
- static bool mg_send_mqtt_properties(struct mg_connection *c,
- struct mg_mqtt_prop *props, size_t nprops) {
- size_t total_size = get_properties_length(props, nprops);
- uint8_t buf_v[4] = {0, 0, 0, 0};
- uint8_t buf[4] = {0, 0, 0, 0};
- size_t i, len = encode_varint(buf, total_size);
- if (!mg_send(c, buf, (size_t) len)) return false;
- for (i = 0; i < nprops; i++) {
- if (!mg_send(c, &props[i].id, sizeof(props[i].id))) return false;
- switch (mqtt_prop_type_by_id(props[i].id)) {
- case MQTT_PROP_TYPE_STRING_PAIR:
- if (!mg_send_u16(c, mg_htons((uint16_t) props[i].key.len)) ||
- !mg_send(c, props[i].key.buf, props[i].key.len) ||
- !mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)) ||
- !mg_send(c, props[i].val.buf, props[i].val.len))
- return false;
- break;
- case MQTT_PROP_TYPE_BYTE:
- if (!mg_send(c, &props[i].iv, sizeof(uint8_t))) return false;
- break;
- case MQTT_PROP_TYPE_SHORT:
- if (!mg_send_u16(c, mg_htons((uint16_t) props[i].iv))) return false;
- break;
- case MQTT_PROP_TYPE_INT:
- if (!mg_send_u32(c, mg_htonl((uint32_t) props[i].iv))) return false;
- break;
- case MQTT_PROP_TYPE_STRING:
- if (!mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)) ||
- !mg_send(c, props[i].val.buf, props[i].val.len))
- return false;
- break;
- case MQTT_PROP_TYPE_BINARY_DATA:
- if (!mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)) ||
- !mg_send(c, props[i].val.buf, props[i].val.len))
- return false;
- break;
- case MQTT_PROP_TYPE_VARIABLE_INT:
- len = encode_varint(buf_v, props[i].iv);
- if (!mg_send(c, buf_v, (size_t) len)) return false;
- break;
- }
- }
- return true;
- }
- size_t mg_mqtt_next_prop(struct mg_mqtt_message *msg, struct mg_mqtt_prop *prop,
- size_t ofs) {
- uint8_t *props = (uint8_t *) msg->dgram.buf + msg->props_start;
- uint8_t *props_end = props + msg->props_size;
- uint8_t *i = props + ofs;
- size_t new_pos = ofs, len;
-
- if (msg->props_start > msg->dgram.len ||
- msg->props_size > msg->dgram.len - msg->props_start ||
- ofs >= msg->props_size)
- return 0;
- memset(prop, 0, sizeof(struct mg_mqtt_prop));
- prop->id = i[0];
- i++, new_pos++;
- switch (mqtt_prop_type_by_id(prop->id)) {
- case MQTT_PROP_TYPE_STRING_PAIR:
- if (i + 2 > props_end) return 0;
- prop->key.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
- if (i + 2 + prop->key.len > props_end) return 0;
- prop->key.buf = (char *) i + 2;
- i += 2 + prop->key.len;
- if (i + 2 > props_end) return 0;
- prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
- prop->val.buf = (char *) i + 2;
- if (i + 2 + prop->val.len > props_end) return 0;
- new_pos += 2 * sizeof(uint16_t) + prop->val.len + prop->key.len;
- break;
- case MQTT_PROP_TYPE_BYTE:
- if (i + 1 > props_end) return 0;
- prop->iv = (uint8_t) i[0];
- new_pos++;
- break;
- case MQTT_PROP_TYPE_SHORT:
- if (i + 2 > props_end) return 0;
- prop->iv = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
- new_pos += sizeof(uint16_t);
- break;
- case MQTT_PROP_TYPE_INT:
- if (i + 4 > props_end) return 0;
- prop->iv = ((uint32_t) i[0] << 24) | ((uint32_t) i[1] << 16) |
- ((uint32_t) i[2] << 8) | i[3];
- new_pos += sizeof(uint32_t);
- break;
- case MQTT_PROP_TYPE_STRING:
- if (i + 2 > props_end) return 0;
- prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
- prop->val.buf = (char *) i + 2;
- if (i + 2 + prop->val.len > props_end) return 0;
- new_pos += 2 + prop->val.len;
- break;
- case MQTT_PROP_TYPE_BINARY_DATA:
- if (i + 2 > props_end) return 0;
- prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]);
- prop->val.buf = (char *) i + 2;
- if (i + 2 + prop->val.len > props_end) return 0;
- new_pos += 2 + prop->val.len;
- break;
- case MQTT_PROP_TYPE_VARIABLE_INT:
- len = decode_varint(i, (size_t) (props_end - i), &prop->iv);
- if (i + len > props_end) return 0;
- new_pos = (len == 0) ? 0 : new_pos + len;
- break;
- default:
- new_pos = 0;
- break;
- }
- return new_pos;
- }
- void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
- char client_id[21];
- struct mg_str cid = opts->client_id;
- size_t total_len = 7 + 1 + 2 + 2;
- uint8_t hdr[8] = {0, 4, 'M', 'Q', 'T', 'T', 0, 0};
- hdr[6] = opts->version;
- if (cid.len == 0) {
- mg_random_str(client_id, sizeof(client_id) - 1);
- 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->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->topic.len > 0) { // allow zero-length msgs, message.len is size_t
- total_len += 4 + (uint32_t) opts->topic.len + (uint32_t) opts->message.len;
- hdr[7] |= MQTT_HAS_WILL;
- }
- if (opts->clean || cid.len == 0) hdr[7] |= MQTT_CLEAN_SESSION;
- if (opts->retain) hdr[7] |= MQTT_WILL_RETAIN;
- total_len += (uint32_t) cid.len;
- if (c->is_mqtt5) {
- total_len += get_props_size(opts->props, opts->num_props);
- if (hdr[7] & MQTT_HAS_WILL)
- total_len += get_props_size(opts->will_props, opts->num_will_props);
- }
- // keepalive == 0 means "do not disconnect us!"
- if (!mqtt_send_header(c, MQTT_CMD_CONNECT, 0, (uint32_t) total_len) ||
- !mg_send(c, hdr, sizeof(hdr)) ||
- !mg_send_u16(c, mg_htons((uint16_t) opts->keepalive)))
- goto fail;
- if (c->is_mqtt5 && !mg_send_mqtt_properties(c, opts->props, opts->num_props))
- goto fail;
- if (!mg_send_u16(c, mg_htons((uint16_t) cid.len)) ||
- !mg_send(c, cid.buf, cid.len))
- goto fail;
- if (hdr[7] & MQTT_HAS_WILL) {
- if (c->is_mqtt5 &&
- !mg_send_mqtt_properties(c, opts->will_props, opts->num_will_props))
- goto fail;
- if (!mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)) ||
- !mg_send(c, opts->topic.buf, opts->topic.len) ||
- !mg_send_u16(c, mg_htons((uint16_t) opts->message.len)) ||
- !mg_send(c, opts->message.buf, opts->message.len))
- goto fail;
- }
- if (opts->user.len > 0 &&
- (!mg_send_u16(c, mg_htons((uint16_t) opts->user.len)) ||
- !mg_send(c, opts->user.buf, opts->user.len)))
- goto fail;
- if (opts->pass.len > 0 &&
- (!mg_send_u16(c, mg_htons((uint16_t) opts->pass.len)) ||
- !mg_send(c, opts->pass.buf, opts->pass.len)))
- goto fail;
- return;
- fail:
- mg_error(c, "OOM");
- }
- uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
- uint16_t id = opts->retransmit_id;
- uint8_t flags = (uint8_t) (((opts->qos & 3) << 1) | (opts->retain ? 1 : 0));
- size_t len = 2 + opts->topic.len + opts->message.len;
- MG_DEBUG(("%lu [%.*s] <- [%.*s%c", c->id, (int) opts->topic.len,
- (char *) opts->topic.buf,
- (int) (opts->message.len <= 10 ? opts->message.len : 10),
- (char *) opts->message.buf, opts->message.len <= 10 ? ']' : ' '));
- if (opts->qos > 0) len += 2;
- if (c->is_mqtt5) len += get_props_size(opts->props, opts->num_props);
- if (opts->qos > 0 && id != 0) flags |= 1 << 3;
- if (!mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, (uint32_t) len) ||
- !mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)) ||
- !mg_send(c, opts->topic.buf, opts->topic.len))
- goto fail;
- if (opts->qos > 0) { // need to send 'id' field
- if (id == 0) { // generate new one if not resending
- if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id;
- id = c->mgr->mqtt_id;
- }
- if (!mg_send_u16(c, mg_htons(id))) goto fail;
- }
- if (c->is_mqtt5 && !mg_send_mqtt_properties(c, opts->props, opts->num_props))
- goto fail;
- if (opts->message.len > 0 &&
- !mg_send(c, opts->message.buf, opts->message.len))
- goto fail;
- return id;
- fail:
- mg_error(c, "OOM");
- return id;
- }
- static void mg_mqtt_sub_unsub(struct mg_connection *c,
- const struct mg_mqtt_opts *opts, uint8_t cmd) {
- uint8_t qos_ = opts->qos & 3;
- bool is_sub = cmd == MQTT_CMD_SUBSCRIBE;
- size_t plen = c->is_mqtt5 ? get_props_size(opts->props, opts->num_props) : 0;
- size_t len = 2 + opts->topic.len + 2 + (is_sub ? 1 : 0) + plen;
- if (!mqtt_send_header(c, cmd, 2, (uint32_t) len)) goto fail;
- if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id;
- if (!mg_send_u16(c, mg_htons(c->mgr->mqtt_id))) goto fail;
- if (c->is_mqtt5 && !mg_send_mqtt_properties(c, opts->props, opts->num_props))
- goto fail;
- if (!mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)) ||
- !mg_send(c, opts->topic.buf, opts->topic.len))
- goto fail;
- if (is_sub && !mg_send(c, &qos_, sizeof(qos_))) goto fail;
- return;
- fail:
- mg_error(c, "OOM");
- }
- void mg_mqtt_sub(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
- mg_mqtt_sub_unsub(c, opts, MQTT_CMD_SUBSCRIBE);
- }
- void mg_mqtt_unsub(struct mg_connection *c, const struct mg_mqtt_opts *opts) {
- mg_mqtt_sub_unsub(c, opts, MQTT_CMD_UNSUBSCRIBE);
- }
- 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, tmp;
- memset(m, 0, sizeof(*m));
- m->dgram.buf = (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.buf = (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) {
- len_len = (uint32_t) decode_varint(p, (size_t) (end - p), &tmp);
- if (!len_len) return MQTT_MALFORMED;
- m->props_size = (size_t) tmp;
- m->props_start = (size_t) (p + len_len - buf);
- p += len_len + m->props_size;
- }
- if (p > end) return MQTT_MALFORMED;
- m->data.buf = (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) {
- 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.buf));
- 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", c->id, (int) mm.topic.len,
- mm.topic.buf,
- (int) (mm.data.len <= 10 ? mm.data.len : 10), mm.data.buf,
- mm.data.len <= 10 ? ']' : ' '));
- if (mm.qos > 0) {
- uint16_t id = mg_ntohs(mm.id);
- uint32_t remaining_len = sizeof(id);
- if (c->is_mqtt5) remaining_len += 2; // 3.4.2
- if (!mqtt_send_header(c,
- (uint8_t) (mm.qos == 2 ? MQTT_CMD_PUBREC
- : MQTT_CMD_PUBACK),
- 0, remaining_len) ||
- !mg_send(c, &id, sizeof(id)))
- goto fail;
- if (c->is_mqtt5) {
- uint16_t zero = 0;
- if (!mg_send(c, &zero, sizeof(zero))) goto fail;
- }
- }
- mg_call(c, MG_EV_MQTT_MSG, &mm); // let the app handle qos stuff
- break;
- }
- case MQTT_CMD_PUBREC: { // MQTT5: 3.5.2-1 TODO(): variable header rc
- uint16_t id = mg_ntohs(mm.id);
- uint32_t remaining_len = sizeof(id); // MQTT5 3.6.2-1
- if (!mqtt_send_header(c, MQTT_CMD_PUBREL, 2,
- remaining_len) // MQTT5 3.6.1-1, flags = 2
- || !mg_send(c, &id, sizeof(id)))
- goto fail;
- break;
- }
- case MQTT_CMD_PUBREL: { // MQTT5: 3.6.2-1 TODO(): variable header rc
- uint16_t id = mg_ntohs(mm.id);
- uint32_t remaining_len = sizeof(id); // MQTT5 3.7.2-1
- if (!mqtt_send_header(c, MQTT_CMD_PUBCOMP, 0, remaining_len) ||
- !mg_send(c, &id, sizeof(id)))
- goto fail;
- break;
- }
- }
- mg_call(c, MG_EV_MQTT_CMD, &mm);
- mg_iobuf_del(&c->recv, 0, mm.dgram.len);
- } else {
- break;
- }
- }
- }
- (void) ev_data;
- return;
- fail:
- mg_error(c, "OOM");
- }
- 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 *c,
- const struct mg_mqtt_opts *opts) {
- size_t len = 0;
- if (c->is_mqtt5) len = 1 + get_props_size(opts->props, opts->num_props);
- if (!mqtt_send_header(c, MQTT_CMD_DISCONNECT, 0, (uint32_t) len)) goto fail;
- if (c->is_mqtt5) {
- uint8_t zero = 0;
- if (!mg_send(c, &zero, sizeof(zero)) // reason code
- || !mg_send_mqtt_properties(c, opts->props, opts->num_props))
- goto fail;
- }
- return;
- fail:
- mg_error(c, "OOM");
- }
- 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_svc(mgr, url, fn, fn_data, mqtt_cb, NULL);
- if (c != NULL) {
- struct mg_mqtt_opts empty;
- memset(&empty, 0, sizeof(empty));
- mg_mqtt_login(c, opts == NULL ? &empty : opts);
- }
- 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;
- size_t expected = mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap);
- size_t actual = c->send.len - old;
- if (actual != expected) {
- mg_error(c, "OOM");
- c->send.len = old;
- actual = 0;
- }
- return actual;
- }
- 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;
- }
- bool mg_dscp(struct mg_connection *c, uint8_t dscp) {
- #if MG_ENABLE_SOCKET
- #if MG_ENABLE_FREERTOS_TCP || MG_ARCH == MG_ARCH_THREADX || \
- MG_ARCH == MG_ARCH_TIRTOS || MG_ARCH == MG_ARCH_WIN32
- (void) c;
- (void) dscp;
- #else
- int tos = dscp << 2, level = IPPROTO_IP, optname = IP_TOS;
- c->dscp = (unsigned) (dscp & 63);
- #if MG_ENABLE_IPV6 && !MG_ENABLE_LWIP
- if (c->loc.is_ip6)
- level = IPPROTO_IPV6, optname = IPV6_TCLASS;
- #endif
- return setsockopt((MG_SOCKET_TYPE) (size_t) c->fd, level, optname, (char *) &tos, sizeof(tos)) == 0;
- #endif
- #elif MG_ENABLE_TCPIP
- c->dscp = (unsigned) (dscp & 63);
- return true;
- #endif
- return false;
- }
- static bool mg_atonl(struct mg_str str, struct mg_addr *addr) {
- uint32_t localhost = mg_htonl(0x7f000001);
- if (mg_strcasecmp(str, mg_str("localhost")) != 0) return false;
- memcpy(addr->addr.ip, &localhost, sizeof(uint32_t));
- addr->is_ip6 = false;
- return true;
- }
- static bool mg_atone(struct mg_str str, struct mg_addr *addr) {
- if (str.len > 0) return false;
- memset(addr->addr.ip, 0, sizeof(addr->addr.ip));
- 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.buf[i] >= '0' && str.buf[i] <= '9') {
- int octet = data[num_dots] * 10 + (str.buf[i] - '0');
- if (octet > 255) return false;
- data[num_dots] = (uint8_t) octet;
- } else if (str.buf[i] == '.') {
- if (num_dots >= 3 || i == 0 || str.buf[i - 1] == '.') return false;
- num_dots++;
- } else {
- return false;
- }
- }
- if (num_dots != 3 || str.buf[i - 1] == '.') return false;
- memcpy(&addr->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;
- uint32_t ipv4;
- if (str.len < 14) return false;
- if (str.buf[0] != ':' || str.buf[1] != ':' || str.buf[6] != ':') return false;
- for (i = 2; i < 6; i++) {
- if (str.buf[i] != 'f' && str.buf[i] != 'F') return false;
- }
- // struct mg_str s = mg_str_n(&str.buf[7], str.len - 7);
- if (!mg_aton4(mg_str_n(&str.buf[7], str.len - 7), addr)) return false;
- memcpy(&ipv4, addr->addr.ip, sizeof(ipv4));
- memset(addr->addr.ip, 0, sizeof(addr->addr.ip));
- addr->addr.ip[10] = addr->addr.ip[11] = 255;
- memcpy(&addr->addr.ip[12], &ipv4, 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;
- uint16_t val = 0;
- bool have = false;
- addr->scope_id = 0;
- if (str.len > 2 && str.buf[0] == '[') str.buf++, str.len -= 2;
- if (mg_v4mapped(str, addr)) return true; // sets addr->is_ip6
- for (i = 0; i < str.len; i++) {
- int x = str.buf[i] >= '0' && str.buf[i] <= '9' ? str.buf[i] - '0'
- : str.buf[i] >= 'a' && str.buf[i] <= 'f' ? str.buf[i] - 'a' + 10
- : str.buf[i] >= 'A' && str.buf[i] <= 'F' ? str.buf[i] - 'A' + 10
- : -1;
- if (x >= 0) {
- if (i > j + 3) return false;
- val = (uint16_t) ((val << 4) | (uint16_t) x);
- have = true;
- continue;
- }
- if (have) {
- addr->addr.ip[n] = (uint8_t) (val >> 8),
- addr->addr.ip[n + 1] = (uint8_t) val;
- val = 0, have = false;
- }
- if (str.buf[i] == ':') {
- j = i + 1;
- if (i > 0 && str.buf[i - 1] == ':') {
- if (dc != 42) return false;
- dc = n; // Double colon
- if (i > 1 && str.buf[i - 2] == ':') return false;
- } else if (i > 0) {
- n += 2;
- }
- if (n > 14) return false;
- addr->addr.ip[n] = addr->addr.ip[n + 1] = 0; // For trailing ::
- } else if (str.buf[i] == '%') { // Scope ID, last in string
- if (mg_str_to_num(mg_str_n(&str.buf[i + 1], str.len - i - 1), 10,
- &addr->scope_id, sizeof(uint8_t))) {
- addr->is_ip6 = true;
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
- if (have)
- addr->addr.ip[n] = (uint8_t) (val >> 8),
- addr->addr.ip[n + 1] = (uint8_t) val;
- if (n < 14 && dc == 42) return false;
- if (n < 14) {
- memmove(&addr->addr.ip[dc + (14 - n)], &addr->addr.ip[dc], n - dc + 2);
- memset(&addr->addr.ip[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.buf));
- 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 *) mg_calloc(1, sizeof(*c) + mgr->extraconnsize);
- if (c != NULL) {
- c->mgr = mgr;
- c->send.align = c->recv.align = c->rtls.align = MG_IO_SIZE;
- c->id = ++mgr->nextid;
- MG_PROF_INIT(c);
- }
- 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 %ld closed", c->id, c->fd));
- MG_PROF_DUMP(c);
- MG_PROF_FREE(c);
- mg_tls_free(c);
- mg_iobuf_free(&c->recv);
- mg_iobuf_free(&c->send);
- mg_iobuf_free(&c->rtls);
- mg_bzero((unsigned char *) c, sizeof(*c));
- mg_free(c);
- }
- struct mg_connection *mg_connect_svc(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data,
- mg_event_handler_t pfn, void *pfn_data) {
- struct mg_connection *c = NULL;
- if (url == NULL || url[0] == '\0') {
- MG_ERROR(("null url"));
- #if MG_ENABLE_TCPIP
- } else if (mgr->ifp != NULL && mgr->ifp->state != MG_TCPIP_STATE_READY) {
- MG_ERROR(("Network is down"));
- #endif
- } 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;
- c->is_tls = (mg_url_is_ssl(url) != 0);
- c->pfn = pfn;
- c->pfn_data = pfn_data;
- mg_call(c, MG_EV_OPEN, (void *) url);
- MG_DEBUG(("%lu %ld %s", c->id, c->fd, url));
- mg_resolve(c, url);
- }
- return c;
- }
- struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
- mg_event_handler_t fn, void *fn_data) {
- return mg_connect_svc(mgr, url, fn, fn_data, NULL, NULL);
- }
- 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", url));
- MG_PROF_FREE(c);
- mg_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;
- c->is_tls = (mg_url_is_ssl(url) != 0);
- mg_call(c, MG_EV_OPEN, NULL);
- MG_DEBUG(("%lu %ld %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 *) mg_calloc(1, sizeof(*t));
- if (t != NULL) {
- flags |= MG_TIMER_AUTODELETE; // We have alloc'ed it, so autodelete
- mg_timer_init(&mgr->timers, t, milliseconds, flags, fn, arg);
- }
- return t;
- }
- long mg_io_recv(struct mg_connection *c, void *buf, size_t len) {
- if (c->rtls.len == 0) return MG_IO_WAIT;
- if (len > c->rtls.len) len = c->rtls.len;
- memcpy(buf, c->rtls.buf, len);
- mg_iobuf_del(&c->rtls, 0, len);
- return (long) len;
- }
- 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, mg_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
- mg_tls_ctx_free(mgr);
- #if MG_ENABLE_TCPIP
- if (mgr->ifp) mg_tcpip_free(mgr->ifp);
- #endif
- }
- void mg_mgr_init(struct mg_mgr *mgr) {
- memset(mgr, 0, sizeof(*mgr));
- #if MG_ENABLE_EPOLL
- if ((mgr->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
- MG_ERROR(("epoll_create1 errno %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 MG_ARCH == MG_ARCH_UNIX
- // Ignore SIGPIPE signal, so if client cancels the request, it
- // won't kill the whole process.
- signal(SIGPIPE, SIG_IGN);
- #elif MG_ENABLE_TCPIP_DRIVER_INIT && defined(MG_TCPIP_DRIVER_INIT)
- MG_TCPIP_DRIVER_INIT(mgr);
- #endif
- #if MG_ENABLE_BSD_SOCKETS
- mgr->pipe.q = NULL;
- #else
- mgr->pipe.fd = MG_INVALID_SOCKET;
- #endif
- mgr->dnstimeout = 3000;
- mgr->dns4.url = "udp://8.8.8.8:53";
- mgr->dns6.url = "udp://[2001:4860:4860::8888]:53";
- mg_tls_ctx_init(mgr);
- MG_DEBUG(("MG_IO_SIZE: %lu, TLS: %s", MG_IO_SIZE,
- MG_TLS == MG_TLS_NONE ? "none"
- : MG_TLS == MG_TLS_MBED ? "MbedTLS"
- : MG_TLS == MG_TLS_OPENSSL ? "OpenSSL"
- : MG_TLS == MG_TLS_BUILTIN ? "builtin"
- : MG_TLS == MG_TLS_WOLFSSL ? "WolfSSL"
- : "custom"));
- }
- #if MG_ENABLE_TCPIP
- void mg_tcpip_mapip(struct mg_connection *, struct mg_addr *);
- #endif
- void mg_multicast_restore(struct mg_connection *c, uint8_t *from) {
- memcpy(&c->rem, from, sizeof(c->rem));
- #if MG_ENABLE_TCPIP
- mg_tcpip_mapip(c, &c->rem);
- #endif
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/net_builtin.c"
- #endif
- #if MG_ENABLE_TCPIP
- #define MG_EPHEMERAL_PORT_BASE 32768
- #define PDIFF(a, b) ((size_t) (((char *) (b)) - ((char *) (a))))
- #ifndef MG_TCPIP_KEEPALIVE_MS
- #define MG_TCPIP_KEEPALIVE_MS 45000 // TCP keep-alive period, ms
- #endif
- #define MG_TCPIP_ACK_MS 150 // Timeout for ACKing
- #define MG_TCPIP_ARP_MS 100 // Timeout for ARP response
- #define MG_TCPIP_SYN_MS 15000 // Timeout for connection establishment
- #define MG_TCPIP_FIN_MS 1000 // Timeout for closing connection
- #ifndef MG_TCPIP_WIN
- #define MG_TCPIP_WIN 6000 // TCP window size
- #endif
- struct connstate {
- uint32_t seq, ack; // TCP seq/ack counters
- uint64_t timer; // TCP timer (see 'ttype' below)
- uint32_t acked; // Last ACK-ed number
- size_t unacked; // Not acked bytes
- uint32_t maxseq; // Max send seq (ack + window)
- uint16_t win; // destination current window size
- uint16_t dmss; // destination MSS (from TCP opts)
- uint8_t mac[sizeof(struct mg_l2addr)]; // Peer hw address
- uint8_t ttype; // Timer type:
- #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
- #define MIP_TTYPE_ARP 2 // ARP resolve sent, waiting for response
- #define MIP_TTYPE_SYN 3 // SYN sent, waiting for response
- #define MIP_TTYPE_FIN 4 // FIN sent, waiting until terminating the connection
- uint8_t tmiss; // Number of keep-alive misses
- bool fin_rcvd; // We have received FIN from the peer
- bool twclosure; // 3-way closure done
- };
- #if defined(__DCC__)
- #pragma pack(1)
- #else
- #pragma pack(push, 1)
- #endif
- struct ip {
- uint8_t ver; // Version
- uint8_t tos; // DS field
- uint16_t len; // Datagram length
- uint16_t id; // Unused
- uint16_t frag; // Fragmentation
- #define IP_FRAG_OFFSET_MSK 0x1fff
- #define IP_MORE_FRAGS_MSK 0x2000
- 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 label[3]; // Flow label
- uint16_t plen; // Payload length
- uint8_t next; // Upper level protocol
- uint8_t hops; // Hop limit
- uint64_t src[2]; // Source IP
- uint64_t dst[2]; // Destination IP
- };
- struct icmp {
- uint8_t type;
- uint8_t code;
- uint16_t csum;
- };
- struct icmp6 {
- uint8_t type;
- uint8_t code;
- uint16_t csum;
- };
- struct ndp_na {
- uint8_t res[4]; // R S O, reserved
- uint64_t addr[2]; // Target address
- };
- struct ndp_ra {
- uint8_t cur_hop_limit;
- uint8_t flags; // M,O,Prf,Resvd
- uint16_t router_lifetime;
- uint32_t reachable_time;
- uint32_t retrans_timer;
- };
- 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_STDFLAGS 0x3f
- // #define TH_ECE 0x40 // not part of TCP but RFC-3168 (ECN)
- // #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[30 + sizeof(((struct mg_tcpip_if *) 0)->dhcp_name)];
- };
- struct dhcp6 {
- union {
- uint8_t type;
- uint32_t xid;
- };
- uint8_t options[30 + sizeof(((struct mg_tcpip_if *) 0)->dhcp_name)];
- };
- struct pseudoip {
- uint32_t src; // Source IP
- uint32_t dst; // Destination IP
- uint8_t zero;
- uint8_t proto; // Upper level protocol
- uint16_t len; // Datagram length
- };
- struct pseudoip6 {
- uint64_t src[2]; // Source IP
- uint64_t dst[2]; // Destination IP
- uint32_t plen; // Payload length
- uint8_t zero[3];
- uint8_t next; // Upper level protocol
- };
- #if defined(__DCC__)
- #pragma pack(0)
- #else
- #pragma pack(pop)
- #endif
- // pkt is 8-bit aligned, pointers to headers hint compilers to generate
- // byte-copy code for micros with alignment constraints
- struct pkt {
- struct mg_str raw; // Raw packet data
- struct mg_str pay; // Payload data
- uint8_t *l2; // Ethernet, PPP [, etc] frame data
- struct arp *arp;
- struct ip *ip;
- struct ip6 *ip6;
- struct icmp *icmp;
- struct icmp6 *icmp6;
- struct tcp *tcp;
- struct udp *udp;
- struct dhcp *dhcp;
- struct dhcp6 *dhcp6;
- };
- // L2 API
- void mg_l2_init(struct mg_tcpip_if *ifp);
- uint8_t *mg_l2_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
- uint8_t *src, uint8_t *dst, uint8_t *frame);
- size_t mg_l2_trailer(struct mg_tcpip_if *ifp, size_t len, uint8_t *cur);
- bool mg_l2_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
- struct mg_str *pay, struct mg_str *raw);
- uint8_t *mg_l2_getaddr(struct mg_tcpip_if *ifp, uint8_t *frame);
- uint8_t *mg_l2_mapip(enum mg_l2type type, enum mg_l2addrtype addrtype,
- struct mg_addr *ip);
- #if MG_ENABLE_IPV6
- bool mg_l2_genip6(enum mg_l2type type, uint64_t *ip6, uint8_t prefix_len,
- uint8_t *addr);
- bool mg_l2_ip6get(enum mg_l2type type, uint8_t *addr, uint8_t *opts,
- uint8_t len);
- uint8_t mg_l2_ip6put(enum mg_l2type type, uint8_t *addr, uint8_t *opts);
- #endif
- bool mg_l2_poll(struct mg_tcpip_if *ifp, bool expired_1000ms);
- static void mg_tcpip_call(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
- #if 0 && MG_ENABLE_PROFILE
- const char *names[] = {"TCPIP_EV_STATE_CHANGE", "TCPIP_EV_DHCP_DNS",
- "TCPIP_EV_DHCP_SNTP", "TCPIP_EV_ARP",
- "TCPIP_EV_TIMER_1S", "TCPIP_EV_WIFI_SCAN_RESULT",
- "TCPIP_EV_WIFI_SCAN_END", "TCPIP_EV_WIFI_CONNECT_ERR",
- "TCPIP_EV_DRIVER", "TCPIP_EV_STATE6_CHANGE",
- "TCPIP_EV_USER"};
- if (ev != MG_TCPIP_EV_TIMER_1S && ev < (int) (sizeof(names) / sizeof(names[0]))) {
- MG_PROF_ADD(ifp, names[ev]); // TODO(): call MG_PROF_DUMP() MG_PROF_FREE()
- }
- #endif
- // Fire protocol handler first, user handler second. See #2559
- if (ifp->pfn != NULL) ifp->pfn(ifp, ev, ev_data);
- if (ifp->fn != NULL) ifp->fn(ifp, ev, ev_data);
- }
- static void send_syn(struct mg_connection *c);
- static void mkpay(struct pkt *pkt, void *p) {
- pkt->pay =
- mg_str_n((char *) p, (size_t) (&pkt->pay.buf[pkt->pay.len] - (char *) p));
- }
- // NOTE(): DOES NOT handle reentries after odd length, use last
- static uint32_t csumup(uint32_t sum, const void *buf, size_t len) {
- size_t i;
- const uint8_t *p = (const uint8_t *) buf;
- for (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((uint16_t) ((uint16_t) ~sum & 0xffff));
- }
- static uint16_t ipcsum(const void *buf, size_t len) {
- uint32_t sum = csumup(0, buf, len);
- return csumfin(sum);
- }
- static bool ipcsum_ok(const void *d) {
- struct ip *ip = (struct ip *) d;
- return (ipcsum(d, (ip->ver & 0x0F) * 4) == 0);
- }
- static bool icmpcsum_ok(const void *d, size_t len) {
- return (ipcsum(d, len) == 0);
- }
- static uint16_t pcsum(void *d, void *p, size_t plen) {
- uint32_t sum;
- struct ip *ip = (struct ip *) d;
- #if defined(__DCC__)
- volatile /* Makes PPC & Diab4.3 happy */
- #endif
- struct pseudoip pip;
- pip.src = ip->src;
- pip.dst = ip->dst;
- pip.zero = 0;
- pip.proto = ip->proto;
- pip.len = mg_htons((uint16_t) plen);
- sum = csumup(0, &pip, sizeof(pip)); // even length
- sum = csumup(sum, p, plen); // possibly odd length: last
- return csumfin(sum);
- }
- static bool udpcsum_ok(void *d, void *u) {
- struct udp *udp = (struct udp *) u;
- if (udp->csum == 0) return true;
- if (udp->csum == 0xFFFF) udp->csum = 0;
- return (pcsum(d, u, (size_t) mg_ntohs(udp->len)) == 0);
- }
- static bool tcpcsum_ok(void *d, void *t) {
- struct ip *ip = (struct ip *) d;
- return (pcsum(d, t, (size_t) (mg_ntohs(ip->len) - (ip->ver & 0x0F) * 4)) ==
- 0);
- }
- #if MG_ENABLE_IPV6
- static uint16_t p6csum(void *d, void *p, size_t plen) {
- uint32_t sum;
- struct ip6 *ip6 = (struct ip6 *) d;
- #if defined(__DCC__)
- volatile /* Makes PPC & Diab4.3 happy */
- #endif
- struct pseudoip6 pip6;
- pip6.src[0] = ip6->src[0], pip6.src[1] = ip6->src[1];
- pip6.dst[0] = ip6->dst[0], pip6.dst[1] = ip6->dst[1];
- pip6.zero[0] = 0, pip6.zero[1] = 0, pip6.zero[2] = 0;
- pip6.plen = mg_htonl((uint32_t) plen);
- pip6.next = ip6->next;
- sum = csumup(0, &pip6, sizeof(pip6)); // even length
- sum = csumup(sum, p, plen); // possibly odd length: last
- return csumfin(sum);
- }
- static bool udp6csum_ok(void *d, void *u) {
- struct udp *udp = (struct udp *) u;
- if (udp->csum == 0) return false; // mandatory in IPv6
- if (udp->csum == 0xFFFF) udp->csum = 0;
- return (p6csum(d, u, (size_t) mg_ntohs(udp->len)) == 0);
- }
- static bool tcp6csum_ok(void *d, void *t) {
- struct ip6 *ip6 = (struct ip6 *) d;
- return (p6csum(d, t, (size_t) mg_ntohs(ip6->plen)) == 0);
- }
- static bool icmp6csum_ok(void *d, void *i) {
- struct ip6 *ip6 = (struct ip6 *) d;
- return (p6csum(d, i, (size_t) mg_ntohs(ip6->plen)) == 0);
- }
- static void ip6sn(uint64_t *addr, uint64_t *sn_addr) {
- // Build solicited-node multicast address from a given unicast IP
- // RFC-4291 2.7
- uint8_t *sn = (uint8_t *) sn_addr;
- memset(sn_addr, 0, 16);
- sn[0] = 0xff;
- sn[1] = 0x02;
- sn[11] = 0x01;
- sn[12] = 0xff;
- sn[13] = ((uint8_t *) addr)[13];
- sn[14] = ((uint8_t *) addr)[14];
- sn[15] = ((uint8_t *) addr)[15];
- }
- static const struct mg_addr ip6_allrouters = {
- {{0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02}}, 0, 0, true};
- static const struct mg_addr ip6_allnodes = {
- {{0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}}, 0, 0, true};
- #define MG_IP6MATCH(a, b) (a[0] == b[0] && a[1] == b[1])
- #endif
- static void settmout(struct mg_connection *c, uint8_t type) {
- struct mg_tcpip_if *ifp = c->mgr->ifp;
- struct connstate *s = (struct connstate *) (c + 1);
- unsigned n = type == MIP_TTYPE_ACK ? MG_TCPIP_ACK_MS
- : type == MIP_TTYPE_ARP ? MG_TCPIP_ARP_MS
- : type == MIP_TTYPE_SYN ? MG_TCPIP_SYN_MS
- : type == MIP_TTYPE_FIN ? MG_TCPIP_FIN_MS
- : MG_TCPIP_KEEPALIVE_MS;
- if (s->ttype == MIP_TTYPE_FIN) return; // skip if 3-way closing
- s->timer = ifp->now + n;
- s->ttype = type;
- MG_VERBOSE(("%lu %d -> %llx", c->id, type, s->timer));
- }
- static size_t driver_output(struct mg_tcpip_if *ifp, size_t len) {
- size_t n = ifp->driver->tx(ifp->tx.buf, len, ifp);
- if (n == len) ifp->nsent++;
- return n;
- }
- // RFC826, ARP assumes Ethernet MAC addresses
- void mg_tcpip_arp_request(struct mg_tcpip_if *ifp, uint32_t ip, uint8_t *mac) {
- uint8_t *l2p = (uint8_t *) ifp->tx.buf;
- struct arp *arp = (struct arp *) mg_l2_header(
- ifp, MG_TCPIP_L2PROTO_ARP, ifp->mac,
- mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_BCAST, NULL), l2p);
- 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));
- if (mac != NULL) memcpy(arp->tha, mac, sizeof(arp->tha));
- driver_output(ifp, mg_l2_trailer(ifp, sizeof(*arp), (uint8_t *) (arp + 1)));
- }
- static void onstatechange(struct mg_tcpip_if *ifp) {
- if (ifp->state == MG_TCPIP_STATE_IP &&
- (ifp->l2type == MG_TCPIP_L2_PPP || ifp->l2type == MG_TCPIP_L2_PPPoE))
- ifp->state = MG_TCPIP_STATE_READY;
- 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->l2type == MG_TCPIP_L2_ETH ||
- ifp->l2type == MG_TCPIP_L2_PPPoE) // TODO(): print other l2
- MG_INFO((" MAC: %M", mg_print_mac, ifp->mac));
- if (ifp->l2type == MG_TCPIP_L2_ETH)
- mg_tcpip_arp_request(ifp, ifp->ip, ifp->mac); // gratuitous ARP annc
- if (ifp->is_ip_changed) {
- struct mg_connection *c;
- for (c = ifp->mgr->conns; c != NULL; c = c->next) {
- if (!c->is_listening && !c->is_udp) c->is_closing = 1;
- }
- ifp->is_ip_changed = false;
- }
- } else if (ifp->state == MG_TCPIP_STATE_IP) {
- if (ifp->gw != 0 && ifp->l2type == MG_TCPIP_L2_ETH)
- mg_tcpip_arp_request(ifp, ifp->gw, NULL); // unsolicited GW ARP request
- } else if (ifp->state == MG_TCPIP_STATE_UP) {
- srand((unsigned int) mg_millis());
- } else if (ifp->state == MG_TCPIP_STATE_DOWN) {
- MG_ERROR(("Link down"));
- }
- mg_tcpip_call(ifp, MG_TCPIP_EV_STATE_CHANGE, &ifp->state);
- }
- static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint8_t proto,
- uint8_t dscp, uint32_t ip_src, uint32_t ip_dst,
- size_t plen) {
- // ifp->tx.buf is 8-bit aligned, keep other headers as pointers, see pkt
- uint8_t *l2p = (uint8_t *) ifp->tx.buf;
- struct ip *ip = (struct ip *) mg_l2_header(ifp, MG_TCPIP_L2PROTO_IPV4,
- ifp->mac, l2_dst, l2p);
- memset(ip, 0, sizeof(*ip));
- ip->ver = 0x45; // Version 4, header length 5 words
- ip->tos = (uint8_t) (dscp << 2);
- ip->frag = mg_htons(0x4000); // 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;
- }
- #if MG_ENABLE_IPV6
- static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
- uint8_t next, uint8_t dscp, uint64_t *ip_src,
- uint64_t *ip_dst, size_t plen);
- #endif
- static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
- struct mg_addr *ip_src, struct mg_addr *ip_dst, uint8_t dscp,
- const void *buf, size_t len) {
- uint8_t *l3p;
- size_t l2_len;
- struct ip *ip = NULL;
- struct udp *udp;
- #if MG_ENABLE_IPV6
- struct ip6 *ip6 = NULL;
- if (ip_dst->is_ip6) {
- ip6 = tx_ip6(ifp, l2_dst, 17, dscp, ip_src->addr.ip6, ip_dst->addr.ip6,
- len + sizeof(struct udp));
- udp = (struct udp *) (ip6 + 1);
- l2_len = sizeof(*ip6) + sizeof(*udp) + len;
- l3p = (uint8_t *) ip6;
- } else
- #endif
- {
- ip = tx_ip(ifp, l2_dst, 17, dscp, ip_src->addr.ip4, ip_dst->addr.ip4,
- len + sizeof(struct udp));
- udp = (struct udp *) (ip + 1);
- l2_len = sizeof(*ip) + sizeof(*udp) + len;
- l3p = (uint8_t *) ip;
- }
- udp->sport = ip_src->port;
- udp->dport = ip_dst->port;
- udp->len = mg_htons((uint16_t) (sizeof(*udp) + len));
- udp->csum = 0;
- memmove(udp + 1, buf, len);
- #if MG_ENABLE_IPV6
- if (ip_dst->is_ip6) {
- udp->csum = p6csum(ip6, udp, sizeof(*udp) + len);
- } else
- #endif
- {
- udp->csum = pcsum(ip, udp, sizeof(*udp) + len);
- }
- l2_len = mg_l2_trailer(ifp, l2_len, l3p + l2_len);
- return (driver_output(ifp, l2_len) == l2_len);
- }
- static bool tx_udp4(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint32_t ip_src,
- uint16_t sport, uint32_t ip_dst, uint16_t dport,
- const void *buf, size_t len) {
- struct mg_addr ips, ipd;
- memset(&ips, 0, sizeof(ips));
- ips.addr.ip4 = ip_src;
- ips.port = sport;
- memset(&ipd, 0, sizeof(ipd));
- ipd.addr.ip4 = ip_dst;
- ipd.port = dport;
- return tx_udp(ifp, l2_dst, &ips, &ipd, 0, buf, len);
- }
- static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint32_t ip_src,
- uint32_t ip_dst, uint8_t *opts, size_t optslen,
- bool ciaddr) {
- // https://datatracker.ietf.org/doc/html/rfc2132#section-9.6
- // NOTE(): assumes Ethernet: htype=1 hlen=6, copy 6 bytes
- 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, 6);
- memcpy(&dhcp.xid, ifp->mac + 2, sizeof(dhcp.xid));
- memcpy(&dhcp.options, opts, optslen);
- if (ciaddr) dhcp.ciaddr = ip_src;
- tx_udp4(ifp, l2_dst, ip_src, mg_htons(68), ip_dst, mg_htons(67), &dhcp,
- sizeof(dhcp));
- }
- // RFC-2131 #4.3.6, #4.4.1; RFC-2132 #9.8
- static void tx_dhcp_request_sel(struct mg_tcpip_if *ifp, uint32_t ip_req,
- uint32_t ip_srv) {
- uint8_t extra = (uint8_t) ((ifp->enable_req_dns ? 1 : 0) +
- (ifp->enable_req_sntp ? 1 : 0));
- size_t len = strlen(ifp->dhcp_name);
- size_t olen = 21 + len + extra + 2 + 1; // Total length of options
- #define OPTS_MAXLEN (21 + sizeof(ifp->dhcp_name) + 2 + 2 + 1)
- uint8_t opts[OPTS_MAXLEN]; // Allocate options (max size possible)
- uint8_t *p = opts;
- assert(olen <= sizeof(opts));
- memset(opts, 0, sizeof(opts));
- *p++ = 53, *p++ = 1, *p++ = 3; // Type: DHCP request
- *p++ = 54, *p++ = 4, memcpy(p, &ip_srv, 4), p += 4; // DHCP server ID
- *p++ = 50, *p++ = 4, memcpy(p, &ip_req, 4), p += 4; // Requested IP
- *p++ = 12, *p++ = (uint8_t) (len & 255); // DHCP host
- memcpy(p, ifp->dhcp_name, len), p += len; // name
- *p++ = 55, *p++ = 2 + extra, *p++ = 1, *p++ = 3; // GW, MASK
- if (ifp->enable_req_dns) *p++ = 6; // DNS
- if (ifp->enable_req_sntp) *p++ = 42; // SNTP
- *p++ = 255; // End of options
- // assert((size_t) (p - opts) < olen);
- tx_dhcp(ifp, mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_BCAST, NULL), 0,
- 0xffffffff, opts, olen, 0);
- MG_DEBUG(("DHCP req sent"));
- }
- // RFC-2131 #4.3.6, #4.4.5 (renewing: unicast, rebinding: bcast)
- static void tx_dhcp_request_re(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
- uint32_t ip_src, uint32_t ip_dst) {
- uint8_t opts[] = {
- 53, 1, 3, // Type: DHCP request
- 255 // End of options
- };
- tx_dhcp(ifp, l2_dst, ip_src, ip_dst, opts, sizeof(opts), true);
- MG_DEBUG(("DHCP req sent"));
- }
- static void tx_dhcp_discover(struct mg_tcpip_if *ifp) {
- uint8_t opts[] = {
- 53, 1, 1, // Type: DHCP discover
- 55, 2, 1, 3, // Parameters: ip, mask
- 255 // End of options
- };
- tx_dhcp(ifp, mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_BCAST, NULL), 0,
- 0xffffffff, opts, sizeof(opts), false);
- MG_DEBUG(("DHCP discover sent. Our MAC: %M", mg_print_mac, ifp->mac));
- }
- 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_arplooking && pkt->arp && pkt->arp->spa == c->rem.addr.ip4) break;
- #if MG_ENABLE_IPV6
- if (c->is_arplooking && pkt->icmp6 && pkt->icmp6->type == 136) {
- struct ndp_na *na = (struct ndp_na *) (pkt->icmp6 + 1);
- if (MG_IP6MATCH(na->addr, c->rem.addr.ip6)) break;
- }
- #endif
- if (c->is_udp && pkt->udp && c->loc.port == pkt->udp->dport &&
- !(c->loc.is_ip6 ^ (pkt->ip6 != NULL))) // IP or IPv6 to same dest
- break;
- if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport &&
- ((lsn && c->is_listening && !(c->loc.is_ip6 ^ (pkt->ip6 != NULL))) ||
- (!lsn && !c->is_listening && c->rem.port == pkt->tcp->sport &&
- ((!c->loc.is_ip6 && c->rem.addr.ip4 == pkt->ip->src)
- #if MG_ENABLE_IPV6
- || (c->loc.is_ip6 && MG_IP6MATCH(c->rem.addr.ip6, pkt->ip6->src))
- #endif
- )))) // validate addr for established (not listening) conns
- break;
- }
- return c;
- }
- static void l2addr_resolved(struct mg_connection *c);
- static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
- bool is_udp, struct pkt *pkt);
- // RFC826, ARP assumes Ethernet MAC addresses
- 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_VERBOSE(("ARP req from %M", mg_print_ip4, &pkt->arp->spa));
- uint8_t *l2p = (uint8_t *) ifp->tx.buf;
- struct arp *arp = (struct arp *) mg_l2_header(
- ifp, MG_TCPIP_L2PROTO_ARP, ifp->mac, mg_l2_getaddr(ifp, pkt->l2), l2p);
- *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_mac,
- ifp->mac));
- driver_output(ifp, mg_l2_trailer(ifp, sizeof(*arp), (uint8_t *) (arp + 1)));
- } else if (pkt->arp->op == mg_htons(2)) {
- if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return;
- // MG_VERBOSE(("ARP resp from %M", mg_print_ip4, &pkt->arp->spa));
- if (pkt->arp->spa == ifp->gw) {
- // Got response for the GW ARP request. Set ifp->gwmac and IP -> READY
- memcpy(ifp->gwmac, pkt->arp->sha, sizeof(ifp->gwmac));
- ifp->gw_ready = true;
- if (ifp->state == MG_TCPIP_STATE_IP) {
- ifp->state = MG_TCPIP_STATE_READY;
- onstatechange(ifp);
- }
- } 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.addr.ip4, mg_print_mac, s->mac));
- c->is_arplooking = 0;
- l2addr_resolved(c);
- }
- }
- }
- }
- static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- size_t plen = pkt->pay.len;
- if (!icmpcsum_ok(pkt->icmp, sizeof(struct icmp) + plen)) return;
- if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) {
- size_t l2_max_overhead = ifp->framesize - ifp->l2mtu;
- size_t hlen = sizeof(struct ip) + sizeof(struct icmp);
- size_t room = ifp->tx.len - hlen - l2_max_overhead;
- uint8_t *l2addr;
- struct ip *ip;
- struct icmp *icmp;
- struct mg_addr ips;
- ips.addr.ip4 = pkt->ip->src;
- ips.is_ip6 = false;
- if ((l2addr = get_return_l2addr(ifp, &ips, false, pkt)) == NULL)
- return; // safety net for lousy networks
- if (plen > room) plen = room;
- ip = tx_ip(ifp, l2addr, 1, 0, ifp->ip, pkt->ip->src, sizeof(*icmp) + plen);
- icmp = (struct icmp *) (ip + 1);
- memset(icmp, 0, sizeof(*icmp)); // Set csum, type, code to 0
- memcpy(icmp + 1, pkt->pay.buf, plen); // Copy RX payload to TX
- icmp->csum = ipcsum(icmp, sizeof(*icmp) + plen);
- driver_output(
- ifp, mg_l2_trailer(ifp, hlen + plen, (uint8_t *) ip + hlen + plen));
- }
- }
- static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip);
- static bool dhcp_opt_len_ok(uint8_t len, uint8_t *p, uint8_t *end) {
- return (len >= 4 && (len & 3) == 0 && p + 6 < end);
- }
- static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- uint32_t ip = 0, gw = 0, mask = 0, lease = 0, dns = 0, sntp = 0, owner = 0;
- uint8_t msgtype = 0, state = ifp->state;
- // perform size check first, then access fields
- uint8_t *p = (uint8_t *) pkt->pay.buf,
- *end = (uint8_t *) &pkt->pay.buf[pkt->pay.len];
- // min header length checked at payload calculation, options are optional
- if (memcmp(&pkt->dhcp->xid, ifp->mac + 2, sizeof(pkt->dhcp->xid))) return;
- while (p < end && p[0] != 255) { // RFC-2132 9
- if (p[0] == 0) { p++; continue; } // Pad
- if ((size_t) (end - p) < 2 || (size_t) (end - p) < 2U + p[1]) break;
- if (p[0] == 1) { // Mask, 3.3
- if (p[1] != 4) break;
- memcpy(&mask, p + 2, sizeof(mask));
- } else if (p[0] == 3) { // GW, 3.5
- if (!dhcp_opt_len_ok(p[1], p, end)) break;
- memcpy(&gw, p + 2, sizeof(gw));
- ip = pkt->dhcp->yiaddr;
- } else if (ifp->enable_req_dns && p[0] == 6) { // DNS, 3.8
- if (!dhcp_opt_len_ok(p[1], p, end)) break;
- memcpy(&dns, p + 2, sizeof(dns));
- } else if (ifp->enable_req_sntp && p[0] == 42) { // SNTP, 8.3
- if (!dhcp_opt_len_ok(p[1], p, end)) break;
- memcpy(&sntp, p + 2, sizeof(sntp));
- } else if (p[0] == 51) { // Lease
- if (p[1] != 4) break;
- memcpy(&lease, p + 2, sizeof(lease));
- lease = mg_ntohl(lease);
- } else if (p[0] == 53) { // Msg Type
- if (p[1] != 1) break;
- msgtype = p[2];
- } else if (p[0] == 54) { // Server id 9.7
- if (p[1] != 4) break;
- memcpy(&owner, p + 2, sizeof(sntp)); // This is the lease owner
- }
- p += p[1] + 2;
- }
- // Process message type, RFC-1533 (9.4); RFC-2131 (3.1, 4)
- if (msgtype == 6 && ifp->ip == ip) { // DHCPNACK, release IP
- ifp->state = MG_TCPIP_STATE_UP, ifp->ip = 0;
- } else if (msgtype == 2 && ifp->state == MG_TCPIP_STATE_UP && ip && gw &&
- lease) { // DHCPOFFER
- // select a server (2131 4.4.1, 2132 9.7): lease owner takes precedence,
- // otherwise use siaddr (fallback to IP source addr on foul play).
- // This is broadcast, otherwise siaddr would be the destination.
- tx_dhcp_request_sel(ifp, ip,
- owner ? owner
- : pkt->dhcp->siaddr ? pkt->dhcp->siaddr
- : pkt->ip->src);
- ifp->state = MG_TCPIP_STATE_REQ; // REQUESTING state
- } else if (msgtype == 5) { // DHCPACK
- if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP
- uint64_t rand;
- ifp->lease_expire = ifp->now + (uint64_t) lease * 1000;
- MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000));
- // assume DHCP server = router until ARP resolves
- memcpy(ifp->gwmac, mg_l2_getaddr(ifp, pkt->l2), sizeof(ifp->gwmac));
- ifp->gw_ready = true; // NOTE(): actual gw ARP won't retry now
- if (ifp->ip != ip) ifp->is_ip_changed = true;
- ifp->ip = ip, ifp->gw = gw, ifp->mask = mask;
- ifp->state = MG_TCPIP_STATE_IP; // BOUND state
- mg_random(&rand, sizeof(rand));
- srand((unsigned int) (rand + mg_millis()));
- if (ifp->enable_req_dns && dns != 0) {
- setdns4(ifp, &dns);
- mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_DNS, &dns);
- }
- if (ifp->enable_req_sntp && sntp != 0)
- mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_SNTP, &sntp);
- } else if (ifp->state == MG_TCPIP_STATE_READY && ifp->ip == ip) { // renew
- ifp->lease_expire = ifp->now + (uint64_t) lease * 1000;
- MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000));
- } // TODO(): accept provided T1/T2 and store server IP for renewal (4.4)
- }
- if (ifp->state != state) onstatechange(ifp);
- }
- // 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 *mac;
- uint8_t op = 0, *p = (uint8_t *) pkt->pay.buf,
- *end = (uint8_t *) &pkt->pay.buf[pkt->pay.len];
- // NOTE(): assumes Ethernet: htype=1 hlen=6, copy 6 bytes
- struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
- // min header length checked at payload calculation, options are optional
- res.yiaddr = ifp->ip;
- ((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1
- while (p < end && p[0] != 255) { // Parse options
- if (p[0] == 0) { p++; continue; } // Pad
- if ((size_t) (end - p) < 2 || (size_t) (end - p) < 2U + p[1]) break;
- if (p[0] == 53) { // Message type
- if (p[1] != 1) break;
- 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, 0, // 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
- };
- opts[2] = msg;
- 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;
- mac = mg_l2_getaddr(ifp, pkt->l2);
- if (ifp->enable_get_gateway) {
- ifp->gw = res.yiaddr; // set gw IP, best-effort gwmac as DHCP server's
- memcpy(ifp->gwmac, mac, sizeof(ifp->gwmac));
- }
- tx_udp4(ifp, mac, ifp->ip, mg_htons(67), op == 1 ? ~0U : res.yiaddr,
- mg_htons(68), &res, sizeof(res));
- }
- }
- #if MG_ENABLE_IPV6
- static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
- uint8_t next, uint8_t dscp, uint64_t *ip_src,
- uint64_t *ip_dst, size_t plen) {
- // ifp->tx.buf is 8-bit aligned, keep other headers as pointers, see pkt
- uint8_t *l2p = (uint8_t *) ifp->tx.buf;
- struct ip6 *ip6 = (struct ip6 *) mg_l2_header(ifp, MG_TCPIP_L2PROTO_IPV6,
- ifp->mac, l2_dst, l2p);
- memset(ip6, 0, sizeof(*ip6));
- ip6->ver = (uint8_t) (0x60 | (dscp >> 2)); // Version 6, traffic class
- ip6->label[0] = (uint8_t) (dscp << 6);
- ip6->plen = mg_htons((uint16_t) plen);
- ip6->next = next;
- ip6->hops = 255; // NDP requires max
- ip6->src[0] = *ip_src++;
- ip6->src[1] = *ip_src;
- ip6->dst[0] = *ip_dst++;
- ip6->dst[1] = *ip_dst;
- return ip6;
- }
- static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint64_t *ip_src,
- uint64_t *ip_dst, uint8_t type, uint8_t code,
- const void *buf, size_t len) {
- struct ip6 *ip6;
- struct icmp6 *icmp6;
- ip6 = tx_ip6(ifp, l2_dst, 58, 0, ip_src, ip_dst, sizeof(*icmp6) + len);
- icmp6 = (struct icmp6 *) (ip6 + 1);
- memset(icmp6, 0, sizeof(*icmp6)); // Set csum to 0
- icmp6->type = type;
- icmp6->code = code;
- memcpy(icmp6 + 1, buf, len); // Copy payload
- icmp6->csum = 0; // RFC-4443 2.3, RFC-8200 8.1
- icmp6->csum = p6csum(ip6, icmp6, sizeof(*icmp6) + len);
- driver_output(ifp,
- mg_l2_trailer(ifp, sizeof(*ip6) + sizeof(*icmp6) + len,
- (uint8_t *) (ip6 + 1) + sizeof(*icmp6) + len));
- }
- // Neighbor Discovery Protocol, RFC-4861
- // Neighbor Advertisement, 4.4
- static void tx_ndp_na(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
- uint64_t *ip_src, uint64_t *ip_dst, bool solicited,
- uint8_t *l2) {
- uint8_t data[20 + 16]; // NOTE(): optional len upto 2 hw addr
- memset(data, 0, sizeof(data));
- data[0] = solicited ? 0x60 : 0x20; // O + S
- memcpy(data + 4, ip_src, 16); // Target address
- data[20] = 2; // 4.6.1, target hwaddr
- data[21] = mg_l2_ip6put(ifp->l2type, l2, data + 22); // option length / 8
- tx_icmp6(ifp, l2_dst, ip_src, ip_dst, 136, 0, data,
- 20 + (size_t) (8 * data[21]));
- }
- static void onstate6change(struct mg_tcpip_if *ifp);
- static void rx_ndp_na(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- struct ndp_na *na = (struct ndp_na *) pkt->pay.buf;
- uint8_t *opts = (uint8_t *) (na + 1),
- *endp = opts + pkt->pay.len - sizeof(*na);
- if (pkt->pay.len < (sizeof(*na) + 2)) return; // first 2 bytes in opts
- if ((na->res[0] & 0x40) == 0) return; // not "solicited"
- if (*opts++ != 2) return; // no target hwaddr, must have
- MG_VERBOSE(("NDP NA resp from %M", mg_print_ip6, (char *) &na->addr));
- if (MG_IP6MATCH(na->addr, ifp->gw6)) {
- // Got response for the GW NS request. Set ifp->gw6mac and IP6 -> READY
- uint8_t len = *opts++; // check valid hwaddr and get it
- if ((opts + 8 * len - 2) > endp) return; // truncated
- if (!mg_l2_ip6get(ifp->l2type, ifp->gw6mac, opts, len)) return;
- ifp->gw6_ready = true;
- if (ifp->state6 == MG_TCPIP_STATE_IP) {
- ifp->state6 = MG_TCPIP_STATE_READY;
- onstate6change(ifp);
- }
- } else {
- struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
- if (c != NULL && c->is_arplooking) {
- struct connstate *s = (struct connstate *) (c + 1);
- uint8_t len = *opts++; // check valid hwaddr and get it
- if ((opts + 8 * len - 2) > endp) return; // truncated
- if (!mg_l2_ip6get(ifp->l2type, s->mac, opts, len)) return;
- MG_DEBUG(("%lu NDP resolved %M -> %M", c->id, mg_print_ip6,
- &c->rem.addr.ip6, mg_print_l2addr, ifp->l2type, s->mac));
- c->is_arplooking = 0;
- l2addr_resolved(c);
- }
- }
- }
- // Neighbor Solicitation, 4.3
- static void rx_ndp_ns(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- struct ndp_na *ns = (struct ndp_na *) pkt->pay.buf; // struct ndp_ns = ndp_na
- uint64_t target[2];
- if (pkt->pay.len < (sizeof(*ns) + 2)) return; // first 2 bytes in opts
- memcpy(target, ns->addr, sizeof(target));
- if (MG_IP6MATCH(target, ifp->ip6ll) || MG_IP6MATCH(target, ifp->ip6)) {
- uint64_t req[2]; // requester address
- uint8_t l2[sizeof(struct mg_l2addr)];
- uint8_t len, *opts = (uint8_t *) (ns + 1),
- *endp = opts + pkt->pay.len - sizeof(*ns);
- if (*opts++ != 1) return; // no requester hwaddr (source)
- len = *opts++; // check valid hwaddr and get it
- if ((opts + 8 * len - 2) > endp) return; // truncated
- if (!mg_l2_ip6get(ifp->l2type, l2, opts, len)) return;
- req[0] = pkt->ip6->src[0], req[1] = pkt->ip6->src[1]; // align to 64-bit
- tx_ndp_na(ifp, l2, target, req, true, ifp->mac);
- }
- }
- // - use solicited node multicast to resolve a l2 address (l2_addr = NULL)
- // - use unicast to verify presence (l2_addr = neighbor l2 address)
- static void tx_ndp_ns(struct mg_tcpip_if *ifp, uint64_t *ip_dst,
- uint8_t *l2_addr) {
- uint8_t payload[4 + 16 + 16]; // NOTE(): 16 --> optional len upto 2 hw addr
- uint64_t ip_unspec[2] = {0, 0};
- size_t payload_len = 20;
- bool mcast = (l2_addr == NULL);
- uint64_t ip_mcast[2] = {0, 0};
- uint8_t *l2 = l2_addr;
- if (ifp->l2type == MG_TCPIP_L2_PPP || ifp->l2type == MG_TCPIP_L2_PPPoE) {
- MG_DEBUG(("SKIP NS for %M", mg_print_ip6, ip_dst));
- return;
- }
- memset(payload, 0, sizeof(payload));
- memcpy(payload + 4, ip_dst, 16);
- if (mcast) {
- struct mg_addr ipd;
- ip6sn(ip_dst, ip_mcast);
- ipd.addr.ip6[0] = ip_mcast[0], ipd.addr.ip6[1] = ip_mcast[1],
- ipd.is_ip6 = true;
- l2 = mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_MCAST6, &ipd);
- }
- payload_len = 20;
- // TODO(robertc2000): using only link-local IP addr for now
- // We might consider to add an option to use either link-local or global IP
- if (!MG_IP6MATCH(ifp->ip6ll, ip_unspec)) {
- payload[20] = 1; // 4.6.1, source hwaddr; option length in 8-byte units
- payload[21] = mg_l2_ip6put(ifp->l2type, ifp->mac, payload + 22);
- payload_len += 8 * payload[21];
- }
- tx_icmp6(ifp, l2, ifp->ip6ll, mcast ? ip_mcast : ip_dst, 135, 0, payload,
- payload_len);
- }
- // Router Solicitation, 4.1
- static void tx_ndp_rs(struct mg_tcpip_if *ifp) {
- uint8_t payload[4 + 16]; // reserved + optional len upto 2 hw addr NOTE()
- size_t payload_len = 4;
- uint64_t ip_unspec[2] = {0, 0};
- memset(payload, 0, sizeof(payload));
- if (!MG_IP6MATCH(ifp->ip6ll, ip_unspec)) {
- payload[4] = 1; // 4.6.1, source hwaddr; option length in 8-byte units
- payload[5] = mg_l2_ip6put(ifp->l2type, ifp->mac, payload + 6);
- payload_len += 8 * payload[5];
- }
- tx_icmp6(ifp,
- mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_MCAST6,
- (struct mg_addr *) &ip6_allrouters),
- ifp->ip6ll, (uint64_t *) ip6_allrouters.addr.ip6, 133, 0, payload,
- payload_len);
- MG_DEBUG(("NDP Router Solicitation sent"));
- }
- static void fill_prefix(uint8_t *dst, uint8_t *src, uint8_t len) {
- uint8_t full = len / 8;
- uint8_t rem = len % 8;
- if (full > 0) memcpy(dst, src, full);
- if (rem > 0) {
- uint8_t mask = (uint8_t) (0xFF << (8 - rem));
- dst[full] |= src[full] & mask; // mg_l2_genip6() zeroes dst
- }
- }
- static bool match_prefix(uint8_t *newp, uint8_t *curp, uint8_t len) {
- uint8_t full = len / 8;
- uint8_t rem = len % 8;
- if (full > 0 && memcmp(curp, newp, full) != 0) return false;
- if (rem > 0) {
- uint8_t mask = (uint8_t) (0xFF << (8 - rem));
- if (curp[full] != (newp[full] & mask)) return false;
- }
- return true;
- }
- static bool fill_global(struct mg_tcpip_if *ifp, uint8_t *prefix,
- uint8_t prefix_len) {
- if (!mg_l2_genip6(ifp->l2type, ifp->ip6, prefix_len, ifp->mac)) return false;
- fill_prefix((uint8_t *) ifp->ip6, prefix, prefix_len);
- fill_prefix(ifp->prefix, prefix, prefix_len);
- ifp->prefix_len = prefix_len;
- return true;
- }
- // Router Advertisement, 4.2
- static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- struct ndp_ra *ra = (struct ndp_ra *) pkt->pay.buf;
- uint8_t *opts = (uint8_t *) (ra + 1);
- size_t opt_left = pkt->pay.len - sizeof(*ra);
- bool gotl2addr = false, gotprefix = false, changed = false;
- uint8_t l2[sizeof(struct mg_l2addr)];
- uint32_t mtu = 0;
- uint8_t *prefix = NULL, prefix_len = 0;
- if (pkt->pay.len < sizeof(*ra)) return;
- if (ifp->state6 == MG_TCPIP_STATE_UP) {
- MG_DEBUG(("Received NDP RA")); // fill gw6 address
- // parse options
- while (opt_left >= 2) {
- uint8_t type = opts[0], len = opts[1];
- size_t length = (size_t) len * 8;
- if (length == 0 || length > opt_left) break; // malformed
- if (type == 1 && length >= 8) {
- // Received router's L2 address
- if (!mg_l2_ip6get(ifp->l2type, l2, opts + 2, len)) break;
- gotl2addr = true;
- } else if (type == 5 && length >= 8) {
- // process MTU if available, ignore if it smells
- mtu = MG_LOAD_BE32(opts + 4);
- if (mtu < 1280 || mtu > ifp->l2mtu) mtu = 0; // RFC-8200, minimum MTU
- } else if (type == 3 && length >= 32) {
- // process prefix, 4.6.2, ignore if it smells
- uint8_t pfx_flags = opts[3]; // L=0x80, A=0x40
- uint32_t valid = MG_LOAD_BE32(opts + 4);
- uint32_t pref_lifetime = MG_LOAD_BE32(opts + 8);
- prefix_len = opts[2];
- if (prefix_len >= 128) break;
- prefix = opts + 16;
- // TODO (robertc2000): handle prefix options if necessary
- (void) pfx_flags;
- (void) valid;
- (void) pref_lifetime;
- gotprefix = true;
- if (prefix_len != ifp->prefix_len ||
- !match_prefix(prefix, ifp->prefix, ifp->prefix_len))
- changed = true;
- }
- opts += length;
- opt_left -= length;
- }
- // fill prefix and global
- if (gotprefix && !fill_global(ifp, prefix, prefix_len)) return;
- if (changed) ifp->is_ip6_changed = true;
- ifp->gw6[0] = pkt->ip6->src[0], ifp->gw6[1] = pkt->ip6->src[1];
- if (gotl2addr) memcpy(ifp->gw6mac, l2, sizeof(ifp->gw6mac));
- if (gotl2addr || ifp->l2type == MG_TCPIP_L2_PPP ||
- ifp->l2type == MG_TCPIP_L2_PPPoE) {
- ifp->state6 = MG_TCPIP_STATE_READY;
- ifp->gw6_ready = true;
- }
- if (mtu != 0 && ifp->mtu != mtu) {
- MG_ERROR(
- ("got an MTU: %u, that differs from the configured one. "
- "All devices in an IPv6 network should have the same MTU, "
- "using the router's instead...",
- mtu));
- ifp->mtu = (uint16_t) mtu;
- }
- if (ifp->state6 != MG_TCPIP_STATE_READY) {
- tx_ndp_ns(ifp, ifp->gw6, NULL); // unsolicited GW hwaddr resolution
- ifp->state6 = MG_TCPIP_STATE_IP;
- }
- onstate6change(ifp);
- }
- }
- static void rx_icmp6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- if (!icmp6csum_ok(pkt->ip6, pkt->icmp6)) return;
- switch (pkt->icmp6->type) {
- case 128: { // Echo Request, RFC-4443 4.1
- uint64_t target[2];
- target[0] = pkt->ip6->dst[0], target[1] = pkt->ip6->dst[1];
- if (MG_IP6MATCH(target, ifp->ip6ll) || MG_IP6MATCH(target, ifp->ip6)) {
- size_t l2_max_overhead = ifp->framesize - ifp->l2mtu;
- size_t hlen = sizeof(struct ip6) + sizeof(struct icmp6);
- size_t room = ifp->tx.len - hlen - l2_max_overhead, plen = pkt->pay.len;
- struct mg_addr ips;
- uint8_t *l2addr;
- ips.addr.ip6[0] = pkt->ip6->src[0], ips.addr.ip6[1] = pkt->ip6->src[1];
- ips.is_ip6 = true;
- if ((l2addr = get_return_l2addr(ifp, &ips, false, pkt)) == NULL)
- return; // safety net for lousy networks
- if (plen > room) plen = room; // Copy (truncated) RX payload to TX
- // Echo Reply, 4.2
- tx_icmp6(ifp, l2addr, target, ips.addr.ip6, 129, 0, pkt->pay.buf, plen);
- }
- } break;
- case 134: // Router Advertisement
- rx_ndp_ra(ifp, pkt);
- break;
- case 135: // Neighbor Solicitation
- rx_ndp_ns(ifp, pkt);
- break;
- case 136: // Neighbor Advertisement
- rx_ndp_na(ifp, pkt);
- break;
- default:
- if (mg_log_level >= MG_LL_VERBOSE)
- mg_hexdump(pkt->icmp6, pkt->pay.len > 16 ? 16 : pkt->pay.len);
- break;
- }
- }
- static void onstate6change(struct mg_tcpip_if *ifp) {
- if (ifp->state6 == MG_TCPIP_STATE_READY) {
- MG_INFO(("READY, IP: %M", mg_print_ip6, &ifp->ip6));
- MG_INFO((" GW: %M", mg_print_ip6, &ifp->gw6));
- if (ifp->l2type == MG_TCPIP_L2_ETH) // TODO(): print other l2
- MG_INFO((" MAC: %M", mg_print_mac, &ifp->mac));
- if (ifp->l2type == MG_TCPIP_L2_ETH) { // gratuitous NA annc
- tx_ndp_na(ifp, // RFC-4861 7.2.6
- mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_MCAST6,
- (struct mg_addr *) &ip6_allnodes),
- ifp->ip6ll, (uint64_t *) ip6_allnodes.addr.ip6, false,
- ifp->mac);
- if (ifp->ip6[0] != 0 && ifp->ip6[1] != 0)
- tx_ndp_na(ifp, // RFC-9131 4.1
- mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_MCAST6,
- (struct mg_addr *) &ip6_allrouters),
- ifp->ip6, (uint64_t *) ip6_allrouters.addr.ip6, false,
- ifp->mac);
- }
- if (ifp->is_ip6_changed) {
- struct mg_connection *c;
- for (c = ifp->mgr->conns; c != NULL; c = c->next) {
- if (!c->is_listening && !c->is_udp) c->is_closing = 1;
- }
- ifp->is_ip6_changed = false;
- }
- } else if (ifp->state6 == MG_TCPIP_STATE_IP) {
- if ((ifp->gw6[0] != 0 || ifp->gw6[1] != 0) &&
- (ifp->l2type != MG_TCPIP_L2_PPP && ifp->l2type != MG_TCPIP_L2_PPPoE))
- tx_ndp_ns(ifp, ifp->gw6, NULL); // unsolicited GW hwaddr resolution
- } else if (ifp->state6 == MG_TCPIP_STATE_UP) {
- MG_INFO(("IP: %M", mg_print_ip6, &ifp->ip6ll));
- }
- if (ifp->state6 > MG_TCPIP_STATE_UP)
- mg_tcpip_call(ifp, MG_TCPIP_EV_STATE6_CHANGE, &ifp->state6);
- }
- #endif
- static uint8_t *tcpip_mapip(struct mg_tcpip_if *ifp, struct mg_addr *ip) {
- #if MG_ENABLE_IPV6
- if (ip->is_ip6) {
- if (MG_IP6MATCH(ip->addr.ip6, ip6_allnodes.addr.ip6)) // local broadcast
- return mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_MCAST6,
- (struct mg_addr *) &ip6_allnodes);
- if (*ip->addr.ip == 0xFF) // multicast
- return mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_MCAST6, ip);
- } else
- #endif
- { // global/local broadcast
- if (ip->addr.ip4 == 0xffffffff || ip->addr.ip4 == (ifp->ip | ~ifp->mask))
- return mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_BCAST, NULL);
- if ((*ip->addr.ip & 0xE0) == 0xE0) // 224 ~ 239 = E0 ~ EF, multicast
- return mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_MCAST, ip);
- }
- return NULL;
- }
- static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
- bool is_udp, struct pkt *pkt) {
- uint8_t *l2addr;
- if (is_udp && (l2addr = tcpip_mapip(ifp, rem)) != NULL)
- return l2addr; // broadcast or multicast
- #if MG_ENABLE_IPV6
- if (rem->is_ip6) {
- if (rem->addr.ip6[0] == ifp->ip6ll[0] ||
- match_prefix((uint8_t *) rem->addr.ip6, ifp->prefix, ifp->prefix_len))
- return mg_l2_getaddr(ifp, pkt->l2); // same LAN, get from frame
- if (ifp->gw6_ready) // use the router
- return ifp->gw6mac; // ignore source address in frame
- } else
- #endif
- {
- if (ifp->ip != 0 && ((rem->addr.ip4 & ifp->mask) == (ifp->ip & ifp->mask)))
- return mg_l2_getaddr(ifp, pkt->l2); // same LAN, get from frame
- if (ifp->gw_ready) // use the router
- return ifp->gwmac; // ignore source address in frame
- }
- MG_ERROR(("%M %s: No way back, can't respond", mg_print_ip_port, rem,
- is_udp ? "UDP" : "TCP"));
- return NULL;
- }
- static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
- struct connstate *s;
- uint8_t *l2addr;
- if (c == NULL) return false; // No UDP listener on this port
- s = (struct connstate *) (c + 1);
- c->rem.port = pkt->udp->sport;
- #if MG_ENABLE_IPV6
- if (c->loc.is_ip6) { // matching of v4/v6 to dest is done bt getpeer()
- if (!udp6csum_ok(pkt->ip6, pkt->udp)) return false;
- c->rem.addr.ip6[0] = pkt->ip6->src[0],
- c->rem.addr.ip6[1] = pkt->ip6->src[1], c->rem.is_ip6 = true;
- } else
- #endif
- {
- if (!udpcsum_ok(pkt->ip, pkt->udp)) return false;
- c->rem.addr.ip4 = pkt->ip->src;
- }
- if ((l2addr = get_return_l2addr(ifp, &c->rem, true, pkt)) == NULL)
- return false; // safety net for lousy networks
- memcpy(s->mac, l2addr, sizeof(s->mac));
- 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.buf, pkt->pay.len);
- c->recv.len += pkt->pay.len;
- mg_call(c, MG_EV_READ, &pkt->pay.len);
- }
- return true;
- }
- static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
- struct mg_addr *ip_src, struct mg_addr *ip_dst,
- uint8_t dscp, uint8_t flags, uint32_t seq, uint32_t ack,
- const void *buf, size_t len) {
- uint8_t *l3p;
- struct ip *ip = NULL;
- struct tcp *tcp;
- uint16_t opts[4 / 2];
- size_t hlen = sizeof(*tcp);
- #if MG_ENABLE_IPV6
- struct ip6 *ip6 = NULL;
- #endif
- // Handle any options first, here, to determine header size
- if (flags & TH_SYN) { // Send MSS
- uint16_t mss;
- #if MG_ENABLE_IPV6 // RFC-9293 3.7.1; RFC-6691 2
- mss = (uint16_t) (ifp->mtu - 60);
- #else
- mss = (uint16_t) (ifp->mtu - 40);
- #endif
- opts[0] = mg_htons(0x0204); // RFC-9293 3.2
- opts[1] = mg_htons(mss);
- hlen += sizeof(opts); // always whole number of 32-bit words
- }
- #if MG_ENABLE_IPV6
- if (ip_dst->is_ip6) {
- ip6 = tx_ip6(ifp, l2_dst, 6, dscp, ip_src->addr.ip6, ip_dst->addr.ip6,
- hlen + len);
- tcp = (struct tcp *) (ip6 + 1);
- l3p = (uint8_t *) ip6;
- } else
- #endif
- {
- ip = tx_ip(ifp, l2_dst, 6, dscp, ip_src->addr.ip4, ip_dst->addr.ip4,
- hlen + len);
- tcp = (struct tcp *) (ip + 1);
- l3p = (uint8_t *) ip;
- }
- memset(tcp, 0, sizeof(*tcp));
- memmove(tcp + 1, opts, hlen - sizeof(*tcp)); // copy opts if any
- if (buf != NULL && len) memmove((uint8_t *) tcp + hlen, buf, len);
- tcp->sport = ip_src->port;
- tcp->dport = ip_dst->port;
- tcp->seq = seq;
- tcp->ack = ack;
- tcp->flags = flags;
- tcp->win = mg_htons(MG_TCPIP_WIN);
- tcp->off = (uint8_t) (hlen / 4 << 4);
- #if MG_ENABLE_IPV6
- if (ip_dst->is_ip6) {
- tcp->csum = p6csum(ip6, tcp, hlen + len);
- } else
- #endif
- {
- tcp->csum = pcsum(ip, tcp, hlen + len);
- }
- MG_VERBOSE(("TCP %M -> %M fl %x len %u", mg_print_ip_port, ip_src,
- mg_print_ip_port, ip_dst, tcp->flags, len));
- return driver_output(
- ifp, mg_l2_trailer(ifp, PDIFF(l3p, (uint8_t *) tcp + hlen + len),
- (uint8_t *) tcp + hlen + len));
- }
- static size_t tx_tcp_ctrlresp(struct mg_tcpip_if *ifp, struct pkt *pkt,
- uint8_t flags, uint32_t seqno) {
- uint32_t ackno = mg_htonl(mg_ntohl(pkt->tcp->seq) + (uint32_t) pkt->pay.len +
- ((pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0));
- struct mg_addr ips, ipd;
- uint8_t *l2addr;
- memset(&ips, 0, sizeof(ips));
- memset(&ipd, 0, sizeof(ipd));
- if (pkt->ip != NULL) {
- ips.addr.ip4 = pkt->ip->dst;
- ipd.addr.ip4 = pkt->ip->src;
- } else {
- ips.addr.ip6[0] = pkt->ip6->dst[0], ips.addr.ip6[1] = pkt->ip6->dst[1];
- ipd.addr.ip6[0] = pkt->ip6->src[0], ipd.addr.ip6[1] = pkt->ip6->src[1];
- ips.is_ip6 = true;
- ipd.is_ip6 = true;
- }
- ips.port = pkt->tcp->dport;
- ipd.port = pkt->tcp->sport;
- if ((l2addr = get_return_l2addr(ifp, &ipd, false, pkt)) == NULL)
- return 0; // safety net for lousy networks
- return tx_tcp(ifp, l2addr, &ips, &ipd, 0, flags, seqno, ackno, NULL, 0);
- }
- static size_t tx_tcp_rst(struct mg_tcpip_if *ifp, struct pkt *pkt, bool toack) {
- return tx_tcp_ctrlresp(ifp, pkt,
- (uint8_t) (toack ? TH_RST : (TH_RST | TH_ACK)),
- toack ? pkt->tcp->ack : 0);
- }
- static struct mg_connection *accept_conn(struct mg_connection *lsn,
- struct pkt *pkt, uint16_t mss) {
- struct connstate *s;
- uint8_t *l2addr;
- struct mg_connection *c = mg_alloc_conn(lsn->mgr);
- if (c == NULL) {
- MG_ERROR(("OOM"));
- return NULL;
- }
- s = (struct connstate *) (c + 1);
- s->dmss = mss; // from options in client SYN
- s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
- s->win = mg_ntohs(pkt->tcp->win), s->maxseq = (uint32_t) (s->seq + s->win);
- #if MG_ENABLE_IPV6
- if (lsn->loc.is_ip6) {
- c->rem.addr.ip6[0] = pkt->ip6->src[0],
- c->rem.addr.ip6[1] = pkt->ip6->src[1], c->rem.is_ip6 = true;
- c->loc.addr.ip6[0] = c->mgr->ifp->ip6[0],
- c->loc.addr.ip6[1] = c->mgr->ifp->ip6[1], c->loc.is_ip6 = true;
- // TODO(): compare lsn to link-local, or rem as link-local: use ll instead
- } else
- #endif
- {
- c->rem.addr.ip4 = pkt->ip->src;
- c->loc.addr.ip4 = c->mgr->ifp->ip;
- }
- c->rem.port = pkt->tcp->sport;
- c->loc.port = lsn->loc.port;
- if ((l2addr = get_return_l2addr(lsn->mgr->ifp, &c->rem, false, pkt)) ==
- NULL) {
- free(c); // safety net for lousy networks, not actually needed
- return NULL; // as path has already been checked at SYN (sending SYN+ACK)
- }
- memcpy(s->mac, l2addr, sizeof(s->mac));
- settmout(c, MIP_TTYPE_KEEPALIVE);
- 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->pfn_data = lsn->pfn_data;
- c->fn = lsn->fn;
- c->fn_data = lsn->fn_data;
- c->is_tls = lsn->is_tls;
- mg_call(c, MG_EV_OPEN, NULL);
- mg_call(c, MG_EV_ACCEPT, NULL);
- if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
- return c;
- }
- static size_t trim_len(struct mg_connection *c, size_t len) {
- struct mg_tcpip_if *ifp = c->mgr->ifp;
- size_t l2_max_overhead = ifp->framesize - ifp->l2mtu;
- size_t ip_max_h_len = c->rem.is_ip6 ? 40 : 24; // we don't send options
- size_t tcp_max_h_len = 60 /* RFC-9293 3.7.1; RFC-6691 2 */, udp_h_len = 8;
- size_t max_headers_len =
- ip_max_h_len + (c->is_udp ? udp_h_len : tcp_max_h_len);
- size_t min_mtu = c->rem.is_ip6 ? 1280 /* RFC-8200, IPv6 minimum */
- : c->is_udp ? 68 /* RFC-791, IP minimum */
- : max_headers_len /* fit full TCP header */;
- // NOTE(): We are effectively reducing transmitted TCP segment length by 20,
- // accounting for possible options; though we currently don't send options
- // except for SYN.
- // If the frame exceeds the available buffer, trim the length.
- if (len + max_headers_len + l2_max_overhead > ifp->tx.len)
- len = ifp->tx.len - max_headers_len - l2_max_overhead;
- // Ensure the MTU isn't lower than the minimum allowed value
- if (ifp->mtu < min_mtu) {
- MG_ERROR(("MTU is lower than minimum, raising to %lu", min_mtu));
- ifp->mtu = (uint16_t) min_mtu;
- }
- // If the total packet size exceeds the MTU, trim the length
- if (len + max_headers_len > ifp->mtu) {
- len = ifp->mtu - max_headers_len;
- if (c->is_udp) MG_ERROR(("UDP datagram exceeds MTU. Truncating it."));
- }
- return len;
- }
- static bool udp_send(struct mg_connection *c, const void *buf, size_t len) {
- struct mg_tcpip_if *ifp = c->mgr->ifp;
- struct connstate *s = (struct connstate *) (c + 1);
- struct mg_addr ips;
- memset(&ips, 0, sizeof(ips));
- #if MG_ENABLE_IPV6
- if (c->loc.is_ip6) {
- ips.addr.ip6[0] = ifp->ip6[0], ips.addr.ip6[1] = ifp->ip6[1],
- ips.is_ip6 = true;
- // TODO(): detect link-local (c->rem) and use it
- } else
- #endif
- {
- ips.addr.ip4 = ifp->ip;
- }
- ips.port = c->loc.port;
- return tx_udp(ifp, s->mac, &ips, &c->rem, c->dscp, buf, len);
- }
- long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
- struct connstate *s = (struct connstate *) (c + 1);
- len = trim_len(c, len);
- if (c->is_udp) {
- if (!udp_send(c, buf, len)) return MG_IO_WAIT;
- } else { // TCP, cap to peer's MSS and check window
- struct mg_tcpip_if *ifp = c->mgr->ifp;
- size_t sent;
- uint32_t room = s->maxseq - s->seq;
- if (room == 0) return MG_IO_WAIT;
- if (len > s->dmss) len = s->dmss; // RFC-6691: reduce if sending opts
- if ((uint32_t) len > room) len = room;
- sent = tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_PUSH | TH_ACK,
- mg_htonl(s->seq), mg_htonl(s->ack), buf, len);
- if (sent == 0) {
- return MG_IO_WAIT;
- } else if (sent == (size_t) -1) {
- return MG_IO_ERR;
- } else {
- s->seq += (uint32_t) len;
- if (s->ttype == MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_KEEPALIVE);
- }
- }
- return (long) len;
- }
- static void handle_tls_recv(struct mg_connection *c) {
- size_t avail = mg_tls_pending(c); // will change after mg_tls_recv()
- size_t min = avail > MG_MAX_RECV_SIZE ? MG_MAX_RECV_SIZE : avail;
- struct mg_iobuf *io = &c->recv; // allocated on first avail > 0
- if (io->size - io->len < min && !mg_iobuf_resize(io, io->len + min)) {
- mg_error(c, "oom");
- } else {
- // Decrypt data directly into c->recv. If io->buf = NULL or
- // io->len = io->size (no room), there can be outstanding data that can't be
- // moved (mg_tls_pending() > 0 after mg_tls_recv() returns). So
- // mg_tls_recv() takes care of returning 0 in this case (commented as "MIP")
- long n = mg_tls_recv(c, io->buf != NULL ? &io->buf[io->len] : io->buf,
- 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 n < 0: outstanding data to be moved to c->recv
- }
- }
- static void handle_ack(struct connstate *s, uint32_t ackno, uint16_t win) {
- if (ackno < (s->seq - s->win) || ackno > s->seq) return;
- s->maxseq = (uint32_t) (ackno + win);
- s->win = win;
- }
- 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 ? &c->rtls : &c->recv;
- uint32_t seq = mg_ntohl(pkt->tcp->seq);
- if (pkt->tcp->flags & TH_FIN) {
- uint8_t flags = TH_ACK;
- if (mg_ntohl(pkt->tcp->seq) != s->ack) {
- MG_VERBOSE(("ignoring FIN, %x != %x", mg_ntohl(pkt->tcp->seq), s->ack));
- tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
- mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
- return;
- }
- // If we initiated the closure, we reply with ACK upon receiving FIN
- // If we didn't initiate it, we reply with FIN as part of the normal TCP
- // closure process
- s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len + 1);
- s->fin_rcvd = true;
- if (c->is_draining && s->ttype == MIP_TTYPE_FIN) {
- if (s->seq == mg_htonl(pkt->tcp->ack)) { // Simultaneous closure ?
- s->seq++; // Yes. Increment our SEQ
- } else { // Otherwise,
- s->seq = mg_htonl(pkt->tcp->ack); // Set to peer's ACK
- }
- s->twclosure = true;
- } else {
- // Peer closed first: send ACK only, enter CLOSE_WAIT.
- // The connection loop will call init_closure after pending send data
- // is flushed, then send our FIN.
- c->is_draining = 1;
- }
- tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, flags,
- mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
- if (pkt->pay.len == 0) return; // if no data, we're done
- } else if (pkt->pay.len <= 1 && mg_ntohl(pkt->tcp->seq) == s->ack - 1) {
- // Keep-Alive (RFC-9293 3.8.4, allow erroneous implementations)
- MG_VERBOSE(("%lu keepalive ACK", c->id));
- tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
- mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
- return; // no data to process
- } else if (pkt->pay.len == 0) { // this is an ACK
- if (pkt->tcp->flags & TH_ACK)
- handle_ack(s, mg_ntohl(pkt->tcp->ack), mg_ntohs(pkt->tcp->win));
- if (s->fin_rcvd && s->ttype == MIP_TTYPE_FIN) s->twclosure = true;
- return; // no data to process
- } 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 {
- MG_VERBOSE(("SEQ != ACK: %x %x %x", seq, s->ack, ack));
- tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
- mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
- }
- return; // drop it
- } else if (io->size - io->len < pkt->pay.len &&
- !mg_iobuf_resize(io, io->len + pkt->pay.len)) {
- mg_error(c, "oom");
- return; // drop it
- }
- if (pkt->tcp->flags & TH_ACK)
- handle_ack(s, mg_ntohl(pkt->tcp->ack), mg_ntohs(pkt->tcp->win));
- // 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 c->rtls 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 c->rtls
- memcpy(&io->buf[io->len], pkt->pay.buf, pkt->pay.len);
- io->len += pkt->pay.len;
- MG_VERBOSE(("%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);
- s->unacked += pkt->pay.len;
- // size_t diff = s->acked <= s->ack ? s->ack - s->acked : s->ack;
- if (s->unacked > MG_TCPIP_WIN / 2 && s->acked != s->ack) {
- // Send ACK immediately
- MG_VERBOSE(("%lu imm ACK %lu", c->id, s->acked));
- tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
- mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
- s->unacked = 0;
- s->acked = s->ack;
- if (s->ttype != MIP_TTYPE_KEEPALIVE) settmout(c, MIP_TTYPE_KEEPALIVE);
- } else {
- // if not already running, setup a timer to send an ACK later
- if (s->ttype != MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_ACK);
- }
- if (c->is_tls) {
- c->is_tls_hs ? mg_tls_handshake(c) : handle_tls_recv(c);
- } else {
- // Plain text connection, data is already in c->recv, trigger MG_EV_READ
- mg_call(c, MG_EV_READ, &pkt->pay.len);
- }
- }
- // TCP backlog
- struct mg_backlog {
- uint16_t port, mss; // use port=0 for available entries
- uint8_t age;
- };
- static int backlog_insert(struct mg_connection *c, uint16_t port,
- uint16_t mss) {
- struct mg_backlog *p = (struct mg_backlog *) c->data;
- size_t i;
- for (i = 0; i < sizeof(c->data) / sizeof(*p); i++) {
- if (p[i].port != 0) continue;
- p[i].age = 2; // remove after two calls, average 1.5 call rate
- p[i].port = port, p[i].mss = mss;
- return (int) i;
- }
- return -1;
- }
- static struct mg_backlog *backlog_retrieve(struct mg_connection *c,
- uint16_t key, uint16_t port) {
- struct mg_backlog *p = (struct mg_backlog *) c->data;
- if (key >= sizeof(c->data) / sizeof(*p)) return NULL;
- if (p[key].port != port) return NULL;
- p += key;
- return p;
- }
- static void backlog_remove(struct mg_connection *c, uint16_t key) {
- struct mg_backlog *p = (struct mg_backlog *) c->data;
- p[key].port = 0;
- }
- static void backlog_maintain(struct mg_connection *c) {
- struct mg_backlog *p = (struct mg_backlog *) c->data;
- size_t i; // dec age and remove those where it reaches 0
- for (i = 0; i < sizeof(c->data) / sizeof(*p); i++) {
- if (p[i].port == 0) continue;
- if (p[i].age != 0) --p[i].age;
- if (p[i].age == 0) p[i].port = 0;
- }
- }
- static void backlog_poll(struct mg_mgr *mgr) {
- struct mg_connection *c = NULL;
- for (c = mgr->conns; c != NULL; c = c->next) {
- if (!c->is_udp && c->is_listening) backlog_maintain(c);
- }
- }
- // process options (MSS)
- static bool handle_opt(struct connstate *s, struct tcp *tcp, bool ip6) {
- uint8_t *opts = (uint8_t *) (tcp + 1);
- int len = 4 * ((int) (tcp->off >> 4) - ((int) sizeof(*tcp) / 4));
- s->dmss = ip6 ? 1220 : 536; // assume default, RFC-9293 3.7.1
- while (len > 0) { // RFC-9293 3.1 3.2
- uint8_t kind = opts[0], optlen = 1;
- if (kind != 1) { // No-Operation
- if (kind == 0) break; // End of Option List
- if (len < 2 || opts[1] == 0 || opts[1] > len) return false; // Malformed
- optlen = opts[1];
- if (kind == 2 && optlen == 4) // set received MSS
- s->dmss = (uint16_t) (((uint16_t) opts[2] << 8) + opts[3]);
- }
- MG_VERBOSE(("kind: %u, optlen: %u, len: %d\n", kind, optlen, len));
- opts += optlen;
- len -= optlen;
- }
- return true;
- }
- 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 MG_ENABLE_IPV6 // matching of v4/v6 to dest is done by getpeer()
- if (pkt->ip6 != NULL && !tcp6csum_ok(pkt->ip6, pkt->tcp)) return;
- #endif
- if (pkt->ip != NULL && !tcpcsum_ok(pkt->ip, pkt->tcp)) return;
- pkt->tcp->flags &= TH_STDFLAGS; // tolerate creative usage (ECN, ?)
- // Order is VERY important; RFC-9293 3.5.2
- // - check clients (Group 1) and established connections (Group 3)
- if (c != NULL && c->is_connecting && pkt->tcp->flags == (TH_SYN | TH_ACK)) {
- // client got a server connection accept
- if (!handle_opt(s, pkt->tcp, pkt->ip6 != NULL))
- return; // process options (MSS)
- s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1;
- s->win = mg_ntohs(pkt->tcp->win), s->maxseq = (uint32_t) (s->seq + s->win);
- tx_tcp_ctrlresp(ifp, pkt, TH_ACK, pkt->tcp->ack);
- c->is_connecting = 0; // Client connected
- settmout(c, MIP_TTYPE_KEEPALIVE);
- mg_call(c, MG_EV_CONNECT, NULL); // Let user know
- if (c->is_tls_hs) mg_tls_handshake(c);
- if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
- } else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) {
- mg_error(c, "connection refused");
- } else if (c != NULL && pkt->tcp->flags & TH_RST) {
- uint32_t seqno = mg_ntohl(pkt->tcp->seq);
- if (seqno >= s->ack && seqno < (s->ack + MG_TCPIP_WIN)) // RFC-9293 3.5.3
- mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
- } else if (c != NULL) {
- // process segment
- 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
- // - we don't listen on that port; RFC-9293 3.5.2 Group 1
- // - check listening connections; RFC-9293 3.5.2 Group 2
- if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) {
- // not listening on that port
- if (!(pkt->tcp->flags & TH_RST)) {
- tx_tcp_rst(ifp, pkt, pkt->tcp->flags & TH_ACK);
- } // else silently discard
- } else if (pkt->tcp->flags == TH_SYN) {
- // listener receives a connection request
- struct connstate cs; // At this point, s = NULL, there is no connection
- int key;
- uint32_t isn;
- if (pkt->tcp->sport != 0) {
- if (!handle_opt(&cs, pkt->tcp, pkt->ip6 != NULL))
- return; // process options (MSS)
- key = backlog_insert(c, pkt->tcp->sport,
- cs.dmss); // backlog options (MSS)
- if (key < 0) return; // no room in backlog, discard SYN, client retries
- // Use peer's src port and bl key as ISN, to later identify the
- // handshake
- isn = (mg_htonl(((uint32_t) key << 16) | mg_ntohs(pkt->tcp->sport)));
- if (tx_tcp_ctrlresp(ifp, pkt, TH_SYN | TH_ACK, isn) == 0)
- backlog_remove(c, (uint16_t) key); // safety net for lousy networks
- } // what should we do when port=0 ? Linux takes port 0 as any other
- // port
- } else if (pkt->tcp->flags == TH_ACK) {
- // listener receives an ACK
- struct mg_backlog *b = NULL;
- if ((uint16_t) (mg_htonl(pkt->tcp->ack) - 1) ==
- mg_htons(pkt->tcp->sport)) {
- uint16_t key = (uint16_t) ((mg_htonl(pkt->tcp->ack) - 1) >> 16);
- b = backlog_retrieve(c, key, pkt->tcp->sport);
- if (b != NULL) { // ACK is a response to a SYN+ACK
- accept_conn(c, pkt, b->mss); // pass options
- backlog_remove(c, key);
- } // else not an actual match, reset
- }
- if (b == NULL) tx_tcp_rst(ifp, pkt, true);
- } else if (pkt->tcp->flags & TH_RST) {
- // silently discard
- } else if (pkt->tcp->flags & TH_ACK) { // ACK + something else != RST
- tx_tcp_rst(ifp, pkt, true);
- } else if (pkt->tcp->flags & TH_SYN) { // SYN + something else != ACK
- tx_tcp_rst(ifp, pkt, false);
- } // else silently discard
- }
- static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- uint8_t ihl;
- uint16_t frag, len;
- if (pkt->pay.len < sizeof(*pkt->ip)) return; // Truncated
- if ((pkt->ip->ver >> 4) != 4) return; // Not IP
- ihl = pkt->ip->ver & 0x0F;
- if (ihl < 5) return; // bad IHL
- if (pkt->pay.len < (uint16_t) (ihl * 4)) return; // Truncated / malformed
- // There can be link padding, take length from IP header
- len = mg_ntohs(pkt->ip->len); // IP datagram length
- if (len < (uint16_t) (ihl * 4) || len > pkt->pay.len) return; // malformed
- pkt->pay.len = len; // strip padding
- mkpay(pkt, (uint32_t *) pkt->ip + ihl); // account for opts
- if (!ipcsum_ok(pkt->ip)) return;
- frag = mg_ntohs(pkt->ip->frag);
- if (frag & IP_MORE_FRAGS_MSK || frag & IP_FRAG_OFFSET_MSK) {
- struct mg_connection *c;
- if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->pay.buf);
- if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf);
- c = getpeer(ifp->mgr, pkt, false);
- if (c) mg_error(c, "Received fragmented packet");
- } else if (pkt->ip->proto == 1) {
- pkt->icmp = (struct icmp *) (pkt->pay.buf);
- 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->pay.buf);
- if (pkt->pay.len < sizeof(*pkt->udp)) return; // truncated
- // Take length from UDP header
- len = mg_ntohs(pkt->udp->len); // UDP datagram length
- if (len < sizeof(*pkt->udp) || len > pkt->pay.len) return; // malformed
- pkt->pay.len = len; // strip excess data
- mkpay(pkt, pkt->udp + 1);
- MG_VERBOSE(("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 (ifp->enable_dhcp_client && pkt->udp->dport == mg_htons(68) &&
- len >= offsetof(struct dhcp, options)) {
- pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
- mkpay(pkt, &pkt->dhcp->options);
- rx_dhcp_client(ifp, pkt);
- } else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(67) &&
- len >= offsetof(struct dhcp, options)) {
- pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
- mkpay(pkt, &pkt->dhcp->options);
- rx_dhcp_server(ifp, pkt);
- } else if (!rx_udp(ifp, pkt)) {
- // Should send ICMP Destination Unreachable for unicasts, but keep
- // silent
- }
- } else if (pkt->ip->proto == 6) {
- uint8_t off;
- pkt->tcp = (struct tcp *) (pkt->pay.buf);
- if (pkt->pay.len < sizeof(*pkt->tcp)) return;
- off = pkt->tcp->off >> 4; // account for opts
- if (pkt->pay.len < (uint16_t) (4 * off)) return;
- mkpay(pkt, (uint32_t *) pkt->tcp + off);
- MG_VERBOSE(("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);
- } else {
- MG_DEBUG(("Unknown IP proto %x", (int) pkt->ip->proto));
- if (mg_log_level >= MG_LL_VERBOSE)
- mg_hexdump(pkt->ip, pkt->pay.len >= 32 ? 32 : pkt->pay.len);
- }
- }
- #if MG_ENABLE_IPV6
- static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
- uint16_t len = 0, plen;
- uint8_t next, *nhdr;
- bool loop = true;
- if (pkt->pay.len < sizeof(*pkt->ip6)) return; // Truncated
- if ((pkt->ip6->ver >> 4) != 0x6) return; // Not IPv6
- plen = mg_ntohs(pkt->ip6->plen);
- if (plen > (pkt->pay.len - sizeof(*pkt->ip6))) return; // malformed
- next = pkt->ip6->next;
- nhdr = (uint8_t *) (pkt->ip6 + 1);
- while (loop) {
- uint16_t hlen;
- switch (next) {
- case 0: // Hop-by-Hop 4.3
- case 43: // Routing 4.4
- case 60: // Destination Options 4.6
- case 51: // Authentication RFC-4302
- MG_INFO(("IPv6 extension header %d", (int) next));
- if (((uint32_t) len + 2) > plen) return; // nhdr[0, 1]; malformed
- next = nhdr[0];
- hlen = (uint16_t) (8 * (nhdr[1] + 1));
- if (((uint32_t) len + hlen) > plen) return; // malformed
- len += hlen;
- nhdr += hlen;
- break;
- case 44: // Fragment 4.5
- {
- struct mg_connection *c;
- if (((uint32_t) len + 2) > plen) return; // nhdr[0, 1]; malformed
- if (nhdr[0] == 17) pkt->udp = (struct udp *) (pkt->pay.buf);
- if (nhdr[0] == 6) pkt->tcp = (struct tcp *) (pkt->pay.buf);
- c = getpeer(ifp->mgr, pkt, false);
- if (c) mg_error(c, "Received fragmented packet");
- }
- return;
- case 59: // No Next Header 4.7
- return;
- case 50: // IPsec ESP RFC-4303, unsupported
- default:
- loop = false;
- break;
- }
- }
- // There can be link padding, take payload length from IPv6 header - options
- pkt->pay.buf = (char *) nhdr;
- pkt->pay.len = plen - len;
- if (next == 58) {
- pkt->icmp6 = (struct icmp6 *) (pkt->pay.buf);
- if (pkt->pay.len < sizeof(*pkt->icmp6)) return;
- mkpay(pkt, pkt->icmp6 + 1);
- MG_DEBUG(("ICMPv6 %M -> %M len %u", mg_print_ip6, &pkt->ip6->src,
- mg_print_ip6, &pkt->ip6->dst, (int) pkt->pay.len));
- rx_icmp6(ifp, pkt);
- } else if (next == 17) {
- pkt->udp = (struct udp *) (pkt->pay.buf);
- if (pkt->pay.len < sizeof(*pkt->udp)) return;
- // Take length from UDP header
- len = mg_ntohs(pkt->udp->len); // UDP datagram length
- if (len < sizeof(*pkt->udp) || len > pkt->pay.len) return; // malformed
- pkt->pay.len = len; // strip excess data
- mkpay(pkt, pkt->udp + 1);
- MG_DEBUG(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
- mg_ntohs(pkt->udp->sport), mg_print_ip6, &pkt->ip6->dst,
- mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
- if (ifp->enable_dhcp6_client && pkt->udp->dport == mg_htons(546)) {
- pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1);
- mkpay(pkt, pkt->dhcp6 + 1);
- // rx_dhcp6_client(ifp, pkt);
- #if 0
- } else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(547)) {
- pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1);
- mkpay(pkt, pkt->dhcp6 + 1);
- rx_dhcp6_server(ifp, pkt);
- #endif
- } else if (!rx_udp(ifp, pkt)) {
- // Should send ICMPv6 Destination Unreachable for unicasts, keep silent
- }
- } else if (next == 6) {
- uint8_t off;
- pkt->tcp = (struct tcp *) (pkt->pay.buf);
- if (pkt->pay.len < sizeof(*pkt->tcp)) return;
- off = pkt->tcp->off >> 4; // account for opts
- if (pkt->pay.len < (uint16_t) (4 * off)) return;
- mkpay(pkt, (uint32_t *) pkt->tcp + off);
- MG_DEBUG(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
- mg_ntohs(pkt->tcp->sport), mg_print_ip6, &pkt->ip6->dst,
- mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
- rx_tcp(ifp, pkt);
- } else {
- MG_DEBUG(("Unknown IPv6 next hdr %x", (int) next));
- if (mg_log_level >= MG_LL_VERBOSE)
- mg_hexdump(pkt->ip6, pkt->pay.len >= 32 ? 32 : pkt->pay.len);
- }
- }
- #else
- #define rx_ip6(x, y)
- #endif
- static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) {
- struct pkt pkt;
- enum mg_l2proto proto;
- memset(&pkt, 0, sizeof(pkt));
- pkt.raw.buf = (char *) buf;
- pkt.raw.len = len;
- pkt.l2 = (uint8_t *) pkt.raw.buf;
- if (!mg_l2_rx(ifp, &proto, &pkt.pay, &pkt.raw)) return;
- if (ifp->state < MG_TCPIP_STATE_UP) return; // discard while L2 is not up
- if (proto == MG_TCPIP_L2PROTO_ARP) {
- pkt.arp = (struct arp *) (pkt.pay.buf);
- if (pkt.pay.len < sizeof(*pkt.arp)) return; // Truncated
- mg_tcpip_call(ifp, MG_TCPIP_EV_ARP, &pkt.raw);
- rx_arp(ifp, &pkt);
- } else if (proto == MG_TCPIP_L2PROTO_IPV6) {
- pkt.ip6 = (struct ip6 *) (pkt.pay.buf);
- rx_ip6(ifp, &pkt);
- } else if (proto == MG_TCPIP_L2PROTO_IPV4) {
- pkt.ip = (struct ip *) (pkt.pay.buf);
- rx_ip(ifp, &pkt);
- }
- }
- static void mg_ip_poll(struct mg_tcpip_if *ifp, bool s1) {
- if (ifp->state < MG_TCPIP_STATE_UP) return;
- // DHCP RFC-2131 (4.4)
- if (ifp->enable_dhcp_client && s1) {
- if (ifp->state == MG_TCPIP_STATE_UP) {
- tx_dhcp_discover(ifp); // INIT (4.4.1)
- } else if (ifp->state == MG_TCPIP_STATE_READY &&
- ifp->lease_expire > 0) { // BOUND / RENEWING / REBINDING
- if (ifp->now >= ifp->lease_expire) {
- ifp->state = MG_TCPIP_STATE_UP, ifp->ip = 0; // expired, release IP
- onstatechange(ifp);
- } else if (ifp->now + 30UL * 60UL * 1000UL > ifp->lease_expire &&
- ((ifp->now / 1000) % 60) == 0) {
- // hack: 30 min before deadline, try to rebind (4.3.6) every min
- tx_dhcp_request_re(
- ifp, mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_BCAST, NULL), ifp->ip,
- 0xffffffff);
- } // TODO(): Handle T1 (RENEWING) and T2 (REBINDING) (4.4.5)
- }
- }
- }
- static void mg_ip_link(struct mg_tcpip_if *ifp, bool drv_up, bool l2_up) {
- bool cur_drv = (ifp->state != MG_TCPIP_STATE_DOWN);
- bool cur_l2 = (ifp->state >= MG_TCPIP_STATE_UP);
- if (!l2_up && ifp->enable_dhcp_client) ifp->ip = 0;
- if (drv_up != cur_drv || l2_up != cur_l2) { // link/L2 state has changed
- ifp->state = !drv_up ? MG_TCPIP_STATE_DOWN
- : !l2_up ? MG_TCPIP_STATE_LINK_UP
- : ifp->enable_dhcp_client || ifp->ip == 0 ? MG_TCPIP_STATE_UP
- : MG_TCPIP_STATE_IP;
- onstatechange(ifp);
- } else if (!ifp->enable_dhcp_client && ifp->state == MG_TCPIP_STATE_UP &&
- ifp->ip) {
- ifp->state = MG_TCPIP_STATE_IP; // ifp->fn has set an IP
- onstatechange(ifp);
- }
- }
- #if MG_ENABLE_IPV6
- static void mg_ip6_poll(struct mg_tcpip_if *ifp, bool s1) {
- if (ifp->state6 < MG_TCPIP_STATE_UP) return;
- if (ifp->enable_slaac && s1 && ifp->state6 == MG_TCPIP_STATE_UP)
- tx_ndp_rs(ifp);
- }
- static void mg_ip6_link(struct mg_tcpip_if *ifp, bool drv_up, bool l2_up) {
- bool cur_drv = (ifp->state6 != MG_TCPIP_STATE_DOWN);
- bool cur_l2 = (ifp->state6 >= MG_TCPIP_STATE_UP);
- const uint8_t px[8] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0}; // RFC-4291 2.5.6
- if (drv_up != cur_drv || l2_up != cur_l2) { // link/L2 state has changed
- if (ifp->l2type == MG_TCPIP_L2_ETH && l2_up && ifp->ip6ll[0] == 0 &&
- ifp->ip6ll[1] == 0) { // gen ll address
- mg_l2_genip6(ifp->l2type, ifp->ip6ll, 64, ifp->mac);
- memcpy(ifp->ip6ll, px, 8); // RFC-2464 5
- } // just got our link local address if we didn't have one.
- // If static configuration is used, global addresses,
- // prefix length, and gw are already filled at this point.
- if (ifp->ip6[0] == 0 && ifp->ip6[1] == 0) ifp->enable_slaac = true;
- if (!l2_up && ifp->enable_slaac) ifp->ip6[0] = ifp->ip6[1] = 0;
- ifp->state6 = !drv_up ? MG_TCPIP_STATE_DOWN
- : !l2_up ? MG_TCPIP_STATE_LINK_UP
- : ifp->ip6ll[0] == 0 || ifp->enable_slaac || ifp->ip6[0] == 0
- ? MG_TCPIP_STATE_UP
- : MG_TCPIP_STATE_IP;
- onstate6change(ifp);
- } else if (!ifp->enable_slaac && ifp->state6 == MG_TCPIP_STATE_UP &&
- ifp->ip6ll[0] != 0 && ifp->ip6[0] != 0) {
- ifp->state6 = MG_TCPIP_STATE_IP; // ifp->fn has set an IP
- onstate6change(ifp);
- }
- if ((ifp->l2type == MG_TCPIP_L2_PPP || ifp->l2type == MG_TCPIP_L2_PPPoE) &&
- ifp->state6 == MG_TCPIP_STATE_UP && ifp->ip6ll[0] == 0 &&
- ifp->ip6ll[1] != 0) { // IPV6CP has got an IFCID, gen ll address
- memcpy(ifp->ip6ll, px, 8); // RFC-5072 5
- onstate6change(ifp);
- } // just got our link local address if we didn't have one.
- }
- #else
- #define mg_ip6_poll(x, y)
- #define mg_ip6_link(x, y, z)
- #endif
- static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
- struct mg_connection *c;
- bool expired_1000ms = mg_timer_expired(&ifp->timer_1000ms, 1000, now);
- ifp->now = now;
- if (expired_1000ms) {
- #if MG_ENABLE_TCPIP_PRINT_DEBUG_STATS
- const char *names[] = {"down", "lnk up", "lnk rdy", "req", "ip", "ready"};
- size_t max = sizeof(names) / sizeof(char *);
- unsigned int state = ifp->state >= max ? max - 1 : ifp->state;
- MG_INFO(("Status: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u", names[state],
- mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent, ifp->ndrop,
- ifp->nerr));
- #if MG_ENABLE_IPV6
- state = ifp->state6 >= max ? max - 1 : ifp->state6;
- if (state > MG_TCPIP_STATE_UP)
- MG_INFO(("Status: %s, IPv6: %M", names[state], mg_print_ip6, &ifp->ip6));
- #endif
- #endif
- backlog_poll(ifp->mgr);
- }
- // Handle gw ARP request timeout, order is important
- if (expired_1000ms && ifp->state == MG_TCPIP_STATE_IP) {
- ifp->state = MG_TCPIP_STATE_READY; // keep best-effort MAC or poison mark
- onstatechange(ifp);
- }
- if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && !ifp->gw_ready &&
- ifp->gw != 0)
- mg_tcpip_arp_request(ifp, ifp->gw, NULL); // retry GW ARP request
- #if MG_ENABLE_IPV6
- // Handle gw NS/NA req/resp timeout, order is important
- if (expired_1000ms && ifp->state6 == MG_TCPIP_STATE_IP) {
- ifp->state6 = MG_TCPIP_STATE_READY; // keep best-effort MAC or poison mark
- onstate6change(ifp);
- }
- if (expired_1000ms && ifp->state == MG_TCPIP_STATE_READY && !ifp->gw6_ready &&
- (ifp->gw6[0] != 0 || ifp->gw6[1] != 0))
- tx_ndp_ns(ifp, ifp->gw6, NULL); // retry GW hwaddr resolution
- #endif
- { // poll driver and let L2 do its work, if any
- // Handle physical interface up/down status
- bool drv_up, l2_up;
- drv_up = ifp->driver->poll ? ifp->driver->poll(ifp, expired_1000ms) : true;
- l2_up = mg_l2_poll(ifp, expired_1000ms); // Handle L2 up/down link status;
- if (expired_1000ms) { // ifp->state rules over state6
- mg_ip_link(ifp, drv_up, l2_up); // Handle IPv4
- mg_ip6_link(ifp, drv_up, l2_up); // Handle IPv6
- if (ifp->state < MG_TCPIP_STATE_UP) MG_ERROR(("Network is down"));
- mg_tcpip_call(ifp, MG_TCPIP_EV_TIMER_1S, NULL);
- }
- }
- mg_ip_poll(ifp, expired_1000ms); // Handle IPv4
- mg_ip6_poll(ifp, expired_1000ms); // Handle IPv6
- // Read data from the network (mg_tcpip_rx() will discard for us)
- if (ifp->driver->rx != NULL) { // Simple polling driver, returns one frame
- size_t len =
- ifp->driver->rx(ifp->recv_queue.buf, ifp->recv_queue.size, ifp);
- if (len > 0) {
- ifp->nrecv++;
- mg_tcpip_rx(ifp, ifp->recv_queue.buf, len);
- }
- } else { // Complex poll / Interrupt-based driver. Queues recvd frames
- char *buf;
- size_t len, cnt = 7; // Max 7 packets to fetch
- while (cnt-- > 0 && (len = mg_queue_next(&ifp->recv_queue, &buf)) > 0) {
- mg_tcpip_rx(ifp, buf, len);
- mg_queue_del(&ifp->recv_queue, len);
- }
- }
- if (ifp->state < MG_TCPIP_STATE_UP) return; // need to let L2 do its job
- // Process timeouts
- for (c = ifp->mgr->conns; c != NULL; c = c->next) {
- struct connstate *s = (struct connstate *) (c + 1);
- if ((c->is_udp && !c->is_arplooking) || c->is_listening || c->is_resolving)
- continue;
- if (ifp->now > s->timer) {
- if (s->ttype == MIP_TTYPE_ARP) {
- mg_error(c, "ARP timeout");
- } else if (c->is_udp) {
- continue;
- } else if (s->ttype == MIP_TTYPE_ACK && s->acked != s->ack) {
- MG_VERBOSE(("%lu ack %x %x", c->id, s->seq, s->ack));
- tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK, mg_htonl(s->seq),
- mg_htonl(s->ack), NULL, 0);
- s->acked = s->ack;
- } else if (s->ttype == MIP_TTYPE_SYN) {
- mg_error(c, "Connection timeout");
- } else if (s->ttype == MIP_TTYPE_FIN) {
- c->is_closing = 1;
- continue;
- } else {
- if (s->tmiss++ > 2) {
- mg_error(c, "keepalive");
- } else {
- MG_VERBOSE(("%lu keepalive", c->id));
- tx_tcp(ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_ACK,
- mg_htonl(s->seq - 1), mg_htonl(s->ack), NULL, 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 L2 address is not set, make a random one; fill MTU
- mg_l2_init(ifp);
- ifp->mtu = ifp->l2mtu;
- if (ifp->dhcp_name[0] == '\0') // If DHCP name is not set, use "mip"
- memcpy(ifp->dhcp_name, "mip", 4);
- ifp->dhcp_name[sizeof(ifp->dhcp_name) - 1] = '\0'; // Just in case
- if (ifp->driver->init && !ifp->driver->init(ifp)) {
- MG_ERROR(("driver init failed"));
- } else {
- ifp->tx.buf = (char *) mg_calloc(1, ifp->framesize),
- ifp->tx.len = ifp->framesize;
- if (ifp->recv_queue.size == 0)
- ifp->recv_queue.size = ifp->driver->rx ? ifp->framesize : 8192;
- ifp->recv_queue.buf = (char *) mg_calloc(1, ifp->recv_queue.size);
- ifp->timer_1000ms = mg_millis();
- mgr->ifp = ifp;
- ifp->mgr = mgr;
- mgr->extraconnsize = sizeof(struct connstate);
- if (ifp->ip == 0 && ifp->l2type != MG_TCPIP_L2_PPP &&
- ifp->l2type != MG_TCPIP_L2_PPPoE)
- ifp->enable_dhcp_client = true;
- 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
- if (ifp->tx.buf == NULL || ifp->recv_queue.buf == NULL) MG_ERROR(("OOM"));
- }
- }
- void mg_tcpip_free(struct mg_tcpip_if *ifp) {
- mg_free(ifp->recv_queue.buf);
- mg_free(ifp->tx.buf);
- mg_free(ifp->dns4_url);
- }
- 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));
- tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_SYN, isn, 0, NULL,
- 0);
- }
- static void l2addr_resolved(struct mg_connection *c) {
- if (c->is_udp) {
- c->is_connecting = 0;
- mg_call(c, MG_EV_CONNECT, NULL);
- } else {
- send_syn(c);
- settmout(c, MIP_TTYPE_SYN);
- }
- }
- void mg_connect_resolved(struct mg_connection *c) {
- struct mg_tcpip_if *ifp = c->mgr->ifp;
- uint8_t *l2addr;
- c->is_resolving = 0;
- if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE;
- c->loc.port = mg_htons(ifp->eport++);
- #if MG_ENABLE_IPV6
- if (c->rem.is_ip6) {
- if (c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
- c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1];
- } else { // use global address
- c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1];
- }
- c->loc.is_ip6 = true;
- } else
- #endif
- {
- c->loc.addr.ip4 = ifp->ip;
- }
- 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);
- c->is_connecting = 1;
- if (c->is_udp && (l2addr = tcpip_mapip(ifp, &c->rem)) != NULL) {
- struct connstate *s = (struct connstate *) (c + 1);
- memcpy(s->mac, l2addr, sizeof(s->mac));
- l2addr_resolved(c); // broadcast or multicast
- #if MG_ENABLE_IPV6
- } else if (c->rem.is_ip6) {
- if (match_prefix((uint8_t *) c->rem.addr.ip6, ifp->prefix,
- ifp->prefix_len) // same global LAN
- || (c->rem.addr.ip6[0] == ifp->ip6ll[0] // same local LAN
- && !MG_IP6MATCH(c->rem.addr.ip6, ifp->gw6))) { // and not gw
- // If we're in the same LAN, fire a Neighbor Solicitation
- MG_DEBUG(("%lu NS lookup...", c->id));
- tx_ndp_ns(ifp, c->rem.addr.ip6, NULL); // RFC-4861 4.3, requesting
- settmout(c, MIP_TTYPE_ARP);
- c->is_arplooking = 1;
- } else if (ifp->gw6_ready) {
- struct connstate *s = (struct connstate *) (c + 1);
- memcpy(s->mac, ifp->gw6mac, sizeof(s->mac));
- l2addr_resolved(c);
- } else {
- MG_ERROR(("No IPv6 gateway, can't connect"));
- }
- #endif
- } else {
- uint32_t rem_ip = c->rem.addr.ip4;
- if (ifp->ip && ((rem_ip & ifp->mask) == (ifp->ip & ifp->mask)) &&
- rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
- // If we're in the same LAN, fire an ARP lookup.
- MG_DEBUG(("%lu ARP lookup...", c->id));
- mg_tcpip_arp_request(ifp, rem_ip, NULL);
- settmout(c, MIP_TTYPE_ARP);
- c->is_arplooking = 1;
- } else if (ifp->gw_ready) {
- struct connstate *s = (struct connstate *) (c + 1);
- memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
- l2addr_resolved(c);
- } else {
- MG_ERROR(("No gateway, can't connect"));
- }
- }
- }
- bool mg_open_listener(struct mg_connection *c, const char *url) {
- 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));
- return false;
- }
- 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);
- // TODO(): mg_tls_send() may return 0 forever on steady OOM
- if (len == MG_IO_ERR) {
- mg_error(c, "tx err");
- } else if (len > 0) {
- mg_iobuf_del(&c->send, 0, (size_t) len);
- mg_call(c, MG_EV_WRITE, &len);
- }
- }
- static void init_closure(struct mg_connection *c) {
- struct connstate *s = (struct connstate *) (c + 1);
- if (c->is_udp == false && c->is_listening == false &&
- c->is_connecting == false) { // For TCP conns,
- tx_tcp(c->mgr->ifp, s->mac, &c->loc, &c->rem, c->dscp, TH_FIN | TH_ACK,
- mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
- settmout(c, MIP_TTYPE_FIN);
- }
- }
- static void close_conn(struct mg_connection *c) {
- struct connstate *s = (struct connstate *) (c + 1);
- mg_close_conn(c);
- (void) s;
- }
- 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_timer_poll(&mgr->timers, now);
- mg_ota_poll(mgr);
- if (mgr->ifp == NULL || mgr->ifp->driver == NULL) return;
- mg_tcpip_poll(mgr->ifp, now);
- for (c = mgr->conns; c != NULL; c = tmp) {
- struct connstate *s = (struct connstate *) (c + 1);
- bool is_tls = c->is_tls && !c->is_resolving && !c->is_arplooking &&
- !c->is_listening && !c->is_connecting;
- tmp = c->next;
- mg_call(c, MG_EV_POLL, &now);
- MG_VERBOSE(("%lu .. %c%c%c%c%c %lu %lu", 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',
- mg_tls_pending(c), c->rtls.len));
- // order is important, TLS conn close with > 1 record in buffer (below)
- if (is_tls && (c->rtls.len > 0 || mg_tls_pending(c) > 0))
- c->is_tls_hs ? mg_tls_handshake(c) : handle_tls_recv(c);
- if (can_write(c)) write_conn(c);
- if (is_tls && c->send.len == 0) mg_tls_flush(c);
- if (c->is_draining && c->send.len == 0 && s->ttype != MIP_TTYPE_FIN)
- init_closure(c);
- // For non-TLS, close immediately upon completing the 3-way closure
- // For TLS, handle any pending data (above) until MIP_TTYPE_FIN expires
- if (s->twclosure &&
- (!c->is_tls || (c->rtls.len == 0 && mg_tls_pending(c) == 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 = c->mgr->ifp;
- bool res = false;
- if (!c->loc.is_ip6 && (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY)) {
- mg_error(c, "net down");
- #if MG_ENABLE_IPV6
- } else if (c->loc.is_ip6 && ifp->state6 != MG_TCPIP_STATE_READY) {
- mg_error(c, "net down");
- #endif
- } else if (c->is_udp && (c->is_arplooking || c->is_resolving)) {
- // Fail to send, no target MAC or IP
- MG_VERBOSE(("still resolving..."));
- } else if (c->is_udp) {
- len = trim_len(c, len); // Trimming length if necessary
- res = udp_send(c, buf, len);
- } else {
- res = len == 0 || mg_iobuf_add(&c->send, c->send.len, buf, len) > 0;
- // returning 0 means an OOM condition (iobuf couldn't resize), yet this is
- // so far recoverable, let the caller decide
- }
- return res;
- }
- uint8_t mcast_addr[6] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0xfb};
- void mg_multicast_add(struct mg_connection *c, char *ip) {
- (void) ip; // ip4/6_mcastmac(mcast_mac, &ip); ipv6 param
- // TODO(): actual IP -> MAC; check database, update
- c->mgr->ifp->update_mac_hash_table = true; // mark dirty
- }
- bool mg_dnsc_init(struct mg_mgr *mgr, struct mg_dns *dnsc);
- static void setdns4(struct mg_tcpip_if *ifp, uint32_t *ip) {
- struct mg_dns *dnsc;
- mg_free(ifp->dns4_url);
- ifp->dns4_url = mg_mprintf("udp://%M:53", mg_print_ip4, ip);
- dnsc = &ifp->mgr->dns4;
- dnsc->url = (const char *) ifp->dns4_url;
- MG_DEBUG(("Set DNS URL to %s", dnsc->url));
- if (ifp->mgr->use_dns6) return;
- if (dnsc->c != NULL) mg_close_conn(dnsc->c);
- if (!mg_dnsc_init(ifp->mgr, dnsc)) // create DNS connection
- MG_ERROR(("DNS connection creation failed"));
- }
- void mg_tcpip_mapip(struct mg_connection *c, struct mg_addr *ip) {
- struct connstate *s = (struct connstate *) (c + 1);
- uint8_t *l2addr = tcpip_mapip(c->mgr->ifp, ip);
- if (l2addr == NULL) return;
- memcpy(s->mac, l2addr, sizeof(s->mac));
- }
- #endif // MG_ENABLE_TCPIP
- #ifdef MG_ENABLE_LINES
- #line 1 "src/ota.c"
- #endif
- #ifndef MG_OTA_MAX_URL_LEN
- #define MG_OTA_MAX_URL_LEN 256
- #endif
- // Scannable version tag embedded in every firmware binary, for server-side
- // version extraction. The version string starts after the "MG_VERSION:" prefix.
- static const char s_fw_version[] = "MG_VERSION:" MG_OTA_FIRMWARE_VERSION;
- static bool s_autocommit_ok; // True after OTA server confirms "same version"
- static struct mg_ota_state {
- char json_url[MG_OTA_MAX_URL_LEN];
- char url[MG_OTA_MAX_URL_LEN];
- size_t size;
- uint8_t sha256[32];
- void (*fn)(const char *error_message);
- } *s_ota;
- static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data);
- static bool s_tls_init(struct mg_connection *c, const char *url) {
- struct mg_tls_opts opts;
- memset(&opts, 0, sizeof(opts));
- opts.ca = mg_str(MG_OTA_TLS_CA);
- opts.name = mg_url_host(url);
- if (opts.ca.len == 0) {
- mg_error(c, "MG_OTA_TLS_CA is not configured");
- } else {
- mg_tls_init(c, &opts);
- }
- return c->is_tls_hs != 0 && !c->is_closing;
- }
- #if MG_ENABLE_CUSTOM_DEVICE_ID
- #else
- void mg_ota_device_id(char *buf, size_t len) {
- #if defined(UID_BASE) && \
- (defined(__SYSTEM_STM32F4XX_H) || defined(__SYSTEM_STM32F7XX_H) || \
- defined(SYSTEM_STM32H5XX_H) || defined(SYSTEM_STM32H7XX_H) || \
- defined(SYSTEM_STM32N6XX_H) || defined(SYSTEM_STM32U5XX_H))
- uint32_t *p = (uint32_t *) UID_BASE;
- mg_snprintf(buf, len, "stm32_%08x%08x%08x", p[0], p[1], p[2]);
- #elif MG_ARCH == MG_ARCH_PICOSDK
- pico_unique_board_id_t x = {0};
- pico_get_unique_board_id(&x);
- mg_snprintf(buf, len, "rp_%02x%02x%02x%02x%02x%02x%02x%02x", x.id[0], x.id[1],
- x.id[2], x.id[3], x.id[4], x.id[5], x.id[6], x.id[7]);
- #elif defined(OCOTP_BASE) && OCOTP_BASE == 0x401F4000u
- mg_snprintf(buf, len, "rt10xx_%08x%08x", OCOTP->CFG0, OCOTP->CFG1);
- #elif defined(OCOTP_BASE) && OCOTP_BASE == 0x40CAC000u
- mg_snprintf(buf, len, "rt11xx_%08x%08x", OCOTP->FUSEN[16].FUSE,
- OCOTP->FUSEN[17].FUSE);
- #elif MG_ARCH == MG_ARCH_ESP32
- uint8_t mac[6] = {0};
- esp_efuse_mac_get_default(mac);
- mg_snprintf(buf, len, "esp32_%02x%02x%02x%02x%02x%02x", mac[0], mac[1],
- mac[2], mac[3], mac[4], mac[5]);
- #else
- mg_snprintf(buf, len, "%d", 0);
- #endif
- }
- #endif
- static void s_version_fn(struct mg_connection *c, int ev, void *ev_data) {
- uint64_t expiration = *(uint64_t *) c->data;
- if (s_ota == NULL) {
- c->fn = NULL;
- c->is_closing = 1;
- return;
- }
- if (ev == MG_EV_POLL) {
- if (mg_millis() > expiration) mg_error(c, "Metadata timeout");
- } else if (ev == MG_EV_CONNECT) {
- char id[40];
- struct mg_str host = mg_url_host(s_ota->json_url);
- const char *uri = mg_url_uri(s_ota->json_url);
- const char *sep = strchr(uri, '?') == NULL ? "?" : "&";
- if (mg_url_is_ssl(s_ota->json_url)) {
- if (!s_tls_init(c, s_ota->json_url)) return;
- }
- mg_ota_device_id(id, sizeof(id));
- id[sizeof(id) - 1] = '\0';
- mg_printf(
- c,
- "GET %s%sarch=%d&version=%s&id=%s&interval=%d&boot=%d HTTP/1.1\r\n"
- "Host: %.*s\r\n"
- "Connection: close\r\n\r\n",
- uri, sep, MG_ARCH, s_fw_version + 11, id, MG_OTA_PULL_INTERVAL_SECONDS,
- MG_OTA_STATE_GET(), host.len, host.buf);
- } else if (ev == MG_EV_HTTP_MSG) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- char version[MG_OTA_MAX_VERSION_LEN];
- double result;
- MG_DEBUG(("Got metadata: %.*s", hm->body.len, hm->body.buf));
- if (mg_http_status(hm) != 200 || mg_json_get(hm->body, "$", NULL) != 0 ||
- !mg_json_unescape(hm->body, "$.version", version, sizeof(version)) ||
- !mg_json_unescape(hm->body, "$.url", s_ota->url, sizeof(s_ota->url)) ||
- !mg_json_get_num(hm->body, "$.size", &result) || result <= 0) {
- char buf[100];
- mg_snprintf(buf, sizeof(buf), "Bad metadata: %.*s", hm->body.len,
- hm->body.buf);
- s_ota->fn(buf);
- mg_free(s_ota);
- s_ota = NULL;
- } else if (strcmp(version, s_fw_version + 11) == 0) {
- s_autocommit_ok = true;
- s_ota->fn("Same version");
- mg_free(s_ota);
- s_ota = NULL;
- } else {
- struct mg_connection *fc;
- s_ota->size = (size_t) result;
- // TODO (robertc2000): parse and validate sha256
- MG_DEBUG(("Firmware version: %s, url: %s, size: %ld", version, s_ota->url,
- s_ota->size));
- fc = mg_http_connect(c->mgr, s_ota->url, s_firmware_fn, NULL);
- if (fc == NULL) {
- s_ota->fn("Failed to connect");
- mg_free(s_ota);
- s_ota = NULL;
- } else {
- *(uint64_t *) fc->data = mg_millis() + 5 * 1000; // Set expiration
- }
- }
- c->is_closing = 1;
- } else if (ev == MG_EV_ERROR) {
- s_ota->fn((char *) ev_data);
- mg_free(s_ota);
- s_ota = NULL;
- }
- }
- static void status_fn(const char *errmsg) {
- if (errmsg) MG_ERROR(("OTA failed: %s", errmsg));
- }
- static void status_fn_2(struct mg_connection *c, const char *errmsg) {
- if (s_ota) s_ota->fn(errmsg);
- mg_free(s_ota);
- s_ota = NULL;
- mg_http_reply(c, errmsg ? 500 : 200, "", "%s\n", errmsg ? errmsg : "ok");
- }
- static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data) {
- uint64_t expiration = *(uint64_t *) c->data;
- if (s_ota == NULL) {
- c->fn = NULL;
- c->is_closing = 1;
- return;
- }
- if (ev == MG_EV_POLL) {
- if (mg_millis() > expiration) mg_error(c, "OTA timeout");
- } else if (ev == MG_EV_CONNECT) {
- struct mg_str host = mg_url_host(s_ota->url);
- if (mg_url_is_ssl(s_ota->url)) {
- if (!s_tls_init(c, s_ota->url)) return;
- }
- mg_printf(c,
- "GET %s HTTP/1.1\r\n"
- "Host: %.*s\r\n"
- "Connection: close\r\n\r\n",
- mg_url_uri(s_ota->url), (int) host.len, host.buf);
- *(uint64_t *) c->data = mg_millis() + 300 * 1000; // Set expiration
- } else if (ev == MG_EV_HTTP_HDRS) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- int status = mg_http_status(hm);
- if (status != 200 || hm->body.len != s_ota->size) {
- mg_error(c, "Bad HTTP response: status %d, size %lu vs %lu", status,
- (unsigned long) hm->body.len, (unsigned long) s_ota->size);
- } else {
- MG_DEBUG(("Beginning OTA (%lu bytes)", (unsigned long) s_ota->size));
- mg_http_start_ota(c, hm, status_fn_2);
- }
- } else if (ev == MG_EV_ERROR) {
- s_ota->fn((char *) ev_data);
- mg_free(s_ota);
- s_ota = NULL;
- }
- }
- void mg_ota_url_check(struct mg_mgr *mgr, const char *json_url,
- void (*fn)(const char *error_message)) {
- if (fn == NULL) fn = status_fn;
- if (s_ota != NULL) {
- fn("OTA already in progress");
- } else if ((s_ota = (struct mg_ota_state *) mg_calloc(1, sizeof(*s_ota))) ==
- NULL) {
- fn("Out of memory");
- } else {
- struct mg_connection *c;
- mg_snprintf(s_ota->json_url, sizeof(s_ota->json_url), "%s", json_url);
- MG_DEBUG(("Connecting to %s", json_url));
- c = mg_http_connect(mgr, s_ota->json_url, s_version_fn, NULL);
- if (c == NULL) {
- mg_free(s_ota);
- s_ota = NULL;
- fn("Failed to connect");
- } else {
- s_ota->fn = fn;
- *(uint64_t *) c->data = mg_millis() + 5 * 1000; // Set expiration
- }
- }
- }
- void mg_ota_poll(struct mg_mgr *mgr) {
- static uint64_t t = 5000; // Fire first time 5 sec after boot
- static uint64_t feed_timer; // Advances 500ms per tick; tracks elapsed time
- if (MG_OTA_STATE_GET() == MG_OTA_FAILED &&
- mg_timer_expired(&feed_timer, 500, mg_millis())) {
- if (feed_timer < (uint64_t) MG_OTA_ROLLBACK_TIMEOUT_SECONDS * 1000) {
- MG_OTA_ROLLBACK_TIMER_FEED(); // Feed watchdog every 500ms
- } else if (s_autocommit_ok) {
- MG_INFO(("Auto-committing firmware"));
- MG_OTA_STATE_SET(MG_OTA_CONFIRMED); // Stop feeding → IWDG resets cleanly
- } else {
- MG_INFO(("No commit confirmation, rolling back"));
- // Stop feeding → IWDG fires → resets into MG_OTA_FAILED → rollback
- }
- }
- if (MG_OTA_URL != NULL &&
- mg_timer_expired(&t, MG_OTA_PULL_INTERVAL_SECONDS * 1000, mg_millis())) {
- mg_ota_url_check(mgr, MG_OTA_URL, MG_OTA_STATUS_FN);
- }
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/ota_ch32v307.c"
- #endif
- #if MG_OTA == MG_OTA_CH32V307
- // RM: https://www.wch-ic.com/downloads/CH32FV2x_V3xRM_PDF.html
- static bool mg_ch32v307_write(void *, const void *, size_t);
- static bool mg_ch32v307_swap(void);
- static struct mg_flash s_mg_flash_ch32v307 = {
- (void *) 0x08000000, // Start
- 480 * 1024, // Size, first 320k is 0-wait
- 4 * 1024, // Sector size, 4k
- 4, // Align, 32 bit
- mg_ch32v307_write,
- mg_ch32v307_swap,
- };
- #define FLASH_BASE 0x40022000
- #define FLASH_ACTLR (FLASH_BASE + 0)
- #define FLASH_KEYR (FLASH_BASE + 4)
- #define FLASH_OBKEYR (FLASH_BASE + 8)
- #define FLASH_STATR (FLASH_BASE + 12)
- #define FLASH_CTLR (FLASH_BASE + 16)
- #define FLASH_ADDR (FLASH_BASE + 20)
- #define FLASH_OBR (FLASH_BASE + 28)
- #define FLASH_WPR (FLASH_BASE + 32)
- MG_IRAM static void flash_unlock(void) {
- static bool unlocked;
- if (unlocked == false) {
- MG_REG(FLASH_KEYR) = 0x45670123;
- MG_REG(FLASH_KEYR) = 0xcdef89ab;
- unlocked = true;
- }
- }
- MG_IRAM static void flash_wait(void) {
- while (MG_REG(FLASH_STATR) & MG_BIT(0)) (void) 0;
- }
- MG_IRAM static void mg_ch32v307_erase(void *addr) {
- // MG_INFO(("%p", addr));
- flash_unlock();
- flash_wait();
- MG_REG(FLASH_ADDR) = (uint32_t) addr;
- MG_REG(FLASH_CTLR) |= MG_BIT(1) | MG_BIT(6); // PER | STRT;
- flash_wait();
- }
- MG_IRAM static bool is_page_boundary(const void *addr) {
- uint32_t val = (uint32_t) addr;
- return (val & (s_mg_flash_ch32v307.secsz - 1)) == 0;
- }
- MG_IRAM static bool mg_ch32v307_write(void *addr, const void *buf, size_t len) {
- // MG_INFO(("%p %p %lu", addr, buf, len));
- // mg_hexdump(buf, len);
- flash_unlock();
- const uint16_t *src = (uint16_t *) buf, *end = &src[len / 2];
- uint16_t *dst = (uint16_t *) addr;
- MG_REG(FLASH_CTLR) |= MG_BIT(0); // Set PG
- // MG_INFO(("CTLR: %#lx", MG_REG(FLASH_CTLR)));
- while (src < end) {
- if (is_page_boundary(dst)) mg_ch32v307_erase(dst);
- *dst++ = *src++;
- flash_wait();
- }
- MG_REG(FLASH_CTLR) &= ~MG_BIT(0); // Clear PG
- return true;
- }
- MG_IRAM bool mg_ch32v307_swap(void) {
- return true;
- }
- // just overwrite instead of swap
- MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) {
- // no stdlib calls here
- for (size_t ofs = 0; ofs < s; ofs += ss) {
- mg_ch32v307_write(p1 + ofs, p2 + ofs, ss);
- }
- *((volatile uint32_t *) 0xbeef0000) |= 1U << 7; // NVIC_SystemReset()
- }
- bool mg_ota_begin(size_t new_firmware_size) {
- return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_ch32v307);
- }
- bool mg_ota_write(const void *buf, size_t len) {
- return mg_ota_flash_write(buf, len, &s_mg_flash_ch32v307);
- }
- bool mg_ota_end(void) {
- if (mg_ota_flash_end(&s_mg_flash_ch32v307)) {
- // Swap partitions. Pray power does not go away
- MG_INFO(("Swapping partitions, size %u (%u sectors)",
- s_mg_flash_ch32v307.size,
- s_mg_flash_ch32v307.size / s_mg_flash_ch32v307.secsz));
- MG_INFO(("Do NOT power off..."));
- mg_log_level = MG_LL_NONE;
- // TODO() disable IRQ, s_flash_irq_disabled = true;
- // Runs in RAM, will reset when finished
- single_bank_swap(
- (char *) s_mg_flash_ch32v307.start,
- (char *) s_mg_flash_ch32v307.start + s_mg_flash_ch32v307.size / 2,
- s_mg_flash_ch32v307.size / 2, s_mg_flash_ch32v307.secsz);
- }
- return false;
- }
- struct mg_flash *mg_flash = &s_mg_flash_ch32v307;
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/ota_dummy.c"
- #endif
- #if MG_OTA == MG_OTA_NONE
- bool mg_ota_begin(size_t new_firmware_size) {
- (void) new_firmware_size;
- return true;
- }
- bool mg_ota_write(const void *buf, size_t len) {
- (void) buf, (void) len;
- return true;
- }
- bool mg_ota_end(void) {
- return true;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/ota_esp32.c"
- #endif
- #if MG_ARCH == MG_ARCH_ESP32 && MG_OTA == MG_OTA_ESP32
- static const esp_partition_t *s_ota_update_partition;
- static esp_ota_handle_t s_ota_update_handle;
- static bool s_ota_success;
- // Those empty macros do nothing, but mark places in the code which could
- // potentially trigger a watchdog reboot due to the log flash erase operation
- #define disable_wdt()
- #define enable_wdt()
- bool mg_ota_begin(size_t new_firmware_size) {
- if (s_ota_update_partition != NULL) {
- MG_ERROR(("Update in progress. Call mg_ota_end() ?"));
- return false;
- } else {
- s_ota_success = false;
- disable_wdt();
- s_ota_update_partition = esp_ota_get_next_update_partition(NULL);
- esp_err_t err = esp_ota_begin(s_ota_update_partition, new_firmware_size,
- &s_ota_update_handle);
- enable_wdt();
- MG_DEBUG(("esp_ota_begin(): %d", err));
- s_ota_success = (err == ESP_OK);
- }
- return s_ota_success;
- }
- bool mg_ota_write(const void *buf, size_t len) {
- disable_wdt();
- esp_err_t err = esp_ota_write(s_ota_update_handle, buf, len);
- enable_wdt();
- MG_INFO(("esp_ota_write(): %d", err));
- s_ota_success = err == ESP_OK;
- return s_ota_success;
- }
- bool mg_ota_end(void) {
- esp_err_t err = esp_ota_end(s_ota_update_handle);
- MG_DEBUG(("esp_ota_end(%p): %d", s_ota_update_handle, err));
- if (s_ota_success && err == ESP_OK) {
- err = esp_ota_set_boot_partition(s_ota_update_partition);
- s_ota_success = (err == ESP_OK);
- }
- MG_DEBUG(("Finished ESP32 OTA, success: %d", s_ota_success));
- s_ota_update_partition = NULL;
- if (s_ota_success) {
- MG_OTA_STATE_SET(MG_OTA_TESTING);
- esp_restart();
- }
- return s_ota_success;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/ota_imxrt.c"
- #endif
- #if MG_OTA >= MG_OTA_RT1020 && MG_OTA <= MG_OTA_RT1170
- static bool mg_imxrt_write(void *, const void *, size_t);
- static bool mg_imxrt_swap(void);
- #if MG_OTA <= MG_OTA_RT1060
- #define MG_IMXRT_FLASH_START 0x60000000
- #define FLEXSPI_NOR_INSTANCE 0
- #elif MG_OTA == MG_OTA_RT1064
- #define MG_IMXRT_FLASH_START 0x70000000
- #define FLEXSPI_NOR_INSTANCE 1
- #else // RT1170
- #define MG_IMXRT_FLASH_START 0x30000000
- #define FLEXSPI_NOR_INSTANCE 1
- #endif
- #if MG_OTA == MG_OTA_RT1050
- #define MG_IMXRT_SECTOR_SIZE (256 * 1024)
- #define MG_IMXRT_PAGE_SIZE 512
- #else
- #define MG_IMXRT_SECTOR_SIZE (4 * 1024)
- #define MG_IMXRT_PAGE_SIZE 256
- #endif
- // TODO(): fill at init, support more devices in a dynamic way
- // TODO(): then, check alignment is <= 256, see Wizard's #251
- static struct mg_flash s_mg_flash_imxrt = {
- (void *) MG_IMXRT_FLASH_START, // Start,
- 4 * 1024 * 1024, // Size, 4mb
- MG_IMXRT_SECTOR_SIZE, // Sector size
- MG_IMXRT_PAGE_SIZE, // Align
- mg_imxrt_write,
- mg_imxrt_swap,
- };
- struct mg_flexspi_lut_seq {
- uint8_t seqNum;
- uint8_t seqId;
- uint16_t reserved;
- };
- struct mg_flexspi_mem_config {
- uint32_t tag;
- uint32_t version;
- uint32_t reserved0;
- uint8_t readSampleClkSrc;
- uint8_t csHoldTime;
- uint8_t csSetupTime;
- uint8_t columnAddressWidth;
- uint8_t deviceModeCfgEnable;
- uint8_t deviceModeType;
- uint16_t waitTimeCfgCommands;
- struct mg_flexspi_lut_seq deviceModeSeq;
- uint32_t deviceModeArg;
- uint8_t configCmdEnable;
- uint8_t configModeType[3];
- struct mg_flexspi_lut_seq configCmdSeqs[3];
- uint32_t reserved1;
- uint32_t configCmdArgs[3];
- uint32_t reserved2;
- uint32_t controllerMiscOption;
- uint8_t deviceType;
- uint8_t sflashPadType;
- uint8_t serialClkFreq;
- uint8_t lutCustomSeqEnable;
- uint32_t reserved3[2];
- uint32_t sflashA1Size;
- uint32_t sflashA2Size;
- uint32_t sflashB1Size;
- uint32_t sflashB2Size;
- uint32_t csPadSettingOverride;
- uint32_t sclkPadSettingOverride;
- uint32_t dataPadSettingOverride;
- uint32_t dqsPadSettingOverride;
- uint32_t timeoutInMs;
- uint32_t commandInterval;
- uint16_t dataValidTime[2];
- uint16_t busyOffset;
- uint16_t busyBitPolarity;
- uint32_t lookupTable[64];
- struct mg_flexspi_lut_seq lutCustomSeq[12];
- uint32_t reserved4[4];
- };
- struct mg_flexspi_nor_config {
- struct mg_flexspi_mem_config memConfig;
- uint32_t pageSize;
- uint32_t sectorSize;
- uint8_t ipcmdSerialClkFreq;
- uint8_t isUniformBlockSize;
- uint8_t reserved0[2];
- uint8_t serialNorType;
- uint8_t needExitNoCmdMode;
- uint8_t halfClkForNonReadCmd;
- uint8_t needRestoreNoCmdMode;
- uint32_t blockSize;
- uint32_t reserve2[11];
- };
- /* FLEXSPI memory config block related defintions */
- #define MG_FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian
- #define MG_FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0
- #define MG_FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \
- (MG_FLEXSPI_LUT_OPERAND0(op0) | MG_FLEXSPI_LUT_NUM_PADS0(pad0) | \
- MG_FLEXSPI_LUT_OPCODE0(cmd0) | MG_FLEXSPI_LUT_OPERAND1(op1) | \
- MG_FLEXSPI_LUT_NUM_PADS1(pad1) | MG_FLEXSPI_LUT_OPCODE1(cmd1))
- #define MG_CMD_SDR 0x01
- #define MG_CMD_DDR 0x21
- #define MG_DUMMY_SDR 0x0C
- #define MG_DUMMY_DDR 0x2C
- #define MG_DUMMY_RWDS_DDR 0x2D
- #define MG_RADDR_SDR 0x02
- #define MG_RADDR_DDR 0x22
- #define MG_CADDR_DDR 0x23
- #define MG_READ_SDR 0x09
- #define MG_READ_DDR 0x29
- #define MG_WRITE_SDR 0x08
- #define MG_WRITE_DDR 0x28
- #define MG_STOP 0
- #define MG_FLEXSPI_1PAD 0
- #define MG_FLEXSPI_2PAD 1
- #define MG_FLEXSPI_4PAD 2
- #define MG_FLEXSPI_8PAD 3
- #define MG_FLEXSPI_QSPI_LUT \
- { \
- [0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0xEB, MG_RADDR_SDR, \
- MG_FLEXSPI_4PAD, 0x18), \
- [1] = MG_FLEXSPI_LUT_SEQ(MG_DUMMY_SDR, MG_FLEXSPI_4PAD, 0x06, MG_READ_SDR, \
- MG_FLEXSPI_4PAD, 0x04), \
- [4 * 1 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x05, \
- MG_READ_SDR, MG_FLEXSPI_1PAD, 0x04), \
- [4 * 3 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x06, \
- MG_STOP, MG_FLEXSPI_1PAD, 0x0), \
- [4 * 5 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x20, \
- MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18), \
- [4 * 8 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0xD8, \
- MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18), \
- [4 * 9 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x02, \
- MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18), \
- [4 * 9 + 1] = MG_FLEXSPI_LUT_SEQ(MG_WRITE_SDR, MG_FLEXSPI_1PAD, 0x04, \
- MG_STOP, MG_FLEXSPI_1PAD, 0x0), \
- [4 * 11 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x60, \
- MG_STOP, MG_FLEXSPI_1PAD, 0x0), \
- }
- #define MG_FLEXSPI_HYPER_LUT \
- { \
- [0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xA0, MG_RADDR_DDR, \
- MG_FLEXSPI_8PAD, 0x18), \
- [1] = MG_FLEXSPI_LUT_SEQ(MG_CADDR_DDR, MG_FLEXSPI_8PAD, 0x10, \
- MG_DUMMY_DDR, MG_FLEXSPI_8PAD, 0x0C), \
- [2] = MG_FLEXSPI_LUT_SEQ(MG_READ_DDR, MG_FLEXSPI_8PAD, 0x04, MG_STOP, \
- MG_FLEXSPI_1PAD, 0x0), \
- [4 * 1 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \
- [4 * 1 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \
- [4 * 1 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \
- [4 * 1 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x70), \
- [4 * 2 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xA0, \
- MG_RADDR_DDR, MG_FLEXSPI_8PAD, 0x18), \
- [4 * 2 + 1] = \
- MG_FLEXSPI_LUT_SEQ(MG_CADDR_DDR, MG_FLEXSPI_8PAD, 0x10, \
- MG_DUMMY_RWDS_DDR, MG_FLEXSPI_8PAD, 0x0B), \
- [4 * 2 + 2] = MG_FLEXSPI_LUT_SEQ(MG_READ_DDR, MG_FLEXSPI_8PAD, 0x4, \
- MG_STOP, MG_FLEXSPI_1PAD, 0x0), \
- [4 * 3 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \
- [4 * 3 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \
- [4 * 3 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \
- [4 * 3 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \
- [4 * 4 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \
- [4 * 4 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x55), \
- [4 * 4 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x02), \
- [4 * 4 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x55), \
- [4 * 5 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \
- [4 * 5 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \
- [4 * 5 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \
- [4 * 5 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x80), \
- [4 * 6 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \
- [4 * 6 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \
- [4 * 6 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \
- [4 * 6 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \
- [4 * 7 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \
- [4 * 7 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x55), \
- [4 * 7 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x02), \
- [4 * 7 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x55), \
- [4 * 8 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_RADDR_DDR, MG_FLEXSPI_8PAD, 0x18), \
- [4 * 8 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CADDR_DDR, MG_FLEXSPI_8PAD, 0x10, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \
- [4 * 8 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x30, \
- MG_STOP, MG_FLEXSPI_1PAD, 0x0), \
- [4 * 9 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \
- [4 * 9 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \
- [4 * 9 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \
- [4 * 9 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xA0), \
- [4 * 10 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_RADDR_DDR, MG_FLEXSPI_8PAD, 0x18), \
- [4 * 10 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CADDR_DDR, MG_FLEXSPI_8PAD, 0x10, \
- MG_WRITE_DDR, MG_FLEXSPI_8PAD, 0x80), \
- [4 * 11 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \
- [4 * 11 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \
- [4 * 11 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \
- [4 * 11 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x80), \
- [4 * 12 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \
- [4 * 12 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \
- [4 * 12 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \
- [4 * 12 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \
- [4 * 13 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \
- [4 * 13 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x55), \
- [4 * 13 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x02), \
- [4 * 13 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x55), \
- [4 * 14 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0), \
- [4 * 14 + 1] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0xAA), \
- [4 * 14 + 2] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x05), \
- [4 * 14 + 3] = MG_FLEXSPI_LUT_SEQ(MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x0, \
- MG_CMD_DDR, MG_FLEXSPI_8PAD, 0x10), \
- }
- #define MG_LUT_CUSTOM_SEQ \
- { \
- {.seqNum = 0, .seqId = 0, .reserved = 0}, \
- {.seqNum = 2, .seqId = 1, .reserved = 0}, \
- {.seqNum = 2, .seqId = 3, .reserved = 0}, \
- {.seqNum = 4, .seqId = 5, .reserved = 0}, \
- {.seqNum = 2, .seqId = 9, .reserved = 0}, \
- {.seqNum = 4, .seqId = 11, .reserved = 0}, \
- }
- #define MG_FLEXSPI_LUT_OPERAND0(x) (((uint32_t) (((uint32_t) (x)))) & 0xFFU)
- #define MG_FLEXSPI_LUT_NUM_PADS0(x) \
- (((uint32_t) (((uint32_t) (x)) << 8U)) & 0x300U)
- #define MG_FLEXSPI_LUT_OPCODE0(x) \
- (((uint32_t) (((uint32_t) (x)) << 10U)) & 0xFC00U)
- #define MG_FLEXSPI_LUT_OPERAND1(x) \
- (((uint32_t) (((uint32_t) (x)) << 16U)) & 0xFF0000U)
- #define MG_FLEXSPI_LUT_NUM_PADS1(x) \
- (((uint32_t) (((uint32_t) (x)) << 24U)) & 0x3000000U)
- #define MG_FLEXSPI_LUT_OPCODE1(x) \
- (((uint32_t) (((uint32_t) (x)) << 26U)) & 0xFC000000U)
- #if MG_OTA == MG_OTA_RT1020 || MG_OTA == MG_OTA_RT1050
- // RT102X and RT105x boards support ROM API version 1.4
- struct mg_flexspi_nor_driver_interface {
- uint32_t version;
- int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config);
- int (*program)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t dst_addr, const uint32_t *src);
- uint32_t reserved;
- int (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t start, uint32_t lengthInBytes);
- uint32_t reserved2;
- int (*update_lut)(uint32_t instance, uint32_t seqIndex,
- const uint32_t *lutBase, uint32_t seqNumber);
- int (*xfer)(uint32_t instance, char *xfer);
- void (*clear_cache)(uint32_t instance);
- };
- #elif MG_OTA <= MG_OTA_RT1064
- // RT104x and RT106x support ROM API version 1.5
- struct mg_flexspi_nor_driver_interface {
- uint32_t version;
- int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config);
- int (*program)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t dst_addr, const uint32_t *src);
- int (*erase_all)(uint32_t instance, struct mg_flexspi_nor_config *config);
- int (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t start, uint32_t lengthInBytes);
- int (*read)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t *dst, uint32_t addr, uint32_t lengthInBytes);
- void (*clear_cache)(uint32_t instance);
- int (*xfer)(uint32_t instance, char *xfer);
- int (*update_lut)(uint32_t instance, uint32_t seqIndex,
- const uint32_t *lutBase, uint32_t seqNumber);
- int (*get_config)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t *option);
- };
- #else
- // RT117x support ROM API version 1.7
- struct mg_flexspi_nor_driver_interface {
- uint32_t version;
- int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config);
- int (*program)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t dst_addr, const uint32_t *src);
- int (*erase_all)(uint32_t instance, struct mg_flexspi_nor_config *config);
- int (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t start, uint32_t lengthInBytes);
- int (*read)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t *dst, uint32_t addr, uint32_t lengthInBytes);
- uint32_t reserved;
- int (*xfer)(uint32_t instance, char *xfer);
- int (*update_lut)(uint32_t instance, uint32_t seqIndex,
- const uint32_t *lutBase, uint32_t seqNumber);
- int (*get_config)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t *option);
- int (*erase_sector)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t address);
- int (*erase_block)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t address);
- void (*hw_reset)(uint32_t instance, uint32_t resetLogic);
- int (*wait_busy)(uint32_t instance, struct mg_flexspi_nor_config *config,
- bool isParallelMode, uint32_t address);
- int (*set_clock_source)(uint32_t instance, uint32_t clockSrc);
- void (*config_clock)(uint32_t instance, uint32_t freqOption,
- uint32_t sampleClkMode);
- };
- #endif
- #if MG_OTA <= MG_OTA_RT1064
- #define MG_FLEXSPI_BASE 0x402A8000
- #define flexspi_nor \
- (*((struct mg_flexspi_nor_driver_interface **) (*(uint32_t *) 0x0020001c + \
- 16)))
- #else
- #define MG_FLEXSPI_BASE 0x400CC000
- #define flexspi_nor \
- (*((struct mg_flexspi_nor_driver_interface **) (*(uint32_t *) 0x0021001c + \
- 12)))
- #endif
- static bool s_flash_irq_disabled;
- MG_IRAM static bool flash_page_start(volatile uint32_t *dst) {
- char *base = (char *) s_mg_flash_imxrt.start,
- *end = base + s_mg_flash_imxrt.size;
- volatile char *p = (char *) dst;
- return p >= base && p < end && ((p - base) % s_mg_flash_imxrt.secsz) == 0;
- }
- #if MG_OTA == MG_OTA_RT1050
- // Configuration for Hyper flash memory
- static struct mg_flexspi_nor_config default_config = {
- .memConfig =
- {
- .tag = MG_FLEXSPI_CFG_BLK_TAG,
- .version = MG_FLEXSPI_CFG_BLK_VERSION,
- .readSampleClkSrc = 3, // ReadSampleClk_LoopbackFromDqsPad
- .csHoldTime = 3,
- .csSetupTime = 3,
- .columnAddressWidth = 3u,
- .controllerMiscOption =
- MG_BIT(6) | MG_BIT(4) | MG_BIT(3) | MG_BIT(0),
- .deviceType = 1, // serial NOR
- .sflashPadType = 8,
- .serialClkFreq = 7, // 133MHz
- .sflashA1Size = 64 * 1024 * 1024,
- .dataValidTime = {15, 0},
- .busyOffset = 15,
- .busyBitPolarity = 1,
- .lutCustomSeqEnable = 0x1,
- .lookupTable = MG_FLEXSPI_HYPER_LUT,
- .lutCustomSeq = MG_LUT_CUSTOM_SEQ,
- },
- .pageSize = 512,
- .sectorSize = 256 * 1024,
- .ipcmdSerialClkFreq = 1,
- .serialNorType = 1u,
- .blockSize = 256 * 1024,
- .isUniformBlockSize = true};
- #else
- // Note: this QSPI configuration works for RTs supporting QSPI
- // Configuration for QSPI memory
- static struct mg_flexspi_nor_config default_config = {
- .memConfig = {.tag = MG_FLEXSPI_CFG_BLK_TAG,
- .version = MG_FLEXSPI_CFG_BLK_VERSION,
- .readSampleClkSrc = 1, // ReadSampleClk_LoopbackFromDqsPad
- .csHoldTime = 3,
- .csSetupTime = 3,
- .controllerMiscOption = MG_BIT(4),
- .deviceType = 1, // serial NOR
- .sflashPadType = 4,
- .serialClkFreq = 7, // 133MHz
- .sflashA1Size = 8 * 1024 * 1024,
- .lookupTable = MG_FLEXSPI_QSPI_LUT},
- .pageSize = 256,
- .sectorSize = 4 * 1024,
- .ipcmdSerialClkFreq = 1,
- .blockSize = 64 * 1024,
- .isUniformBlockSize = false};
- #endif
- // must reside in RAM, as flash will be erased
- MG_IRAM static int flexspi_nor_get_config(
- struct mg_flexspi_nor_config **config) {
- *config = &default_config;
- return 0;
- }
- #if 0
- // ROM API get_config call (ROM version >= 1.5)
- MG_IRAM static int flexspi_nor_get_config(
- struct mg_flexspi_nor_config **config) {
- uint32_t options[] = {0xc0000000, 0x00};
- MG_ARM_DISABLE_IRQ();
- uint32_t status =
- flexspi_nor->get_config(FLEXSPI_NOR_INSTANCE, *config, options);
- if (!s_flash_irq_disabled) {
- MG_ARM_ENABLE_IRQ();
- }
- if (status) {
- MG_ERROR(("Failed to extract flash configuration: status %u", status));
- }
- return status;
- }
- #endif
- MG_IRAM static void mg_spin(volatile uint32_t count) {
- while (count--) (void) 0;
- }
- MG_IRAM static void flash_wait(void) {
- while ((*((volatile uint32_t *) (MG_FLEXSPI_BASE + 0xE0)) & MG_BIT(1)) == 0)
- mg_spin(1);
- }
- MG_IRAM static bool flash_erase(struct mg_flexspi_nor_config *config,
- void *addr) {
- if (flash_page_start(addr) == false) {
- MG_ERROR(("%p is not on a sector boundary", addr));
- return false;
- }
- void *dst = (void *) ((char *) addr - (char *) s_mg_flash_imxrt.start);
- bool ok = (flexspi_nor->erase(FLEXSPI_NOR_INSTANCE, config, (uint32_t) dst,
- s_mg_flash_imxrt.secsz) == 0);
- MG_DEBUG(("Sector starting at %p erasure: %s", addr, ok ? "ok" : "fail"));
- return ok;
- }
- #if 0
- // standalone erase call
- MG_IRAM static bool mg_imxrt_erase(void *addr) {
- struct mg_flexspi_nor_config config, *config_ptr = &config;
- bool ret;
- // Interrupts must be disabled before calls to ROM API in RT1020 and 1060
- MG_ARM_DISABLE_IRQ();
- ret = (flexspi_nor_get_config(&config_ptr) == 0);
- if (ret) ret = flash_erase(config_ptr, addr);
- MG_ARM_ENABLE_IRQ();
- return ret;
- }
- #endif
- MG_IRAM static bool mg_imxrt_write(void *addr, const void *buf, size_t len) {
- struct mg_flexspi_nor_config config, *config_ptr = &config;
- bool ok = false;
- // Interrupts must be disabled before calls to ROM API in RT1020 and 1060
- MG_ARM_DISABLE_IRQ();
- if (flexspi_nor_get_config(&config_ptr) != 0) goto fwxit;
- if ((len % s_mg_flash_imxrt.align) != 0) {
- MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_imxrt.align));
- goto fwxit;
- }
- if ((char *) addr < (char *) s_mg_flash_imxrt.start) {
- MG_ERROR(("Invalid flash write address: %p", addr));
- goto fwxit;
- }
- uint32_t *dst = (uint32_t *) addr;
- uint32_t *src = (uint32_t *) buf;
- uint32_t *end = (uint32_t *) ((char *) buf + len);
- ok = true;
- while (ok && src < end) {
- if (flash_page_start(dst) && flash_erase(config_ptr, dst) == false) {
- ok = false;
- break;
- }
- uint32_t status;
- uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_imxrt.start;
- if ((char *) buf >= (char *) s_mg_flash_imxrt.start) {
- // If we copy from FLASH to FLASH, then we first need to copy the source
- // to RAM
- size_t tmp_buf_size = s_mg_flash_imxrt.align / sizeof(uint32_t);
- uint32_t tmp[tmp_buf_size];
- for (size_t i = 0; i < tmp_buf_size; i++) {
- flash_wait();
- tmp[i] = src[i];
- }
- status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, config_ptr,
- (uint32_t) dst_ofs, tmp);
- } else {
- status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, config_ptr,
- (uint32_t) dst_ofs, src);
- }
- src = (uint32_t *) ((char *) src + s_mg_flash_imxrt.align);
- dst = (uint32_t *) ((char *) dst + s_mg_flash_imxrt.align);
- if (status != 0) {
- ok = false;
- }
- }
- MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, ok ? "ok" : "fail"));
- fwxit:
- if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ();
- return ok;
- }
- MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) {
- char *scratch = p2 + s;
- for (size_t ofs = 0; ofs < s; ofs += ss) {
- MG_OTA_ROLLBACK_TIMER_FEED();
- mg_imxrt_write(scratch, p1 + ofs, ss);
- MG_OTA_ROLLBACK_TIMER_FEED();
- mg_imxrt_write(p1 + ofs, p2 + ofs, ss);
- MG_OTA_ROLLBACK_TIMER_FEED();
- mg_imxrt_write(p2 + ofs, scratch, ss);
- }
- *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
- }
- MG_IRAM static bool mg_imxrt_swap(void) {
- size_t ss = s_mg_flash_imxrt.secsz;
- char *p1 = (char *) s_mg_flash_imxrt.start;
- char *p2 = p1 + s_mg_flash_imxrt.size / 2;
- size_t s = s_mg_flash_imxrt.size / 2 - ss;
- MG_INFO(("Swapping partitions, %u bytes", s));
- MG_INFO(("Do NOT power off..."));
- MG_OTA_ROLLBACK_TIMER_FEED();
- mg_log_level = MG_LL_NONE;
- s_flash_irq_disabled = true;
- single_bank_swap(p1, p2, s, ss);
- return true; // unreachable
- }
- bool mg_ota_begin(size_t new_firmware_size) {
- size_t max = s_mg_flash_imxrt.size / 2 - s_mg_flash_imxrt.secsz;
- if (new_firmware_size > max) {
- MG_ERROR(("Firmware %lu too big for single-bank OTA, max %lu",
- new_firmware_size, max));
- return false;
- }
- return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_imxrt);
- }
- bool mg_ota_write(const void *buf, size_t len) {
- return mg_ota_flash_write(buf, len, &s_mg_flash_imxrt);
- }
- bool mg_ota_end(void) {
- mg_ota_flash_end(&s_mg_flash_imxrt);
- return false;
- }
- struct mg_flash *mg_flash = &s_mg_flash_imxrt;
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/ota_mcxn.c"
- #endif
- #if MG_OTA == MG_OTA_MCXN
- // - Flash phrase: 16 bytes; smallest portion programmed in one operation.
- // - Flash page: 128 bytes; largest portion programmed in one operation.
- // - Flash sector: 8 KB; smallest portion that can be erased in one operation.
- // - Flash API mg_flash_driver->program: "start" and "len" must be page-size
- // aligned; to use 'phrase', FMU register access is needed. Using ROM
- static bool mg_mcxn_write(void *, const void *, size_t);
- static bool mg_mcxn_swap(void);
- static struct mg_flash s_mg_flash_mcxn = {
- (void *) 0, // Start, filled at init
- 0, // Size, filled at init
- 0, // Sector size, filled at init
- 0, // Align, filled at init
- mg_mcxn_write,
- mg_mcxn_swap,
- };
- struct mg_flash_config {
- uint32_t addr;
- uint32_t size;
- uint32_t blocks;
- uint32_t page_size;
- uint32_t sector_size;
- uint32_t ffr[6];
- uint32_t reserved0[5];
- uint32_t *bootctx;
- bool useahb;
- };
- struct mg_flash_driver_interface {
- uint32_t version;
- uint32_t (*init)(struct mg_flash_config *);
- uint32_t (*erase)(struct mg_flash_config *, uint32_t start, uint32_t len,
- uint32_t key);
- uint32_t (*program)(struct mg_flash_config *, uint32_t start, uint8_t *src,
- uint32_t len);
- uint32_t (*verify_erase)(struct mg_flash_config *, uint32_t start,
- uint32_t len);
- uint32_t (*verify_program)(struct mg_flash_config *, uint32_t start,
- uint32_t len, const uint8_t *expected,
- uint32_t *addr, uint32_t *failed);
- uint32_t reserved1[12];
- uint32_t (*read)(struct mg_flash_config *, uint32_t start, uint8_t *dest,
- uint32_t len);
- uint32_t reserved2[4];
- uint32_t (*deinit)(struct mg_flash_config *);
- };
- #define mg_flash_driver \
- ((struct mg_flash_driver_interface *) (*((uint32_t *) 0x1303fc00 + 4)))
- #define MG_MCXN_FLASK_KEY (('k' << 24) | ('e' << 16) | ('f' << 8) | 'l')
- MG_IRAM static bool flash_sector_start(volatile uint32_t *dst) {
- char *base = (char *) s_mg_flash_mcxn.start,
- *end = base + s_mg_flash_mcxn.size;
- volatile char *p = (char *) dst;
- return p >= base && p < end && ((p - base) % s_mg_flash_mcxn.secsz) == 0;
- }
- MG_IRAM static bool flash_erase(struct mg_flash_config *config, void *addr) {
- if (flash_sector_start(addr) == false) {
- MG_ERROR(("%p is not on a sector boundary", addr));
- return false;
- }
- uint32_t dst =
- (uint32_t) addr - (uint32_t) s_mg_flash_mcxn.start; // future-proof
- uint32_t status = mg_flash_driver->erase(config, dst, s_mg_flash_mcxn.secsz,
- MG_MCXN_FLASK_KEY);
- bool ok = (status == 0);
- if (!ok) MG_ERROR(("Flash write error: %lu", status));
- MG_DEBUG(("Sector starting at %p erasure: %s", addr, ok ? "ok" : "fail"));
- return ok;
- }
- #if 0
- // read-while-write, no need to disable IRQs for standalone usage
- MG_IRAM static bool mg_mcxn_erase(void *addr) {
- uint32_t status;
- struct mg_flash_config config;
- if ((status = mg_flash_driver->init(&config)) != 0) {
- MG_ERROR(("Flash driver init error: %lu", status));
- return false;
- }
- bool ok = flash_erase(&config, addr);
- mg_flash_driver->deinit(&config);
- return ok;
- }
- #endif
- MG_IRAM static bool mg_mcxn_swap(void) {
- // TODO(): no devices so far
- return true;
- }
- static bool s_flash_irq_disabled;
- MG_IRAM static bool mg_mcxn_write(void *addr, const void *buf, size_t len) {
- bool ok = false;
- uint32_t status;
- struct mg_flash_config config;
- if ((status = mg_flash_driver->init(&config)) != 0) {
- MG_ERROR(("Flash driver init error: %lu", status));
- return false;
- }
- if ((len % s_mg_flash_mcxn.align) != 0) {
- MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_mcxn.align));
- goto fwxit;
- }
- if ((((size_t) addr - (size_t) s_mg_flash_mcxn.start) %
- s_mg_flash_mcxn.align) != 0) {
- MG_ERROR(("%p is not on a page boundary", addr));
- goto fwxit;
- }
- uint32_t *dst = (uint32_t *) addr;
- uint32_t *src = (uint32_t *) buf;
- uint32_t *end = (uint32_t *) ((char *) buf + len);
- ok = true;
- MG_ARM_DISABLE_IRQ();
- while (ok && src < end) {
- if (flash_sector_start(dst) && flash_erase(&config, dst) == false) {
- ok = false;
- break;
- }
- uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_mcxn.start;
- // assume source is in RAM or in a different bank or read-while-write
- status = mg_flash_driver->program(&config, dst_ofs, (uint8_t *) src,
- s_mg_flash_mcxn.align);
- src = (uint32_t *) ((char *) src + s_mg_flash_mcxn.align);
- dst = (uint32_t *) ((char *) dst + s_mg_flash_mcxn.align);
- if (status != 0) {
- MG_ERROR(("Flash write error: %lu", status));
- ok = false;
- }
- }
- if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ();
- MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, ok ? "ok" : "fail"));
- fwxit:
- mg_flash_driver->deinit(&config);
- return ok;
- }
- // try to swap (honor dual image), otherwise just overwrite
- MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) {
- char *tmp = mg_calloc(1, ss);
- // no stdlib calls here
- for (size_t ofs = 0; ofs < s; ofs += ss) {
- if (tmp != NULL)
- for (size_t i = 0; i < ss; i++) tmp[i] = p1[ofs + i];
- mg_mcxn_write(p1 + ofs, p2 + ofs, ss);
- if (tmp != NULL) mg_mcxn_write(p2 + ofs, tmp, ss);
- }
- *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
- }
- bool mg_ota_begin(size_t new_firmware_size) {
- uint32_t status;
- struct mg_flash_config config;
- if ((status = mg_flash_driver->init(&config)) != 0) {
- MG_ERROR(("Flash driver init error: %lu", status));
- return false;
- }
- s_mg_flash_mcxn.start = (void *) config.addr;
- s_mg_flash_mcxn.size = config.size;
- s_mg_flash_mcxn.secsz = config.sector_size;
- s_mg_flash_mcxn.align = config.page_size;
- mg_flash_driver->deinit(&config);
- MG_DEBUG(
- ("%lu-byte flash @%p, using %lu-byte sectors with %lu-byte-aligned pages",
- s_mg_flash_mcxn.size, s_mg_flash_mcxn.start, s_mg_flash_mcxn.secsz,
- s_mg_flash_mcxn.align));
- return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_mcxn);
- }
- bool mg_ota_write(const void *buf, size_t len) {
- return mg_ota_flash_write(buf, len, &s_mg_flash_mcxn);
- }
- bool mg_ota_end(void) {
- if (mg_ota_flash_end(&s_mg_flash_mcxn)) {
- if (0) { // is_dualbank()
- // TODO(): no devices so far
- *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
- } else {
- // Swap partitions. Pray power does not go away
- MG_INFO(("Swapping partitions, size %u (%u sectors)",
- s_mg_flash_mcxn.size,
- s_mg_flash_mcxn.size / s_mg_flash_mcxn.secsz));
- MG_INFO(("Do NOT power off..."));
- mg_log_level = MG_LL_NONE;
- s_flash_irq_disabled = true;
- // Runs in RAM, will reset when finished
- single_bank_swap(
- (char *) s_mg_flash_mcxn.start,
- (char *) s_mg_flash_mcxn.start + s_mg_flash_mcxn.size / 2,
- s_mg_flash_mcxn.size / 2, s_mg_flash_mcxn.secsz);
- }
- }
- return false;
- }
- struct mg_flash *mg_flash = &s_mg_flash_mcxn;
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/ota_picosdk.c"
- #endif
- #if MG_OTA == MG_OTA_PICOSDK
- // Both RP2040 and RP2350 have no flash, low-level flash access support in
- // bootrom, and high-level support in Pico-SDK (2.0+ for the RP2350)
- // - The RP2350 in RISC-V mode is not tested
- // NOTE(): See OTA design notes
- static bool mg_picosdk_write(void *, const void *, size_t);
- static bool mg_picosdk_swap(void);
- static struct mg_flash s_mg_flash_picosdk = {
- (void *) 0x10000000, // Start; functions handle offset
- #ifdef PICO_FLASH_SIZE_BYTES
- PICO_FLASH_SIZE_BYTES, // Size, from board definitions
- #else
- 0x200000, // Size, guess... is 2M enough ?
- #endif
- FLASH_SECTOR_SIZE, // Sector size, from hardware_flash
- FLASH_PAGE_SIZE, // Align, from hardware_flash
- mg_picosdk_write, mg_picosdk_swap,
- };
- #define MG_MODULO2(x, m) ((x) & ((m) -1))
- static bool __no_inline_not_in_flash_func(flash_sector_start)(
- volatile uint32_t *dst) {
- char *base = (char *) s_mg_flash_picosdk.start,
- *end = base + s_mg_flash_picosdk.size;
- volatile char *p = (char *) dst;
- return p >= base && p < end &&
- MG_MODULO2(p - base, s_mg_flash_picosdk.secsz) == 0;
- }
- static bool __no_inline_not_in_flash_func(flash_erase)(void *addr) {
- if (flash_sector_start(addr) == false) {
- MG_ERROR(("%p is not on a sector boundary", addr));
- return false;
- }
- void *dst = (void *) ((char *) addr - (char *) s_mg_flash_picosdk.start);
- flash_range_erase((uint32_t) dst, s_mg_flash_picosdk.secsz);
- MG_DEBUG(("Sector starting at %p erasure", addr));
- return true;
- }
- static bool s_flash_irq_disabled;
- static bool __no_inline_not_in_flash_func(mg_picosdk_write)(void *addr,
- const void *buf,
- size_t len) {
- if ((len % s_mg_flash_picosdk.align) != 0) {
- MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_picosdk.align));
- return false;
- }
- if ((((size_t) addr - (size_t) s_mg_flash_picosdk.start) %
- s_mg_flash_picosdk.align) != 0) {
- MG_ERROR(("%p is not on a page boundary", addr));
- return false;
- }
- uint32_t *dst = (uint32_t *) addr;
- uint32_t *src = (uint32_t *) buf;
- uint32_t *end = (uint32_t *) ((char *) buf + len);
- #ifndef __riscv
- MG_ARM_DISABLE_IRQ();
- #else
- asm volatile("csrrc zero, mstatus, %0" : : "i"(1 << 3) : "memory");
- #endif
- while (src < end) {
- uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_picosdk.start;
- if (flash_sector_start(dst) && flash_erase(dst) == false) break;
- // flash_range_program() runs in RAM and handles writing up to
- // FLASH_PAGE_SIZE bytes. Source must not be in flash
- flash_range_program((uint32_t) dst_ofs, (uint8_t *) src,
- s_mg_flash_picosdk.align);
- src = (uint32_t *) ((char *) src + s_mg_flash_picosdk.align);
- dst = (uint32_t *) ((char *) dst + s_mg_flash_picosdk.align);
- }
- if (!s_flash_irq_disabled) {
- #ifndef __riscv
- MG_ARM_ENABLE_IRQ();
- #else
- asm volatile("csrrs mstatus, %0" : : "i"(1 << 3) : "memory");
- #endif
- }
- MG_DEBUG(("Flash write %lu bytes @ %p.", len, dst));
- return true;
- }
- // just overwrite instead of swap
- static void __no_inline_not_in_flash_func(single_bank_swap)(char *p1, char *p2,
- size_t s,
- size_t ss) {
- char *tmp_1 = mg_calloc(1, ss); // copy from 1st partition
- if (tmp_1 == NULL) return;
- char *tmp_2 = mg_calloc(1, ss); // copy from 2nd partition
- if (tmp_2 == NULL) return;
- #if PICO_RP2040
- uint32_t xip[256 / sizeof(uint32_t)];
- void *dst_1 = (void *) ((char *) p1 - (char *) s_mg_flash_picosdk.start);
- void *dst_2 = (void *) ((char *) p2- (char *) s_mg_flash_picosdk.start);
- // use SDK function calls to get BootROM function pointers
- rom_connect_internal_flash_fn connect = (rom_connect_internal_flash_fn) rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH);
- rom_flash_exit_xip_fn xit = (rom_flash_exit_xip_fn) rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP);
- rom_flash_range_program_fn program = (rom_flash_range_program_fn) rom_func_lookup(ROM_FUNC_FLASH_RANGE_PROGRAM);
- rom_flash_flush_cache_fn flush = (rom_flash_flush_cache_fn) rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE);
- // no stdlib calls here.
- MG_ARM_DISABLE_IRQ();
- // 2nd bootloader (XIP) is in flash, SDK functions copy it to RAM on entry
- for (size_t i = 0; i < 256 / sizeof(uint32_t); i++)
- xip[i] = ((uint32_t *) (s_mg_flash_picosdk.start))[i];
- // flash has been erased, no XIP to copy. Only BootROM calls possible
- for (uint32_t ofs = 0; ofs < s; ofs += ss) {
- for (size_t i = 0; i < ss; i++) {
- tmp_1[i] = p1[ofs + i];
- tmp_2[i] = p2[ofs + i];
- }
- flash_range_erase((uint32_t) dst_1 + ofs, ss);
- flash_range_erase((uint32_t) dst_2 + ofs, ss);
- __compiler_memory_barrier();
- connect();
- xit();
- program((uint32_t) dst_1 + ofs, tmp_2, ss);
- program((uint32_t) dst_2 + ofs, tmp_1, ss);
- flush();
- ((void (*)(void))((intptr_t) xip + 1))(); // enter XIP again
- }
- *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; // AIRCR = SYSRESETREQ
- #else
- // RP2350 has BootRAM and copies second bootloader there, SDK uses that copy,
- // It might also be able to take advantage of partition swapping
- rom_reboot_fn reboot = (rom_reboot_fn) rom_func_lookup(ROM_FUNC_REBOOT);
- for (size_t ofs = 0; ofs < s; ofs += ss) {
- for (size_t i = 0; i < ss; i++) {
- tmp_1[i] = p1[ofs + i];
- tmp_2[i] = p2[ofs + i];
- }
- mg_picosdk_write(p1 + ofs, tmp_2, ss);
- mg_picosdk_write(p2 + ofs, tmp_1, ss);
- }
- reboot(BOOT_TYPE_NORMAL | 0x100, 1, 0, 0); // 0x100: NO_RETURN_ON_SUCCESS
- #endif
- }
- static bool __no_inline_not_in_flash_func(mg_picosdk_swap)(void) {
- // TODO(): RP2350 might have some A/B functionality (DS 5.1)
- // Swap partitions. Pray power does not go away
- MG_INFO(("Swapping partitions, size %u (%u sectors)",
- s_mg_flash_picosdk.size,
- s_mg_flash_picosdk.size / s_mg_flash_picosdk.secsz));
- MG_INFO(("Do NOT power off..."));
- mg_log_level = MG_LL_NONE;
- s_flash_irq_disabled = true;
- // Runs in RAM, will reset when finished or return on failure
- single_bank_swap(
- (char *) s_mg_flash_picosdk.start,
- (char *) s_mg_flash_picosdk.start + s_mg_flash_picosdk.size / 2,
- s_mg_flash_picosdk.size / 2, s_mg_flash_picosdk.secsz);
- return false;
- }
- bool mg_ota_begin(size_t new_firmware_size) {
- return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_picosdk);
- }
- bool mg_ota_write(const void *buf, size_t len) {
- return mg_ota_flash_write(buf, len, &s_mg_flash_picosdk);
- }
- bool mg_ota_end(void) {
- if (mg_ota_flash_end(&s_mg_flash_picosdk));
- return false;
- }
- struct mg_flash *mg_flash = &s_mg_flash_picosdk;
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/ota_rw612.c"
- #endif
- #if MG_OTA == MG_OTA_RW612
- MG_IRAM static bool mg_frdm_write(void *, const void *, size_t);
- static bool mg_frdm_swap(void);
- static struct mg_flash s_mg_flash_frdm = {(void *) 0x08000000, // Start,
- 0x200000, // Size
- 0x1000, // Sector size
- 0x100, // Align
- mg_frdm_write,
- mg_frdm_swap};
- struct mg_flexspi_lut_seq {
- uint8_t seqNum;
- uint8_t seqId;
- uint16_t reserved;
- };
- struct mg_flexspi_mem_config {
- uint32_t tag;
- uint32_t version;
- uint32_t reserved0;
- uint8_t readSampleClkSrc;
- uint8_t csHoldTime;
- uint8_t csSetupTime;
- uint8_t columnAddressWidth;
- uint8_t deviceModeCfgEnable;
- uint8_t deviceModeType;
- uint16_t waitTimeCfgCommands;
- struct mg_flexspi_lut_seq deviceModeSeq;
- uint32_t deviceModeArg;
- uint8_t configCmdEnable;
- uint8_t configModeType[3];
- struct mg_flexspi_lut_seq configCmdSeqs[3];
- uint32_t reserved1;
- uint32_t configCmdArgs[3];
- uint32_t reserved2;
- uint32_t controllerMiscOption;
- uint8_t deviceType;
- uint8_t sflashPadType;
- uint8_t serialClkFreq;
- uint8_t lutCustomSeqEnable;
- uint32_t reserved3[2];
- uint32_t sflashA1Size;
- uint32_t sflashA2Size;
- uint32_t sflashB1Size;
- uint32_t sflashB2Size;
- uint32_t csPadSettingOverride;
- uint32_t sclkPadSettingOverride;
- uint32_t dataPadSettingOverride;
- uint32_t dqsPadSettingOverride;
- uint32_t timeoutInMs;
- uint32_t commandInterval;
- uint16_t dataValidTime[2];
- uint16_t busyOffset;
- uint16_t busyBitPolarity;
- uint32_t lookupTable[64];
- struct mg_flexspi_lut_seq lutCustomSeq[12];
- uint32_t reserved4[4];
- };
- struct mg_flexspi_nor_config {
- struct mg_flexspi_mem_config memConfig;
- uint32_t pageSize;
- uint32_t sectorSize;
- uint8_t ipcmdSerialClkFreq;
- uint8_t isUniformBlockSize;
- uint8_t isDataOrderSwapped;
- uint8_t reserved0[1];
- uint8_t serialNorType;
- uint8_t needExitNoCmdMode;
- uint8_t halfClkForNonReadCmd;
- uint8_t needRestoreNoCmdMode;
- uint32_t blockSize;
- uint32_t flashStateCtx;
- uint32_t reserve2[10];
- };
- struct mg_flexspi_nor_driver_interface {
- uint32_t version;
- uint32_t (*init)(uint32_t instance, struct mg_flexspi_nor_config *config);
- uint32_t (*wait_busy)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t address, bool keepState);
- uint32_t (*page_program)(uint32_t instance,
- struct mg_flexspi_nor_config *config,
- uint32_t dstAddr, const uint32_t *src,
- bool keepState);
- uint32_t (*erase_all)(uint32_t instance,
- struct mg_flexspi_nor_config *config);
- uint32_t (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t start, uint32_t length);
- uint32_t (*erase_sector)(uint32_t instance,
- struct mg_flexspi_nor_config *config,
- uint32_t address);
- uint32_t (*erase_block)(uint32_t instance,
- struct mg_flexspi_nor_config *config,
- uint32_t address);
- uint32_t (*read)(uint32_t instance, struct mg_flexspi_nor_config *config,
- uint32_t *dst, uint32_t start, uint32_t bytes);
- void (*config_clock)(uint32_t instance, uint32_t freqOption,
- uint32_t sampleClkMode);
- uint32_t (*set_clock_source)(uint32_t clockSrc);
- uint32_t (*get_config)(uint32_t instance,
- struct mg_flexspi_nor_config *config,
- uint32_t *option);
- void (*hw_reset)(uint32_t instance, uint32_t reset_logic);
- uint32_t (*xfer)(uint32_t instance, char *xfer);
- uint32_t (*update_lut)(uint32_t instance, uint32_t seqIndex,
- const uint32_t *lutBase, uint32_t numberOfSeq);
- uint32_t (*partial_program)(uint32_t instance,
- struct mg_flexspi_nor_config *config,
- uint32_t dstAddr, const uint32_t *src,
- uint32_t length, bool keepState);
- };
- #define MG_FLEXSPI_CFG_BLK_TAG (0x42464346UL)
- #define MG_FLEXSPI_BASE 0x40134000UL
- #define MG_CMD_SDR 0x01
- #define MG_RADDR_SDR 0x02
- #define MG_WRITE_SDR 0x08
- #define MG_READ_SDR 0x09
- #define MG_DUMMY_SDR 0x0C
- #define MG_STOP_EXE 0
- #define MG_FLEXSPI_1PAD 0
- #define MG_FLEXSPI_4PAD 2
- #define MG_FLEXSPI_LUT_OPERAND0(x) (((x) &0xFF) << 0)
- #define MG_FLEXSPI_LUT_NUM_PADS0(x) (((x) &0x3) << 8)
- #define MG_FLEXSPI_LUT_OPCODE0(x) (((x) &0x3F) << 10)
- #define MG_FLEXSPI_LUT_OPERAND1(x) (((x) &0xFF) << 16)
- #define MG_FLEXSPI_LUT_NUM_PADS1(x) (((x) &0x3) << 24)
- #define MG_FLEXSPI_LUT_OPCODE1(x) (((x) &0x3F) << 26)
- #define MG_FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \
- (MG_FLEXSPI_LUT_OPERAND0(op0) | MG_FLEXSPI_LUT_NUM_PADS0(pad0) | \
- MG_FLEXSPI_LUT_OPCODE0(cmd0) | MG_FLEXSPI_LUT_OPERAND1(op1) | \
- MG_FLEXSPI_LUT_NUM_PADS1(pad1) | MG_FLEXSPI_LUT_OPCODE1(cmd1))
- struct mg_flexspi_nor_config default_config = {
- .memConfig =
- {
- .tag = MG_FLEXSPI_CFG_BLK_TAG,
- .version = 0,
- .readSampleClkSrc = 1,
- .csHoldTime = 3,
- .csSetupTime = 3,
- .deviceModeCfgEnable = 1,
- .deviceModeSeq = {.seqNum = 1, .seqId = 2},
- .deviceModeArg = 0x0740,
- .configCmdEnable = 0,
- .deviceType = 0x1,
- .sflashPadType = 4,
- .serialClkFreq = 4,
- .sflashA1Size = 0x4000000U,
- .sflashA2Size = 0,
- .sflashB1Size = 0,
- .sflashB2Size = 0,
- .lookupTable =
- {
- [0] =
- MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0xEB,
- MG_RADDR_SDR, MG_FLEXSPI_4PAD, 0x18),
- [1] =
- MG_FLEXSPI_LUT_SEQ(MG_DUMMY_SDR, MG_FLEXSPI_4PAD, 0x06,
- MG_READ_SDR, MG_FLEXSPI_4PAD, 0x04),
- [4 * 1 + 0] =
- MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x05,
- MG_READ_SDR, MG_FLEXSPI_1PAD, 0x04),
- [4 * 2 + 0] =
- MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x01,
- MG_WRITE_SDR, MG_FLEXSPI_1PAD, 0x02),
- [4 * 3 + 0] =
- MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x06,
- MG_STOP_EXE, MG_FLEXSPI_1PAD, 0x00),
- [4 * 5 + 0] =
- MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x20,
- MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18),
- [4 * 8 + 0] =
- MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x52,
- MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18),
- [4 * 9 + 0] =
- MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x02,
- MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18),
- [4 * 9 + 1] =
- MG_FLEXSPI_LUT_SEQ(MG_WRITE_SDR, MG_FLEXSPI_1PAD, 0x00,
- MG_STOP_EXE, MG_FLEXSPI_1PAD, 0x00),
- [4 * 11 + 0] =
- MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x60,
- MG_STOP_EXE, MG_FLEXSPI_1PAD, 0x00),
- },
- },
- .pageSize = 0x100,
- .sectorSize = 0x1000,
- .ipcmdSerialClkFreq = 0,
- .blockSize = 0x8000,
- };
- #define MG_FLEXSPI_NOR_INSTANCE 0
- #define MG_ROMAPI_ADDRESS 0x13030000U
- #define flexspi_nor \
- ((struct mg_flexspi_nor_driver_interface *) (( \
- (uint32_t *) MG_ROMAPI_ADDRESS)[5]))
- MG_IRAM static bool flash_page_start(volatile uint32_t *dst) {
- char *base = (char *) s_mg_flash_frdm.start,
- *end = base + s_mg_flash_frdm.size;
- volatile char *p = (char *) dst;
- return p >= base && p < end && ((p - base) % s_mg_flash_frdm.secsz) == 0;
- }
- MG_IRAM static int flexspi_nor_get_config(
- struct mg_flexspi_nor_config *config) {
- uint32_t option = 0xc0000004;
- return flexspi_nor->get_config(MG_FLEXSPI_NOR_INSTANCE, config, &option);
- }
- MG_IRAM static int flash_init(void) {
- static bool initialized = false;
- if (!initialized) {
- struct mg_flexspi_nor_config config;
- memset(&config, 0, sizeof(config));
- flexspi_nor->set_clock_source(0);
- flexspi_nor->config_clock(MG_FLEXSPI_NOR_INSTANCE, 1, 0);
- if (flexspi_nor->init(MG_FLEXSPI_NOR_INSTANCE, &default_config)) {
- return 1;
- }
- flexspi_nor_get_config(&config);
- if (flexspi_nor->init(MG_FLEXSPI_NOR_INSTANCE, &config)) {
- return 1;
- }
- initialized = true;
- }
- return 0;
- }
- MG_IRAM static bool flash_erase(struct mg_flexspi_nor_config *config,
- void *addr) {
- if (flash_page_start(addr) == false) {
- MG_ERROR(("%p is not on a sector boundary", addr));
- return false;
- }
- void *dst = (void *) ((char *) addr - (char *) s_mg_flash_frdm.start);
- bool ok = (flexspi_nor->erase_sector(MG_FLEXSPI_NOR_INSTANCE, config,
- (uint32_t) dst) == 0);
- MG_INFO(("Sector starting at %p erasure: %s", addr, ok ? "ok" : "fail"));
- return ok;
- }
- MG_IRAM bool mg_frdm_swap(void) {
- return true;
- }
- MG_IRAM static void flash_wait(void) {
- while ((*((volatile uint32_t *) (MG_FLEXSPI_BASE + 0xE0)) & MG_BIT(1)) == 0)
- (void) 0;
- }
- static bool s_flash_irq_disabled;
- MG_IRAM static bool mg_frdm_write(void *addr, const void *buf, size_t len) {
- struct mg_flexspi_nor_config config;
- bool ok = false;
- MG_ARM_DISABLE_IRQ();
- if (flash_init() != 0) goto fwxit;
- if (flexspi_nor_get_config(&config) != 0) goto fwxit;
- if ((len % s_mg_flash_frdm.align) != 0) {
- MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_frdm.align));
- goto fwxit;
- }
- if ((char *) addr < (char *) s_mg_flash_frdm.start) {
- MG_ERROR(("Invalid flash write address: %p", addr));
- goto fwxit;
- }
- uint32_t *dst = (uint32_t *) addr;
- uint32_t *src = (uint32_t *) buf;
- uint32_t *end = (uint32_t *) ((char *) buf + len);
- ok = true;
- while (ok && src < end) {
- if (flash_page_start(dst) && flash_erase(&config, dst) == false) {
- ok = false;
- break;
- }
- uint32_t status;
- uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_frdm.start;
- if ((char *) buf >= (char *) s_mg_flash_frdm.start &&
- (char *) buf <
- (char *) (s_mg_flash_frdm.start + s_mg_flash_frdm.size)) {
- // If we copy from FLASH to FLASH, then we first need to copy the source
- // to RAM
- size_t tmp_buf_size = s_mg_flash_frdm.align / sizeof(uint32_t);
- uint32_t tmp[tmp_buf_size];
- for (size_t i = 0; i < tmp_buf_size; i++) {
- flash_wait();
- tmp[i] = src[i];
- }
- status = flexspi_nor->page_program(MG_FLEXSPI_NOR_INSTANCE, &config,
- (uint32_t) dst_ofs, tmp, false);
- } else {
- status = flexspi_nor->page_program(MG_FLEXSPI_NOR_INSTANCE, &config,
- (uint32_t) dst_ofs, src, false);
- }
- src = (uint32_t *) ((char *) src + s_mg_flash_frdm.align);
- dst = (uint32_t *) ((char *) dst + s_mg_flash_frdm.align);
- if (status != 0) {
- ok = false;
- }
- }
- MG_INFO(("Flash write %lu bytes @ %p: %s.", len, dst, ok ? "ok" : "fail"));
- fwxit:
- if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ();
- return ok;
- }
- // just overwrite instead of swap
- MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) {
- // no stdlib calls here
- for (size_t ofs = 0; ofs < s; ofs += ss) {
- mg_frdm_write(p1 + ofs, p2 + ofs, ss);
- }
- *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
- }
- bool mg_ota_begin(size_t new_firmware_size) {
- return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_frdm);
- }
- bool mg_ota_write(const void *buf, size_t len) {
- return mg_ota_flash_write(buf, len, &s_mg_flash_frdm);
- }
- bool mg_ota_end(void) {
- if (mg_ota_flash_end(&s_mg_flash_frdm)) {
- if (0) { // is_dualbank()
- // TODO(): no devices so far
- *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
- } else {
- // Swap partitions. Pray power does not go away
- MG_INFO(("Swapping partitions, size %u (%u sectors)",
- s_mg_flash_frdm.size,
- s_mg_flash_frdm.size / s_mg_flash_frdm.secsz));
- MG_INFO(("Do NOT power off..."));
- mg_log_level = MG_LL_NONE;
- s_flash_irq_disabled = true;
- // Runs in RAM, will reset when finished
- single_bank_swap(
- (char *) s_mg_flash_frdm.start,
- (char *) s_mg_flash_frdm.start + s_mg_flash_frdm.size / 2,
- s_mg_flash_frdm.size / 2, s_mg_flash_frdm.secsz);
- }
- }
- return false;
- }
- struct mg_flash *mg_flash = &s_mg_flash_frdm;
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/ota_stm32f.c"
- #endif
- #if MG_OTA == MG_OTA_STM32F
- static bool mg_stm32f_write(void *, const void *, size_t);
- static bool mg_stm32f_swap(void);
- static struct mg_flash s_mg_flash_stm32f = {
- (void *) 0x08000000, // Start
- 0, // Size, FLASH_SIZE_REG
- 0, // Irregular sector size
- 32, // Align, 256 bit
- mg_stm32f_write,
- mg_stm32f_swap,
- };
- #define MG_FLASH_BASE 0x40023c00
- #define MG_FLASH_KEYR 0x04
- #define MG_FLASH_SR 0x0c
- #define MG_FLASH_CR 0x10
- #define MG_FLASH_OPTCR 0x14
- #define MG_FLASH_SIZE_REG_F7 0x1FF0F442
- #define MG_FLASH_SIZE_REG_F4 0x1FFF7A22
- #define STM_DBGMCU_IDCODE 0xE0042000
- #define STM_DEV_ID (MG_REG(STM_DBGMCU_IDCODE) & (MG_BIT(12) - 1))
- #define SYSCFG_MEMRMP 0x40013800
- #define MG_FLASH_SIZE_REG_LOCATION \
- ((STM_DEV_ID >= 0x449) ? MG_FLASH_SIZE_REG_F7 : MG_FLASH_SIZE_REG_F4)
- static size_t flash_size(void) {
- return (MG_REG(MG_FLASH_SIZE_REG_LOCATION) & 0xFFFF) * 1024;
- }
- MG_IRAM static size_t last_sector_size(void) {
- return (STM_DEV_ID >= 0x449 ? 256 : 128) * 1024;
- }
- MG_IRAM static int is_dualbank(void) {
- // only F42x/F43x series (0x419) support dual bank
- return STM_DEV_ID == 0x419;
- }
- MG_IRAM static void flash_unlock(void) {
- if (MG_REG(MG_FLASH_BASE + MG_FLASH_CR) & MG_BIT(31)) {
- MG_REG(MG_FLASH_BASE + MG_FLASH_KEYR) = 0x45670123;
- MG_REG(MG_FLASH_BASE + MG_FLASH_KEYR) = 0xcdef89ab;
- }
- }
- #define MG_FLASH_CONFIG_16_64_128 1 // used by STM32F7
- #define MG_FLASH_CONFIG_32_128_256 2 // used by STM32F4 and F2
- MG_IRAM static bool flash_page_start(volatile uint32_t *dst) {
- char *base = (char *) s_mg_flash_stm32f.start;
- char *end = base + s_mg_flash_stm32f.size;
- if (is_dualbank() && dst >= (uint32_t *) (base + (end - base) / 2)) {
- dst = (uint32_t *) ((uint32_t) dst - (end - base) / 2);
- }
- uint32_t flash_config = MG_FLASH_CONFIG_16_64_128;
- if (STM_DEV_ID >= 0x449) {
- flash_config = MG_FLASH_CONFIG_32_128_256;
- }
- volatile char *p = (char *) dst;
- if (p >= base && p < end) {
- if (p < base + 16 * 1024 * 4 * flash_config) {
- if ((p - base) % (16 * 1024 * flash_config) == 0) return true;
- } else if (p == base + 16 * 1024 * 4 * flash_config) {
- return true;
- } else if ((p - base) % (128 * 1024 * flash_config) == 0) {
- return true;
- }
- }
- return false;
- }
- MG_IRAM static int flash_sector(volatile uint32_t *addr) {
- char *base = (char *) s_mg_flash_stm32f.start;
- char *end = base + s_mg_flash_stm32f.size;
- bool addr_in_bank_2 = false;
- if (is_dualbank() && addr >= (uint32_t *) (base + (end - base) / 2)) {
- addr = (uint32_t *) ((uint32_t) addr - (end - base) / 2);
- addr_in_bank_2 = true;
- }
- volatile char *p = (char *) addr;
- uint32_t flash_config = MG_FLASH_CONFIG_16_64_128;
- if (STM_DEV_ID >= 0x449) {
- flash_config = MG_FLASH_CONFIG_32_128_256;
- }
- int sector = -1;
- if (p >= base && p < end) {
- if (p < base + 16 * 1024 * 4 * flash_config) {
- sector = (p - base) / (16 * 1024 * flash_config);
- } else if (p >= base + 64 * 1024 * flash_config &&
- p < base + 128 * 1024 * flash_config) {
- sector = 4;
- } else {
- sector = (p - base) / (128 * 1024 * flash_config) + 4;
- }
- }
- if (sector == -1) return -1;
- if (addr_in_bank_2) sector += 12; // a bank has 12 sectors
- return sector;
- }
- MG_IRAM static bool flash_is_err(void) {
- return MG_REG(MG_FLASH_BASE + MG_FLASH_SR) & ((MG_BIT(7) - 1) << 1);
- }
- MG_IRAM static void flash_wait(void) {
- while (MG_REG(MG_FLASH_BASE + MG_FLASH_SR) & (MG_BIT(16))) (void) 0;
- }
- MG_IRAM static void flash_clear_err(void) {
- flash_wait(); // Wait until ready
- MG_REG(MG_FLASH_BASE + MG_FLASH_SR) = 0xf2; // Clear all errors
- }
- MG_IRAM static bool mg_stm32f_erase(void *addr) {
- bool ok = false;
- if (flash_page_start(addr) == false) {
- MG_ERROR(("%p is not on a sector boundary", addr));
- } else {
- int sector = flash_sector(addr);
- if (sector < 0) return false;
- uint32_t sector_reg = sector;
- if (is_dualbank() && sector >= 12) {
- // 3.9.8 Flash control register (FLASH_CR) for F42xxx and F43xxx
- // BITS[7:3]
- sector_reg -= 12;
- sector_reg |= MG_BIT(4);
- }
- flash_unlock();
- flash_wait();
- uint32_t cr = MG_BIT(1); // SER
- cr |= MG_BIT(16); // STRT
- cr |= (sector_reg & 31) << 3; // sector
- MG_REG(MG_FLASH_BASE + MG_FLASH_CR) = cr;
- ok = !flash_is_err();
- MG_DEBUG(("Erase sector %lu @ %p %s. CR %#lx SR %#lx", sector, addr,
- ok ? "ok" : "fail", MG_REG(MG_FLASH_BASE + MG_FLASH_CR),
- MG_REG(MG_FLASH_BASE + MG_FLASH_SR)));
- // After we have erased the sector, set CR flags for programming
- // 2 << 8 is word write parallelism, bit(0) is PG. RM0385, section 3.7.5
- MG_REG(MG_FLASH_BASE + MG_FLASH_CR) = MG_BIT(0) | (2 << 8);
- flash_clear_err();
- }
- return ok;
- }
- MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss);
- static bool s_flash_irq_disabled;
- MG_IRAM static bool mg_stm32f_write(void *addr, const void *buf, size_t len) {
- if ((len % s_mg_flash_stm32f.align) != 0) {
- MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_stm32f.align));
- return false;
- }
- uint32_t *dst = (uint32_t *) addr;
- uint32_t *src = (uint32_t *) buf;
- uint32_t *end = (uint32_t *) ((char *) buf + len);
- bool ok = true;
- MG_ARM_DISABLE_IRQ();
- flash_unlock();
- flash_clear_err();
- MG_REG(MG_FLASH_BASE + MG_FLASH_CR) = MG_BIT(0) | MG_BIT(9); // PG, 32-bit
- flash_wait();
- MG_DEBUG(("Writing flash @ %p, %lu bytes", addr, len));
- while (ok && src < end) {
- if (flash_page_start(dst) && mg_stm32f_erase(dst) == false) break;
- *(volatile uint32_t *) dst++ = *src++;
- MG_DSB(); // ensure flash is written with no errors
- flash_wait();
- if (flash_is_err()) ok = false;
- }
- if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ();
- MG_DEBUG(("Flash write %lu bytes @ %p: %s. CR %#lx SR %#lx", len, dst,
- ok ? "ok" : "fail", MG_REG(MG_FLASH_BASE + MG_FLASH_CR),
- MG_REG(MG_FLASH_BASE + MG_FLASH_SR)));
- MG_REG(MG_FLASH_BASE + MG_FLASH_CR) &= ~MG_BIT(0); // Clear programming flag
- return ok;
- }
- MG_IRAM static bool mg_stm32f_swap(void) {
- // STM32 F42x/F43x support dual bank, however, the memory mapping
- // change will not be carried through a hard reset. Therefore, we use
- // the single-bank scratch-sector swap for this family as well.
- size_t ss = last_sector_size();
- char *p1 = (char *) s_mg_flash_stm32f.start;
- char *p2;
- size_t s;
- s_mg_flash_stm32f.size = flash_size();
- p2 = p1 + s_mg_flash_stm32f.size / 2;
- s = s_mg_flash_stm32f.size / 2 - ss;
- MG_INFO(("Swapping partitions, %u bytes", s));
- MG_INFO(("Do NOT power off..."));
- MG_OTA_ROLLBACK_TIMER_FEED();
- mg_log_level = MG_LL_NONE;
- s_flash_irq_disabled = true;
- single_bank_swap(p1, p2, s, ss);
- return true; // unreachable
- }
- // True exchange between first half [p1..p1+s) and second half [p2..p2+s),
- // using the last sector of the second half as scratch. Runs from RAM and resets.
- MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) {
- char *scratch = p2 + s;
- for (size_t ofs = 0; ofs < s; ofs += ss) {
- MG_OTA_ROLLBACK_TIMER_FEED();
- mg_stm32f_write(scratch, p1 + ofs, ss);
- MG_OTA_ROLLBACK_TIMER_FEED();
- mg_stm32f_write(p1 + ofs, p2 + ofs, ss);
- MG_OTA_ROLLBACK_TIMER_FEED();
- mg_stm32f_write(p2 + ofs, scratch, ss);
- }
- *(volatile uint32_t *) 0xe000ed0cU = 0x5fa0004U; // NVIC_SystemReset()
- }
- bool mg_ota_begin(size_t new_firmware_size) {
- size_t max;
- s_mg_flash_stm32f.size = flash_size();
- #ifdef __ZEPHYR__
- *((uint32_t *)0xE000ED94) = 0;
- MG_DEBUG(("Jailbreak %s", *((uint32_t *)0xE000ED94) == 0 ? "successful" : "failed"));
- #endif
- max = s_mg_flash_stm32f.size / 2 - last_sector_size();
- if (new_firmware_size > max) {
- MG_ERROR(("Firmware %lu too big for single-bank OTA, max %lu",
- new_firmware_size, max));
- return false;
- }
- return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_stm32f);
- }
- bool mg_ota_write(const void *buf, size_t len) {
- return mg_ota_flash_write(buf, len, &s_mg_flash_stm32f);
- }
- bool mg_ota_end(void) {
- mg_ota_flash_end(&s_mg_flash_stm32f);
- return false;
- }
- struct mg_flash *mg_flash = &s_mg_flash_stm32f;
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/ota_stm32h5.c"
- #endif
- #if MG_OTA == MG_OTA_STM32H5
- static bool mg_stm32h5_write(void *, const void *, size_t);
- static bool mg_stm32h5_swap(void);
- static struct mg_flash s_mg_flash_stm32h5 = {
- (void *) 0x08000000, // Start
- 0, // Size, FLASHSIZE_BASE
- 8 * 1024, // Sector size, 8k
- 16, // Align, 128 bit
- mg_stm32h5_write,
- mg_stm32h5_swap,
- };
- #define MG_FLASH_BASE 0x40022000 // Base address of the flash controller
- #define FLASH_KEYR (MG_FLASH_BASE + 0x4) // See RM0481 7.11
- #define FLASH_OPTKEYR (MG_FLASH_BASE + 0xc)
- #define FLASH_OPTCR (MG_FLASH_BASE + 0x1c)
- #define FLASH_NSSR (MG_FLASH_BASE + 0x20)
- #define FLASH_NSCR (MG_FLASH_BASE + 0x28)
- #define FLASH_NSCCR (MG_FLASH_BASE + 0x30)
- #define FLASH_OPTSR_CUR (MG_FLASH_BASE + 0x50)
- #define FLASH_OPTSR_PRG (MG_FLASH_BASE + 0x54)
- #ifndef FLASHSIZE_BASE
- #define FLASHSIZE_BASE 0x08FFF80CUL
- #endif
- static size_t flash_size(void) {
- uint32_t kb = MG_REG(FLASHSIZE_BASE) & 0xFFFF;
- return (kb == 0 || kb == 0xFFFF) ? 2 * 1024 * 1024 : kb * 1024;
- }
- static uint32_t sectors_per_bank(void) {
- return (uint32_t) (s_mg_flash_stm32h5.size / 2 / s_mg_flash_stm32h5.secsz);
- }
- static void flash_unlock(void) {
- if (MG_REG(FLASH_NSCR) & MG_BIT(0)) {
- MG_REG(FLASH_KEYR) = 0x45670123;
- MG_REG(FLASH_KEYR) = 0xcdef89ab;
- }
- if (MG_REG(FLASH_OPTCR) & MG_BIT(0)) {
- MG_REG(FLASH_OPTKEYR) = 0x08192a3b;
- MG_REG(FLASH_OPTKEYR) = 0x4c5d6e7f;
- }
- }
- static int flash_page_start(volatile uint32_t *dst) {
- char *base = (char *) s_mg_flash_stm32h5.start,
- *end = base + s_mg_flash_stm32h5.size;
- volatile char *p = (char *) dst;
- return p >= base && p < end && ((p - base) % s_mg_flash_stm32h5.secsz) == 0;
- }
- static bool flash_is_err(void) {
- return MG_REG(FLASH_NSSR) & ((MG_BIT(8) - 1) << 17); // RM0481 7.11.9
- }
- static void flash_wait(void) {
- while ((MG_REG(FLASH_NSSR) & MG_BIT(0)) &&
- (MG_REG(FLASH_NSSR) & MG_BIT(16)) == 0) {
- (void) 0;
- }
- }
- static void flash_clear_err(void) {
- flash_wait(); // Wait until ready
- MG_REG(FLASH_NSCCR) = ((MG_BIT(9) - 1) << 16U); // Clear all errors
- }
- static bool flash_bank_is_swapped(void) {
- return MG_REG(FLASH_OPTCR) & MG_BIT(31); // RM0481 7.11.8
- }
- static bool mg_stm32h5_erase(void *location) {
- bool ok = false;
- if (flash_page_start(location) == false) {
- MG_ERROR(("%p is not on a sector boundary"));
- } else {
- uintptr_t diff = (char *) location - (char *) s_mg_flash_stm32h5.start;
- uint32_t sector = diff / s_mg_flash_stm32h5.secsz;
- uint32_t bank_sectors = sectors_per_bank();
- uint32_t saved_cr = MG_REG(FLASH_NSCR); // Save CR value
- flash_unlock();
- flash_clear_err();
- MG_REG(FLASH_NSCR) = 0;
- if ((sector < bank_sectors && flash_bank_is_swapped()) ||
- (sector >= bank_sectors && !flash_bank_is_swapped())) {
- MG_REG(FLASH_NSCR) |= MG_BIT(31); // Set FLASH_CR_BKSEL
- }
- if (sector >= bank_sectors) sector -= bank_sectors;
- MG_REG(FLASH_NSCR) |= MG_BIT(2) | (sector << 6); // Erase | sector_num
- MG_REG(FLASH_NSCR) |= MG_BIT(5); // Start erasing
- flash_wait();
- ok = !flash_is_err();
- MG_DEBUG(("Erase sector %lu @ %p: %s. CR %#lx SR %#lx", sector, location,
- ok ? "ok" : "fail", MG_REG(FLASH_NSCR), MG_REG(FLASH_NSSR)));
- // mg_hexdump(location, 32);
- MG_REG(FLASH_NSCR) = saved_cr; // Restore saved CR
- }
- return ok;
- }
- static bool mg_stm32h5_swap(void) {
- uint32_t desired = flash_bank_is_swapped() ? 0 : MG_BIT(31);
- flash_unlock();
- flash_clear_err();
- MG_SET_BITS(MG_REG(FLASH_OPTSR_PRG), MG_BIT(31), desired);
- MG_REG(FLASH_OPTCR) |= MG_BIT(1); // OPTSTART; triggers auto-reset on H5
- while ((MG_REG(FLASH_OPTSR_CUR) & MG_BIT(31)) != desired) (void) 0;
- return true;
- }
- static bool mg_stm32h5_write(void *addr, const void *buf, size_t len) {
- if ((len % s_mg_flash_stm32h5.align) != 0) {
- MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_stm32h5.align));
- return false;
- }
- uint32_t *dst = (uint32_t *) addr;
- uint32_t *src = (uint32_t *) buf;
- uint32_t *end = (uint32_t *) ((char *) buf + len);
- bool ok = true;
- MG_ARM_DISABLE_IRQ();
- flash_unlock();
- flash_clear_err();
- MG_REG(FLASH_NSCR) = MG_BIT(1); // Set programming flag
- while (ok && src < end) {
- if (flash_page_start(dst) && mg_stm32h5_erase(dst) == false) {
- ok = false;
- break;
- }
- *(volatile uint32_t *) dst++ = *src++;
- flash_wait();
- if (flash_is_err()) ok = false;
- }
- MG_ARM_ENABLE_IRQ();
- MG_DEBUG(("Flash write %lu bytes @ %p: %s. CR %#lx SR %#lx", len, dst,
- flash_is_err() ? "fail" : "ok", MG_REG(FLASH_NSCR),
- MG_REG(FLASH_NSSR)));
- MG_REG(FLASH_NSCR) = 0; // Clear flags
- return ok;
- }
- bool mg_ota_begin(size_t new_firmware_size) {
- s_mg_flash_stm32h5.size = flash_size();
- #ifdef __ZEPHYR__
- *((uint32_t *)0xE000ED94) = 0;
- MG_DEBUG(("Jailbreak %s", *((uint32_t *)0xE000ED94) == 0 ? "successful" : "failed"));
- #endif
- return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_stm32h5);
- }
- bool mg_ota_write(const void *buf, size_t len) {
- return mg_ota_flash_write(buf, len, &s_mg_flash_stm32h5);
- }
- bool mg_ota_end(void) {
- if (!mg_ota_flash_end(&s_mg_flash_stm32h5)) return false;
- *(volatile uint32_t *) 0xe000ed0c = 0x5fa0004U; // NVIC_SystemReset()
- return true;
- }
- struct mg_flash *mg_flash = &s_mg_flash_stm32h5;
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/ota_stm32h7.c"
- #endif
- #if MG_OTA == MG_OTA_STM32H7 || MG_OTA == MG_OTA_STM32H7_DUAL_CORE
- // - H723/735 RM 4.3.3: Note: The application can simultaneously request a read
- // and a write operation through the AXI interface.
- // - We only need IRAM for partition swapping in the H723, however, all
- // related functions must reside in IRAM for this to be possible.
- // - Linker files for other devices won't define a .iram section so there's no
- // associated penalty
- static bool mg_stm32h7_write(void *, const void *, size_t);
- static bool mg_stm32h7_swap(void);
- static struct mg_flash s_mg_flash_stm32h7 = {
- (void *) 0x08000000, // Start
- 0, // Size, FLASH_SIZE_REG
- 128 * 1024, // Sector size, 128k
- 32, // Align, 256 bit
- mg_stm32h7_write,
- mg_stm32h7_swap,
- };
- #define FLASH_BASE1 0x52002000 // Base address for bank1
- #define FLASH_BASE2 0x52002100 // Base address for bank2
- #define FLASH_KEYR 0x04 // See RM0433 4.9.2
- #define FLASH_OPTKEYR 0x08
- #define FLASH_OPTCR 0x18
- #define FLASH_SR 0x10
- #define FLASH_CR 0x0c
- #define FLASH_CCR 0x14
- #define FLASH_OPTSR_CUR 0x1c
- #define FLASH_OPTSR_PRG 0x20
- #define FLASH_SIZE_REG 0x1ff1e880
- #define IS_DUALCORE() (MG_OTA == MG_OTA_STM32H7_DUAL_CORE)
- MG_IRAM static size_t flash_size(void) {
- size_t size = MG_REG(FLASH_SIZE_REG) * 1024;
- return IS_DUALCORE() ? size / 2 : size;
- }
- MG_IRAM static bool is_dualbank(void) {
- if (IS_DUALCORE()) {
- // H745/H755 and H747/H757 are running on dual core.
- // Using only the 1st bank (mapped to CM7), in order not to interfere
- // with the 2nd bank (CM4), possibly causing CM4 to boot unexpectedly.
- return false;
- }
- return (s_mg_flash_stm32h7.size < 2 * 1024 * 1024) ? false : true;
- }
- MG_IRAM static void flash_unlock(void) {
- if (MG_REG(FLASH_BASE1 + FLASH_CR) & MG_BIT(0)) {
- MG_REG(FLASH_BASE1 + FLASH_KEYR) = 0x45670123;
- MG_REG(FLASH_BASE1 + FLASH_KEYR) = 0xcdef89ab;
- }
- if (is_dualbank() && MG_REG(FLASH_BASE2 + FLASH_CR) & MG_BIT(0)) {
- MG_REG(FLASH_BASE2 + FLASH_KEYR) = 0x45670123;
- MG_REG(FLASH_BASE2 + FLASH_KEYR) = 0xcdef89ab;
- }
- if (MG_REG(FLASH_BASE1 + FLASH_OPTCR) & MG_BIT(0)) {
- MG_REG(FLASH_BASE1 + FLASH_OPTKEYR) = 0x08192a3b; // opt reg is "shared"
- MG_REG(FLASH_BASE1 + FLASH_OPTKEYR) = 0x4c5d6e7f; // thus unlock once
- }
- }
- MG_IRAM static bool flash_page_start(volatile uint32_t *dst) {
- char *base = (char *) s_mg_flash_stm32h7.start,
- *end = base + s_mg_flash_stm32h7.size;
- volatile char *p = (char *) dst;
- return p >= base && p < end && ((p - base) % s_mg_flash_stm32h7.secsz) == 0;
- }
- MG_IRAM static bool flash_is_err(uint32_t bank) {
- return MG_REG(bank + FLASH_SR) & ((MG_BIT(11) - 1) << 17); // RM0433 4.9.5
- }
- MG_IRAM static void flash_wait(uint32_t bank) {
- while (MG_REG(bank + FLASH_SR) & (MG_BIT(0) | MG_BIT(2))) (void) 0;
- }
- MG_IRAM static void flash_clear_err(uint32_t bank) {
- flash_wait(bank); // Wait until ready
- MG_REG(bank + FLASH_CCR) = ((MG_BIT(11) - 1) << 16U); // Clear all errors
- }
- MG_IRAM static bool flash_bank_is_swapped(uint32_t bank) {
- return MG_REG(bank + FLASH_OPTCR) & MG_BIT(31); // RM0433 4.9.7
- }
- // Figure out flash bank based on the address
- MG_IRAM static uint32_t flash_bank(void *addr) {
- size_t ofs = (char *) addr - (char *) s_mg_flash_stm32h7.start;
- if (!is_dualbank()) return FLASH_BASE1;
- return ofs < s_mg_flash_stm32h7.size / 2 ? FLASH_BASE1 : FLASH_BASE2;
- }
- // read-while-write, no need to disable IRQs for standalone usage
- MG_IRAM static bool mg_stm32h7_erase(void *addr) {
- bool ok = false;
- if (flash_page_start(addr) == false) {
- MG_ERROR(("%p is not on a sector boundary", addr));
- } else {
- uintptr_t diff = (char *) addr - (char *) s_mg_flash_stm32h7.start;
- uint32_t sector = diff / s_mg_flash_stm32h7.secsz;
- uint32_t bank = flash_bank(addr);
- uint32_t saved_cr = MG_REG(bank + FLASH_CR); // Save CR value
- flash_unlock();
- if (sector > 7) sector -= 8;
- flash_clear_err(bank);
- MG_REG(bank + FLASH_CR) = MG_BIT(5); // 32-bit write parallelism
- MG_REG(bank + FLASH_CR) |= (sector & 7U) << 8U; // Sector to erase
- MG_REG(bank + FLASH_CR) |= MG_BIT(2); // Sector erase bit
- MG_REG(bank + FLASH_CR) |= MG_BIT(7); // Start erasing
- flash_wait(bank);
- ok = !flash_is_err(bank);
- MG_DEBUG(("Erase sector %lu @ %p %s. CR %#lx SR %#lx", sector, addr,
- ok ? "ok" : "fail", MG_REG(bank + FLASH_CR),
- MG_REG(bank + FLASH_SR)));
- MG_REG(bank + FLASH_CR) = saved_cr; // Restore CR
- }
- return ok;
- }
- MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss);
- static bool s_flash_irq_disabled;
- MG_IRAM static bool mg_stm32h7_swap(void) {
- s_mg_flash_stm32h7.size = flash_size();
- if (!is_dualbank()) {
- // True sector-by-sector swap using the last sector of the second half as
- // scratch. Resets inside single_bank_swap, never returns.
- size_t ss = s_mg_flash_stm32h7.secsz;
- char *p1 = (char *) s_mg_flash_stm32h7.start;
- char *p2 = p1 + s_mg_flash_stm32h7.size / 2;
- size_t s = s_mg_flash_stm32h7.size / 2 - ss;
- MG_INFO(("Swapping partitions, %u bytes (%u sectors)", s, s / ss));
- MG_INFO(("Do NOT power off..."));
- MG_OTA_ROLLBACK_TIMER_FEED();
- mg_log_level = MG_LL_NONE;
- s_flash_irq_disabled = true;
- single_bank_swap(p1, p2, s, ss);
- return true; // unreachable
- }
- uint32_t bank = FLASH_BASE1;
- uint32_t desired = flash_bank_is_swapped(bank) ? 0 : MG_BIT(31);
- flash_unlock();
- flash_clear_err(bank);
- MG_SET_BITS(MG_REG(bank + FLASH_OPTSR_PRG), MG_BIT(31), desired);
- MG_REG(bank + FLASH_OPTCR) |= MG_BIT(1); // OPTSTART
- while ((MG_REG(bank + FLASH_OPTSR_CUR) & MG_BIT(31)) != desired) (void) 0;
- return true;
- }
- MG_IRAM static bool mg_stm32h7_write(void *addr, const void *buf, size_t len) {
- if ((len % s_mg_flash_stm32h7.align) != 0) {
- MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_stm32h7.align));
- return false;
- }
- uint32_t bank = flash_bank(addr);
- uint32_t *dst = (uint32_t *) addr;
- uint32_t *src = (uint32_t *) buf;
- uint32_t *end = (uint32_t *) ((char *) buf + len);
- bool ok = true;
- MG_ARM_DISABLE_IRQ();
- flash_unlock();
- flash_clear_err(bank);
- MG_REG(bank + FLASH_CR) = MG_BIT(1); // Set programming flag
- MG_REG(bank + FLASH_CR) |= MG_BIT(5); // 32-bit write parallelism
- while (ok && src < end) {
- if (flash_page_start(dst) && mg_stm32h7_erase(dst) == false) {
- ok = false;
- break;
- }
- *(volatile uint32_t *) dst++ = *src++;
- flash_wait(bank);
- if (flash_is_err(bank)) ok = false;
- }
- if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ();
- MG_DEBUG(("Flash write %lu bytes @ %p: %s. CR %#lx SR %#lx", len, addr,
- ok ? "ok" : "fail", MG_REG(bank + FLASH_CR),
- MG_REG(bank + FLASH_SR)));
- MG_REG(bank + FLASH_CR) &= ~MG_BIT(1); // Clear programming flag
- return ok;
- }
- // True sector-by-sector exchange between first half [p1..p1+s) and second half
- // [p2..p2+s), using the sector at p2+s as scratch. Runs from RAM. Resets on
- // completion. Symmetric: calling twice returns flash to original state.
- MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) {
- char *scratch = p2 + s; // Last sector of second half, reserved as scratch
- for (size_t ofs = 0; ofs < s; ofs += ss) {
- MG_OTA_ROLLBACK_TIMER_FEED();
- mg_stm32h7_write(scratch, p1 + ofs, ss); // Save p1[i] to scratch
- MG_OTA_ROLLBACK_TIMER_FEED();
- mg_stm32h7_write(p1 + ofs, p2 + ofs, ss); // Copy p2[i] to p1[i]
- MG_OTA_ROLLBACK_TIMER_FEED();
- mg_stm32h7_write(p2 + ofs, scratch, ss); // Copy scratch to p2[i]
- }
- *(volatile uint32_t *) 0xe000ed0cU = 0x5fa0004U; // NVIC_SystemReset()
- }
- bool mg_ota_begin(size_t new_firmware_size) {
- s_mg_flash_stm32h7.size = flash_size();
- #ifdef __ZEPHYR__
- *((uint32_t *) 0xE000ED94) = 0;
- MG_DEBUG(("Jailbreak %s", *((uint32_t *) 0xE000ED94) == 0 ? "ok" : "failed"));
- #endif
- if (!is_dualbank()) {
- // Last sector of the second half is reserved as swap scratch; enforce limit.
- size_t max = s_mg_flash_stm32h7.size / 2 - s_mg_flash_stm32h7.secsz;
- if (new_firmware_size > max) {
- MG_ERROR(("Firmware %lu too big for single-bank OTA, max %lu", new_firmware_size, max));
- return false;
- }
- }
- return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_stm32h7);
- }
- bool mg_ota_write(const void *buf, size_t len) {
- return mg_ota_flash_write(buf, len, &s_mg_flash_stm32h7);
- }
- bool mg_ota_end(void) {
- if (mg_ota_flash_end(&s_mg_flash_stm32h7)) {
- if (is_dualbank()) {
- // Bank swap is deferred until reset, been executing in flash, reset
- *(volatile uint32_t *) 0xe000ed0cU = 0x5fa0004U; // NVIC_SystemReset()
- }
- }
- return false;
- }
- struct mg_flash *mg_flash = &s_mg_flash_stm32h7;
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/printf.c"
- #endif
- size_t mg_queue_printf(struct mg_queue *q, const char *fmt, ...) {
- char *buf;
- size_t len;
- va_list ap1, ap2;
- va_start(ap1, fmt);
- len = mg_vsnprintf(NULL, 0, fmt, &ap1);
- va_end(ap1);
- if (len == 0 || mg_queue_book(q, &buf, len + 1) < len + 1)
- return 0; // Nah. Not enough space
- va_start(ap2, fmt);
- len = mg_vsnprintf(buf, len + 1, fmt, &ap2);
- mg_queue_add(q, len);
- va_end(ap2);
- 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
- }
- }
- void mg_pfn_iobuf_noresize(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 = {0, 0, 0, 0};
- size_t n;
- io.buf = (uint8_t *) buf, io.size = len;
- n = mg_vxprintf(mg_pfn_iobuf_noresize, &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->addr.ip);
- return print_ip4(out, arg, (uint8_t *) &addr->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));
- }
- static size_t print_mac(void (*out)(char, void *), void *arg, uint8_t *p) {
- return mg_xprintf(out, arg, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2],
- p[3], p[4], p[5]);
- }
- size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap) {
- uint8_t *p = va_arg(*ap, uint8_t *);
- return print_mac(out, arg, p);
- }
- static size_t print_ieee64(void (*out)(char, void *), void *arg, uint8_t *p) {
- return mg_xprintf(out, arg, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", p[0],
- p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
- }
- size_t mg_print_ieee64(void (*out)(char, void *), void *arg, va_list *ap) {
- uint8_t *p = va_arg(*ap, uint8_t *);
- return print_ieee64(out, arg, p);
- }
- #if MG_ENABLE_TCPIP
- size_t mg_print_l2addr(void (*out)(char, void *), void *arg, va_list *ap) {
- enum mg_l2type type = (enum mg_l2type) va_arg(*ap, int);
- switch (type) {
- case MG_TCPIP_L2_ETH:
- case MG_TCPIP_L2_PPPoE: {
- uint8_t *p = va_arg(*ap, uint8_t *);
- return print_mac(out, arg, p);
- } break;
- default: break;
- }
- return 0;
- }
- #endif
- 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] = {0, 0, '=', '='};
- tmp[0] = t[c1 >> 2], tmp[1] = 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);
- }
- size_t mg_print_html_esc(void (*out)(char, void *), void *arg, va_list *ap) {
- size_t i, n = 0;
- int len = va_arg(*ap, int);
- const char *s = va_arg(*ap, const char *);
- for (i = 0; i < (size_t) len; i++) {
- const char *esc = NULL;
- switch (s[i]) {
- // clang-format off
- case '&': esc = "&"; break;
- case '<': esc = "<"; break;
- case '>': esc = ">"; break;
- case '"': esc = """; break;
- default: break;
- // clang-format on
- }
- if (esc != NULL) {
- while (*esc != '\0') {
- out(*esc++, arg);
- n++;
- }
- } else {
- out(s[i], arg);
- n++;
- }
- }
- return n;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/queue.c"
- #endif
- #if (defined(__GNUC__) && (__GNUC__ > 4) || \
- (defined(__GNUC_MINOR__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \
- 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 *) mg_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;
- mg_free((void *) r->method.buf);
- mg_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.buf);
- }
- }
- void mg_rpc_process(struct mg_rpc_req *r) {
- int len, off = mg_json_get(r->frame, "$.method", &len);
- if (off > 0 && r->frame.buf[off] == '"') {
- struct mg_str method = mg_str_n(&r->frame.buf[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.buf); // 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.buf[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.buf[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.buf);
- }
- 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/sha256.c"
- #endif
- // https://github.com/B-Con/crypto-algorithms
- // Author: Brad Conte (brad AT bradconte.com)
- // Disclaimer: This code is presented "as is" without any guarantees.
- // Details: Defines the API for the corresponding SHA1 implementation.
- // Copyright: public domain
- #define ror(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
- #define ch(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
- #define maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
- #define ep0(x) (ror(x, 2) ^ ror(x, 13) ^ ror(x, 22))
- #define ep1(x) (ror(x, 6) ^ ror(x, 11) ^ ror(x, 25))
- #define sig0(x) (ror(x, 7) ^ ror(x, 18) ^ ((x) >> 3))
- #define sig1(x) (ror(x, 17) ^ ror(x, 19) ^ ((x) >> 10))
- static const uint32_t mg_sha256_k[64] = {
- 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
- 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
- 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
- 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
- 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
- 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
- 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
- 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
- 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
- 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
- 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
- void mg_sha256_init(mg_sha256_ctx *ctx) {
- ctx->len = 0;
- ctx->bits = 0;
- ctx->state[0] = 0x6a09e667;
- ctx->state[1] = 0xbb67ae85;
- ctx->state[2] = 0x3c6ef372;
- ctx->state[3] = 0xa54ff53a;
- ctx->state[4] = 0x510e527f;
- ctx->state[5] = 0x9b05688c;
- ctx->state[6] = 0x1f83d9ab;
- ctx->state[7] = 0x5be0cd19;
- }
- static void mg_sha256_chunk(mg_sha256_ctx *ctx) {
- int i, j;
- uint32_t a, b, c, d, e, f, g, h;
- uint32_t m[64];
- for (i = 0, j = 0; i < 16; ++i, j += 4)
- m[i] = (uint32_t) (((uint32_t) ctx->buffer[j] << 24) |
- ((uint32_t) ctx->buffer[j + 1] << 16) |
- ((uint32_t) ctx->buffer[j + 2] << 8) |
- ((uint32_t) ctx->buffer[j + 3]));
- for (; i < 64; ++i)
- m[i] = sig1(m[i - 2]) + m[i - 7] + sig0(m[i - 15]) + m[i - 16];
- a = ctx->state[0];
- b = ctx->state[1];
- c = ctx->state[2];
- d = ctx->state[3];
- e = ctx->state[4];
- f = ctx->state[5];
- g = ctx->state[6];
- h = ctx->state[7];
- for (i = 0; i < 64; ++i) {
- uint32_t t1 = h + ep1(e) + ch(e, f, g) + mg_sha256_k[i] + m[i];
- uint32_t t2 = ep0(a) + maj(a, b, c);
- h = g;
- g = f;
- f = e;
- e = d + t1;
- d = c;
- c = b;
- b = a;
- a = t1 + t2;
- }
- ctx->state[0] += a;
- ctx->state[1] += b;
- ctx->state[2] += c;
- ctx->state[3] += d;
- ctx->state[4] += e;
- ctx->state[5] += f;
- ctx->state[6] += g;
- ctx->state[7] += h;
- }
- void mg_sha256_update(mg_sha256_ctx *ctx, const unsigned char *data,
- size_t len) {
- size_t i;
- for (i = 0; i < len; i++) {
- ctx->buffer[ctx->len] = data[i];
- if ((++ctx->len) == 64) {
- mg_sha256_chunk(ctx);
- ctx->bits += 512;
- ctx->len = 0;
- }
- }
- }
- // TODO: make final reusable (remove side effects)
- void mg_sha256_final(unsigned char digest[32], mg_sha256_ctx *ctx) {
- uint32_t i = ctx->len;
- if (i < 56) {
- ctx->buffer[i++] = 0x80;
- while (i < 56) {
- ctx->buffer[i++] = 0x00;
- }
- } else {
- ctx->buffer[i++] = 0x80;
- while (i < 64) {
- ctx->buffer[i++] = 0x00;
- }
- mg_sha256_chunk(ctx);
- memset(ctx->buffer, 0, 56);
- }
- ctx->bits += ctx->len * 8;
- ctx->buffer[63] = (uint8_t) ((ctx->bits) & 0xff);
- ctx->buffer[62] = (uint8_t) ((ctx->bits >> 8) & 0xff);
- ctx->buffer[61] = (uint8_t) ((ctx->bits >> 16) & 0xff);
- ctx->buffer[60] = (uint8_t) ((ctx->bits >> 24) & 0xff);
- ctx->buffer[59] = (uint8_t) ((ctx->bits >> 32) & 0xff);
- ctx->buffer[58] = (uint8_t) ((ctx->bits >> 40) & 0xff);
- ctx->buffer[57] = (uint8_t) ((ctx->bits >> 48) & 0xff);
- ctx->buffer[56] = (uint8_t) ((ctx->bits >> 56) & 0xff);
- mg_sha256_chunk(ctx);
- for (i = 0; i < 4; ++i) {
- digest[i] = (uint8_t) ((ctx->state[0] >> (24 - i * 8)) & 0xff);
- digest[i + 4] = (uint8_t) ((ctx->state[1] >> (24 - i * 8)) & 0xff);
- digest[i + 8] = (uint8_t) ((ctx->state[2] >> (24 - i * 8)) & 0xff);
- digest[i + 12] = (uint8_t) ((ctx->state[3] >> (24 - i * 8)) & 0xff);
- digest[i + 16] = (uint8_t) ((ctx->state[4] >> (24 - i * 8)) & 0xff);
- digest[i + 20] = (uint8_t) ((ctx->state[5] >> (24 - i * 8)) & 0xff);
- digest[i + 24] = (uint8_t) ((ctx->state[6] >> (24 - i * 8)) & 0xff);
- digest[i + 28] = (uint8_t) ((ctx->state[7] >> (24 - i * 8)) & 0xff);
- }
- }
- void mg_sha256(uint8_t dst[32], uint8_t *data, size_t datasz) {
- mg_sha256_ctx ctx;
- mg_sha256_init(&ctx);
- mg_sha256_update(&ctx, data, datasz);
- mg_sha256_final(dst, &ctx);
- }
- void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data,
- size_t datasz) {
- mg_sha256_ctx ctx;
- uint8_t k[64] = {0};
- uint8_t o_pad[64], i_pad[64];
- unsigned int i;
- memset(i_pad, 0x36, sizeof(i_pad));
- memset(o_pad, 0x5c, sizeof(o_pad));
- if (keysz < 64) {
- if (keysz > 0) memmove(k, key, keysz);
- } else {
- mg_sha256_init(&ctx);
- mg_sha256_update(&ctx, key, keysz);
- mg_sha256_final(k, &ctx);
- }
- for (i = 0; i < sizeof(k); i++) {
- i_pad[i] ^= k[i];
- o_pad[i] ^= k[i];
- }
- mg_sha256_init(&ctx);
- mg_sha256_update(&ctx, i_pad, sizeof(i_pad));
- mg_sha256_update(&ctx, data, datasz);
- mg_sha256_final(dst, &ctx);
- mg_sha256_init(&ctx);
- mg_sha256_update(&ctx, o_pad, sizeof(o_pad));
- mg_sha256_update(&ctx, dst, 32);
- mg_sha256_final(dst, &ctx);
- }
- #define rotr64(x, n) (((x) >> (n)) | ((x) << (64 - (n))))
- #define ep064(x) (rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39))
- #define ep164(x) (rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41))
- #define sig064(x) (rotr64(x, 1) ^ rotr64(x, 8) ^ ((x) >> 7))
- #define sig164(x) (rotr64(x, 19) ^ rotr64(x, 61) ^ ((x) >> 6))
- static const uint64_t mg_sha256_k2[80] = {
- #if defined(__DCC__)
- 0x428a2f98d728ae22ull, 0x7137449123ef65cdull, 0xb5c0fbcfec4d3b2full,
- 0xe9b5dba58189dbbcull, 0x3956c25bf348b538ull, 0x59f111f1b605d019ull,
- 0x923f82a4af194f9bull, 0xab1c5ed5da6d8118ull, 0xd807aa98a3030242ull,
- 0x12835b0145706fbeull, 0x243185be4ee4b28cull, 0x550c7dc3d5ffb4e2ull,
- 0x72be5d74f27b896full, 0x80deb1fe3b1696b1ull, 0x9bdc06a725c71235ull,
- 0xc19bf174cf692694ull, 0xe49b69c19ef14ad2ull, 0xefbe4786384f25e3ull,
- 0x0fc19dc68b8cd5b5ull, 0x240ca1cc77ac9c65ull, 0x2de92c6f592b0275ull,
- 0x4a7484aa6ea6e483ull, 0x5cb0a9dcbd41fbd4ull, 0x76f988da831153b5ull,
- 0x983e5152ee66dfabull, 0xa831c66d2db43210ull, 0xb00327c898fb213full,
- 0xbf597fc7beef0ee4ull, 0xc6e00bf33da88fc2ull, 0xd5a79147930aa725ull,
- 0x06ca6351e003826full, 0x142929670a0e6e70ull, 0x27b70a8546d22ffcull,
- 0x2e1b21385c26c926ull, 0x4d2c6dfc5ac42aedull, 0x53380d139d95b3dfull,
- 0x650a73548baf63deull, 0x766a0abb3c77b2a8ull, 0x81c2c92e47edaee6ull,
- 0x92722c851482353bull, 0xa2bfe8a14cf10364ull, 0xa81a664bbc423001ull,
- 0xc24b8b70d0f89791ull, 0xc76c51a30654be30ull, 0xd192e819d6ef5218ull,
- 0xd69906245565a910ull, 0xf40e35855771202aull, 0x106aa07032bbd1b8ull,
- 0x19a4c116b8d2d0c8ull, 0x1e376c085141ab53ull, 0x2748774cdf8eeb99ull,
- 0x34b0bcb5e19b48a8ull, 0x391c0cb3c5c95a63ull, 0x4ed8aa4ae3418acbull,
- 0x5b9cca4f7763e373ull, 0x682e6ff3d6b2b8a3ull, 0x748f82ee5defb2fcull,
- 0x78a5636f43172f60ull, 0x84c87814a1f0ab72ull, 0x8cc702081a6439ecull,
- 0x90befffa23631e28ull, 0xa4506cebde82bde9ull, 0xbef9a3f7b2c67915ull,
- 0xc67178f2e372532bull, 0xca273eceea26619cull, 0xd186b8c721c0c207ull,
- 0xeada7dd6cde0eb1eull, 0xf57d4f7fee6ed178ull, 0x06f067aa72176fbaull,
- 0x0a637dc5a2c898a6ull, 0x113f9804bef90daeull, 0x1b710b35131c471bull,
- 0x28db77f523047d84ull, 0x32caab7b40c72493ull, 0x3c9ebe0a15c9bebcull,
- 0x431d67c49c100d4cull, 0x4cc5d4becb3e42b6ull, 0x597f299cfc657e2aull,
- 0x5fcb6fab3ad6faecull, 0x6c44198c4a475817ull
- #else
- 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f,
- 0xe9b5dba58189dbbc, 0x3956c25bf348b538, 0x59f111f1b605d019,
- 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242,
- 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
- 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235,
- 0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3,
- 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, 0x2de92c6f592b0275,
- 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
- 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f,
- 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725,
- 0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc,
- 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
- 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6,
- 0x92722c851482353b, 0xa2bfe8a14cf10364, 0xa81a664bbc423001,
- 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218,
- 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
- 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99,
- 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb,
- 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc,
- 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
- 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915,
- 0xc67178f2e372532b, 0xca273eceea26619c, 0xd186b8c721c0c207,
- 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba,
- 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
- 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
- 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a,
- 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
- #endif
- };
- static void mg_sha384_transform(mg_sha384_ctx *ctx, const uint8_t data[]) {
- uint64_t m[80];
- uint64_t a, b, c, d, e, f, g, h;
- int i, j;
- for (i = 0, j = 0; i < 16; ++i, j += 8)
- m[i] = ((uint64_t) data[j] << 56) | ((uint64_t) data[j + 1] << 48) |
- ((uint64_t) data[j + 2] << 40) | ((uint64_t) data[j + 3] << 32) |
- ((uint64_t) data[j + 4] << 24) | ((uint64_t) data[j + 5] << 16) |
- ((uint64_t) data[j + 6] << 8) | ((uint64_t) data[j + 7]);
- for (; i < 80; ++i)
- m[i] = sig164(m[i - 2]) + m[i - 7] + sig064(m[i - 15]) + m[i - 16];
- a = ctx->state[0];
- b = ctx->state[1];
- c = ctx->state[2];
- d = ctx->state[3];
- e = ctx->state[4];
- f = ctx->state[5];
- g = ctx->state[6];
- h = ctx->state[7];
- for (i = 0; i < 80; ++i) {
- uint64_t t1 = h + ep164(e) + ch(e, f, g) + mg_sha256_k2[i] + m[i];
- uint64_t t2 = ep064(a) + maj(a, b, c);
- h = g;
- g = f;
- f = e;
- e = d + t1;
- d = c;
- c = b;
- b = a;
- a = t1 + t2;
- }
- ctx->state[0] += a;
- ctx->state[1] += b;
- ctx->state[2] += c;
- ctx->state[3] += d;
- ctx->state[4] += e;
- ctx->state[5] += f;
- ctx->state[6] += g;
- ctx->state[7] += h;
- }
- void mg_sha384_init(mg_sha384_ctx *ctx) {
- ctx->datalen = 0;
- ctx->bitlen[0] = 0;
- ctx->bitlen[1] = 0;
- #if defined(__DCC__)
- ctx->state[0] = 0xcbbb9d5dc1059ed8ull;
- ctx->state[1] = 0x629a292a367cd507ull;
- ctx->state[2] = 0x9159015a3070dd17ull;
- ctx->state[3] = 0x152fecd8f70e5939ull;
- ctx->state[4] = 0x67332667ffc00b31ull;
- ctx->state[5] = 0x8eb44a8768581511ull;
- ctx->state[6] = 0xdb0c2e0d64f98fa7ull;
- ctx->state[7] = 0x47b5481dbefa4fa4ull;
- #else
- ctx->state[0] = 0xcbbb9d5dc1059ed8;
- ctx->state[1] = 0x629a292a367cd507;
- ctx->state[2] = 0x9159015a3070dd17;
- ctx->state[3] = 0x152fecd8f70e5939;
- ctx->state[4] = 0x67332667ffc00b31;
- ctx->state[5] = 0x8eb44a8768581511;
- ctx->state[6] = 0xdb0c2e0d64f98fa7;
- ctx->state[7] = 0x47b5481dbefa4fa4;
- #endif
- }
- void mg_sha384_update(mg_sha384_ctx *ctx, const uint8_t *data, size_t len) {
- size_t i;
- for (i = 0; i < len; ++i) {
- ctx->buffer[ctx->datalen] = data[i];
- ctx->datalen++;
- if (ctx->datalen == 128) {
- mg_sha384_transform(ctx, ctx->buffer);
- ctx->bitlen[1] += 1024;
- if (ctx->bitlen[1] < 1024) ctx->bitlen[0]++;
- ctx->datalen = 0;
- }
- }
- }
- void mg_sha384_final(uint8_t hash[48], mg_sha384_ctx *ctx) {
- size_t i = ctx->datalen;
- if (ctx->datalen < 112) {
- ctx->buffer[i++] = 0x80;
- while (i < 112) ctx->buffer[i++] = 0x00;
- } else {
- ctx->buffer[i++] = 0x80;
- while (i < 128) ctx->buffer[i++] = 0x00;
- mg_sha384_transform(ctx, ctx->buffer);
- memset(ctx->buffer, 0, 112);
- }
- ctx->bitlen[1] += ctx->datalen * 8;
- if (ctx->bitlen[1] < ctx->datalen * 8) ctx->bitlen[0]++;
- ctx->buffer[127] = (uint8_t) (ctx->bitlen[1]);
- ctx->buffer[126] = (uint8_t) (ctx->bitlen[1] >> 8);
- ctx->buffer[125] = (uint8_t) (ctx->bitlen[1] >> 16);
- ctx->buffer[124] = (uint8_t) (ctx->bitlen[1] >> 24);
- ctx->buffer[123] = (uint8_t) (ctx->bitlen[1] >> 32);
- ctx->buffer[122] = (uint8_t) (ctx->bitlen[1] >> 40);
- ctx->buffer[121] = (uint8_t) (ctx->bitlen[1] >> 48);
- ctx->buffer[120] = (uint8_t) (ctx->bitlen[1] >> 56);
- ctx->buffer[119] = (uint8_t) (ctx->bitlen[0]);
- ctx->buffer[118] = (uint8_t) (ctx->bitlen[0] >> 8);
- ctx->buffer[117] = (uint8_t) (ctx->bitlen[0] >> 16);
- ctx->buffer[116] = (uint8_t) (ctx->bitlen[0] >> 24);
- ctx->buffer[115] = (uint8_t) (ctx->bitlen[0] >> 32);
- ctx->buffer[114] = (uint8_t) (ctx->bitlen[0] >> 40);
- ctx->buffer[113] = (uint8_t) (ctx->bitlen[0] >> 48);
- ctx->buffer[112] = (uint8_t) (ctx->bitlen[0] >> 56);
- mg_sha384_transform(ctx, ctx->buffer);
- for (i = 0; i < 6; ++i) {
- hash[i * 8] = (uint8_t) ((ctx->state[i] >> 56) & 0xff);
- hash[i * 8 + 1] = (uint8_t) ((ctx->state[i] >> 48) & 0xff);
- hash[i * 8 + 2] = (uint8_t) ((ctx->state[i] >> 40) & 0xff);
- hash[i * 8 + 3] = (uint8_t) ((ctx->state[i] >> 32) & 0xff);
- hash[i * 8 + 4] = (uint8_t) ((ctx->state[i] >> 24) & 0xff);
- hash[i * 8 + 5] = (uint8_t) ((ctx->state[i] >> 16) & 0xff);
- hash[i * 8 + 6] = (uint8_t) ((ctx->state[i] >> 8) & 0xff);
- hash[i * 8 + 7] = (uint8_t) (ctx->state[i] & 0xff);
- }
- }
- void mg_sha384(uint8_t dst[48], uint8_t *data, size_t datasz) {
- mg_sha384_ctx ctx;
- mg_sha384_init(&ctx);
- mg_sha384_update(&ctx, data, datasz);
- mg_sha384_final(dst, &ctx);
- }
- #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
- uint64_t mg_boot_timestamp_ms = 0; // Updated by SNTP
- uint64_t mg_now(void) {
- return mg_millis() + mg_boot_timestamp_ms;
- }
- 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 epoch_milliseconds = -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 origin_time = gettimestamp((uint32_t *) &buf[24]);
- int64_t receive_time = gettimestamp((uint32_t *) &buf[32]);
- int64_t transmit_time = gettimestamp((uint32_t *) &buf[40]);
- int64_t now = (int64_t) mg_millis();
- int64_t latency = (now - origin_time) - (transmit_time - receive_time);
- epoch_milliseconds = transmit_time + latency / 2;
- mg_boot_timestamp_ms = (uint64_t) (epoch_milliseconds - now);
- } else {
- MG_ERROR(("unexpected version: %d", version));
- }
- return epoch_milliseconds;
- }
- static void sntp_cb(struct mg_connection *c, int ev, void *ev_data) {
- uint64_t *expiration_time = (uint64_t *) c->data;
- if (ev == MG_EV_OPEN) {
- *expiration_time = mg_millis() + 3000; // Store expiration time in 3s
- } else if (ev == MG_EV_CONNECT) {
- mg_sntp_request(c);
- } else if (ev == MG_EV_READ) {
- int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len);
- if (milliseconds > 0) {
- mg_boot_timestamp_ms = (uint64_t) milliseconds - mg_millis();
- mg_call(c, MG_EV_SNTP_TIME, (uint64_t *) &milliseconds);
- MG_DEBUG(("%lu got time: %lld ms from epoch", c->id, milliseconds));
- }
- // mg_iobuf_del(&c->recv, 0, c->recv.len); // Free receive buffer
- c->is_closing = 1;
- } else if (ev == MG_EV_POLL) {
- if (mg_millis() > *expiration_time) c->is_closing = 1;
- } else if (ev == MG_EV_CLOSE) {
- }
- (void) ev_data;
- }
- 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 *fn_data) {
- if (url == NULL) url = "udp://time.google.com:123";
- return mg_connect_svc(mgr, url, fn, fn_data, sntp_cb, NULL);
- }
- #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;
- memcpy(&usa->sin.sin_addr, a->addr.ip, sizeof(uint32_t));
- #if MG_ENABLE_IPV6
- if (a->is_ip6) {
- usa->sin.sin_family = AF_INET6;
- usa->sin6.sin6_port = a->port;
- usa->sin6.sin6_scope_id = a->scope_id;
- memcpy(&usa->sin6.sin6_addr, a->addr.ip, sizeof(a->addr.ip));
- 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;
- #if MG_ENABLE_IPV6
- if (is_ip6) {
- memcpy(a->addr.ip, &usa->sin6.sin6_addr, sizeof(a->addr.ip));
- a->port = usa->sin6.sin6_port;
- a->scope_id = (uint8_t) usa->sin6.sin6_scope_id;
- } else
- #endif
- {
- a->port = usa->sin.sin_port;
- memcpy(&a->addr.ip, &usa->sin.sin_addr, sizeof(uint32_t));
- }
- }
- 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));
- }
- }
- // Get the local 'addr' the stack will use to connect to 'to'
- void mg_getlocaddr(struct mg_connection *c, struct mg_addr *to,
- struct mg_addr *addr);
- void mg_getlocaddr(struct mg_connection *c, struct mg_addr *to,
- struct mg_addr *addr) {
- union usa usa;
- socklen_t slen;
- MG_SOCKET_TYPE fd;
- int rc, af = to->is_ip6 ? AF_INET6 : AF_INET;
- fd = socket(af, SOCK_DGRAM, IPPROTO_UDP);
- if (fd == MG_INVALID_SOCKET) {
- mg_error(c, "socket(): %d", MG_SOCK_ERR(-1));
- return;
- }
- // NOTE(): TI-RTOS NDK may require binding
- slen = tousa(to, &usa);
- if ((rc = connect(fd, &usa.sa, slen)) != 0) {
- mg_error(c, "connect: %d", MG_SOCK_ERR(rc));
- } else {
- setlocaddr(fd, addr);
- }
- closesocket(fd);
- }
- 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) {
- 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);
- }
- MG_VERBOSE(("%lu %ld %d", c->id, n, MG_SOCK_ERR(n)));
- if (MG_SOCK_PENDING(n)) return MG_IO_WAIT;
- if (MG_SOCK_RESET(n)) return MG_IO_ERR; // See #1507, #3031
- 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 %ld %lu:%lu:%lu %ld err %d", c->id, c->fd, c->send.len,
- c->recv.len, c->rtls.len, n, MG_SOCK_ERR(n)));
- iolog(c, (char *) buf, n, false);
- return n > 0;
- } else {
- return len == 0 || mg_iobuf_add(&c->send, c->send.len, buf, len) > 0;
- // returning 0 means an OOM condition (iobuf couldn't resize), yet this is
- // so far recoverable, let the caller decide
- }
- }
- 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_THREADX
- // NetxDuo fails to send large blocks of data to the non-blocking sockets
- (void) fd;
- // 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
- }
- void mg_multicast_add(struct mg_connection *c, char *ip);
- void mg_multicast_add(struct mg_connection *c, char *ip) {
- #if MG_ENABLE_RL
- MG_ERROR(("unsupported"));
- #elif MG_ENABLE_FREERTOS_TCP
- // TODO(): prvAllowIPPacketIPv4()
- #else
- // lwIP, Unix, Windows, Zephyr 4+(, AzureRTOS ?)
- #if MG_ENABLE_LWIP && !LWIP_IGMP
- MG_ERROR(("LWIP_IGMP not defined, no multicast support"));
- #else
- #if defined(__ZEPHYR__) && ZEPHYR_VERSION_CODE < 0x40000
- MG_ERROR(("struct ip_mreq not defined"));
- #else
- struct ip_mreq mreq;
- mreq.imr_multiaddr.s_addr = inet_addr(ip);
- mreq.imr_interface.s_addr = mg_htonl(INADDR_ANY);
- setsockopt(FD(c), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq,
- sizeof(mreq));
- #endif // !Zephyr
- #endif // !lwIP
- #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_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 %d", on, MG_SOCK_ERR(rc)));
- #elif defined(SO_REUSEADDR) && (!defined(LWIP_SOCKET) || SO_REUSE)
- } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
- sizeof(on))) != 0) {
- // 1. SO_REUSEADDR 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 MG_IPV6_V6ONLY
- // Bind only to the V6 address, not V4 address on this port
- } 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;
- }
- static long recv_raw(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);
- }
- MG_VERBOSE(("%lu %ld %d", c->id, n, MG_SOCK_ERR(n)));
- if (MG_SOCK_PENDING(n)) return MG_IO_WAIT;
- if (MG_SOCK_RESET(n)) return MG_IO_ERR; // See #1507, #3031
- if (n <= 0) return MG_IO_ERR;
- return n;
- }
- static bool ioalloc(struct mg_connection *c, struct mg_iobuf *io) {
- bool res = false;
- if (io->len >= MG_MAX_RECV_SIZE) {
- mg_error(c, "MG_MAX_RECV_SIZE");
- } else if (io->size <= io->len &&
- !mg_iobuf_resize(io, io->size + MG_IO_SIZE)) {
- mg_error(c, "OOM");
- } else {
- res = true;
- }
- return res;
- }
- // 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 = MG_IO_WAIT;
- if (ioalloc(c, &c->recv) == false) return; // Oopsie poopsie, OOM
- if (c->is_tls) {
- size_t rtls = c->rtls.len, pending = mg_tls_pending(c);
- long m = MG_IO_WAIT;
- if (c->rtls.len < 16 * 1024 + 40) { // TLS record, header, MAC, padding
- if (!ioalloc(c, &c->rtls)) return;
- n = recv_raw(c, (char *) &c->rtls.buf[c->rtls.len],
- c->rtls.size - c->rtls.len);
- if (n > 0) c->rtls.len += (size_t) n;
- }
- // There can still be > 16K from last iteration, always mg_tls_recv()
- if (c->is_tls_hs) {
- if (n != MG_IO_ERR || c->rtls.len > 0 || pending > 0) {
- mg_tls_handshake(c);
- }
- } else {
- m = mg_tls_recv(c, &c->recv.buf[c->recv.len], c->recv.size - c->recv.len);
- }
- if (n == MG_IO_ERR &&
- (m == MG_IO_ERR || (c->rtls.len == 0 && mg_tls_pending(c) == 0) ||
- (c->is_tls_hs && c->rtls.len == rtls &&
- mg_tls_pending(c) == pending))) {
- // Close only when we have fully drained both rtls and TLS buffers
- c->is_closing = 1; // or there's nothing we can do about it.
- if (m < 0) m = MG_IO_ERR; // but return last record data, see #3104
- }
- n = m;
- } else {
- n = recv_raw(c, &c->recv.buf[c->recv.len], c->recv.size - c->recv.len);
- }
- MG_DEBUG(("%lu %ld %lu:%lu:%lu %ld err %d", c->id, c->fd, c->send.len,
- c->recv.len, c->rtls.len, n, MG_SOCK_ERR(n)));
- iolog(c, (char *) &c->recv.buf[c->recv.len], 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);
- // TODO(): mg_tls_send() may return 0 forever on steady OOM
- MG_DEBUG(("%lu %ld 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;
- setlocaddr(FD(c), &c->loc);
- mg_call(c, MG_EV_CONNECT, NULL);
- MG_EPOLL_MOD(c, 0);
- if (c->is_tls_hs) mg_tls_handshake(c);
- if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
- } else {
- mg_error(c, "socket error");
- }
- }
- static void setsockopts(struct mg_connection *c) {
- #if MG_ENABLE_FREERTOS_TCP || MG_ARCH == MG_ARCH_THREADX || \
- 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 proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
- int rc, af = c->rem.is_ip6 ? AF_INET6 : AF_INET; // c->rem has resolved IP
- c->fd = S2PTR(socket(af, type, proto)); // 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
- setlocaddr(FD(c), &c->loc);
- 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
- setlocaddr(FD(c), &c->loc);
- mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user
- if (c->is_tls_hs) mg_tls_handshake(c);
- if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
- } else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake
- MG_DEBUG(("%lu %ld -> %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);
- #if MG_ENABLE_FREERTOS_TCP
- // FreeRTOS_accept() returns NULL to mean "no pending connection",
- // therefore we avoid retrying it forever on non-blocking listeners
- if (fd == NULL) {
- fd = MG_INVALID_SOCKET;
- break;
- }
- #endif
- } 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_THREADX || defined(__ECOS)
- // NetxDuo, 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 && !MG_ENABLE_EPOLL
- } 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;
- setlocaddr(fd, &c->loc); // set local addr to where the client connected to
- c->pfn = lsn->pfn;
- c->pfn_data = lsn->pfn_data;
- c->fn = lsn->fn;
- c->fn_data = lsn->fn_data;
- c->is_tls = lsn->is_tls;
- MG_DEBUG(("%lu %ld 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);
- if (!c->is_tls_hs) c->is_tls = 0; // user did not call mg_tls_init()
- }
- }
- 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);
- if (c->is_closing) ms = 1;
- }
- 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 (c->rtls.len > 0 || mg_tls_pending(c) > 0) ms = 1, c->is_readable = 1;
- if (can_write(c)) MG_EPOLL_MOD(c, 1);
- if (c->is_closing) ms = 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;
- if (c->rtls.len > 0 || mg_tls_pending(c) > 0) c->is_readable = 1;
- }
- }
- (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 (c->is_closing) ms = 1;
- if (skip_iotest(c)) {
- // Socket not valid, ignore
- } else {
- // Don't wait if TLS is ready
- if (c->rtls.len > 0 || mg_tls_pending(c) > 0) ms = 1;
- 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 (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);
- if (c->rtls.len > 0 || mg_tls_pending(c) > 0) c->is_readable = 1;
- }
- n++;
- }
- }
- #else
- struct timeval tv = {ms / 1000, (ms % 1000) * 1000}, tv_1ms = {0, 1000}, *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 (c->rtls.len > 0 || mg_tls_pending(c) > 0) tvp = &tv_1ms;
- if (FD(c) > maxfd) maxfd = FD(c);
- if (c->is_closing) tvp = &tv_1ms;
- }
- 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
- if (rc < 0) 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)) {
- #if MG_ARCH == MG_ARCH_THREADX
- // NetxDuo stack returns exceptions for listening connection after accept
- if (c->is_listening == 0) mg_error(c, "socket error");
- #else
- mg_error(c, "socket error");
- #endif
- } 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 (c->rtls.len > 0 || mg_tls_pending(c) > 0) c->is_readable = 1;
- }
- }
- #endif
- }
- static bool mg_socketpair(MG_SOCKET_TYPE sp[2], union usa usa[2]) {
- socklen_t n = sizeof(usa[0].sin);
- bool success = false;
- 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 ((sp[0] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != MG_INVALID_SOCKET &&
- (sp[1] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != 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;
- }
- if (!success) {
- 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;
- }
- return success;
- }
- // mg_wakeup() event handler
- static void wufn(struct mg_connection *c, int ev, void *ev_data) {
- if (ev == MG_EV_READ) {
- unsigned long *id = (unsigned long *) c->recv.buf;
- // MG_INFO(("Got data"));
- // mg_hexdump(c->recv.buf, c->recv.len);
- if (c->recv.len >= sizeof(*id)) {
- struct mg_connection *t;
- for (t = c->mgr->conns; t != NULL; t = t->next) {
- if (t->id == *id) {
- struct mg_str data = mg_str_n((char *) c->recv.buf + sizeof(*id),
- c->recv.len - sizeof(*id));
- mg_call(t, MG_EV_WAKEUP, &data);
- }
- }
- }
- c->recv.len = 0; // Consume received data
- } else if (ev == MG_EV_CLOSE) {
- closesocket(c->mgr->pipe.fd); // When we're closing, close the other
- c->mgr->pipe.fd = MG_INVALID_SOCKET; // side of the socketpair, too
- }
- (void) ev_data;
- }
- bool mg_wakeup_init(struct mg_mgr *mgr) {
- bool ok = false;
- if (mgr->pipe.fd == MG_INVALID_SOCKET) {
- 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)) {
- MG_ERROR(("Cannot create socket pair"));
- } else if ((c = mg_wrapfd(mgr, (int) sp[1], wufn, NULL)) == NULL) {
- closesocket(sp[0]);
- closesocket(sp[1]);
- sp[0] = sp[1] = MG_INVALID_SOCKET;
- } else {
- tomgaddr(&usa[0], &c->rem, false);
- MG_DEBUG(("%lu %ld pipe %ld", c->id, c->fd, (long) sp[0]));
- mgr->pipe.fd = sp[0];
- ok = true;
- }
- }
- return ok;
- }
- bool mg_wakeup(struct mg_mgr *mgr, unsigned long conn_id, const void *buf,
- size_t len) {
- if (mgr->pipe.fd != MG_INVALID_SOCKET && conn_id > 0) {
- char *extended_buf = (char *) alloca(len + sizeof(conn_id));
- memcpy(extended_buf, &conn_id, sizeof(conn_id));
- memcpy(extended_buf + sizeof(conn_id), buf, len);
- send(mgr->pipe.fd, extended_buf, len + sizeof(conn_id), MSG_NONBLOCKING);
- return true;
- }
- return false;
- }
- 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);
- mg_ota_poll(mgr);
- 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 %lu %lu", 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', mg_tls_pending(c), c->rtls.len));
- 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_readable) read_conn(c);
- if (c->is_writable) write_conn(c);
- if (c->is_tls && !c->is_tls_hs && c->send.len == 0) mg_tls_flush(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 == '>' && len >= 2 && buf[len - 1] == '-' && buf[len - 2] == '-') {
- buf[len++] = (char) (ch & 0xff);
- buf[len] = '\0';
- if (sscanf(buf, "<!--#include file=\"%[^\"]", arg) > 0) {
- 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 (!mg_path_is_sane(mg_str(tmp))) {
- MG_ERROR(("SSI include path traversal blocked: %s", arg));
- } else if (depth < MG_MAX_SSI_DEPTH &&
- (data = mg_ssi(tmp, root, depth + 1)) != NULL) {
- size_t datalen = strlen(data);
- size_t ret = mg_iobuf_add(&b, b.len, data, datalen);
- mg_free(data);
- if (datalen > 0 && ret == 0) goto fail;
- } else {
- MG_ERROR(("%s: file=%s error or too deep", path, arg));
- } // TODO(): or OOM at recursive call
- } else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg) > 0) {
- char tmp[MG_PATH_MAX + MG_SSI_BUFSIZ + 10], *data;
- mg_snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
- if (!mg_path_is_sane(mg_str(tmp))) {
- MG_ERROR(("SSI include path traversal blocked: %s", arg));
- } else if (depth < MG_MAX_SSI_DEPTH &&
- (data = mg_ssi(tmp, root, depth + 1)) != NULL) {
- size_t datalen = strlen(data);
- size_t ret = mg_iobuf_add(&b, b.len, data, datalen);
- mg_free(data);
- if (datalen > 0 && ret == 0) goto fail;
- } else {
- MG_ERROR(("%s: virtual=%s error or too deep", path, arg));
- } // TODO(): or OOM at recursive call
- } else {
- // Unknown SSI tag
- MG_ERROR(("Unknown SSI tag: %.*s", (int) len, buf));
- if (len > 0 && mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
- }
- intag = 0;
- len = 0;
- } else if (ch == '<') {
- intag = 1;
- if (len > 0 && mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
- 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)) {
- if (mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
- len = 0;
- }
- }
- }
- if (len > 0 && mg_iobuf_add(&b, b.len, buf, len) == 0) goto fail;
- if (b.len > 0 && mg_iobuf_add(&b, b.len, "", 1) == 0) // nul-terminate
- goto fail;
- fclose(fp);
- }
- (void) depth;
- (void) root;
- return (char *) b.buf;
- fail:
- fclose(fp);
- mg_iobuf_free(&b);
- return NULL;
- }
- 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);
- if (data == NULL) {
- mg_error(c, "OOM");
- return;
- }
- mg_http_reply(c, 200, headers, "%s", data == NULL ? "" : data);
- mg_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;
- str.buf = (char *) s, str.len = (s == NULL) ? 0 : strlen(s);
- return str;
- }
- struct mg_str mg_str_n(const char *s, size_t n) {
- struct mg_str str;
- str.buf = (char *) s, str.len = n;
- return str;
- }
- static int mg_tolc(char c) {
- return (c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c;
- }
- int mg_casecmp(const char *s1, const char *s2) {
- int diff = 0;
- do {
- int c = mg_tolc(*s1++), d = mg_tolc(*s2++);
- diff = c - d;
- } while (diff == 0 && s1[-1] != '\0');
- return diff;
- }
- struct mg_str mg_strdup(const struct mg_str s) {
- struct mg_str r = {NULL, 0};
- if (s.len > 0 && s.buf != NULL) {
- char *sc = (char *) mg_calloc(1, s.len + 1);
- if (sc != NULL) {
- memcpy(sc, s.buf, s.len);
- sc[s.len] = '\0';
- r.buf = 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.buf[i];
- int c2 = str2.buf[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;
- }
- int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2) {
- size_t i = 0;
- while (i < str1.len && i < str2.len) {
- int c1 = mg_tolc(str1.buf[i]);
- int c2 = mg_tolc(str2.buf[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;
- }
- 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->buf = NULL, caps->len = 0;
- while (i < p.len || j < s.len) {
- if (i < p.len && j < s.len &&
- (p.buf[i] == '?' ||
- (p.buf[i] != '*' && p.buf[i] != '#' && s.buf[j] == p.buf[i]))) {
- if (caps == NULL) {
- } else if (p.buf[i] == '?') {
- caps->buf = &s.buf[j], caps->len = 1; // Finalize `?` cap
- caps++, caps->buf = NULL, caps->len = 0; // Init next cap
- } else if (caps->buf != NULL && caps->len == 0) {
- caps->len = (size_t) (&s.buf[j] - caps->buf); // Finalize current cap
- caps++, caps->len = 0, caps->buf = NULL; // Init next cap
- }
- i++, j++;
- } else if (i < p.len && (p.buf[i] == '*' || p.buf[i] == '#')) {
- if (caps && !caps->buf) caps->len = 0, caps->buf = &s.buf[j]; // Init cap
- ni = i++, nj = j + 1;
- } else if (nj > 0 && nj <= s.len &&
- ((ni < p.len && p.buf[ni] == '#') ||
- (j < s.len && s.buf[j] != '/'))) {
- i = ni, j = nj;
- if (caps && caps->buf == NULL && caps->len == 0) {
- caps--, caps->len = 0; // Restart previous cap
- }
- } else {
- return false;
- }
- }
- if (caps && caps->buf && caps->len == 0) {
- caps->len = (size_t) (&s.buf[j] - caps->buf);
- }
- return true;
- }
- bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char sep) {
- if (s.len == 0 || s.buf == NULL) {
- return false; // Empty string, nothing to span - fail
- } else {
- size_t len = 0;
- while (len < s.len && s.buf[len] != sep) len++; // Find separator
- if (a) *a = mg_str_n(s.buf, len); // Init a
- if (b) *b = mg_str_n(s.buf + len, s.len - len); // Init b
- if (b && len < s.len) b->buf++, b->len--; // Skip separator
- return true;
- }
- }
- bool mg_str_to_num(struct mg_str str, int base, void *val, size_t val_len) {
- size_t i = 0, ndigits = 0;
- uint64_t max = val_len == sizeof(uint8_t) ? 0xFF
- : val_len == sizeof(uint16_t) ? 0xFFFF
- : val_len == sizeof(uint32_t) ? 0xFFFFFFFF
- : (uint64_t) ~0;
- uint64_t result = 0;
- if (max == (uint64_t) ~0 && val_len != sizeof(uint64_t)) return false;
- if (base == 0 && str.len >= 2) {
- if (str.buf[i] == '0') {
- i++;
- base = str.buf[i] == 'b' ? 2 : str.buf[i] == 'x' ? 16 : 10;
- if (base != 10) ++i;
- } else {
- base = 10;
- }
- }
- switch (base) {
- case 2:
- while (i < str.len && (str.buf[i] == '0' || str.buf[i] == '1')) {
- uint64_t digit = (uint64_t) (str.buf[i] - '0');
- if (result > max / 2) return false; // Overflow
- result *= 2;
- if (result > max - digit) return false; // Overflow
- result += digit;
- i++, ndigits++;
- }
- break;
- case 10:
- while (i < str.len && str.buf[i] >= '0' && str.buf[i] <= '9') {
- uint64_t digit = (uint64_t) (str.buf[i] - '0');
- if (result > max / 10) return false; // Overflow
- result *= 10;
- if (result > max - digit) return false; // Overflow
- result += digit;
- i++, ndigits++;
- }
- break;
- case 16:
- while (i < str.len) {
- char c = str.buf[i];
- uint64_t digit = (c >= '0' && c <= '9') ? (uint64_t) (c - '0')
- : (c >= 'A' && c <= 'F') ? (uint64_t) (c - '7')
- : (c >= 'a' && c <= 'f') ? (uint64_t) (c - 'W')
- : (uint64_t) ~0;
- if (digit == (uint64_t) ~0) break;
- if (result > max / 16) return false; // Overflow
- result *= 16;
- if (result > max - digit) return false; // Overflow
- result += digit;
- i++, ndigits++;
- }
- break;
- default: return false;
- }
- if (ndigits == 0) return false;
- if (i != str.len) return false;
- if (val_len == 1) {
- *((uint8_t *) val) = (uint8_t) result;
- } else if (val_len == 2) {
- *((uint16_t *) val) = (uint16_t) result;
- } else if (val_len == 4) {
- *((uint32_t *) val) = (uint32_t) result;
- } else {
- *((uint64_t *) val) = (uint64_t) result;
- }
- return true;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/timer.c"
- #endif
- void mg_timer_init(struct mg_timer **head, struct mg_timer *t, uint64_t ms,
- unsigned flags, void (*fn)(void *), void *arg) {
- 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;
- // If this timer is not repeating and marked AUTODELETE, remove it
- if (!(t->flags & MG_TIMER_REPEAT) && (t->flags & MG_TIMER_AUTODELETE)) {
- mg_timer_free(head, t);
- mg_free(t);
- }
- }
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tls_aes128.c"
- #endif
- /******************************************************************************
- *
- * THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
- *
- * This is a simple and straightforward implementation of the AES Rijndael
- * 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus
- * of this work was correctness & accuracy. It is written in 'C' without any
- * particular focus upon optimization or speed. It should be endian (memory
- * byte order) neutral since the few places that care are handled explicitly.
- *
- * This implementation of Rijndael was created by Steven M. Gibson of GRC.com.
- *
- * It is intended for general purpose use, but was written in support of GRC's
- * reference implementation of the SQRL (Secure Quick Reliable Login) client.
- *
- * See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
- *
- * NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
- * REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
- *
- *******************************************************************************/
- #if MG_TLS == MG_TLS_BUILTIN
- /******************************************************************************/
- #define AES_DECRYPTION 1 // whether AES decryption is supported
- /******************************************************************************/
- #define MG_ENCRYPT 1 // specify whether we're encrypting
- #define MG_DECRYPT 0 // or decrypting
- /******************************************************************************
- * AES_INIT_KEYGEN_TABLES : MUST be called once before any AES use
- ******************************************************************************/
- static void aes_init_keygen_tables(void);
- /******************************************************************************
- * AES_SETKEY : called to expand the key for encryption or decryption
- ******************************************************************************/
- static int aes_setkey(
- aes_context *ctx, // pointer to context
- int mode, // 1 or 0 for Encrypt/Decrypt
- const unsigned char *key, // AES input key
- unsigned int keysize); // size in bytes (must be 16, 24, 32 for
- // 128, 192 or 256-bit keys respectively)
- // returns 0 for success
- /******************************************************************************
- * AES_CIPHER : called to encrypt or decrypt ONE 128-bit block of data
- ******************************************************************************/
- static int aes_cipher(
- aes_context *ctx, // pointer to context
- const unsigned char input[16], // 128-bit block to en/decipher
- unsigned char output[16]); // 128-bit output result block
- // returns 0 for success
- /******************************************************************************
- * GCM_CONTEXT : GCM context / holds keytables, instance data, and AES ctx
- ******************************************************************************/
- typedef struct {
- int mode; // cipher direction: encrypt/decrypt
- uint64_t len; // cipher data length processed so far
- uint64_t add_len; // total add data length
- uint64_t HL[16]; // precalculated lo-half HTable
- uint64_t HH[16]; // precalculated hi-half HTable
- unsigned char base_ectr[16]; // first counter-mode cipher output for tag
- unsigned char y[16]; // the current cipher-input IV|Counter value
- unsigned char buf[16]; // buf working value
- aes_context aes_ctx; // cipher context used
- } gcm_context;
- /******************************************************************************
- * GCM_SETKEY : sets the GCM (and AES) keying material for use
- ******************************************************************************/
- static int gcm_setkey(
- gcm_context *ctx, // caller-provided context ptr
- const unsigned char *key, // pointer to cipher key
- const unsigned int keysize // size in bytes (must be 16, 24, 32 for
- // 128, 192 or 256-bit keys respectively)
- ); // returns 0 for success
- /******************************************************************************
- *
- * GCM_CRYPT_AND_TAG
- *
- * This either encrypts or decrypts the user-provided data and, either
- * way, generates an authentication tag of the requested length. It must be
- * called with a GCM context whose key has already been set with GCM_SETKEY.
- *
- * The user would typically call this explicitly to ENCRYPT a buffer of data
- * and optional associated data, and produce its an authentication tag.
- *
- * To reverse the process the user would typically call the companion
- * GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided
- * authentication tag. The GCM_AUTH_DECRYPT function calls this function
- * to perform its decryption and tag generation, which it then compares.
- *
- ******************************************************************************/
- static int gcm_crypt_and_tag(
- gcm_context *ctx, // gcm context with key already setup
- int mode, // cipher direction: MG_ENCRYPT (1) or MG_DECRYPT (0)
- const unsigned char *iv, // pointer to the 12-byte initialization vector
- size_t iv_len, // byte length if the IV. should always be 12
- const unsigned char *add, // pointer to the non-ciphered additional data
- size_t add_len, // byte length of the additional AEAD data
- const unsigned char *input, // pointer to the cipher data source
- unsigned char *output, // pointer to the cipher data destination
- size_t length, // byte length of the cipher data
- unsigned char *tag, // pointer to the tag to be generated
- size_t tag_len); // byte length of the tag to be generated
- /******************************************************************************
- *
- * GCM_START
- *
- * Given a user-provided GCM context, this initializes it, sets the encryption
- * mode, and preprocesses the initialization vector and additional AEAD data.
- *
- ******************************************************************************/
- static int gcm_start(
- gcm_context *ctx, // pointer to user-provided GCM context
- int mode, // MG_ENCRYPT (1) or MG_DECRYPT (0)
- const unsigned char *iv, // pointer to initialization vector
- size_t iv_len, // IV length in bytes (should == 12)
- const unsigned char *add, // pointer to additional AEAD data (NULL if none)
- size_t add_len); // length of additional AEAD data (bytes)
- /******************************************************************************
- *
- * GCM_UPDATE
- *
- * This is called once or more to process bulk plaintext or ciphertext data.
- * We give this some number of bytes of input and it returns the same number
- * of output bytes. If called multiple times (which is fine) all but the final
- * invocation MUST be called with length mod 16 == 0. (Only the final call can
- * have a partial block length of < 128 bits.)
- *
- ******************************************************************************/
- static int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
- size_t length, // length, in bytes, of data to process
- const unsigned char *input, // pointer to source data
- unsigned char *output); // pointer to destination data
- /******************************************************************************
- *
- * GCM_FINISH
- *
- * This is called once after all calls to GCM_UPDATE to finalize the GCM.
- * It performs the final GHASH to produce the resulting authentication TAG.
- *
- ******************************************************************************/
- static int gcm_finish(
- gcm_context *ctx, // pointer to user-provided GCM context
- unsigned char *tag, // ptr to tag buffer - NULL if tag_len = 0
- size_t tag_len); // length, in bytes, of the tag-receiving buf
- /******************************************************************************
- *
- * GCM_ZERO_CTX
- *
- * The GCM context contains both the GCM context and the AES context.
- * This includes keying and key-related material which is security-
- * sensitive, so it MUST be zeroed after use. This function does that.
- *
- ******************************************************************************/
- static void gcm_zero_ctx(gcm_context *ctx);
- /******************************************************************************
- *
- * THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
- *
- * This is a simple and straightforward implementation of the AES Rijndael
- * 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus
- * of this work was correctness & accuracy. It is written in 'C' without any
- * particular focus upon optimization or speed. It should be endian (memory
- * byte order) neutral since the few places that care are handled explicitly.
- *
- * This implementation of Rijndael was created by Steven M. Gibson of GRC.com.
- *
- * It is intended for general purpose use, but was written in support of GRC's
- * reference implementation of the SQRL (Secure Quick Reliable Login) client.
- *
- * See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
- *
- * NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
- * REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
- *
- *******************************************************************************/
- static int aes_tables_inited = 0; // run-once flag for performing key
- // expasion table generation (see below)
- /*
- * The following static local tables must be filled-in before the first use of
- * the GCM or AES ciphers. They are used for the AES key expansion/scheduling
- * and once built are read-only and thread safe. The "gcm_initialize" function
- * must be called once during system initialization to populate these arrays
- * for subsequent use by the AES key scheduler. If they have not been built
- * before attempted use, an error will be returned to the caller.
- *
- * NOTE: GCM Encryption/Decryption does NOT REQUIRE AES decryption. Since
- * GCM uses AES in counter-mode, where the AES cipher output is XORed with
- * the GCM input, we ONLY NEED AES encryption. Thus, to save space AES
- * decryption is typically disabled by setting AES_DECRYPTION to 0 in aes.h.
- */
- // We always need our forward tables
- static unsigned char FSb[256]; // Forward substitution box (FSb)
- static uint32_t FT0[256]; // Forward key schedule assembly tables
- static uint32_t FT1[256];
- static uint32_t FT2[256];
- static uint32_t FT3[256];
- #if AES_DECRYPTION // We ONLY need reverse for decryption
- static unsigned char RSb[256]; // Reverse substitution box (RSb)
- static uint32_t RT0[256]; // Reverse key schedule assembly tables
- static uint32_t RT1[256];
- static uint32_t RT2[256];
- static uint32_t RT3[256];
- #endif /* AES_DECRYPTION */
- static uint32_t RCON[10]; // AES round constants
- /*
- * Platform Endianness Neutralizing Load and Store Macro definitions
- * AES wants platform-neutral Little Endian (LE) byte ordering
- */
- #define GET_UINT32_LE(n, b, i) \
- { \
- (n) = ((uint32_t) (b)[(i)]) | ((uint32_t) (b)[(i) + 1] << 8) | \
- ((uint32_t) (b)[(i) + 2] << 16) | ((uint32_t) (b)[(i) + 3] << 24); \
- }
- #define PUT_UINT32_LE(n, b, i) \
- { \
- (b)[(i)] = (unsigned char) ((n)); \
- (b)[(i) + 1] = (unsigned char) ((n) >> 8); \
- (b)[(i) + 2] = (unsigned char) ((n) >> 16); \
- (b)[(i) + 3] = (unsigned char) ((n) >> 24); \
- }
- /*
- * AES forward and reverse encryption round processing macros
- */
- #define AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) \
- { \
- X0 = *RK++ ^ FT0[(Y0) & 0xFF] ^ FT1[(Y1 >> 8) & 0xFF] ^ \
- FT2[(Y2 >> 16) & 0xFF] ^ FT3[(Y3 >> 24) & 0xFF]; \
- \
- X1 = *RK++ ^ FT0[(Y1) & 0xFF] ^ FT1[(Y2 >> 8) & 0xFF] ^ \
- FT2[(Y3 >> 16) & 0xFF] ^ FT3[(Y0 >> 24) & 0xFF]; \
- \
- X2 = *RK++ ^ FT0[(Y2) & 0xFF] ^ FT1[(Y3 >> 8) & 0xFF] ^ \
- FT2[(Y0 >> 16) & 0xFF] ^ FT3[(Y1 >> 24) & 0xFF]; \
- \
- X3 = *RK++ ^ FT0[(Y3) & 0xFF] ^ FT1[(Y0 >> 8) & 0xFF] ^ \
- FT2[(Y1 >> 16) & 0xFF] ^ FT3[(Y2 >> 24) & 0xFF]; \
- }
- #define AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) \
- { \
- X0 = *RK++ ^ RT0[(Y0) & 0xFF] ^ RT1[(Y3 >> 8) & 0xFF] ^ \
- RT2[(Y2 >> 16) & 0xFF] ^ RT3[(Y1 >> 24) & 0xFF]; \
- \
- X1 = *RK++ ^ RT0[(Y1) & 0xFF] ^ RT1[(Y0 >> 8) & 0xFF] ^ \
- RT2[(Y3 >> 16) & 0xFF] ^ RT3[(Y2 >> 24) & 0xFF]; \
- \
- X2 = *RK++ ^ RT0[(Y2) & 0xFF] ^ RT1[(Y1 >> 8) & 0xFF] ^ \
- RT2[(Y0 >> 16) & 0xFF] ^ RT3[(Y3 >> 24) & 0xFF]; \
- \
- X3 = *RK++ ^ RT0[(Y3) & 0xFF] ^ RT1[(Y2 >> 8) & 0xFF] ^ \
- RT2[(Y1 >> 16) & 0xFF] ^ RT3[(Y0 >> 24) & 0xFF]; \
- }
- /*
- * These macros improve the readability of the key
- * generation initialization code by collapsing
- * repetitive common operations into logical pieces.
- */
- #define ROTL8(x) ((x << 8) & 0xFFFFFFFF) | (x >> 24)
- #define XTIME(x) ((x << 1) ^ ((x & 0x80) ? 0x1B : 0x00))
- #define MUL(x, y) ((x && y) ? pow[(log[x] + log[y]) % 255] : 0)
- #define MIX(x, y) \
- { \
- y = ((y << 1) | (y >> 7)) & 0xFF; \
- x ^= y; \
- }
- #define CPY128 \
- { \
- *RK++ = *SK++; \
- *RK++ = *SK++; \
- *RK++ = *SK++; \
- *RK++ = *SK++; \
- }
- /******************************************************************************
- *
- * AES_INIT_KEYGEN_TABLES
- *
- * Fills the AES key expansion tables allocated above with their static
- * data. This is not "per key" data, but static system-wide read-only
- * table data. THIS FUNCTION IS NOT THREAD SAFE. It must be called once
- * at system initialization to setup the tables for all subsequent use.
- *
- ******************************************************************************/
- void aes_init_keygen_tables(void) {
- int i, x, y, z; // general purpose iteration and computation locals
- int pow[256];
- int log[256];
- if (aes_tables_inited) return;
- // fill the 'pow' and 'log' tables over GF(2^8)
- for (i = 0, x = 1; i < 256; i++) {
- pow[i] = x;
- log[x] = i;
- x = (x ^ XTIME(x)) & 0xFF;
- }
- // compute the round constants
- for (i = 0, x = 1; i < 10; i++) {
- RCON[i] = (uint32_t) x;
- x = XTIME(x) & 0xFF;
- }
- // fill the forward and reverse substitution boxes
- FSb[0x00] = 0x63;
- #if AES_DECRYPTION // whether AES decryption is supported
- RSb[0x63] = 0x00;
- #endif /* AES_DECRYPTION */
- for (i = 1; i < 256; i++) {
- x = y = pow[255 - log[i]];
- MIX(x, y);
- MIX(x, y);
- MIX(x, y);
- MIX(x, y);
- FSb[i] = (unsigned char) (x ^= 0x63);
- #if AES_DECRYPTION // whether AES decryption is supported
- RSb[x] = (unsigned char) i;
- #endif /* AES_DECRYPTION */
- }
- // generate the forward and reverse key expansion tables
- for (i = 0; i < 256; i++) {
- x = FSb[i];
- y = XTIME(x) & 0xFF;
- z = (y ^ x) & 0xFF;
- FT0[i] = ((uint32_t) y) ^ ((uint32_t) x << 8) ^ ((uint32_t) x << 16) ^
- ((uint32_t) z << 24);
- FT1[i] = ROTL8(FT0[i]);
- FT2[i] = ROTL8(FT1[i]);
- FT3[i] = ROTL8(FT2[i]);
- #if AES_DECRYPTION // whether AES decryption is supported
- x = RSb[i];
- RT0[i] = ((uint32_t) MUL(0x0E, x)) ^ ((uint32_t) MUL(0x09, x) << 8) ^
- ((uint32_t) MUL(0x0D, x) << 16) ^ ((uint32_t) MUL(0x0B, x) << 24);
- RT1[i] = ROTL8(RT0[i]);
- RT2[i] = ROTL8(RT1[i]);
- RT3[i] = ROTL8(RT2[i]);
- #endif /* AES_DECRYPTION */
- }
- aes_tables_inited = 1; // flag that the tables have been generated
- } // to permit subsequent use of the AES cipher
- /******************************************************************************
- *
- * AES_SET_ENCRYPTION_KEY
- *
- * This is called by 'aes_setkey' when we're establishing a key for
- * subsequent encryption. We give it a pointer to the encryption
- * context, a pointer to the key, and the key's length in bytes.
- * Valid lengths are: 16, 24 or 32 bytes (128, 192, 256 bits).
- *
- ******************************************************************************/
- static int aes_set_encryption_key(aes_context *ctx, const unsigned char *key,
- unsigned int keysize) {
- unsigned int i; // general purpose iteration local
- uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer
- for (i = 0; i < (keysize >> 2); i++) {
- GET_UINT32_LE(RK[i], key, i << 2);
- }
- switch (ctx->rounds) {
- case 10:
- for (i = 0; i < 10; i++, RK += 4) {
- RK[4] = RK[0] ^ RCON[i] ^ ((uint32_t) FSb[(RK[3] >> 8) & 0xFF]) ^
- ((uint32_t) FSb[(RK[3] >> 16) & 0xFF] << 8) ^
- ((uint32_t) FSb[(RK[3] >> 24) & 0xFF] << 16) ^
- ((uint32_t) FSb[(RK[3]) & 0xFF] << 24);
- RK[5] = RK[1] ^ RK[4];
- RK[6] = RK[2] ^ RK[5];
- RK[7] = RK[3] ^ RK[6];
- }
- break;
- case 12:
- for (i = 0; i < 8; i++, RK += 6) {
- RK[6] = RK[0] ^ RCON[i] ^ ((uint32_t) FSb[(RK[5] >> 8) & 0xFF]) ^
- ((uint32_t) FSb[(RK[5] >> 16) & 0xFF] << 8) ^
- ((uint32_t) FSb[(RK[5] >> 24) & 0xFF] << 16) ^
- ((uint32_t) FSb[(RK[5]) & 0xFF] << 24);
- RK[7] = RK[1] ^ RK[6];
- RK[8] = RK[2] ^ RK[7];
- RK[9] = RK[3] ^ RK[8];
- RK[10] = RK[4] ^ RK[9];
- RK[11] = RK[5] ^ RK[10];
- }
- break;
- case 14:
- for (i = 0; i < 7; i++, RK += 8) {
- RK[8] = RK[0] ^ RCON[i] ^ ((uint32_t) FSb[(RK[7] >> 8) & 0xFF]) ^
- ((uint32_t) FSb[(RK[7] >> 16) & 0xFF] << 8) ^
- ((uint32_t) FSb[(RK[7] >> 24) & 0xFF] << 16) ^
- ((uint32_t) FSb[(RK[7]) & 0xFF] << 24);
- RK[9] = RK[1] ^ RK[8];
- RK[10] = RK[2] ^ RK[9];
- RK[11] = RK[3] ^ RK[10];
- RK[12] = RK[4] ^ ((uint32_t) FSb[(RK[11]) & 0xFF]) ^
- ((uint32_t) FSb[(RK[11] >> 8) & 0xFF] << 8) ^
- ((uint32_t) FSb[(RK[11] >> 16) & 0xFF] << 16) ^
- ((uint32_t) FSb[(RK[11] >> 24) & 0xFF] << 24);
- RK[13] = RK[5] ^ RK[12];
- RK[14] = RK[6] ^ RK[13];
- RK[15] = RK[7] ^ RK[14];
- }
- break;
- default:
- return -1;
- }
- return (0);
- }
- #if AES_DECRYPTION // whether AES decryption is supported
- /******************************************************************************
- *
- * AES_SET_DECRYPTION_KEY
- *
- * This is called by 'aes_setkey' when we're establishing a
- * key for subsequent decryption. We give it a pointer to
- * the encryption context, a pointer to the key, and the key's
- * length in bits. Valid lengths are: 128, 192, or 256 bits.
- *
- ******************************************************************************/
- static int aes_set_decryption_key(aes_context *ctx, const unsigned char *key,
- unsigned int keysize) {
- int i, j;
- aes_context cty; // a calling aes context for set_encryption_key
- uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer
- uint32_t *SK;
- int ret;
- cty.rounds = ctx->rounds; // initialize our local aes context
- cty.rk = cty.buf; // round count and key buf pointer
- if ((ret = aes_set_encryption_key(&cty, key, keysize)) != 0) return (ret);
- SK = cty.rk + cty.rounds * 4;
- CPY128 // copy a 128-bit block from *SK to *RK
- for (i = ctx->rounds - 1, SK -= 8; i > 0; i--, SK -= 8) {
- for (j = 0; j < 4; j++, SK++) {
- *RK++ = RT0[FSb[(*SK) & 0xFF]] ^ RT1[FSb[(*SK >> 8) & 0xFF]] ^
- RT2[FSb[(*SK >> 16) & 0xFF]] ^ RT3[FSb[(*SK >> 24) & 0xFF]];
- }
- }
- CPY128 // copy a 128-bit block from *SK to *RK
- memset(&cty, 0, sizeof(aes_context)); // clear local aes context
- return (0);
- }
- #endif /* AES_DECRYPTION */
- /******************************************************************************
- *
- * AES_SETKEY
- *
- * Invoked to establish the key schedule for subsequent encryption/decryption
- *
- ******************************************************************************/
- static int aes_setkey(aes_context *ctx, // AES context provided by our caller
- int mode, // ENCRYPT or DECRYPT flag
- const unsigned char *key, // pointer to the key
- unsigned int keysize) // key length in bytes
- {
- // since table initialization is not thread safe, we could either add
- // system-specific mutexes and init the AES key generation tables on
- // demand, or ask the developer to simply call "gcm_initialize" once during
- // application startup before threading begins. That's what we choose.
- if (!aes_tables_inited) return (-1); // fail the call when not inited.
- ctx->mode = mode; // capture the key type we're creating
- ctx->rk = ctx->buf; // initialize our round key pointer
- switch (keysize) // set the rounds count based upon the keysize
- {
- case 16:
- ctx->rounds = 10;
- break; // 16-byte, 128-bit key
- case 24:
- ctx->rounds = 12;
- break; // 24-byte, 192-bit key
- case 32:
- ctx->rounds = 14;
- break; // 32-byte, 256-bit key
- default:
- return (-1);
- }
- #if AES_DECRYPTION
- if (mode == MG_DECRYPT) // expand our key for encryption or decryption
- return (aes_set_decryption_key(ctx, key, keysize));
- else /* MG_ENCRYPT */
- #endif /* AES_DECRYPTION */
- return (aes_set_encryption_key(ctx, key, keysize));
- }
- /******************************************************************************
- *
- * AES_CIPHER
- *
- * Perform AES encryption and decryption.
- * The AES context will have been setup with the encryption mode
- * and all keying information appropriate for the task.
- *
- ******************************************************************************/
- static int aes_cipher(aes_context *ctx, const unsigned char input[16],
- unsigned char output[16]) {
- int i;
- uint32_t *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; // general purpose locals
- RK = ctx->rk;
- GET_UINT32_LE(X0, input, 0);
- X0 ^= *RK++; // load our 128-bit
- GET_UINT32_LE(X1, input, 4);
- X1 ^= *RK++; // input buffer in a storage
- GET_UINT32_LE(X2, input, 8);
- X2 ^= *RK++; // memory endian-neutral way
- GET_UINT32_LE(X3, input, 12);
- X3 ^= *RK++;
- #if AES_DECRYPTION // whether AES decryption is supported
- if (ctx->mode == MG_DECRYPT) {
- for (i = (ctx->rounds >> 1) - 1; i > 0; i--) {
- AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
- AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3);
- }
- AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
- X0 = *RK++ ^ ((uint32_t) RSb[(Y0) & 0xFF]) ^
- ((uint32_t) RSb[(Y3 >> 8) & 0xFF] << 8) ^
- ((uint32_t) RSb[(Y2 >> 16) & 0xFF] << 16) ^
- ((uint32_t) RSb[(Y1 >> 24) & 0xFF] << 24);
- X1 = *RK++ ^ ((uint32_t) RSb[(Y1) & 0xFF]) ^
- ((uint32_t) RSb[(Y0 >> 8) & 0xFF] << 8) ^
- ((uint32_t) RSb[(Y3 >> 16) & 0xFF] << 16) ^
- ((uint32_t) RSb[(Y2 >> 24) & 0xFF] << 24);
- X2 = *RK++ ^ ((uint32_t) RSb[(Y2) & 0xFF]) ^
- ((uint32_t) RSb[(Y1 >> 8) & 0xFF] << 8) ^
- ((uint32_t) RSb[(Y0 >> 16) & 0xFF] << 16) ^
- ((uint32_t) RSb[(Y3 >> 24) & 0xFF] << 24);
- X3 = *RK++ ^ ((uint32_t) RSb[(Y3) & 0xFF]) ^
- ((uint32_t) RSb[(Y2 >> 8) & 0xFF] << 8) ^
- ((uint32_t) RSb[(Y1 >> 16) & 0xFF] << 16) ^
- ((uint32_t) RSb[(Y0 >> 24) & 0xFF] << 24);
- } else /* MG_ENCRYPT */
- {
- #endif /* AES_DECRYPTION */
- for (i = (ctx->rounds >> 1) - 1; i > 0; i--) {
- AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
- AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3);
- }
- AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
- X0 = *RK++ ^ ((uint32_t) FSb[(Y0) & 0xFF]) ^
- ((uint32_t) FSb[(Y1 >> 8) & 0xFF] << 8) ^
- ((uint32_t) FSb[(Y2 >> 16) & 0xFF] << 16) ^
- ((uint32_t) FSb[(Y3 >> 24) & 0xFF] << 24);
- X1 = *RK++ ^ ((uint32_t) FSb[(Y1) & 0xFF]) ^
- ((uint32_t) FSb[(Y2 >> 8) & 0xFF] << 8) ^
- ((uint32_t) FSb[(Y3 >> 16) & 0xFF] << 16) ^
- ((uint32_t) FSb[(Y0 >> 24) & 0xFF] << 24);
- X2 = *RK++ ^ ((uint32_t) FSb[(Y2) & 0xFF]) ^
- ((uint32_t) FSb[(Y3 >> 8) & 0xFF] << 8) ^
- ((uint32_t) FSb[(Y0 >> 16) & 0xFF] << 16) ^
- ((uint32_t) FSb[(Y1 >> 24) & 0xFF] << 24);
- X3 = *RK++ ^ ((uint32_t) FSb[(Y3) & 0xFF]) ^
- ((uint32_t) FSb[(Y0 >> 8) & 0xFF] << 8) ^
- ((uint32_t) FSb[(Y1 >> 16) & 0xFF] << 16) ^
- ((uint32_t) FSb[(Y2 >> 24) & 0xFF] << 24);
- #if AES_DECRYPTION // whether AES decryption is supported
- }
- #endif /* AES_DECRYPTION */
- PUT_UINT32_LE(X0, output, 0);
- PUT_UINT32_LE(X1, output, 4);
- PUT_UINT32_LE(X2, output, 8);
- PUT_UINT32_LE(X3, output, 12);
- return (0);
- }
- /* end of aes.c */
- /******************************************************************************
- *
- * THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
- *
- * This is a simple and straightforward implementation of AES-GCM authenticated
- * encryption. The focus of this work was correctness & accuracy. It is written
- * in straight 'C' without any particular focus upon optimization or speed. It
- * should be endian (memory byte order) neutral since the few places that care
- * are handled explicitly.
- *
- * This implementation of AES-GCM was created by Steven M. Gibson of GRC.com.
- *
- * It is intended for general purpose use, but was written in support of GRC's
- * reference implementation of the SQRL (Secure Quick Reliable Login) client.
- *
- * See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
- * http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/
- * gcm/gcm-revised-spec.pdf
- *
- * NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
- * REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
- *
- *******************************************************************************/
- /******************************************************************************
- * ==== IMPLEMENTATION WARNING ====
- *
- * This code was developed for use within SQRL's fixed environmnent. Thus, it
- * is somewhat less "general purpose" than it would be if it were designed as
- * a general purpose AES-GCM library. Specifically, it bothers with almost NO
- * error checking on parameter limits, buffer bounds, etc. It assumes that it
- * is being invoked by its author or by someone who understands the values it
- * expects to receive. Its behavior will be undefined otherwise.
- *
- * All functions that might fail are defined to return 'ints' to indicate a
- * problem. Most do not do so now. But this allows for error propagation out
- * of internal functions if robust error checking should ever be desired.
- *
- ******************************************************************************/
- /* Calculating the "GHASH"
- *
- * There are many ways of calculating the so-called GHASH in software, each with
- * a traditional size vs performance tradeoff. The GHASH (Galois field hash) is
- * an intriguing construction which takes two 128-bit strings (also the cipher's
- * block size and the fundamental operation size for the system) and hashes them
- * into a third 128-bit result.
- *
- * Many implementation solutions have been worked out that use large precomputed
- * table lookups in place of more time consuming bit fiddling, and this approach
- * can be scaled easily upward or downward as needed to change the time/space
- * tradeoff. It's been studied extensively and there's a solid body of theory
- * and practice. For example, without using any lookup tables an implementation
- * might obtain 119 cycles per byte throughput, whereas using a simple, though
- * large, key-specific 64 kbyte 8-bit lookup table the performance jumps to 13
- * cycles per byte.
- *
- * And Intel's processors have, since 2010, included an instruction which does
- * the entire 128x128->128 bit job in just several 64x64->128 bit pieces.
- *
- * Since SQRL is interactive, and only processing a few 128-bit blocks, I've
- * settled upon a relatively slower but appealing small-table compromise which
- * folds a bunch of not only time consuming but also bit twiddling into a simple
- * 16-entry table which is attributed to Victor Shoup's 1996 work while at
- * Bellcore: "On Fast and Provably Secure MessageAuthentication Based on
- * Universal Hashing." See: http://www.shoup.net/papers/macs.pdf
- * See, also section 4.1 of the "gcm-revised-spec" cited above.
- */
- /*
- * This 16-entry table of pre-computed constants is used by the
- * GHASH multiplier to improve over a strictly table-free but
- * significantly slower 128x128 bit multiple within GF(2^128).
- */
- static const uint64_t last4[16] = {
- 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
- 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0};
- /*
- * Platform Endianness Neutralizing Load and Store Macro definitions
- * GCM wants platform-neutral Big Endian (BE) byte ordering
- */
- #define GET_UINT32_BE(n, b, i) \
- { \
- (n) = ((uint32_t) (b)[(i)] << 24) | ((uint32_t) (b)[(i) + 1] << 16) | \
- ((uint32_t) (b)[(i) + 2] << 8) | ((uint32_t) (b)[(i) + 3]); \
- }
- #define PUT_UINT32_BE(n, b, i) \
- { \
- (b)[(i)] = (unsigned char) ((n) >> 24); \
- (b)[(i) + 1] = (unsigned char) ((n) >> 16); \
- (b)[(i) + 2] = (unsigned char) ((n) >> 8); \
- (b)[(i) + 3] = (unsigned char) ((n)); \
- }
- /******************************************************************************
- *
- * GCM_INITIALIZE
- *
- * Must be called once to initialize the GCM library.
- *
- * At present, this only calls the AES keygen table generator, which expands
- * the AES keying tables for use. This is NOT A THREAD-SAFE function, so it
- * MUST be called during system initialization before a multi-threading
- * environment is running.
- *
- ******************************************************************************/
- int mg_gcm_initialize(void) {
- aes_init_keygen_tables();
- return (0);
- }
- /******************************************************************************
- *
- * GCM_MULT
- *
- * Performs a GHASH operation on the 128-bit input vector 'x', setting
- * the 128-bit output vector to 'x' times H using our precomputed tables.
- * 'x' and 'output' are seen as elements of GCM's GF(2^128) Galois field.
- *
- ******************************************************************************/
- static void gcm_mult(
- gcm_context *ctx, // pointer to established context
- const unsigned char x[16], // pointer to 128-bit input vector
- unsigned char output[16]) // pointer to 128-bit output vector
- {
- int i;
- unsigned char lo, hi, rem;
- uint64_t zh, zl;
- lo = (unsigned char) (x[15] & 0x0f);
- hi = (unsigned char) (x[15] >> 4);
- zh = ctx->HH[lo];
- zl = ctx->HL[lo];
- for (i = 15; i >= 0; i--) {
- lo = (unsigned char) (x[i] & 0x0f);
- hi = (unsigned char) (x[i] >> 4);
- if (i != 15) {
- rem = (unsigned char) (zl & 0x0f);
- zl = (zh << 60) | (zl >> 4);
- zh = (zh >> 4);
- zh ^= (uint64_t) last4[rem] << 48;
- zh ^= ctx->HH[lo];
- zl ^= ctx->HL[lo];
- }
- rem = (unsigned char) (zl & 0x0f);
- zl = (zh << 60) | (zl >> 4);
- zh = (zh >> 4);
- zh ^= (uint64_t) last4[rem] << 48;
- zh ^= ctx->HH[hi];
- zl ^= ctx->HL[hi];
- }
- PUT_UINT32_BE(zh >> 32, output, 0);
- PUT_UINT32_BE(zh, output, 4);
- PUT_UINT32_BE(zl >> 32, output, 8);
- PUT_UINT32_BE(zl, output, 12);
- }
- /******************************************************************************
- *
- * GCM_SETKEY
- *
- * This is called to set the AES-GCM key. It initializes the AES key
- * and populates the gcm context's pre-calculated HTables.
- *
- ******************************************************************************/
- static int gcm_setkey(
- gcm_context *ctx, // pointer to caller-provided gcm context
- const unsigned char *key, // pointer to the AES encryption key
- const unsigned int keysize) // size in bytes (must be 16, 24, 32 for
- // 128, 192 or 256-bit keys respectively)
- {
- int ret, i, j;
- uint64_t hi, lo;
- uint64_t vl, vh;
- unsigned char h[16];
- memset(ctx, 0, sizeof(gcm_context)); // zero caller-provided GCM context
- memset(h, 0, 16); // initialize the block to encrypt
- // encrypt the null 128-bit block to generate a key-based value
- // which is then used to initialize our GHASH lookup tables
- if ((ret = aes_setkey(&ctx->aes_ctx, MG_ENCRYPT, key, keysize)) != 0)
- return (ret);
- if ((ret = aes_cipher(&ctx->aes_ctx, h, h)) != 0) return (ret);
- GET_UINT32_BE(hi, h, 0); // pack h as two 64-bit ints, big-endian
- GET_UINT32_BE(lo, h, 4);
- vh = (uint64_t) hi << 32 | lo;
- GET_UINT32_BE(hi, h, 8);
- GET_UINT32_BE(lo, h, 12);
- vl = (uint64_t) hi << 32 | lo;
- ctx->HL[8] = vl; // 8 = 1000 corresponds to 1 in GF(2^128)
- ctx->HH[8] = vh;
- ctx->HH[0] = 0; // 0 corresponds to 0 in GF(2^128)
- ctx->HL[0] = 0;
- for (i = 4; i > 0; i >>= 1) {
- uint32_t T = (uint32_t) (vl & 1) * 0xe1000000U;
- vl = (vh << 63) | (vl >> 1);
- vh = (vh >> 1) ^ ((uint64_t) T << 32);
- ctx->HL[i] = vl;
- ctx->HH[i] = vh;
- }
- for (i = 2; i < 16; i <<= 1) {
- uint64_t *HiL = ctx->HL + i, *HiH = ctx->HH + i;
- vh = *HiH;
- vl = *HiL;
- for (j = 1; j < i; j++) {
- HiH[j] = vh ^ ctx->HH[j];
- HiL[j] = vl ^ ctx->HL[j];
- }
- }
- return (0);
- }
- /******************************************************************************
- *
- * GCM processing occurs four phases: SETKEY, START, UPDATE and FINISH.
- *
- * SETKEY:
- *
- * START: Sets the Encryption/Decryption mode.
- * Accepts the initialization vector and additional data.
- *
- * UPDATE: Encrypts or decrypts the plaintext or ciphertext.
- *
- * FINISH: Performs a final GHASH to generate the authentication tag.
- *
- ******************************************************************************
- *
- * GCM_START
- *
- * Given a user-provided GCM context, this initializes it, sets the encryption
- * mode, and preprocesses the initialization vector and additional AEAD data.
- *
- ******************************************************************************/
- int gcm_start(
- gcm_context *ctx, // pointer to user-provided GCM context
- int mode, // GCM_ENCRYPT or GCM_DECRYPT
- const unsigned char *iv, // pointer to initialization vector
- size_t iv_len, // IV length in bytes (should == 12)
- const unsigned char *add, // ptr to additional AEAD data (NULL if none)
- size_t add_len) // length of additional AEAD data (bytes)
- {
- int ret; // our error return if the AES encrypt fails
- unsigned char work_buf[16]; // XOR source built from provided IV if len != 16
- const unsigned char *p; // general purpose array pointer
- size_t use_len; // byte count to process, up to 16 bytes
- size_t i; // local loop iterator
- // since the context might be reused under the same key
- // we zero the working buffers for this next new process
- memset(ctx->y, 0x00, sizeof(ctx->y));
- memset(ctx->buf, 0x00, sizeof(ctx->buf));
- ctx->len = 0;
- ctx->add_len = 0;
- ctx->mode = mode; // set the GCM encryption/decryption mode
- ctx->aes_ctx.mode = MG_ENCRYPT; // GCM *always* runs AES in ENCRYPTION mode
- if (iv_len == 12) { // GCM natively uses a 12-byte, 96-bit IV
- memcpy(ctx->y, iv, iv_len); // copy the IV to the top of the 'y' buff
- ctx->y[15] = 1; // start "counting" from 1 (not 0)
- } else // if we don't have a 12-byte IV, we GHASH whatever we've been given
- {
- memset(work_buf, 0x00, 16); // clear the working buffer
- PUT_UINT32_BE(iv_len * 8, work_buf, 12); // place the IV into buffer
- p = iv;
- while (iv_len > 0) {
- use_len = (iv_len < 16) ? iv_len : 16;
- for (i = 0; i < use_len; i++) ctx->y[i] ^= p[i];
- gcm_mult(ctx, ctx->y, ctx->y);
- iv_len -= use_len;
- p += use_len;
- }
- for (i = 0; i < 16; i++) ctx->y[i] ^= work_buf[i];
- gcm_mult(ctx, ctx->y, ctx->y);
- }
- if ((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ctx->base_ectr)) != 0)
- return (ret);
- ctx->add_len = add_len;
- p = add;
- while (add_len > 0) {
- use_len = (add_len < 16) ? add_len : 16;
- for (i = 0; i < use_len; i++) ctx->buf[i] ^= p[i];
- gcm_mult(ctx, ctx->buf, ctx->buf);
- add_len -= use_len;
- p += use_len;
- }
- return (0);
- }
- /******************************************************************************
- *
- * GCM_UPDATE
- *
- * This is called once or more to process bulk plaintext or ciphertext data.
- * We give this some number of bytes of input and it returns the same number
- * of output bytes. If called multiple times (which is fine) all but the final
- * invocation MUST be called with length mod 16 == 0. (Only the final call can
- * have a partial block length of < 128 bits.)
- *
- ******************************************************************************/
- int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
- size_t length, // length, in bytes, of data to process
- const unsigned char *input, // pointer to source data
- unsigned char *output) // pointer to destination data
- {
- int ret; // our error return if the AES encrypt fails
- unsigned char ectr[16]; // counter-mode cipher output for XORing
- size_t use_len; // byte count to process, up to 16 bytes
- size_t i; // local loop iterator
- ctx->len += length; // bump the GCM context's running length count
- while (length > 0) {
- // clamp the length to process at 16 bytes
- use_len = (length < 16) ? length : 16;
- // increment the context's 128-bit IV||Counter 'y' vector
- for (i = 16; i > 12; i--)
- if (++ctx->y[i - 1] != 0) break;
- // encrypt the context's 'y' vector under the established key
- if ((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ectr)) != 0) return (ret);
- // encrypt or decrypt the input to the output
- if (ctx->mode == MG_ENCRYPT) {
- for (i = 0; i < use_len; i++) {
- // XOR the cipher's ouptut vector (ectr) with our input
- output[i] = (unsigned char) (ectr[i] ^ input[i]);
- // now we mix in our data into the authentication hash.
- // if we're ENcrypting we XOR in the post-XOR (output)
- // results, but if we're DEcrypting we XOR in the input
- // data
- ctx->buf[i] ^= output[i];
- }
- } else {
- for (i = 0; i < use_len; i++) {
- // but if we're DEcrypting we XOR in the input data first,
- // i.e. before saving to ouput data, otherwise if the input
- // and output buffer are the same (inplace decryption) we
- // would not get the correct auth tag
- ctx->buf[i] ^= input[i];
- // XOR the cipher's ouptut vector (ectr) with our input
- output[i] = (unsigned char) (ectr[i] ^ input[i]);
- }
- }
- gcm_mult(ctx, ctx->buf, ctx->buf); // perform a GHASH operation
- length -= use_len; // drop the remaining byte count to process
- input += use_len; // bump our input pointer forward
- output += use_len; // bump our output pointer forward
- }
- return (0);
- }
- /******************************************************************************
- *
- * GCM_FINISH
- *
- * This is called once after all calls to GCM_UPDATE to finalize the GCM.
- * It performs the final GHASH to produce the resulting authentication TAG.
- *
- ******************************************************************************/
- int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
- unsigned char *tag, // pointer to buffer which receives the tag
- size_t tag_len) // length, in bytes, of the tag-receiving buf
- {
- unsigned char work_buf[16];
- uint64_t orig_len = ctx->len * 8;
- uint64_t orig_add_len = ctx->add_len * 8;
- size_t i;
- if (tag_len > sizeof(work_buf)) return -1;
- if (tag_len != 0) memcpy(tag, ctx->base_ectr, tag_len);
- if (orig_len || orig_add_len) {
- memset(work_buf, 0x00, 16);
- PUT_UINT32_BE((orig_add_len >> 32), work_buf, 0);
- PUT_UINT32_BE((orig_add_len), work_buf, 4);
- PUT_UINT32_BE((orig_len >> 32), work_buf, 8);
- PUT_UINT32_BE((orig_len), work_buf, 12);
- for (i = 0; i < 16; i++) ctx->buf[i] ^= work_buf[i];
- gcm_mult(ctx, ctx->buf, ctx->buf);
- for (i = 0; i < tag_len; i++) tag[i] ^= ctx->buf[i];
- }
- return (0);
- }
- /******************************************************************************
- *
- * GCM_CRYPT_AND_TAG
- *
- * This either encrypts or decrypts the user-provided data and, either
- * way, generates an authentication tag of the requested length. It must be
- * called with a GCM context whose key has already been set with GCM_SETKEY.
- *
- * The user would typically call this explicitly to ENCRYPT a buffer of data
- * and optional associated data, and produce its an authentication tag.
- *
- * To reverse the process the user would typically call the companion
- * GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided
- * authentication tag. The GCM_AUTH_DECRYPT function calls this function
- * to perform its decryption and tag generation, which it then compares.
- *
- ******************************************************************************/
- int gcm_crypt_and_tag(
- gcm_context *ctx, // gcm context with key already setup
- int mode, // cipher direction: GCM_ENCRYPT or GCM_DECRYPT
- const unsigned char *iv, // pointer to the 12-byte initialization vector
- size_t iv_len, // byte length if the IV. should always be 12
- const unsigned char *add, // pointer to the non-ciphered additional data
- size_t add_len, // byte length of the additional AEAD data
- const unsigned char *input, // pointer to the cipher data source
- unsigned char *output, // pointer to the cipher data destination
- size_t length, // byte length of the cipher data
- unsigned char *tag, // pointer to the tag to be generated
- size_t tag_len) // byte length of the tag to be generated
- { /*
- assuming that the caller has already invoked gcm_setkey to
- prepare the gcm context with the keying material, we simply
- invoke each of the three GCM sub-functions in turn...
- */
- if (gcm_start(ctx, mode, iv, iv_len, add, add_len) != 0) return -1;
- if (gcm_update(ctx, length, input, output) != 0) return -1;
- return gcm_finish(ctx, tag, tag_len);
- }
- /******************************************************************************
- *
- * GCM_ZERO_CTX
- *
- * The GCM context contains both the GCM context and the AES context.
- * This includes keying and key-related material which is security-
- * sensitive, so it MUST be zeroed after use. This function does that.
- *
- ******************************************************************************/
- void gcm_zero_ctx(gcm_context *ctx) {
- // zero the context originally provided to us
- memset(ctx, 0, sizeof(gcm_context));
- }
- //
- // aes-gcm.c
- // Pods
- //
- // Created by Markus Kosmal on 20/11/14.
- //
- //
- int mg_aes_gcm_encrypt(unsigned char *output, //
- const unsigned char *input, size_t input_length,
- const unsigned char *key, const size_t key_len,
- const unsigned char *iv, const size_t iv_len,
- unsigned char *aead, size_t aead_len, unsigned char *tag,
- const size_t tag_len) {
- int ret = 0; // our return value
- gcm_context ctx; // includes the AES context structure
- gcm_setkey(&ctx, key, (unsigned int) key_len);
- ret = gcm_crypt_and_tag(&ctx, MG_ENCRYPT, iv, iv_len, aead, aead_len, input,
- output, input_length, tag, tag_len);
- gcm_zero_ctx(&ctx);
- return (ret);
- }
- int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input,
- size_t input_length, const unsigned char *key,
- const size_t key_len, const unsigned char *iv,
- const size_t iv_len, unsigned char *aead,
- size_t aead_len, const unsigned char *tag,
- const size_t tag_len) {
- int ret = 0; // our return value
- gcm_context ctx; // includes the AES context structure
- unsigned char computed_tag[16];
- if (tag_len > sizeof(computed_tag)) return -1;
- gcm_setkey(&ctx, key, (unsigned int) key_len);
- ret = gcm_crypt_and_tag(&ctx, MG_DECRYPT, iv, iv_len, aead, aead_len, input,
- output, input_length, computed_tag, tag_len);
- gcm_zero_ctx(&ctx);
- if (!mg_memeq(computed_tag, tag, tag_len)) ret = -1;
- return (ret);
- }
- #endif
- // End of aes128 PD
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tls_builtin.c"
- #endif
- #if MG_TLS == MG_TLS_BUILTIN
- #ifndef MG_MAX_TLSOID_DEPTH
- #define MG_MAX_TLSOID_DEPTH 15
- #endif
- // PKCS#8 algorithm OIDs
- static const uint8_t mg_rsa_oid[] = {
- 0x2a, 0x86, 0x48, 0x86, 0xf7,
- 0x0d, 0x01, 0x01, 0x01 // 1.2.840.113549.1.1.1 rsaEncryption
- };
- static const uint8_t mg_ec_public_key_oid[] = {
- 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 // 1.2.840.10045.2.1 ecPublicKey
- };
- static const uint8_t mg_secp256r1_oid[] = {
- 0x2a, 0x86, 0x48, 0xce,
- 0x3d, 0x03, 0x01, 0x07 // 1.2.840.10045.3.1.7 secp256r1
- };
- /* TLS 1.3 Record Content Type (RFC8446 B.1) */
- #define MG_TLS_CHANGE_CIPHER 20
- #define MG_TLS_ALERT 21
- #define MG_TLS_HANDSHAKE 22
- #define MG_TLS_APP_DATA 23
- #define MG_TLS_HEARTBEAT 24
- /* TLS 1.3 Handshake Message Type (RFC8446 B.3) */
- #define MG_TLS_CLIENT_HELLO 1
- #define MG_TLS_SERVER_HELLO 2
- #define MG_TLS_ENCRYPTED_EXTENSIONS 8
- #define MG_TLS_CERTIFICATE 11
- #define MG_TLS_CERTIFICATE_REQUEST 13
- #define MG_TLS_CERTIFICATE_VERIFY 15
- #define MG_TLS_FINISHED 20
- #define MG_TLS_RSA_USE_CRT 1 // CRT instead of naive RSA
- // handshake is re-entrant, so we need to keep track of its state state names
- // refer to RFC8446#A.1
- enum mg_tls_hs_state {
- // Client state machine:
- MG_TLS_STATE_CLIENT_START, // Send ClientHello
- MG_TLS_STATE_CLIENT_WAIT_SH, // Wait for ServerHello
- MG_TLS_STATE_CLIENT_WAIT_EE, // Wait for EncryptedExtensions
- MG_TLS_STATE_CLIENT_WAIT_CERT, // Wait for Certificate
- MG_TLS_STATE_CLIENT_WAIT_CV, // Wait for CertificateVerify
- MG_TLS_STATE_CLIENT_WAIT_FINISH, // Wait for Finish
- MG_TLS_STATE_CLIENT_CONNECTED, // Done
- // Server state machine:
- MG_TLS_STATE_SERVER_START, // Wait for ClientHello
- MG_TLS_STATE_SERVER_WAIT_CERT, // Wait for Certificate
- MG_TLS_STATE_SERVER_WAIT_CV, // Wait for CertificateVerify
- MG_TLS_STATE_SERVER_NEGOTIATED, // Wait for Finish
- MG_TLS_STATE_SERVER_CONNECTED // Done
- };
- // encryption keys for a TLS connection
- struct tls_enc {
- uint32_t sseq; // server sequence number, used in encryption
- uint32_t cseq; // client sequence number, used in decryption
- // keys for AES encryption or ChaCha20
- uint8_t handshake_secret[32];
- uint8_t server_write_key[32];
- uint8_t server_write_iv[12];
- uint8_t server_finished_key[32];
- uint8_t client_write_key[32];
- uint8_t client_write_iv[12];
- uint8_t client_finished_key[32];
- };
- struct mg_rsa_key {
- struct mg_str n; // modulus
- struct mg_str e; // public exponent
- struct mg_str d; // private exponent
- struct mg_str p; // prime1
- struct mg_str q; // prime2
- struct mg_str dP; // exponent1 (d mod (p-1))
- struct mg_str dQ; // exponent2 (d mod (q-1))
- struct mg_str qInv; // coefficient ((inverse of q) mod p)
- };
- // per-connection TLS data
- struct tls_data {
- enum mg_tls_hs_state state; // keep track of connection handshake progress
- struct mg_iobuf send; // For the receive path, we're reusing c->rtls
- size_t recv_offset; // While c->rtls contains full records, reuse that
- size_t recv_len; // buffer but point at individual decrypted messages
- uint8_t content_type; // Last received record content type
- mg_sha256_ctx sha256; // incremental SHA-256 hash for TLS handshake
- uint8_t random[32]; // client random from ClientHello
- uint8_t session_id[32]; // client session ID between the handshake states
- uint8_t x25519_cli[32]; // client X25519 key between the handshake states
- uint8_t x25519_sec[32]; // x25519 secret between the handshake states
- bool skip_verification; // do not perform checks on server certificate
- bool cert_requested; // client received a CertificateRequest
- bool is_twoway; // server is configured to authenticate clients
- bool is_sntp_pending; // TLS handshake is waiting for wall-clock time
- struct mg_connection *timec; // SNTP connection for wall-clock time
- struct mg_str cert_der; // certificate in DER format
- struct mg_str ca_der; // current CA certificate
- struct mg_str *ca_bundle_der; // bundle of CA certificates
- size_t ca_bundle_len; // number of certificates in bundle
- struct mg_str *chain_der; // certificate chain (intermediate certs)
- size_t chain_len; // number of certificates in chain
- uint8_t ec_key[32]; // EC private key
- struct mg_rsa_key rsa;
- struct mg_str rsa_key_der; // RSA private key in DER format
- char hostname[254]; // matching hostname
- bool is_ec_pubkey; // EC or RSA. TODO(): currently unused
- uint8_t pubkey[512 + 16]; // server EC (64) or RSA (512+exp) public key to
- // verify cert
- size_t pubkeysz; // size of the server public key
- uint8_t sighash[32]; // calculated signature verification hash
- struct tls_enc enc; // actual keys in use at this time
- struct tls_enc app_keys; // storage during two-way auth handshake
- };
- #define TLS_RECHDR_SIZE 5 // 1 byte type, 2 bytes version, 2 bytes length
- #define TLS_MSGHDR_SIZE 4 // 1 byte type, 3 bytes length
- #ifdef MG_TLS_SSLKEYLOGFILE
- #include <stdio.h>
- static void mg_ssl_key_log(const char *label, uint8_t client_random[32],
- uint8_t *secret, size_t secretsz) {
- FILE *f;
- char *keylogfile = getenv("SSLKEYLOGFILE");
- if (keylogfile == NULL) return;
- MG_DEBUG(("Dumping key log into %s", keylogfile));
- f = fopen(keylogfile, "a");
- if (f != NULL) {
- size_t i;
- fprintf(f, "%s ", label);
- for (i = 0; i < 32; i++) {
- fprintf(f, "%02x", client_random[i]);
- }
- fprintf(f, " ");
- for (i = 0; i < secretsz; i++) {
- fprintf(f, "%02x", secret[i]);
- }
- fprintf(f, "\n");
- fclose(f);
- } else {
- MG_ERROR(("Cannot open %s", keylogfile));
- }
- }
- #endif
- // for derived tls keys we need SHA256([0]*32)
- static uint8_t zeros[32] = {0};
- static uint8_t zeros_sha256_digest[32] = {
- 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4,
- 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
- 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55};
- // helper to hexdump buffers inline
- static void mg_tls_hexdump(const char *msg, uint8_t *buf, size_t bufsz) {
- MG_VERBOSE(("%s: %M", msg, mg_print_hex, bufsz, buf));
- }
- // helper utilities to parse ASN.1 DER
- struct mg_der_tlv {
- uint8_t type;
- uint32_t len;
- uint8_t *value;
- };
- // parse DER into a TLV record
- static int mg_der_to_tlv(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) {
- uint32_t n = 0;
- if (dersz < 2) return -1;
- tlv->type = der[0];
- tlv->len = der[1];
- tlv->value = der + 2;
- if (tlv->len > 0x7f) { // long-form length
- uint32_t i;
- n = tlv->len - 0x80;
- if (n == 0 || n > 4 || dersz < (2 + n)) return -1;
- tlv->len = 0;
- for (i = 0; i < n; i++) {
- tlv->len = (tlv->len << 8) | der[2 + i];
- }
- tlv->value += n;
- }
- if (tlv->len > dersz - (size_t) (tlv->value - der)) return -1;
- return (int) n;
- }
- static int mg_der_parse(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) {
- int n = mg_der_to_tlv(der, dersz, tlv);
- if (n < 0) return -1;
- if (tlv->len >= (uint32_t)((unsigned) -6)) return -1; // avoid overflow
- return 2 + n + (int) tlv->len; // 2: type, len; n = long form len bytes
- }
- static int mg_der_next(struct mg_der_tlv *parent, struct mg_der_tlv *child) {
- int consumed;
- if (parent->len == 0) return 0;
- consumed = mg_der_parse(parent->value, parent->len, child);
- if (consumed < 0) return -1;
- parent->value += consumed;
- parent->len -= (uint32_t) consumed;
- return 1;
- }
- static int der_find_oid(struct mg_der_tlv *tlv, const uint8_t *oid, size_t oid_len, struct mg_der_tlv *found, int depth) {
- struct mg_der_tlv parent, child;
- int r;
- parent = *tlv;
- while ((r = mg_der_next(&parent, &child)) > 0) {
- if (child.type == 0x06 && child.len == oid_len &&
- memcmp(child.value, oid, oid_len) == 0) {
- return mg_der_next(&parent, found);
- } else if (child.type & 0x20) {
- struct mg_der_tlv sub_parent = child;
- if (depth >= MG_MAX_TLSOID_DEPTH) {
- MG_ERROR(("too nested der"));
- return -1;
- }
- if ((r = der_find_oid(&sub_parent, oid, oid_len, found, depth + 1)) > 0)
- return 1;
- if (r < 0) return -1; // exit on error; r = 0 => not found, keep parsing
- }
- }
- if (r < 0) return -1;
- return 0;
- }
- static int mg_der_find_oid(struct mg_der_tlv *tlv, const uint8_t *oid,
- size_t oid_len, struct mg_der_tlv *found) {
- return der_find_oid(tlv, oid, oid_len, found, 0);
- }
- #if 0
- static void mg_der_debug(struct mg_der_tlv *tlv, int depth) {
- MG_DEBUG(("> %.*sd=%d Type: 0x%02X, Length: %u\n", depth * 4, " ", depth,
- tlv->type, tlv->len));
- if (tlv->type & 0x20) { // Constructed: recurse into children
- struct mg_der_tlv child;
- struct mg_der_tlv parent = *tlv;
- while (mg_der_next(&parent, &child) > 0) {
- mg_der_debug(&child, depth + 1);
- }
- }
- }
- #endif
- // Did we receive a full TLS record in the c->rtls buffer?
- static bool mg_tls_got_record(struct mg_connection *c) {
- return c->rtls.len >= (size_t) TLS_RECHDR_SIZE &&
- c->rtls.len >=
- (size_t) (TLS_RECHDR_SIZE + MG_LOAD_BE16(c->rtls.buf + 3));
- }
- // Remove a single TLS record from the recv buffer
- static void mg_tls_drop_record(struct mg_connection *c) {
- struct mg_iobuf *rio = &c->rtls;
- uint16_t n = MG_LOAD_BE16(rio->buf + 3) + TLS_RECHDR_SIZE;
- mg_iobuf_del(rio, 0, n);
- }
- // Remove a single TLS message from decrypted buffer, remove the wrapping
- // record if it was the last message within a record
- static void mg_tls_drop_message(struct mg_connection *c) {
- uint32_t len;
- struct tls_data *tls = (struct tls_data *) c->tls;
- unsigned char *recv_buf = &c->rtls.buf[tls->recv_offset];
- if (tls->recv_len == 0) return;
- len = MG_LOAD_BE24(recv_buf + 1) + TLS_MSGHDR_SIZE;
- if (tls->recv_len < len) {
- mg_error(c, "wrong size");
- return;
- }
- mg_sha256_update(&tls->sha256, recv_buf, len);
- tls->recv_offset += len;
- tls->recv_len -= len;
- if (tls->recv_len == 0) {
- mg_tls_drop_record(c);
- }
- }
- // TLS1.3 secret derivation based on the key label
- static void mg_tls_derive_secret(const char *label, uint8_t *key, size_t keysz,
- uint8_t *data, size_t datasz, uint8_t *hash,
- size_t hashsz) {
- size_t labelsz = strlen(label);
- uint8_t secret[32];
- uint8_t packed[256] = {0, (uint8_t) hashsz, (uint8_t) labelsz};
- // TODO: assert lengths of label, key, data and hash
- if (labelsz > 0) memmove(packed + 3, label, labelsz);
- packed[3 + labelsz] = (uint8_t) datasz;
- if (datasz > 0) memmove(packed + labelsz + 4, data, datasz);
- packed[4 + labelsz + datasz] = 1;
- mg_hmac_sha256(secret, key, keysz, packed, 5 + labelsz + datasz);
- memmove(hash, secret, hashsz);
- }
- // at this point we have x25519 shared secret, we can generate a set of derived
- // handshake encryption keys
- static void mg_tls_generate_handshake_keys(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- mg_sha256_ctx sha256;
- uint8_t early_secret[32];
- uint8_t pre_extract_secret[32];
- uint8_t hello_hash[32];
- uint8_t server_hs_secret[32];
- uint8_t client_hs_secret[32];
- #if MG_ENABLE_CHACHA20
- const size_t keysz = 32;
- #else
- const size_t keysz = 16;
- #endif
- mg_hmac_sha256(early_secret, NULL, 0, zeros, sizeof(zeros));
- mg_tls_derive_secret("tls13 derived", early_secret, 32, zeros_sha256_digest,
- 32, pre_extract_secret, 32);
- mg_hmac_sha256(tls->enc.handshake_secret, pre_extract_secret,
- sizeof(pre_extract_secret), tls->x25519_sec,
- sizeof(tls->x25519_sec));
- mg_tls_hexdump("hs secret", tls->enc.handshake_secret, 32);
- // mg_sha256_final is not idempotent, need to copy sha256 context to calculate
- // the digest
- memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
- mg_sha256_final(hello_hash, &sha256);
- mg_tls_hexdump("hello hash", hello_hash, 32);
- // derive keys needed for the rest of the handshake
- mg_tls_derive_secret("tls13 s hs traffic", tls->enc.handshake_secret, 32,
- hello_hash, 32, server_hs_secret, 32);
- mg_tls_derive_secret("tls13 c hs traffic", tls->enc.handshake_secret, 32,
- hello_hash, 32, client_hs_secret, 32);
- mg_tls_derive_secret("tls13 key", server_hs_secret, 32, NULL, 0,
- tls->enc.server_write_key, keysz);
- mg_tls_derive_secret("tls13 iv", server_hs_secret, 32, NULL, 0,
- tls->enc.server_write_iv, 12);
- mg_tls_derive_secret("tls13 finished", server_hs_secret, 32, NULL, 0,
- tls->enc.server_finished_key, 32);
- mg_tls_derive_secret("tls13 key", client_hs_secret, 32, NULL, 0,
- tls->enc.client_write_key, keysz);
- mg_tls_derive_secret("tls13 iv", client_hs_secret, 32, NULL, 0,
- tls->enc.client_write_iv, 12);
- mg_tls_derive_secret("tls13 finished", client_hs_secret, 32, NULL, 0,
- tls->enc.client_finished_key, 32);
- mg_tls_hexdump("s hs traffic", server_hs_secret, 32);
- mg_tls_hexdump("s key", tls->enc.server_write_key, keysz);
- mg_tls_hexdump("s iv", tls->enc.server_write_iv, 12);
- mg_tls_hexdump("s finished", tls->enc.server_finished_key, 32);
- mg_tls_hexdump("c hs traffic", client_hs_secret, 32);
- mg_tls_hexdump("c key", tls->enc.client_write_key, keysz);
- mg_tls_hexdump("c iv", tls->enc.client_write_iv, 12);
- mg_tls_hexdump("c finished", tls->enc.client_finished_key, 32);
- #ifdef MG_TLS_SSLKEYLOGFILE
- mg_ssl_key_log("SERVER_HANDSHAKE_TRAFFIC_SECRET", tls->random,
- server_hs_secret, 32);
- mg_ssl_key_log("CLIENT_HANDSHAKE_TRAFFIC_SECRET", tls->random,
- client_hs_secret, 32);
- #endif
- }
- static void mg_tls_generate_application_keys(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- uint8_t hash[32];
- uint8_t premaster_secret[32];
- uint8_t master_secret[32];
- uint8_t server_secret[32];
- uint8_t client_secret[32];
- #if MG_ENABLE_CHACHA20
- const size_t keysz = 32;
- #else
- const size_t keysz = 16;
- #endif
- mg_sha256_ctx sha256;
- memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
- mg_sha256_final(hash, &sha256);
- mg_tls_derive_secret("tls13 derived", tls->enc.handshake_secret, 32,
- zeros_sha256_digest, 32, premaster_secret, 32);
- mg_hmac_sha256(master_secret, premaster_secret, 32, zeros, 32);
- mg_tls_derive_secret("tls13 s ap traffic", master_secret, 32, hash, 32,
- server_secret, 32);
- mg_tls_derive_secret("tls13 key", server_secret, 32, NULL, 0,
- tls->enc.server_write_key, keysz);
- mg_tls_derive_secret("tls13 iv", server_secret, 32, NULL, 0,
- tls->enc.server_write_iv, 12);
- mg_tls_derive_secret("tls13 c ap traffic", master_secret, 32, hash, 32,
- client_secret, 32);
- mg_tls_derive_secret("tls13 key", client_secret, 32, NULL, 0,
- tls->enc.client_write_key, keysz);
- mg_tls_derive_secret("tls13 iv", client_secret, 32, NULL, 0,
- tls->enc.client_write_iv, 12);
- mg_tls_hexdump("s ap traffic", server_secret, 32);
- mg_tls_hexdump("s key", tls->enc.server_write_key, keysz);
- mg_tls_hexdump("s iv", tls->enc.server_write_iv, 12);
- mg_tls_hexdump("s finished", tls->enc.server_finished_key, 32);
- mg_tls_hexdump("c ap traffic", client_secret, 32);
- mg_tls_hexdump("c key", tls->enc.client_write_key, keysz);
- mg_tls_hexdump("c iv", tls->enc.client_write_iv, 12);
- mg_tls_hexdump("c finished", tls->enc.client_finished_key, 32);
- tls->enc.sseq = tls->enc.cseq = 0;
- #ifdef MG_TLS_SSLKEYLOGFILE
- mg_ssl_key_log("SERVER_TRAFFIC_SECRET_0", tls->random, server_secret, 32);
- mg_ssl_key_log("CLIENT_TRAFFIC_SECRET_0", tls->random, client_secret, 32);
- #endif
- }
- // AES GCM encryption of the message + put encoded data into the write buffer
- static bool mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
- size_t msgsz, uint8_t msgtype) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- struct mg_iobuf *wio = &tls->send;
- uint8_t *outmsg;
- uint8_t *tag;
- size_t encsz = msgsz + 16 + 1;
- uint8_t hdr[5] = {MG_TLS_APP_DATA, 0x03, 0x03,
- (uint8_t) ((encsz >> 8) & 0xff), (uint8_t) (encsz & 0xff)};
- uint8_t associated_data[5] = {MG_TLS_APP_DATA, 0x03, 0x03,
- (uint8_t) ((encsz >> 8) & 0xff),
- (uint8_t) (encsz & 0xff)};
- uint8_t nonce[12];
- uint32_t seq = c->is_client ? tls->enc.cseq : tls->enc.sseq;
- uint8_t *key =
- c->is_client ? tls->enc.client_write_key : tls->enc.server_write_key;
- uint8_t *iv =
- c->is_client ? tls->enc.client_write_iv : tls->enc.server_write_iv;
- if (msgsz > 16384) {
- MG_ERROR(("msg longer than recordsz"));
- return false;
- }
- #if MG_ENABLE_CHACHA20
- #else
- mg_gcm_initialize();
- #endif
- memmove(nonce, iv, sizeof(nonce));
- nonce[8] ^= (uint8_t) ((seq >> 24) & 255U);
- nonce[9] ^= (uint8_t) ((seq >> 16) & 255U);
- nonce[10] ^= (uint8_t) ((seq >> 8) & 255U);
- nonce[11] ^= (uint8_t) ((seq) & 255U);
- if (mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)) == 0 ||
- !mg_iobuf_resize(wio, wio->len + encsz))
- return false;
- outmsg = wio->buf + wio->len;
- tag = wio->buf + wio->len + msgsz + 1;
- memmove(outmsg, msg, msgsz);
- outmsg[msgsz] = msgtype;
- #if MG_ENABLE_CHACHA20
- (void) tag; // tag is only used in aes gcm
- {
- size_t n;
- uint8_t *enc = (uint8_t *) mg_calloc(1, msgsz + 256 + 1);
- if (enc == NULL) return false;
- n = mg_chacha20_poly1305_encrypt(enc, key, nonce, associated_data,
- sizeof(associated_data), outmsg,
- msgsz + 1);
- memmove(outmsg, enc, n);
- mg_free(enc);
- }
- #else
- mg_aes_gcm_encrypt(outmsg, outmsg, msgsz + 1, key, 16, nonce, sizeof(nonce),
- associated_data, sizeof(associated_data), tag, 16);
- #endif
- c->is_client ? tls->enc.cseq++ : tls->enc.sseq++;
- wio->len += encsz;
- return true;
- }
- static void verbose_alert(uint8_t *buf) {
- uint8_t level = buf[0], desc = buf[1];
- MG_INFO(("TLS ALERT received: level=%d, desc=%d (%s)", level, desc,
- desc == 0 ? "close_notify"
- : desc == 10 ? "unexpected_message"
- : desc == 20 ? "bad_record_mac"
- : desc == 21 ? "decryption_failed"
- : desc == 40 ? "handshake_failure"
- : desc == 42 ? "bad_certificate"
- : desc == 43 ? "unsupported_certificate"
- : desc == 44 ? "certificate_revoked"
- : desc == 45 ? "certificate_expired"
- : desc == 46 ? "certificate_unknown"
- : desc == 48 ? "unknown_ca"
- : "unknown"));
- }
- // read an encrypted record, decrypt it in place
- static int mg_tls_recv_record(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- struct mg_iobuf *rio = &c->rtls;
- uint16_t msgsz;
- uint8_t *msg;
- uint8_t nonce[12];
- int r;
- uint32_t seq = c->is_client ? tls->enc.sseq : tls->enc.cseq;
- uint8_t *key =
- c->is_client ? tls->enc.server_write_key : tls->enc.client_write_key;
- uint8_t *iv =
- c->is_client ? tls->enc.server_write_iv : tls->enc.client_write_iv;
- if (tls->recv_len > 0) {
- return 0; /* some data from previous record is still present */
- }
- for (;;) {
- if (!mg_tls_got_record(c)) {
- return MG_IO_WAIT;
- }
- if (rio->buf[0] == MG_TLS_APP_DATA) {
- break;
- } else if (rio->buf[0] == MG_TLS_CHANGE_CIPHER) { // skip CCS
- mg_tls_drop_record(c);
- } else if (rio->buf[0] == MG_TLS_ALERT) { // Skip Alerts
- if (rio->len >= 7) {
- verbose_alert(&rio->buf[5]);
- } else {
- MG_INFO(("TLS ALERT packet received (short)"));
- }
- mg_tls_drop_record(c);
- } else {
- mg_error(c, "unexpected packet");
- return -1;
- }
- }
- msgsz = MG_LOAD_BE16(rio->buf + 3);
- msg = rio->buf + 5;
- if (msgsz < 17) {
- mg_error(c, "wrong size");
- return -1;
- }
- memmove(nonce, iv, sizeof(nonce));
- nonce[8] ^= (uint8_t) ((seq >> 24) & 255U);
- nonce[9] ^= (uint8_t) ((seq >> 16) & 255U);
- nonce[10] ^= (uint8_t) ((seq >> 8) & 255U);
- nonce[11] ^= (uint8_t) ((seq) & 255U);
- #if MG_ENABLE_CHACHA20
- {
- uint8_t associated_data[5] = {MG_TLS_APP_DATA, 0x03, 0x03,
- (uint8_t) ((msgsz >> 8) & 0xff),
- (uint8_t) (msgsz & 0xff)};
- uint8_t *dec = (uint8_t *) mg_calloc(1, msgsz);
- size_t n;
- if (dec == NULL) {
- mg_error(c, "TLS OOM");
- return -1;
- }
- n = mg_chacha20_poly1305_decrypt(dec, key, nonce, associated_data,
- sizeof(associated_data), msg, msgsz);
- if (n == (size_t) -1) {
- mg_free(dec);
- mg_error(c, "decryption error");
- return -1;
- }
- memmove(msg, dec, n);
- mg_free(dec);
- }
- #else
- {
- uint8_t associated_data[5] = {MG_TLS_APP_DATA, 0x03, 0x03,
- (uint8_t) ((msgsz >> 8) & 0xff),
- (uint8_t) (msgsz & 0xff)};
- mg_gcm_initialize();
- if (mg_aes_gcm_decrypt(msg, msg, msgsz - 16, key, 16, nonce, sizeof(nonce),
- associated_data, sizeof(associated_data),
- msg + msgsz - 16, 16) != 0) {
- mg_error(c, "GCM tag verify failed");
- return -1;
- }
- }
- #endif
- r = msgsz - 16 - 1;
- tls->content_type = msg[msgsz - 16 - 1];
- if (tls->content_type == MG_TLS_ALERT) { // Process Alerts
- verbose_alert(msg);
- if (msg[0] == 2) {
- mg_error(c, "TLS Fatal alert");
- return -1;
- } else {
- mg_tls_drop_record(c);
- return MG_IO_WAIT;
- }
- }
- tls->recv_offset = (size_t) msg - (size_t) rio->buf;
- tls->recv_len = (size_t) msgsz - 16 - 1;
- c->is_client ? tls->enc.sseq++ : tls->enc.cseq++;
- return r;
- }
- static void mg_tls_calc_cert_verify_hash(struct mg_connection *c,
- uint8_t hash[32], bool is_client) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- uint8_t sig_content[130];
- mg_sha256_ctx sha256;
- memset(sig_content, 0x20, 64);
- if (is_client) {
- uint8_t client_context[34] = "TLS 1.3, client CertificateVerify";
- memcpy(sig_content + 64, client_context, sizeof(client_context));
- } else {
- uint8_t server_context[34] = "TLS 1.3, server CertificateVerify";
- memcpy(sig_content + 64, server_context, sizeof(server_context));
- }
- memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
- mg_sha256_final(sig_content + 98, &sha256);
- mg_sha256_init(&sha256);
- mg_sha256_update(&sha256, sig_content, sizeof(sig_content));
- mg_sha256_final(hash, &sha256);
- }
- // read and parse ClientHello record
- static int mg_tls_server_recv_hello(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- struct mg_iobuf *rio = &c->rtls;
- uint8_t session_id_len;
- uint16_t j;
- uint16_t cipher_suites_len;
- uint16_t ext_len;
- uint8_t *ext;
- uint16_t msgsz;
- if (!mg_tls_got_record(c)) {
- return MG_IO_WAIT;
- }
- if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_CLIENT_HELLO) {
- mg_error(c, "not a client hello packet");
- return -1;
- }
- if (rio->len < 50) goto fail;
- msgsz = MG_LOAD_BE16(rio->buf + 3);
- if (((uint32_t) msgsz + 4) > rio->len) goto fail;
- mg_sha256_update(&tls->sha256, rio->buf + 5, msgsz);
- // store client random
- memmove(tls->random, rio->buf + 11, sizeof(tls->random));
- // store session_id
- session_id_len = rio->buf[43];
- if (session_id_len == sizeof(tls->session_id)) {
- if (rio->len < (size_t)(46 + session_id_len)) goto fail; // 2: ciphers len
- memmove(tls->session_id, rio->buf + 44, session_id_len);
- } else if (session_id_len != 0) {
- MG_ERROR(("bad session id len"));
- goto fail;
- } // session_id_len is either sizeof(tls->session_id) or 0
- if (((uint32_t) 44 + 2 + session_id_len) > rio->len) goto fail;
- cipher_suites_len = MG_LOAD_BE16(rio->buf + 44 + session_id_len);
- if (((uint32_t) cipher_suites_len + 46 + 2 + 2 + session_id_len) > rio->len)
- goto fail;
- ext_len = MG_LOAD_BE16(rio->buf + 48 + session_id_len + cipher_suites_len);
- ext = rio->buf + 50 + session_id_len + cipher_suites_len;
- if (((unsigned char *) ext + ext_len) > (rio->buf + rio->len)) goto fail;
- for (j = 0; j < ext_len;) {
- uint16_t k;
- uint16_t key_exchange_len;
- uint8_t *key_exchange;
- uint16_t n = MG_LOAD_BE16(ext + j + 2);
- if (((uint32_t) n + j + 4) > ext_len) goto fail;
- if (MG_LOAD_BE16(ext + j) != 0x0033) { // not a key share extension, ignore
- j += (uint16_t) (n + 4);
- continue;
- }
- key_exchange_len = MG_LOAD_BE16(ext + j + 4);
- key_exchange = ext + j + 6;
- if ((key_exchange + key_exchange_len) > ((uint8_t *) rio->buf + rio->len))
- goto fail;
- for (k = 0; k < key_exchange_len;) {
- uint16_t m = MG_LOAD_BE16(key_exchange + k + 2);
- if (((uint32_t) m + k + 4) > key_exchange_len) goto fail;
- if (m == 32 && key_exchange[k] == 0x00 && key_exchange[k + 1] == 0x1d) {
- memmove(tls->x25519_cli, key_exchange + k + 4, m);
- mg_tls_drop_record(c);
- return 0;
- }
- k += (uint16_t) (m + 4);
- }
- j += (uint16_t) (n + 4);
- }
- fail:
- mg_error(c, "bad client hello");
- return -1;
- }
- #define PLACEHOLDER_8B 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'
- #define PLACEHOLDER_16B PLACEHOLDER_8B, PLACEHOLDER_8B
- #define PLACEHOLDER_32B PLACEHOLDER_16B, PLACEHOLDER_16B
- // put ServerHello record into wio buffer
- static bool mg_tls_server_send_hello(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- struct mg_iobuf *wio = &tls->send;
- // clang-format off
- uint8_t msg_server_hello[122] = {
- // server hello, tls 1.2
- 0x02, 0x00, 0x00, 0x76, 0x03, 0x03,
- // random (32 bytes)
- PLACEHOLDER_32B,
- // session ID length + session ID (32 bytes)
- 0x20, PLACEHOLDER_32B,
- #if MG_ENABLE_CHACHA20
- // TLS_CHACHA20_POLY1305_SHA256 + no compression
- 0x13, 0x03, 0x00,
- #else
- // TLS_AES_128_GCM_SHA256 + no compression
- 0x13, 0x01, 0x00,
- #endif
- // extensions + keyshare
- 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20,
- // x25519 keyshare
- PLACEHOLDER_32B,
- // supported versions (tls1.3 == 0x304)
- 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04};
- // clang-format on
- // calculate keyshare
- uint8_t x25519_pub[X25519_BYTES];
- uint8_t x25519_prv[X25519_BYTES];
- if (!mg_random(x25519_prv, sizeof(x25519_prv))) mg_error(c, "RNG");
- if( mg_tls_x25519(x25519_pub, x25519_prv, X25519_BASE_POINT, 1) < 0 || mg_tls_x25519(tls->x25519_sec, x25519_prv, tls->x25519_cli, 1) < 0) {
- mg_error(c, "bad key");
- return false;
- }
- mg_tls_hexdump("s x25519 sec", tls->x25519_sec, sizeof(tls->x25519_sec));
- // fill in the gaps: random + session ID + keyshare
- memmove(msg_server_hello + 6, tls->random, sizeof(tls->random));
- memmove(msg_server_hello + 39, tls->session_id, sizeof(tls->session_id));
- memmove(msg_server_hello + 84, x25519_pub, sizeof(x25519_pub));
- // server hello message
- if (mg_iobuf_add(wio, wio->len, "\x16\x03\x03\x00\x7a", 5) == 0 ||
- mg_iobuf_add(wio, wio->len, msg_server_hello, sizeof(msg_server_hello)) ==
- 0)
- return false;
- mg_sha256_update(&tls->sha256, msg_server_hello, sizeof(msg_server_hello));
- // change cipher message
- if (mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) == 0)
- return false;
- return true;
- }
- static bool mg_tls_server_send_ext(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- // server extensions
- uint8_t ext[6] = {0x08, 0, 0, 2, 0, 0};
- mg_sha256_update(&tls->sha256, ext, sizeof(ext));
- return mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE);
- }
- // signature algorithms we actually support:
- // rsa_pkcs1_sha256, rsa_pss_rsae_sha256 and ecdsa_secp256r1_sha256
- static const uint8_t secp256r1_sig_algs[12] = {
- 0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01};
- static bool mg_tls_server_send_cert_request(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- uint8_t req[13 + sizeof(secp256r1_sig_algs)];
- req[0] = MG_TLS_CERTIFICATE_REQUEST; // handshake header
- MG_STORE_BE24(req + 1, 9 + sizeof(secp256r1_sig_algs));
- req[4] = 0; // context length
- MG_STORE_BE16(req + 5, 6 + sizeof(secp256r1_sig_algs)); // extensions length
- MG_STORE_BE16(req + 7, 13); // "signature algorithms"
- MG_STORE_BE16(req + 9, 2 + sizeof(secp256r1_sig_algs)); // length
- MG_STORE_BE16(
- req + 11,
- sizeof(secp256r1_sig_algs)); // signature hash algorithms length
- memcpy(req + 13, (uint8_t *) secp256r1_sig_algs, sizeof(secp256r1_sig_algs));
- mg_sha256_update(&tls->sha256, req, sizeof(req));
- return mg_tls_encrypt(c, req, sizeof(req), MG_TLS_HANDSHAKE);
- }
- static bool mg_tls_send_cert(struct mg_connection *c, bool is_client) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- int send_ca = !is_client && tls->ca_der.len > 0;
- // DER certificate + CA (server optional)
- size_t i, offset, total_size = tls->cert_der.len + 5;
- uint8_t *cert;
- bool res = false;
- for (i = 1; i < tls->chain_len; i++) {
- total_size += tls->chain_der[i].len + 5;
- }
- if (send_ca) {
- total_size += tls->ca_der.len + 5;
- }
- cert = (uint8_t *) mg_calloc(1, 13 + total_size);
- if (cert == NULL) return res;
- cert[0] = MG_TLS_CERTIFICATE; // handshake header
- if (is_client && tls->cert_der.len == 0) total_size = 0; // empty list
- MG_STORE_BE24(cert + 1, total_size + 4);
- cert[4] = 0; // request context
- MG_STORE_BE24(cert + 5, total_size); // 3 bytes: cert (s) length
- offset = 8;
- if (total_size > 0) { // handle empty list, RFC-8446 4.4.2
- MG_STORE_BE24(cert + offset, tls->cert_der.len); // 3 bytes: 1st cert len
- offset += 3;
- // bytes 11+ are certificate in DER format
- memmove(cert + offset, tls->cert_der.buf, tls->cert_der.len);
- offset += tls->cert_der.len;
- MG_STORE_BE16(cert + offset, 0); // certificate extensions (none)
- offset += 2;
- for (i = 1; i < tls->chain_len; i++) {
- MG_STORE_BE24(cert + offset, tls->chain_der[i].len);
- offset += 3;
- memmove(cert + offset, tls->chain_der[i].buf, tls->chain_der[i].len);
- offset += tls->chain_der[i].len;
- MG_STORE_BE16(cert + offset, 0); // certificate extensions (none)
- offset += 2;
- }
- if (send_ca) {
- MG_STORE_BE24(cert + offset, tls->ca_der.len); // 3 bytes: CA cert length
- offset += 3;
- memmove(cert + offset, tls->ca_der.buf,
- tls->ca_der.len); // CA cert data
- offset += tls->ca_der.len;
- MG_STORE_BE16(cert + offset, 0); // certificate extensions (none)
- offset += 2;
- }
- }
- mg_sha256_update(&tls->sha256, cert, offset);
- res = mg_tls_encrypt(c, cert, offset, MG_TLS_HANDSHAKE);
- mg_free(cert);
- return res;
- }
- // type adapter between uECC hash context and our sha256 implementation
- typedef struct SHA256_HashContext {
- MG_UECC_HashContext uECC;
- mg_sha256_ctx ctx;
- } SHA256_HashContext;
- static void init_SHA256(const MG_UECC_HashContext *base) {
- SHA256_HashContext *c = (SHA256_HashContext *) base;
- mg_sha256_init(&c->ctx);
- }
- static void update_SHA256(const MG_UECC_HashContext *base,
- const uint8_t *message, unsigned message_size) {
- SHA256_HashContext *c = (SHA256_HashContext *) base;
- mg_sha256_update(&c->ctx, message, message_size);
- }
- static void finish_SHA256(const MG_UECC_HashContext *base,
- uint8_t *hash_result) {
- SHA256_HashContext *c = (SHA256_HashContext *) base;
- mg_sha256_final(hash_result, &c->ctx);
- }
- static void mg_tls_mgf1(uint8_t *mask, size_t mask_len, const uint8_t *seed,
- size_t seed_len) {
- uint32_t counter = 0;
- size_t chunk, chunk_len, generated = 0;
- while (generated < mask_len) {
- mg_sha256_ctx ctx;
- uint8_t digest[32];
- uint8_t ctr[4];
- ctr[0] = (uint8_t) (counter >> 24);
- ctr[1] = (uint8_t) (counter >> 16);
- ctr[2] = (uint8_t) (counter >> 8);
- ctr[3] = (uint8_t) counter;
- mg_sha256_init(&ctx);
- mg_sha256_update(&ctx, seed, seed_len);
- mg_sha256_update(&ctx, ctr, sizeof(ctr));
- mg_sha256_final(digest, &ctx);
- chunk_len = mask_len - generated;
- chunk = (chunk_len < sizeof(digest) ? chunk_len : sizeof(digest));
- memmove(mask + generated, digest, chunk);
- generated += chunk;
- counter++;
- }
- }
- static unsigned int mg_tls_rsa_bits(const struct mg_str *n) {
- size_t i = 0;
- unsigned int bits = 0;
- while (i < n->len && n->buf[i] == 0) i++;
- if (i == n->len) return 0;
- bits = (unsigned int) ((n->len - i) * 8);
- {
- uint8_t byte = (uint8_t) n->buf[i];
- while ((byte & 0x80U) == 0) {
- bits--;
- byte = (uint8_t) (byte << 1);
- }
- }
- return bits;
- }
- static bool mg_tls_pss_encode(const uint8_t *hash, size_t hashlen,
- const struct mg_str *n, uint8_t *em) {
- size_t emlen = n->len, saltlen = hashlen, dblen, pslen, i;
- uint8_t salt[64]; // Max salt size for any reasonable hash
- uint8_t m[136]; // 8 + max hash (64) + max salt (64) = 136
- uint8_t H[64]; // Max hash size
- uint8_t DB[512]; // Max for 4096-bit RSA
- uint8_t mask[512]; // Max for 4096-bit RSA
- mg_sha256_ctx ctx;
- // Check bounds
- if (saltlen > sizeof(salt) || (8 + hashlen + saltlen) > sizeof(m) ||
- hashlen > sizeof(H) || emlen > sizeof(DB)) {
- MG_ERROR(("RSA key too large for static buffers"));
- return false;
- }
- if (emlen < hashlen + saltlen + 2) {
- return false;
- }
- if (!mg_random(salt, saltlen)) {
- return false;
- }
- MG_VERBOSE(("PSS salt: %M", mg_print_hex, saltlen, salt));
- // Build m = 8 zero bytes || hash || salt
- memset(m, 0, 8);
- memcpy(m + 8, hash, hashlen);
- memcpy(m + 8 + hashlen, salt, saltlen);
- mg_sha256_init(&ctx);
- mg_sha256_update(&ctx, m, 8 + hashlen + saltlen);
- mg_sha256_final(H, &ctx);
- MG_VERBOSE(("PSS H: %M", mg_print_hex, hashlen, H));
- dblen = emlen - hashlen - 1;
- pslen = emlen - hashlen - saltlen - 2;
- // Build DB = PS || 0x01 || salt
- memset(DB, 0, pslen);
- DB[pslen] = 0x01;
- memcpy(DB + pslen + 1, salt, saltlen);
- // Generate mask and apply to DB
- mg_tls_mgf1(mask, dblen, H, hashlen);
- for (i = 0; i < dblen; i++) DB[i] ^= mask[i];
- {
- // PSS standard: emBits = modulus_bit_length - 1
- unsigned rsa_bits = mg_tls_rsa_bits(n);
- unsigned embits = rsa_bits - 1;
- unsigned unused = (unsigned) (8 * emlen - embits);
- MG_VERBOSE(("RSA modulus bits: %u, emBits: %u, emlen: %zu, unused: %u",
- rsa_bits, embits, emlen, unused));
- if (unused > 0 && unused < 8) {
- uint8_t mask_byte = (uint8_t) (0xff >> unused);
- MG_VERBOSE(("Applying mask 0x%02x to first byte (was 0x%02x)", mask_byte,
- DB[0]));
- DB[0] &= mask_byte;
- MG_VERBOSE(("First byte after mask: 0x%02x", DB[0]));
- }
- }
- // Build final em = maskedDB || H || 0xbc
- memcpy(em, DB, dblen);
- memcpy(em + dblen, H, hashlen);
- em[emlen - 1] = 0xbc;
- return true;
- }
- static bool mg_tls_rsa_sign(struct tls_data *tls, const uint8_t *em,
- size_t emlen, uint8_t *sig) {
- const uint8_t *n = (const uint8_t *) tls->rsa.n.buf;
- size_t nlen = tls->rsa.n.len;
- int crt_result;
- while (nlen > 0 && *n == 0) n++, nlen--;
- if (emlen != nlen) return false;
- #if MG_TLS_RSA_USE_CRT
- // RSA CRT (Chinese Remainder Theorem) optimization:
- // s1 = em^dP mod p
- // s2 = em^dQ mod q
- // h = qInv * (s1 - s2) mod p
- // s = s2 + h * q
- if (tls->rsa.p.len == 0 || tls->rsa.q.len == 0 || tls->rsa.dP.len == 0 ||
- tls->rsa.dQ.len == 0 || tls->rsa.qInv.len == 0) {
- MG_ERROR(("CRT parameters missing, cannot use CRT optimization"));
- return false;
- }
- MG_VERBOSE(("Using RSA-CRT optimization"));
- crt_result = mg_rsa_crt_sign(
- em, emlen, (const uint8_t *) tls->rsa.dP.buf, tls->rsa.dP.len,
- (const uint8_t *) tls->rsa.dQ.buf, tls->rsa.dQ.len,
- (const uint8_t *) tls->rsa.p.buf, tls->rsa.p.len,
- (const uint8_t *) tls->rsa.q.buf, tls->rsa.q.len,
- (const uint8_t *) tls->rsa.qInv.buf, tls->rsa.qInv.len, sig, nlen);
- if (crt_result == 0) {
- uint8_t check[512];
- bool verified;
- if (nlen > sizeof(check) ||
- mg_rsa_mod_pow(n, nlen, (const uint8_t *) tls->rsa.e.buf,
- tls->rsa.e.len, sig, nlen, check, nlen) != 0) {
- MG_ERROR(("CRT signature verification failed"));
- return false;
- }
- verified = memcmp(check, em, emlen) == 0;
- mg_bzero(check, sizeof(check));
- if (!verified) {
- MG_ERROR(("CRT signature verification failed"));
- return false;
- }
- MG_VERBOSE(("CRT signature successful (first 4 bytes): %02x %02x %02x %02x",
- sig[0], sig[1], sig[2], sig[3]));
- MG_VERBOSE(("CRT signature successful (last 4 bytes): %02x %02x %02x %02x",
- sig[nlen - 4], sig[nlen - 3], sig[nlen - 2], sig[nlen - 1]));
- return true;
- } else {
- MG_ERROR(("CRT signing failed"));
- return false;
- }
- #else
- int ret;
- // Standard RSA: s = em^d mod n
- memset(sig, 0, nlen);
- ret = mg_rsa_mod_pow(n, nlen, (const uint8_t *) tls->rsa.d.buf,
- tls->rsa.d.len, em, emlen, sig, nlen);
- if (ret == 0) {
- MG_VERBOSE(("RSA signature first 4 bytes: %02x %02x %02x %02x", sig[0],
- sig[1], sig[2], sig[3]));
- MG_VERBOSE(("RSA signature last 4 bytes: %02x %02x %02x %02x",
- sig[nlen - 4], sig[nlen - 3], sig[nlen - 2], sig[nlen - 1]));
- }
- return ret == 0;
- #endif
- }
- static size_t mg_rsa_trim_len(const uint8_t **p, size_t n) {
- while (n > 0 && **p == 0) (*p)++, n--;
- return n;
- }
- static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- uint8_t hash[32] = {0};
- mg_tls_calc_cert_verify_hash(c, (uint8_t *) hash, is_client);
- if (tls->rsa.n.len > 0 && tls->rsa.d.len > 0) {
- // RSA certificate verify packet
- const uint8_t *n = (const uint8_t *) tls->rsa.n.buf;
- size_t nlen = tls->rsa.n.len;
- struct mg_str rn;
- size_t emlen, verifysz;
- uint8_t em[512]; // Max for 4096-bit RSA
- uint8_t verify[520]; // 8 + 512 max
- nlen = mg_rsa_trim_len(&n, nlen);
- rn = mg_str_n((char *) n, nlen);
- emlen = nlen;
- verifysz = 8U + emlen;
- // Check bounds
- if (emlen > sizeof(em) || verifysz > sizeof(verify)) {
- MG_ERROR(("RSA key too large for static buffers"));
- return false;
- }
- if (!mg_tls_pss_encode(hash, sizeof(hash), &rn, em)) {
- MG_ERROR(("Failed PSS encode"));
- return false;
- }
- // Validate PSS encoded message format
- if (em[emlen - 1] != 0xbc) {
- MG_ERROR(("Invalid PSS encoding: last byte is 0x%02x, expected 0xbc",
- em[emlen - 1]));
- return false;
- }
- // Build verify packet header, then sign directly into the packet
- verify[0] = 0x0f;
- MG_STORE_BE24(verify + 1, emlen + 4);
- MG_STORE_BE16(verify + 4, 0x0804);
- MG_STORE_BE16(verify + 6, emlen);
- // Sign directly into the verify buffer (verify + 8 = signature location)
- memset(verify + 8, 0, emlen); // Initialize signature area
- if (!mg_tls_rsa_sign(tls, em, emlen, verify + 8)) {
- MG_ERROR(("Failed RSA sign"));
- return false;
- }
- MG_VERBOSE(
- ("PSS EM first 4: %02x %02x %02x %02x", em[0], em[1], em[2], em[3]));
- MG_VERBOSE(("PSS EM last 4: %02x %02x %02x %02x", em[emlen - 4],
- em[emlen - 3], em[emlen - 2], em[emlen - 1]));
- mg_sha256_update(&tls->sha256, verify, verifysz);
- return mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE);
- } else {
- // EC certificate verify packet
- uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00};
- uint8_t tmp[2 * 32 + 64] = {0};
- struct SHA256_HashContext ctx = {
- {&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp},
- {{0}, 0, 0, {0}}};
- size_t sigsz, verifysz = 0;
- int neg1, neg2;
- uint8_t sig[64] = {0};
- mg_uecc_sign_deterministic(tls->ec_key, hash, sizeof(hash), &ctx.uECC, sig,
- mg_uecc_secp256r1());
- neg1 = !!(sig[0] & 0x80);
- neg2 = !!(sig[32] & 0x80);
- verify[8] = 0x30; // ASN.1 SEQUENCE
- verify[9] = (uint8_t) (68 + neg1 + neg2);
- verify[10] = 0x02; // ASN.1 INTEGER
- verify[11] = (uint8_t) (32 + neg1);
- memmove(verify + 12 + neg1, sig, 32);
- verify[12 + 32 + neg1] = 0x02; // ASN.1 INTEGER
- verify[13 + 32 + neg1] = (uint8_t) (32 + neg2);
- memmove(verify + 14 + 32 + neg1 + neg2, sig + 32, 32);
- sigsz = (size_t) (70 + neg1 + neg2);
- verifysz = 8U + sigsz;
- verify[3] = (uint8_t) (sigsz + 4);
- verify[7] = (uint8_t) sigsz;
- mg_sha256_update(&tls->sha256, verify, verifysz);
- return mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE);
- }
- }
- static bool mg_tls_server_send_finish(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- mg_sha256_ctx sha256;
- uint8_t hash[32];
- uint8_t finish[36] = {0x14, 0, 0, 32};
- memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
- mg_sha256_final(hash, &sha256);
- mg_hmac_sha256(finish + 4, tls->enc.server_finished_key, 32, hash, 32);
- if (!mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE))
- return false;
- mg_sha256_update(&tls->sha256, finish, sizeof(finish));
- return true;
- }
- static int mg_tls_server_recv_finish(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- unsigned char *recv_buf;
- // we have to backup sha256 value to restore it later, since Finished record
- // is exceptional and is not supposed to be added to the rolling hash
- // calculation.
- mg_sha256_ctx sha256 = tls->sha256;
- if (mg_tls_recv_record(c) < 0) {
- return -1;
- }
- recv_buf = &c->rtls.buf[tls->recv_offset];
- if (recv_buf[0] != MG_TLS_FINISHED) {
- mg_error(c, "expected Finish but got msg 0x%02x", recv_buf[0]);
- return -1;
- }
- mg_tls_drop_message(c);
- // restore hash
- tls->sha256 = sha256;
- return 0;
- }
- static bool mg_tls_client_send_hello(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- struct mg_iobuf *wio = &tls->send;
- uint8_t x25519_pub[X25519_BYTES];
- // - "signature algorithms we actually support", see above
- // uint8_t secp256r1_sig_algs[]
- // - all popular signature algorithms (if we don't care about verification)
- uint8_t all_sig_algs[34] = {
- 0x00, 0x0d, 0x00, 0x1e, 0x00, 0x1c, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03,
- 0x08, 0x07, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04,
- 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01};
- uint8_t server_name_ext[9] = {0x00, 0x00, 0x00, 0xfe, 0x00,
- 0xfe, 0x00, 0x00, 0xfe};
- // clang-format off
- uint8_t msg_client_hello[145] = {
- // TLS Client Hello header reported as TLS1.2 (5)
- 0x16, 0x03, 0x03, 0x00, 0xfe,
- // client hello, tls 1.2 (6)
- 0x01, 0x00, 0x00, 0x8c, 0x03, 0x03,
- // random (32 bytes)
- PLACEHOLDER_32B,
- // session ID length + session ID (32 bytes)
- 0x20, PLACEHOLDER_32B, 0x00,
- 0x02, // size = 2 bytes
- #if MG_ENABLE_CHACHA20
- // TLS_CHACHA20_POLY1305_SHA256
- 0x13, 0x03,
- #else
- // TLS_AES_128_GCM_SHA256
- 0x13, 0x01,
- #endif
- // no compression
- 0x01, 0x00,
- // extensions + keyshare
- 0x00, 0xfe,
- // x25519 keyshare
- 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20,
- PLACEHOLDER_32B,
- // supported groups (x25519)
- 0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0x00, 0x1d,
- // supported versions (tls1.3 == 0x304)
- 0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04,
- // session ticket (none)
- 0x00, 0x23, 0x00, 0x00, // 144 bytes till here
- };
- // clang-format on
- const char *hostname = tls->hostname;
- size_t hostnamesz = strlen(tls->hostname);
- size_t hostname_extsz = hostnamesz ? hostnamesz + 9 : 0;
- uint8_t *sig_alg =
- tls->skip_verification ? all_sig_algs : (uint8_t *) secp256r1_sig_algs;
- size_t sig_alg_sz = tls->skip_verification ? sizeof(all_sig_algs)
- : sizeof(secp256r1_sig_algs);
- // patch ClientHello with correct hostname ext length (if any)
- MG_STORE_BE16(msg_client_hello + 3,
- hostname_extsz + 183 - 9 - 34 + sig_alg_sz);
- MG_STORE_BE16(msg_client_hello + 7,
- hostname_extsz + 179 - 9 - 34 + sig_alg_sz);
- MG_STORE_BE16(msg_client_hello + 82,
- hostname_extsz + 104 - 9 - 34 + sig_alg_sz);
- if (hostnamesz > 0) {
- MG_STORE_BE16(server_name_ext + 2, hostnamesz + 5);
- MG_STORE_BE16(server_name_ext + 4, hostnamesz + 3);
- MG_STORE_BE16(server_name_ext + 7, hostnamesz);
- }
- // calculate keyshare
- if (!mg_random(tls->x25519_cli, sizeof(tls->x25519_cli))) mg_error(c, "RNG");
- mg_tls_x25519(x25519_pub, tls->x25519_cli, X25519_BASE_POINT, 1);
- // fill in the gaps: random + session ID + keyshare
- if (!mg_random(tls->session_id, sizeof(tls->session_id))) mg_error(c, "RNG");
- if (!mg_random(tls->random, sizeof(tls->random))) mg_error(c, "RNG");
- memmove(msg_client_hello + 11, tls->random, sizeof(tls->random));
- memmove(msg_client_hello + 44, tls->session_id, sizeof(tls->session_id));
- memmove(msg_client_hello + 94, x25519_pub, sizeof(x25519_pub));
- // client hello message
- if (mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello)) ==
- 0)
- return false;
- mg_sha256_update(&tls->sha256, msg_client_hello + 5,
- sizeof(msg_client_hello) - 5);
- if (mg_iobuf_add(wio, wio->len, sig_alg, sig_alg_sz) == 0) return false;
- mg_sha256_update(&tls->sha256, sig_alg, sig_alg_sz);
- if (hostnamesz > 0) {
- if (mg_iobuf_add(wio, wio->len, server_name_ext, sizeof(server_name_ext)) ==
- 0 ||
- mg_iobuf_add(wio, wio->len, hostname, hostnamesz) == 0)
- return false;
- mg_sha256_update(&tls->sha256, server_name_ext, sizeof(server_name_ext));
- mg_sha256_update(&tls->sha256, (uint8_t *) hostname, hostnamesz);
- }
- // change cipher message
- if (mg_iobuf_add(wio, wio->len, (const char *) "\x14\x03\x03\x00\x01\x01",
- 6) == 0)
- return false;
- return true;
- }
- static int mg_tls_client_recv_hello(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- struct mg_iobuf *rio = &c->rtls;
- uint16_t msgsz;
- uint8_t *ext;
- uint16_t ext_len;
- int j;
- if (!mg_tls_got_record(c)) {
- return MG_IO_WAIT;
- }
- if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->len < 6 || rio->buf[5] != MG_TLS_SERVER_HELLO) {
- if (rio->buf[0] == MG_TLS_ALERT && rio->len >= 7) {
- verbose_alert(&rio->buf[5]);
- mg_error(c, "TLS error alert");
- return -1;
- }
- if (rio->len >= 6) MG_ERROR(("got packet type 0x%02x/0x%02x", rio->buf[0], rio->buf[5]));
- mg_error(c, "not a server hello packet");
- return -1;
- }
- if (rio->len < 5 + 39 + 32 + 3 + 2) goto fail;
- msgsz = MG_LOAD_BE16(rio->buf + 3);
- if (msgsz > rio->len - 5) goto fail;
- mg_sha256_update(&tls->sha256, rio->buf + 5, msgsz);
- ext_len = MG_LOAD_BE16(rio->buf + 5 + 39 + 32 + 3);
- ext = rio->buf + 5 + 39 + 32 + 3 + 2;
- if (ext_len > (rio->len - (5 + 39 + 32 + 3 + 2))) goto fail;
- for (j = 0; j < ext_len;) {
- uint16_t ext_type, ext_len2, group, key_exchange_len;
- uint8_t *key_exchange;
- if ((ext_len - j) < 4) goto fail;
- ext_type = MG_LOAD_BE16(ext + j);
- ext_len2 = MG_LOAD_BE16(ext + j + 2);
- if (ext_len2 > (ext_len - j - 4)) goto fail;
- if (ext_type != 0x0033) { // not a key share extension, ignore
- j += (uint16_t) (ext_len2 + 4);
- continue;
- }
- if (ext_len2 < (2 + 2 + 32)) goto fail;
- group = MG_LOAD_BE16(ext + j + 4);
- if (group != 0x001d) {
- mg_error(c, "bad key exchange group");
- return -1;
- }
- key_exchange_len = MG_LOAD_BE16(ext + j + 6);
- key_exchange = ext + j + 8;
- if (key_exchange_len != 32 || mg_tls_x25519(tls->x25519_sec, tls->x25519_cli, key_exchange, 1) < 0) {
- mg_error(c, "bad key");
- return -1;
- }
- mg_tls_hexdump("c x25519 sec", tls->x25519_sec, 32);
- mg_tls_drop_record(c);
- /* generate handshake keys */
- mg_tls_generate_handshake_keys(c);
- return 0;
- }
- fail:
- mg_error(c, "bad server hello");
- return -1;
- }
- static int mg_tls_client_recv_ext(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- unsigned char *recv_buf;
- if (mg_tls_recv_record(c) < 0) {
- return -1;
- }
- recv_buf = &c->rtls.buf[tls->recv_offset];
- if (recv_buf[0] != MG_TLS_ENCRYPTED_EXTENSIONS) {
- mg_error(c, "expected server extensions but got msg 0x%02x", recv_buf[0]);
- return -1;
- }
- mg_tls_drop_message(c);
- return 0;
- }
- struct mg_tls_cert {
- bool is_ec_pubkey;
- bool is_ca;
- struct mg_str sn;
- struct mg_str pubkey;
- struct mg_der_tlv issuer;
- struct mg_der_tlv subj;
- struct mg_str sig; // signature
- uint8_t tbshash[48]; // 32B for sha256/secp256, 48B for sha384/secp384
- size_t tbshashsz; // actual TBS hash size
- };
- static void mg_der_debug_cert_name(const char *name, struct mg_der_tlv *tlv) {
- struct mg_der_tlv v;
- struct mg_str cn, c, o, ou;
- if (mg_log_level < MG_LL_VERBOSE) return; // skip recursive computations
- cn = c = o = ou = mg_str("");
- if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x03", 3, &v) > 0)
- cn = mg_str_n((const char *) v.value, v.len);
- if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x06", 3, &v) > 0)
- c = mg_str_n((const char *) v.value, v.len);
- if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x0a", 3, &v) > 0)
- o = mg_str_n((const char *) v.value, v.len);
- if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x0b", 3, &v) > 0)
- ou = mg_str_n((const char *) v.value, v.len);
- MG_VERBOSE(("%s: CN=%.*s, C=%.*s, O=%.*s, OU=%.*s", name, cn.len, cn.buf,
- c.len, c.buf, o.len, o.buf, ou.len, ou.buf));
- }
- static uint64_t asnt2t(uint8_t *v, uint8_t type) {
- unsigned int y, mo, d, h, mi, ss;
- y = 10U * (*v++ - '0'), y += (*v++ - '0');
- if (type == 0x17) { // UTCTime, RFC-5280 4.1.2.5.1 YYMMDDHHMMSSZ
- if (y >= 50) return (uint64_t) 0; // 19YY is in the past
- y += 2000;
- } else { // GeneralizedTime, RFC-5280 4.1.2.5.2 YYYYMMDDHHMMSSZ
- y *= 100U, y += 10U * (*v++ - '0'), y += (*v++ - '0');
- }
- mo = 10U * (*v++ - '0'), mo += (*v++ - '0');
- d = 10U * (*v++ - '0'), d += (*v++ - '0');
- h = 10U * (*v++ - '0'), h += (*v++ - '0');
- mi = 10U * (*v++ - '0'), mi += (*v++ - '0');
- ss = 10U * (*v++ - '0'), ss += (*v++ - '0');
- if (*v != 'Z') return 0; // invalid
- return mg_timegm(y, mo, d, h, mi, ss);
- }
- static int mg_tls_parse_cert_der(void *buf, size_t dersz,
- struct mg_tls_cert *cert) {
- uint8_t *tbs, *der = (uint8_t *) buf;
- size_t tbssz;
- struct mg_der_tlv root, tbs_cert, field, algo; // pubkey, signature;
- struct mg_der_tlv pki, pki_algo, pki_key, pki_curve, raw_sig;
- // Parse outermost SEQUENCE
- if (mg_der_parse(der, dersz, &root) <= 0 || root.type != 0x30) return -1;
- // Parse TBSCertificate SEQUENCE
- tbs = root.value;
- if (mg_der_next(&root, &tbs_cert) <= 0 || tbs_cert.type != 0x30) return -1;
- tbssz = (size_t) (tbs_cert.value + tbs_cert.len - tbs);
- // Parse Version (optional field)
- if (mg_der_next(&tbs_cert, &field) <= 0) return -1;
- if (field.type == 0xa0) { // v3
- if (mg_der_parse(field.value, field.len, &field) <= 0 || field.len != 1 ||
- field.value[0] != 2)
- return -1;
- if (mg_der_next(&tbs_cert, &field) <= 0) return -1;
- }
- // Parse Serial Number
- if (field.type != 2) return -1;
- cert->sn = mg_str_n((char *) field.value, field.len);
- MG_VERBOSE(("cert s/n: %M", mg_print_hex, cert->sn.len, cert->sn.buf));
- // Parse signature algorithm (first occurrence)
- if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1;
- if (mg_der_next(&field, &algo) <= 0 || algo.type != 0x06) return -1;
- MG_VERBOSE(("sig algo (oid): %M", mg_print_hex, algo.len, algo.value));
- // Signature algorithm OID mapping
- if (algo.len == 8 &&
- memcmp(algo.value, "\x2A\x86\x48\xCE\x3D\x04\x03\x02", 8) == 0) {
- MG_VERBOSE(("sig algo: ECDSA with SHA256"));
- mg_sha256(cert->tbshash, tbs, tbssz);
- cert->tbshashsz = 32;
- } else if (algo.len == 9 &&
- memcmp(algo.value, "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0B", 9) ==
- 0) {
- MG_VERBOSE(("sig algo: RSA with SHA256"));
- mg_sha256(cert->tbshash, tbs, tbssz);
- cert->tbshashsz = 32;
- } else if (algo.len == 8 &&
- memcmp(algo.value, "\x2A\x86\x48\xCE\x3D\x04\x03\x03", 8) == 0) {
- MG_VERBOSE(("sig algo: ECDSA with SHA384"));
- mg_sha384(cert->tbshash, tbs, tbssz);
- cert->tbshashsz = 48;
- } else if (algo.len == 9 &&
- memcmp(algo.value, "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0C", 9) ==
- 0) {
- MG_VERBOSE(("sig algo: RSA with SHA384"));
- mg_sha384(cert->tbshash, tbs, tbssz);
- cert->tbshashsz = 48;
- } else {
- MG_ERROR(
- ("sig algo: unsupported OID: %M", mg_print_hex, algo.len, algo.value));
- return -1;
- }
- MG_VERBOSE(("tbs hash: %M", mg_print_hex, cert->tbshashsz, cert->tbshash));
- // issuer
- if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1;
- cert->issuer = field;
- mg_der_debug_cert_name("issuer", &field);
- // validity dates (before/after)
- if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1;
- {
- struct mg_der_tlv before, after;
- uint64_t now = mg_now() / 1000U, t;
- if (mg_der_next(&field, &before) <= 0 || ((before.type != 0x17 || before.len != 13) && (before.type != 0x18 || before.len != 15))) return -1;
- if (now < (t = asnt2t(before.value, before.type))) {
- MG_ERROR(("cert is not yet valid: before=%.*s (%lu), now=%lu", before.len, before.value, t, now));
- return -1;
- }
- if (mg_der_next(&field, &after) <= 0 || ((after.type != 0x17 || after.len != 13) && (after.type != 0x18 || after.len != 15))) return -1;
- if (memcmp(after.value, "99991231235959Z", 15) == 0) { // RFC-5280 4.1.2.5
- MG_ERROR(("No well-defined expiration date"));
- return -1;
- }
- if (now > (t = asnt2t(after.value, after.type))) {
- MG_ERROR(("cert is no longer valid: after=%.*s (%lu), now=%lu", after.len, after.value, t, now));
- return -1;
- }
- }
- // subject
- if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1;
- cert->subj = field;
- mg_der_debug_cert_name("subject", &field);
- // subject public key info
- if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1;
- if (mg_der_next(&field, &pki) <= 0 || pki.type != 0x30) return -1;
- if (mg_der_next(&pki, &pki_algo) <= 0 || pki_algo.type != 0x06) return -1;
- // public key algorithm
- MG_VERBOSE(("pk algo (oid): %M", mg_print_hex, pki_algo.len, pki_algo.value));
- if (pki_algo.len == 8 &&
- memcmp(pki_algo.value, "\x2A\x86\x48\xCE\x3D\x03\x01\x07", 8) == 0) {
- cert->is_ec_pubkey = true;
- MG_VERBOSE(("pk algo: ECDSA secp256r1"));
- } else if (pki_algo.len == 8 &&
- memcmp(pki_algo.value, "\x2A\x86\x48\xCE\x3D\x03\x01\x08", 8) ==
- 0) {
- cert->is_ec_pubkey = true;
- MG_VERBOSE(("pk algo: ECDSA secp384r1"));
- } else if (pki_algo.len == 7 &&
- memcmp(pki_algo.value, "\x2A\x86\x48\xCE\x3D\x02\x01", 7) == 0) {
- cert->is_ec_pubkey = true;
- MG_VERBOSE(("pk algo: EC public key"));
- } else if (pki_algo.len == 9 &&
- memcmp(pki_algo.value, "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01",
- 9) == 0) {
- cert->is_ec_pubkey = false;
- MG_VERBOSE(("pk algo: RSA"));
- } else {
- MG_ERROR(("unsupported pk algo: %M", mg_print_hex, pki_algo.len,
- pki_algo.value));
- return -1;
- }
- // Parse public key
- if (cert->is_ec_pubkey) {
- if (mg_der_next(&pki, &pki_curve) <= 0 || pki_curve.type != 0x06) return -1;
- }
- if (mg_der_next(&field, &pki_key) <= 0 || pki_key.type != 0x03) return -1;
- if (cert->is_ec_pubkey) { // Skip leading 0x00 and 0x04 (=uncompressed)
- if (pki_key.len < 2) return -1;
- cert->pubkey = mg_str_n((char *) pki_key.value + 2, pki_key.len - 2);
- } else { // Skip leading 0x00 byte
- if (pki_key.len < 1) return -1;
- cert->pubkey = mg_str_n((char *) pki_key.value + 1, pki_key.len - 1);
- }
- { // parse optional fields
- int r; // unique ids
- while ((r = mg_der_next(&tbs_cert, &field)) > 0 && field.type == 0xa1);
- if (r > 0 && field.type == 0xa3) { // extensions
- bool ca = false, certsign = true;
- struct mg_der_tlv ext, e, i; // ext[e(i, ...), ...]
- if (mg_der_next(&field, &ext) <= 0 || ext.type != 0x30) return -1;
- while (mg_der_next(&ext, &e) > 0) {
- if (mg_der_next(&e, &i) <= 0 || i.type != 0x06) return -1;
- if (i.len == 3 && memcmp(i.value, (uint8_t *) "\x55\x1d\x13", 3) == 0) {
- struct mg_der_tlv s, v; // basicConstraints
- MG_VERBOSE(("basicConstraints"));
- if (mg_der_next(&e, &i) <= 0 || i.type != 0x01 || i.len != 1 || *i.value != 0xff) return -1;
- if (mg_der_next(&e, &i) <= 0 || i.type != 0x04) return -1;
- if (mg_der_next(&i, &s) <= 0 || s.type != 0x30) return -1;
- if (s.len == 0) break;
- if (mg_der_next(&s, &v) <= 0) return -1;
- if (v.type == 0x01 && v.len == 1 && *v.value == 0xff) ca = true;
- } else if (i.len == 3 && memcmp(i.value, (uint8_t *) "\x55\x1d\x0f", 3) == 0) {
- struct mg_der_tlv b; // keyUsage, enforce when present
- MG_VERBOSE(("keyUsage"));
- if (mg_der_next(&e, &i) <= 0) return -1;
- if (i.type == 0x01) { // SHOULD, defaults to false
- if (i.len != 1) return -1;
- if (mg_der_next(&e, &i) <= 0) return -1;
- }
- if (i.type != 0x04) return -1;
- if (mg_der_next(&i, &b) <= 0 || b.type != 0x03) return -1;
- if (!(b.value[1] & MG_BIT(2))) certsign = false;
- }
- }
- if (ca && certsign) cert->is_ca = true;
- }
- }
- // Parse signature
- if (mg_der_next(&root, &field) <= 0 || field.type != 0x30) return -1;
- if (mg_der_next(&root, &raw_sig) <= 0 || raw_sig.type != 0x03) return -1;
- if (raw_sig.len < 1 || raw_sig.value[0] != 0x00) return -1;
- cert->sig = mg_str_n((char *) raw_sig.value + 1, raw_sig.len - 1);
- MG_VERBOSE(("sig: %M", mg_print_hex, cert->sig.len, cert->sig.buf));
- return 0;
- }
- static int countdots(struct mg_str s) {
- int count = 0;
- size_t len = s.len;
- char *p = s.buf;
- while (len--) {
- if (*(p++) == '.') ++count;
- }
- return count;
- }
- static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz,
- const char *server_name,
- struct mg_addr *server_ip) {
- struct mg_der_tlv root, field, name;
- if (mg_der_parse((uint8_t *) der, dersz, &root) < 0) {
- MG_ERROR(("failed to parse certificate"));
- return -1;
- }
- if (mg_der_find_oid(&root, (uint8_t *) "\x55\x1d\x11", 3, &field) <= 0) {
- MG_ERROR(("failed to extract SAN"));
- return -1;
- }
- if (mg_der_parse(field.value, field.len, &field) < 0) {
- MG_ERROR(("SAN is not a constructed object"));
- return -1;
- }
- while (mg_der_next(&field, &name) > 0) {
- if (name.type == 0x87 && name.len == 4) { // this is an IPv4 address
- MG_VERBOSE(("Found SAN, IP: %M", mg_print_ip4, name.value));
- if (!server_ip->is_ip6 &&
- *((uint32_t *) name.value) == server_ip->addr.ip4)
- return 1; // and matches the one we're connected to
- #if MG_ENABLE_IPV6
- } else if (name.type == 0x87 && name.len == 16) { // is an IPv6 address
- MG_VERBOSE(("Found SAN, IPv6: %M", mg_print_ip6, name.value));
- if (server_ip->is_ip6 && memcmp(name.value, server_ip->addr.ip6, 16) == 0)
- return 1; // and matches the one we're connected to
- #endif
- } else { // this is a text SAN
- struct mg_str sn, tn;
- MG_VERBOSE(("Found SAN, (%u): %.*s", name.type, name.len, name.value));
- sn = mg_str(server_name), tn = mg_str_n((char *) name.value, name.len);
- if (countdots(sn) == countdots(tn) && mg_match(sn, tn, NULL))
- return 1; // and matches the host name
- }
- }
- return -1;
- }
- static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
- const struct mg_tls_cert *issuer) {
- if (issuer->is_ec_pubkey) {
- uint8_t sig[128];
- struct mg_der_tlv seq = {0, 0, 0}, a = {0, 0, 0}, b = {0, 0, 0};
- mg_der_parse((uint8_t *) cert->sig.buf, cert->sig.len, &seq);
- mg_der_next(&seq, &a);
- mg_der_next(&seq, &b);
- if (a.len == 0 || b.len == 0) {
- MG_ERROR(("cert verification error"));
- return 0;
- }
- #if MG_UECC_SUPPORTS_secp256r1
- if (issuer->pubkey.len == 64) {
- const uint32_t N = 32;
- if (a.len > N) a.value += (a.len - N), a.len = N; // padding
- if (b.len > N) b.value += (b.len - N), b.len = N;
- memset(sig, 0, N - a.len); // short encoding
- memmove(sig + (N - a.len), a.value, a.len);
- memset(sig + N, 0, N - b.len);
- memmove(sig + N + (N - b.len), b.value, b.len);
- return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash,
- (unsigned) cert->tbshashsz, sig,
- mg_uecc_secp256r1());
- } else
- #endif
- #if MG_UECC_SUPPORTS_secp384r1
- if (issuer->pubkey.len == 96) {
- const uint32_t N = 48;
- if (a.len > N) a.value += (a.len - N), a.len = N;
- if (b.len > N) b.value += (b.len - N), b.len = N;
- memmove(sig, a.value, N);
- memmove(sig + N, b.value, N);
- return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash,
- (unsigned) cert->tbshashsz, sig,
- mg_uecc_secp384r1());
- } else
- #endif
- {
- MG_ERROR(("unsupported public key length: %d", issuer->pubkey.len));
- return 0;
- }
- } else {
- const uint8_t *n;
- size_t nlen;
- uint8_t sig2[512]; // 4096 bits
- struct mg_der_tlv seq, modulus, exponent;
- if (mg_der_parse((uint8_t *) issuer->pubkey.buf, issuer->pubkey.len,
- &seq) <= 0 ||
- mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 ||
- modulus.len == 0 || mg_der_next(&seq, &exponent) <= 0 ||
- exponent.type != 2 || exponent.len == 0) {
- return -1;
- }
- n = modulus.value, nlen = mg_rsa_trim_len(&n, modulus.len);
- if (nlen > sizeof(sig2) || cert->tbshashsz > nlen ||
- mg_rsa_mod_pow(modulus.value, modulus.len, exponent.value,
- exponent.len, (uint8_t *) cert->sig.buf, cert->sig.len,
- sig2, nlen) != 0) {
- return 0;
- }
- return mg_rsa_pkcs_verify(sig2, nlen, cert->tbshash, cert->tbshashsz);
- }
- }
- static int mg_tls_verify_cert_cn(struct mg_der_tlv *subj, const char *host) {
- struct mg_der_tlv v;
- int matched = 0;
- if (mg_der_find_oid(subj, (uint8_t *) "\x55\x04\x03", 3, &v) > 0) {
- struct mg_str hn, cn;
- MG_VERBOSE(("using CN: %.*s <-> %s", v.len, v.value, host));
- hn = mg_str(host), cn = mg_str_n((char *) v.value, v.len);
- matched = (int) (countdots(hn) == countdots(cn) && mg_match(hn, cn, NULL));
- }
- return matched;
- }
- static int tls_bundle_find(struct tls_data *tls, struct mg_der_tlv *name,
- struct mg_tls_cert *cert) {
- size_t i;
- struct mg_der_tlv v;
- struct mg_str *p = tls->ca_bundle_der, tgt;
- if (!mg_der_find_oid(name, (uint8_t *) "\x55\x04\x03", 3, &v)) return false;
- tgt = mg_str_n((const char *) v.value, v.len);
- for (i = 0; i < tls->ca_bundle_len; i++, p++) {
- struct mg_str subj;
- if (mg_tls_parse_cert_der(p->buf, p->len, cert) < 0 ||
- !mg_der_find_oid(&cert->subj, (uint8_t *) "\x55\x04\x03", 3, &v)) {
- MG_ERROR(("failed to parse certificate #%u in bundle", i + 1));
- continue; // skip this certificate but don't halt the process
- }
- subj = mg_str_n((const char *) v.value, v.len);
- MG_VERBOSE(("#%u: %.*s (%.*s)", i + 1, subj.len, subj.buf, tgt.len, tgt.buf));
- if (mg_strcasecmp(subj, tgt) == 0) return 1;
- }
- return 0;
- }
- static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- unsigned char *recv_buf;
- if (mg_tls_recv_record(c) < 0) {
- return -1;
- }
- recv_buf = &c->rtls.buf[tls->recv_offset];
- if (recv_buf[0] == MG_TLS_CERTIFICATE_REQUEST) {
- MG_VERBOSE(("got certificate request"));
- mg_tls_drop_message(c);
- tls->cert_requested = 1;
- return -1;
- }
- if (recv_buf[0] != MG_TLS_CERTIFICATE) {
- mg_error(c, "expected %s certificate but got msg 0x%02x",
- is_client ? "server" : "client", recv_buf[0]);
- return -1;
- }
- if (tls->recv_len < 13) { // 8 + 3 + 2, chain len + cert len + ext len
- mg_error(c, "certificate list too short");
- return -1;
- }
- {
- // Normally, there are 2-3 certs in a chain (when is_client)
- struct mg_tls_cert certs[8];
- int certnum = 0;
- uint32_t full_cert_chain_len = MG_LOAD_BE24(recv_buf + 1);
- uint32_t cert_chain_len = MG_LOAD_BE24(recv_buf + 5);
- uint8_t *p = recv_buf + 8;
- uint8_t *endp = recv_buf + cert_chain_len + 8;
- bool found_ca = false;
- struct mg_tls_cert ca;
- if (cert_chain_len != full_cert_chain_len - 4 || cert_chain_len > (tls->recv_len - 8)) {
- MG_ERROR(("full chain length: %d, chain length: %d, msg length: %d", full_cert_chain_len, cert_chain_len, tls->recv_len));
- mg_error(c, "invalid certificate chain length");
- return -1;
- }
- memset(certs, 0, sizeof(certs));
- memset(&ca, 0, sizeof(ca));
- if (tls->ca_der.len > 0) { // single cert or server
- if (mg_tls_parse_cert_der(tls->ca_der.buf, tls->ca_der.len, &ca) < 0) {
- mg_error(c, "failed to parse CA certificate");
- return -1;
- }
- MG_VERBOSE(("CA serial: %M", mg_print_hex, ca.sn.len, ca.sn.buf));
- }
- while (p < endp) {
- struct mg_tls_cert *ci = &certs[certnum++];
- uint32_t certsz;
- uint16_t certext;
- uint8_t *cert = p + 3;
- if ((endp - p) < 5) { // 3 + 2, cert len + ext len fields
- mg_error(c, "truncated certificate in chain");
- return -1;
- }
- certsz = MG_LOAD_BE24(p);
- p = cert + certsz + 2; // skip cert extensions (size only, not supported)
- if (p > endp) {
- mg_error(c, "invalid certificate length");
- return -1;
- }
- certext = MG_LOAD_BE16(cert + certsz);
- if (certext != 0) {
- mg_error(c, "certificate extensions are not supported");
- return -1;
- }
- if (mg_tls_parse_cert_der(cert, certsz, ci) < 0) {
- mg_error(c, "failed to parse certificate");
- return -1;
- }
- if (ci == certs) {
- // First certificate in the chain is peer cert, check SAN if requested,
- // and store public key for further CertVerify step
- if (tls->hostname[0] != '\0' &&
- mg_tls_verify_cert_san(cert, certsz, tls->hostname, &c->rem) <= 0 &&
- mg_tls_verify_cert_cn(&ci->subj, tls->hostname) <= 0) {
- mg_error(c, "failed to verify hostname");
- return -1;
- }
- if (ci->pubkey.len > sizeof(tls->pubkey)) {
- mg_error(c, "peer public key too large");
- return -1;
- }
- memmove(tls->pubkey, ci->pubkey.buf, ci->pubkey.len);
- tls->pubkeysz = ci->pubkey.len;
- } else {
- if (!ci->is_ca || !mg_tls_verify_cert_signature(ci - 1, ci)) {
- mg_error(c, "failed to verify certificate chain %s", ci->is_ca ? "" : "(not a true CA)");
- return -1;
- }
- }
- if (tls->ca_bundle_len > 0) { // bundle, find subject and compare keys
- int r;
- MG_VERBOSE(("Search current cert in bundle"));
- r = tls_bundle_find(tls, &ci->subj, &ca); // find current cert ci
- if (r < 0) {
- mg_error(c, "failed to parse CA bundle");
- return -1;
- } else if (r > 0 && ca.pubkey.len == ci->pubkey.len &&
- memcmp(ca.pubkey.buf, ci->pubkey.buf, ca.pubkey.len) == 0) {
- found_ca = true;
- MG_VERBOSE(("CA serial: %M", mg_print_hex, ca.sn.len, ca.sn.buf));
- break;
- } // else either r = 0 => subj not found or pubkey not matching
- }
- if (certnum == sizeof(certs) / sizeof(certs[0]) - 1) {
- mg_error(c, "too many certificates in the chain");
- return -1;
- }
- }
- if (!found_ca && certnum > 0 && tls->ca_bundle_len > 0) { // bundle
- int r;
- MG_VERBOSE(("Search bundle for issuer of last cert in chain"));
- r = tls_bundle_find(tls, &certs[certnum - 1].issuer, &ca);
- if (r <= 0) {
- mg_error(c, r < 0 ? "failed to parse CA bundle"
- : "failed to find issuing CA in bundle");
- return -1;
- } else if (!ca.is_ca || // candidate issuer found
- !mg_tls_verify_cert_signature(&certs[certnum - 1], &ca)) {
- mg_error(c, "failed to verify CA");
- return -1;
- } // issuer verified. NOTE(): may fail if two diff CAs share a CN
- MG_VERBOSE(("CA serial: %M", mg_print_hex, ca.sn.len, ca.sn.buf));
- }
- if (!found_ca && tls->ca_der.len > 0) { // single cert or server
- if (certnum < 1 ||
- !mg_tls_verify_cert_signature(&certs[certnum - 1], &ca)) {
- mg_error(c, "failed to verify CA");
- return -1;
- } else if (is_client) {
- MG_VERBOSE(("no CA in chain; verification with builtin CA passed"));
- }
- }
- }
- mg_tls_drop_message(c);
- mg_tls_calc_cert_verify_hash(c, tls->sighash, !is_client);
- return 0;
- }
- static int mg_tls_recv_cert_verify(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- unsigned char *recv_buf;
- if (mg_tls_recv_record(c) < 0) {
- return -1;
- }
- recv_buf = &c->rtls.buf[tls->recv_offset];
- if (recv_buf[0] != MG_TLS_CERTIFICATE_VERIFY) {
- mg_error(c, "expected %s certificate verify but got msg 0x%02x",
- c->is_client ? "server" : "client", recv_buf[0]);
- return -1;
- }
- if (tls->recv_len < 8) {
- mg_error(c, "server certificate verify is too short: %d bytes",
- tls->recv_len);
- return -1;
- }
- // Ignore CertificateVerify if strict checks are not required
- if (tls->skip_verification) {
- mg_tls_drop_message(c);
- return 0;
- }
- {
- uint16_t sigalg = MG_LOAD_BE16(recv_buf + 4);
- uint16_t siglen = MG_LOAD_BE16(recv_buf + 6);
- uint8_t *sigbuf = recv_buf + 8;
- if (siglen > tls->recv_len - 8) {
- mg_error(c, "invalid certverify signature length: %d, expected %d",
- siglen, tls->recv_len - 8);
- return -1;
- }
- MG_VERBOSE(
- ("certificate verification, algo=%04x, siglen=%d", sigalg, siglen));
- if (sigalg == 0x0804) { // rsa_pss_rsae_sha256
- uint8_t sig2[512]; // 2048 or 4096 bits
- const uint8_t *n;
- size_t nlen;
- struct mg_der_tlv seq, modulus, exponent;
- if (mg_der_parse(tls->pubkey, tls->pubkeysz, &seq) <= 0 ||
- mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 ||
- modulus.len == 0 || mg_der_next(&seq, &exponent) <= 0 ||
- exponent.type != 2 || exponent.len == 0) {
- mg_error(c, "invalid public key");
- return -1;
- }
- n = modulus.value, nlen = mg_rsa_trim_len(&n, modulus.len);
- if (nlen > sizeof(sig2) ||
- mg_rsa_mod_pow(modulus.value, modulus.len, exponent.value,
- exponent.len, sigbuf, siglen, sig2, nlen) != 0 ||
- !mg_rsa_verify(sig2, nlen, tls->sighash)) {
- mg_error(c, "failed to verify RSA certificate (certverify)");
- return -1;
- }
- MG_VERBOSE(("certificate verification successful (RSA)"));
- } else if (sigalg == 0x0403) { // ecdsa_secp256r1_sha256
- // Extract certificate signature and verify it using pubkey and sighash
- uint8_t sig[64];
- struct mg_der_tlv seq, r, s;
- memset(sig, 0, 64);
- if (mg_der_to_tlv(sigbuf, siglen, &seq) < 0) {
- mg_error(c, "verification message is not an ASN.1 DER sequence");
- return -1;
- }
- if (mg_der_to_tlv(seq.value, seq.len, &r) < 0) {
- mg_error(c, "missing first part of the signature");
- return -1;
- }
- if (mg_der_to_tlv(r.value + r.len, seq.len - r.len, &s) < 0) {
- mg_error(c, "missing second part of the signature");
- return -1;
- }
- // Integers may be padded with zeroes
- if (r.len > 32) r.value = r.value + (r.len - 32), r.len = 32;
- if (s.len > 32) s.value = s.value + (s.len - 32), s.len = 32;
- // r or s may be shorter than 32 bytes, "right-justify" (network order)
- memmove(sig + (32 - r.len), r.value, r.len);
- memmove(sig + 32 + (32 - s.len), s.value, s.len);
- if (mg_uecc_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), sig,
- mg_uecc_secp256r1()) != 1) {
- mg_error(c, "failed to verify EC certificate (certverify)");
- return -1;
- }
- MG_VERBOSE(("certificate verification successful (EC)"));
- } else {
- // From
- // https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml:
- // 0805 = rsa_pss_rsae_sha384
- // 0806 = rsa_pss_rsae_sha512
- // 0807 = ed25519
- // 0808 = ed448
- // 0809 = rsa_pss_pss_sha256
- // 080A = rsa_pss_pss_sha384
- // 080B = rsa_pss_pss_sha512
- MG_ERROR(("unsupported certverify signature scheme: %x of %d bytes",
- sigalg, siglen));
- return -1;
- }
- }
- mg_tls_drop_message(c);
- return 0;
- }
- static int mg_tls_client_recv_finish(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- unsigned char *recv_buf;
- if (mg_tls_recv_record(c) < 0) {
- return -1;
- }
- recv_buf = &c->rtls.buf[tls->recv_offset];
- if (recv_buf[0] != MG_TLS_FINISHED) {
- mg_error(c, "expected server finished but got msg 0x%02x", recv_buf[0]);
- return -1;
- }
- mg_tls_drop_message(c);
- return 0;
- }
- static bool mg_tls_client_send_finish(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- mg_sha256_ctx sha256;
- uint8_t hash[32];
- uint8_t finish[36] = {0x14, 0, 0, 32};
- memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx));
- mg_sha256_final(hash, &sha256);
- mg_hmac_sha256(finish + 4, tls->enc.client_finished_key, 32, hash, 32);
- return mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE);
- }
- static bool mg_tls_client_handshake(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- switch (tls->state) {
- case MG_TLS_STATE_CLIENT_START:
- if (!mg_tls_client_send_hello(c)) return false;
- tls->state = MG_TLS_STATE_CLIENT_WAIT_SH;
- // Fallthrough
- case MG_TLS_STATE_CLIENT_WAIT_SH:
- if (mg_tls_client_recv_hello(c) < 0) break;
- tls->state = MG_TLS_STATE_CLIENT_WAIT_EE;
- // Fallthrough
- case MG_TLS_STATE_CLIENT_WAIT_EE:
- if (mg_tls_client_recv_ext(c) < 0) break;
- tls->state = MG_TLS_STATE_CLIENT_WAIT_CERT;
- // Fallthrough
- case MG_TLS_STATE_CLIENT_WAIT_CERT:
- if (mg_tls_recv_cert(c, true) < 0) break;
- tls->state = MG_TLS_STATE_CLIENT_WAIT_CV;
- // Fallthrough
- case MG_TLS_STATE_CLIENT_WAIT_CV:
- if (mg_tls_recv_cert_verify(c) < 0) break;
- tls->state = MG_TLS_STATE_CLIENT_WAIT_FINISH;
- // Fallthrough
- case MG_TLS_STATE_CLIENT_WAIT_FINISH:
- if (mg_tls_client_recv_finish(c) < 0) break;
- if (tls->cert_requested && tls->cert_der.len > 0) { // two-way auth
- // generate application keys at this point, keep using handshake keys
- struct tls_enc hs_keys = tls->enc;
- mg_tls_generate_application_keys(c);
- tls->app_keys = tls->enc;
- tls->enc = hs_keys;
- if (!mg_tls_send_cert(c, true) || !mg_tls_send_cert_verify(c, true) ||
- !mg_tls_client_send_finish(c))
- return false;
- tls->enc = tls->app_keys;
- } else {
- if (!mg_tls_client_send_finish(c)) return false;
- mg_tls_generate_application_keys(c);
- }
- tls->state = MG_TLS_STATE_CLIENT_CONNECTED;
- c->is_tls_hs = 0;
- mg_call(c, MG_EV_TLS_HS, NULL);
- break;
- default:
- mg_error(c, "unexpected client state: %d", tls->state);
- break;
- }
- return true;
- }
- static bool mg_tls_server_handshake(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- switch (tls->state) {
- case MG_TLS_STATE_SERVER_START:
- if (mg_tls_server_recv_hello(c) < 0) break;
- if (!mg_tls_server_send_hello(c)) return false;
- mg_tls_generate_handshake_keys(c);
- if (!mg_tls_server_send_ext(c)) return false;
- if (tls->is_twoway && !mg_tls_server_send_cert_request(c)) return false;
- if (!mg_tls_send_cert(c, false) || !mg_tls_send_cert_verify(c, false) ||
- !mg_tls_server_send_finish(c))
- return false;
- if (tls->is_twoway) {
- // generate application keys at this point, keep using handshake keys
- struct tls_enc hs_keys = tls->enc;
- mg_tls_generate_application_keys(c);
- tls->app_keys = tls->enc;
- tls->enc = hs_keys;
- tls->state = MG_TLS_STATE_SERVER_WAIT_CERT;
- break;
- }
- tls->state = MG_TLS_STATE_SERVER_NEGOTIATED;
- // fallthrough
- case MG_TLS_STATE_SERVER_NEGOTIATED:
- if (mg_tls_server_recv_finish(c) < 0) break;
- if (tls->is_twoway) { // use previously generated keys
- tls->enc = tls->app_keys;
- } else { // generate keys now
- mg_tls_generate_application_keys(c);
- }
- tls->state = MG_TLS_STATE_SERVER_CONNECTED;
- c->is_tls_hs = 0;
- break;
- case MG_TLS_STATE_SERVER_WAIT_CERT:
- if (mg_tls_recv_cert(c, false) < 0) break;
- tls->state = MG_TLS_STATE_SERVER_WAIT_CV;
- // Fallthrough
- case MG_TLS_STATE_SERVER_WAIT_CV:
- if (mg_tls_recv_cert_verify(c) < 0) break;
- tls->state = MG_TLS_STATE_SERVER_NEGOTIATED;
- break;
- default:
- mg_error(c, "unexpected server state: %d", tls->state);
- break;
- }
- return true;
- }
- void mg_tls_handshake(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- long n;
- bool res;
- if (c->is_closing) return; // we don't clear rx buf, so ignore what's left
- if (tls->is_sntp_pending) return;
- if (c->is_client) {
- // will clear is_hs when sending last chunk
- res = mg_tls_client_handshake(c);
- } else {
- res = mg_tls_server_handshake(c);
- }
- if (!res) {
- mg_error(c, "TLS OOM");
- return;
- }
- while (tls->send.len > 0 &&
- (n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
- mg_iobuf_del(&tls->send, 0, (size_t) n);
- } // if last chunk fails to be sent, it will be sent with first app data,
- // otherwise, it needs to be flushed
- }
- static int mg_rsa_parse_der_int(const uint8_t **p, const uint8_t *end,
- struct mg_str *out) {
- const uint8_t *start = *p, *value_start, *value_end;
- uint8_t i;
- uint32_t len;
- if (end - start < 2) {
- MG_VERBOSE(("DER INT: not enough bytes (%d < 2)", (int) (end - start)));
- return -1;
- }
- if (start[0] != 0x02) {
- MG_VERBOSE(("DER INT: expected 0x02, got 0x%02x", start[0]));
- return -1;
- }
- len = start[1];
- *p = start + 2;
- if (len > 0x7F) {
- // Long form length
- uint8_t len_bytes = len & 0x7F;
- MG_VERBOSE(("DER INT: long form, %d length bytes", len_bytes));
- if (len_bytes == 0 || len_bytes > 4 || (size_t) (end - *p) < len_bytes) {
- MG_VERBOSE(("DER INT: not enough bytes for length"));
- return -1;
- }
- len = 0;
- for (i = 0; i < len_bytes; i++) {
- len = (len << 8) | (*p)[i];
- }
- *p += len_bytes;
- }
- MG_VERBOSE(("DER INT: length=%u, remaining=%d", len, (int) (end - *p)));
- if ((size_t) (end - *p) < len) {
- MG_VERBOSE(("DER INT: length exceeds remaining bytes"));
- return -1;
- }
- // The encoded length tells us how many bytes to consume from the stream
- value_start = *p;
- value_end = *p + len;
- // Skip leading zero byte if present (for positive numbers)
- // This doesn't change how many bytes we consume, just what we expose
- if (len > 0 && (*p)[0] == 0x00) {
- (*p)++;
- len--;
- }
- out->buf = (char *) *p;
- out->len = len;
- // Advance pointer by the ORIGINAL encoded length, not the adjusted length
- *p = value_end;
- MG_VERBOSE(("DER INT: parsed %u bytes (skipped zero=%d)", len,
- (size_t) (value_end - value_start) != (size_t) len ? 1 : 0));
- return 0;
- }
- // RFC 5915 ECPrivateKey ::= SEQUENCE {
- // version INTEGER { ecPrivkeyVer1(1) },
- // privateKey OCTET STRING,
- // parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
- // publicKey [1] BIT STRING OPTIONAL
- // }
- static int mg_parse_ec_private_key(const uint8_t *der, size_t dersz,
- uint8_t *ec_key) {
- struct mg_der_tlv root, version, private_key_octets;
- if (mg_der_parse((uint8_t *) der, dersz, &root) < 0 || root.type != 0x30) {
- MG_ERROR(("EC private key: invalid SEQUENCE"));
- return -1;
- }
- if (mg_der_next(&root, &version) <= 0 || version.type != 0x02) {
- MG_ERROR(("EC private key: invalid version"));
- return -1;
- }
- if (mg_der_next(&root, &private_key_octets) <= 0 ||
- private_key_octets.type != 0x04) {
- MG_ERROR(("EC private key: invalid privateKey OCTET STRING"));
- return -1;
- }
- if (private_key_octets.len != 32) {
- MG_ERROR(
- ("EC private key: expected 32 bytes, got %u", private_key_octets.len));
- return -1;
- }
- memcpy(ec_key, private_key_octets.value, 32);
- return 0;
- }
- // Parse RSA private key from DER format
- // RSAPrivateKey ::= SEQUENCE {
- // version INTEGER (0),
- // modulus INTEGER, -- n
- // publicExponent INTEGER, -- e
- // privateExponent INTEGER, -- d
- // prime1 INTEGER, -- p
- // prime2 INTEGER, -- q
- // exponent1 INTEGER, -- dP = d mod (p-1)
- // exponent2 INTEGER, -- dQ = d mod (q-1)
- // coefficient INTEGER, -- qInv = (inverse of q) mod p
- // }
- static int mg_rsa_parse_key(const uint8_t *der, size_t dersz,
- struct mg_rsa_key *key) {
- const uint8_t *p = der;
- const uint8_t *end = der + dersz;
- uint32_t seq_len;
- struct mg_str version;
- memset(key, 0, sizeof(*key));
- // Parse outer SEQUENCE
- if (end - p < 2) {
- MG_ERROR(("RSA key too short for SEQUENCE header"));
- return -1;
- }
- if (p[0] != 0x30) {
- MG_ERROR(("RSA key: expected SEQUENCE (0x30), got 0x%02x", p[0]));
- return -1;
- }
- seq_len = p[1];
- p += 2;
- if (seq_len > 0x7F) {
- // Long form length
- uint8_t i, len_bytes = seq_len & 0x7F;
- MG_VERBOSE(("Long form length: %d bytes", len_bytes));
- if (len_bytes == 0 || len_bytes > 4 || (size_t) (end - p) < len_bytes) {
- MG_ERROR(("Not enough bytes for long form length"));
- return -1;
- }
- seq_len = 0;
- for (i = 0; i < len_bytes; i++) {
- seq_len = (seq_len << 8) | p[i];
- }
- p += len_bytes;
- }
- MG_VERBOSE(
- ("SEQUENCE length: %u, total DER size: %u", seq_len, (unsigned) dersz));
- if ((size_t) (end - p) < seq_len) {
- MG_ERROR(("SEQUENCE length exceeds buffer"));
- return -1;
- }
- end = p + seq_len; // Adjust end to sequence boundary
- // Parse version (should be 0)
- MG_VERBOSE(("Before version: offset=%d, bytes: %02x %02x %02x %02x",
- (int) (p - der), p < end ? p[0] : 0xFF,
- p + 1 < end ? p[1] : 0xFF, p + 2 < end ? p[2] : 0xFF,
- p + 3 < end ? p[3] : 0xFF));
- if (mg_rsa_parse_der_int(&p, end, &version) < 0) {
- MG_ERROR(("Failed to parse version"));
- return -1;
- }
- MG_DEBUG(("Version: %d byte(s), value=%d, offset now=%d", (int) version.len,
- version.len > 0 ? (int) (unsigned char) version.buf[0] : -1,
- (int) (p - der)));
- // Parse the 8 components: n, e, d, p, q, dP, dQ, qInv
- MG_VERBOSE(("Before n: offset=%d, bytes: %02x %02x %02x %02x %02x %02x",
- (int) (p - der), p < end ? p[0] : 0xFF,
- p + 1 < end ? p[1] : 0xFF, p + 2 < end ? p[2] : 0xFF,
- p + 3 < end ? p[3] : 0xFF, p + 4 < end ? p[4] : 0xFF,
- p + 5 < end ? p[5] : 0xFF));
- if (mg_rsa_parse_der_int(&p, end, &key->n) < 0) {
- MG_ERROR(("Failed to parse n (modulus)"));
- return -1;
- }
- MG_VERBOSE(("Parsed n: %d bytes, offset now=%d, consumed=%d bytes total",
- (int) key->n.len, (int) (p - der), (int) (p - der)));
- MG_VERBOSE((" First 8 bytes of n: %02x %02x %02x %02x %02x %02x %02x %02x",
- key->n.len > 0 ? (unsigned char) key->n.buf[0] : 0xFF,
- key->n.len > 1 ? (unsigned char) key->n.buf[1] : 0xFF,
- key->n.len > 2 ? (unsigned char) key->n.buf[2] : 0xFF,
- key->n.len > 3 ? (unsigned char) key->n.buf[3] : 0xFF,
- key->n.len > 4 ? (unsigned char) key->n.buf[4] : 0xFF,
- key->n.len > 5 ? (unsigned char) key->n.buf[5] : 0xFF,
- key->n.len > 6 ? (unsigned char) key->n.buf[6] : 0xFF,
- key->n.len > 7 ? (unsigned char) key->n.buf[7] : 0xFF));
- MG_VERBOSE((" Next bytes after n: %02x %02x %02x %02x %02x %02x",
- p < end ? p[0] : 0xFF, p + 1 < end ? p[1] : 0xFF,
- p + 2 < end ? p[2] : 0xFF, p + 3 < end ? p[3] : 0xFF,
- p + 4 < end ? p[4] : 0xFF, p + 5 < end ? p[5] : 0xFF));
- if (mg_rsa_parse_der_int(&p, end, &key->e) < 0) {
- MG_ERROR(("Failed to parse e (public exponent), bytes remaining: %d",
- (int) (end - p)));
- if (end - p >= 4) {
- MG_ERROR((" Next 4 bytes: %02x %02x %02x %02x", p[0], p[1], p[2], p[3]));
- }
- return -1;
- }
- if (mg_rsa_parse_der_int(&p, end, &key->d) < 0) {
- MG_ERROR(("Failed to parse d (private exponent)"));
- return -1;
- }
- if (mg_rsa_parse_der_int(&p, end, &key->p) < 0) {
- MG_ERROR(("Failed to parse p (prime1)"));
- return -1;
- }
- if (mg_rsa_parse_der_int(&p, end, &key->q) < 0) {
- MG_ERROR(("Failed to parse q (prime2)"));
- return -1;
- }
- if (mg_rsa_parse_der_int(&p, end, &key->dP) < 0) {
- MG_ERROR(("Failed to parse dP (exponent1)"));
- return -1;
- }
- if (mg_rsa_parse_der_int(&p, end, &key->dQ) < 0) {
- MG_ERROR(("Failed to parse dQ (exponent2)"));
- return -1;
- }
- if (mg_rsa_parse_der_int(&p, end, &key->qInv) < 0) {
- MG_ERROR(("Failed to parse qInv (coefficient)"));
- return -1;
- }
- MG_VERBOSE(("Successfully parsed RSA key"));
- return 0;
- }
- // PKCS#8 PrivateKeyInfo ::= SEQUENCE {
- // version INTEGER,
- // privateKeyAlgorithm AlgorithmIdentifier,
- // privateKey OCTET STRING,
- // attributes [0] Attributes OPTIONAL
- // }
- // AlgorithmIdentifier ::= SEQUENCE {
- // algorithm OBJECT IDENTIFIER,
- // parameters ANY OPTIONAL
- // }
- static int mg_parse_pkcs8_key(const uint8_t *der, size_t dersz,
- struct mg_rsa_key *rsa, uint8_t *ec_key) {
- struct mg_der_tlv root, version, alg_id, private_key_octets;
- struct mg_der_tlv alg_oid, alg_params;
- if (mg_der_parse((uint8_t *) der, dersz, &root) < 0 || root.type != 0x30) {
- MG_ERROR(("PKCS#8: invalid PrivateKeyInfo SEQUENCE"));
- return -1;
- }
- if (mg_der_next(&root, &version) <= 0 || version.type != 0x02) {
- MG_ERROR(("PKCS#8: invalid version"));
- return -1;
- }
- if (mg_der_next(&root, &alg_id) <= 0 || alg_id.type != 0x30) {
- MG_ERROR(("PKCS#8: invalid AlgorithmIdentifier SEQUENCE"));
- return -1;
- }
- if (mg_der_next(&alg_id, &alg_oid) <= 0 || alg_oid.type != 0x06) {
- MG_ERROR(("PKCS#8: invalid algorithm OID"));
- return -1;
- }
- if (mg_der_next(&root, &private_key_octets) <= 0 ||
- private_key_octets.type != 0x04) {
- MG_ERROR(("PKCS#8: invalid privateKey OCTET STRING"));
- return -1;
- }
- if (alg_oid.len == sizeof(mg_rsa_oid) &&
- memcmp(alg_oid.value, mg_rsa_oid, sizeof(mg_rsa_oid)) == 0) {
- struct mg_rsa_key rsa_key;
- if (rsa == NULL) return -1;
- if (mg_rsa_parse_key(private_key_octets.value, private_key_octets.len,
- &rsa_key) < 0) {
- MG_ERROR(("PKCS#8: failed to parse inner RSA key"));
- return -1;
- }
- *rsa = rsa_key;
- return 0;
- } else if (alg_oid.len == sizeof(mg_ec_public_key_oid) &&
- memcmp(alg_oid.value, mg_ec_public_key_oid,
- sizeof(mg_ec_public_key_oid)) == 0) {
- if (ec_key == NULL) return -1;
- if (mg_der_next(&alg_id, &alg_params) <= 0 || alg_params.type != 0x06) {
- MG_ERROR(("PKCS#8: invalid EC parameters OID"));
- return -1;
- }
- if (alg_params.len != sizeof(mg_secp256r1_oid) ||
- memcmp(alg_params.value, mg_secp256r1_oid, sizeof(mg_secp256r1_oid)) !=
- 0) {
- MG_ERROR(("PKCS#8: unsupported EC curve (only secp256r1 supported)"));
- return -1;
- }
- return mg_parse_ec_private_key(private_key_octets.value,
- private_key_octets.len, ec_key);
- } else {
- MG_ERROR(("PKCS#8: unsupported algorithm"));
- return -1;
- }
- }
- static int mg_parse_pem(const struct mg_str pem, const struct mg_str label,
- struct mg_str *der) {
- size_t n = 0, m = 0;
- char *s;
- const char *c;
- struct mg_str caps[6]; // number of wildcards + 1
- if (!mg_match(pem, mg_str("#-----BEGIN #-----#-----END #-----#"), caps)) {
- *der = mg_strdup(pem);
- return 0;
- }
- if (mg_strcmp(caps[1], label) != 0 || mg_strcmp(caps[3], label) != 0) {
- return -1; // bad label
- }
- if ((s = (char *) mg_calloc(1, caps[2].len)) == NULL) {
- return -1;
- }
- for (c = caps[2].buf; c < caps[2].buf + caps[2].len; c++) {
- if (*c == ' ' || *c == '\n' || *c == '\r' || *c == '\t') {
- continue;
- }
- s[n++] = *c;
- }
- m = mg_base64_decode(s, n, s, n);
- if (m == 0) {
- mg_free(s);
- return -1;
- }
- der->buf = s;
- der->len = m;
- return 0;
- }
- static int mg_parse_pem_certs(const struct mg_str pem, struct mg_str **ders) {
- int count = 0;
- struct mg_str *certs = NULL;
- const char *p = pem.buf;
- const char *end = pem.buf + pem.len;
- const char *begin_marker = "-----BEGIN CERTIFICATE-----";
- const char *end_marker = "-----END CERTIFICATE-----";
- size_t begin_len = strlen(begin_marker);
- size_t end_len = strlen(end_marker);
- while (p < end) {
- const char *s, *begin = NULL, *finish = NULL;
- struct mg_str cert_pem, cert_der, *new_certs;
- int i;
- for (s = p; s <= end - (int) begin_len; s++) {
- if (memcmp(s, begin_marker, begin_len) == 0) {
- begin = s;
- break;
- }
- }
- if (begin == NULL) break;
- for (s = begin + begin_len; s <= end - (int) end_len; s++) {
- if (memcmp(s, end_marker, end_len) == 0) {
- finish = s + end_len;
- break;
- }
- }
- if (finish == NULL) {
- for (i = 0; i < count; i++) mg_free((void *) certs[i].buf);
- mg_free(certs);
- return -1;
- }
- cert_pem = mg_str_n(begin, (size_t) (finish - begin));
- if (mg_parse_pem(cert_pem, mg_str_s("CERTIFICATE"), &cert_der) < 0) {
- for (i = 0; i < count; i++) mg_free((void *) certs[i].buf);
- mg_free(certs);
- return -1;
- }
- new_certs =
- (struct mg_str *) mg_calloc((size_t) count + 1, sizeof(*new_certs));
- if (new_certs == NULL) {
- mg_free((void *) cert_der.buf);
- for (i = 0; i < count; i++) mg_free((void *) certs[i].buf);
- mg_free(certs);
- return -1;
- }
- if (count > 0) {
- memmove(new_certs, certs, (size_t) count * sizeof(struct mg_str));
- mg_free(certs);
- }
- certs = new_certs;
- certs[count++] = cert_der;
- p = finish;
- }
- *ders = certs;
- return count;
- }
- size_t mg_uecc_parse_private_key(struct mg_str key, uint8_t *buf, size_t len) {
- struct mg_str der = mg_str_n(NULL, 0);
- size_t n = 0;
- if (buf == NULL || len < 32) return 0;
- // current mg_parse_ec_private_key() only handles 32-byte keys
- if (mg_parse_pem(key, mg_str_s("EC PRIVATE KEY"), &der) == 0) {
- if (mg_parse_ec_private_key((uint8_t *) der.buf, der.len, buf) == 0) n = 32;
- } else if (mg_parse_pem(key, mg_str_s("PRIVATE KEY"), &der) == 0) {
- if (mg_parse_pkcs8_key((uint8_t *) der.buf, der.len, NULL, buf) == 0) n = 32;
- }
- mg_free((void *) der.buf);
- return n;
- }
- static void sync_time_cancel(struct mg_connection *c) {
- c->fn = NULL;
- c->is_closing = 1;
- }
- static void sync_time_cb(struct mg_connection *c, int ev, void *ev_data) {
- if (ev == MG_EV_SNTP_TIME || (ev == MG_EV_CLOSE)) {
- struct mg_connection *tlsc = (struct mg_connection *) c->fn_data;
- struct tls_data *tls = (struct tls_data *) tlsc->tls;
- sync_time_cancel(c); // c = tls->timec
- tls->timec = NULL;
- tls->is_sntp_pending = false;
- if (ev == MG_EV_SNTP_TIME) mg_tls_handshake(tlsc);
- if (ev == MG_EV_CLOSE) mg_error(tlsc, "time sync failed");
- }
- (void) ev_data;
- }
- void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
- struct mg_str key;
- struct tls_data *tls =
- (struct tls_data *) mg_calloc(1, sizeof(struct tls_data));
- if (tls == NULL) {
- mg_error(c, "tls oom");
- return;
- }
- tls->state =
- c->is_client ? MG_TLS_STATE_CLIENT_START : MG_TLS_STATE_SERVER_START;
- tls->skip_verification = opts->skip_verification;
- // tls->send.align = MG_IO_SIZE;
- c->tls = tls;
- c->is_tls = c->is_tls_hs = 1;
- mg_sha256_init(&tls->sha256);
- // save hostname (client extension)
- if (opts->name.len > 0) {
- if (opts->name.len >= sizeof(tls->hostname) - 1) {
- mg_error(c, "hostname too long");
- return;
- }
- strncpy((char *) tls->hostname, opts->name.buf, sizeof(tls->hostname) - 1);
- tls->hostname[opts->name.len] = 0;
- }
- // server CA certificate; parse PEM [bundle] or DER
- if (opts->ca.len > 0) {
- struct mg_str *all_certs = NULL;
- int cert_count = mg_parse_pem_certs(opts->ca, &all_certs);
- if (cert_count > 1 && c->is_client) { // use bundle for clients only
- tls->ca_bundle_len = (size_t) cert_count;
- tls->ca_bundle_der = all_certs;
- MG_VERBOSE(("%d-cert bundle", cert_count));
- } else if (cert_count > 0) {
- tls->ca_der.buf = all_certs[0].buf;
- tls->ca_der.len = all_certs[0].len;
- mg_free(all_certs);
- } else { // parse again for a possible DER (or a truncated begin string)
- if (mg_parse_pem(opts->ca, mg_str_s("CERTIFICATE"), &tls->ca_der) < 0) {
- MG_ERROR(("Failed to load CA certificate"));
- goto xit;
- }
- } // ca_bundle_len != 0 && ca_der.len = 0 => bundle
- if (!c->is_client) tls->is_twoway = true; // server + CA: two-way auth
- }
- if (opts->cert.buf == NULL) {
- MG_VERBOSE(("No certificate provided"));
- goto xit;
- }
- // parse PEM or DER certificate
- {
- struct mg_str *all_certs = NULL;
- int cert_count = mg_parse_pem_certs(opts->cert, &all_certs);
- if (cert_count > 0) {
- tls->cert_der.buf = all_certs[0].buf;
- tls->cert_der.len = all_certs[0].len;
- if (cert_count > 1) {
- tls->chain_len = (size_t) cert_count;
- tls->chain_der = all_certs;
- } else {
- mg_free(all_certs);
- }
- } else { // parse again for a possible DER (or a truncated begin string)
- if (mg_parse_pem(opts->cert, mg_str_s("CERTIFICATE"), &tls->cert_der) <
- 0) {
- MG_ERROR(("Failed to load certificate"));
- return;
- }
- }
- }
- // parse PEM or DER EC key
- if (opts->key.buf == NULL) {
- mg_error(c, "Certificate provided without a private key");
- return;
- }
- if (mg_parse_pem(opts->key, mg_str_s("EC PRIVATE KEY"), &key) == 0) {
- // expect ASN.1 SEQUENCE=[INTEGER=1, BITSTRING of 32 bytes, ...]
- // 30 nn 02 01 01 04 20 [key] ...
- if (key.len < (2 + 5 + 32) || key.buf[0] != 0x30 ||
- (key.buf[1] & 0x80) != 0 ||
- memcmp(key.buf + 2, "\x02\x01\x01\x04\x20", 5) != 0) {
- mg_error(c, "EC private key: invalid ASN.1");
- } else {
- memmove(tls->ec_key, key.buf + 7, 32);
- }
- mg_free((void *) key.buf);
- } else if (mg_parse_pem(opts->key, mg_str_s("RSA PRIVATE KEY"), &key) == 0) {
- struct mg_rsa_key rsa_key;
- // RSA private key found, store it for later use
- tls->rsa_key_der = key;
- // parse and validate the key structure
- // we keep the DER buffer, rsa_key just points into it
- if (mg_rsa_parse_key((const uint8_t *) key.buf, key.len, &rsa_key) < 0) {
- MG_ERROR(("Failed to parse RSA private key structure"));
- mg_free((void *) key.buf);
- tls->rsa_key_der = mg_str_n(NULL, 0);
- mg_error(c, "Invalid RSA private key format");
- return;
- }
- MG_VERBOSE(("RSA key components:"));
- MG_VERBOSE((" n (modulus): %d bytes", (int) rsa_key.n.len));
- MG_VERBOSE((" e (pubexp): %d bytes", (int) rsa_key.e.len));
- MG_VERBOSE((" d (privexp): %d bytes", (int) rsa_key.d.len));
- MG_VERBOSE((" p (prime1): %d bytes", (int) rsa_key.p.len));
- MG_VERBOSE((" q (prime2): %d bytes", (int) rsa_key.q.len));
- MG_VERBOSE((" dP: %d bytes", (int) rsa_key.dP.len));
- MG_VERBOSE((" dQ: %d bytes", (int) rsa_key.dQ.len));
- MG_VERBOSE((" qInv: %d bytes", (int) rsa_key.qInv.len));
- // Copy parsed RSA key components to tls->rsa for signing operations
- tls->rsa = rsa_key;
- } else if (mg_parse_pem(opts->key, mg_str_s("PRIVATE KEY"), &key) == 0) {
- if (mg_parse_pkcs8_key((const uint8_t *) key.buf, key.len, &tls->rsa,
- tls->ec_key) == 0) {
- if (tls->rsa.n.len > 0) {
- tls->rsa_key_der = key;
- MG_INFO(("Parsed PKCS#8 RSA private key: %d bytes", (int) key.len));
- } else {
- mg_free((void *) key.buf);
- MG_INFO(("Parsed PKCS#8 EC private key"));
- }
- } else {
- mg_free((void *) key.buf);
- mg_error(c, "Unsupported PKCS#8 private key format, algorithm, or curve");
- return;
- }
- } else {
- mg_error(
- c, "Expected EC PRIVATE KEY, RSA PRIVATE KEY, or PRIVATE KEY (PKCS#8)");
- return;
- }
- xit:
- if (mg_boot_timestamp_ms == 0 && !tls->skip_verification &&
- (tls->ca_bundle_len > 0 || tls->ca_der.len > 0)) {
- struct mg_connection *timec;
- timec = mg_sntp_connect(c->mgr, NULL, sync_time_cb, c);
- if (timec == NULL) {
- mg_error(c, "time sync failed");
- return;
- }
- tls->is_sntp_pending = true;
- tls->timec = timec;
- }
- }
- void mg_tls_free(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- size_t i;
- if (tls != NULL) {
- if (tls->timec != NULL) {
- sync_time_cancel(tls->timec);
- tls->timec = NULL;
- }
- mg_iobuf_free(&tls->send);
- if (tls->chain_der != NULL) {
- for (i = 0; i < tls->chain_len; i++) {
- mg_free((void *) tls->chain_der[i].buf);
- }
- mg_free(tls->chain_der);
- } else {
- mg_free((void *) tls->cert_der.buf);
- }
- if (tls->ca_bundle_der != NULL) {
- for (i = 0; i < tls->ca_bundle_len; i++) {
- mg_free((void *) tls->ca_bundle_der[i].buf);
- }
- mg_free(tls->ca_bundle_der);
- }
- mg_free((void *) tls->ca_der.buf);
- mg_free((void *) tls->rsa_key_der.buf);
- }
- mg_free(c->tls);
- c->tls = NULL;
- }
- long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- long n = MG_IO_WAIT;
- bool was_throttled = c->is_tls_throttled; // see #3074
- if (!was_throttled) { // encrypt new data
- if (len > MG_IO_SIZE) len = MG_IO_SIZE;
- if (len > 16384) len = 16384;
- if (!mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA))
- return 0; // returning 0 means an OOM condition (iobuf couldn't resize),
- // yet this is so far recoverable, let the caller decide
- } // else, resend outstanding encrypted data in tls->send
- while (tls->send.len > 0 &&
- (n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
- mg_iobuf_del(&tls->send, 0, (size_t) n);
- } // if last chunk fails to be sent, it needs to be flushed
- c->is_tls_throttled = (tls->send.len > 0 && n == MG_IO_WAIT);
- MG_VERBOSE(("%lu %ld %ld %ld %c %c", c->id, (long) len, (long) tls->send.len,
- n, was_throttled ? 'T' : 't', c->is_tls_throttled ? 'T' : 't'));
- if (n == MG_IO_ERR) return MG_IO_ERR;
- if (was_throttled) return MG_IO_WAIT; // sent throttled data instead
- return (long) len; // return len even when throttled, already encripted that
- }
- long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
- int r = 0;
- struct tls_data *tls = (struct tls_data *) c->tls;
- unsigned char *recv_buf;
- size_t minlen;
- for (;;) {
- r = mg_tls_recv_record(c);
- if (r < 0) return r;
- if (tls->content_type == MG_TLS_APP_DATA) break;
- tls->recv_len = 0;
- mg_tls_drop_record(c);
- }
- if (buf == NULL || len == 0) return 0L;
- recv_buf = &c->rtls.buf[tls->recv_offset];
- minlen = len < tls->recv_len ? len : tls->recv_len;
- memmove(buf, recv_buf, minlen);
- tls->recv_offset += minlen;
- tls->recv_len -= minlen;
- if (tls->recv_len == 0) mg_tls_drop_record(c);
- return (long) minlen;
- }
- size_t mg_tls_pending(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- return tls != NULL ? tls->recv_len : 0;
- }
- void mg_tls_flush(struct mg_connection *c) {
- struct tls_data *tls = (struct tls_data *) c->tls;
- long n;
- while (tls->send.len > 0 &&
- (n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) {
- mg_iobuf_del(&tls->send, 0, (size_t) n);
- }
- }
- void mg_tls_ctx_init(struct mg_mgr *mgr) {
- (void) mgr;
- }
- void mg_tls_ctx_free(struct mg_mgr *mgr) {
- (void) mgr;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tls_chacha20.c"
- #endif
- // portable8439 v1.0.1
- // Source: https://github.com/DavyLandman/portable8439
- // Licensed under CC0-1.0
- // Contains poly1305-donna e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781 (Public
- // Domain)
- #if MG_TLS == MG_TLS_BUILTIN && MG_ENABLE_CHACHA20
- // ******* BEGIN: chacha-portable/chacha-portable.h ********
- #if !defined(__cplusplus) && !defined(_MSC_VER) && \
- (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
- #error "C99 or newer required"
- #endif
- #define CHACHA20_KEY_SIZE (32)
- #define CHACHA20_NONCE_SIZE (12)
- #if defined(_MSC_VER) || defined(__cplusplus)
- // add restrict support
- #if ((defined(_MSC_VER) && _MSC_VER >= 1900) && !defined(__cplusplus)) || \
- defined(__clang__) || defined(__GNUC__)
- #define restrict __restrict
- #else
- #define restrict
- #endif
- #endif
- // xor data with a ChaCha20 keystream as per RFC8439
- static PORTABLE_8439_DECL void chacha20_xor_stream(
- uint8_t *restrict dest, const uint8_t *restrict source, size_t length,
- const uint8_t key[CHACHA20_KEY_SIZE],
- const uint8_t nonce[CHACHA20_NONCE_SIZE], uint32_t counter);
- static PORTABLE_8439_DECL void rfc8439_keygen(
- uint8_t poly_key[32], const uint8_t key[CHACHA20_KEY_SIZE],
- const uint8_t nonce[CHACHA20_NONCE_SIZE]);
- // ******* END: chacha-portable/chacha-portable.h ********
- // ******* BEGIN: poly1305-donna/poly1305-donna.h ********
- #include <stddef.h>
- typedef struct poly1305_context {
- size_t aligner;
- unsigned char opaque[136];
- } poly1305_context;
- static PORTABLE_8439_DECL void poly1305_init(poly1305_context *ctx,
- const unsigned char key[32]);
- static PORTABLE_8439_DECL void poly1305_update(poly1305_context *ctx,
- const unsigned char *m,
- size_t bytes);
- static PORTABLE_8439_DECL void poly1305_finish(poly1305_context *ctx,
- unsigned char mac[16]);
- // ******* END: poly1305-donna/poly1305-donna.h ********
- // ******* BEGIN: chacha-portable.c ********
- #include <assert.h>
- #include <string.h>
- // this is a fresh implementation of chacha20, based on the description in
- // rfc8349 it's such a nice compact algorithm that it is easy to do. In
- // relationship to other c implementation this implementation:
- // - pure c99
- // - big & little endian support
- // - safe for architectures that don't support unaligned reads
- //
- // Next to this, we try to be fast as possible without resorting inline
- // assembly.
- // based on https://sourceforge.net/p/predef/wiki/Endianness/
- #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
- __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- #define __HAVE_LITTLE_ENDIAN 1
- #elif defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \
- defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \
- defined(__MIPSEL) || defined(__MIPSEL__) || defined(__XTENSA_EL__) || \
- defined(__AVR__)
- #define __HAVE_LITTLE_ENDIAN 1
- #endif
- // DO NOT test for LITTLE_ENDIAN, as it is defined as 1234 when including
- // sys/types.h in GCC
- #ifndef TEST_SLOW_PATH
- #if defined(__HAVE_LITTLE_ENDIAN)
- #define FAST_PATH
- #endif
- #endif
- #define CHACHA20_STATE_WORDS (16)
- #define CHACHA20_BLOCK_SIZE (CHACHA20_STATE_WORDS * sizeof(uint32_t))
- #ifdef FAST_PATH
- #define store_32_le(target, source) memcpy(&(target), source, sizeof(uint32_t))
- #else
- #define store_32_le(target, source) \
- target = (uint32_t) (source)[0] | ((uint32_t) (source)[1]) << 8 | \
- ((uint32_t) (source)[2]) << 16 | ((uint32_t) (source)[3]) << 24
- #endif
- static void initialize_state(uint32_t state[CHACHA20_STATE_WORDS],
- const uint8_t key[CHACHA20_KEY_SIZE],
- const uint8_t nonce[CHACHA20_NONCE_SIZE],
- uint32_t counter) {
- #if 0
- #ifdef static_assert
- static_assert(sizeof(uint32_t) == 4,
- "We don't support systems that do not conform to standard of "
- "uint32_t being exact 32bit wide");
- #endif
- #endif
- state[0] = 0x61707865;
- state[1] = 0x3320646e;
- state[2] = 0x79622d32;
- state[3] = 0x6b206574;
- store_32_le(state[4], key);
- store_32_le(state[5], key + 4);
- store_32_le(state[6], key + 8);
- store_32_le(state[7], key + 12);
- store_32_le(state[8], key + 16);
- store_32_le(state[9], key + 20);
- store_32_le(state[10], key + 24);
- store_32_le(state[11], key + 28);
- state[12] = counter;
- store_32_le(state[13], nonce);
- store_32_le(state[14], nonce + 4);
- store_32_le(state[15], nonce + 8);
- }
- #define increment_counter(state) (state)[12]++
- // source: http://blog.regehr.org/archives/1063
- #define rotl32a(x, n) ((x) << (n)) | ((x) >> (32 - (n)))
- #define Qround(a, b, c, d) \
- a += b; \
- d ^= a; \
- d = rotl32a(d, 16); \
- c += d; \
- b ^= c; \
- b = rotl32a(b, 12); \
- a += b; \
- d ^= a; \
- d = rotl32a(d, 8); \
- c += d; \
- b ^= c; \
- b = rotl32a(b, 7);
- #define TIMES16(x) \
- x(0) x(1) x(2) x(3) x(4) x(5) x(6) x(7) x(8) x(9) x(10) x(11) x(12) x(13) \
- x(14) x(15)
- static void core_block(const uint32_t *restrict start,
- uint32_t *restrict output) {
- int i;
- // instead of working on the output array,
- // we let the compiler allocate 16 local variables on the stack
- #define __LV(i) uint32_t __t##i = start[i];
- TIMES16(__LV)
- #define __Q(a, b, c, d) Qround(__t##a, __t##b, __t##c, __t##d)
- for (i = 0; i < 10; i++) {
- __Q(0, 4, 8, 12);
- __Q(1, 5, 9, 13);
- __Q(2, 6, 10, 14);
- __Q(3, 7, 11, 15);
- __Q(0, 5, 10, 15);
- __Q(1, 6, 11, 12);
- __Q(2, 7, 8, 13);
- __Q(3, 4, 9, 14);
- }
- #define __FIN(i) output[i] = start[i] + __t##i;
- TIMES16(__FIN)
- }
- #define U8(x) ((uint8_t) ((x) & 0xFF))
- #ifdef FAST_PATH
- #define xor32_le(dst, src, pad) \
- uint32_t __value; \
- memcpy(&__value, src, sizeof(uint32_t)); \
- __value ^= *(pad); \
- memcpy(dst, &__value, sizeof(uint32_t));
- #else
- #define xor32_le(dst, src, pad) \
- (dst)[0] = (src)[0] ^ U8(*(pad)); \
- (dst)[1] = (src)[1] ^ U8(*(pad) >> 8); \
- (dst)[2] = (src)[2] ^ U8(*(pad) >> 16); \
- (dst)[3] = (src)[3] ^ U8(*(pad) >> 24);
- #endif
- #define index8_32(a, ix) ((a) + ((ix) * sizeof(uint32_t)))
- #define xor32_blocks(dest, source, pad, words) \
- for (i = 0; i < words; i++) { \
- xor32_le(index8_32(dest, i), index8_32(source, i), (pad) + i) \
- }
- static void xor_block(uint8_t *restrict dest, const uint8_t *restrict source,
- const uint32_t *restrict pad, unsigned int chunk_size) {
- unsigned int i, full_blocks = chunk_size / (unsigned int) sizeof(uint32_t);
- // have to be carefull, we are going back from uint32 to uint8, so endianness
- // matters again
- xor32_blocks(dest, source, pad, full_blocks)
- dest += full_blocks * sizeof(uint32_t);
- source += full_blocks * sizeof(uint32_t);
- pad += full_blocks;
- switch (chunk_size % sizeof(uint32_t)) {
- case 1:
- dest[0] = source[0] ^ U8(*pad);
- break;
- case 2:
- dest[0] = source[0] ^ U8(*pad);
- dest[1] = source[1] ^ U8(*pad >> 8);
- break;
- case 3:
- dest[0] = source[0] ^ U8(*pad);
- dest[1] = source[1] ^ U8(*pad >> 8);
- dest[2] = source[2] ^ U8(*pad >> 16);
- break;
- }
- }
- static void chacha20_xor_stream(uint8_t *restrict dest,
- const uint8_t *restrict source, size_t length,
- const uint8_t key[CHACHA20_KEY_SIZE],
- const uint8_t nonce[CHACHA20_NONCE_SIZE],
- uint32_t counter) {
- uint32_t state[CHACHA20_STATE_WORDS];
- uint32_t pad[CHACHA20_STATE_WORDS];
- size_t i, b, last_block, full_blocks = length / CHACHA20_BLOCK_SIZE;
- initialize_state(state, key, nonce, counter);
- for (b = 0; b < full_blocks; b++) {
- core_block(state, pad);
- increment_counter(state);
- xor32_blocks(dest, source, pad, CHACHA20_STATE_WORDS) dest +=
- CHACHA20_BLOCK_SIZE;
- source += CHACHA20_BLOCK_SIZE;
- }
- last_block = length % CHACHA20_BLOCK_SIZE;
- if (last_block > 0) {
- core_block(state, pad);
- xor_block(dest, source, pad, (unsigned int) last_block);
- }
- }
- #ifdef FAST_PATH
- #define serialize(poly_key, result) memcpy(poly_key, result, 32)
- #else
- #define store32_le(target, source) \
- (target)[0] = U8(*(source)); \
- (target)[1] = U8(*(source) >> 8); \
- (target)[2] = U8(*(source) >> 16); \
- (target)[3] = U8(*(source) >> 24);
- #define serialize(poly_key, result) \
- for (i = 0; i < 32 / sizeof(uint32_t); i++) { \
- store32_le(index8_32(poly_key, i), result + i); \
- }
- #endif
- static void rfc8439_keygen(uint8_t poly_key[32],
- const uint8_t key[CHACHA20_KEY_SIZE],
- const uint8_t nonce[CHACHA20_NONCE_SIZE]) {
- uint32_t state[CHACHA20_STATE_WORDS];
- uint32_t result[CHACHA20_STATE_WORDS];
- size_t i;
- initialize_state(state, key, nonce, 0);
- core_block(state, result);
- serialize(poly_key, result);
- (void) i;
- }
- // ******* END: chacha-portable.c ********
- // ******* BEGIN: poly1305-donna.c ********
- /* auto detect between 32bit / 64bit */
- #if /* uint128 available on 64bit system*/ \
- (defined(__SIZEOF_INT128__) && \
- defined(__LP64__)) /* MSVC 64bit compiler */ \
- || (defined(_MSC_VER) && defined(_M_X64)) /* gcc >= 4.4 64bit */ \
- || (defined(__GNUC__) && defined(__LP64__) && \
- ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4))))
- #define __GUESS64
- #else
- #define __GUESS32
- #endif
- #if defined(POLY1305_8BIT)
- /*
- poly1305 implementation using 8 bit * 8 bit = 16 bit multiplication and
- 32 bit addition
- based on the public domain reference version in supercop by djb
- static */
- #if defined(_MSC_VER) && _MSC_VER < 1700
- #define POLY1305_NOINLINE
- #elif defined(_MSC_VER)
- #define POLY1305_NOINLINE __declspec(noinline)
- #elif defined(__GNUC__)
- #define POLY1305_NOINLINE __attribute__((noinline))
- #else
- #define POLY1305_NOINLINE
- #endif
- #define poly1305_block_size 16
- /* 17 + sizeof(size_t) + 51*sizeof(unsigned char) */
- typedef struct poly1305_state_internal_t {
- unsigned char buffer[poly1305_block_size];
- size_t leftover;
- unsigned char h[17];
- unsigned char r[17];
- unsigned char pad[17];
- unsigned char final;
- } poly1305_state_internal_t;
- static void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
- poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
- size_t i;
- st->leftover = 0;
- /* h = 0 */
- for (i = 0; i < 17; i++) st->h[i] = 0;
- /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
- st->r[0] = key[0] & 0xff;
- st->r[1] = key[1] & 0xff;
- st->r[2] = key[2] & 0xff;
- st->r[3] = key[3] & 0x0f;
- st->r[4] = key[4] & 0xfc;
- st->r[5] = key[5] & 0xff;
- st->r[6] = key[6] & 0xff;
- st->r[7] = key[7] & 0x0f;
- st->r[8] = key[8] & 0xfc;
- st->r[9] = key[9] & 0xff;
- st->r[10] = key[10] & 0xff;
- st->r[11] = key[11] & 0x0f;
- st->r[12] = key[12] & 0xfc;
- st->r[13] = key[13] & 0xff;
- st->r[14] = key[14] & 0xff;
- st->r[15] = key[15] & 0x0f;
- st->r[16] = 0;
- /* save pad for later */
- for (i = 0; i < 16; i++) st->pad[i] = key[i + 16];
- st->pad[16] = 0;
- st->final = 0;
- }
- static void poly1305_add(unsigned char h[17], const unsigned char c[17]) {
- unsigned short u;
- unsigned int i;
- for (u = 0, i = 0; i < 17; i++) {
- u += (unsigned short) h[i] + (unsigned short) c[i];
- h[i] = (unsigned char) u & 0xff;
- u >>= 8;
- }
- }
- static void poly1305_squeeze(unsigned char h[17], unsigned long hr[17]) {
- unsigned long u;
- unsigned int i;
- u = 0;
- for (i = 0; i < 16; i++) {
- u += hr[i];
- h[i] = (unsigned char) u & 0xff;
- u >>= 8;
- }
- u += hr[16];
- h[16] = (unsigned char) u & 0x03;
- u >>= 2;
- u += (u << 2); /* u *= 5; */
- for (i = 0; i < 16; i++) {
- u += h[i];
- h[i] = (unsigned char) u & 0xff;
- u >>= 8;
- }
- h[16] += (unsigned char) u;
- }
- static void poly1305_freeze(unsigned char h[17]) {
- const unsigned char minusp[17] = {0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xfc};
- unsigned char horig[17], negative;
- unsigned int i;
- /* compute h + -p */
- for (i = 0; i < 17; i++) horig[i] = h[i];
- poly1305_add(h, minusp);
- /* select h if h < p, or h + -p if h >= p */
- negative = -(h[16] >> 7);
- for (i = 0; i < 17; i++) h[i] ^= negative & (horig[i] ^ h[i]);
- }
- static void poly1305_blocks(poly1305_state_internal_t *st,
- const unsigned char *m, size_t bytes) {
- const unsigned char hibit = st->final ^ 1; /* 1 << 128 */
- while (bytes >= poly1305_block_size) {
- unsigned long hr[17], u;
- unsigned char c[17];
- unsigned int i, j;
- /* h += m */
- for (i = 0; i < 16; i++) c[i] = m[i];
- c[16] = hibit;
- poly1305_add(st->h, c);
- /* h *= r */
- for (i = 0; i < 17; i++) {
- u = 0;
- for (j = 0; j <= i; j++) {
- u += (unsigned short) st->h[j] * st->r[i - j];
- }
- for (j = i + 1; j < 17; j++) {
- unsigned long v = (unsigned short) st->h[j] * st->r[i + 17 - j];
- v = ((v << 8) + (v << 6)); /* v *= (5 << 6); */
- u += v;
- }
- hr[i] = u;
- }
- /* (partial) h %= p */
- poly1305_squeeze(st->h, hr);
- m += poly1305_block_size;
- bytes -= poly1305_block_size;
- }
- }
- static POLY1305_NOINLINE void poly1305_finish(poly1305_context *ctx,
- unsigned char mac[16]) {
- poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
- size_t i;
- /* process the remaining block */
- if (st->leftover) {
- size_t i = st->leftover;
- st->buffer[i++] = 1;
- for (; i < poly1305_block_size; i++) st->buffer[i] = 0;
- st->final = 1;
- poly1305_blocks(st, st->buffer, poly1305_block_size);
- }
- /* fully reduce h */
- poly1305_freeze(st->h);
- /* h = (h + pad) % (1 << 128) */
- poly1305_add(st->h, st->pad);
- for (i = 0; i < 16; i++) mac[i] = st->h[i];
- /* zero out the state */
- for (i = 0; i < 17; i++) st->h[i] = 0;
- for (i = 0; i < 17; i++) st->r[i] = 0;
- for (i = 0; i < 17; i++) st->pad[i] = 0;
- }
- #elif defined(POLY1305_16BIT)
- /*
- poly1305 implementation using 16 bit * 16 bit = 32 bit multiplication
- and 32 bit addition static */
- #if defined(_MSC_VER) && _MSC_VER < 1700
- #define POLY1305_NOINLINE
- #elif defined(_MSC_VER)
- #define POLY1305_NOINLINE __declspec(noinline)
- #elif defined(__GNUC__)
- #define POLY1305_NOINLINE __attribute__((noinline))
- #else
- #define POLY1305_NOINLINE
- #endif
- #define poly1305_block_size 16
- /* 17 + sizeof(size_t) + 18*sizeof(unsigned short) */
- typedef struct poly1305_state_internal_t {
- unsigned char buffer[poly1305_block_size];
- size_t leftover;
- unsigned short r[10];
- unsigned short h[10];
- unsigned short pad[8];
- unsigned char final;
- } poly1305_state_internal_t;
- /* interpret two 8 bit unsigned integers as a 16 bit unsigned integer in little
- * endian */
- static unsigned short U8TO16(const unsigned char *p) {
- return (((unsigned short) (p[0] & 0xff)) |
- ((unsigned short) (p[1] & 0xff) << 8));
- }
- /* store a 16 bit unsigned integer as two 8 bit unsigned integers in little
- * endian */
- static void U16TO8(unsigned char *p, unsigned short v) {
- p[0] = (v) & 0xff;
- p[1] = (v >> 8) & 0xff;
- }
- static void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
- poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
- unsigned short t0, t1, t2, t3, t4, t5, t6, t7;
- size_t i;
- /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
- t0 = U8TO16(&key[0]);
- st->r[0] = (t0) & 0x1fff;
- t1 = U8TO16(&key[2]);
- st->r[1] = ((t0 >> 13) | (t1 << 3)) & 0x1fff;
- t2 = U8TO16(&key[4]);
- st->r[2] = ((t1 >> 10) | (t2 << 6)) & 0x1f03;
- t3 = U8TO16(&key[6]);
- st->r[3] = ((t2 >> 7) | (t3 << 9)) & 0x1fff;
- t4 = U8TO16(&key[8]);
- st->r[4] = ((t3 >> 4) | (t4 << 12)) & 0x00ff;
- st->r[5] = ((t4 >> 1)) & 0x1ffe;
- t5 = U8TO16(&key[10]);
- st->r[6] = ((t4 >> 14) | (t5 << 2)) & 0x1fff;
- t6 = U8TO16(&key[12]);
- st->r[7] = ((t5 >> 11) | (t6 << 5)) & 0x1f81;
- t7 = U8TO16(&key[14]);
- st->r[8] = ((t6 >> 8) | (t7 << 8)) & 0x1fff;
- st->r[9] = ((t7 >> 5)) & 0x007f;
- /* h = 0 */
- for (i = 0; i < 10; i++) st->h[i] = 0;
- /* save pad for later */
- for (i = 0; i < 8; i++) st->pad[i] = U8TO16(&key[16 + (2 * i)]);
- st->leftover = 0;
- st->final = 0;
- }
- static void poly1305_blocks(poly1305_state_internal_t *st,
- const unsigned char *m, size_t bytes) {
- const unsigned short hibit = (st->final) ? 0 : (1 << 11); /* 1 << 128 */
- unsigned short t0, t1, t2, t3, t4, t5, t6, t7;
- unsigned long d[10];
- unsigned long c;
- while (bytes >= poly1305_block_size) {
- size_t i, j;
- /* h += m[i] */
- t0 = U8TO16(&m[0]);
- st->h[0] += (t0) & 0x1fff;
- t1 = U8TO16(&m[2]);
- st->h[1] += ((t0 >> 13) | (t1 << 3)) & 0x1fff;
- t2 = U8TO16(&m[4]);
- st->h[2] += ((t1 >> 10) | (t2 << 6)) & 0x1fff;
- t3 = U8TO16(&m[6]);
- st->h[3] += ((t2 >> 7) | (t3 << 9)) & 0x1fff;
- t4 = U8TO16(&m[8]);
- st->h[4] += ((t3 >> 4) | (t4 << 12)) & 0x1fff;
- st->h[5] += ((t4 >> 1)) & 0x1fff;
- t5 = U8TO16(&m[10]);
- st->h[6] += ((t4 >> 14) | (t5 << 2)) & 0x1fff;
- t6 = U8TO16(&m[12]);
- st->h[7] += ((t5 >> 11) | (t6 << 5)) & 0x1fff;
- t7 = U8TO16(&m[14]);
- st->h[8] += ((t6 >> 8) | (t7 << 8)) & 0x1fff;
- st->h[9] += ((t7 >> 5)) | hibit;
- /* h *= r, (partial) h %= p */
- for (i = 0, c = 0; i < 10; i++) {
- d[i] = c;
- for (j = 0; j < 10; j++) {
- d[i] += (unsigned long) st->h[j] *
- ((j <= i) ? st->r[i - j] : (5 * st->r[i + 10 - j]));
- /* Sum(h[i] * r[i] * 5) will overflow slightly above 6 products with an
- * unclamped r, so carry at 5 */
- if (j == 4) {
- c = (d[i] >> 13);
- d[i] &= 0x1fff;
- }
- }
- c += (d[i] >> 13);
- d[i] &= 0x1fff;
- }
- c = ((c << 2) + c); /* c *= 5 */
- c += d[0];
- d[0] = ((unsigned short) c & 0x1fff);
- c = (c >> 13);
- d[1] += c;
- for (i = 0; i < 10; i++) st->h[i] = (unsigned short) d[i];
- m += poly1305_block_size;
- bytes -= poly1305_block_size;
- }
- }
- static POLY1305_NOINLINE void poly1305_finish(poly1305_context *ctx,
- unsigned char mac[16]) {
- poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
- unsigned short c;
- unsigned short g[10];
- unsigned short mask;
- unsigned long f;
- size_t i;
- /* process the remaining block */
- if (st->leftover) {
- size_t i = st->leftover;
- st->buffer[i++] = 1;
- for (; i < poly1305_block_size; i++) st->buffer[i] = 0;
- st->final = 1;
- poly1305_blocks(st, st->buffer, poly1305_block_size);
- }
- /* fully carry h */
- c = st->h[1] >> 13;
- st->h[1] &= 0x1fff;
- for (i = 2; i < 10; i++) {
- st->h[i] += c;
- c = st->h[i] >> 13;
- st->h[i] &= 0x1fff;
- }
- st->h[0] += (c * 5);
- c = st->h[0] >> 13;
- st->h[0] &= 0x1fff;
- st->h[1] += c;
- c = st->h[1] >> 13;
- st->h[1] &= 0x1fff;
- st->h[2] += c;
- /* compute h + -p */
- g[0] = st->h[0] + 5;
- c = g[0] >> 13;
- g[0] &= 0x1fff;
- for (i = 1; i < 10; i++) {
- g[i] = st->h[i] + c;
- c = g[i] >> 13;
- g[i] &= 0x1fff;
- }
- /* select h if h < p, or h + -p if h >= p */
- mask = (c ^ 1) - 1;
- for (i = 0; i < 10; i++) g[i] &= mask;
- mask = ~mask;
- for (i = 0; i < 10; i++) st->h[i] = (st->h[i] & mask) | g[i];
- /* h = h % (2^128) */
- st->h[0] = ((st->h[0]) | (st->h[1] << 13)) & 0xffff;
- st->h[1] = ((st->h[1] >> 3) | (st->h[2] << 10)) & 0xffff;
- st->h[2] = ((st->h[2] >> 6) | (st->h[3] << 7)) & 0xffff;
- st->h[3] = ((st->h[3] >> 9) | (st->h[4] << 4)) & 0xffff;
- st->h[4] = ((st->h[4] >> 12) | (st->h[5] << 1) | (st->h[6] << 14)) & 0xffff;
- st->h[5] = ((st->h[6] >> 2) | (st->h[7] << 11)) & 0xffff;
- st->h[6] = ((st->h[7] >> 5) | (st->h[8] << 8)) & 0xffff;
- st->h[7] = ((st->h[8] >> 8) | (st->h[9] << 5)) & 0xffff;
- /* mac = (h + pad) % (2^128) */
- f = (unsigned long) st->h[0] + st->pad[0];
- st->h[0] = (unsigned short) f;
- for (i = 1; i < 8; i++) {
- f = (unsigned long) st->h[i] + st->pad[i] + (f >> 16);
- st->h[i] = (unsigned short) f;
- }
- for (i = 0; i < 8; i++) U16TO8(mac + (i * 2), st->h[i]);
- /* zero out the state */
- for (i = 0; i < 10; i++) st->h[i] = 0;
- for (i = 0; i < 10; i++) st->r[i] = 0;
- for (i = 0; i < 8; i++) st->pad[i] = 0;
- }
- #elif defined(POLY1305_32BIT) || \
- (!defined(POLY1305_64BIT) && defined(__GUESS32))
- /*
- poly1305 implementation using 32 bit * 32 bit = 64 bit multiplication
- and 64 bit addition static */
- #if defined(_MSC_VER) && _MSC_VER < 1700
- #define POLY1305_NOINLINE
- #elif defined(_MSC_VER)
- #define POLY1305_NOINLINE __declspec(noinline)
- #elif defined(__GNUC__)
- #define POLY1305_NOINLINE __attribute__((noinline))
- #else
- #define POLY1305_NOINLINE
- #endif
- #define poly1305_block_size 16
- /* 17 + sizeof(size_t) + 14*sizeof(unsigned long) */
- typedef struct poly1305_state_internal_t {
- unsigned long r[5];
- unsigned long h[5];
- unsigned long pad[4];
- size_t leftover;
- unsigned char buffer[poly1305_block_size];
- unsigned char final;
- } poly1305_state_internal_t;
- /* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little
- * endian */
- static unsigned long U8TO32(const unsigned char *p) {
- return (((unsigned long) (p[0] & 0xff)) |
- ((unsigned long) (p[1] & 0xff) << 8) |
- ((unsigned long) (p[2] & 0xff) << 16) |
- ((unsigned long) (p[3] & 0xff) << 24));
- }
- /* store a 32 bit unsigned integer as four 8 bit unsigned integers in little
- * endian */
- static void U32TO8(unsigned char *p, unsigned long v) {
- p[0] = (unsigned char) ((v) & 0xff);
- p[1] = (unsigned char) ((v >> 8) & 0xff);
- p[2] = (unsigned char) ((v >> 16) & 0xff);
- p[3] = (unsigned char) ((v >> 24) & 0xff);
- }
- static void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
- poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
- /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
- st->r[0] = (U8TO32(&key[0])) & 0x3ffffff;
- st->r[1] = (U8TO32(&key[3]) >> 2) & 0x3ffff03;
- st->r[2] = (U8TO32(&key[6]) >> 4) & 0x3ffc0ff;
- st->r[3] = (U8TO32(&key[9]) >> 6) & 0x3f03fff;
- st->r[4] = (U8TO32(&key[12]) >> 8) & 0x00fffff;
- /* h = 0 */
- st->h[0] = 0;
- st->h[1] = 0;
- st->h[2] = 0;
- st->h[3] = 0;
- st->h[4] = 0;
- /* save pad for later */
- st->pad[0] = U8TO32(&key[16]);
- st->pad[1] = U8TO32(&key[20]);
- st->pad[2] = U8TO32(&key[24]);
- st->pad[3] = U8TO32(&key[28]);
- st->leftover = 0;
- st->final = 0;
- }
- static void poly1305_blocks(poly1305_state_internal_t *st,
- const unsigned char *m, size_t bytes) {
- const unsigned long hibit = (st->final) ? 0 : (1UL << 24); /* 1 << 128 */
- unsigned long r0, r1, r2, r3, r4;
- unsigned long s1, s2, s3, s4;
- unsigned long h0, h1, h2, h3, h4;
- uint64_t d0, d1, d2, d3, d4;
- unsigned long c;
- r0 = st->r[0];
- r1 = st->r[1];
- r2 = st->r[2];
- r3 = st->r[3];
- r4 = st->r[4];
- s1 = r1 * 5;
- s2 = r2 * 5;
- s3 = r3 * 5;
- s4 = r4 * 5;
- h0 = st->h[0];
- h1 = st->h[1];
- h2 = st->h[2];
- h3 = st->h[3];
- h4 = st->h[4];
- while (bytes >= poly1305_block_size) {
- /* h += m[i] */
- h0 += (U8TO32(m + 0)) & 0x3ffffff;
- h1 += (U8TO32(m + 3) >> 2) & 0x3ffffff;
- h2 += (U8TO32(m + 6) >> 4) & 0x3ffffff;
- h3 += (U8TO32(m + 9) >> 6) & 0x3ffffff;
- h4 += (U8TO32(m + 12) >> 8) | hibit;
- /* h *= r */
- d0 = ((uint64_t) h0 * r0) + ((uint64_t) h1 * s4) + ((uint64_t) h2 * s3) +
- ((uint64_t) h3 * s2) + ((uint64_t) h4 * s1);
- d1 = ((uint64_t) h0 * r1) + ((uint64_t) h1 * r0) + ((uint64_t) h2 * s4) +
- ((uint64_t) h3 * s3) + ((uint64_t) h4 * s2);
- d2 = ((uint64_t) h0 * r2) + ((uint64_t) h1 * r1) + ((uint64_t) h2 * r0) +
- ((uint64_t) h3 * s4) + ((uint64_t) h4 * s3);
- d3 = ((uint64_t) h0 * r3) + ((uint64_t) h1 * r2) + ((uint64_t) h2 * r1) +
- ((uint64_t) h3 * r0) + ((uint64_t) h4 * s4);
- d4 = ((uint64_t) h0 * r4) + ((uint64_t) h1 * r3) + ((uint64_t) h2 * r2) +
- ((uint64_t) h3 * r1) + ((uint64_t) h4 * r0);
- /* (partial) h %= p */
- c = (unsigned long) (d0 >> 26);
- h0 = (unsigned long) d0 & 0x3ffffff;
- d1 += c;
- c = (unsigned long) (d1 >> 26);
- h1 = (unsigned long) d1 & 0x3ffffff;
- d2 += c;
- c = (unsigned long) (d2 >> 26);
- h2 = (unsigned long) d2 & 0x3ffffff;
- d3 += c;
- c = (unsigned long) (d3 >> 26);
- h3 = (unsigned long) d3 & 0x3ffffff;
- d4 += c;
- c = (unsigned long) (d4 >> 26);
- h4 = (unsigned long) d4 & 0x3ffffff;
- h0 += c * 5;
- c = (h0 >> 26);
- h0 = h0 & 0x3ffffff;
- h1 += c;
- m += poly1305_block_size;
- bytes -= poly1305_block_size;
- }
- st->h[0] = h0;
- st->h[1] = h1;
- st->h[2] = h2;
- st->h[3] = h3;
- st->h[4] = h4;
- }
- static POLY1305_NOINLINE void poly1305_finish(poly1305_context *ctx,
- unsigned char mac[16]) {
- poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
- unsigned long h0, h1, h2, h3, h4, c;
- unsigned long g0, g1, g2, g3, g4;
- uint64_t f;
- unsigned long mask;
- /* process the remaining block */
- if (st->leftover) {
- size_t i = st->leftover;
- st->buffer[i++] = 1;
- for (; i < poly1305_block_size; i++) st->buffer[i] = 0;
- st->final = 1;
- poly1305_blocks(st, st->buffer, poly1305_block_size);
- }
- /* fully carry h */
- h0 = st->h[0];
- h1 = st->h[1];
- h2 = st->h[2];
- h3 = st->h[3];
- h4 = st->h[4];
- c = h1 >> 26;
- h1 = h1 & 0x3ffffff;
- h2 += c;
- c = h2 >> 26;
- h2 = h2 & 0x3ffffff;
- h3 += c;
- c = h3 >> 26;
- h3 = h3 & 0x3ffffff;
- h4 += c;
- c = h4 >> 26;
- h4 = h4 & 0x3ffffff;
- h0 += c * 5;
- c = h0 >> 26;
- h0 = h0 & 0x3ffffff;
- h1 += c;
- /* compute h + -p */
- g0 = h0 + 5;
- c = g0 >> 26;
- g0 &= 0x3ffffff;
- g1 = h1 + c;
- c = g1 >> 26;
- g1 &= 0x3ffffff;
- g2 = h2 + c;
- c = g2 >> 26;
- g2 &= 0x3ffffff;
- g3 = h3 + c;
- c = g3 >> 26;
- g3 &= 0x3ffffff;
- g4 = h4 + c - (1UL << 26);
- /* select h if h < p, or h + -p if h >= p */
- mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1;
- g0 &= mask;
- g1 &= mask;
- g2 &= mask;
- g3 &= mask;
- g4 &= mask;
- mask = ~mask;
- h0 = (h0 & mask) | g0;
- h1 = (h1 & mask) | g1;
- h2 = (h2 & mask) | g2;
- h3 = (h3 & mask) | g3;
- h4 = (h4 & mask) | g4;
- /* h = h % (2^128) */
- h0 = ((h0) | (h1 << 26)) & 0xffffffff;
- h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff;
- h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
- h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff;
- /* mac = (h + pad) % (2^128) */
- f = (uint64_t) h0 + st->pad[0];
- h0 = (unsigned long) f;
- f = (uint64_t) h1 + st->pad[1] + (f >> 32);
- h1 = (unsigned long) f;
- f = (uint64_t) h2 + st->pad[2] + (f >> 32);
- h2 = (unsigned long) f;
- f = (uint64_t) h3 + st->pad[3] + (f >> 32);
- h3 = (unsigned long) f;
- U32TO8(mac + 0, h0);
- U32TO8(mac + 4, h1);
- U32TO8(mac + 8, h2);
- U32TO8(mac + 12, h3);
- /* zero out the state */
- st->h[0] = 0;
- st->h[1] = 0;
- st->h[2] = 0;
- st->h[3] = 0;
- st->h[4] = 0;
- st->r[0] = 0;
- st->r[1] = 0;
- st->r[2] = 0;
- st->r[3] = 0;
- st->r[4] = 0;
- st->pad[0] = 0;
- st->pad[1] = 0;
- st->pad[2] = 0;
- st->pad[3] = 0;
- }
- #else
- /*
- poly1305 implementation using 64 bit * 64 bit = 128 bit multiplication
- and 128 bit addition static */
- #if defined(_MSC_VER)
- typedef struct uint128_t {
- uint64_t lo;
- uint64_t hi;
- } uint128_t;
- #define MUL128(out, x, y) out.lo = _umul128((x), (y), &out.hi)
- #define ADD(out, in) \
- { \
- uint64_t t = out.lo; \
- out.lo += in.lo; \
- out.hi += (out.lo < t) + in.hi; \
- }
- #define ADDLO(out, in) \
- { \
- uint64_t t = out.lo; \
- out.lo += in; \
- out.hi += (out.lo < t); \
- }
- #define SHR(in, shift) (__shiftright128(in.lo, in.hi, (shift)))
- #define LO(in) (in.lo)
- #if defined(_MSC_VER) && _MSC_VER < 1700
- #define POLY1305_NOINLINE
- #else
- #define POLY1305_NOINLINE __declspec(noinline)
- #endif
- #elif defined(__GNUC__)
- #if defined(__SIZEOF_INT128__)
- // Get rid of GCC warning "ISO C does not support '__int128' types"
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wpedantic"
- typedef unsigned __int128 uint128_t;
- #pragma GCC diagnostic pop
- #else
- typedef unsigned uint128_t __attribute__((mode(TI)));
- #endif
- #define MUL128(out, x, y) out = ((uint128_t) x * y)
- #define ADD(out, in) out += in
- #define ADDLO(out, in) out += in
- #define SHR(in, shift) (uint64_t) (in >> (shift))
- #define LO(in) (uint64_t) (in)
- #define POLY1305_NOINLINE __attribute__((noinline))
- #endif
- #define poly1305_block_size 16
- /* 17 + sizeof(size_t) + 8*sizeof(uint64_t) */
- typedef struct poly1305_state_internal_t {
- uint64_t r[3];
- uint64_t h[3];
- uint64_t pad[2];
- size_t leftover;
- unsigned char buffer[poly1305_block_size];
- unsigned char final;
- } poly1305_state_internal_t;
- /* interpret eight 8 bit unsigned integers as a 64 bit unsigned integer in
- * little endian */
- static uint64_t U8TO64(const unsigned char *p) {
- return (((uint64_t) (p[0] & 0xff)) | ((uint64_t) (p[1] & 0xff) << 8) |
- ((uint64_t) (p[2] & 0xff) << 16) | ((uint64_t) (p[3] & 0xff) << 24) |
- ((uint64_t) (p[4] & 0xff) << 32) | ((uint64_t) (p[5] & 0xff) << 40) |
- ((uint64_t) (p[6] & 0xff) << 48) | ((uint64_t) (p[7] & 0xff) << 56));
- }
- /* store a 64 bit unsigned integer as eight 8 bit unsigned integers in little
- * endian */
- static void U64TO8(unsigned char *p, uint64_t v) {
- p[0] = (unsigned char) ((v) & 0xff);
- p[1] = (unsigned char) ((v >> 8) & 0xff);
- p[2] = (unsigned char) ((v >> 16) & 0xff);
- p[3] = (unsigned char) ((v >> 24) & 0xff);
- p[4] = (unsigned char) ((v >> 32) & 0xff);
- p[5] = (unsigned char) ((v >> 40) & 0xff);
- p[6] = (unsigned char) ((v >> 48) & 0xff);
- p[7] = (unsigned char) ((v >> 56) & 0xff);
- }
- static void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
- poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
- uint64_t t0, t1;
- /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
- t0 = U8TO64(&key[0]);
- t1 = U8TO64(&key[8]);
- st->r[0] = (t0) & 0xffc0fffffff;
- st->r[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff;
- st->r[2] = ((t1 >> 24)) & 0x00ffffffc0f;
- /* h = 0 */
- st->h[0] = 0;
- st->h[1] = 0;
- st->h[2] = 0;
- /* save pad for later */
- st->pad[0] = U8TO64(&key[16]);
- st->pad[1] = U8TO64(&key[24]);
- st->leftover = 0;
- st->final = 0;
- }
- static void poly1305_blocks(poly1305_state_internal_t *st,
- const unsigned char *m, size_t bytes) {
- const uint64_t hibit = (st->final) ? 0 : ((uint64_t) 1 << 40); /* 1 << 128 */
- uint64_t r0, r1, r2;
- uint64_t s1, s2;
- uint64_t h0, h1, h2;
- uint64_t c;
- uint128_t d0, d1, d2, d;
- r0 = st->r[0];
- r1 = st->r[1];
- r2 = st->r[2];
- h0 = st->h[0];
- h1 = st->h[1];
- h2 = st->h[2];
- s1 = r1 * (5 << 2);
- s2 = r2 * (5 << 2);
- while (bytes >= poly1305_block_size) {
- uint64_t t0, t1;
- /* h += m[i] */
- t0 = U8TO64(&m[0]);
- t1 = U8TO64(&m[8]);
- h0 += ((t0) & 0xfffffffffff);
- h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff);
- h2 += (((t1 >> 24)) & 0x3ffffffffff) | hibit;
- /* h *= r */
- MUL128(d0, h0, r0);
- MUL128(d, h1, s2);
- ADD(d0, d);
- MUL128(d, h2, s1);
- ADD(d0, d);
- MUL128(d1, h0, r1);
- MUL128(d, h1, r0);
- ADD(d1, d);
- MUL128(d, h2, s2);
- ADD(d1, d);
- MUL128(d2, h0, r2);
- MUL128(d, h1, r1);
- ADD(d2, d);
- MUL128(d, h2, r0);
- ADD(d2, d);
- /* (partial) h %= p */
- c = SHR(d0, 44);
- h0 = LO(d0) & 0xfffffffffff;
- ADDLO(d1, c);
- c = SHR(d1, 44);
- h1 = LO(d1) & 0xfffffffffff;
- ADDLO(d2, c);
- c = SHR(d2, 42);
- h2 = LO(d2) & 0x3ffffffffff;
- h0 += c * 5;
- c = (h0 >> 44);
- h0 = h0 & 0xfffffffffff;
- h1 += c;
- m += poly1305_block_size;
- bytes -= poly1305_block_size;
- }
- st->h[0] = h0;
- st->h[1] = h1;
- st->h[2] = h2;
- }
- static POLY1305_NOINLINE void poly1305_finish(poly1305_context *ctx,
- unsigned char mac[16]) {
- poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
- uint64_t h0, h1, h2, c;
- uint64_t g0, g1, g2;
- uint64_t t0, t1;
- /* process the remaining block */
- if (st->leftover) {
- size_t i = st->leftover;
- st->buffer[i] = 1;
- for (i = i + 1; i < poly1305_block_size; i++) st->buffer[i] = 0;
- st->final = 1;
- poly1305_blocks(st, st->buffer, poly1305_block_size);
- }
- /* fully carry h */
- h0 = st->h[0];
- h1 = st->h[1];
- h2 = st->h[2];
- c = (h1 >> 44);
- h1 &= 0xfffffffffff;
- h2 += c;
- c = (h2 >> 42);
- h2 &= 0x3ffffffffff;
- h0 += c * 5;
- c = (h0 >> 44);
- h0 &= 0xfffffffffff;
- h1 += c;
- c = (h1 >> 44);
- h1 &= 0xfffffffffff;
- h2 += c;
- c = (h2 >> 42);
- h2 &= 0x3ffffffffff;
- h0 += c * 5;
- c = (h0 >> 44);
- h0 &= 0xfffffffffff;
- h1 += c;
- /* compute h + -p */
- g0 = h0 + 5;
- c = (g0 >> 44);
- g0 &= 0xfffffffffff;
- g1 = h1 + c;
- c = (g1 >> 44);
- g1 &= 0xfffffffffff;
- g2 = h2 + c - ((uint64_t) 1 << 42);
- /* select h if h < p, or h + -p if h >= p */
- c = (g2 >> ((sizeof(uint64_t) * 8) - 1)) - 1;
- g0 &= c;
- g1 &= c;
- g2 &= c;
- c = ~c;
- h0 = (h0 & c) | g0;
- h1 = (h1 & c) | g1;
- h2 = (h2 & c) | g2;
- /* h = (h + pad) */
- t0 = st->pad[0];
- t1 = st->pad[1];
- h0 += ((t0) & 0xfffffffffff);
- c = (h0 >> 44);
- h0 &= 0xfffffffffff;
- h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff) + c;
- c = (h1 >> 44);
- h1 &= 0xfffffffffff;
- h2 += (((t1 >> 24)) & 0x3ffffffffff) + c;
- h2 &= 0x3ffffffffff;
- /* mac = h % (2^128) */
- h0 = ((h0) | (h1 << 44));
- h1 = ((h1 >> 20) | (h2 << 24));
- U64TO8(&mac[0], h0);
- U64TO8(&mac[8], h1);
- /* zero out the state */
- st->h[0] = 0;
- st->h[1] = 0;
- st->h[2] = 0;
- st->r[0] = 0;
- st->r[1] = 0;
- st->r[2] = 0;
- st->pad[0] = 0;
- st->pad[1] = 0;
- }
- #endif
- static void poly1305_update(poly1305_context *ctx, const unsigned char *m,
- size_t bytes) {
- poly1305_state_internal_t *st = (poly1305_state_internal_t *) ctx;
- size_t i;
- /* handle leftover */
- if (st->leftover) {
- size_t want = (poly1305_block_size - st->leftover);
- if (want > bytes) want = bytes;
- for (i = 0; i < want; i++) st->buffer[st->leftover + i] = m[i];
- bytes -= want;
- m += want;
- st->leftover += want;
- if (st->leftover < poly1305_block_size) return;
- poly1305_blocks(st, st->buffer, poly1305_block_size);
- st->leftover = 0;
- }
- /* process full blocks */
- if (bytes >= poly1305_block_size) {
- size_t want = (bytes & (size_t) ~(poly1305_block_size - 1));
- poly1305_blocks(st, m, want);
- m += want;
- bytes -= want;
- }
- /* store leftover */
- if (bytes) {
- for (i = 0; i < bytes; i++) st->buffer[st->leftover + i] = m[i];
- st->leftover += bytes;
- }
- }
- // ******* END: poly1305-donna.c ********
- // ******* BEGIN: portable8439.c ********
- #define __CHACHA20_BLOCK_SIZE (64)
- #define __POLY1305_KEY_SIZE (32)
- static PORTABLE_8439_DECL uint8_t __ZEROES[16] = {0};
- static PORTABLE_8439_DECL void pad_if_needed(poly1305_context *ctx,
- size_t size) {
- size_t padding = size % 16;
- if (padding != 0) {
- poly1305_update(ctx, __ZEROES, 16 - padding);
- }
- }
- #define __u8(v) ((uint8_t) ((v) & 0xFF))
- // TODO: make this depending on the unaligned/native read size possible
- static PORTABLE_8439_DECL void write_64bit_int(poly1305_context *ctx,
- uint64_t value) {
- uint8_t result[8];
- result[0] = __u8(value);
- result[1] = __u8(value >> 8);
- result[2] = __u8(value >> 16);
- result[3] = __u8(value >> 24);
- result[4] = __u8(value >> 32);
- result[5] = __u8(value >> 40);
- result[6] = __u8(value >> 48);
- result[7] = __u8(value >> 56);
- poly1305_update(ctx, result, 8);
- }
- static PORTABLE_8439_DECL void poly1305_calculate_mac(
- uint8_t *mac, const uint8_t *cipher_text, size_t cipher_text_size,
- const uint8_t key[RFC_8439_KEY_SIZE],
- const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t *ad,
- size_t ad_size) {
- // init poly key (section 2.6)
- uint8_t poly_key[__POLY1305_KEY_SIZE] = {0};
- poly1305_context poly_ctx;
- rfc8439_keygen(poly_key, key, nonce);
- // start poly1305 mac
- poly1305_init(&poly_ctx, poly_key);
- if (ad != NULL && ad_size > 0) {
- // write AD if present
- poly1305_update(&poly_ctx, ad, ad_size);
- pad_if_needed(&poly_ctx, ad_size);
- }
- // now write the cipher text
- poly1305_update(&poly_ctx, cipher_text, cipher_text_size);
- pad_if_needed(&poly_ctx, cipher_text_size);
- // write sizes
- write_64bit_int(&poly_ctx, ad_size);
- write_64bit_int(&poly_ctx, cipher_text_size);
- // calculate MAC
- poly1305_finish(&poly_ctx, mac);
- }
- #define MG_PM(p) ((size_t) (p))
- // pointers overlap if the smaller either ahead of the end,
- // or its end is before the start of the other
- //
- // s_size should be smaller or equal to b_size
- #define MG_OVERLAPPING(s, s_size, b, b_size) \
- (MG_PM(s) < MG_PM((b) + (b_size))) && (MG_PM(b) < MG_PM((s) + (s_size)))
- PORTABLE_8439_DECL size_t mg_chacha20_poly1305_encrypt(
- uint8_t *restrict cipher_text, const uint8_t key[RFC_8439_KEY_SIZE],
- const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t *restrict ad,
- size_t ad_size, const uint8_t *restrict plain_text,
- size_t plain_text_size) {
- size_t new_size = plain_text_size + RFC_8439_TAG_SIZE;
- if (MG_OVERLAPPING(plain_text, plain_text_size, cipher_text, new_size)) {
- return (size_t) -1;
- }
- chacha20_xor_stream(cipher_text, plain_text, plain_text_size, key, nonce, 1);
- poly1305_calculate_mac(cipher_text + plain_text_size, cipher_text,
- plain_text_size, key, nonce, ad, ad_size);
- return new_size;
- }
- PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt(
- uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE],
- const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t *restrict ad,
- size_t ad_size, const uint8_t *restrict cipher_text,
- size_t cipher_text_size) {
- // first we calculate the mac and see if it lines up, only then do we decrypt
- size_t actual_size = cipher_text_size - RFC_8439_TAG_SIZE;
- uint8_t computed_mac[RFC_8439_TAG_SIZE];
- if (MG_OVERLAPPING(plain_text, actual_size, cipher_text, cipher_text_size)) {
- return (size_t) -1;
- }
- poly1305_calculate_mac(computed_mac, cipher_text, actual_size, key, nonce, ad,
- ad_size);
- if (!mg_memeq(computed_mac, cipher_text + actual_size, RFC_8439_TAG_SIZE))
- return (size_t) -1;
- chacha20_xor_stream(plain_text, cipher_text, actual_size, key, nonce, 1);
- return actual_size;
- }
- // ******* END: portable8439.c ********
- #endif // MG_TLS == MG_TLS_BUILTIN
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tls_dummy.c"
- #endif
- #if MG_TLS == MG_TLS_NONE
- 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;
- }
- void mg_tls_flush(struct mg_connection *c) {
- (void) c;
- }
- void mg_tls_ctx_init(struct mg_mgr *mgr) {
- (void) mgr;
- }
- void mg_tls_ctx_free(struct mg_mgr *mgr) {
- (void) mgr;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tls_mbed.c"
- #endif
- #if MG_TLS == MG_TLS_MBED
- #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000 && \
- MBEDTLS_VERSION_NUMBER < 0x04000000
- #define MG_MBEDTLS_RNG_GET , mg_mbed_rng, NULL
- #else
- #define MG_MBEDTLS_RNG_GET
- #endif
- static int mg_tls_err(struct mg_connection *c, int rc) {
- char s[80];
- mbedtls_strerror(rc, s, sizeof(s));
- MG_ERROR(("%lu %s", ((struct mg_connection *) c)->id, s));
- return rc;
- }
- #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x04000000
- #else
- static int mg_mbed_rng(void *ctx, unsigned char *buf, size_t len) {
- (void) ctx;
- return mg_random(buf, len) ? 0 : -1;
- }
- #endif
- static bool mg_load_cert(struct mg_str str, mbedtls_x509_crt *p) {
- int rc;
- if (str.buf == NULL || str.buf[0] == '\0' || str.buf[0] == '*') return true;
- if (!MG_IS_DER(str.buf)) str.len++; // PEM, include trailing NUL
- if ((rc = mbedtls_x509_crt_parse(p, (uint8_t *) str.buf, str.len)) != 0) {
- MG_ERROR(("cert err %#x", -rc));
- return false;
- }
- return true;
- }
- static bool mg_load_key(struct mg_str str, mbedtls_pk_context *p) {
- int rc;
- if (str.buf == NULL || str.buf[0] == '\0' || str.buf[0] == '*') return true;
- if (!MG_IS_DER(str.buf)) str.len++; // PEM, include trailing NUL
- if ((rc = mbedtls_pk_parse_key(p, (uint8_t *) str.buf, str.len, NULL,
- 0 MG_MBEDTLS_RNG_GET)) != 0) {
- MG_ERROR(("key err %#x", -rc));
- return false;
- }
- return true;
- }
- void mg_tls_free(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- if (tls != NULL) {
- 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);
- #ifdef MBEDTLS_SSL_SESSION_TICKETS
- mbedtls_ssl_ticket_free(&tls->ticket);
- #endif
- // PSA has global data. Do not call mbedtls_psa_crypto_free() here,
- // it will free all global resources. Call it when actually freeing all
- // application resources (main() exits)
- mg_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_READ;
- // 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
- if (tls->check_name && (mbedtls_ssl_get_verify_result(&tls->ssl) &
- MBEDTLS_X509_BADCERT_CN_MISMATCH)) {
- mg_error(c, "failed to verify hostname");
- return;
- } // ignore MBEDTLS_X509_BADCERT_NOT_TRUSTED, no CA cert given
- 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", -mg_tls_err(c, rc)); // Error
- }
- }
- 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;
- }
- void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
- struct mg_tls *tls = (struct mg_tls *) mg_calloc(1, sizeof(*tls));
- int rc = 0;
- bool check_name = false;
- c->tls = tls;
- if (c->tls == NULL) {
- mg_error(c, "TLS OOM");
- goto fail;
- }
- if (c->is_listening) goto fail;
- MG_DEBUG(("%lu Setting TLS", c->id));
- MG_PROF_ADD(c, "mbedtls_init_start");
- 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", -mg_tls_err(c, rc));
- goto fail;
- }
- #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x04000000
- MG_INFO(("PSA is in control of random number generation"));
- #else
- mbedtls_ssl_conf_rng(&tls->conf, mg_mbed_rng, c);
- #endif
- if (c->is_client && opts->name.buf != NULL && opts->name.len > 0 &&
- opts->name.buf[0] != '\0') {
- char *host = mg_mprintf("%.*s", opts->name.len, opts->name.buf);
- mbedtls_ssl_set_hostname(&tls->ssl, host);
- #if !defined(MBEDTLS_VERSION_NUMBER) || MBEDTLS_VERSION_NUMBER < 0x03030000 || \
- MBEDTLS_VERSION_NUMBER >= 0x04000000
- // NOTE: MBEDTLS_SSL_VERIFY_OPTIONAL is not supported for TLS1.3 on client
- // side See https://github.com/Mbed-TLS/mbedtls/issues/7075
- check_name = true;
- #endif
- MG_DEBUG(("%lu hostname verification: %s", c->id, host));
- mg_free(host);
- } else {
- MG_DEBUG(("%lu skipping hostname verification", c->id));
- mbedtls_ssl_set_hostname(&tls->ssl, NULL);
- }
- if (opts->ca.len == 0 || mg_strcmp(opts->ca, mg_str("*")) == 0) {
- mbedtls_ssl_conf_authmode(&tls->conf, check_name // see set comment above
- ? MBEDTLS_SSL_VERIFY_OPTIONAL
- : MBEDTLS_SSL_VERIFY_NONE);
- tls->check_name = check_name; // host name set but no CA cert given
- } else {
- if (mg_load_cert(opts->ca, &tls->ca) == false) goto fail;
- mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->ca, NULL);
- mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
- }
- if (!mg_load_cert(opts->cert, &tls->cert)) goto fail;
- if (!mg_load_key(opts->key, &tls->pk)) goto fail;
- if (tls->cert.version &&
- (rc = mbedtls_ssl_conf_own_cert(&tls->conf, &tls->cert, &tls->pk)) != 0) {
- mg_error(c, "own cert %#x", -mg_tls_err(c, rc));
- goto fail;
- }
- #ifdef MBEDTLS_SSL_SESSION_TICKETS
- if (!c->is_client && c->mgr->tls_ctx != NULL) {
- mbedtls_ssl_conf_session_tickets_cb(
- &tls->conf, mbedtls_ssl_ticket_write, mbedtls_ssl_ticket_parse,
- &((struct mg_tls_ctx *) c->mgr->tls_ctx)->tickets);
- }
- #endif
- if ((rc = mbedtls_ssl_setup(&tls->ssl, &tls->conf)) != 0) {
- mg_error(c, "setup err %#x", -mg_tls_err(c, rc));
- goto fail;
- }
- c->is_tls = 1;
- c->is_tls_hs = 1;
- mbedtls_ssl_set_bio(&tls->ssl, c, mg_net_send, mg_net_recv, 0);
- MG_PROF_ADD(c, "mbedtls_init_end");
- 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 (!c->is_tls_hs && (buf == NULL || len == 0) && n == 0) return 0; // MIP
- if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE)
- return MG_IO_WAIT;
- #if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
- if (n == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) {
- return MG_IO_WAIT;
- }
- #endif
- 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;
- bool was_throttled = c->is_tls_throttled; // see #3074
- n = was_throttled ? mbedtls_ssl_write(&tls->ssl, tls->throttled_buf,
- tls->throttled_len) /* flush old data */
- : mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf,
- len); // encrypt current data
- #if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
- if (n == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
- return MG_IO_WAIT;
- #endif
- c->is_tls_throttled =
- (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE);
- if (was_throttled && c->is_tls_throttled) return MG_IO_WAIT; // no changes
- if (c->is_tls_throttled) {
- tls->throttled_buf =
- (unsigned char *) buf; // MbedTLS code actually ignores
- tls->throttled_len = len; // these, but let's play API rules
- return MG_IO_WAIT;
- }
- if (n <= 0) return MG_IO_ERR;
- return n;
- }
- void mg_tls_flush(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- if (c->is_tls_throttled && c->is_draining) {
- long n =
- mbedtls_ssl_write(&tls->ssl, tls->throttled_buf, tls->throttled_len);
- #if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
- if (n == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) return;
- #endif
- c->is_tls_throttled =
- (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE);
- }
- }
- void mg_tls_ctx_init(struct mg_mgr *mgr) {
- #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000 && \
- defined(MBEDTLS_PSA_CRYPTO_C)
- psa_crypto_init(); // Initializes global PSA resources, no-op if already done
- #endif
- #ifdef MBEDTLS_SSL_SESSION_TICKETS
- {
- struct mg_tls_ctx *ctx = (struct mg_tls_ctx *) mg_calloc(1, sizeof(*ctx));
- if (ctx == NULL) {
- MG_ERROR(("TLS context init OOM"));
- } else {
- int rc;
- mbedtls_ssl_ticket_init(&ctx->tickets);
- #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x04000000
- rc = mbedtls_ssl_ticket_setup(&ctx->tickets, PSA_ALG_GCM,
- PSA_KEY_TYPE_AES, 128, 86400);
- #else
- rc = mbedtls_ssl_ticket_setup(&ctx->tickets, mg_mbed_rng, NULL,
- MBEDTLS_CIPHER_AES_128_GCM, 86400);
- #endif
- if (rc != 0) {
- MG_ERROR((" mbedtls_ssl_ticket_setup %#x", -rc));
- mbedtls_ssl_ticket_free(&ctx->tickets);
- mg_free(ctx);
- } else {
- mgr->tls_ctx = ctx;
- }
- }
- }
- #else
- (void) mgr;
- #endif
- }
- void mg_tls_ctx_free(struct mg_mgr *mgr) {
- struct mg_tls_ctx *ctx = (struct mg_tls_ctx *) mgr->tls_ctx;
- if (ctx != NULL) {
- #ifdef MBEDTLS_SSL_SESSION_TICKETS
- mbedtls_ssl_ticket_free(&ctx->tickets);
- #endif
- mg_free(ctx);
- mgr->tls_ctx = NULL;
- }
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tls_openssl.c"
- #endif
- #if MG_TLS == MG_TLS_OPENSSL || MG_TLS == MG_TLS_WOLFSSL
- static int tls_err_cb(const char *s, size_t len, void *c) {
- int n = (int) len - 1;
- MG_ERROR(("%lu %.*s", ((struct mg_connection *) c)->id, n, s));
- return 0; // undocumented
- }
- static int mg_tls_err(struct mg_connection *c, 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_cb(tls_err_cb, c);
- ERR_clear_error();
- if (err == SSL_ERROR_WANT_READ) return 0;
- if (err == SSL_ERROR_WANT_WRITE) return 0;
- return err;
- }
- #if MG_TLS != MG_TLS_WOLFSSL
- static STACK_OF(X509_INFO) * load_ca_certs(struct mg_str ca) {
- BIO *bio = BIO_new_mem_buf(ca.buf, (int) ca.len);
- STACK_OF(X509_INFO) *certs =
- bio ? PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL) : NULL;
- if (bio) BIO_free(bio);
- return certs;
- }
- static bool add_ca_certs(SSL_CTX *ctx, STACK_OF(X509_INFO) * certs) {
- int i;
- X509_STORE *cert_store = SSL_CTX_get_cert_store(ctx);
- if (cert_store == NULL) return false;
- for (i = 0; i < sk_X509_INFO_num(certs); i++) {
- X509_INFO *cert_info = sk_X509_INFO_value(certs, i);
- if (cert_info->x509 && !X509_STORE_add_cert(cert_store, cert_info->x509))
- return false;
- }
- return true;
- }
- #endif
- static EVP_PKEY *load_key(struct mg_str s) {
- BIO *bio = BIO_new_mem_buf(s.buf, (int) (long) s.len);
- EVP_PKEY *key = bio ? PEM_read_bio_PrivateKey(bio, NULL, 0, NULL) : NULL;
- if (bio) BIO_free(bio);
- return key;
- }
- static int load_cert(SSL *ssl, struct mg_str s) {
- BIO *bio = BIO_new_mem_buf(s.buf, (int) (long) s.len);
- X509 *cert = NULL;
- int rc = 0;
- if (bio == NULL) return 0;
- if (MG_IS_DER(s.buf)) {
- cert = d2i_X509_bio(bio, NULL);
- rc = cert == NULL ? 0 : SSL_use_certificate(ssl, cert);
- } else {
- cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
- rc = cert == NULL ? 0 : SSL_use_certificate(ssl, cert);
- #if MG_TLS != MG_TLS_WOLFSSL
- X509_free(cert);
- while (rc == 1) {
- cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
- if (cert == NULL) {
- ERR_clear_error(); // PEM_read_bio_X509 sets an error on EOF
- break;
- }
- rc = (int) SSL_add1_chain_cert(ssl, cert);
- X509_free(cert);
- }
- cert = NULL;
- #endif
- }
- X509_free(cert);
- if (bio) BIO_free(bio);
- return rc;
- }
- static long mg_bio_ctrl(BIO *b, int cmd, long larg, void *pargs) {
- long ret = 0;
- if (cmd == BIO_CTRL_PUSH) ret = 1;
- if (cmd == BIO_CTRL_POP) ret = 1;
- if (cmd == BIO_CTRL_FLUSH) ret = 1;
- #if MG_TLS == MG_TLS_OPENSSL
- if (cmd == BIO_C_SET_NBIO) ret = 1;
- #endif
- // MG_DEBUG(("%d -> %ld", cmd, ret));
- (void) b, (void) cmd, (void) larg, (void) pargs;
- return ret;
- }
- static int mg_bio_read(BIO *bio, char *buf, int len) {
- struct mg_connection *c = (struct mg_connection *) BIO_get_data(bio);
- long res = mg_io_recv(c, buf, (size_t) len);
- // MG_DEBUG(("%p %d %ld", buf, len, res));
- len = res > 0 ? (int) res : -1;
- if (res == MG_IO_WAIT) BIO_set_retry_read(bio);
- return len;
- }
- static int mg_bio_write(BIO *bio, const char *buf, int len) {
- struct mg_connection *c = (struct mg_connection *) BIO_get_data(bio);
- long res = mg_io_send(c, buf, (size_t) len);
- // MG_DEBUG(("%p %d %ld", buf, len, res));
- len = res > 0 ? (int) res : -1;
- if (res == MG_IO_WAIT) BIO_set_retry_write(bio);
- return len;
- }
- #ifdef MG_TLS_SSLKEYLOGFILE
- static void ssl_keylog_cb(const SSL *ssl, const char *line) {
- FILE *f;
- char *keylogfile = getenv("SSLKEYLOGFILE");
- if (keylogfile == NULL) return;
- f = fopen(keylogfile, "a");
- if (f != NULL) {
- fprintf(f, "%s\n", line);
- fflush(f);
- fclose(f);
- } else {
- MG_ERROR(("Cannot open %s", keylogfile));
- }
- (void) ssl;
- }
- #endif
- 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);
- BIO_meth_free(tls->bm);
- mg_free(tls->name);
- mg_free(tls);
- c->tls = NULL;
- }
- void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
- struct mg_tls *tls = (struct mg_tls *) mg_calloc(1, sizeof(*tls));
- const char *id = "mongoose";
- bool check_name = false;
- static unsigned char s_initialised = 0;
- BIO *bio = NULL;
- int rc;
- c->tls = tls;
- if (tls == NULL) {
- mg_error(c, "TLS OOM");
- goto fail;
- }
- if (!s_initialised) {
- SSL_library_init();
- s_initialised++;
- }
- MG_DEBUG(("%lu Setting TLS", c->id));
- tls->ctx = c->is_client ? SSL_CTX_new(TLS_client_method())
- : SSL_CTX_new(TLS_server_method());
- if (tls->ctx == NULL) {
- mg_error(c, "SSL_CTX_new");
- goto fail;
- }
- #ifdef MG_TLS_SSLKEYLOGFILE
- SSL_CTX_set_keylog_callback(tls->ctx, ssl_keylog_cb);
- #endif
- 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 (c->is_client && opts->name.buf != NULL && opts->name.len > 0 && opts->name.buf[0] != '\0') {
- tls->name = mg_mprintf("%.*s", (int) opts->name.len, opts->name.buf);
- if (tls->name == NULL) {
- mg_error(c, "TLS OOM");
- goto fail;
- }
- check_name = true;
- }
- #if MG_TLS == MG_TLS_WOLFSSL && !defined(OPENSSL_COMPATIBLE_DEFAULTS)
- if (opts->ca.len == 0 || mg_strcmp(opts->ca, mg_str("*")) == 0) {
- // Older versions require that either the CA is loaded or SSL_VERIFY_NONE
- // explicitly set
- SSL_set_verify(tls->ssl, SSL_VERIFY_NONE, NULL);
- }
- #endif
- if (opts->ca.buf != NULL && opts->ca.len > 0 && opts->ca.buf[0] != '\0') {
- SSL_set_verify(tls->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
- NULL);
- #if MG_TLS == MG_TLS_WOLFSSL
- extern int wolfSSL_CTX_load_verify_buffer(SSL_CTX *, const unsigned char *,
- long, int);
- rc = wolfSSL_CTX_load_verify_buffer(tls->ctx,
- (const unsigned char *) opts->ca.buf,
- (long) opts->ca.len, SSL_FILETYPE_PEM);
- if (rc != 1) {
- mg_error(c, "CA err");
- goto fail;
- }
- #else
- STACK_OF(X509_INFO) *certs = load_ca_certs(opts->ca);
- rc = add_ca_certs(tls->ctx, certs);
- sk_X509_INFO_pop_free(certs, X509_INFO_free);
- if (!rc) {
- mg_error(c, "CA err");
- goto fail;
- }
- #endif
- } else {
- tls->check_name = check_name; // host name set but no CA cert given
- }
- if (opts->cert.buf != NULL && opts->cert.buf[0] != '\0') {
- rc = load_cert(tls->ssl, opts->cert);
- if (rc != 1) {
- mg_error(c, "CERT err %d", mg_tls_err(c, tls, rc));
- goto fail;
- }
- }
- if (opts->key.buf != NULL && opts->key.buf[0] != '\0') {
- EVP_PKEY *key = load_key(opts->key);
- rc = key == NULL ? 0 : SSL_use_PrivateKey(tls->ssl, key);
- EVP_PKEY_free(key);
- if (key == NULL || rc != 1) {
- mg_error(c, "KEY err %d", mg_tls_err(c, tls, rc));
- goto fail;
- }
- }
- SSL_set_mode(tls->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- #if MG_TLS == MG_TLS_OPENSSL && OPENSSL_VERSION_NUMBER > 0x10002000L
- (void) SSL_set_ecdh_auto(tls->ssl, 1);
- #endif
- #if OPENSSL_VERSION_NUMBER >= 0x10100000L
- if (tls->name != NULL) {
- #if MG_TLS != MG_TLS_WOLFSSL || LIBWOLFSSL_VERSION_HEX >= 0x05005002
- SSL_set1_host(tls->ssl, tls->name);
- #else
- X509_VERIFY_PARAM_set1_host(SSL_get0_param(tls->ssl), tls->name, 0);
- #endif
- SSL_set_tlsext_host_name(tls->ssl, tls->name);
- }
- #endif
- #if MG_TLS == MG_TLS_WOLFSSL
- tls->bm = BIO_meth_new(0, "bio_mg");
- #else
- tls->bm = BIO_meth_new(BIO_get_new_index() | BIO_TYPE_SOURCE_SINK, "bio_mg");
- #endif
- BIO_meth_set_write(tls->bm, mg_bio_write);
- BIO_meth_set_read(tls->bm, mg_bio_read);
- BIO_meth_set_ctrl(tls->bm, mg_bio_ctrl);
- bio = BIO_new(tls->bm);
- BIO_set_data(bio, c);
- SSL_set_bio(tls->ssl, bio, bio);
- c->is_tls = 1;
- c->is_tls_hs = 1;
- MG_DEBUG(("%lu SSL %s OK", c->id, c->is_accepted ? "accept" : "client"));
- return;
- fail:
- mg_tls_free(c);
- }
- void mg_tls_handshake(struct mg_connection *c) {
- struct mg_tls *tls = (struct mg_tls *) c->tls;
- int rc = c->is_client ? SSL_connect(tls->ssl) : SSL_accept(tls->ssl);
- if (rc == 1) {
- if (tls->check_name) { // host name set but no CA cert given
- X509 *cert = SSL_get_peer_certificate(tls->ssl);
- bool ok =
- cert != NULL && X509_check_host(cert, tls->name, 0, 0, NULL) == 1;
- X509_free(cert);
- if (!ok) {
- mg_error(c, "failed to verify hostname");
- return;
- }
- }
- 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(c, tls, rc);
- if (code != 0) mg_error(c, "tls hs: rc %d, err %d", rc, code);
- }
- }
- 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;
- #if MG_TLS == MG_TLS_WOLFSSL
- uint8_t dummy; // WolfSSL requires destination != NULL
- if (buf == NULL && len == 0) buf = &dummy;
- #endif
- n = SSL_read(tls->ssl, buf, (int) len);
- if (!c->is_tls_hs && (buf == NULL || len == 0) && n == 0) return 0; // MIP
- if (n < 0 && mg_tls_err(c, 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(c, tls, n) == 0) return MG_IO_WAIT;
- if (n <= 0) return MG_IO_ERR;
- return n;
- }
- void mg_tls_flush(struct mg_connection *c) {
- (void) c;
- }
- void mg_tls_ctx_init(struct mg_mgr *mgr) {
- (void) mgr;
- }
- void mg_tls_ctx_free(struct mg_mgr *mgr) {
- (void) mgr;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tls_rsa.c"
- #endif
- #if MG_TLS == MG_TLS_BUILTIN
- /*
- * The RSA bigint backend below is derived from BearSSL's i31 RSA code.
- *
- * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
- #define BR_MAX_RSA_SIZE 4096
- #define BR_MAX_RSA_FACTOR ((BR_MAX_RSA_SIZE + 64) >> 1)
- #define BR_I31_WORDS(bits) (2 + (((bits) + 30) / 31))
- #define BR_RSA_WORDS BR_I31_WORDS(BR_MAX_RSA_SIZE)
- #define BR_RSA_FACTOR_WORDS BR_I31_WORDS(BR_MAX_RSA_FACTOR)
- #define BR_64 1
- #define MUL31(x, y) ((uint64_t) (x) * (uint64_t) (y))
- #define MUL31_lo(x, y) (((uint32_t) (x) * (uint32_t) (y)) & 0x7fffffffU)
- #define CCOPY br_ccopy
- static uint32_t NOT(uint32_t ctl) { return ctl ^ 1; }
- static uint32_t MUX(uint32_t ctl, uint32_t x, uint32_t y) {
- return y ^ (-ctl & (x ^ y));
- }
- static uint32_t EQ(uint32_t x, uint32_t y) {
- uint32_t q = x ^ y;
- return NOT((q | -q) >> 31);
- }
- static uint32_t NEQ(uint32_t x, uint32_t y) {
- uint32_t q = x ^ y;
- return (q | -q) >> 31;
- }
- static uint32_t GT(uint32_t x, uint32_t y) {
- uint32_t z = y - x;
- return (z ^ ((x ^ y) & (x ^ z))) >> 31;
- }
- #define GE(x, y) NOT(GT(y, x))
- #define LT(x, y) GT(y, x)
- static uint32_t BIT_LENGTH(uint32_t x) {
- uint32_t k, c;
- k = NEQ(x, 0);
- c = GT(x, 0xffff); x = MUX(c, x >> 16, x); k += c << 4;
- c = GT(x, 0x00ff); x = MUX(c, x >> 8, x); k += c << 3;
- c = GT(x, 0x000f); x = MUX(c, x >> 4, x); k += c << 2;
- c = GT(x, 0x0003); x = MUX(c, x >> 2, x); k += c << 1;
- k += GT(x, 0x0001);
- return k;
- }
- static void br_enc32be(void *dst, uint32_t x) {
- unsigned char *buf = (unsigned char *) dst;
- buf[0] = (unsigned char) (x >> 24);
- buf[1] = (unsigned char) (x >> 16);
- buf[2] = (unsigned char) (x >> 8);
- buf[3] = (unsigned char) x;
- }
- static void br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len) {
- unsigned char *d = (unsigned char *) dst;
- const unsigned char *s = (const unsigned char *) src;
- while (len-- > 0) {
- uint32_t x = *s++, y = *d;
- *d++ = (unsigned char) MUX(ctl, x, y);
- }
- }
- static uint32_t br_i31_add(uint32_t *, const uint32_t *, uint32_t);
- static uint32_t br_i31_sub(uint32_t *, const uint32_t *, uint32_t);
- static uint32_t br_i31_bit_length(uint32_t *, size_t);
- static void br_i31_decode(uint32_t *, const void *, size_t);
- static void br_i31_encode(void *, size_t, const uint32_t *);
- static uint32_t br_i31_ninv31(uint32_t);
- static uint32_t br_divrem(uint32_t, uint32_t, uint32_t, uint32_t *);
- static void br_i31_muladd_small(uint32_t *, uint32_t, const uint32_t *);
- static void br_i31_reduce(uint32_t *, const uint32_t *, const uint32_t *);
- static void br_i31_rshift(uint32_t *, int);
- static void br_i31_decode_reduce(uint32_t *, const void *, size_t, const uint32_t *);
- static void br_i31_mulacc(uint32_t *, const uint32_t *, const uint32_t *);
- static void br_i31_montymul(uint32_t *, const uint32_t *, const uint32_t *, const uint32_t *, uint32_t);
- static void br_i31_to_monty(uint32_t *, const uint32_t *);
- static void br_i31_modpow(uint32_t *, const unsigned char *, size_t, const uint32_t *, uint32_t, uint32_t *, uint32_t *);
- static void br_i31_zero(uint32_t *x, uint32_t bit_len) {
- *x++ = bit_len;
- memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x);
- }
- static uint32_t br_rem(uint32_t hi, uint32_t lo, uint32_t d) {
- uint32_t r;
- br_divrem(hi, lo, d, &r);
- return r;
- }
- static uint32_t br_div(uint32_t hi, uint32_t lo, uint32_t d) {
- uint32_t r;
- return br_divrem(hi, lo, d, &r);
- }
- static uint32_t
- br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl)
- {
- uint32_t cc;
- size_t u, m;
- cc = 0;
- m = (a[0] + 63) >> 5;
- for (u = 1; u < m; u ++) {
- uint32_t aw, bw, naw;
- aw = a[u];
- bw = b[u];
- naw = aw + bw + cc;
- cc = naw >> 31;
- a[u] = MUX(ctl, naw & (uint32_t)0x7FFFFFFF, aw);
- }
- return cc;
- }
- static uint32_t
- br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl)
- {
- uint32_t cc;
- size_t u, m;
- cc = 0;
- m = (a[0] + 63) >> 5;
- for (u = 1; u < m; u ++) {
- uint32_t aw, bw, naw;
- aw = a[u];
- bw = b[u];
- naw = aw - bw - cc;
- cc = naw >> 31;
- a[u] = MUX(ctl, naw & 0x7FFFFFFF, aw);
- }
- return cc;
- }
- static uint32_t
- br_i31_bit_length(uint32_t *x, size_t xlen)
- {
- uint32_t tw, twk;
- tw = 0;
- twk = 0;
- while (xlen -- > 0) {
- uint32_t w, c;
- c = EQ(tw, 0);
- w = x[xlen];
- tw = MUX(c, w, tw);
- twk = MUX(c, (uint32_t)xlen, twk);
- }
- return (twk << 5) + BIT_LENGTH(tw);
- }
- static void
- br_i31_decode(uint32_t *x, const void *src, size_t len)
- {
- const unsigned char *buf;
- size_t u, v;
- uint32_t acc;
- int acc_len;
- buf = (const unsigned char *) src;
- u = len;
- v = 1;
- acc = 0;
- acc_len = 0;
- while (u -- > 0) {
- uint32_t b;
- b = buf[u];
- acc |= (b << acc_len);
- acc_len += 8;
- if (acc_len >= 31) {
- x[v ++] = acc & (uint32_t)0x7FFFFFFF;
- acc_len -= 31;
- acc = b >> (8 - acc_len);
- }
- }
- if (acc_len != 0) {
- x[v ++] = acc;
- }
- x[0] = br_i31_bit_length(x + 1, v - 1);
- }
- static void
- br_i31_encode(void *dst, size_t len, const uint32_t *x)
- {
- unsigned char *buf;
- size_t k, xlen;
- uint32_t acc;
- int acc_len;
- xlen = (x[0] + 31) >> 5;
- if (xlen == 0) {
- memset(dst, 0, len);
- return;
- }
- buf = (unsigned char *)dst + len;
- k = 1;
- acc = 0;
- acc_len = 0;
- while (len != 0) {
- uint32_t w;
- w = (k <= xlen) ? x[k] : 0;
- k ++;
- if (acc_len == 0) {
- acc = w;
- acc_len = 31;
- } else {
- uint32_t z;
- z = acc | (w << acc_len);
- acc_len --;
- acc = w >> (31 - acc_len);
- if (len >= 4) {
- buf -= 4;
- len -= 4;
- br_enc32be(buf, z);
- } else {
- switch (len) {
- case 3:
- buf[-3] = (unsigned char)(z >> 16);
- /* fall through */
- case 2:
- buf[-2] = (unsigned char)(z >> 8);
- /* fall through */
- case 1:
- buf[-1] = (unsigned char)z;
- break;
- }
- return;
- }
- }
- }
- }
- static uint32_t
- br_i31_ninv31(uint32_t x)
- {
- uint32_t y;
- y = 2 - x;
- y *= 2 - y * x;
- y *= 2 - y * x;
- y *= 2 - y * x;
- y *= 2 - y * x;
- return MUX(x & 1, -y, 0) & 0x7FFFFFFF;
- }
- static uint32_t
- br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r)
- {
- /* TODO: optimize this */
- uint32_t q;
- uint32_t ch, cf;
- int k;
- q = 0;
- ch = EQ(hi, d);
- hi = MUX(ch, 0, hi);
- for (k = 31; k > 0; k --) {
- int j;
- uint32_t w, ctl, hi2, lo2;
- j = 32 - k;
- w = (hi << j) | (lo >> k);
- ctl = GE(w, d) | (hi >> k);
- hi2 = (w - d) >> j;
- lo2 = lo - (d << k);
- hi = MUX(ctl, hi2, hi);
- lo = MUX(ctl, lo2, lo);
- q |= ctl << k;
- }
- cf = GE(lo, d) | hi;
- q |= cf;
- *r = MUX(cf, lo - d, lo);
- return q;
- }
- static void
- br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m)
- {
- uint32_t m_bitlen;
- unsigned mblr;
- size_t u, mlen;
- uint32_t a0, a1, b0, hi, g, q, tb;
- uint32_t under, over;
- uint32_t cc;
- /*
- * We can test on the modulus bit length since we accept to
- * leak that length.
- */
- m_bitlen = m[0];
- if (m_bitlen == 0) {
- return;
- }
- if (m_bitlen <= 31) {
- uint32_t lo;
- hi = x[1] >> 1;
- lo = (x[1] << 31) | z;
- x[1] = br_rem(hi, lo, m[1]);
- return;
- }
- mlen = (m_bitlen + 31) >> 5;
- mblr = (unsigned)m_bitlen & 31;
- /*
- * Principle: we estimate the quotient (x*2^31+z)/m by
- * doing a 64/32 division with the high words.
- *
- * Let:
- * w = 2^31
- * a = (w*a0 + a1) * w^N + a2
- * b = b0 * w^N + b2
- * such that:
- * 0 <= a0 < w
- * 0 <= a1 < w
- * 0 <= a2 < w^N
- * w/2 <= b0 < w
- * 0 <= b2 < w^N
- * a < w*b
- * I.e. the two top words of a are a0:a1, the top word of b is
- * b0, we ensured that b0 is "full" (high bit set), and a is
- * such that the quotient q = a/b fits on one word (0 <= q < w).
- *
- * If a = b*q + r (with 0 <= r < q), we can estimate q by
- * doing an Euclidean division on the top words:
- * a0*w+a1 = b0*u + v (with 0 <= v < b0)
- * Then the following holds:
- * 0 <= u <= w
- * u-2 <= q <= u
- */
- hi = x[mlen];
- if (mblr == 0) {
- a0 = x[mlen];
- memmove(x + 2, x + 1, (mlen - 1) * sizeof *x);
- x[1] = z;
- a1 = x[mlen];
- b0 = m[mlen];
- } else {
- a0 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr))
- & 0x7FFFFFFF;
- memmove(x + 2, x + 1, (mlen - 1) * sizeof *x);
- x[1] = z;
- a1 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr))
- & 0x7FFFFFFF;
- b0 = ((m[mlen] << (31 - mblr)) | (m[mlen - 1] >> mblr))
- & 0x7FFFFFFF;
- }
- /*
- * We estimate a divisor q. If the quotient returned by br_div()
- * is g:
- * -- If a0 == b0 then g == 0; we want q = 0x7FFFFFFF.
- * -- Otherwise:
- * -- if g == 0 then we set q = 0;
- * -- otherwise, we set q = g - 1.
- * The properties described above then ensure that the true
- * quotient is q-1, q or q+1.
- *
- * Take care that a0, a1 and b0 are 31-bit words, not 32-bit. We
- * must adjust the parameters to br_div() accordingly.
- */
- g = br_div(a0 >> 1, a1 | (a0 << 31), b0);
- q = MUX(EQ(a0, b0), 0x7FFFFFFF, MUX(EQ(g, 0), 0, g - 1));
- /*
- * We subtract q*m from x (with the extra high word of value 'hi').
- * Since q may be off by 1 (in either direction), we may have to
- * add or subtract m afterwards.
- *
- * The 'tb' flag will be true (1) at the end of the loop if the
- * result is greater than or equal to the modulus (not counting
- * 'hi' or the carry).
- */
- cc = 0;
- tb = 1;
- for (u = 1; u <= mlen; u ++) {
- uint32_t mw, zw, xw, nxw;
- uint64_t zl;
- mw = m[u];
- zl = MUL31(mw, q) + cc;
- cc = (uint32_t)(zl >> 31);
- zw = (uint32_t)zl & (uint32_t)0x7FFFFFFF;
- xw = x[u];
- nxw = xw - zw;
- cc += nxw >> 31;
- nxw &= 0x7FFFFFFF;
- x[u] = nxw;
- tb = MUX(EQ(nxw, mw), tb, GT(nxw, mw));
- }
- /*
- * If we underestimated q, then either cc < hi (one extra bit
- * beyond the top array word), or cc == hi and tb is true (no
- * extra bit, but the result is not lower than the modulus). In
- * these cases we must subtract m once.
- *
- * Otherwise, we may have overestimated, which will show as
- * cc > hi (thus a negative result). Correction is adding m once.
- */
- over = GT(cc, hi);
- under = ~over & (tb | LT(cc, hi));
- br_i31_add(x, m, over);
- br_i31_sub(x, m, under);
- }
- static void
- br_i31_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m)
- {
- uint32_t m_bitlen, a_bitlen;
- size_t mlen, alen, u;
- m_bitlen = m[0];
- mlen = (m_bitlen + 31) >> 5;
- x[0] = m_bitlen;
- if (m_bitlen == 0) {
- return;
- }
- /*
- * If the source is shorter, then simply copy all words from a[]
- * and zero out the upper words.
- */
- a_bitlen = a[0];
- alen = (a_bitlen + 31) >> 5;
- if (a_bitlen < m_bitlen) {
- memcpy(x + 1, a + 1, alen * sizeof *a);
- for (u = alen; u < mlen; u ++) {
- x[u + 1] = 0;
- }
- return;
- }
- /*
- * The source length is at least equal to that of the modulus.
- * We must thus copy N-1 words, and input the remaining words
- * one by one.
- */
- memcpy(x + 1, a + 2 + (alen - mlen), (mlen - 1) * sizeof *a);
- x[mlen] = 0;
- for (u = 1 + alen - mlen; u > 0; u --) {
- br_i31_muladd_small(x, a[u], m);
- }
- }
- static void
- br_i31_rshift(uint32_t *x, int count)
- {
- size_t u, len;
- uint32_t r;
- len = (x[0] + 31) >> 5;
- if (len == 0) {
- return;
- }
- r = x[1] >> count;
- for (u = 2; u <= len; u ++) {
- uint32_t w;
- w = x[u];
- x[u - 1] = ((w << (31 - count)) | r) & 0x7FFFFFFF;
- r = w >> count;
- }
- x[len] = r;
- }
- static void
- br_i31_decode_reduce(uint32_t *x,
- const void *src, size_t len, const uint32_t *m)
- {
- uint32_t m_ebitlen, m_rbitlen;
- size_t mblen, k;
- const unsigned char *buf;
- uint32_t acc;
- int acc_len;
- /*
- * Get the encoded bit length.
- */
- m_ebitlen = m[0];
- /*
- * Special case for an invalid (null) modulus.
- */
- if (m_ebitlen == 0) {
- x[0] = 0;
- return;
- }
- /*
- * Clear the destination.
- */
- br_i31_zero(x, m_ebitlen);
- /*
- * First decode directly as many bytes as possible. This requires
- * computing the actual bit length.
- */
- m_rbitlen = m_ebitlen >> 5;
- m_rbitlen = (m_ebitlen & 31) + (m_rbitlen << 5) - m_rbitlen;
- mblen = (m_rbitlen + 7) >> 3;
- k = mblen - 1;
- if (k >= len) {
- br_i31_decode(x, src, len);
- x[0] = m_ebitlen;
- return;
- }
- buf = (const unsigned char *) src;
- br_i31_decode(x, buf, k);
- x[0] = m_ebitlen;
- /*
- * Input remaining bytes, using 31-bit words.
- */
- acc = 0;
- acc_len = 0;
- while (k < len) {
- uint32_t v;
- v = buf[k ++];
- if (acc_len >= 23) {
- acc_len -= 23;
- acc <<= (8 - acc_len);
- acc |= v >> acc_len;
- br_i31_muladd_small(x, acc, m);
- acc = v & (0xFF >> (8 - acc_len));
- } else {
- acc = (acc << 8) | v;
- acc_len += 8;
- }
- }
- /*
- * We may have some bits accumulated. We then perform a shift to
- * be able to inject these bits as a full 31-bit word.
- */
- if (acc_len != 0) {
- acc = (acc | (x[1] << acc_len)) & 0x7FFFFFFF;
- br_i31_rshift(x, 31 - acc_len);
- br_i31_muladd_small(x, acc, m);
- }
- }
- static void
- br_i31_mulacc(uint32_t *d, const uint32_t *a, const uint32_t *b)
- {
- size_t alen, blen, u;
- uint32_t dl, dh;
- alen = (a[0] + 31) >> 5;
- blen = (b[0] + 31) >> 5;
- /*
- * We want to add the two bit lengths, but these are encoded,
- * which requires some extra care.
- */
- dl = (a[0] & 31) + (b[0] & 31);
- dh = (a[0] >> 5) + (b[0] >> 5);
- d[0] = (dh << 5) + dl + (~(uint32_t)(dl - 31) >> 31);
- for (u = 0; u < blen; u ++) {
- uint32_t f;
- size_t v;
- /*
- * Carry always fits on 31 bits; we want to keep it in a
- * 32-bit register on 32-bit architectures (on a 64-bit
- * architecture, cast down from 64 to 32 bits means
- * clearing the high bits, which is not free; on a 32-bit
- * architecture, the same operation really means ignoring
- * the top register, which has negative or zero cost).
- */
- #if BR_64
- uint64_t cc;
- #else
- uint32_t cc;
- #endif
- f = b[1 + u];
- cc = 0;
- for (v = 0; v < alen; v ++) {
- uint64_t z;
- z = (uint64_t)d[1 + u + v] + MUL31(f, a[1 + v]) + cc;
- cc = z >> 31;
- d[1 + u + v] = (uint32_t)z & 0x7FFFFFFF;
- }
- d[1 + u + alen] = (uint32_t)cc;
- }
- }
- static void
- br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y,
- const uint32_t *m, uint32_t m0i)
- {
- /*
- * Each outer loop iteration computes:
- * d <- (d + xu*y + f*m) / 2^31
- * We have xu <= 2^31-1 and f <= 2^31-1.
- * Thus, if d <= 2*m-1 on input, then:
- * 2*m-1 + 2*(2^31-1)*m <= (2^32)*m-1
- * and the new d value is less than 2*m.
- *
- * We represent d over 31-bit words, with an extra word 'dh'
- * which can thus be only 0 or 1.
- */
- size_t len, len4, u, v;
- uint32_t dh;
- len = (m[0] + 31) >> 5;
- len4 = len & ~(size_t)3;
- br_i31_zero(d, m[0]);
- dh = 0;
- for (u = 0; u < len; u ++) {
- /*
- * The carry for each operation fits on 32 bits:
- * d[v+1] <= 2^31-1
- * xu*y[v+1] <= (2^31-1)*(2^31-1)
- * f*m[v+1] <= (2^31-1)*(2^31-1)
- * r <= 2^32-1
- * (2^31-1) + 2*(2^31-1)*(2^31-1) + (2^32-1) = 2^63 - 2^31
- * After division by 2^31, the new r is then at most 2^32-1
- *
- * Using a 32-bit carry has performance benefits on 32-bit
- * systems; however, on 64-bit architectures, we prefer to
- * keep the carry (r) in a 64-bit register, thus avoiding some
- * "clear high bits" operations.
- */
- uint32_t f, xu;
- #if BR_64
- uint64_t r;
- #else
- uint32_t r;
- #endif
- xu = x[u + 1];
- f = MUL31_lo((d[1] + MUL31_lo(x[u + 1], y[1])), m0i);
- r = 0;
- for (v = 0; v < len4; v += 4) {
- uint64_t z;
- z = (uint64_t)d[v + 1] + MUL31(xu, y[v + 1])
- + MUL31(f, m[v + 1]) + r;
- r = z >> 31;
- d[v + 0] = (uint32_t)z & 0x7FFFFFFF;
- z = (uint64_t)d[v + 2] + MUL31(xu, y[v + 2])
- + MUL31(f, m[v + 2]) + r;
- r = z >> 31;
- d[v + 1] = (uint32_t)z & 0x7FFFFFFF;
- z = (uint64_t)d[v + 3] + MUL31(xu, y[v + 3])
- + MUL31(f, m[v + 3]) + r;
- r = z >> 31;
- d[v + 2] = (uint32_t)z & 0x7FFFFFFF;
- z = (uint64_t)d[v + 4] + MUL31(xu, y[v + 4])
- + MUL31(f, m[v + 4]) + r;
- r = z >> 31;
- d[v + 3] = (uint32_t)z & 0x7FFFFFFF;
- }
- for (; v < len; v ++) {
- uint64_t z;
- z = (uint64_t)d[v + 1] + MUL31(xu, y[v + 1])
- + MUL31(f, m[v + 1]) + r;
- r = z >> 31;
- d[v] = (uint32_t)z & 0x7FFFFFFF;
- }
- /*
- * Since the new dh can only be 0 or 1, the addition of
- * the old dh with the carry MUST fit on 32 bits, and
- * thus can be done into dh itself.
- */
- dh += (uint32_t) r;
- d[len] = dh & 0x7FFFFFFF;
- dh >>= 31;
- }
- /*
- * We must write back the bit length because it was overwritten in
- * the loop (not overwriting it would require a test in the loop,
- * which would yield bigger and slower code).
- */
- d[0] = m[0];
- /*
- * d[] may still be greater than m[] at that point; notably, the
- * 'dh' word may be non-zero.
- */
- br_i31_sub(d, m, NEQ(dh, 0) | NOT(br_i31_sub(d, m, 0)));
- }
- static void
- br_i31_to_monty(uint32_t *x, const uint32_t *m)
- {
- uint32_t k;
- for (k = (m[0] + 31) >> 5; k > 0; k --) {
- br_i31_muladd_small(x, 0, m);
- }
- }
- static void
- br_i31_modpow(uint32_t *x,
- const unsigned char *e, size_t elen,
- const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2)
- {
- size_t mlen;
- uint32_t k;
- /*
- * 'mlen' is the length of m[] expressed in bytes (including
- * the "bit length" first field).
- */
- mlen = ((m[0] + 63) >> 5) * sizeof m[0];
- /*
- * Throughout the algorithm:
- * -- t1[] is in Montgomery representation; it contains x, x^2,
- * x^4, x^8...
- * -- The result is accumulated, in normal representation, in
- * the x[] array.
- * -- t2[] is used as destination buffer for each multiplication.
- *
- * Note that there is no need to call br_i32_from_monty().
- */
- memcpy(t1, x, mlen);
- br_i31_to_monty(t1, m);
- br_i31_zero(x, m[0]);
- x[1] = 1;
- for (k = 0; k < ((uint32_t)elen << 3); k ++) {
- uint32_t ctl;
- ctl = (e[elen - 1 - (k >> 3)] >> (k & 7)) & 1;
- br_i31_montymul(t2, x, t1, m, m0i);
- CCOPY(ctl, x, t2, mlen);
- br_i31_montymul(t2, t1, t1, m, m0i);
- memcpy(t1, t2, mlen);
- }
- }
- static size_t rsa_trim(const uint8_t **p, size_t n) {
- while (n > 0 && **p == 0) (*p)++, n--;
- return n;
- }
- static int rsa_i31_len(size_t nbytes) {
- int z = (int) (nbytes * 8U);
- int n = 1;
- while (z > 0) z -= 31, n++;
- return n + (n & 1);
- }
- static int rsa_fail(uint8_t *out, size_t outsz) {
- mg_bzero(out, outsz);
- return -1;
- }
- static int rsa_i31_eq(const uint32_t *a, const uint32_t *b) {
- uint32_t r = a[0] ^ b[0];
- size_t u, n = (a[0] + 31) >> 5;
- for (u = 1; u <= n; u++) r |= a[u] ^ b[u];
- return r == 0;
- }
- int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp,
- size_t expsz, const uint8_t *msg, size_t msgsz,
- uint8_t *out, size_t outsz) {
- uint32_t tmp[4 * BR_RSA_WORDS], *m = tmp, *x, *t1, *t2, m0i;
- int fwlen;
- if (out == NULL) return -1;
- mg_bzero(out, outsz);
- if (mod == NULL || exp == NULL || msg == NULL) return -1;
- modsz = rsa_trim(&mod, modsz);
- expsz = rsa_trim(&exp, expsz);
- if (modsz < 256 || modsz > (BR_MAX_RSA_SIZE >> 3) || expsz == 0 ||
- expsz > modsz || outsz != modsz || msgsz != modsz) {
- return -1;
- }
- if (memcmp(msg, mod, modsz) >= 0) return -1;
- fwlen = rsa_i31_len(modsz);
- if (4 * (size_t) fwlen > sizeof(tmp) / sizeof(tmp[0])) return -1;
- x = m + fwlen, t1 = x + fwlen, t2 = t1 + fwlen;
- br_i31_decode(m, mod, modsz);
- m0i = br_i31_ninv31(m[1]);
- if ((m0i & 1) == 0) return rsa_fail((uint8_t *) tmp, sizeof(tmp));
- br_i31_decode_reduce(x, msg, msgsz, m);
- br_i31_modpow(x, exp, expsz, m, m0i, t1, t2);
- br_i31_encode(out, outsz, x);
- mg_bzero((uint8_t *) tmp, sizeof(tmp));
- return 0;
- }
- int mg_rsa_crt_sign(const uint8_t *em, size_t em_len,
- const uint8_t *dP, size_t dP_len,
- const uint8_t *dQ, size_t dQ_len,
- const uint8_t *p, size_t p_len,
- const uint8_t *q, size_t q_len,
- const uint8_t *qInv, size_t qInv_len,
- uint8_t *signature, size_t sig_len) {
- uint32_t tmp[8 * BR_RSA_FACTOR_WORDS], *mq = tmp, *s2, *mp, *s1, *t1, *t2;
- uint32_t *c1, *c2;
- uint32_t p0i, q0i;
- size_t plen, qlen, xlen = sig_len, u;
- int fwlen, ok = 0;
- uint8_t x[BR_MAX_RSA_SIZE >> 3];
- if (signature == NULL) return -1;
- mg_bzero(signature, sig_len);
- if (em == NULL || dP == NULL || dQ == NULL || p == NULL || q == NULL ||
- qInv == NULL) {
- return -1;
- }
- p_len = rsa_trim(&p, p_len);
- q_len = rsa_trim(&q, q_len);
- dP_len = rsa_trim(&dP, dP_len);
- dQ_len = rsa_trim(&dQ, dQ_len);
- qInv_len = rsa_trim(&qInv, qInv_len);
- if (p_len < 128 || q_len < 128 || p_len > (BR_MAX_RSA_FACTOR >> 3) ||
- q_len > (BR_MAX_RSA_FACTOR >> 3) || dP_len == 0 || dQ_len == 0 ||
- qInv_len == 0 || dP_len > p_len || dQ_len > q_len ||
- qInv_len > p_len || xlen > sizeof(x) || em_len == 0 ||
- em_len > xlen) {
- return -1;
- }
- plen = p_len, qlen = q_len;
- fwlen = rsa_i31_len(plen > qlen ? plen : qlen);
- if (8 * fwlen > (int) (sizeof(tmp) / sizeof(tmp[0]))) return -1;
- s2 = mq + fwlen, mp = mq + 2 * fwlen, s1 = mq + 3 * fwlen;
- t1 = mq + 4 * fwlen, t2 = mq + 5 * fwlen;
- c1 = mq + 6 * fwlen, c2 = mq + 7 * fwlen;
- memset(x, 0, xlen - em_len);
- memcpy(x + xlen - em_len, em, em_len);
- br_i31_decode(mq, q, qlen);
- br_i31_decode(mp, p, plen);
- q0i = br_i31_ninv31(mq[1]);
- p0i = br_i31_ninv31(mp[1]);
- if (((p0i & q0i) & 1) == 0) goto done;
- memset(t2, 0, 2 * (size_t) fwlen * sizeof(*t2));
- t2[0] = mq[0];
- br_i31_mulacc(t2, mq, mp);
- if (xlen != ((t2[0] + 7) >> 3)) goto done;
- br_i31_encode(signature, xlen, t2);
- for (u = xlen, ok = 0; u > 0; u--) {
- uint32_t wn = signature[u - 1], wx = x[u - 1];
- ok = (int) (((wx - (wn + (uint32_t) ok)) >> 8) & 1);
- }
- if (!ok) goto done;
- ok = 0;
- br_i31_decode_reduce(s2, x, xlen, mq);
- br_i31_modpow(s2, dQ, dQ_len, mq, q0i, t1, t2);
- br_i31_decode_reduce(s1, x, xlen, mp);
- br_i31_modpow(s1, dP, dP_len, mp, p0i, t1, t2);
- memcpy(c1, s1, (size_t) fwlen * sizeof(*s1));
- memcpy(c2, s2, (size_t) fwlen * sizeof(*s2));
- br_i31_reduce(t2, s2, mp);
- br_i31_add(s1, mp, br_i31_sub(s1, t2, 1));
- br_i31_to_monty(s1, mp);
- br_i31_decode_reduce(t1, qInv, qInv_len, mp);
- br_i31_montymul(t2, s1, t1, mp, p0i);
- br_i31_mulacc(s2, mq, t2);
- br_i31_reduce(t1, s2, mq);
- ok = rsa_i31_eq(t1, c2);
- br_i31_decode(mp, p, plen);
- br_i31_reduce(t1, s2, mp);
- ok &= rsa_i31_eq(t1, c1);
- if (!ok) goto done;
- ok = 0;
- br_i31_encode(signature, sig_len, s2);
- ok = 1;
- done:
- mg_bzero((uint8_t *) tmp, sizeof(tmp));
- mg_bzero(x, sizeof(x));
- if (!ok) return rsa_fail(signature, sig_len);
- return 0;
- }
- // MGF1-SHA256 RFC 8017 B.2.1
- static void mgf1_sha256(const uint8_t *seed, size_t seed_len,
- uint8_t *mask, size_t mask_len) {
- uint8_t cnt[4]; // I2OSP(counter, 4), big-endian
- uint8_t tmp[32]; // one SHA-256 output block
- uint32_t counter = 0;
- size_t off = 0;
- mg_sha256_ctx ctx;
- while (off < mask_len) {
- size_t n = mask_len - off;
- if (n > 32) n = 32;
- br_enc32be(cnt, counter);
- mg_sha256_init(&ctx);
- mg_sha256_update(&ctx, seed, seed_len);
- mg_sha256_update(&ctx, cnt, 4);
- mg_sha256_final(tmp, &ctx);
- memcpy(mask + off, tmp, n);
- off += n;
- ++counter;
- }
- }
- // Full RSASSA-PSS-VERIFY for rsa_pss_rsae_sha256 (RFC 8017 9.1.2)
- // em - output of RSA public-key primitive (nlen bytes)
- // nlen - modulus byte length (66 to 512)
- // mhash - SHA-256 of the TLS 1.3 signed content (tls->sighash, 32 bytes)
- static bool pss_verify_sha256(const uint8_t *em, size_t nlen, const uint8_t *mhash) {
- // TLS 1.3: H_len = salt_len = 32
- uint8_t db[479]; // unmasked DB after MGF1 XOR
- uint8_t dbmask[479]; // MGF1-SHA256(H, db_len); nlen - hLen - 1 = 479
- uint8_t Hprime[32]; // SHA-256(0x00*8 || mhash || salt)
- uint8_t Mprime[8 + 32 + 32]; // 0*8 || mhash || salt
- const uint8_t *H;
- const uint8_t *salt;
- size_t db_len;
- size_t i;
- uint8_t bad;
-
- if (nlen < 32 + 32 + 2 || nlen - 32 - 1 > sizeof(db)) return -1;
- if (em[nlen - 1] != 0xbc) return false;
- if (em[0] & 0x80) return false;
- db_len = nlen - 32 - 1;
- H = em + db_len;
- mgf1_sha256(H, 32, dbmask, db_len);
- for (i = 0; i < db_len; i++) db[i] = em[i] ^ dbmask[i];
- db[0] &= 0x7f; // DB = 0x00^(db_len-salt_len-1) || 0x01 || salt(salt_Len)
- bad = 0;
- for (i = 0; i < db_len - 32 - 1; i++) bad |= db[i];
- bad |= db[db_len - 32 - 1] ^ 0x01;
- if (bad != 0) return false;
- salt = db + db_len - 32;
- memset(Mprime, 0, 8);
- memcpy(Mprime + 8, mhash, 32);
- memcpy(Mprime + 8 + 32, salt, 32);
- mg_sha256(Hprime, Mprime, sizeof(Mprime));
- bad = 0;
- for (i = 0; i < 32; i++) bad |= Hprime[i] ^ H[i];
- return (bad == 0);
- }
- bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash) {
- return pss_verify_sha256(em, nlen, mhash);
- }
- // Full RSASSA-PKCS1-v1_5-VERIFY for rsa_pkcs1_sha256/sha384 (RFC 8017 9.2)
- // em - output of RSA public-key primitive (nlen bytes)
- // nlen - modulus byte length
- // hash - SHA-256 or SHA-384 of the certificate TBS data
- // hashlen - hash length, 32 for SHA-256 or 48 for SHA-384
- static bool pkcs1_v15_verify(const uint8_t *em, size_t nlen,
- const uint8_t *hash, size_t hashlen) {
- static const uint8_t sha256_di[] = {
- 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
- 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
- static const uint8_t sha384_di[] = {
- 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
- 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30};
- const uint8_t *di = hashlen == 32 ? sha256_di : sha384_di;
- size_t disz = hashlen == 32 ? sizeof(sha256_di) : sizeof(sha384_di);
- size_t pslen, i;
- uint8_t bad = 0;
- if (hashlen != 32 && hashlen != 48) return false;
- if (nlen < 3 + 8 + disz + hashlen) return false;
- pslen = nlen - 3 - disz - hashlen;
- bad |= (uint8_t) (em[0] | (em[1] ^ 0x01));
- for (i = 0; i < pslen; i++) bad |= (uint8_t) (em[2 + i] ^ 0xff);
- bad |= em[2 + pslen];
- for (i = 0; i < disz; i++) bad |= (uint8_t) (em[3 + pslen + i] ^ di[i]);
- for (i = 0; i < hashlen; i++)
- bad |= (uint8_t) (em[3 + pslen + disz + i] ^ hash[i]);
- return bad == 0;
- }
- bool mg_rsa_pkcs_verify(const uint8_t *em, size_t nlen, const uint8_t *hash,
- size_t hashlen) {
- return pkcs1_v15_verify(em, nlen, hash, hashlen);
- }
- #endif /* MG_TLS == MG_TLS_BUILTIN */
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tls_uecc.c"
- #endif
- /* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */
- #if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
- #ifndef MG_UECC_RNG_MAX_TRIES
- #define MG_UECC_RNG_MAX_TRIES 64
- #endif
- #if MG_UECC_ENABLE_VLI_API
- #define MG_UECC_VLI_API
- #else
- #define MG_UECC_VLI_API static
- #endif
- #if (MG_UECC_PLATFORM == mg_uecc_avr) || (MG_UECC_PLATFORM == mg_uecc_arm) || \
- (MG_UECC_PLATFORM == mg_uecc_arm_thumb) || \
- (MG_UECC_PLATFORM == mg_uecc_arm_thumb2)
- #define MG_UECC_CONCATX(a, ...) a##__VA_ARGS__
- #define MG_UECC_CONCAT(a, ...) MG_UECC_CONCATX(a, __VA_ARGS__)
- #define STRX(a) #a
- #define STR(a) STRX(a)
- #define EVAL(...) EVAL1(EVAL1(EVAL1(EVAL1(__VA_ARGS__))))
- #define EVAL1(...) EVAL2(EVAL2(EVAL2(EVAL2(__VA_ARGS__))))
- #define EVAL2(...) EVAL3(EVAL3(EVAL3(EVAL3(__VA_ARGS__))))
- #define EVAL3(...) EVAL4(EVAL4(EVAL4(EVAL4(__VA_ARGS__))))
- #define EVAL4(...) __VA_ARGS__
- #define DEC_1 0
- #define DEC_2 1
- #define DEC_3 2
- #define DEC_4 3
- #define DEC_5 4
- #define DEC_6 5
- #define DEC_7 6
- #define DEC_8 7
- #define DEC_9 8
- #define DEC_10 9
- #define DEC_11 10
- #define DEC_12 11
- #define DEC_13 12
- #define DEC_14 13
- #define DEC_15 14
- #define DEC_16 15
- #define DEC_17 16
- #define DEC_18 17
- #define DEC_19 18
- #define DEC_20 19
- #define DEC_21 20
- #define DEC_22 21
- #define DEC_23 22
- #define DEC_24 23
- #define DEC_25 24
- #define DEC_26 25
- #define DEC_27 26
- #define DEC_28 27
- #define DEC_29 28
- #define DEC_30 29
- #define DEC_31 30
- #define DEC_32 31
- #define DEC_(N) MG_UECC_CONCAT(DEC_, N)
- #define SECOND_ARG(_, val, ...) val
- #define SOME_CHECK_0 ~, 0
- #define GET_SECOND_ARG(...) SECOND_ARG(__VA_ARGS__, SOME, )
- #define SOME_OR_0(N) GET_SECOND_ARG(MG_UECC_CONCAT(SOME_CHECK_, N))
- #define MG_UECC_EMPTY(...)
- #define DEFER(...) __VA_ARGS__ MG_UECC_EMPTY()
- #define REPEAT_NAME_0() REPEAT_0
- #define REPEAT_NAME_SOME() REPEAT_SOME
- #define REPEAT_0(...)
- #define REPEAT_SOME(N, stuff) \
- DEFER(MG_UECC_CONCAT(REPEAT_NAME_, SOME_OR_0(DEC_(N))))()(DEC_(N), stuff) stuff
- #define REPEAT(N, stuff) EVAL(REPEAT_SOME(N, stuff))
- #define REPEATM_NAME_0() REPEATM_0
- #define REPEATM_NAME_SOME() REPEATM_SOME
- #define REPEATM_0(...)
- #define REPEATM_SOME(N, macro) \
- macro(N) DEFER(MG_UECC_CONCAT(REPEATM_NAME_, SOME_OR_0(DEC_(N))))()(DEC_(N), macro)
- #define REPEATM(N, macro) EVAL(REPEATM_SOME(N, macro))
- #endif
- //
- #if (MG_UECC_WORD_SIZE == 1)
- #if MG_UECC_SUPPORTS_secp160r1
- #define MG_UECC_MAX_WORDS 21 /* Due to the size of curve_n. */
- #endif
- #if MG_UECC_SUPPORTS_secp192r1
- #undef MG_UECC_MAX_WORDS
- #define MG_UECC_MAX_WORDS 24
- #endif
- #if MG_UECC_SUPPORTS_secp224r1
- #undef MG_UECC_MAX_WORDS
- #define MG_UECC_MAX_WORDS 28
- #endif
- #if (MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1)
- #undef MG_UECC_MAX_WORDS
- #define MG_UECC_MAX_WORDS 32
- #endif
- #if MG_UECC_SUPPORTS_secp384r1
- #undef MG_UECC_MAX_WORDS
- #define MG_UECC_MAX_WORDS 48
- #endif
- #elif (MG_UECC_WORD_SIZE == 4)
- #if MG_UECC_SUPPORTS_secp160r1
- #define MG_UECC_MAX_WORDS 6 /* Due to the size of curve_n. */
- #endif
- #if MG_UECC_SUPPORTS_secp192r1
- #undef MG_UECC_MAX_WORDS
- #define MG_UECC_MAX_WORDS 6
- #endif
- #if MG_UECC_SUPPORTS_secp224r1
- #undef MG_UECC_MAX_WORDS
- #define MG_UECC_MAX_WORDS 7
- #endif
- #if (MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1)
- #undef MG_UECC_MAX_WORDS
- #define MG_UECC_MAX_WORDS 8
- #endif
- #if MG_UECC_SUPPORTS_secp384r1
- #undef MG_UECC_MAX_WORDS
- #define MG_UECC_MAX_WORDS 12
- #endif
- #elif (MG_UECC_WORD_SIZE == 8)
- #if MG_UECC_SUPPORTS_secp160r1
- #define MG_UECC_MAX_WORDS 3
- #endif
- #if MG_UECC_SUPPORTS_secp192r1
- #undef MG_UECC_MAX_WORDS
- #define MG_UECC_MAX_WORDS 3
- #endif
- #if MG_UECC_SUPPORTS_secp224r1
- #undef MG_UECC_MAX_WORDS
- #define MG_UECC_MAX_WORDS 4
- #endif
- #if (MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1)
- #undef MG_UECC_MAX_WORDS
- #define MG_UECC_MAX_WORDS 4
- #endif
- #if MG_UECC_SUPPORTS_secp384r1
- #undef MG_UECC_MAX_WORDS
- #define MG_UECC_MAX_WORDS 6
- #endif
- #endif /* MG_UECC_WORD_SIZE */
- #define BITS_TO_WORDS(num_bits) \
- ((wordcount_t) ((num_bits + ((MG_UECC_WORD_SIZE * 8) - 1)) / \
- (MG_UECC_WORD_SIZE * 8)))
- #define BITS_TO_BYTES(num_bits) ((num_bits + 7) / 8)
- struct MG_UECC_Curve_t {
- wordcount_t num_words;
- wordcount_t num_bytes;
- bitcount_t num_n_bits;
- mg_uecc_word_t p[MG_UECC_MAX_WORDS];
- mg_uecc_word_t n[MG_UECC_MAX_WORDS];
- mg_uecc_word_t G[MG_UECC_MAX_WORDS * 2];
- mg_uecc_word_t b[MG_UECC_MAX_WORDS];
- void (*double_jacobian)(mg_uecc_word_t *X1, mg_uecc_word_t *Y1,
- mg_uecc_word_t *Z1, MG_UECC_Curve curve);
- #if MG_UECC_SUPPORT_COMPRESSED_POINT
- void (*mod_sqrt)(mg_uecc_word_t *a, MG_UECC_Curve curve);
- #endif
- void (*x_side)(mg_uecc_word_t *result, const mg_uecc_word_t *x,
- MG_UECC_Curve curve);
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- void (*mmod_fast)(mg_uecc_word_t *result, mg_uecc_word_t *product);
- #endif
- };
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- static void bcopy(uint8_t *dst, const uint8_t *src, unsigned num_bytes) {
- while (0 != num_bytes) {
- num_bytes--;
- dst[num_bytes] = src[num_bytes];
- }
- }
- #endif
- static cmpresult_t mg_uecc_vli_cmp_unsafe(const mg_uecc_word_t *left,
- const mg_uecc_word_t *right,
- wordcount_t num_words);
- #if (MG_UECC_PLATFORM == mg_uecc_arm || \
- MG_UECC_PLATFORM == mg_uecc_arm_thumb || \
- MG_UECC_PLATFORM == mg_uecc_arm_thumb2)
- #endif
- #if (MG_UECC_PLATFORM == mg_uecc_avr)
- #endif
- #ifndef asm_clear
- #define asm_clear 0
- #endif
- #ifndef asm_set
- #define asm_set 0
- #endif
- #ifndef asm_add
- #define asm_add 0
- #endif
- #ifndef asm_sub
- #define asm_sub 0
- #endif
- #ifndef asm_mult
- #define asm_mult 0
- #endif
- #ifndef asm_rshift1
- #define asm_rshift1 0
- #endif
- #ifndef asm_mmod_fast_secp256r1
- #define asm_mmod_fast_secp256r1 0
- #endif
- static MG_UECC_RNG_Function g_rng_function = NULL;
- void mg_uecc_set_rng(MG_UECC_RNG_Function rng_function) {
- g_rng_function = rng_function;
- }
- MG_UECC_RNG_Function mg_uecc_get_rng(void) {
- return g_rng_function;
- }
- int mg_uecc_curve_private_key_size(MG_UECC_Curve curve) {
- return BITS_TO_BYTES(curve->num_n_bits);
- }
- int mg_uecc_curve_public_key_size(MG_UECC_Curve curve) {
- return 2 * curve->num_bytes;
- }
- #if !asm_clear
- MG_UECC_VLI_API void mg_uecc_vli_clear(mg_uecc_word_t *vli,
- wordcount_t num_words) {
- wordcount_t i;
- for (i = 0; i < num_words; ++i) {
- vli[i] = 0;
- }
- }
- #endif /* !asm_clear */
- /* Constant-time comparison to zero - secure way to compare long integers */
- /* Returns 1 if vli == 0, 0 otherwise. */
- MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_isZero(const mg_uecc_word_t *vli,
- wordcount_t num_words) {
- mg_uecc_word_t bits = 0;
- wordcount_t i;
- for (i = 0; i < num_words; ++i) {
- bits |= vli[i];
- }
- return (bits == 0);
- }
- /* Returns nonzero if bit 'bit' of vli is set. */
- MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_testBit(const mg_uecc_word_t *vli,
- bitcount_t bit) {
- return (vli[bit >> MG_UECC_WORD_BITS_SHIFT] &
- ((mg_uecc_word_t) 1 << (bit & MG_UECC_WORD_BITS_MASK)));
- }
- /* Counts the number of words in vli. */
- static wordcount_t vli_numDigits(const mg_uecc_word_t *vli,
- const wordcount_t max_words) {
- wordcount_t i;
- /* Search from the end until we find a non-zero digit.
- We do it in reverse because we expect that most digits will be nonzero. */
- for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) {
- }
- return (i + 1);
- }
- /* Counts the number of bits required to represent vli. */
- MG_UECC_VLI_API bitcount_t mg_uecc_vli_numBits(const mg_uecc_word_t *vli,
- const wordcount_t max_words) {
- mg_uecc_word_t i;
- mg_uecc_word_t digit;
- wordcount_t num_digits = vli_numDigits(vli, max_words);
- if (num_digits == 0) {
- return 0;
- }
- digit = vli[num_digits - 1];
- for (i = 0; digit; ++i) {
- digit >>= 1;
- }
- return (((bitcount_t) ((num_digits - 1) << MG_UECC_WORD_BITS_SHIFT)) +
- (bitcount_t) i);
- }
- /* Sets dest = src. */
- #if !asm_set
- MG_UECC_VLI_API void mg_uecc_vli_set(mg_uecc_word_t *dest,
- const mg_uecc_word_t *src,
- wordcount_t num_words) {
- wordcount_t i;
- for (i = 0; i < num_words; ++i) {
- dest[i] = src[i];
- }
- }
- #endif /* !asm_set */
- /* Returns sign of left - right. */
- static cmpresult_t mg_uecc_vli_cmp_unsafe(const mg_uecc_word_t *left,
- const mg_uecc_word_t *right,
- wordcount_t num_words) {
- wordcount_t i;
- for (i = num_words - 1; i >= 0; --i) {
- if (left[i] > right[i]) {
- return 1;
- } else if (left[i] < right[i]) {
- return -1;
- }
- }
- return 0;
- }
- /* Constant-time comparison function - secure way to compare long integers */
- /* Returns one if left == right, zero otherwise. */
- MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_equal(const mg_uecc_word_t *left,
- const mg_uecc_word_t *right,
- wordcount_t num_words) {
- mg_uecc_word_t diff = 0;
- wordcount_t i;
- for (i = num_words - 1; i >= 0; --i) {
- diff |= (left[i] ^ right[i]);
- }
- return (diff == 0);
- }
- MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result,
- const mg_uecc_word_t *left,
- const mg_uecc_word_t *right,
- wordcount_t num_words);
- /* Returns sign of left - right, in constant time. */
- MG_UECC_VLI_API cmpresult_t mg_uecc_vli_cmp(const mg_uecc_word_t *left,
- const mg_uecc_word_t *right,
- wordcount_t num_words) {
- mg_uecc_word_t tmp[MG_UECC_MAX_WORDS];
- mg_uecc_word_t neg = !!mg_uecc_vli_sub(tmp, left, right, num_words);
- mg_uecc_word_t equal = mg_uecc_vli_isZero(tmp, num_words);
- return (cmpresult_t) (!equal - 2 * neg);
- }
- /* Computes vli = vli >> 1. */
- #if !asm_rshift1
- MG_UECC_VLI_API void mg_uecc_vli_rshift1(mg_uecc_word_t *vli,
- wordcount_t num_words) {
- mg_uecc_word_t *end = vli;
- mg_uecc_word_t carry = 0;
- vli += num_words;
- while (vli-- > end) {
- mg_uecc_word_t temp = *vli;
- *vli = (temp >> 1) | carry;
- carry = temp << (MG_UECC_WORD_BITS - 1);
- }
- }
- #endif /* !asm_rshift1 */
- /* Computes result = left + right, returning carry. Can modify in place. */
- #if !asm_add
- MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_add(mg_uecc_word_t *result,
- const mg_uecc_word_t *left,
- const mg_uecc_word_t *right,
- wordcount_t num_words) {
- mg_uecc_word_t carry = 0;
- wordcount_t i;
- for (i = 0; i < num_words; ++i) {
- mg_uecc_word_t sum = left[i] + right[i] + carry;
- if (sum != left[i]) {
- carry = (sum < left[i]);
- }
- result[i] = sum;
- }
- return carry;
- }
- #endif /* !asm_add */
- /* Computes result = left - right, returning borrow. Can modify in place. */
- #if !asm_sub
- MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result,
- const mg_uecc_word_t *left,
- const mg_uecc_word_t *right,
- wordcount_t num_words) {
- mg_uecc_word_t borrow = 0;
- wordcount_t i;
- for (i = 0; i < num_words; ++i) {
- mg_uecc_word_t diff = left[i] - right[i] - borrow;
- if (diff != left[i]) {
- borrow = (diff > left[i]);
- }
- result[i] = diff;
- }
- return borrow;
- }
- #endif /* !asm_sub */
- #if !asm_mult || (MG_UECC_SQUARE_FUNC && !asm_square) || \
- (MG_UECC_SUPPORTS_secp256k1 && (MG_UECC_OPTIMIZATION_LEVEL > 0) && \
- ((MG_UECC_WORD_SIZE == 1) || (MG_UECC_WORD_SIZE == 8)))
- static void muladd(mg_uecc_word_t a, mg_uecc_word_t b, mg_uecc_word_t *r0,
- mg_uecc_word_t *r1, mg_uecc_word_t *r2) {
- #if MG_UECC_WORD_SIZE == 8
- uint64_t a0 = a & 0xffffffff;
- uint64_t a1 = a >> 32;
- uint64_t b0 = b & 0xffffffff;
- uint64_t b1 = b >> 32;
- uint64_t i0 = a0 * b0;
- uint64_t i1 = a0 * b1;
- uint64_t i2 = a1 * b0;
- uint64_t i3 = a1 * b1;
- uint64_t p0, p1;
- i2 += (i0 >> 32);
- i2 += i1;
- if (i2 < i1) { /* overflow */
- i3 += 0x100000000;
- }
- p0 = (i0 & 0xffffffff) | (i2 << 32);
- p1 = i3 + (i2 >> 32);
- *r0 += p0;
- *r1 += (p1 + (*r0 < p0));
- *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0));
- #else
- mg_uecc_dword_t p = (mg_uecc_dword_t) a * b;
- mg_uecc_dword_t r01 = ((mg_uecc_dword_t) (*r1) << MG_UECC_WORD_BITS) | *r0;
- r01 += p;
- *r2 += (r01 < p);
- *r1 = (mg_uecc_word_t) (r01 >> MG_UECC_WORD_BITS);
- *r0 = (mg_uecc_word_t) r01;
- #endif
- }
- #endif /* muladd needed */
- #if !asm_mult
- MG_UECC_VLI_API void mg_uecc_vli_mult(mg_uecc_word_t *result,
- const mg_uecc_word_t *left,
- const mg_uecc_word_t *right,
- wordcount_t num_words) {
- mg_uecc_word_t r0 = 0;
- mg_uecc_word_t r1 = 0;
- mg_uecc_word_t r2 = 0;
- wordcount_t i, k;
- /* Compute each digit of result in sequence, maintaining the carries. */
- for (k = 0; k < num_words; ++k) {
- for (i = 0; i <= k; ++i) {
- muladd(left[i], right[k - i], &r0, &r1, &r2);
- }
- result[k] = r0;
- r0 = r1;
- r1 = r2;
- r2 = 0;
- }
- for (k = num_words; k < num_words * 2 - 1; ++k) {
- for (i = (wordcount_t) ((k + 1) - num_words); i < num_words; ++i) {
- muladd(left[i], right[k - i], &r0, &r1, &r2);
- }
- result[k] = r0;
- r0 = r1;
- r1 = r2;
- r2 = 0;
- }
- result[num_words * 2 - 1] = r0;
- }
- #endif /* !asm_mult */
- #if MG_UECC_SQUARE_FUNC
- #if !asm_square
- static void mul2add(mg_uecc_word_t a, mg_uecc_word_t b, mg_uecc_word_t *r0,
- mg_uecc_word_t *r1, mg_uecc_word_t *r2) {
- #if MG_UECC_WORD_SIZE == 8
- uint64_t a0 = a & 0xffffffffull;
- uint64_t a1 = a >> 32;
- uint64_t b0 = b & 0xffffffffull;
- uint64_t b1 = b >> 32;
- uint64_t i0 = a0 * b0;
- uint64_t i1 = a0 * b1;
- uint64_t i2 = a1 * b0;
- uint64_t i3 = a1 * b1;
- uint64_t p0, p1;
- i2 += (i0 >> 32);
- i2 += i1;
- if (i2 < i1) { /* overflow */
- i3 += 0x100000000ull;
- }
- p0 = (i0 & 0xffffffffull) | (i2 << 32);
- p1 = i3 + (i2 >> 32);
- *r2 += (p1 >> 63);
- p1 = (p1 << 1) | (p0 >> 63);
- p0 <<= 1;
- *r0 += p0;
- *r1 += (p1 + (*r0 < p0));
- *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0));
- #else
- mg_uecc_dword_t p = (mg_uecc_dword_t) a * b;
- mg_uecc_dword_t r01 = ((mg_uecc_dword_t) (*r1) << MG_UECC_WORD_BITS) | *r0;
- *r2 += (p >> (MG_UECC_WORD_BITS * 2 - 1));
- p *= 2;
- r01 += p;
- *r2 += (r01 < p);
- *r1 = r01 >> MG_UECC_WORD_BITS;
- *r0 = (mg_uecc_word_t) r01;
- #endif
- }
- MG_UECC_VLI_API void mg_uecc_vli_square(mg_uecc_word_t *result,
- const mg_uecc_word_t *left,
- wordcount_t num_words) {
- mg_uecc_word_t r0 = 0;
- mg_uecc_word_t r1 = 0;
- mg_uecc_word_t r2 = 0;
- wordcount_t i, k;
- for (k = 0; k < num_words * 2 - 1; ++k) {
- mg_uecc_word_t min = (k < num_words ? 0 : (k + 1) - num_words);
- for (i = min; i <= k && i <= k - i; ++i) {
- if (i < k - i) {
- mul2add(left[i], left[k - i], &r0, &r1, &r2);
- } else {
- muladd(left[i], left[k - i], &r0, &r1, &r2);
- }
- }
- result[k] = r0;
- r0 = r1;
- r1 = r2;
- r2 = 0;
- }
- result[num_words * 2 - 1] = r0;
- }
- #endif /* !asm_square */
- #else /* MG_UECC_SQUARE_FUNC */
- #if MG_UECC_ENABLE_VLI_API
- MG_UECC_VLI_API void mg_uecc_vli_square(mg_uecc_word_t *result,
- const mg_uecc_word_t *left,
- wordcount_t num_words) {
- mg_uecc_vli_mult(result, left, left, num_words);
- }
- #endif /* MG_UECC_ENABLE_VLI_API */
- #endif /* MG_UECC_SQUARE_FUNC */
- /* Computes result = (left + right) % mod.
- Assumes that left < mod and right < mod, and that result does not overlap
- mod. */
- MG_UECC_VLI_API void mg_uecc_vli_modAdd(mg_uecc_word_t *result,
- const mg_uecc_word_t *left,
- const mg_uecc_word_t *right,
- const mg_uecc_word_t *mod,
- wordcount_t num_words) {
- mg_uecc_word_t carry = mg_uecc_vli_add(result, left, right, num_words);
- if (carry || mg_uecc_vli_cmp_unsafe(mod, result, num_words) != 1) {
- /* result > mod (result = mod + remainder), so subtract mod to get
- * remainder. */
- mg_uecc_vli_sub(result, result, mod, num_words);
- }
- }
- /* Computes result = (left - right) % mod.
- Assumes that left < mod and right < mod, and that result does not overlap
- mod. */
- MG_UECC_VLI_API void mg_uecc_vli_modSub(mg_uecc_word_t *result,
- const mg_uecc_word_t *left,
- const mg_uecc_word_t *right,
- const mg_uecc_word_t *mod,
- wordcount_t num_words) {
- mg_uecc_word_t l_borrow = mg_uecc_vli_sub(result, left, right, num_words);
- if (l_borrow) {
- /* In this case, result == -diff == (max int) - diff. Since -x % d == d - x,
- we can get the correct result from result + mod (with overflow). */
- mg_uecc_vli_add(result, result, mod, num_words);
- }
- }
- /* Computes result = product % mod, where product is 2N words long. */
- /* Currently only designed to work for curve_p or curve_n. */
- MG_UECC_VLI_API void mg_uecc_vli_mmod(mg_uecc_word_t *result,
- mg_uecc_word_t *product,
- const mg_uecc_word_t *mod,
- wordcount_t num_words) {
- mg_uecc_word_t mod_multiple[2 * MG_UECC_MAX_WORDS];
- mg_uecc_word_t tmp[2 * MG_UECC_MAX_WORDS];
- mg_uecc_word_t *v[2] = {tmp, product};
- mg_uecc_word_t index;
- /* Shift mod so its highest set bit is at the maximum position. */
- bitcount_t shift = (bitcount_t) ((num_words * 2 * MG_UECC_WORD_BITS) -
- mg_uecc_vli_numBits(mod, num_words));
- wordcount_t word_shift = (wordcount_t) (shift / MG_UECC_WORD_BITS);
- wordcount_t bit_shift = (wordcount_t) (shift % MG_UECC_WORD_BITS);
- mg_uecc_word_t carry = 0;
- mg_uecc_vli_clear(mod_multiple, word_shift);
- if (bit_shift > 0) {
- for (index = 0; index < (mg_uecc_word_t) num_words; ++index) {
- mod_multiple[(mg_uecc_word_t) word_shift + index] =
- (mg_uecc_word_t) (mod[index] << bit_shift) | carry;
- carry = mod[index] >> (MG_UECC_WORD_BITS - bit_shift);
- }
- } else {
- mg_uecc_vli_set(mod_multiple + word_shift, mod, num_words);
- }
- for (index = 1; shift >= 0; --shift) {
- mg_uecc_word_t borrow = 0;
- wordcount_t i;
- for (i = 0; i < num_words * 2; ++i) {
- mg_uecc_word_t diff = v[index][i] - mod_multiple[i] - borrow;
- if (diff != v[index][i]) {
- borrow = (diff > v[index][i]);
- }
- v[1 - index][i] = diff;
- }
- index = !(index ^ borrow); /* Swap the index if there was no borrow */
- mg_uecc_vli_rshift1(mod_multiple, num_words);
- mod_multiple[num_words - 1] |= mod_multiple[num_words]
- << (MG_UECC_WORD_BITS - 1);
- mg_uecc_vli_rshift1(mod_multiple + num_words, num_words);
- }
- mg_uecc_vli_set(result, v[index], num_words);
- }
- /* Computes result = (left * right) % mod. */
- MG_UECC_VLI_API void mg_uecc_vli_modMult(mg_uecc_word_t *result,
- const mg_uecc_word_t *left,
- const mg_uecc_word_t *right,
- const mg_uecc_word_t *mod,
- wordcount_t num_words) {
- mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS];
- mg_uecc_vli_mult(product, left, right, num_words);
- mg_uecc_vli_mmod(result, product, mod, num_words);
- }
- MG_UECC_VLI_API void mg_uecc_vli_modMult_fast(mg_uecc_word_t *result,
- const mg_uecc_word_t *left,
- const mg_uecc_word_t *right,
- MG_UECC_Curve curve) {
- mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS];
- mg_uecc_vli_mult(product, left, right, curve->num_words);
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- curve->mmod_fast(result, product);
- #else
- mg_uecc_vli_mmod(result, product, curve->p, curve->num_words);
- #endif
- }
- #if MG_UECC_SQUARE_FUNC
- #if MG_UECC_ENABLE_VLI_API
- /* Computes result = left^2 % mod. */
- MG_UECC_VLI_API void mg_uecc_vli_modSquare(mg_uecc_word_t *result,
- const mg_uecc_word_t *left,
- const mg_uecc_word_t *mod,
- wordcount_t num_words) {
- mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS];
- mg_uecc_vli_square(product, left, num_words);
- mg_uecc_vli_mmod(result, product, mod, num_words);
- }
- #endif /* MG_UECC_ENABLE_VLI_API */
- MG_UECC_VLI_API void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result,
- const mg_uecc_word_t *left,
- MG_UECC_Curve curve) {
- mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS];
- mg_uecc_vli_square(product, left, curve->num_words);
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- curve->mmod_fast(result, product);
- #else
- mg_uecc_vli_mmod(result, product, curve->p, curve->num_words);
- #endif
- }
- #else /* MG_UECC_SQUARE_FUNC */
- #if MG_UECC_ENABLE_VLI_API
- MG_UECC_VLI_API void mg_uecc_vli_modSquare(mg_uecc_word_t *result,
- const mg_uecc_word_t *left,
- const mg_uecc_word_t *mod,
- wordcount_t num_words) {
- mg_uecc_vli_modMult(result, left, left, mod, num_words);
- }
- #endif /* MG_UECC_ENABLE_VLI_API */
- MG_UECC_VLI_API void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result,
- const mg_uecc_word_t *left,
- MG_UECC_Curve curve) {
- mg_uecc_vli_modMult_fast(result, left, left, curve);
- }
- #endif /* MG_UECC_SQUARE_FUNC */
- #define EVEN(vli) (!(vli[0] & 1))
- static void vli_modInv_update(mg_uecc_word_t *uv, const mg_uecc_word_t *mod,
- wordcount_t num_words) {
- mg_uecc_word_t carry = 0;
- if (!EVEN(uv)) {
- carry = mg_uecc_vli_add(uv, uv, mod, num_words);
- }
- mg_uecc_vli_rshift1(uv, num_words);
- if (carry) {
- uv[num_words - 1] |= HIGH_BIT_SET;
- }
- }
- /* Computes result = (1 / input) % mod. All VLIs are the same size.
- See "From Euclid's GCD to Montgomery Multiplication to the Great Divide" */
- MG_UECC_VLI_API void mg_uecc_vli_modInv(mg_uecc_word_t *result,
- const mg_uecc_word_t *input,
- const mg_uecc_word_t *mod,
- wordcount_t num_words) {
- mg_uecc_word_t a[MG_UECC_MAX_WORDS], b[MG_UECC_MAX_WORDS],
- u[MG_UECC_MAX_WORDS], v[MG_UECC_MAX_WORDS];
- cmpresult_t cmpResult;
- if (mg_uecc_vli_isZero(input, num_words)) {
- mg_uecc_vli_clear(result, num_words);
- return;
- }
- mg_uecc_vli_set(a, input, num_words);
- mg_uecc_vli_set(b, mod, num_words);
- mg_uecc_vli_clear(u, num_words);
- u[0] = 1;
- mg_uecc_vli_clear(v, num_words);
- while ((cmpResult = mg_uecc_vli_cmp_unsafe(a, b, num_words)) != 0) {
- if (EVEN(a)) {
- mg_uecc_vli_rshift1(a, num_words);
- vli_modInv_update(u, mod, num_words);
- } else if (EVEN(b)) {
- mg_uecc_vli_rshift1(b, num_words);
- vli_modInv_update(v, mod, num_words);
- } else if (cmpResult > 0) {
- mg_uecc_vli_sub(a, a, b, num_words);
- mg_uecc_vli_rshift1(a, num_words);
- if (mg_uecc_vli_cmp_unsafe(u, v, num_words) < 0) {
- mg_uecc_vli_add(u, u, mod, num_words);
- }
- mg_uecc_vli_sub(u, u, v, num_words);
- vli_modInv_update(u, mod, num_words);
- } else {
- mg_uecc_vli_sub(b, b, a, num_words);
- mg_uecc_vli_rshift1(b, num_words);
- if (mg_uecc_vli_cmp_unsafe(v, u, num_words) < 0) {
- mg_uecc_vli_add(v, v, mod, num_words);
- }
- mg_uecc_vli_sub(v, v, u, num_words);
- vli_modInv_update(v, mod, num_words);
- }
- }
- mg_uecc_vli_set(result, u, num_words);
- }
- /* ------ Point operations ------ */
- /* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */
- #ifndef _UECC_CURVE_SPECIFIC_H_
- #define _UECC_CURVE_SPECIFIC_H_
- #define num_bytes_secp160r1 20
- #define num_bytes_secp192r1 24
- #define num_bytes_secp224r1 28
- #define num_bytes_secp256r1 32
- #define num_bytes_secp256k1 32
- #define num_bytes_secp384r1 48
- #if (MG_UECC_WORD_SIZE == 1)
- #define num_words_secp160r1 20
- #define num_words_secp192r1 24
- #define num_words_secp224r1 28
- #define num_words_secp256r1 32
- #define num_words_secp256k1 32
- #define num_words_secp384r1 48
- #define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) \
- 0x##a, 0x##b, 0x##c, 0x##d, 0x##e, 0x##f, 0x##g, 0x##h
- #define BYTES_TO_WORDS_4(a, b, c, d) 0x##a, 0x##b, 0x##c, 0x##d
- #elif (MG_UECC_WORD_SIZE == 4)
- #define num_words_secp160r1 5
- #define num_words_secp192r1 6
- #define num_words_secp224r1 7
- #define num_words_secp256r1 8
- #define num_words_secp256k1 8
- #define num_words_secp384r1 12
- #define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##d##c##b##a, 0x##h##g##f##e
- #define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a
- #elif (MG_UECC_WORD_SIZE == 8)
- #define num_words_secp160r1 3
- #define num_words_secp192r1 3
- #define num_words_secp224r1 4
- #define num_words_secp256r1 4
- #define num_words_secp256k1 4
- #define num_words_secp384r1 6
- #define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##h##g##f##e##d##c##b##a##U
- #define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a##U
- #endif /* MG_UECC_WORD_SIZE */
- #if MG_UECC_SUPPORTS_secp160r1 || MG_UECC_SUPPORTS_secp192r1 || \
- MG_UECC_SUPPORTS_secp224r1 || MG_UECC_SUPPORTS_secp256r1 || \
- MG_UECC_SUPPORTS_secp384r1
- static void double_jacobian_default(mg_uecc_word_t *X1, mg_uecc_word_t *Y1,
- mg_uecc_word_t *Z1, MG_UECC_Curve curve) {
- /* t1 = X, t2 = Y, t3 = Z */
- mg_uecc_word_t t4[MG_UECC_MAX_WORDS];
- mg_uecc_word_t t5[MG_UECC_MAX_WORDS];
- wordcount_t num_words = curve->num_words;
- if (mg_uecc_vli_isZero(Z1, num_words)) {
- return;
- }
- mg_uecc_vli_modSquare_fast(t4, Y1, curve); /* t4 = y1^2 */
- mg_uecc_vli_modMult_fast(t5, X1, t4, curve); /* t5 = x1*y1^2 = A */
- mg_uecc_vli_modSquare_fast(t4, t4, curve); /* t4 = y1^4 */
- mg_uecc_vli_modMult_fast(Y1, Y1, Z1, curve); /* t2 = y1*z1 = z3 */
- mg_uecc_vli_modSquare_fast(Z1, Z1, curve); /* t3 = z1^2 */
- mg_uecc_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */
- mg_uecc_vli_modAdd(Z1, Z1, Z1, curve->p, num_words); /* t3 = 2*z1^2 */
- mg_uecc_vli_modSub(Z1, X1, Z1, curve->p, num_words); /* t3 = x1 - z1^2 */
- mg_uecc_vli_modMult_fast(X1, X1, Z1, curve); /* t1 = x1^2 - z1^4 */
- mg_uecc_vli_modAdd(Z1, X1, X1, curve->p,
- num_words); /* t3 = 2*(x1^2 - z1^4) */
- mg_uecc_vli_modAdd(X1, X1, Z1, curve->p,
- num_words); /* t1 = 3*(x1^2 - z1^4) */
- if (mg_uecc_vli_testBit(X1, 0)) {
- mg_uecc_word_t l_carry = mg_uecc_vli_add(X1, X1, curve->p, num_words);
- mg_uecc_vli_rshift1(X1, num_words);
- X1[num_words - 1] |= l_carry << (MG_UECC_WORD_BITS - 1);
- } else {
- mg_uecc_vli_rshift1(X1, num_words);
- }
- /* t1 = 3/2*(x1^2 - z1^4) = B */
- mg_uecc_vli_modSquare_fast(Z1, X1, curve); /* t3 = B^2 */
- mg_uecc_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - A */
- mg_uecc_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - 2A = x3 */
- mg_uecc_vli_modSub(t5, t5, Z1, curve->p, num_words); /* t5 = A - x3 */
- mg_uecc_vli_modMult_fast(X1, X1, t5, curve); /* t1 = B * (A - x3) */
- mg_uecc_vli_modSub(t4, X1, t4, curve->p,
- num_words); /* t4 = B * (A - x3) - y1^4 = y3 */
- mg_uecc_vli_set(X1, Z1, num_words);
- mg_uecc_vli_set(Z1, Y1, num_words);
- mg_uecc_vli_set(Y1, t4, num_words);
- }
- /* Computes result = x^3 + ax + b. result must not overlap x. */
- static void x_side_default(mg_uecc_word_t *result, const mg_uecc_word_t *x,
- MG_UECC_Curve curve) {
- mg_uecc_word_t _3[MG_UECC_MAX_WORDS] = {3}; /* -a = 3 */
- wordcount_t num_words = curve->num_words;
- mg_uecc_vli_modSquare_fast(result, x, curve); /* r = x^2 */
- mg_uecc_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */
- mg_uecc_vli_modMult_fast(result, result, x, curve); /* r = x^3 - 3x */
- mg_uecc_vli_modAdd(result, result, curve->b, curve->p,
- num_words); /* r = x^3 - 3x + b */
- }
- #endif /* MG_UECC_SUPPORTS_secp... */
- #if MG_UECC_SUPPORT_COMPRESSED_POINT
- #if MG_UECC_SUPPORTS_secp160r1 || MG_UECC_SUPPORTS_secp192r1 || \
- MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1 || \
- MG_UECC_SUPPORTS_secp384r1
- /* Compute a = sqrt(a) (mod curve_p). */
- static void mod_sqrt_default(mg_uecc_word_t *a, MG_UECC_Curve curve) {
- bitcount_t i;
- mg_uecc_word_t p1[MG_UECC_MAX_WORDS] = {1};
- mg_uecc_word_t l_result[MG_UECC_MAX_WORDS] = {1};
- wordcount_t num_words = curve->num_words;
- /* When curve->p == 3 (mod 4), we can compute
- sqrt(a) = a^((curve->p + 1) / 4) (mod curve->p). */
- mg_uecc_vli_add(p1, curve->p, p1, num_words); /* p1 = curve_p + 1 */
- for (i = mg_uecc_vli_numBits(p1, num_words) - 1; i > 1; --i) {
- mg_uecc_vli_modSquare_fast(l_result, l_result, curve);
- if (mg_uecc_vli_testBit(p1, i)) {
- mg_uecc_vli_modMult_fast(l_result, l_result, a, curve);
- }
- }
- mg_uecc_vli_set(a, l_result, num_words);
- }
- #endif /* MG_UECC_SUPPORTS_secp... */
- #endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */
- #if MG_UECC_SUPPORTS_secp160r1
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- static void vli_mmod_fast_secp160r1(mg_uecc_word_t *result,
- mg_uecc_word_t *product);
- #endif
- static const struct MG_UECC_Curve_t curve_secp160r1 = {
- num_words_secp160r1,
- num_bytes_secp160r1,
- 161, /* num_n_bits */
- {BYTES_TO_WORDS_8(FF, FF, FF, 7F, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_4(FF, FF, FF, FF)},
- {BYTES_TO_WORDS_8(57, 22, 75, CA, D3, AE, 27, F9),
- BYTES_TO_WORDS_8(C8, F4, 01, 00, 00, 00, 00, 00),
- BYTES_TO_WORDS_8(00, 00, 00, 00, 01, 00, 00, 00)},
- {BYTES_TO_WORDS_8(82, FC, CB, 13, B9, 8B, C3, 68),
- BYTES_TO_WORDS_8(89, 69, 64, 46, 28, 73, F5, 8E),
- BYTES_TO_WORDS_4(68, B5, 96, 4A),
- BYTES_TO_WORDS_8(32, FB, C5, 7A, 37, 51, 23, 04),
- BYTES_TO_WORDS_8(12, C9, DC, 59, 7D, 94, 68, 31),
- BYTES_TO_WORDS_4(55, 28, A6, 23)},
- {BYTES_TO_WORDS_8(45, FA, 65, C5, AD, D4, D4, 81),
- BYTES_TO_WORDS_8(9F, F8, AC, 65, 8B, 7A, BD, 54),
- BYTES_TO_WORDS_4(FC, BE, 97, 1C)},
- &double_jacobian_default,
- #if MG_UECC_SUPPORT_COMPRESSED_POINT
- &mod_sqrt_default,
- #endif
- &x_side_default,
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- &vli_mmod_fast_secp160r1
- #endif
- };
- MG_UECC_Curve mg_uecc_secp160r1(void) {
- return &curve_secp160r1;
- }
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1)
- /* Computes result = product % curve_p
- see http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf page 354
- Note that this only works if log2(omega) < log2(p) / 2 */
- static void omega_mult_secp160r1(mg_uecc_word_t *result,
- const mg_uecc_word_t *right);
- #if MG_UECC_WORD_SIZE == 8
- static void vli_mmod_fast_secp160r1(mg_uecc_word_t *result,
- mg_uecc_word_t *product) {
- mg_uecc_word_t tmp[2 * num_words_secp160r1];
- mg_uecc_word_t copy;
- mg_uecc_vli_clear(tmp, num_words_secp160r1);
- mg_uecc_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1);
- omega_mult_secp160r1(tmp,
- product + num_words_secp160r1 - 1); /* (Rq, q) = q * c */
- product[num_words_secp160r1 - 1] &= 0xffffffff;
- copy = tmp[num_words_secp160r1 - 1];
- tmp[num_words_secp160r1 - 1] &= 0xffffffff;
- mg_uecc_vli_add(result, product, tmp,
- num_words_secp160r1); /* (C, r) = r + q */
- mg_uecc_vli_clear(product, num_words_secp160r1);
- tmp[num_words_secp160r1 - 1] = copy;
- omega_mult_secp160r1(product, tmp + num_words_secp160r1 - 1); /* Rq*c */
- mg_uecc_vli_add(result, result, product,
- num_words_secp160r1); /* (C1, r) = r + Rq*c */
- while (mg_uecc_vli_cmp_unsafe(result, curve_secp160r1.p,
- num_words_secp160r1) > 0) {
- mg_uecc_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1);
- }
- }
- static void omega_mult_secp160r1(uint64_t *result, const uint64_t *right) {
- uint32_t carry;
- unsigned i;
- /* Multiply by (2^31 + 1). */
- carry = 0;
- for (i = 0; i < num_words_secp160r1; ++i) {
- uint64_t tmp = (right[i] >> 32) | (right[i + 1] << 32);
- result[i] = (tmp << 31) + tmp + carry;
- carry = (tmp >> 33) + (result[i] < tmp || (carry && result[i] == tmp));
- }
- result[i] = carry;
- }
- #else
- static void vli_mmod_fast_secp160r1(mg_uecc_word_t *result,
- mg_uecc_word_t *product) {
- mg_uecc_word_t tmp[2 * num_words_secp160r1];
- mg_uecc_word_t carry;
- mg_uecc_vli_clear(tmp, num_words_secp160r1);
- mg_uecc_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1);
- omega_mult_secp160r1(tmp,
- product + num_words_secp160r1); /* (Rq, q) = q * c */
- carry = mg_uecc_vli_add(result, product, tmp,
- num_words_secp160r1); /* (C, r) = r + q */
- mg_uecc_vli_clear(product, num_words_secp160r1);
- omega_mult_secp160r1(product, tmp + num_words_secp160r1); /* Rq*c */
- carry += mg_uecc_vli_add(result, result, product,
- num_words_secp160r1); /* (C1, r) = r + Rq*c */
- while (carry > 0) {
- --carry;
- mg_uecc_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1);
- }
- if (mg_uecc_vli_cmp_unsafe(result, curve_secp160r1.p, num_words_secp160r1) >
- 0) {
- mg_uecc_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1);
- }
- }
- #endif
- #if MG_UECC_WORD_SIZE == 1
- static void omega_mult_secp160r1(uint8_t *result, const uint8_t *right) {
- uint8_t carry;
- uint8_t i;
- /* Multiply by (2^31 + 1). */
- mg_uecc_vli_set(result + 4, right, num_words_secp160r1); /* 2^32 */
- mg_uecc_vli_rshift1(result + 4, num_words_secp160r1); /* 2^31 */
- result[3] = right[0] << 7; /* get last bit from shift */
- carry = mg_uecc_vli_add(result, result, right,
- num_words_secp160r1); /* 2^31 + 1 */
- for (i = num_words_secp160r1; carry; ++i) {
- uint16_t sum = (uint16_t) result[i] + carry;
- result[i] = (uint8_t) sum;
- carry = sum >> 8;
- }
- }
- #elif MG_UECC_WORD_SIZE == 4
- static void omega_mult_secp160r1(uint32_t *result, const uint32_t *right) {
- uint32_t carry;
- unsigned i;
- /* Multiply by (2^31 + 1). */
- mg_uecc_vli_set(result + 1, right, num_words_secp160r1); /* 2^32 */
- mg_uecc_vli_rshift1(result + 1, num_words_secp160r1); /* 2^31 */
- result[0] = right[0] << 31; /* get last bit from shift */
- carry = mg_uecc_vli_add(result, result, right,
- num_words_secp160r1); /* 2^31 + 1 */
- for (i = num_words_secp160r1; carry; ++i) {
- uint64_t sum = (uint64_t) result[i] + carry;
- result[i] = (uint32_t) sum;
- carry = sum >> 32;
- }
- }
- #endif /* MG_UECC_WORD_SIZE */
- #endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1) */
- #endif /* MG_UECC_SUPPORTS_secp160r1 */
- #if MG_UECC_SUPPORTS_secp192r1
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- static void vli_mmod_fast_secp192r1(mg_uecc_word_t *result,
- mg_uecc_word_t *product);
- #endif
- static const struct MG_UECC_Curve_t curve_secp192r1 = {
- num_words_secp192r1,
- num_bytes_secp192r1,
- 192, /* num_n_bits */
- {BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)},
- {BYTES_TO_WORDS_8(31, 28, D2, B4, B1, C9, 6B, 14),
- BYTES_TO_WORDS_8(36, F8, DE, 99, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)},
- {BYTES_TO_WORDS_8(12, 10, FF, 82, FD, 0A, FF, F4),
- BYTES_TO_WORDS_8(00, 88, A1, 43, EB, 20, BF, 7C),
- BYTES_TO_WORDS_8(F6, 90, 30, B0, 0E, A8, 8D, 18),
- BYTES_TO_WORDS_8(11, 48, 79, 1E, A1, 77, F9, 73),
- BYTES_TO_WORDS_8(D5, CD, 24, 6B, ED, 11, 10, 63),
- BYTES_TO_WORDS_8(78, DA, C8, FF, 95, 2B, 19, 07)},
- {BYTES_TO_WORDS_8(B1, B9, 46, C1, EC, DE, B8, FE),
- BYTES_TO_WORDS_8(49, 30, 24, 72, AB, E9, A7, 0F),
- BYTES_TO_WORDS_8(E7, 80, 9C, E5, 19, 05, 21, 64)},
- &double_jacobian_default,
- #if MG_UECC_SUPPORT_COMPRESSED_POINT
- &mod_sqrt_default,
- #endif
- &x_side_default,
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- &vli_mmod_fast_secp192r1
- #endif
- };
- MG_UECC_Curve mg_uecc_secp192r1(void) {
- return &curve_secp192r1;
- }
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- /* Computes result = product % curve_p.
- See algorithm 5 and 6 from http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf
- */
- #if MG_UECC_WORD_SIZE == 1
- static void vli_mmod_fast_secp192r1(uint8_t *result, uint8_t *product) {
- uint8_t tmp[num_words_secp192r1];
- uint8_t carry;
- mg_uecc_vli_set(result, product, num_words_secp192r1);
- mg_uecc_vli_set(tmp, &product[24], num_words_secp192r1);
- carry = mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
- tmp[0] = tmp[1] = tmp[2] = tmp[3] = tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0;
- tmp[8] = product[24];
- tmp[9] = product[25];
- tmp[10] = product[26];
- tmp[11] = product[27];
- tmp[12] = product[28];
- tmp[13] = product[29];
- tmp[14] = product[30];
- tmp[15] = product[31];
- tmp[16] = product[32];
- tmp[17] = product[33];
- tmp[18] = product[34];
- tmp[19] = product[35];
- tmp[20] = product[36];
- tmp[21] = product[37];
- tmp[22] = product[38];
- tmp[23] = product[39];
- carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
- tmp[0] = tmp[8] = product[40];
- tmp[1] = tmp[9] = product[41];
- tmp[2] = tmp[10] = product[42];
- tmp[3] = tmp[11] = product[43];
- tmp[4] = tmp[12] = product[44];
- tmp[5] = tmp[13] = product[45];
- tmp[6] = tmp[14] = product[46];
- tmp[7] = tmp[15] = product[47];
- tmp[16] = tmp[17] = tmp[18] = tmp[19] = tmp[20] = tmp[21] = tmp[22] =
- tmp[23] = 0;
- carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
- while (carry || mg_uecc_vli_cmp_unsafe(curve_secp192r1.p, result,
- num_words_secp192r1) != 1) {
- carry -=
- mg_uecc_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1);
- }
- }
- #elif MG_UECC_WORD_SIZE == 4
- static void vli_mmod_fast_secp192r1(uint32_t *result, uint32_t *product) {
- uint32_t tmp[num_words_secp192r1];
- int carry;
- mg_uecc_vli_set(result, product, num_words_secp192r1);
- mg_uecc_vli_set(tmp, &product[6], num_words_secp192r1);
- carry = mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
- tmp[0] = tmp[1] = 0;
- tmp[2] = product[6];
- tmp[3] = product[7];
- tmp[4] = product[8];
- tmp[5] = product[9];
- carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
- tmp[0] = tmp[2] = product[10];
- tmp[1] = tmp[3] = product[11];
- tmp[4] = tmp[5] = 0;
- carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
- while (carry || mg_uecc_vli_cmp_unsafe(curve_secp192r1.p, result,
- num_words_secp192r1) != 1) {
- carry -=
- mg_uecc_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1);
- }
- }
- #else
- static void vli_mmod_fast_secp192r1(uint64_t *result, uint64_t *product) {
- uint64_t tmp[num_words_secp192r1];
- int carry;
- mg_uecc_vli_set(result, product, num_words_secp192r1);
- mg_uecc_vli_set(tmp, &product[3], num_words_secp192r1);
- carry = (int) mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
- tmp[0] = 0;
- tmp[1] = product[3];
- tmp[2] = product[4];
- carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
- tmp[0] = tmp[1] = product[5];
- tmp[2] = 0;
- carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1);
- while (carry || mg_uecc_vli_cmp_unsafe(curve_secp192r1.p, result,
- num_words_secp192r1) != 1) {
- carry -=
- mg_uecc_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1);
- }
- }
- #endif /* MG_UECC_WORD_SIZE */
- #endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0) */
- #endif /* MG_UECC_SUPPORTS_secp192r1 */
- #if MG_UECC_SUPPORTS_secp224r1
- #if MG_UECC_SUPPORT_COMPRESSED_POINT
- static void mod_sqrt_secp224r1(mg_uecc_word_t *a, MG_UECC_Curve curve);
- #endif
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- static void vli_mmod_fast_secp224r1(mg_uecc_word_t *result,
- mg_uecc_word_t *product);
- #endif
- static const struct MG_UECC_Curve_t curve_secp224r1 = {
- num_words_secp224r1,
- num_bytes_secp224r1,
- 224, /* num_n_bits */
- {BYTES_TO_WORDS_8(01, 00, 00, 00, 00, 00, 00, 00),
- BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_4(FF, FF, FF, FF)},
- {BYTES_TO_WORDS_8(3D, 2A, 5C, 5C, 45, 29, DD, 13),
- BYTES_TO_WORDS_8(3E, F0, B8, E0, A2, 16, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_4(FF, FF, FF, FF)},
- {BYTES_TO_WORDS_8(21, 1D, 5C, 11, D6, 80, 32, 34),
- BYTES_TO_WORDS_8(22, 11, C2, 56, D3, C1, 03, 4A),
- BYTES_TO_WORDS_8(B9, 90, 13, 32, 7F, BF, B4, 6B),
- BYTES_TO_WORDS_4(BD, 0C, 0E, B7),
- BYTES_TO_WORDS_8(34, 7E, 00, 85, 99, 81, D5, 44),
- BYTES_TO_WORDS_8(64, 47, 07, 5A, A0, 75, 43, CD),
- BYTES_TO_WORDS_8(E6, DF, 22, 4C, FB, 23, F7, B5),
- BYTES_TO_WORDS_4(88, 63, 37, BD)},
- {BYTES_TO_WORDS_8(B4, FF, 55, 23, 43, 39, 0B, 27),
- BYTES_TO_WORDS_8(BA, D8, BF, D7, B7, B0, 44, 50),
- BYTES_TO_WORDS_8(56, 32, 41, F5, AB, B3, 04, 0C),
- BYTES_TO_WORDS_4(85, 0A, 05, B4)},
- &double_jacobian_default,
- #if MG_UECC_SUPPORT_COMPRESSED_POINT
- &mod_sqrt_secp224r1,
- #endif
- &x_side_default,
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- &vli_mmod_fast_secp224r1
- #endif
- };
- MG_UECC_Curve mg_uecc_secp224r1(void) {
- return &curve_secp224r1;
- }
- #if MG_UECC_SUPPORT_COMPRESSED_POINT
- /* Routine 3.2.4 RS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */
- static void mod_sqrt_secp224r1_rs(mg_uecc_word_t *d1, mg_uecc_word_t *e1,
- mg_uecc_word_t *f1, const mg_uecc_word_t *d0,
- const mg_uecc_word_t *e0,
- const mg_uecc_word_t *f0) {
- mg_uecc_word_t t[num_words_secp224r1];
- mg_uecc_vli_modSquare_fast(t, d0, &curve_secp224r1); /* t <-- d0 ^ 2 */
- mg_uecc_vli_modMult_fast(e1, d0, e0, &curve_secp224r1); /* e1 <-- d0 * e0 */
- mg_uecc_vli_modAdd(d1, t, f0, curve_secp224r1.p,
- num_words_secp224r1); /* d1 <-- t + f0 */
- mg_uecc_vli_modAdd(e1, e1, e1, curve_secp224r1.p,
- num_words_secp224r1); /* e1 <-- e1 + e1 */
- mg_uecc_vli_modMult_fast(f1, t, f0, &curve_secp224r1); /* f1 <-- t * f0 */
- mg_uecc_vli_modAdd(f1, f1, f1, curve_secp224r1.p,
- num_words_secp224r1); /* f1 <-- f1 + f1 */
- mg_uecc_vli_modAdd(f1, f1, f1, curve_secp224r1.p,
- num_words_secp224r1); /* f1 <-- f1 + f1 */
- }
- /* Routine 3.2.5 RSS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */
- static void mod_sqrt_secp224r1_rss(mg_uecc_word_t *d1, mg_uecc_word_t *e1,
- mg_uecc_word_t *f1, const mg_uecc_word_t *d0,
- const mg_uecc_word_t *e0,
- const mg_uecc_word_t *f0,
- const bitcount_t j) {
- bitcount_t i;
- mg_uecc_vli_set(d1, d0, num_words_secp224r1); /* d1 <-- d0 */
- mg_uecc_vli_set(e1, e0, num_words_secp224r1); /* e1 <-- e0 */
- mg_uecc_vli_set(f1, f0, num_words_secp224r1); /* f1 <-- f0 */
- for (i = 1; i <= j; i++) {
- mod_sqrt_secp224r1_rs(d1, e1, f1, d1, e1, f1); /* RS (d1,e1,f1,d1,e1,f1) */
- }
- }
- /* Routine 3.2.6 RM; from http://www.nsa.gov/ia/_files/nist-routines.pdf */
- static void mod_sqrt_secp224r1_rm(mg_uecc_word_t *d2, mg_uecc_word_t *e2,
- mg_uecc_word_t *f2, const mg_uecc_word_t *c,
- const mg_uecc_word_t *d0,
- const mg_uecc_word_t *e0,
- const mg_uecc_word_t *d1,
- const mg_uecc_word_t *e1) {
- mg_uecc_word_t t1[num_words_secp224r1];
- mg_uecc_word_t t2[num_words_secp224r1];
- mg_uecc_vli_modMult_fast(t1, e0, e1, &curve_secp224r1); /* t1 <-- e0 * e1 */
- mg_uecc_vli_modMult_fast(t1, t1, c, &curve_secp224r1); /* t1 <-- t1 * c */
- /* t1 <-- p - t1 */
- mg_uecc_vli_modSub(t1, curve_secp224r1.p, t1, curve_secp224r1.p,
- num_words_secp224r1);
- mg_uecc_vli_modMult_fast(t2, d0, d1, &curve_secp224r1); /* t2 <-- d0 * d1 */
- mg_uecc_vli_modAdd(t2, t2, t1, curve_secp224r1.p,
- num_words_secp224r1); /* t2 <-- t2 + t1 */
- mg_uecc_vli_modMult_fast(t1, d0, e1, &curve_secp224r1); /* t1 <-- d0 * e1 */
- mg_uecc_vli_modMult_fast(e2, d1, e0, &curve_secp224r1); /* e2 <-- d1 * e0 */
- mg_uecc_vli_modAdd(e2, e2, t1, curve_secp224r1.p,
- num_words_secp224r1); /* e2 <-- e2 + t1 */
- mg_uecc_vli_modSquare_fast(f2, e2, &curve_secp224r1); /* f2 <-- e2^2 */
- mg_uecc_vli_modMult_fast(f2, f2, c, &curve_secp224r1); /* f2 <-- f2 * c */
- /* f2 <-- p - f2 */
- mg_uecc_vli_modSub(f2, curve_secp224r1.p, f2, curve_secp224r1.p,
- num_words_secp224r1);
- mg_uecc_vli_set(d2, t2, num_words_secp224r1); /* d2 <-- t2 */
- }
- /* Routine 3.2.7 RP; from http://www.nsa.gov/ia/_files/nist-routines.pdf */
- static void mod_sqrt_secp224r1_rp(mg_uecc_word_t *d1, mg_uecc_word_t *e1,
- mg_uecc_word_t *f1, const mg_uecc_word_t *c,
- const mg_uecc_word_t *r) {
- wordcount_t i;
- wordcount_t pow2i = 1;
- mg_uecc_word_t d0[num_words_secp224r1];
- mg_uecc_word_t e0[num_words_secp224r1] = {1}; /* e0 <-- 1 */
- mg_uecc_word_t f0[num_words_secp224r1];
- mg_uecc_vli_set(d0, r, num_words_secp224r1); /* d0 <-- r */
- /* f0 <-- p - c */
- mg_uecc_vli_modSub(f0, curve_secp224r1.p, c, curve_secp224r1.p,
- num_words_secp224r1);
- for (i = 0; i <= 6; i++) {
- mod_sqrt_secp224r1_rss(d1, e1, f1, d0, e0, f0,
- pow2i); /* RSS (d1,e1,f1,d0,e0,f0,2^i) */
- mod_sqrt_secp224r1_rm(d1, e1, f1, c, d1, e1, d0,
- e0); /* RM (d1,e1,f1,c,d1,e1,d0,e0) */
- mg_uecc_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */
- mg_uecc_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */
- mg_uecc_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */
- pow2i *= 2;
- }
- }
- /* Compute a = sqrt(a) (mod curve_p). */
- /* Routine 3.2.8 mp_mod_sqrt_224; from
- * http://www.nsa.gov/ia/_files/nist-routines.pdf */
- static void mod_sqrt_secp224r1(mg_uecc_word_t *a, MG_UECC_Curve curve) {
- (void) curve;
- bitcount_t i;
- mg_uecc_word_t e1[num_words_secp224r1];
- mg_uecc_word_t f1[num_words_secp224r1];
- mg_uecc_word_t d0[num_words_secp224r1];
- mg_uecc_word_t e0[num_words_secp224r1];
- mg_uecc_word_t f0[num_words_secp224r1];
- mg_uecc_word_t d1[num_words_secp224r1];
- /* s = a; using constant instead of random value */
- mod_sqrt_secp224r1_rp(d0, e0, f0, a, a); /* RP (d0, e0, f0, c, s) */
- mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0,
- f0); /* RS (d1, e1, f1, d0, e0, f0) */
- for (i = 1; i <= 95; i++) {
- mg_uecc_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */
- mg_uecc_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */
- mg_uecc_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */
- mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0,
- f0); /* RS (d1, e1, f1, d0, e0, f0) */
- if (mg_uecc_vli_isZero(d1, num_words_secp224r1)) { /* if d1 == 0 */
- break;
- }
- }
- mg_uecc_vli_modInv(f1, e0, curve_secp224r1.p,
- num_words_secp224r1); /* f1 <-- 1 / e0 */
- mg_uecc_vli_modMult_fast(a, d0, f1, &curve_secp224r1); /* a <-- d0 / e0 */
- }
- #endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- /* Computes result = product % curve_p
- from http://www.nsa.gov/ia/_files/nist-routines.pdf */
- #if MG_UECC_WORD_SIZE == 1
- static void vli_mmod_fast_secp224r1(uint8_t *result, uint8_t *product) {
- uint8_t tmp[num_words_secp224r1];
- int8_t carry;
- /* t */
- mg_uecc_vli_set(result, product, num_words_secp224r1);
- /* s1 */
- tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
- tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0;
- tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0;
- tmp[12] = product[28];
- tmp[13] = product[29];
- tmp[14] = product[30];
- tmp[15] = product[31];
- tmp[16] = product[32];
- tmp[17] = product[33];
- tmp[18] = product[34];
- tmp[19] = product[35];
- tmp[20] = product[36];
- tmp[21] = product[37];
- tmp[22] = product[38];
- tmp[23] = product[39];
- tmp[24] = product[40];
- tmp[25] = product[41];
- tmp[26] = product[42];
- tmp[27] = product[43];
- carry = mg_uecc_vli_add(result, result, tmp, num_words_secp224r1);
- /* s2 */
- tmp[12] = product[44];
- tmp[13] = product[45];
- tmp[14] = product[46];
- tmp[15] = product[47];
- tmp[16] = product[48];
- tmp[17] = product[49];
- tmp[18] = product[50];
- tmp[19] = product[51];
- tmp[20] = product[52];
- tmp[21] = product[53];
- tmp[22] = product[54];
- tmp[23] = product[55];
- tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0;
- carry += mg_uecc_vli_add(result, result, tmp, num_words_secp224r1);
- /* d1 */
- tmp[0] = product[28];
- tmp[1] = product[29];
- tmp[2] = product[30];
- tmp[3] = product[31];
- tmp[4] = product[32];
- tmp[5] = product[33];
- tmp[6] = product[34];
- tmp[7] = product[35];
- tmp[8] = product[36];
- tmp[9] = product[37];
- tmp[10] = product[38];
- tmp[11] = product[39];
- tmp[12] = product[40];
- tmp[13] = product[41];
- tmp[14] = product[42];
- tmp[15] = product[43];
- tmp[16] = product[44];
- tmp[17] = product[45];
- tmp[18] = product[46];
- tmp[19] = product[47];
- tmp[20] = product[48];
- tmp[21] = product[49];
- tmp[22] = product[50];
- tmp[23] = product[51];
- tmp[24] = product[52];
- tmp[25] = product[53];
- tmp[26] = product[54];
- tmp[27] = product[55];
- carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1);
- /* d2 */
- tmp[0] = product[44];
- tmp[1] = product[45];
- tmp[2] = product[46];
- tmp[3] = product[47];
- tmp[4] = product[48];
- tmp[5] = product[49];
- tmp[6] = product[50];
- tmp[7] = product[51];
- tmp[8] = product[52];
- tmp[9] = product[53];
- tmp[10] = product[54];
- tmp[11] = product[55];
- tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0;
- tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0;
- tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0;
- tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0;
- carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1);
- if (carry < 0) {
- do {
- carry += mg_uecc_vli_add(result, result, curve_secp224r1.p,
- num_words_secp224r1);
- } while (carry < 0);
- } else {
- while (carry || mg_uecc_vli_cmp_unsafe(curve_secp224r1.p, result,
- num_words_secp224r1) != 1) {
- carry -= mg_uecc_vli_sub(result, result, curve_secp224r1.p,
- num_words_secp224r1);
- }
- }
- }
- #elif MG_UECC_WORD_SIZE == 4
- static void vli_mmod_fast_secp224r1(uint32_t *result, uint32_t *product) {
- uint32_t tmp[num_words_secp224r1];
- int carry;
- /* t */
- mg_uecc_vli_set(result, product, num_words_secp224r1);
- /* s1 */
- tmp[0] = tmp[1] = tmp[2] = 0;
- tmp[3] = product[7];
- tmp[4] = product[8];
- tmp[5] = product[9];
- tmp[6] = product[10];
- carry = mg_uecc_vli_add(result, result, tmp, num_words_secp224r1);
- /* s2 */
- tmp[3] = product[11];
- tmp[4] = product[12];
- tmp[5] = product[13];
- tmp[6] = 0;
- carry += mg_uecc_vli_add(result, result, tmp, num_words_secp224r1);
- /* d1 */
- tmp[0] = product[7];
- tmp[1] = product[8];
- tmp[2] = product[9];
- tmp[3] = product[10];
- tmp[4] = product[11];
- tmp[5] = product[12];
- tmp[6] = product[13];
- carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1);
- /* d2 */
- tmp[0] = product[11];
- tmp[1] = product[12];
- tmp[2] = product[13];
- tmp[3] = tmp[4] = tmp[5] = tmp[6] = 0;
- carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1);
- if (carry < 0) {
- do {
- carry += mg_uecc_vli_add(result, result, curve_secp224r1.p,
- num_words_secp224r1);
- } while (carry < 0);
- } else {
- while (carry || mg_uecc_vli_cmp_unsafe(curve_secp224r1.p, result,
- num_words_secp224r1) != 1) {
- carry -= mg_uecc_vli_sub(result, result, curve_secp224r1.p,
- num_words_secp224r1);
- }
- }
- }
- #else
- static void vli_mmod_fast_secp224r1(uint64_t *result, uint64_t *product) {
- uint64_t tmp[num_words_secp224r1];
- int carry = 0;
- /* t */
- mg_uecc_vli_set(result, product, num_words_secp224r1);
- result[num_words_secp224r1 - 1] &= 0xffffffff;
- /* s1 */
- tmp[0] = 0;
- tmp[1] = product[3] & 0xffffffff00000000ull;
- tmp[2] = product[4];
- tmp[3] = product[5] & 0xffffffff;
- mg_uecc_vli_add(result, result, tmp, num_words_secp224r1);
- /* s2 */
- tmp[1] = product[5] & 0xffffffff00000000ull;
- tmp[2] = product[6];
- tmp[3] = 0;
- mg_uecc_vli_add(result, result, tmp, num_words_secp224r1);
- /* d1 */
- tmp[0] = (product[3] >> 32) | (product[4] << 32);
- tmp[1] = (product[4] >> 32) | (product[5] << 32);
- tmp[2] = (product[5] >> 32) | (product[6] << 32);
- tmp[3] = product[6] >> 32;
- carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1);
- /* d2 */
- tmp[0] = (product[5] >> 32) | (product[6] << 32);
- tmp[1] = product[6] >> 32;
- tmp[2] = tmp[3] = 0;
- carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1);
- if (carry < 0) {
- do {
- carry += mg_uecc_vli_add(result, result, curve_secp224r1.p,
- num_words_secp224r1);
- } while (carry < 0);
- } else {
- while (mg_uecc_vli_cmp_unsafe(curve_secp224r1.p, result,
- num_words_secp224r1) != 1) {
- mg_uecc_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1);
- }
- }
- }
- #endif /* MG_UECC_WORD_SIZE */
- #endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0) */
- #endif /* MG_UECC_SUPPORTS_secp224r1 */
- #if MG_UECC_SUPPORTS_secp256r1
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- static void vli_mmod_fast_secp256r1(mg_uecc_word_t *result,
- mg_uecc_word_t *product);
- #endif
- static const struct MG_UECC_Curve_t curve_secp256r1 = {
- num_words_secp256r1,
- num_bytes_secp256r1,
- 256, /* num_n_bits */
- {BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, 00, 00, 00, 00),
- BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00),
- BYTES_TO_WORDS_8(01, 00, 00, 00, FF, FF, FF, FF)},
- {BYTES_TO_WORDS_8(51, 25, 63, FC, C2, CA, B9, F3),
- BYTES_TO_WORDS_8(84, 9E, 17, A7, AD, FA, E6, BC),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF)},
- {BYTES_TO_WORDS_8(96, C2, 98, D8, 45, 39, A1, F4),
- BYTES_TO_WORDS_8(A0, 33, EB, 2D, 81, 7D, 03, 77),
- BYTES_TO_WORDS_8(F2, 40, A4, 63, E5, E6, BC, F8),
- BYTES_TO_WORDS_8(47, 42, 2C, E1, F2, D1, 17, 6B),
- BYTES_TO_WORDS_8(F5, 51, BF, 37, 68, 40, B6, CB),
- BYTES_TO_WORDS_8(CE, 5E, 31, 6B, 57, 33, CE, 2B),
- BYTES_TO_WORDS_8(16, 9E, 0F, 7C, 4A, EB, E7, 8E),
- BYTES_TO_WORDS_8(9B, 7F, 1A, FE, E2, 42, E3, 4F)},
- {BYTES_TO_WORDS_8(4B, 60, D2, 27, 3E, 3C, CE, 3B),
- BYTES_TO_WORDS_8(F6, B0, 53, CC, B0, 06, 1D, 65),
- BYTES_TO_WORDS_8(BC, 86, 98, 76, 55, BD, EB, B3),
- BYTES_TO_WORDS_8(E7, 93, 3A, AA, D8, 35, C6, 5A)},
- &double_jacobian_default,
- #if MG_UECC_SUPPORT_COMPRESSED_POINT
- &mod_sqrt_default,
- #endif
- &x_side_default,
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- &vli_mmod_fast_secp256r1
- #endif
- };
- MG_UECC_Curve mg_uecc_secp256r1(void) {
- return &curve_secp256r1;
- }
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1)
- /* Computes result = product % curve_p
- from http://www.nsa.gov/ia/_files/nist-routines.pdf */
- #if MG_UECC_WORD_SIZE == 1
- static void vli_mmod_fast_secp256r1(uint8_t *result, uint8_t *product) {
- uint8_t tmp[num_words_secp256r1];
- int8_t carry;
- /* t */
- mg_uecc_vli_set(result, product, num_words_secp256r1);
- /* s1 */
- tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
- tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0;
- tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0;
- tmp[12] = product[44];
- tmp[13] = product[45];
- tmp[14] = product[46];
- tmp[15] = product[47];
- tmp[16] = product[48];
- tmp[17] = product[49];
- tmp[18] = product[50];
- tmp[19] = product[51];
- tmp[20] = product[52];
- tmp[21] = product[53];
- tmp[22] = product[54];
- tmp[23] = product[55];
- tmp[24] = product[56];
- tmp[25] = product[57];
- tmp[26] = product[58];
- tmp[27] = product[59];
- tmp[28] = product[60];
- tmp[29] = product[61];
- tmp[30] = product[62];
- tmp[31] = product[63];
- carry = mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1);
- carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
- /* s2 */
- tmp[12] = product[48];
- tmp[13] = product[49];
- tmp[14] = product[50];
- tmp[15] = product[51];
- tmp[16] = product[52];
- tmp[17] = product[53];
- tmp[18] = product[54];
- tmp[19] = product[55];
- tmp[20] = product[56];
- tmp[21] = product[57];
- tmp[22] = product[58];
- tmp[23] = product[59];
- tmp[24] = product[60];
- tmp[25] = product[61];
- tmp[26] = product[62];
- tmp[27] = product[63];
- tmp[28] = tmp[29] = tmp[30] = tmp[31] = 0;
- carry += mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1);
- carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
- /* s3 */
- tmp[0] = product[32];
- tmp[1] = product[33];
- tmp[2] = product[34];
- tmp[3] = product[35];
- tmp[4] = product[36];
- tmp[5] = product[37];
- tmp[6] = product[38];
- tmp[7] = product[39];
- tmp[8] = product[40];
- tmp[9] = product[41];
- tmp[10] = product[42];
- tmp[11] = product[43];
- tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0;
- tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0;
- tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0;
- tmp[24] = product[56];
- tmp[25] = product[57];
- tmp[26] = product[58];
- tmp[27] = product[59];
- tmp[28] = product[60];
- tmp[29] = product[61];
- tmp[30] = product[62];
- tmp[31] = product[63];
- carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
- /* s4 */
- tmp[0] = product[36];
- tmp[1] = product[37];
- tmp[2] = product[38];
- tmp[3] = product[39];
- tmp[4] = product[40];
- tmp[5] = product[41];
- tmp[6] = product[42];
- tmp[7] = product[43];
- tmp[8] = product[44];
- tmp[9] = product[45];
- tmp[10] = product[46];
- tmp[11] = product[47];
- tmp[12] = product[52];
- tmp[13] = product[53];
- tmp[14] = product[54];
- tmp[15] = product[55];
- tmp[16] = product[56];
- tmp[17] = product[57];
- tmp[18] = product[58];
- tmp[19] = product[59];
- tmp[20] = product[60];
- tmp[21] = product[61];
- tmp[22] = product[62];
- tmp[23] = product[63];
- tmp[24] = product[52];
- tmp[25] = product[53];
- tmp[26] = product[54];
- tmp[27] = product[55];
- tmp[28] = product[32];
- tmp[29] = product[33];
- tmp[30] = product[34];
- tmp[31] = product[35];
- carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
- /* d1 */
- tmp[0] = product[44];
- tmp[1] = product[45];
- tmp[2] = product[46];
- tmp[3] = product[47];
- tmp[4] = product[48];
- tmp[5] = product[49];
- tmp[6] = product[50];
- tmp[7] = product[51];
- tmp[8] = product[52];
- tmp[9] = product[53];
- tmp[10] = product[54];
- tmp[11] = product[55];
- tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0;
- tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0;
- tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0;
- tmp[24] = product[32];
- tmp[25] = product[33];
- tmp[26] = product[34];
- tmp[27] = product[35];
- tmp[28] = product[40];
- tmp[29] = product[41];
- tmp[30] = product[42];
- tmp[31] = product[43];
- carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
- /* d2 */
- tmp[0] = product[48];
- tmp[1] = product[49];
- tmp[2] = product[50];
- tmp[3] = product[51];
- tmp[4] = product[52];
- tmp[5] = product[53];
- tmp[6] = product[54];
- tmp[7] = product[55];
- tmp[8] = product[56];
- tmp[9] = product[57];
- tmp[10] = product[58];
- tmp[11] = product[59];
- tmp[12] = product[60];
- tmp[13] = product[61];
- tmp[14] = product[62];
- tmp[15] = product[63];
- tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0;
- tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0;
- tmp[24] = product[36];
- tmp[25] = product[37];
- tmp[26] = product[38];
- tmp[27] = product[39];
- tmp[28] = product[44];
- tmp[29] = product[45];
- tmp[30] = product[46];
- tmp[31] = product[47];
- carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
- /* d3 */
- tmp[0] = product[52];
- tmp[1] = product[53];
- tmp[2] = product[54];
- tmp[3] = product[55];
- tmp[4] = product[56];
- tmp[5] = product[57];
- tmp[6] = product[58];
- tmp[7] = product[59];
- tmp[8] = product[60];
- tmp[9] = product[61];
- tmp[10] = product[62];
- tmp[11] = product[63];
- tmp[12] = product[32];
- tmp[13] = product[33];
- tmp[14] = product[34];
- tmp[15] = product[35];
- tmp[16] = product[36];
- tmp[17] = product[37];
- tmp[18] = product[38];
- tmp[19] = product[39];
- tmp[20] = product[40];
- tmp[21] = product[41];
- tmp[22] = product[42];
- tmp[23] = product[43];
- tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0;
- tmp[28] = product[48];
- tmp[29] = product[49];
- tmp[30] = product[50];
- tmp[31] = product[51];
- carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
- /* d4 */
- tmp[0] = product[56];
- tmp[1] = product[57];
- tmp[2] = product[58];
- tmp[3] = product[59];
- tmp[4] = product[60];
- tmp[5] = product[61];
- tmp[6] = product[62];
- tmp[7] = product[63];
- tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0;
- tmp[12] = product[36];
- tmp[13] = product[37];
- tmp[14] = product[38];
- tmp[15] = product[39];
- tmp[16] = product[40];
- tmp[17] = product[41];
- tmp[18] = product[42];
- tmp[19] = product[43];
- tmp[20] = product[44];
- tmp[21] = product[45];
- tmp[22] = product[46];
- tmp[23] = product[47];
- tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0;
- tmp[28] = product[52];
- tmp[29] = product[53];
- tmp[30] = product[54];
- tmp[31] = product[55];
- carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
- if (carry < 0) {
- do {
- carry += mg_uecc_vli_add(result, result, curve_secp256r1.p,
- num_words_secp256r1);
- } while (carry < 0);
- } else {
- while (carry || mg_uecc_vli_cmp_unsafe(curve_secp256r1.p, result,
- num_words_secp256r1) != 1) {
- carry -= mg_uecc_vli_sub(result, result, curve_secp256r1.p,
- num_words_secp256r1);
- }
- }
- }
- #elif MG_UECC_WORD_SIZE == 4
- static void vli_mmod_fast_secp256r1(uint32_t *result, uint32_t *product) {
- uint32_t tmp[num_words_secp256r1];
- int carry;
- /* t */
- mg_uecc_vli_set(result, product, num_words_secp256r1);
- /* s1 */
- tmp[0] = tmp[1] = tmp[2] = 0;
- tmp[3] = product[11];
- tmp[4] = product[12];
- tmp[5] = product[13];
- tmp[6] = product[14];
- tmp[7] = product[15];
- carry = (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1);
- carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
- /* s2 */
- tmp[3] = product[12];
- tmp[4] = product[13];
- tmp[5] = product[14];
- tmp[6] = product[15];
- tmp[7] = 0;
- carry += (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1);
- carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
- /* s3 */
- tmp[0] = product[8];
- tmp[1] = product[9];
- tmp[2] = product[10];
- tmp[3] = tmp[4] = tmp[5] = 0;
- tmp[6] = product[14];
- tmp[7] = product[15];
- carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
- /* s4 */
- tmp[0] = product[9];
- tmp[1] = product[10];
- tmp[2] = product[11];
- tmp[3] = product[13];
- tmp[4] = product[14];
- tmp[5] = product[15];
- tmp[6] = product[13];
- tmp[7] = product[8];
- carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
- /* d1 */
- tmp[0] = product[11];
- tmp[1] = product[12];
- tmp[2] = product[13];
- tmp[3] = tmp[4] = tmp[5] = 0;
- tmp[6] = product[8];
- tmp[7] = product[10];
- carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
- /* d2 */
- tmp[0] = product[12];
- tmp[1] = product[13];
- tmp[2] = product[14];
- tmp[3] = product[15];
- tmp[4] = tmp[5] = 0;
- tmp[6] = product[9];
- tmp[7] = product[11];
- carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
- /* d3 */
- tmp[0] = product[13];
- tmp[1] = product[14];
- tmp[2] = product[15];
- tmp[3] = product[8];
- tmp[4] = product[9];
- tmp[5] = product[10];
- tmp[6] = 0;
- tmp[7] = product[12];
- carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
- /* d4 */
- tmp[0] = product[14];
- tmp[1] = product[15];
- tmp[2] = 0;
- tmp[3] = product[9];
- tmp[4] = product[10];
- tmp[5] = product[11];
- tmp[6] = 0;
- tmp[7] = product[13];
- carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
- if (carry < 0) {
- do {
- carry += (int) mg_uecc_vli_add(result, result, curve_secp256r1.p,
- num_words_secp256r1);
- } while (carry < 0);
- } else {
- while (carry || mg_uecc_vli_cmp_unsafe(curve_secp256r1.p, result,
- num_words_secp256r1) != 1) {
- carry -= (int) mg_uecc_vli_sub(result, result, curve_secp256r1.p,
- num_words_secp256r1);
- }
- }
- }
- #else
- static void vli_mmod_fast_secp256r1(uint64_t *result, uint64_t *product) {
- uint64_t tmp[num_words_secp256r1];
- int carry;
- /* t */
- mg_uecc_vli_set(result, product, num_words_secp256r1);
- /* s1 */
- tmp[0] = 0;
- tmp[1] = product[5] & 0xffffffff00000000U;
- tmp[2] = product[6];
- tmp[3] = product[7];
- carry = (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1);
- carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
- /* s2 */
- tmp[1] = product[6] << 32;
- tmp[2] = (product[6] >> 32) | (product[7] << 32);
- tmp[3] = product[7] >> 32;
- carry += (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1);
- carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
- /* s3 */
- tmp[0] = product[4];
- tmp[1] = product[5] & 0xffffffff;
- tmp[2] = 0;
- tmp[3] = product[7];
- carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
- /* s4 */
- tmp[0] = (product[4] >> 32) | (product[5] << 32);
- tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000U);
- tmp[2] = product[7];
- tmp[3] = (product[6] >> 32) | (product[4] << 32);
- carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1);
- /* d1 */
- tmp[0] = (product[5] >> 32) | (product[6] << 32);
- tmp[1] = (product[6] >> 32);
- tmp[2] = 0;
- tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32);
- carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
- /* d2 */
- tmp[0] = product[6];
- tmp[1] = product[7];
- tmp[2] = 0;
- tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000);
- carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
- /* d3 */
- tmp[0] = (product[6] >> 32) | (product[7] << 32);
- tmp[1] = (product[7] >> 32) | (product[4] << 32);
- tmp[2] = (product[4] >> 32) | (product[5] << 32);
- tmp[3] = (product[6] << 32);
- carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
- /* d4 */
- tmp[0] = product[7];
- tmp[1] = product[4] & 0xffffffff00000000U;
- tmp[2] = product[5];
- tmp[3] = product[6] & 0xffffffff00000000U;
- carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1);
- if (carry < 0) {
- do {
- carry += (int) mg_uecc_vli_add(result, result, curve_secp256r1.p,
- num_words_secp256r1);
- } while (carry < 0);
- } else {
- while (carry || mg_uecc_vli_cmp_unsafe(curve_secp256r1.p, result,
- num_words_secp256r1) != 1) {
- carry -= (int) mg_uecc_vli_sub(result, result, curve_secp256r1.p,
- num_words_secp256r1);
- }
- }
- }
- #endif /* MG_UECC_WORD_SIZE */
- #endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1) */
- #endif /* MG_UECC_SUPPORTS_secp256r1 */
- #if MG_UECC_SUPPORTS_secp256k1
- static void double_jacobian_secp256k1(mg_uecc_word_t *X1, mg_uecc_word_t *Y1,
- mg_uecc_word_t *Z1, MG_UECC_Curve curve);
- static void x_side_secp256k1(mg_uecc_word_t *result, const mg_uecc_word_t *x,
- MG_UECC_Curve curve);
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- static void vli_mmod_fast_secp256k1(mg_uecc_word_t *result,
- mg_uecc_word_t *product);
- #endif
- static const struct MG_UECC_Curve_t curve_secp256k1 = {
- num_words_secp256k1,
- num_bytes_secp256k1,
- 256, /* num_n_bits */
- {BYTES_TO_WORDS_8(2F, FC, FF, FF, FE, FF, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)},
- {BYTES_TO_WORDS_8(41, 41, 36, D0, 8C, 5E, D2, BF),
- BYTES_TO_WORDS_8(3B, A0, 48, AF, E6, DC, AE, BA),
- BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)},
- {BYTES_TO_WORDS_8(98, 17, F8, 16, 5B, 81, F2, 59),
- BYTES_TO_WORDS_8(D9, 28, CE, 2D, DB, FC, 9B, 02),
- BYTES_TO_WORDS_8(07, 0B, 87, CE, 95, 62, A0, 55),
- BYTES_TO_WORDS_8(AC, BB, DC, F9, 7E, 66, BE, 79),
- BYTES_TO_WORDS_8(B8, D4, 10, FB, 8F, D0, 47, 9C),
- BYTES_TO_WORDS_8(19, 54, 85, A6, 48, B4, 17, FD),
- BYTES_TO_WORDS_8(A8, 08, 11, 0E, FC, FB, A4, 5D),
- BYTES_TO_WORDS_8(65, C4, A3, 26, 77, DA, 3A, 48)},
- {BYTES_TO_WORDS_8(07, 00, 00, 00, 00, 00, 00, 00),
- BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00),
- BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00),
- BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00)},
- &double_jacobian_secp256k1,
- #if MG_UECC_SUPPORT_COMPRESSED_POINT
- &mod_sqrt_default,
- #endif
- &x_side_secp256k1,
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- &vli_mmod_fast_secp256k1
- #endif
- };
- MG_UECC_Curve mg_uecc_secp256k1(void) {
- return &curve_secp256k1;
- }
- /* Double in place */
- static void double_jacobian_secp256k1(mg_uecc_word_t *X1, mg_uecc_word_t *Y1,
- mg_uecc_word_t *Z1, MG_UECC_Curve curve) {
- /* t1 = X, t2 = Y, t3 = Z */
- mg_uecc_word_t t4[num_words_secp256k1];
- mg_uecc_word_t t5[num_words_secp256k1];
- if (mg_uecc_vli_isZero(Z1, num_words_secp256k1)) {
- return;
- }
- mg_uecc_vli_modSquare_fast(t5, Y1, curve); /* t5 = y1^2 */
- mg_uecc_vli_modMult_fast(t4, X1, t5, curve); /* t4 = x1*y1^2 = A */
- mg_uecc_vli_modSquare_fast(X1, X1, curve); /* t1 = x1^2 */
- mg_uecc_vli_modSquare_fast(t5, t5, curve); /* t5 = y1^4 */
- mg_uecc_vli_modMult_fast(Z1, Y1, Z1, curve); /* t3 = y1*z1 = z3 */
- mg_uecc_vli_modAdd(Y1, X1, X1, curve->p,
- num_words_secp256k1); /* t2 = 2*x1^2 */
- mg_uecc_vli_modAdd(Y1, Y1, X1, curve->p,
- num_words_secp256k1); /* t2 = 3*x1^2 */
- if (mg_uecc_vli_testBit(Y1, 0)) {
- mg_uecc_word_t carry =
- mg_uecc_vli_add(Y1, Y1, curve->p, num_words_secp256k1);
- mg_uecc_vli_rshift1(Y1, num_words_secp256k1);
- Y1[num_words_secp256k1 - 1] |= carry << (MG_UECC_WORD_BITS - 1);
- } else {
- mg_uecc_vli_rshift1(Y1, num_words_secp256k1);
- }
- /* t2 = 3/2*(x1^2) = B */
- mg_uecc_vli_modSquare_fast(X1, Y1, curve); /* t1 = B^2 */
- mg_uecc_vli_modSub(X1, X1, t4, curve->p,
- num_words_secp256k1); /* t1 = B^2 - A */
- mg_uecc_vli_modSub(X1, X1, t4, curve->p,
- num_words_secp256k1); /* t1 = B^2 - 2A = x3 */
- mg_uecc_vli_modSub(t4, t4, X1, curve->p,
- num_words_secp256k1); /* t4 = A - x3 */
- mg_uecc_vli_modMult_fast(Y1, Y1, t4, curve); /* t2 = B * (A - x3) */
- mg_uecc_vli_modSub(Y1, Y1, t5, curve->p,
- num_words_secp256k1); /* t2 = B * (A - x3) - y1^4 = y3 */
- }
- /* Computes result = x^3 + b. result must not overlap x. */
- static void x_side_secp256k1(mg_uecc_word_t *result, const mg_uecc_word_t *x,
- MG_UECC_Curve curve) {
- mg_uecc_vli_modSquare_fast(result, x, curve); /* r = x^2 */
- mg_uecc_vli_modMult_fast(result, result, x, curve); /* r = x^3 */
- mg_uecc_vli_modAdd(result, result, curve->b, curve->p,
- num_words_secp256k1); /* r = x^3 + b */
- }
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256k1)
- static void omega_mult_secp256k1(mg_uecc_word_t *result,
- const mg_uecc_word_t *right);
- static void vli_mmod_fast_secp256k1(mg_uecc_word_t *result,
- mg_uecc_word_t *product) {
- mg_uecc_word_t tmp[2 * num_words_secp256k1];
- mg_uecc_word_t carry;
- mg_uecc_vli_clear(tmp, num_words_secp256k1);
- mg_uecc_vli_clear(tmp + num_words_secp256k1, num_words_secp256k1);
- omega_mult_secp256k1(tmp,
- product + num_words_secp256k1); /* (Rq, q) = q * c */
- carry = mg_uecc_vli_add(result, product, tmp,
- num_words_secp256k1); /* (C, r) = r + q */
- mg_uecc_vli_clear(product, num_words_secp256k1);
- omega_mult_secp256k1(product, tmp + num_words_secp256k1); /* Rq*c */
- carry += mg_uecc_vli_add(result, result, product,
- num_words_secp256k1); /* (C1, r) = r + Rq*c */
- while (carry > 0) {
- --carry;
- mg_uecc_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1);
- }
- if (mg_uecc_vli_cmp_unsafe(result, curve_secp256k1.p, num_words_secp256k1) >
- 0) {
- mg_uecc_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1);
- }
- }
- #if MG_UECC_WORD_SIZE == 1
- static void omega_mult_secp256k1(uint8_t *result, const uint8_t *right) {
- /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */
- mg_uecc_word_t r0 = 0;
- mg_uecc_word_t r1 = 0;
- mg_uecc_word_t r2 = 0;
- wordcount_t k;
- /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */
- muladd(0xD1, right[0], &r0, &r1, &r2);
- result[0] = r0;
- r0 = r1;
- r1 = r2;
- /* r2 is still 0 */
- for (k = 1; k < num_words_secp256k1; ++k) {
- muladd(0x03, right[k - 1], &r0, &r1, &r2);
- muladd(0xD1, right[k], &r0, &r1, &r2);
- result[k] = r0;
- r0 = r1;
- r1 = r2;
- r2 = 0;
- }
- muladd(0x03, right[num_words_secp256k1 - 1], &r0, &r1, &r2);
- result[num_words_secp256k1] = r0;
- result[num_words_secp256k1 + 1] = r1;
- /* add the 2^32 multiple */
- result[4 + num_words_secp256k1] =
- mg_uecc_vli_add(result + 4, result + 4, right, num_words_secp256k1);
- }
- #elif MG_UECC_WORD_SIZE == 4
- static void omega_mult_secp256k1(uint32_t *result, const uint32_t *right) {
- /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */
- uint32_t carry = 0;
- wordcount_t k;
- for (k = 0; k < num_words_secp256k1; ++k) {
- uint64_t p = (uint64_t) 0x3D1 * right[k] + carry;
- result[k] = (uint32_t) p;
- carry = p >> 32;
- }
- result[num_words_secp256k1] = carry;
- /* add the 2^32 multiple */
- result[1 + num_words_secp256k1] =
- mg_uecc_vli_add(result + 1, result + 1, right, num_words_secp256k1);
- }
- #else
- static void omega_mult_secp256k1(uint64_t *result, const uint64_t *right) {
- mg_uecc_word_t r0 = 0;
- mg_uecc_word_t r1 = 0;
- mg_uecc_word_t r2 = 0;
- wordcount_t k;
- /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */
- for (k = 0; k < num_words_secp256k1; ++k) {
- muladd(0x1000003D1ull, right[k], &r0, &r1, &r2);
- result[k] = r0;
- r0 = r1;
- r1 = r2;
- r2 = 0;
- }
- result[num_words_secp256k1] = r0;
- }
- #endif /* MG_UECC_WORD_SIZE */
- #endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0 && && !asm_mmod_fast_secp256k1) */
- #endif /* MG_UECC_SUPPORTS_secp256k1 */
- #if MG_UECC_SUPPORTS_secp384r1
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- static void vli_mmod_fast_secp384r1(mg_uecc_word_t *result,
- mg_uecc_word_t *product);
- #endif
- static const struct MG_UECC_Curve_t curve_secp384r1 = {
- num_words_secp384r1,
- num_bytes_secp384r1,
- 384, /* num_n_bits */
- /* p */
- {BYTES_TO_WORDS_8(FF, FF, FF, FF, 00, 00, 00, 00),
- BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)},
- /* n */
- {BYTES_TO_WORDS_8(73, 29, C5, CC, 6A, 19, EC, EC),
- BYTES_TO_WORDS_8(7A, A7, B0, 48, B2, 0D, 1A, 58),
- BYTES_TO_WORDS_8(DF, 2D, 37, F4, 81, 4D, 63, C7),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF),
- BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)},
- /* G = (Gx || Gy) little-endian */
- {BYTES_TO_WORDS_8(B7, 0A, 76, 72, 38, 5E, 54, 3A),
- BYTES_TO_WORDS_8(6C, 29, 55, BF, 5D, F2, 02, 55),
- BYTES_TO_WORDS_8(38, 2A, 54, 82, E0, 41, F7, 59),
- BYTES_TO_WORDS_8(98, 9B, A7, 8B, 62, 3B, 1D, 6E),
- BYTES_TO_WORDS_8(74, AD, 20, F3, 1E, C7, B1, 8E),
- BYTES_TO_WORDS_8(37, 05, 8B, BE, 22, CA, 87, AA),
- BYTES_TO_WORDS_8(5F, 0E, EA, 90, 7C, 1D, 43, 7A),
- BYTES_TO_WORDS_8(9D, 81, 7E, 1D, CE, B1, 60, 0A),
- BYTES_TO_WORDS_8(C0, B8, F0, B5, 13, 31, DA, E9),
- BYTES_TO_WORDS_8(7C, 14, 9A, 28, BD, 1D, F4, F8),
- BYTES_TO_WORDS_8(29, DC, 92, 92, BF, 98, 9E, 5D),
- BYTES_TO_WORDS_8(6F, 2C, 26, 96, 4A, DE, 17, 36)},
- /* b */
- {BYTES_TO_WORDS_8(EF, 2A, EC, D3, ED, C8, 85, 2A),
- BYTES_TO_WORDS_8(9D, D1, 2E, 8A, 8D, 39, 56, C6),
- BYTES_TO_WORDS_8(5A, 87, 13, 50, 8F, 08, 14, 03),
- BYTES_TO_WORDS_8(12, 41, 81, FE, 6E, 9C, 1D, 18),
- BYTES_TO_WORDS_8(19, 2D, F8, E3, 6B, 05, 8E, 98),
- BYTES_TO_WORDS_8(E4, E7, 3E, E2, A7, 2F, 31, B3)},
- &double_jacobian_default,
- #if MG_UECC_SUPPORT_COMPRESSED_POINT
- &mod_sqrt_default,
- #endif
- &x_side_default,
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- &vli_mmod_fast_secp384r1
- #endif
- };
- MG_UECC_Curve mg_uecc_secp384r1(void) {
- return &curve_secp384r1;
- }
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- static void vli_mmod_fast_secp384r1(mg_uecc_word_t *result,
- mg_uecc_word_t *product) {
- mg_uecc_vli_mmod(result, product, curve_secp384r1.p, num_words_secp384r1);
- }
- #endif
- #endif /* MG_UECC_SUPPORTS_secp384r1 */
- #endif /* _UECC_CURVE_SPECIFIC_H_ */
- /* Returns 1 if 'point' is the point at infinity, 0 otherwise. */
- #define EccPoint_isZero(point, curve) \
- mg_uecc_vli_isZero((point), (wordcount_t) ((curve)->num_words * 2))
- /* Point multiplication algorithm using Montgomery's ladder with co-Z
- coordinates. From http://eprint.iacr.org/2011/338.pdf
- */
- /* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */
- static void apply_z(mg_uecc_word_t *X1, mg_uecc_word_t *Y1,
- const mg_uecc_word_t *const Z, MG_UECC_Curve curve) {
- mg_uecc_word_t t1[MG_UECC_MAX_WORDS];
- mg_uecc_vli_modSquare_fast(t1, Z, curve); /* z^2 */
- mg_uecc_vli_modMult_fast(X1, X1, t1, curve); /* x1 * z^2 */
- mg_uecc_vli_modMult_fast(t1, t1, Z, curve); /* z^3 */
- mg_uecc_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */
- }
- /* P = (x1, y1) => 2P, (x2, y2) => P' */
- static void XYcZ_initial_double(mg_uecc_word_t *X1, mg_uecc_word_t *Y1,
- mg_uecc_word_t *X2, mg_uecc_word_t *Y2,
- const mg_uecc_word_t *const initial_Z,
- MG_UECC_Curve curve) {
- mg_uecc_word_t z[MG_UECC_MAX_WORDS];
- wordcount_t num_words = curve->num_words;
- if (initial_Z) {
- mg_uecc_vli_set(z, initial_Z, num_words);
- } else {
- mg_uecc_vli_clear(z, num_words);
- z[0] = 1;
- }
- mg_uecc_vli_set(X2, X1, num_words);
- mg_uecc_vli_set(Y2, Y1, num_words);
- apply_z(X1, Y1, z, curve);
- curve->double_jacobian(X1, Y1, z, curve);
- apply_z(X2, Y2, z, curve);
- }
- /* Input P = (x1, y1, Z), Q = (x2, y2, Z)
- Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3)
- or P => P', Q => P + Q
- */
- static void XYcZ_add(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, mg_uecc_word_t *X2,
- mg_uecc_word_t *Y2, MG_UECC_Curve curve) {
- /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
- mg_uecc_word_t t5[MG_UECC_MAX_WORDS] = {0};
- wordcount_t num_words = curve->num_words;
- mg_uecc_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */
- mg_uecc_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */
- mg_uecc_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */
- mg_uecc_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */
- mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */
- mg_uecc_vli_modSquare_fast(t5, Y2, curve); /* t5 = (y2 - y1)^2 = D */
- mg_uecc_vli_modSub(t5, t5, X1, curve->p, num_words); /* t5 = D - B */
- mg_uecc_vli_modSub(t5, t5, X2, curve->p, num_words); /* t5 = D - B - C = x3 */
- mg_uecc_vli_modSub(X2, X2, X1, curve->p, num_words); /* t3 = C - B */
- mg_uecc_vli_modMult_fast(Y1, Y1, X2, curve); /* t2 = y1*(C - B) */
- mg_uecc_vli_modSub(X2, X1, t5, curve->p, num_words); /* t3 = B - x3 */
- mg_uecc_vli_modMult_fast(Y2, Y2, X2, curve); /* t4 = (y2 - y1)*(B - x3) */
- mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y3 */
- mg_uecc_vli_set(X2, t5, num_words);
- }
- /* Input P = (x1, y1, Z), Q = (x2, y2, Z)
- Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3)
- or P => P - Q, Q => P + Q
- */
- static void XYcZ_addC(mg_uecc_word_t *X1, mg_uecc_word_t *Y1,
- mg_uecc_word_t *X2, mg_uecc_word_t *Y2,
- MG_UECC_Curve curve) {
- /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
- mg_uecc_word_t t5[MG_UECC_MAX_WORDS] = {0};
- mg_uecc_word_t t6[MG_UECC_MAX_WORDS];
- mg_uecc_word_t t7[MG_UECC_MAX_WORDS];
- wordcount_t num_words = curve->num_words;
- mg_uecc_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */
- mg_uecc_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */
- mg_uecc_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */
- mg_uecc_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */
- mg_uecc_vli_modAdd(t5, Y2, Y1, curve->p, num_words); /* t5 = y2 + y1 */
- mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */
- mg_uecc_vli_modSub(t6, X2, X1, curve->p, num_words); /* t6 = C - B */
- mg_uecc_vli_modMult_fast(Y1, Y1, t6, curve); /* t2 = y1 * (C - B) = E */
- mg_uecc_vli_modAdd(t6, X1, X2, curve->p, num_words); /* t6 = B + C */
- mg_uecc_vli_modSquare_fast(X2, Y2, curve); /* t3 = (y2 - y1)^2 = D */
- mg_uecc_vli_modSub(X2, X2, t6, curve->p,
- num_words); /* t3 = D - (B + C) = x3 */
- mg_uecc_vli_modSub(t7, X1, X2, curve->p, num_words); /* t7 = B - x3 */
- mg_uecc_vli_modMult_fast(Y2, Y2, t7, curve); /* t4 = (y2 - y1)*(B - x3) */
- mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p,
- num_words); /* t4 = (y2 - y1)*(B - x3) - E = y3 */
- mg_uecc_vli_modSquare_fast(t7, t5, curve); /* t7 = (y2 + y1)^2 = F */
- mg_uecc_vli_modSub(t7, t7, t6, curve->p,
- num_words); /* t7 = F - (B + C) = x3' */
- mg_uecc_vli_modSub(t6, t7, X1, curve->p, num_words); /* t6 = x3' - B */
- mg_uecc_vli_modMult_fast(t6, t6, t5, curve); /* t6 = (y2+y1)*(x3' - B) */
- mg_uecc_vli_modSub(Y1, t6, Y1, curve->p,
- num_words); /* t2 = (y2+y1)*(x3' - B) - E = y3' */
- mg_uecc_vli_set(X1, t7, num_words);
- }
- /* result may overlap point. */
- static void EccPoint_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point,
- const mg_uecc_word_t *scalar,
- const mg_uecc_word_t *initial_Z, bitcount_t num_bits,
- MG_UECC_Curve curve) {
- /* R0 and R1 */
- mg_uecc_word_t Rx[2][MG_UECC_MAX_WORDS];
- mg_uecc_word_t Ry[2][MG_UECC_MAX_WORDS];
- mg_uecc_word_t z[MG_UECC_MAX_WORDS];
- bitcount_t i;
- mg_uecc_word_t nb;
- wordcount_t num_words = curve->num_words;
- mg_uecc_vli_set(Rx[1], point, num_words);
- mg_uecc_vli_set(Ry[1], point + num_words, num_words);
- XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], initial_Z, curve);
- for (i = num_bits - 2; i > 0; --i) {
- nb = !mg_uecc_vli_testBit(scalar, i);
- XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve);
- XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve);
- }
- nb = !mg_uecc_vli_testBit(scalar, 0);
- XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve);
- /* Find final 1/Z value. */
- mg_uecc_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */
- mg_uecc_vli_modMult_fast(z, z, Ry[1 - nb], curve); /* Yb * (X1 - X0) */
- mg_uecc_vli_modMult_fast(z, z, point, curve); /* xP * Yb * (X1 - X0) */
- mg_uecc_vli_modInv(z, z, curve->p, num_words); /* 1 / (xP * Yb * (X1 - X0)) */
- /* yP / (xP * Yb * (X1 - X0)) */
- mg_uecc_vli_modMult_fast(z, z, point + num_words, curve);
- mg_uecc_vli_modMult_fast(z, z, Rx[1 - nb],
- curve); /* Xb * yP / (xP * Yb * (X1 - X0)) */
- /* End 1/Z calculation */
- XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve);
- apply_z(Rx[0], Ry[0], z, curve);
- mg_uecc_vli_set(result, Rx[0], num_words);
- mg_uecc_vli_set(result + num_words, Ry[0], num_words);
- }
- static mg_uecc_word_t regularize_k(const mg_uecc_word_t *const k,
- mg_uecc_word_t *k0, mg_uecc_word_t *k1,
- MG_UECC_Curve curve) {
- wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits);
- bitcount_t num_n_bits = curve->num_n_bits;
- mg_uecc_word_t carry =
- mg_uecc_vli_add(k0, k, curve->n, num_n_words) ||
- (num_n_bits < ((bitcount_t) num_n_words * MG_UECC_WORD_SIZE * 8) &&
- mg_uecc_vli_testBit(k0, num_n_bits));
- mg_uecc_vli_add(k1, k0, curve->n, num_n_words);
- return carry;
- }
- /* Generates a random integer in the range 0 < random < top.
- Both random and top have num_words words. */
- MG_UECC_VLI_API int mg_uecc_generate_random_int(mg_uecc_word_t *random,
- const mg_uecc_word_t *top,
- wordcount_t num_words) {
- mg_uecc_word_t mask = (mg_uecc_word_t) -1;
- mg_uecc_word_t tries;
- bitcount_t num_bits = mg_uecc_vli_numBits(top, num_words);
- for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) {
- unsigned int len = (unsigned int) (num_words * MG_UECC_WORD_SIZE);
- if (!(g_rng_function == NULL ? mg_random((uint8_t *) random, len)
- : g_rng_function((uint8_t *) random, len)))
- return 0;
- random[num_words - 1] &=
- mask >> ((bitcount_t) (num_words * MG_UECC_WORD_SIZE * 8 - num_bits));
- if (!mg_uecc_vli_isZero(random, num_words) &&
- mg_uecc_vli_cmp(top, random, num_words) == 1) {
- return 1;
- }
- }
- return 0;
- }
- static mg_uecc_word_t EccPoint_compute_public_key(mg_uecc_word_t *result,
- mg_uecc_word_t *private_key,
- MG_UECC_Curve curve) {
- mg_uecc_word_t tmp1[MG_UECC_MAX_WORDS];
- mg_uecc_word_t tmp2[MG_UECC_MAX_WORDS];
- mg_uecc_word_t *p2[2] = {tmp1, tmp2};
- mg_uecc_word_t *initial_Z = 0;
- mg_uecc_word_t carry;
- /* Regularize the bitcount for the private key so that attackers cannot use a
- side channel attack to learn the number of leading zeros. */
- carry = regularize_k(private_key, tmp1, tmp2, curve);
- /* If an RNG function was specified, try to get a random initial Z value to
- improve protection against side-channel attacks. */
- if (g_rng_function) {
- if (!mg_uecc_generate_random_int(p2[carry], curve->p, curve->num_words)) {
- return 0;
- }
- initial_Z = p2[carry];
- }
- EccPoint_mult(result, curve->G, p2[!carry], initial_Z,
- (bitcount_t) (curve->num_n_bits + 1), curve);
- if (EccPoint_isZero(result, curve)) {
- return 0;
- }
- return 1;
- }
- #if MG_UECC_WORD_SIZE == 1
- MG_UECC_VLI_API void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes,
- const uint8_t *native) {
- wordcount_t i;
- for (i = 0; i < num_bytes; ++i) {
- bytes[i] = native[(num_bytes - 1) - i];
- }
- }
- MG_UECC_VLI_API void mg_uecc_vli_bytesToNative(uint8_t *native,
- const uint8_t *bytes,
- int num_bytes) {
- mg_uecc_vli_nativeToBytes(native, num_bytes, bytes);
- }
- #else
- MG_UECC_VLI_API void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes,
- const mg_uecc_word_t *native) {
- int i;
- for (i = 0; i < num_bytes; ++i) {
- unsigned b = (unsigned) (num_bytes - 1 - i);
- bytes[i] = (uint8_t) (native[b / MG_UECC_WORD_SIZE] >>
- (8 * (b % MG_UECC_WORD_SIZE)));
- }
- }
- MG_UECC_VLI_API void mg_uecc_vli_bytesToNative(mg_uecc_word_t *native,
- const uint8_t *bytes,
- int num_bytes) {
- int i;
- mg_uecc_vli_clear(native,
- (wordcount_t) ((num_bytes + (MG_UECC_WORD_SIZE - 1)) /
- MG_UECC_WORD_SIZE));
- for (i = 0; i < num_bytes; ++i) {
- unsigned b = (unsigned) (num_bytes - 1 - i);
- native[b / MG_UECC_WORD_SIZE] |= (mg_uecc_word_t) bytes[i]
- << (8 * (b % MG_UECC_WORD_SIZE));
- }
- }
- #endif /* MG_UECC_WORD_SIZE */
- int mg_uecc_make_key(uint8_t *public_key, uint8_t *private_key,
- MG_UECC_Curve curve) {
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- mg_uecc_word_t *_private = (mg_uecc_word_t *) private_key;
- mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key;
- #else
- mg_uecc_word_t _private[MG_UECC_MAX_WORDS];
- mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2];
- #endif
- mg_uecc_word_t tries;
- for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) {
- if (!mg_uecc_generate_random_int(_private, curve->n,
- BITS_TO_WORDS(curve->num_n_bits))) {
- return 0;
- }
- if (EccPoint_compute_public_key(_public, _private, curve)) {
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0
- mg_uecc_vli_nativeToBytes(private_key, BITS_TO_BYTES(curve->num_n_bits),
- _private);
- mg_uecc_vli_nativeToBytes(public_key, curve->num_bytes, _public);
- mg_uecc_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes,
- _public + curve->num_words);
- #endif
- return 1;
- }
- }
- return 0;
- }
- int mg_uecc_shared_secret(const uint8_t *public_key, const uint8_t *private_key,
- uint8_t *secret, MG_UECC_Curve curve) {
- mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2];
- mg_uecc_word_t _private[MG_UECC_MAX_WORDS];
- mg_uecc_word_t tmp[MG_UECC_MAX_WORDS];
- mg_uecc_word_t *p2[2] = {_private, tmp};
- mg_uecc_word_t *initial_Z = 0;
- mg_uecc_word_t carry;
- wordcount_t num_words = curve->num_words;
- wordcount_t num_bytes = curve->num_bytes;
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- bcopy((uint8_t *) _private, private_key, num_bytes);
- bcopy((uint8_t *) _public, public_key, num_bytes * 2);
- #else
- mg_uecc_vli_bytesToNative(_private, private_key,
- BITS_TO_BYTES(curve->num_n_bits));
- mg_uecc_vli_bytesToNative(_public, public_key, num_bytes);
- mg_uecc_vli_bytesToNative(_public + num_words, public_key + num_bytes,
- num_bytes);
- #endif
- /* Regularize the bitcount for the private key so that attackers cannot use a
- side channel attack to learn the number of leading zeros. */
- carry = regularize_k(_private, _private, tmp, curve);
- /* If an RNG function was specified, try to get a random initial Z value to
- improve protection against side-channel attacks. */
- if (g_rng_function) {
- if (!mg_uecc_generate_random_int(p2[carry], curve->p, num_words)) {
- return 0;
- }
- initial_Z = p2[carry];
- }
- EccPoint_mult(_public, _public, p2[!carry], initial_Z,
- (bitcount_t) (curve->num_n_bits + 1), curve);
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- bcopy((uint8_t *) secret, (uint8_t *) _public, num_bytes);
- #else
- mg_uecc_vli_nativeToBytes(secret, num_bytes, _public);
- #endif
- return !EccPoint_isZero(_public, curve);
- }
- #if MG_UECC_SUPPORT_COMPRESSED_POINT
- void mg_uecc_compress(const uint8_t *public_key, uint8_t *compressed,
- MG_UECC_Curve curve) {
- wordcount_t i;
- for (i = 0; i < curve->num_bytes; ++i) {
- compressed[i + 1] = public_key[i];
- }
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- compressed[0] = 2 + (public_key[curve->num_bytes] & 0x01);
- #else
- compressed[0] = 2 + (public_key[curve->num_bytes * 2 - 1] & 0x01);
- #endif
- }
- void mg_uecc_decompress(const uint8_t *compressed, uint8_t *public_key,
- MG_UECC_Curve curve) {
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- mg_uecc_word_t *point = (mg_uecc_word_t *) public_key;
- #else
- mg_uecc_word_t point[MG_UECC_MAX_WORDS * 2];
- #endif
- mg_uecc_word_t *y = point + curve->num_words;
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- bcopy(public_key, compressed + 1, curve->num_bytes);
- #else
- mg_uecc_vli_bytesToNative(point, compressed + 1, curve->num_bytes);
- #endif
- curve->x_side(y, point, curve);
- curve->mod_sqrt(y, curve);
- if ((uint8_t) (y[0] & 0x01) != (compressed[0] & 0x01)) {
- mg_uecc_vli_sub(y, curve->p, y, curve->num_words);
- }
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0
- mg_uecc_vli_nativeToBytes(public_key, curve->num_bytes, point);
- mg_uecc_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes, y);
- #endif
- }
- #endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */
- MG_UECC_VLI_API int mg_uecc_valid_point(const mg_uecc_word_t *point,
- MG_UECC_Curve curve) {
- mg_uecc_word_t tmp1[MG_UECC_MAX_WORDS];
- mg_uecc_word_t tmp2[MG_UECC_MAX_WORDS];
- wordcount_t num_words = curve->num_words;
- /* The point at infinity is invalid. */
- if (EccPoint_isZero(point, curve)) {
- return 0;
- }
- /* x and y must be smaller than p. */
- if (mg_uecc_vli_cmp_unsafe(curve->p, point, num_words) != 1 ||
- mg_uecc_vli_cmp_unsafe(curve->p, point + num_words, num_words) != 1) {
- return 0;
- }
- mg_uecc_vli_modSquare_fast(tmp1, point + num_words, curve);
- curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */
- /* Make sure that y^2 == x^3 + ax + b */
- return (int) (mg_uecc_vli_equal(tmp1, tmp2, num_words));
- }
- int mg_uecc_valid_public_key(const uint8_t *public_key, MG_UECC_Curve curve) {
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key;
- #else
- mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2];
- #endif
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0
- mg_uecc_vli_bytesToNative(_public, public_key, curve->num_bytes);
- mg_uecc_vli_bytesToNative(_public + curve->num_words,
- public_key + curve->num_bytes, curve->num_bytes);
- #endif
- return mg_uecc_valid_point(_public, curve);
- }
- int mg_uecc_compute_public_key(const uint8_t *private_key, uint8_t *public_key,
- MG_UECC_Curve curve) {
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- mg_uecc_word_t *_private = (mg_uecc_word_t *) private_key;
- mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key;
- #else
- mg_uecc_word_t _private[MG_UECC_MAX_WORDS];
- mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2];
- #endif
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0
- mg_uecc_vli_bytesToNative(_private, private_key,
- BITS_TO_BYTES(curve->num_n_bits));
- #endif
- /* Make sure the private key is in the range [1, n-1]. */
- if (mg_uecc_vli_isZero(_private, BITS_TO_WORDS(curve->num_n_bits))) {
- return 0;
- }
- if (mg_uecc_vli_cmp(curve->n, _private, BITS_TO_WORDS(curve->num_n_bits)) !=
- 1) {
- return 0;
- }
- /* Compute public key. */
- if (!EccPoint_compute_public_key(_public, _private, curve)) {
- return 0;
- }
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0
- mg_uecc_vli_nativeToBytes(public_key, curve->num_bytes, _public);
- mg_uecc_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes,
- _public + curve->num_words);
- #endif
- return 1;
- }
- /* -------- ECDSA code -------- */
- static void bits2int(mg_uecc_word_t *native, const uint8_t *bits,
- unsigned bits_size, MG_UECC_Curve curve) {
- unsigned num_n_bytes = (unsigned) BITS_TO_BYTES(curve->num_n_bits);
- unsigned num_n_words = (unsigned) BITS_TO_WORDS(curve->num_n_bits);
- int shift;
- mg_uecc_word_t carry;
- mg_uecc_word_t *ptr;
- if (bits_size > num_n_bytes) {
- bits_size = num_n_bytes;
- }
- mg_uecc_vli_clear(native, (wordcount_t) num_n_words);
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- bcopy((uint8_t *) native, bits, bits_size);
- #else
- mg_uecc_vli_bytesToNative(native, bits, (int) bits_size);
- #endif
- if (bits_size * 8 <= (unsigned) curve->num_n_bits) {
- return;
- }
- shift = (int) bits_size * 8 - curve->num_n_bits;
- carry = 0;
- ptr = native + num_n_words;
- while (ptr-- > native) {
- mg_uecc_word_t temp = *ptr;
- *ptr = (temp >> shift) | carry;
- carry = temp << (MG_UECC_WORD_BITS - shift);
- }
- /* Reduce mod curve_n */
- if (mg_uecc_vli_cmp_unsafe(curve->n, native, (wordcount_t) num_n_words) !=
- 1) {
- mg_uecc_vli_sub(native, native, curve->n, (wordcount_t) num_n_words);
- }
- }
- static int mg_uecc_sign_with_k_internal(const uint8_t *private_key,
- const uint8_t *message_hash,
- unsigned hash_size, mg_uecc_word_t *k,
- uint8_t *signature,
- MG_UECC_Curve curve) {
- mg_uecc_word_t tmp[MG_UECC_MAX_WORDS];
- mg_uecc_word_t s[MG_UECC_MAX_WORDS];
- mg_uecc_word_t *k2[2] = {tmp, s};
- mg_uecc_word_t *initial_Z = 0;
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- mg_uecc_word_t *p = (mg_uecc_word_t *) signature;
- #else
- mg_uecc_word_t p[MG_UECC_MAX_WORDS * 2];
- #endif
- mg_uecc_word_t carry;
- wordcount_t num_words = curve->num_words;
- wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits);
- bitcount_t num_n_bits = curve->num_n_bits;
- /* Make sure 0 < k < curve_n */
- if (mg_uecc_vli_isZero(k, num_words) ||
- mg_uecc_vli_cmp(curve->n, k, num_n_words) != 1) {
- return 0;
- }
- carry = regularize_k(k, tmp, s, curve);
- /* If an RNG function was specified, try to get a random initial Z value to
- improve protection against side-channel attacks. */
- if (g_rng_function) {
- if (!mg_uecc_generate_random_int(k2[carry], curve->p, num_words)) {
- return 0;
- }
- initial_Z = k2[carry];
- }
- EccPoint_mult(p, curve->G, k2[!carry], initial_Z,
- (bitcount_t) (num_n_bits + 1), curve);
- if (mg_uecc_vli_isZero(p, num_words)) {
- return 0;
- }
- /* If an RNG function was specified, get a random number
- to prevent side channel analysis of k. */
- if (!g_rng_function) {
- mg_uecc_vli_clear(tmp, num_n_words);
- tmp[0] = 1;
- } else if (!mg_uecc_generate_random_int(tmp, curve->n, num_n_words)) {
- return 0;
- }
- /* Prevent side channel analysis of mg_uecc_vli_modInv() to determine
- bits of k / the private key by premultiplying by a random number */
- mg_uecc_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k' = rand * k */
- mg_uecc_vli_modInv(k, k, curve->n, num_n_words); /* k = 1 / k' */
- mg_uecc_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k = 1 / k */
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0
- mg_uecc_vli_nativeToBytes(signature, curve->num_bytes, p); /* store r */
- #endif
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- bcopy((uint8_t *) tmp, private_key, BITS_TO_BYTES(curve->num_n_bits));
- #else
- mg_uecc_vli_bytesToNative(tmp, private_key,
- BITS_TO_BYTES(curve->num_n_bits)); /* tmp = d */
- #endif
- s[num_n_words - 1] = 0;
- mg_uecc_vli_set(s, p, num_words);
- mg_uecc_vli_modMult(s, tmp, s, curve->n, num_n_words); /* s = r*d */
- bits2int(tmp, message_hash, hash_size, curve);
- mg_uecc_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */
- mg_uecc_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */
- if (mg_uecc_vli_numBits(s, num_n_words) > (bitcount_t) curve->num_bytes * 8) {
- return 0;
- }
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- bcopy((uint8_t *) signature + curve->num_bytes, (uint8_t *) s,
- curve->num_bytes);
- #else
- mg_uecc_vli_nativeToBytes(signature + curve->num_bytes, curve->num_bytes, s);
- #endif
- return 1;
- }
- #if 0
- /* For testing - sign with an explicitly specified k value */
- int mg_uecc_sign_with_k(const uint8_t *private_key, const uint8_t *message_hash,
- unsigned hash_size, const uint8_t *k, uint8_t *signature,
- MG_UECC_Curve curve) {
- mg_uecc_word_t k2[MG_UECC_MAX_WORDS];
- bits2int(k2, k, (unsigned) BITS_TO_BYTES(curve->num_n_bits), curve);
- return mg_uecc_sign_with_k_internal(private_key, message_hash, hash_size, k2,
- signature, curve);
- }
- #endif
- int mg_uecc_sign(const uint8_t *private_key, const uint8_t *message_hash,
- unsigned hash_size, uint8_t *signature, MG_UECC_Curve curve) {
- mg_uecc_word_t k[MG_UECC_MAX_WORDS];
- mg_uecc_word_t tries;
- for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) {
- if (!mg_uecc_generate_random_int(k, curve->n,
- BITS_TO_WORDS(curve->num_n_bits))) {
- return 0;
- }
- if (mg_uecc_sign_with_k_internal(private_key, message_hash, hash_size, k,
- signature, curve)) {
- return 1;
- }
- }
- return 0;
- }
- /* Compute an HMAC using K as a key (as in RFC 6979). Note that K is always
- the same size as the hash result size. */
- static void HMAC_init(const MG_UECC_HashContext *hash_context,
- const uint8_t *K) {
- uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size;
- unsigned i;
- for (i = 0; i < hash_context->result_size; ++i) pad[i] = K[i] ^ 0x36;
- for (; i < hash_context->block_size; ++i) pad[i] = 0x36;
- hash_context->init_hash(hash_context);
- hash_context->update_hash(hash_context, pad, hash_context->block_size);
- }
- static void HMAC_update(const MG_UECC_HashContext *hash_context,
- const uint8_t *message, unsigned message_size) {
- hash_context->update_hash(hash_context, message, message_size);
- }
- static void HMAC_finish(const MG_UECC_HashContext *hash_context,
- const uint8_t *K, uint8_t *result) {
- uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size;
- unsigned i;
- for (i = 0; i < hash_context->result_size; ++i) pad[i] = K[i] ^ 0x5c;
- for (; i < hash_context->block_size; ++i) pad[i] = 0x5c;
- hash_context->finish_hash(hash_context, result);
- hash_context->init_hash(hash_context);
- hash_context->update_hash(hash_context, pad, hash_context->block_size);
- hash_context->update_hash(hash_context, result, hash_context->result_size);
- hash_context->finish_hash(hash_context, result);
- }
- /* V = HMAC_K(V) */
- static void update_V(const MG_UECC_HashContext *hash_context, uint8_t *K,
- uint8_t *V) {
- HMAC_init(hash_context, K);
- HMAC_update(hash_context, V, hash_context->result_size);
- HMAC_finish(hash_context, K, V);
- }
- /* Deterministic signing, similar to RFC 6979. Differences are:
- * We just use H(m) directly rather than bits2octets(H(m))
- (it is not reduced modulo curve_n).
- * We generate a value for k (aka T) directly rather than converting
- endianness.
- Layout of hash_context->tmp: <K> | <V> | (1 byte overlapped 0x00 or 0x01) /
- <HMAC pad> */
- int mg_uecc_sign_deterministic(const uint8_t *private_key,
- const uint8_t *message_hash, unsigned hash_size,
- const MG_UECC_HashContext *hash_context,
- uint8_t *signature, MG_UECC_Curve curve) {
- uint8_t *K = hash_context->tmp;
- uint8_t *V = K + hash_context->result_size;
- wordcount_t num_bytes = curve->num_bytes;
- wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits);
- bitcount_t num_n_bits = curve->num_n_bits;
- mg_uecc_word_t tries;
- unsigned i;
- for (i = 0; i < hash_context->result_size; ++i) {
- V[i] = 0x01;
- K[i] = 0;
- }
- /* K = HMAC_K(V || 0x00 || int2octets(x) || h(m)) */
- HMAC_init(hash_context, K);
- V[hash_context->result_size] = 0x00;
- HMAC_update(hash_context, V, hash_context->result_size + 1);
- HMAC_update(hash_context, private_key, (unsigned int) num_bytes);
- HMAC_update(hash_context, message_hash, hash_size);
- HMAC_finish(hash_context, K, K);
- update_V(hash_context, K, V);
- /* K = HMAC_K(V || 0x01 || int2octets(x) || h(m)) */
- HMAC_init(hash_context, K);
- V[hash_context->result_size] = 0x01;
- HMAC_update(hash_context, V, hash_context->result_size + 1);
- HMAC_update(hash_context, private_key, (unsigned int) num_bytes);
- HMAC_update(hash_context, message_hash, hash_size);
- HMAC_finish(hash_context, K, K);
- update_V(hash_context, K, V);
- for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) {
- mg_uecc_word_t T[MG_UECC_MAX_WORDS];
- uint8_t *T_ptr = (uint8_t *) T;
- wordcount_t T_bytes = 0;
- for (;;) {
- update_V(hash_context, K, V);
- for (i = 0; i < hash_context->result_size; ++i) {
- T_ptr[T_bytes++] = V[i];
- if (T_bytes >= num_n_words * MG_UECC_WORD_SIZE) {
- goto filled;
- }
- }
- }
- filled:
- if ((bitcount_t) num_n_words * MG_UECC_WORD_SIZE * 8 > num_n_bits) {
- mg_uecc_word_t mask = (mg_uecc_word_t) -1;
- T[num_n_words - 1] &=
- mask >>
- ((bitcount_t) (num_n_words * MG_UECC_WORD_SIZE * 8 - num_n_bits));
- }
- if (mg_uecc_sign_with_k_internal(private_key, message_hash, hash_size, T,
- signature, curve)) {
- return 1;
- }
- /* K = HMAC_K(V || 0x00) */
- HMAC_init(hash_context, K);
- V[hash_context->result_size] = 0x00;
- HMAC_update(hash_context, V, hash_context->result_size + 1);
- HMAC_finish(hash_context, K, K);
- update_V(hash_context, K, V);
- }
- return 0;
- }
- static bitcount_t smax(bitcount_t a, bitcount_t b) {
- return (a > b ? a : b);
- }
- int mg_uecc_verify(const uint8_t *public_key, const uint8_t *message_hash,
- unsigned hash_size, const uint8_t *signature,
- MG_UECC_Curve curve) {
- mg_uecc_word_t u1[MG_UECC_MAX_WORDS], u2[MG_UECC_MAX_WORDS];
- mg_uecc_word_t z[MG_UECC_MAX_WORDS];
- mg_uecc_word_t sum[MG_UECC_MAX_WORDS * 2];
- mg_uecc_word_t rx[MG_UECC_MAX_WORDS];
- mg_uecc_word_t ry[MG_UECC_MAX_WORDS];
- mg_uecc_word_t tx[MG_UECC_MAX_WORDS];
- mg_uecc_word_t ty[MG_UECC_MAX_WORDS];
- mg_uecc_word_t tz[MG_UECC_MAX_WORDS];
- const mg_uecc_word_t *points[4];
- const mg_uecc_word_t *point;
- bitcount_t num_bits;
- bitcount_t i;
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key;
- #else
- mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2];
- #endif
- mg_uecc_word_t r[MG_UECC_MAX_WORDS], s[MG_UECC_MAX_WORDS];
- wordcount_t num_words = curve->num_words;
- wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits);
- rx[num_n_words - 1] = 0;
- r[num_n_words - 1] = 0;
- s[num_n_words - 1] = 0;
- #if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN
- bcopy((uint8_t *) r, signature, curve->num_bytes);
- bcopy((uint8_t *) s, signature + curve->num_bytes, curve->num_bytes);
- #else
- mg_uecc_vli_bytesToNative(_public, public_key, curve->num_bytes);
- mg_uecc_vli_bytesToNative(_public + num_words, public_key + curve->num_bytes,
- curve->num_bytes);
- mg_uecc_vli_bytesToNative(r, signature, curve->num_bytes);
- mg_uecc_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes);
- #endif
- /* r, s must not be 0. */
- if (mg_uecc_vli_isZero(r, num_words) || mg_uecc_vli_isZero(s, num_words)) {
- return 0;
- }
- /* r, s must be < n. */
- if (mg_uecc_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 ||
- mg_uecc_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) {
- return 0;
- }
- /* Calculate u1 and u2. */
- mg_uecc_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */
- u1[num_n_words - 1] = 0;
- bits2int(u1, message_hash, hash_size, curve);
- mg_uecc_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */
- mg_uecc_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */
- /* Calculate sum = G + Q. */
- mg_uecc_vli_set(sum, _public, num_words);
- mg_uecc_vli_set(sum + num_words, _public + num_words, num_words);
- mg_uecc_vli_set(tx, curve->G, num_words);
- mg_uecc_vli_set(ty, curve->G + num_words, num_words);
- mg_uecc_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */
- XYcZ_add(tx, ty, sum, sum + num_words, curve);
- mg_uecc_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */
- apply_z(sum, sum + num_words, z, curve);
- /* Use Shamir's trick to calculate u1*G + u2*Q */
- points[0] = 0;
- points[1] = curve->G;
- points[2] = _public;
- points[3] = sum;
- num_bits = smax(mg_uecc_vli_numBits(u1, num_n_words),
- mg_uecc_vli_numBits(u2, num_n_words));
- point =
- points[(!!mg_uecc_vli_testBit(u1, (bitcount_t) (num_bits - 1))) |
- ((!!mg_uecc_vli_testBit(u2, (bitcount_t) (num_bits - 1))) << 1)];
- mg_uecc_vli_set(rx, point, num_words);
- mg_uecc_vli_set(ry, point + num_words, num_words);
- mg_uecc_vli_clear(z, num_words);
- z[0] = 1;
- for (i = num_bits - 2; i >= 0; --i) {
- mg_uecc_word_t index;
- curve->double_jacobian(rx, ry, z, curve);
- index = (!!mg_uecc_vli_testBit(u1, i)) |
- (mg_uecc_word_t) ((!!mg_uecc_vli_testBit(u2, i)) << 1);
- point = points[index];
- if (point) {
- mg_uecc_vli_set(tx, point, num_words);
- mg_uecc_vli_set(ty, point + num_words, num_words);
- apply_z(tx, ty, z, curve);
- mg_uecc_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */
- XYcZ_add(tx, ty, rx, ry, curve);
- mg_uecc_vli_modMult_fast(z, z, tz, curve);
- }
- }
- mg_uecc_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */
- apply_z(rx, ry, z, curve);
- /* v = x1 (mod n) */
- if (mg_uecc_vli_cmp_unsafe(curve->n, rx, num_n_words) != 1) {
- mg_uecc_vli_sub(rx, rx, curve->n, num_n_words);
- }
- /* Accept only if v == r. */
- return (int) (mg_uecc_vli_equal(rx, r, num_words));
- }
- #if MG_UECC_ENABLE_VLI_API
- unsigned mg_uecc_curve_num_words(MG_UECC_Curve curve) {
- return curve->num_words;
- }
- unsigned mg_uecc_curve_num_bytes(MG_UECC_Curve curve) {
- return curve->num_bytes;
- }
- unsigned mg_uecc_curve_num_bits(MG_UECC_Curve curve) {
- return curve->num_bytes * 8;
- }
- unsigned mg_uecc_curve_num_n_words(MG_UECC_Curve curve) {
- return BITS_TO_WORDS(curve->num_n_bits);
- }
- unsigned mg_uecc_curve_num_n_bytes(MG_UECC_Curve curve) {
- return BITS_TO_BYTES(curve->num_n_bits);
- }
- unsigned mg_uecc_curve_num_n_bits(MG_UECC_Curve curve) {
- return curve->num_n_bits;
- }
- const mg_uecc_word_t *mg_uecc_curve_p(MG_UECC_Curve curve) {
- return curve->p;
- }
- const mg_uecc_word_t *mg_uecc_curve_n(MG_UECC_Curve curve) {
- return curve->n;
- }
- const mg_uecc_word_t *mg_uecc_curve_G(MG_UECC_Curve curve) {
- return curve->G;
- }
- const mg_uecc_word_t *mg_uecc_curve_b(MG_UECC_Curve curve) {
- return curve->b;
- }
- #if MG_UECC_SUPPORT_COMPRESSED_POINT
- void mg_uecc_vli_mod_sqrt(mg_uecc_word_t *a, MG_UECC_Curve curve) {
- curve->mod_sqrt(a, curve);
- }
- #endif
- void mg_uecc_vli_mmod_fast(mg_uecc_word_t *result, mg_uecc_word_t *product,
- MG_UECC_Curve curve) {
- #if (MG_UECC_OPTIMIZATION_LEVEL > 0)
- curve->mmod_fast(result, product);
- #else
- mg_uecc_vli_mmod(result, product, curve->p, curve->num_words);
- #endif
- }
- void mg_uecc_point_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point,
- const mg_uecc_word_t *scalar, MG_UECC_Curve curve) {
- mg_uecc_word_t tmp1[MG_UECC_MAX_WORDS];
- mg_uecc_word_t tmp2[MG_UECC_MAX_WORDS];
- mg_uecc_word_t *p2[2] = {tmp1, tmp2};
- mg_uecc_word_t carry = regularize_k(scalar, tmp1, tmp2, curve);
- EccPoint_mult(result, point, p2[!carry], 0, curve->num_n_bits + 1, curve);
- }
- #endif /* MG_UECC_ENABLE_VLI_API */
- #endif // MG_TLS_BUILTIN
- // End of uecc BSD-2
- #ifdef MG_ENABLE_LINES
- #line 1 "src/tls_x25519.c"
- #endif
- /**
- * Adapted from STROBE: https://strobe.sourceforge.io/
- * Copyright (c) 2015-2016 Cryptography Research, Inc.
- * Author: Mike Hamburg
- * License: MIT License
- */
- #if MG_TLS == MG_TLS_BUILTIN
- const uint8_t X25519_BASE_POINT[X25519_BYTES] = {9};
- #define X25519_WBITS 32
- typedef uint32_t limb_t;
- typedef uint64_t dlimb_t;
- typedef int64_t sdlimb_t;
- #define NLIMBS (256 / X25519_WBITS)
- typedef limb_t mg_fe[NLIMBS];
- static limb_t umaal(limb_t *carry, limb_t acc, limb_t mand, limb_t mier) {
- dlimb_t tmp = (dlimb_t) mand * mier + acc + *carry;
- *carry = (limb_t) (tmp >> X25519_WBITS);
- return (limb_t) tmp;
- }
- // These functions are implemented in terms of umaal on ARM
- static limb_t adc(limb_t *carry, limb_t acc, limb_t mand) {
- dlimb_t total = (dlimb_t) *carry + acc + mand;
- *carry = (limb_t) (total >> X25519_WBITS);
- return (limb_t) total;
- }
- static limb_t adc0(limb_t *carry, limb_t acc) {
- dlimb_t total = (dlimb_t) *carry + acc;
- *carry = (limb_t) (total >> X25519_WBITS);
- return (limb_t) total;
- }
- // - Precondition: carry is small.
- // - Invariant: result of propagate is < 2^255 + 1 word
- // - In particular, always less than 2p.
- // - Also, output x >= min(x,19)
- static void propagate(mg_fe x, limb_t over) {
- unsigned i;
- limb_t carry;
- over = x[NLIMBS - 1] >> (X25519_WBITS - 1) | over << 1;
- x[NLIMBS - 1] &= ~((limb_t) 1 << (X25519_WBITS - 1));
- carry = over * 19;
- for (i = 0; i < NLIMBS; i++) {
- x[i] = adc0(&carry, x[i]);
- }
- }
- static void add(mg_fe out, const mg_fe a, const mg_fe b) {
- unsigned i;
- limb_t carry = 0;
- for (i = 0; i < NLIMBS; i++) {
- out[i] = adc(&carry, a[i], b[i]);
- }
- propagate(out, carry);
- }
- static void sub(mg_fe out, const mg_fe a, const mg_fe b) {
- unsigned i;
- sdlimb_t carry = -38;
- for (i = 0; i < NLIMBS; i++) {
- carry = carry + a[i] - b[i];
- out[i] = (limb_t) carry;
- carry >>= X25519_WBITS;
- }
- propagate(out, (limb_t) (1 + carry));
- }
- // `b` can contain less than 8 limbs, thus we use `limb_t *` instead of `mg_fe`
- // to avoid build warnings
- static void mul(mg_fe out, const mg_fe a, const limb_t *b, unsigned nb) {
- limb_t accum[2 * NLIMBS] = {0};
- unsigned i, j;
- limb_t carry2;
- for (i = 0; i < nb; i++) {
- limb_t mand = b[i];
- carry2 = 0;
- for (j = 0; j < NLIMBS; j++) {
- limb_t tmp; // "a" may be misaligned
- memcpy(&tmp, &a[j], sizeof(tmp)); // So make an aligned copy
- accum[i + j] = umaal(&carry2, accum[i + j], mand, tmp);
- }
- accum[i + j] = carry2;
- }
- carry2 = 0;
- for (j = 0; j < NLIMBS; j++) {
- out[j] = umaal(&carry2, accum[j], 38, accum[j + NLIMBS]);
- }
- propagate(out, carry2);
- }
- static void sqr(mg_fe out, const mg_fe a) {
- mul(out, a, a, NLIMBS);
- }
- static void mul1(mg_fe out, const mg_fe a) {
- mul(out, a, out, NLIMBS);
- }
- static void sqr1(mg_fe a) {
- mul1(a, a);
- }
- static void condswap(limb_t a[2 * NLIMBS], limb_t b[2 * NLIMBS],
- limb_t doswap) {
- unsigned i;
- for (i = 0; i < 2 * NLIMBS; i++) {
- limb_t xor_ab = (a[i] ^ b[i]) & doswap;
- a[i] ^= xor_ab;
- b[i] ^= xor_ab;
- }
- }
- // Canonicalize a field element x, reducing it to the least residue which is
- // congruent to it mod 2^255-19
- // - Precondition: x < 2^255 + 1 word
- static limb_t canon(mg_fe x) {
- // First, add 19.
- unsigned i;
- limb_t carry0 = 19;
- limb_t res;
- sdlimb_t carry;
- for (i = 0; i < NLIMBS; i++) {
- x[i] = adc0(&carry0, x[i]);
- }
- propagate(x, carry0);
- // Here, 19 <= x2 < 2^255
- // - This is because we added 19, so before propagate it can't be less
- // than 19. After propagate, it still can't be less than 19, because if
- // propagate does anything it adds 19.
- // - We know that the high bit must be clear, because either the input was ~
- // 2^255 + one word + 19 (in which case it propagates to at most 2 words) or
- // it was < 2^255. So now, if we subtract 19, we will get back to something in
- // [0,2^255-19).
- carry = -19;
- res = 0;
- for (i = 0; i < NLIMBS; i++) {
- carry += x[i];
- res |= x[i] = (limb_t) carry;
- carry >>= X25519_WBITS;
- }
- return (limb_t) (((dlimb_t) res - 1) >> X25519_WBITS);
- }
- static const limb_t a24[1] = {121665};
- static void ladder_part1(mg_fe xs[5]) {
- limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4];
- add(t1, x2, z2); // t1 = A
- sub(z2, x2, z2); // z2 = B
- add(x2, x3, z3); // x2 = C
- sub(z3, x3, z3); // z3 = D
- mul1(z3, t1); // z3 = DA
- mul1(x2, z2); // x3 = BC
- add(x3, z3, x2); // x3 = DA+CB
- sub(z3, z3, x2); // z3 = DA-CB
- sqr1(t1); // t1 = AA
- sqr1(z2); // z2 = BB
- sub(x2, t1, z2); // x2 = E = AA-BB
- mul(z2, x2, a24, sizeof(a24) / sizeof(a24[0])); // z2 = E*a24
- add(z2, z2, t1); // z2 = E*a24 + AA
- }
- static void ladder_part2(mg_fe xs[5], const mg_fe x1) {
- limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4];
- sqr1(z3); // z3 = (DA-CB)^2
- mul1(z3, x1); // z3 = x1 * (DA-CB)^2
- sqr1(x3); // x3 = (DA+CB)^2
- mul1(z2, x2); // z2 = AA*(E*a24+AA)
- sub(x2, t1, x2); // x2 = BB again
- mul1(x2, t1); // x2 = AA*BB
- }
- static void x25519_core(mg_fe xs[5], const uint8_t scalar[X25519_BYTES],
- const uint8_t *x1, int clamp) {
- int i;
- mg_fe x1_limbs;
- limb_t swap = 0;
- limb_t *x2 = xs[0], *x3 = xs[2], *z3 = xs[3];
- memset(xs, 0, 4 * sizeof(mg_fe));
- x2[0] = z3[0] = 1;
- for (i = 0; i < NLIMBS; i++) {
- x3[i] = x1_limbs[i] =
- MG_U32(x1[i * 4 + 3], x1[i * 4 + 2], x1[i * 4 + 1], x1[i * 4]);
- }
- for (i = 255; i >= 0; i--) {
- uint8_t bytei = scalar[i / 8];
- limb_t doswap;
- if (clamp) {
- if (i / 8 == 0) {
- bytei &= (uint8_t) ~7U;
- } else if (i / 8 == X25519_BYTES - 1) {
- bytei &= 0x7F;
- bytei |= 0x40;
- }
- }
- doswap = 0 - (limb_t) ((bytei >> (i % 8)) & 1);
- condswap(x2, x3, swap ^ doswap);
- swap = doswap;
- ladder_part1(xs);
- ladder_part2(xs, (const limb_t *) x1_limbs);
- }
- condswap(x2, x3, swap);
- }
- int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES],
- const uint8_t x1[X25519_BYTES], int clamp) {
- int i, ret;
- mg_fe xs[5], out_limbs;
- limb_t *x2, *z2, *z3, *prev;
- static const struct {
- uint8_t a, c, n;
- } steps[13] = {{2, 1, 1}, {2, 1, 1}, {4, 2, 3}, {2, 4, 6}, {3, 1, 1},
- {3, 2, 12}, {4, 3, 25}, {2, 3, 25}, {2, 4, 50}, {3, 2, 125},
- {3, 1, 2}, {3, 1, 2}, {3, 1, 1}};
- x25519_core(xs, scalar, x1, clamp);
- // Precomputed inversion chain
- x2 = xs[0];
- z2 = xs[1];
- z3 = xs[3];
- prev = z2;
- for (i = 0; i < 13; i++) {
- int j;
- limb_t *a = xs[steps[i].a];
- for (j = steps[i].n; j > 0; j--) {
- sqr(a, prev);
- prev = a;
- }
- mul1(a, xs[steps[i].c]);
- }
- // Here prev = z3
- // x2 /= z2
- mul(out_limbs, x2, z3, NLIMBS);
- ret = (int) canon(out_limbs);
- if (!clamp) ret = 0;
- for (i = 0; i < NLIMBS; i++) {
- uint32_t n = out_limbs[i];
- out[i * 4] = (uint8_t) (n & 0xff);
- out[i * 4 + 1] = (uint8_t) ((n >> 8) & 0xff);
- out[i * 4 + 2] = (uint8_t) ((n >> 16) & 0xff);
- out[i * 4 + 3] = (uint8_t) ((n >> 24) & 0xff);
- }
- return ret;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/url.c"
- #endif
- struct url {
- size_t key, user, pass, host, port, uri, end;
- };
- bool 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 || strncmp(url, "tcps:", 5) == 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
- // Not using memset for zeroing memory, cause it can be dropped by compiler
- // See https://github.com/cesanta/mongoose/pull/1265
- void mg_bzero(volatile unsigned char *buf, size_t len) {
- if (buf != NULL) {
- while (len--) *buf++ = 0;
- }
- }
- uint64_t mg_timegm(unsigned int year, unsigned int month, unsigned int day,
- unsigned int hour, unsigned int min, unsigned int sec) {
- static const uint16_t dm[12] = {0, 31, 59, 90, 120, 151,
- 181, 212, 243, 273, 304, 334};
- const uint64_t day_secs = 86400;
- const uint64_t year_secs = 31536000;
- unsigned int y, ly;
- if (year < 1970) return 0;
- y = year - 1900;
- ly = month > 2 ? y + 1 : y;
- return (uint64_t) sec + 60 * min + 3600 * hour +
- day_secs * (dm[month - 1] + day - 1) + year_secs * (y - 70) +
- day_secs * ((ly - 69) / 4) - day_secs * ((ly - 1) / 100) +
- day_secs * ((ly + 299) / 400);
- }
- bool mg_memeq(const void *a, const void *b, size_t n) {
- const uint8_t *p = (const uint8_t *) a, *q = (const uint8_t *) b;
- uint8_t r = 0;
- size_t i;
- for (i = 0; i < n; i++) r = (uint8_t) (r | (uint8_t) (p[i] ^ q[i]));
- return r == 0;
- }
- #if MG_ENABLE_CUSTOM_RANDOM
- #else
- bool mg_random(void *buf, size_t len) {
- bool success = false;
- unsigned char *p = (unsigned char *) buf;
- #if MG_ARCH == MG_ARCH_ESP32
- while (len--) *p++ = (unsigned char) (esp_random() & 255);
- success = true;
- #elif MG_ARCH == MG_ARCH_CUBE && defined(HAL_RNG_MODULE_ENABLED)
- extern RNG_HandleTypeDef hrng;
- for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
- uint32_t r = HAL_RNG_ReadLastRandomNumber(&hrng);
- memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
- }
- success = true;
- #elif MG_ARCH == MG_ARCH_PICOSDK
- while (len--) *p++ = (unsigned char) (get_rand_32() & 255);
- success = true;
- #elif MG_ARCH == MG_ARCH_ZEPHYR
- #if MG_TLS == MG_TLS_BUILTIN || \
- (MG_TLS == MG_TLS_MBED && (!defined(MBEDTLS_VERSION_NUMBER) || \
- MBEDTLS_VERSION_NUMBER < 0x04000000))
- return (sys_csrand_get(buf, len) == 0); // do not fallback on reseed error
- #else
- sys_rand_get(buf, len);
- success = true;
- #endif
- #elif MG_ARCH == MG_ARCH_WIN32
- #if defined(_MSC_VER) && _MSC_VER < 1700
- static bool initialised = false;
- static HCRYPTPROV hProv;
- // CryptGenRandom() implementation earlier than 2008 is weak, see
- // https://en.wikipedia.org/wiki/CryptGenRandom
- if (!initialised) {
- initialised = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
- CRYPT_VERIFYCONTEXT);
- }
- if (initialised) success = CryptGenRandom(hProv, len, p);
- #else
- size_t i;
- for (i = 0; i < len; i++) {
- unsigned int rand_v;
- if (rand_s(&rand_v) == 0) {
- p[i] = (unsigned char) (rand_v & 255);
- } else {
- break;
- }
- }
- success = (i == len);
- #endif
- #elif MG_ARCH == MG_ARCH_UNIX
- FILE *fp = fopen("/dev/urandom", "rb");
- if (fp != NULL) {
- if (fread(buf, 1, len, fp) == len) success = true;
- fclose(fp);
- }
- #endif
- // If everything above did not work, fallback to a pseudo random generator
- if (success == false) {
- MG_ERROR(("Weak RNG: using rand()"));
- while (len--) *p++ = (unsigned char) (rand() & 255);
- }
- return success;
- }
- #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_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 b = *(uint8_t *) buf++;
- crc = crclut[(crc ^ b) & 0x0F] ^ (crc >> 4);
- crc = crclut[(crc ^ (b >> 4)) & 0x0F] ^ (crc >> 4);
- }
- return ~crc;
- }
- uint16_t mg_crc16(uint16_t crc, const char *buf, size_t len) {
- static const uint16_t crclut[16] = {
- // table for polynomial 0x8408 (reflected)
- 0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387,
- 0x8408, 0x9489, 0xA50A, 0xB58B, 0xC60C, 0xD68D, 0xE70E, 0xF78F};
- unsigned int c = (unsigned int) ~crc & 0xffff;
- while (len--) {
- uint8_t b = *(uint8_t *) buf++;
- c = crclut[(c ^ b) & 0x0F] ^ (c >> 4);
- c = crclut[(c ^ (b >> 4)) & 0x0F] ^ (c >> 4);
- }
- return (uint16_t) ~c;
- }
- 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, struct mg_addr *remote_ip) {
- struct mg_str entry;
- int allowed = acl.len == 0 ? '+' : '-'; // If any ACL is set, deny by default
- uint32_t remote_ip4;
- if (remote_ip->is_ip6) {
- return -1; // TODO(): handle IPv6 ACL and addresses
- } else { // IPv4
- memcpy((void *) &remote_ip4, remote_ip->addr.ip, sizeof(remote_ip4));
- while (mg_span(acl, &entry, &acl, ',')) {
- uint32_t net, mask;
- if (entry.buf[0] != '+' && entry.buf[0] != '-') return -1;
- if (parse_net(&entry.buf[1], &net, &mask) == 0) return -2;
- if ((mg_ntohl(remote_ip4) & mask) == net) allowed = entry.buf[0];
- }
- }
- return allowed == '+';
- }
- bool mg_path_is_sane(const struct mg_str path) {
- const char *s = path.buf;
- size_t n = path.len;
- if (n == 0 || path.buf[0] == '\0') return true;
- if (s[0] == '~') return false; // Starts with ~
- if (s[0] == '.' && n > 1 && s[1] == '.')
- return false; // Starts with ..
- for (; n > 0 && s[0] != '\0'; s++, n--) {
- if ((s[0] == '/' || s[0] == '\\') && n >= 2 && s[1] == '.' && n > 2 &&
- s[2] == '.')
- return false; // Subdir starts with ..
- }
- if (n > 0) return false; // embedded nul (terminator not counted in len)
- return true;
- }
- #if MG_ENABLE_CUSTOM_MILLIS
- #else
- uint64_t mg_millis(void) {
- #if MG_ARCH == MG_ARCH_ESP8266 || MG_ARCH == MG_ARCH_ESP32 || MG_ENABLE_FREERTOS
- return xTaskGetTickCount() * portTICK_PERIOD_MS;
- #elif MG_ARCH == MG_ARCH_THREADX
- 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_RTTHREAD
- return (uint64_t) ((rt_tick_get() * 1000) / RT_TICK_PER_SECOND);
- #elif MG_ARCH == MG_ARCH_WIN32
- return GetTickCount();
- #elif MG_ARCH == MG_ARCH_PICOSDK
- return time_us_64() / 1000;
- #elif MG_ARCH == MG_ARCH_CUBE
- return (uint64_t) HAL_GetTick();
- #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
- // network format equates big endian order
- uint16_t mg_ntohs(uint16_t net) {
- return MG_LOAD_BE16(&net);
- }
- uint32_t mg_ntohl(uint32_t net) {
- return MG_LOAD_BE32(&net);
- }
- uint64_t mg_ntohll(uint64_t net) {
- return MG_LOAD_BE64(&net);
- }
- void mg_delayms(unsigned int ms) {
- uint64_t to = mg_millis() + ms + 1;
- while (mg_millis() < to) (void) 0;
- }
- #if MG_ENABLE_CUSTOM_CALLOC
- #else
- void *mg_calloc(size_t count, size_t size) {
- return calloc(count, size);
- }
- void mg_free(void *ptr) {
- free(ptr);
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/wifi_dummy.c"
- #endif
- #if (!defined(MG_ENABLE_DRIVER_PICO_W) || !MG_ENABLE_DRIVER_PICO_W) && \
- (!defined(MG_ENABLE_DRIVER_CYW) || !MG_ENABLE_DRIVER_CYW) && \
- (!defined(MG_ENABLE_DRIVER_CYW_SDIO) || !MG_ENABLE_DRIVER_CYW_SDIO) && \
- (!defined(MG_ENABLE_DRIVER_NXP_WIFI) || !MG_ENABLE_DRIVER_NXP_WIFI) && \
- (!defined(MG_ENABLE_DRIVER_ST67W6) || !MG_ENABLE_DRIVER_ST67W6)
- bool mg_wifi_scan(void) {
- MG_ERROR(("No Wi-Fi driver enabled"));
- return false;
- }
- bool mg_wifi_connect(struct mg_wifi_data *wifi) {
- (void) wifi;
- return mg_wifi_scan();
- }
- bool mg_wifi_disconnect(void) {
- return mg_wifi_scan();
- }
- bool mg_wifi_ap_start(struct mg_wifi_data *wifi) {
- (void) wifi;
- return mg_wifi_scan();
- }
- bool mg_wifi_ap_stop(void) {
- return mg_wifi_scan();
- }
- #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->buf, 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, sizeof(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->buf);
- }
- if (!mg_send(c, "\r\n", 2)) mg_error(c, "OOM");
- }
- 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);
- if (!mg_send(c, header, header_len)) return 0;
- if (!mg_send(c, buf, len)) return header_len;
- MG_VERBOSE(("WS out: %d [%.*s]", (int) len, (int) len, buf));
- 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, "ws handshake error");
- } else {
- struct mg_http_message hm;
- if (mg_http_parse((char *) c->recv.buf, c->recv.len, &hm)) {
- c->is_websocket = 1;
- mg_call(c, MG_EV_WS_OPEN, &hm);
- } else {
- mg_error(c, "ws handshake error");
- }
- }
- 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) {
- 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;
- size_t len;
- uint8_t final, op;
- m.data.buf = s, m.data.len = msg.data_len, m.flags = msg.flags;
- len = msg.header_len + msg.data_len;
- 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.buf));
- 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.buf, 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 && (ofs > 0)) {
- 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) 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, sizeof(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.buf, 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!
- if (mg_iobuf_add(&c->send, c->send.len, NULL, header_len) != 0) {
- 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
- } // returning 0 means an OOM condition (iobuf couldn't resize), yet this is
- return c->send.len; // so far recoverable, let the caller decide
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/at_cmd.c"
- #endif
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_ATCMD) && MG_ENABLE_DRIVER_ATCMD
- #define MG_HDLC_FLAG 0x7e // HDLC frame delimiter
- #define MG_HDLC_ESC 0x7d // HDLC escape byte for byte stuffing
- #define MG_ATCMD_AT_TIMEOUT 2000
- static size_t print_atcmd(void (*out)(char, void *), void *arg, va_list *ap) {
- struct mg_str s = va_arg(*ap, struct mg_str);
- size_t i;
- for (i = 0; i < s.len; i++)
- out((char) (s.buf[i] < 0x20 ? '.' : s.buf[i]), arg);
- return s.len;
- }
- static void mg_atcmd_reset(struct mg_tcpip_driver_atcmd_data *dd) {
- dd->script_index = 0;
- dd->deadline = 0;
- if (dd->reset) dd->reset(dd->usart);
- }
- static bool mg_atcmd_handle(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_atcmd_data *dd = (struct mg_tcpip_driver_atcmd_data *) ifp->driver_data;
- if (dd->script == NULL || dd->script_index < 0) return true;
- if (dd->deadline == 0) dd->deadline = mg_millis() + MG_ATCMD_AT_TIMEOUT;
- for (;;) {
- if (dd->script_index % 2 == 0) { // send AT command
- const char *cmd = dd->script[dd->script_index];
- MG_DEBUG(("send AT[%d]: %M", dd->script_index, print_atcmd, mg_str(cmd)));
- while (*cmd != '\0') dd->tx(dd->usart, (uint8_t) *cmd++);
- dd->script_index++;
- ifp->recv_queue.head = 0;
- } else { // check AT command response
- const char *expect = dd->script[dd->script_index];
- struct mg_queue *q = &ifp->recv_queue;
- for (;;) {
- int c;
- bool is_timeout = dd->deadline > 0 && mg_millis() > dd->deadline;
- bool is_overflow = q->head >= q->size - 1;
- if (is_timeout || is_overflow) {
- MG_ERROR(("AT error: %s, retrying...", is_timeout ? "timeout" : "overflow"));
- mg_atcmd_reset(dd);
- return false; // FAIL
- }
- if ((c = dd->rx(dd->usart)) < 0) return false; // no data
- q->buf[q->head++] = (char) c;
- if (mg_match(mg_str_n(q->buf, q->head), mg_str(expect), NULL)) {
- MG_DEBUG(("recv AT[%d]: %M", dd->script_index, print_atcmd,
- mg_str_n(q->buf, q->head)));
- dd->script_index++;
- q->head = 0;
- break;
- }
- }
- }
- if (dd->script[dd->script_index] == NULL) {
- MG_DEBUG(("finished AT script"));
- dd->script_index = -1;
- return true;
- }
- }
- }
- static void byte_stuff(struct mg_tcpip_driver_atcmd_data *dd, uint8_t b);
- static bool byte_unstuff(struct mg_tcpip_driver_atcmd_data *dd, uint8_t *p);
- static size_t mg_atcmd_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_atcmd_data *dd = (struct mg_tcpip_driver_atcmd_data *) ifp->driver_data;
- struct mg_queue *q = &ifp->recv_queue;
-
- if (!dd->link) return 0;
- while (q->head < q->size) { // read as many bytes as possible
- uint8_t b;
- int c = dd->rx(dd->usart);
- if (c < 0) return 0; // no more bytes, exit
- b = (uint8_t) c;
- if (b == MG_HDLC_FLAG) {
- if (q->head == 0) { // first flag: skip
- dd->unstuffing = false;
- continue;
- } else { // last flag: end of frame, exit loop
- break;
- }
- }
- if (!dd->no_byte_stuff && !byte_unstuff(dd, &b)) continue;
- q->buf[q->head++] = (char) b;
- }
- len = (q->head <= len) ? q->head : 0;
- memmove(buf, q->buf, len);
- q->head = 0;
- return len;
- }
- static size_t mg_atcmd_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_atcmd_data *dd = (struct mg_tcpip_driver_atcmd_data *) ifp->driver_data;
- uint8_t *p = (uint8_t *) buf;
- size_t n = len;
- if (!dd->link) return 0;
- dd->tx(dd->usart, MG_HDLC_FLAG);
- while (n--) {
- if (dd->no_byte_stuff) {
- dd->tx(dd->usart, *p++);
- } else {
- byte_stuff(dd, *p++);
- }
- }
- dd->tx(dd->usart, MG_HDLC_FLAG);
- return len;
- }
- static bool mg_atcmd_init(struct mg_tcpip_if *ifp) {
- ifp->recv_queue.size = 1506; // PPP MTU=1500 + header + HDLC w/ no flags
- return true;
- }
- static bool mg_atcmd_poll(struct mg_tcpip_if *ifp, bool s1) {
- struct mg_tcpip_driver_atcmd_data *dd = (struct mg_tcpip_driver_atcmd_data *) ifp->driver_data;
- dd->link = mg_atcmd_handle(ifp);
- return s1 ? dd->link :false;
- }
- struct mg_tcpip_driver mg_tcpip_driver_atcmd = {mg_atcmd_init, mg_atcmd_tx, mg_atcmd_rx, mg_atcmd_poll};
- static void byte_stuff(struct mg_tcpip_driver_atcmd_data *dd, uint8_t b) {
- if ((b < 0x20) || (b == MG_HDLC_ESC) || (b == MG_HDLC_FLAG)) {
- dd->tx(dd->usart, MG_HDLC_ESC);
- dd->tx(dd->usart, (uint8_t) (b ^ 0x20));
- } else {
- dd->tx(dd->usart, b);
- }
- }
- static bool byte_unstuff(struct mg_tcpip_driver_atcmd_data *dd, uint8_t *p) {
- if (!dd->unstuffing) {
- if (*p == MG_HDLC_ESC) {
- dd->unstuffing = true;
- return false;
- } else {
- return true;
- }
- }
- dd->unstuffing = false;
- *p ^= 0x20;
- return true;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/cmsis.c"
- #endif
- // https://arm-software.github.io/CMSIS_5/Driver/html/index.html
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_CMSIS) && MG_ENABLE_DRIVER_CMSIS
- extern ARM_DRIVER_ETH_MAC Driver_ETH_MAC0;
- extern ARM_DRIVER_ETH_PHY Driver_ETH_PHY0;
- static struct mg_tcpip_if *s_ifp;
- static void mac_cb(uint32_t);
- static bool cmsis_init(struct mg_tcpip_if *);
- static bool cmsis_poll(struct mg_tcpip_if *, bool);
- static size_t cmsis_tx(const void *, size_t, struct mg_tcpip_if *);
- static size_t cmsis_rx(void *, size_t, struct mg_tcpip_if *);
- struct mg_tcpip_driver mg_tcpip_driver_cmsis = {cmsis_init, cmsis_tx, NULL,
- cmsis_poll};
- static bool cmsis_init(struct mg_tcpip_if *ifp) {
- ARM_ETH_MAC_ADDR addr;
- s_ifp = ifp;
- ARM_DRIVER_ETH_MAC *mac = &Driver_ETH_MAC0;
- ARM_DRIVER_ETH_PHY *phy = &Driver_ETH_PHY0;
- ARM_ETH_MAC_CAPABILITIES cap = mac->GetCapabilities();
- if (mac->Initialize(mac_cb) != ARM_DRIVER_OK) return false;
- if (phy->Initialize(mac->PHY_Read, mac->PHY_Write) != ARM_DRIVER_OK)
- return false;
- if (cap.event_rx_frame == 0) // polled mode driver
- mg_tcpip_driver_cmsis.rx = cmsis_rx;
- mac->PowerControl(ARM_POWER_FULL);
- if (cap.mac_address) { // driver provides MAC address
- mac->GetMacAddress(&addr);
- memcpy(ifp->mac, &addr, sizeof(addr));
- } else { // we provide MAC address
- memcpy(&addr, ifp->mac, sizeof(addr));
- mac->SetMacAddress(&addr);
- }
- phy->PowerControl(ARM_POWER_FULL);
- phy->SetInterface(cap.media_interface);
- phy->SetMode(ARM_ETH_PHY_AUTO_NEGOTIATE);
- return true;
- }
- static size_t cmsis_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
- ARM_DRIVER_ETH_MAC *mac = &Driver_ETH_MAC0;
- if (mac->SendFrame(buf, (uint32_t) len, 0) != ARM_DRIVER_OK) {
- ifp->nerr++;
- return 0;
- }
- ifp->nsent++;
- return len;
- }
- static void cmsis_update_hash_table(struct mg_tcpip_if *ifp) {
- // TODO(): read database, rebuild hash table
- ARM_DRIVER_ETH_MAC *mac = &Driver_ETH_MAC0;
- ARM_ETH_MAC_ADDR addr;
- memcpy(&addr, mcast_addr, sizeof(addr));
- mac->SetAddressFilter(&addr, 1);
- (void) ifp;
- }
- static bool cmsis_poll(struct mg_tcpip_if *ifp, bool s1) {
- if (ifp->update_mac_hash_table) {
- cmsis_update_hash_table(ifp);
- ifp->update_mac_hash_table = false;
- }
- if (!s1) return false;
- ARM_DRIVER_ETH_PHY *phy = &Driver_ETH_PHY0;
- ARM_DRIVER_ETH_MAC *mac = &Driver_ETH_MAC0;
- bool up = (phy->GetLinkState() == ARM_ETH_LINK_UP) ? 1 : 0; // link state
- if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // just went up
- ARM_ETH_LINK_INFO st = phy->GetLinkInfo();
- mac->Control(ARM_ETH_MAC_CONFIGURE,
- (st.speed << ARM_ETH_MAC_SPEED_Pos) |
- (st.duplex << ARM_ETH_MAC_DUPLEX_Pos) |
- ARM_ETH_MAC_ADDRESS_BROADCAST);
- MG_DEBUG(("Link is %uM %s-duplex",
- (st.speed == 2) ? 1000
- : st.speed ? 100
- : 10,
- st.duplex ? "full" : "half"));
- mac->Control(ARM_ETH_MAC_CONTROL_TX, 1);
- mac->Control(ARM_ETH_MAC_CONTROL_RX, 1);
- } else if ((ifp->state != MG_TCPIP_STATE_DOWN) && !up) { // just went down
- mac->Control(ARM_ETH_MAC_FLUSH,
- ARM_ETH_MAC_FLUSH_TX | ARM_ETH_MAC_FLUSH_RX);
- mac->Control(ARM_ETH_MAC_CONTROL_TX, 0);
- mac->Control(ARM_ETH_MAC_CONTROL_RX, 0);
- }
- return up;
- }
- static void mac_cb(uint32_t ev) {
- if ((ev & ARM_ETH_MAC_EVENT_RX_FRAME) == 0) return;
- ARM_DRIVER_ETH_MAC *mac = &Driver_ETH_MAC0;
- uint32_t len = mac->GetRxFrameSize(); // CRC already stripped
- if (len >= 60 && len <= 1518) { // proper frame
- char *p;
- if (mg_queue_book(&s_ifp->recv_queue, &p, len) >= len) { // have room
- if ((len = mac->ReadFrame((uint8_t *) p, len)) > 0) { // copy succeeds
- mg_queue_add(&s_ifp->recv_queue, len);
- s_ifp->nrecv++;
- }
- return;
- }
- s_ifp->ndrop++;
- }
- mac->ReadFrame(NULL, 0); // otherwise, discard
- }
- static size_t cmsis_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) {
- ARM_DRIVER_ETH_MAC *mac = &Driver_ETH_MAC0;
- uint32_t len = mac->GetRxFrameSize(); // CRC already stripped
- if (len >= 60 && len <= 1518 &&
- ((len = mac->ReadFrame(buf, (uint32_t) buflen)) > 0))
- return len;
- if (len > 0) mac->ReadFrame(NULL, 0); // discard bad frames
- (void) ifp;
- return 0;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/cyw.c"
- #endif
- #if MG_ENABLE_TCPIP && \
- ((defined(MG_ENABLE_DRIVER_CYW) && MG_ENABLE_DRIVER_CYW) || \
- (defined(MG_ENABLE_DRIVER_CYW_SDIO) && MG_ENABLE_DRIVER_CYW_SDIO))
- #ifndef MG_ENABLE_DRIVER_CYW
- #define MG_ENABLE_DRIVER_CYW 0
- #endif
- #ifndef MG_ENABLE_DRIVER_CYW_SDIO
- #define MG_ENABLE_DRIVER_CYW_SDIO 0
- #endif
- static struct mg_tcpip_if *s_ifp;
- static uint32_t s_ip, s_mask;
- static bool s_link, s_auth, s_join;
- static void wifi_cb(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
- struct mg_wifi_data *wifi = &((struct mg_tcpip_driver_cyw_data *) ifp->driver_data)->wifi;
- if (wifi->apmode && ev == MG_TCPIP_EV_STATE_CHANGE && *(uint8_t *) ev_data == MG_TCPIP_STATE_UP) {
- MG_DEBUG(("Access Point started"));
- s_ip = ifp->ip, ifp->ip = wifi->apip;
- s_mask = ifp->mask, ifp->mask = wifi->apmask;
- ifp->enable_dhcp_client = false;
- ifp->enable_dhcp_server = true;
- }
- }
- static bool cyw_init(uint8_t *mac);
- static void cyw_poll(void);
- static bool mg_tcpip_driver_cyw_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_cyw_data *d =
- (struct mg_tcpip_driver_cyw_data *) ifp->driver_data;
- struct mg_wifi_data *wifi = &d->wifi;
- if (MG_BIG_ENDIAN) {
- MG_ERROR(("Big-endian host"));
- return false;
- }
- s_ifp = ifp;
- s_ip = ifp->ip;
- s_mask = ifp->mask;
- s_link = s_auth = s_join = false;
- ifp->pfn = wifi_cb;
- if (!cyw_init(ifp->mac)) return false;
- if (wifi->apmode) {
- return mg_wifi_ap_start(wifi);
- } else if (wifi->ssid != NULL && wifi->pass != NULL) {
- return mg_wifi_connect(wifi);
- }
- return true;
- }
- static size_t mg_cyw_tx(unsigned int ifc, void *data, size_t len);
- size_t mg_tcpip_driver_cyw_output(const void *buf, size_t len,
- struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_cyw_data *d =
- (struct mg_tcpip_driver_cyw_data *) ifp->driver_data;
- return mg_cyw_tx(d->wifi.apmode ? 1 : 0, (void *) buf, len) >= len ? len : 0;
- }
- static bool mg_tcpip_driver_cyw_poll(struct mg_tcpip_if *ifp, bool s1) {
- cyw_poll();
- if (!s1) return false;
- struct mg_tcpip_driver_cyw_data *d =
- (struct mg_tcpip_driver_cyw_data *) ifp->driver_data;
- return d->wifi.apmode ? s_link : s_link && s_auth && s_join;
- }
- struct mg_tcpip_driver mg_tcpip_driver_cyw = {mg_tcpip_driver_cyw_init,
- mg_tcpip_driver_cyw_output, NULL,
- mg_tcpip_driver_cyw_poll};
- // - DS:
- // https://www.mouser.com/datasheet/2/196/Infineon_CYW43439_DataSheet_v03_00_EN-3074791.pdf
- // - WHD: https://github.com/Infineon/wifi-host-driver
- //
- // | e <-- event data
- // |-----
- // net | vnd <-- network (TCP/IP) | vendor header (Broadcom (bcm))
- // -----|-----
- // IOCTL | ETH | ETH <-- IOCTL/IOVAR: chip control | ETH: Ethernet header
- // -------|-----|-----
- // CDC | BDC | BDC
- // ------- ----- -----
- // SDPCM <-- includes SDIO bus arbitration, not used in SPI
- // -------------------
- // SPI | SDIO <-- padded to 32-bit | 64-bytes
- //
- // - SDPCM has 3 channels (control, data, and asynchronous data)
- // - SPI has 4 "functions", F0 to F3, to access different blocks in the chip,
- // like the SPI/SDIO controller, chip backplane, and 2 DMA I/Os; these are
- // usually handled by SDPCM but we need to explicitly access the I/O controller
- // and chip backplane during initialization
- // - SDIO has 3 functions (proper SDIO terminology), F0 to F2, coincident with
- // those for SPI, accessed through standard SDIO practices. There is no F3.
- // Processor core firmware is loaded to TCM RAM, along with module-dependent
- // (hardware design) NVRAM data, via the chip backplane access through the bus
- // Once the chip has been initialized, information regarding regulatory
- // constraints (CLM blob, “Country Locale Matrix”), is loaded as an IOVAR. This
- // is tied to the module being certified, hence it is also module-dependent.
- // - Result: chip firmware + module NVRAM data + module CLM blob
- #pragma pack(push, 1)
- // all little endian
- struct cdc_hdr {
- uint32_t cmd; // ioctl command value
- uint16_t olen; // output buflen
- uint16_t ilen; // input buflen (excludes header)
- uint32_t flags;
- uint32_t status;
- };
- struct bdc_hdr {
- uint8_t flags; // Flags
- uint8_t priority; // 802.1d Priority (low 3 bits)
- uint8_t flags2;
- uint8_t data_offset; // Offset from end of BDC header to packet data, in
- // 4-uint8_t words. Leaves room for optional headers.
- };
- struct sdpcm_sw_hdr {
- uint8_t sequence; // Sequence number of pkt
- uint8_t channel_and_flags; // IOCTL/IOVAR or User Data or Event
- uint8_t next_length;
- uint8_t header_length; // Offset to BDC or CDC header
- uint8_t wireless_flow_control;
- uint8_t bus_data_credit; // Credit from WLAN Chip
- uint8_t _reserved[2];
- };
- struct sdpcm_hdr {
- uint16_t len;
- uint16_t _len; // ~len
- struct sdpcm_sw_hdr sw_hdr;
- };
- struct data_hdr {
- struct sdpcm_hdr sdpcm;
- uint8_t pad[2];
- struct bdc_hdr bdc;
- };
- // gSPI, CYW43439 DS 4.2.1 Fig.12, 2-bit field
- #define CYW_SPID_FUNC_BUS 0 // F0
- #define CYW_SPID_FUNC_CHIP 1 // F1
- #define CYW_SPID_FUNC_WLAN 2 // F2
- // SDIO functions, 3-bit field; CYW4343W and CYW43439 DS 4.1
- #define CYW_SDIO_FUNC_BUS 0 // F0
- #define CYW_SDIO_FUNC_CHIP 1 // F1
- #define CYW_SDIO_FUNC_WLAN 2 // F2
- #define CYW_SDPCM_CTRL_HDR 0
- #define CYW_SDPCM_ASYNC_HDR 1
- #define CYW_SDPCM_DATA_HDR 2
- #pragma pack(pop)
- static uint8_t s_tx_seqno;
- static uint32_t txdata[2048 / 4], resp[2048 / 4];
- static void cyw_handle_cdc(struct cdc_hdr *cdc, size_t len);
- static void cyw_handle_bdc(struct bdc_hdr *bdc, size_t len);
- static void cyw_handle_bdc_evnt(struct bdc_hdr *bdc, size_t len);
- static size_t cyw_bus_specific_poll(uint32_t *dest);
- static void cyw_update_hash_table(void);
- // High-level comm stuff
- static void cyw_poll(void) {
- struct sdpcm_hdr *sdpcm = (struct sdpcm_hdr *) resp;
- unsigned int channel;
- if (s_ifp->update_mac_hash_table) {
- // first call to _poll() is after _init(), so this is safe
- cyw_update_hash_table();
- s_ifp->update_mac_hash_table = false;
- }
- if (cyw_bus_specific_poll(resp) == 0) return;
- if ((sdpcm->len ^ sdpcm->_len) != 0xffff || sdpcm->len < sizeof(*sdpcm) ||
- sdpcm->len > 2048 - sizeof(*sdpcm))
- return;
- channel = sdpcm->sw_hdr.channel_and_flags & 0x0F;
- if (channel == CYW_SDPCM_CTRL_HDR) {
- if (sdpcm->len >= sizeof(*sdpcm) + sizeof(struct cdc_hdr)) {
- struct cdc_hdr *cdc =
- (struct cdc_hdr *) ((size_t) sdpcm + sdpcm->sw_hdr.header_length);
- size_t len = sdpcm->len - sdpcm->sw_hdr.header_length;
- cyw_handle_cdc(cdc, len);
- }
- } else if (channel == CYW_SDPCM_DATA_HDR) {
- if (sdpcm->len >= sizeof(*sdpcm) + sizeof(struct bdc_hdr)) {
- struct bdc_hdr *bdc =
- (struct bdc_hdr *) ((size_t) sdpcm + sdpcm->sw_hdr.header_length);
- size_t len = sdpcm->len - sdpcm->sw_hdr.header_length;
- cyw_handle_bdc(bdc, len);
- }
- } else if (channel == CYW_SDPCM_ASYNC_HDR) {
- struct bdc_hdr *bdc =
- (struct bdc_hdr *) ((size_t) sdpcm + sdpcm->sw_hdr.header_length);
- size_t len_ = sdpcm->len - sdpcm->sw_hdr.header_length;
- cyw_handle_bdc_evnt(bdc, len_);
- } // else silently discard
- }
- // WLAN frame reception
- static void cyw_handle_bdc(struct bdc_hdr *bdc, size_t len) {
- uint8_t *payload = (uint8_t *) &bdc[bdc->data_offset + 1];
- mg_tcpip_qwrite(payload, len - (payload - (uint8_t *) bdc), s_ifp);
- }
- static size_t cyw_bus_specific_tx(uint32_t *data, uint16_t len);
- // WLAN frame transmission
- static size_t mg_cyw_tx(unsigned int ifc, void *data, size_t len) {
- struct data_hdr *hdr = (struct data_hdr *) txdata;
- uint16_t txlen = (uint16_t) (len + sizeof(*hdr));
- memset(txdata, 0, sizeof(*hdr));
- memcpy((uint8_t *) txdata + sizeof(*hdr), data, len);
- // TODO(): hdr->bdc.priority = map IP to TOS if supporting QoS/ToS
- hdr->bdc.flags = 2 << 4; // BDC version 2
- hdr->bdc.flags2 = (uint8_t) ifc; // 0 -> STA, 1 -> AP
- // hdr->bdc.data_offset = 0; // actually zeroed above
- hdr->sdpcm.len = txlen;
- hdr->sdpcm._len = (uint16_t) ~txlen;
- hdr->sdpcm.sw_hdr.sequence = ++s_tx_seqno;
- hdr->sdpcm.sw_hdr.channel_and_flags = CYW_SDPCM_DATA_HDR,
- hdr->sdpcm.sw_hdr.header_length = offsetof(struct data_hdr, bdc);
- return cyw_bus_specific_tx(txdata, txlen);
- }
- // WLAN event handling
- #pragma pack(push, 1)
- // all in network order
- struct eth_hdr { // TODO(scaprile) reuse 'eth' in net_builtin.c
- uint8_t dest[6];
- uint8_t src[6];
- uint16_t type;
- };
- struct bcm_vendor_hdr {
- uint16_t subtype; // vendor specific: 0x8001
- uint16_t length; // bytes following this field
- uint8_t version; // 0
- uint8_t oui[3]; // vendor specific: 0x00 0x10 0x18
- uint16_t usr_subtype;
- };
- struct bcm_evnt_hdr {
- uint16_t version; // 1: fields up to ifname; 2: as shown
- uint16_t flags;
- uint32_t event_type;
- uint32_t status;
- uint32_t reason;
- uint32_t auth_type;
- uint32_t datalen;
- uint8_t addr[6]; // Station address (if applicable)
- char ifname[16];
- uint8_t ifidx;
- uint8_t bss_cfg_idx;
- };
- struct evnt_msg {
- struct eth_hdr eth;
- // struct vendor_hdr; but we only handle Broadcom (Wi-Fi processor) events
- struct bcm_vendor_hdr bcm;
- struct bcm_evnt_hdr event;
- };
- #pragma pack(pop)
- struct scan_result;
- static void cyw_handle_scan_result(uint32_t status, struct scan_result *data,
- size_t len);
- // Do not call any IOCTL functions here, otherwise revise cyw_ioctl_wait()
- static void cyw_handle_bdc_evnt(struct bdc_hdr *bdc, size_t len) {
- struct evnt_msg *msg = (struct evnt_msg *) &bdc[bdc->data_offset + 1];
- MG_VERBOSE(("%u bytes event", len));
- if (mg_log_level >= MG_LL_VERBOSE) mg_hexdump((void *) bdc, len);
- if (mg_ntohs(msg->eth.type) != 0x886C || msg->bcm.oui[0] != 0x00 ||
- msg->bcm.oui[1] != 0x10 || msg->bcm.oui[2] != 0x18)
- return; // discard if not Broadcom
- if (mg_ntohl(msg->event.datalen) <=
- len - ((uint8_t *) msg - (uint8_t *) bdc)) {
- uint32_t event_type = mg_ntohl(msg->event.event_type);
- uint32_t status = mg_ntohl(msg->event.status);
- uint32_t reason = mg_ntohl(msg->event.reason);
- uint16_t flags = mg_ntohs(msg->event.flags);
- MG_VERBOSE(("BCM evt %lu %lu %lu %p", event_type, status, reason, flags));
- if (event_type == 16 && status == 0) { // Link
- s_link = flags & 1;
- } else if (event_type == 46 && s_link) { // PSK sup with link up
- if (status == 6) { // Keyed
- } else if ((status == 4 || status == 8 || status == 10) &&
- reason == 15) { // Wait M1/M3/G1
- MG_ERROR(("AUTH TIMEOUT"));
- s_auth = false;
- } else {
- MG_ERROR(("AUTH FAILED"));
- s_auth = false;
- }
- } else if (event_type == 3 && status != 6) { // Auth (not unsolicited)
- if (status == 0) { // Success
- s_auth = true;
- } else {
- MG_ERROR(("AUTH TIMEOUT"));
- s_auth = false;
- }
- } else if (event_type == 1) { // Join
- if (status == 0) { // Success
- s_join = true;
- } else {
- MG_ERROR(("%s", status == 3 /* No networks */ ? "SSID NOT FOUND"
- : "JOIN FAILED"));
- s_join = false;
- mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_CONNECT_ERR, &status);
- }
- } else if (event_type == 12 || event_type == 5) { // Disassoc, Deauth
- s_auth = false;
- } else if (event_type == 69) { // Scan result
- struct scan_result *data = (struct scan_result *) (&msg->event + 1);
- size_t dlen = mg_ntohl(msg->event.datalen);
- if (dlen > len - ((uint8_t *) data - (uint8_t *) bdc)) return;
- cyw_handle_scan_result(status, data, dlen);
- }
- } // else silently discard
- }
- static bool cyw_ioctl_get_(unsigned int ifc, unsigned int cmd, void *data,
- size_t len);
- static bool cyw_ioctl_set_(unsigned int ifc, unsigned int cmd, void *data,
- size_t len);
- static bool cyw_ioctl_iovar_get_(unsigned int ifc, char *var, void *data,
- size_t len);
- static bool cyw_ioctl_iovar_set_(unsigned int ifc, char *var, void *data,
- size_t len);
- // clang-format off
- // convenience: ioctl funcs on default ifc (0), as only AP needs ifc 1
- __attribute__((unused)) static bool cyw_ioctl_get(unsigned int cmd, void *data, size_t len) { return cyw_ioctl_get_(0, cmd, data, len); }
- static bool cyw_ioctl_set(unsigned int cmd, void *data, size_t len) { return cyw_ioctl_set_(0, cmd, data, len); }
- static bool cyw_ioctl_iovar_get(char *var, void *data, size_t len) { return cyw_ioctl_iovar_get_(0, var, data, len); }
- static bool cyw_ioctl_iovar_set(char *var, void *data, size_t len) { return cyw_ioctl_iovar_set_(0, var, data, len); }
- // clang-format on
- // Wi-Fi network stuff
- // clang-format off
- static bool cyw_wifi_connect(char *ssid, char *pass) {
- uint32_t sup_wpa[2] = {0, 1}; // bss index 0 = STA, not open
- static const uint32_t eapver[2] = {0, (uint32_t) -1}, // accept AP version
- tmo[2] = {0, 2500};
- uint32_t data[64/4 + 1]; // max pass length: 64 for WPA, 128 for WPA3 SAE
- uint16_t *da = (uint16_t *) data;
- unsigned int len;
- uint32_t val;
- val = 4; // security type: 0 for none, 2 for WPA, 4 for WPA2/WPA3, 6 for mixed WPA/WPA2
- // sup_wpa[1] = 0 if not using security
- if (!(cyw_ioctl_set(134 /* SET_WSEC */, (uint8_t *)&val, sizeof(val))
- && cyw_ioctl_iovar_set("bsscfg:sup_wpa", (void *)sup_wpa, sizeof(sup_wpa))
- && cyw_ioctl_iovar_set("bsscfg:sup_wpa2_eapver", (void *)eapver, sizeof(eapver))
- && cyw_ioctl_iovar_set("bsscfg:sup_wpa_tmo", (void *)tmo, sizeof(tmo)))
- ) return false;
- mg_delayms(2); // allow radio firmware to be ready
- // skip if not using auth
- memset(data, 0, sizeof(data));
- len = strlen(pass);
- da[0] = (uint16_t) len;
- da[1] = 1; // indicates wireless security key, skip for WPA3 SAE
- memcpy((uint8_t *)data + 2 * sizeof(uint16_t), pass, len); // skip for WPA3 SAE
- if (!cyw_ioctl_set(268 /* SET_WSEC_PMK */, data, sizeof(data))) return false; // skip for WPA3 SAE, sizeof/2 if supporting SAE but using WPA
- // for WPA3 SAE: memcpy((uint8_t *)data + sizeof(uint16_t), pass, len); cyw_ioctl_iovar_set("sae_password", data, sizeof(data));
- // resume if not using auth
- val = 1; if (!cyw_ioctl_set(20 /* SET_INFRA */, (uint8_t *)&val, sizeof(val))) return false;
- val = 0; // auth type: 0 for open, 3 for SAE
- if (!cyw_ioctl_set(22 /* SET_AUTH */, (uint8_t *)&val, sizeof(val))) return false;
- val = 1; // MFP capable: 1 for yes, 0 for no; recommended to be set for WPA2+ (2 for 'required', WPA3)
- cyw_ioctl_iovar_set("mfp", (uint8_t *)&val, sizeof(val)); // Old chipsets do not support MFP
- val = 0x80; // auth type: 0 for none, 4 for WPA PSK, 0x80 for WPA2 PSK, 0x40000 for WPA3 SAE PSK
- if (!cyw_ioctl_set(165 /* SET_WPA_AUTH */, (uint8_t *)&val, sizeof(val))) return false;
- len = strlen(ssid);
- data[0] = (uint32_t) len;
- memcpy((uint8_t *)&data[1], ssid, len);
- if (!cyw_ioctl_set(26 /* SET_SSID */, data, len + sizeof(uint32_t))) return false;
- return true;
- }
- static bool cyw_wifi_disconnect(void) {
- return cyw_ioctl_set(52 /* DISASSOC */, NULL, 0);
- }
- // For AP functions, we use explicit ifc selection; both for clarity and maintenance, as some actions are performed on ifc 0, with or without a bss_index, and others are performed on ifc 1
- static bool cyw_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
- uint32_t data[64/4 + 2]; // max pass length: 64 for WPA, 128 for WPA3 SAE
- uint16_t *da = (uint16_t *) data;
- unsigned int len;
- uint32_t val;
- // CHIP DEPENDENCY
- // RPi set the AMPDU parameter for AP (window size = 2) *****************
- // val = 2 ; cyw_ioctl_iovar_set_(0, "ampdu_ba_wsize", (uint8_t *)&val, sizeof(val));
- // some chips might require to turn APSTA off and issue a SET_AP IOCTL
- len = strlen(ssid);
- data[0] = 1; // bss index 1 = AP
- data[1] = (uint32_t) len;
- memcpy((uint8_t *)&data[2], ssid, len);
- // TODO(scaprile): this takes some time to process, or requires a delay before doing it
- if (!cyw_ioctl_iovar_set_(0, "bsscfg:ssid", (uint8_t *)&data, len + 2 * sizeof(uint32_t))) return false;
- // TODO(scaprile): but sometimes this one takes some time to process
- val = (uint32_t) channel; if (!cyw_ioctl_set_(0, 30 /* SET_CHANNEL */, (uint8_t *)&val, sizeof(val))) return false;
- data[0] = 1; // bss index 1 = AP
- data[1] = 0x00400004; // security type: 0 for none, 0x00200002 for WPA, 0x00400004 for WPA2, 0x01000004 for WPA3, 0x01400004 for mixed WPA2/WPA3, 0x00400006 for mixed WPA/WPA2
- // NOTE(): WHD writes & 0xFF if WPS is not enabled (?)
- if (!cyw_ioctl_iovar_set_(0, "bsscfg:wsec", (uint8_t *)&data, 2 * sizeof(uint32_t))) return false;
- val = 1; // MFP capable: 1 for yes, 0 for no; recommended to be set for WPA2+ (2 for 'required', WPA3)
- cyw_ioctl_iovar_set_(1, "mfp", (uint8_t *)&val, sizeof(val)); // Old chipsets do not support MFP
- mg_delayms(2); // allow radio firmware to be ready
- // skip if not using auth
- // WPA, WPA2, mixed WPA/WPA2, mixed WPA2/WPA3
- // NOTE(): WHD does not set SAE password for shared WPA2/WPA3, same do we
- memset(data, 0, sizeof(data));
- len = strlen(pass);
- da[0] = (uint16_t) len; // skip for WPA3 SAE (43430 does NOT support WPA3 in AP)
- da[1] = 1; // indicates wireless security key, skip for WPA3 SAE
- memcpy((uint8_t *)data + 2 * sizeof(uint16_t), pass, len); // skip for WPA3 SAE
- if (!cyw_ioctl_set_(1, 268 /* SET_WSEC_PMK */, data, sizeof(data))) return false; // skip for WPA3 SAE, sizeof/2 if supporting SAE but using WPA
- /* for WPA3 SAE:
- memcpy((uint8_t *)data + sizeof(uint16_t), pass, len);
- cyw_ioctl_iovar_set_(1, "sae_password", data, sizeof(data)); */
- /* for WPA3 or mixed WPA2/WPA3:
- val = 5 ; cyw_ioctl_iovar_set_(1, "sae_max_pwe_loop", (uint8_t *)&val, sizeof(val)); // Some chipsets do not support this */
- // resume if not using auth
- data[0] = 1; // bss index 1 = AP
- data[1] = 0x80; // auth type: 0 for none, 4 for WPA PSK, 0x80 for WPA2 PSK, 0x40000 for WPA3 SAE PSK; ored if mixed
- if (!cyw_ioctl_iovar_set_(0, "bsscfg:wpa_auth", (uint8_t *)&data, 2 * sizeof(uint32_t))) return false;
- val = 1 /* auto */; if (!cyw_ioctl_set_(1, 110 /* SET_GMODE */, (uint8_t *)&val, sizeof(val))) return false;
- // Set multicast tx rate to 11Mbps, may fail in some chipsets, we are enforcing it
- val = 11000000 / 500000; if (!cyw_ioctl_iovar_set_(1, "2g_mrate", (uint8_t *)&val, sizeof(val))) return false;
- val = 1; if (!cyw_ioctl_set_(1, 78 /* SET_DTIMPRD */, (uint8_t *)&val, sizeof(val))) return false;
- data[0] = 1; // bss index 1 = AP
- data[1] = 1; // UP
- // TODO(scaprile): this takes a long time to process
- if (!cyw_ioctl_iovar_set_(0, "bss", (uint8_t *)&data, 2 * sizeof(uint32_t))) return false;
- return true;
- }
- static bool cyw_wifi_ap_stop(void) {
- uint32_t data[2];
- data[0] = 1; // bss index 1 = AP
- data[1] = 0; // DOWN
- if (!cyw_ioctl_iovar_set_(0, "bss", (uint8_t *)&data, 2 * sizeof(uint32_t))) return false;
- // DO WE NEED TO CLEAR CHANNEL ???
- // CHIP DEPENDENCY
- //val = 8 ; cyw_ioctl_iovar_set_(0, "ampdu_ba_wsize", (uint8_t *)&val, sizeof(val));
- return true;
- }
- // WLAN scan handling
- #pragma pack(push, 1)
- // in little endian
- struct wifi_scan_opt {
- uint32_t version;
- uint16_t action;
- uint16_t _;
- uint32_t ssid_len;
- uint8_t ssid[32];
- uint8_t bssid[6];
- int8_t bss_type;
- int8_t scan_type;
- int32_t nprobes;
- int32_t active_time;
- int32_t passive_time;
- int32_t home_time;
- int32_t channel_num;
- uint16_t channel_list[1];
- };
- #pragma pack(pop)
- static bool cyw_wifi_scan(void) {
- struct wifi_scan_opt opts;
- memset(&opts, 0, sizeof(opts));
- opts.version = 1;
- opts.action = 1; // start
- opts._ = 0;
- memset(opts.bssid, 0xff, sizeof(opts.bssid));
- opts.bss_type = 2; // any
- opts.nprobes = -1;
- opts.active_time = -1;
- opts.passive_time = -1;
- opts.home_time = -1;
- opts.channel_num = 0;
- opts.channel_list[0] = 0;
- return cyw_ioctl_iovar_set("escan", (uint8_t *)&opts, sizeof(opts));
- }
- #pragma pack(push, 1)
- // in little endian
- struct scan_bss {
- uint32_t version; // version field
- uint32_t length; // byte length of data in this record, starting at version and including IEs
- uint8_t BSSID[6]; // Unique 6-byte MAC address
- uint16_t beacon_period; // Interval between two consecutive beacon frames. Units are Kusec
- uint16_t capability; // Capability information
- uint8_t SSID_len; // SSID length
- uint8_t SSID[32]; // Array to store SSID
- uint8_t reserved1[1]; // Reserved(padding)
- uint32_t rateset_count; // Count of rates in this set
- uint8_t rateset_rates[16]; // rates in 500kbps units, higher bit set if basic
- uint16_t chanspec; // Channel specification for basic service set
- uint16_t atim_window; // Announcement traffic indication message window size. Units are Kusec
- uint8_t dtim_period; // Delivery traffic indication message period
- uint8_t reserved2[1]; // Reserved(padding)
- int16_t RSSI; // receive signal strength (in dBm)
- int8_t phy_noise; // noise (in dBm)
- uint8_t n_cap; // BSS is 802.11n Capable
- uint8_t reserved3[2]; // Reserved(padding)
- uint32_t nbss_cap; // 802.11n BSS Capabilities (based on HT_CAP_*)
- uint8_t ctl_ch; // 802.11n BSS control channel number
- uint8_t reserved4[3]; // Reserved(padding)
- uint32_t reserved32[1]; // Reserved for expansion of BSS properties
- uint8_t flags; // flags
- uint8_t vht_cap; // BSS is vht capable
- uint8_t reserved5[2]; // Reserved(padding)
- uint8_t basic_mcs[16]; // 802.11N BSS required MCS set
- uint16_t ie_offset; // offset at which IEs start, from beginning
- uint16_t reserved16[1]; // Reserved(padding)
- uint32_t ie_length; // byte length of Information Elements
- int16_t SNR; // Average SNR during frame reception
- };
- struct scan_result {
- uint32_t buflen;
- uint32_t version;
- uint16_t sync_id;
- uint16_t bss_count;
- struct scan_bss bss[1];
- };
- #pragma pack(pop)
- // CHIP DEPENDENCY
- #define CYW_BSS_BANDMASK 0xc000
- #define CYW_BSS_BAND2G 0
- //
- static void cyw_handle_scan_result(uint32_t status, struct scan_result *data, size_t len) {
- MG_VERBOSE(("scan event, status: %ld", status));
- if (status == 0) { // SUCCESS
- MG_VERBOSE(("scan complete"));
- mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_END, NULL);
- } else if (status == 8) { // PARTIAL
- struct mg_wifi_scan_bss_data bss;
- struct scan_bss *sbss = data->bss;
- unsigned int band = sbss->chanspec & CYW_BSS_BANDMASK;
- if (data->version != 109 || data->bss_count != 1) {
- MG_ERROR(("Unsupported: %lu %u", data->version, data->bss_count));
- return;
- }
- if (sbss->length > len - offsetof(struct scan_result, bss) || sbss->SSID_len > sizeof(sbss->SSID) || sbss->ie_offset < sizeof(*sbss) || sbss->ie_offset > (sizeof(*sbss) + sbss->ie_length) || sbss->ie_offset + sbss->ie_length > sbss->length)
- return; // silently discard malformed data
- if (!(sbss->flags & MG_BIT(2))) return; // RSSI_ONCHANNEL, ignore off-channel results
- bss.SSID = mg_str_n((char *)sbss->SSID, sbss->SSID_len);
- bss.BSSID = (char *)sbss->BSSID;
- bss.RSSI = (int8_t)sbss->RSSI;
- bss.has_n = sbss->n_cap != 0;
- bss.channel = bss.has_n ? sbss->ctl_ch : (uint8_t)(sbss->chanspec & 0xff); // n 40MHz vs a/b/g and 20MHz
- bss.band = band & CYW_BSS_BAND2G ? MG_WIFI_BAND_2G : MG_WIFI_BAND_5G;
- bss.security = (sbss->capability & MG_BIT(4) /* CAP_PRIVACY */) ? MG_WIFI_SECURITY_WEP : MG_WIFI_SECURITY_OPEN;
- { // travel IEs (Information Elements) in search of security definitions
- const uint8_t wot1[4] = {0x00, 0x50, 0xf2, 0x01}; // WPA_OUI_TYPE1
- uint8_t *ie = (uint8_t *)sbss + sbss->ie_offset;
- int bytes = (int) sbss->ie_length;
- while (bytes > 0 && ie[1] + 2 < bytes) { // ie[0] -> type, ie[1] -> bytes from ie[2]
- if (ie[0] == 48 /* IE_ID_RSN */) bss.security |= MG_WIFI_SECURITY_WPA2;
- if (ie[0] == 221 /* IE_ID_VENDOR_SPECIFIC */ && memcmp(&ie[2], wot1, 4) == 0)
- bss.security |= MG_WIFI_SECURITY_WPA;
- ie += ie[1] + 2;
- bytes -= ie[1] + 2;
- }
- }
- MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf, bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI, bss.security));
- mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_RESULT, &bss);
- } else {
- MG_ERROR(("scan error"));
- }
- }
- // clang-format on
- // IOCTL stuff. All values read and written are in little endian format
- static uint16_t s_ioctl_reqid;
- // CDC handler for waiting loop
- static uint8_t *s_ioctl_resp;
- static bool s_ioctl_err;
- static void cyw_handle_cdc(struct cdc_hdr *cdc, size_t len) {
- uint8_t *r = (uint8_t *) cdc + sizeof(*cdc);
- MG_VERBOSE(("%u bytes CDC frame", len));
- if ((cdc->flags >> 16) != s_ioctl_reqid) return;
- if (cdc->flags & 1) {
- MG_ERROR(("IOCTL error: %ld", -cdc->status));
- s_ioctl_err = true;
- return;
- }
- if (mg_log_level >= MG_LL_VERBOSE) mg_hexdump((void *) cdc, len);
- MG_VERBOSE(("IOCTL result: %02x %02x %02x %02x ..", r[0], r[1], r[2], r[3]));
- s_ioctl_resp = r;
- }
- // NOTE(): alt no loop handler dispatching IOCTL response to current handler:
- // static void *s_ioctl_hnd; *s_ioctl_hnd(ioctl, len);
- // app is a state machine calling get/sets and advancing via these callbacks
- #pragma pack(push, 1)
- // all little endian
- struct ctrl_hdr {
- struct sdpcm_hdr sdpcm;
- struct cdc_hdr cdc;
- };
- #pragma pack(pop)
- // IOCTL command send
- static void cyw_ioctl_send_cmd(unsigned int ifc, unsigned int cmd, bool set,
- size_t len) {
- struct ctrl_hdr *hdr = (struct ctrl_hdr *) txdata;
- uint16_t txlen = (uint16_t) (len + sizeof(*hdr));
- memset(txdata, 0, sizeof(*hdr));
- hdr->cdc.cmd = cmd;
- hdr->cdc.olen = (uint16_t) len;
- // hdr->cdc.ilen = 0; // actually zeroed above
- hdr->cdc.flags = ((uint32_t) ++s_ioctl_reqid << 16) | ((ifc & 0xf) << 12) |
- (set ? MG_BIT(1) : 0);
- hdr->sdpcm.len = txlen;
- hdr->sdpcm._len = (uint16_t) ~txlen;
- hdr->sdpcm.sw_hdr.sequence = ++s_tx_seqno;
- hdr->sdpcm.sw_hdr.channel_and_flags = CYW_SDPCM_CTRL_HDR;
- hdr->sdpcm.sw_hdr.header_length = offsetof(struct ctrl_hdr, cdc);
- cyw_bus_specific_tx(txdata, txlen);
- }
- // just send respective commands, response handled via CDC handler
- static void cyw_ioctl_send_get(unsigned int ifc, unsigned int cmd) {
- cyw_ioctl_send_cmd(ifc, cmd, false, 0);
- }
- static void cyw_ioctl_send_set(unsigned int ifc, unsigned int cmd, void *data,
- size_t len) {
- if (data != NULL && len > 0)
- memcpy((uint8_t *) txdata + sizeof(struct ctrl_hdr), data, len);
- cyw_ioctl_send_cmd(ifc, cmd, true, (uint16_t) len);
- }
- static void cyw_ioctl_send_iovar_get(unsigned int ifc, char *var, size_t len) {
- unsigned int namelen = strlen(var) + 1; // include '\0'
- // cmd = GET IOVAR, "set" the name...
- cyw_ioctl_send_set(ifc, 262, var, len > namelen ? len : namelen);
- }
- static void cyw_ioctl_send_iovar_set2(unsigned int ifc, char *var, void *data1,
- size_t len1, void *data2, size_t len2) {
- struct ctrl_hdr *hdr = (struct ctrl_hdr *) txdata;
- unsigned int namelen = strlen(var) + 1; // include '\0'
- uint16_t txlen, payload_len = (uint16_t) (namelen + len1 + len2);
- memcpy((uint8_t *) txdata + sizeof(*hdr), var, namelen);
- memcpy((uint8_t *) txdata + namelen + sizeof(*hdr), data1, len1);
- if (data2 != NULL)
- memcpy((uint8_t *) txdata + namelen + sizeof(*hdr) + len1, data2, len2);
- txlen = (uint16_t) (payload_len + sizeof(*hdr));
- cyw_ioctl_send_cmd(ifc, 263, true, txlen); // cmd = SET IOVAR
- }
- __attribute__((unused)) static void cyw_ioctl_send_iovar_set(unsigned int ifc,
- char *var,
- void *data,
- size_t len) {
- cyw_ioctl_send_iovar_set2(ifc, var, data, len, NULL, 0);
- }
- static inline bool delayms(unsigned int ms) {
- mg_delayms(ms);
- return true;
- }
- // wait for a response, meanwhile delivering received frames and events
- static bool cyw_ioctl_wait(void) {
- unsigned int times = 100;
- s_ioctl_resp = NULL;
- s_ioctl_err = false;
- do { // IOCTL response processing does not call any other IOCTL function
- cyw_poll(); // otherwise we can't allow them to pile up here
- // network frames will be pushed to the queue so that is safe
- } while (s_ioctl_resp == NULL && !s_ioctl_err && times-- > 0 && delayms(1));
- MG_VERBOSE(("resp: %lp, err: %c, times: %d", s_ioctl_resp,
- s_ioctl_err ? '1' : '0', (int) times));
- return s_ioctl_resp != NULL;
- }
- static bool cyw_ioctl_waitdata(void *data, size_t len) {
- if (!cyw_ioctl_wait()) return false;
- memcpy(data, s_ioctl_resp, len);
- return true;
- }
- // send respective commands, wait for a response or timeout
- static bool cyw_ioctl_get_(unsigned int ifc, unsigned int cmd, void *data,
- size_t len) {
- cyw_ioctl_send_get(ifc, cmd);
- return cyw_ioctl_waitdata(data, len);
- }
- static bool cyw_ioctl_set_(unsigned int ifc, unsigned int cmd, void *data,
- size_t len) {
- cyw_ioctl_send_set(ifc, cmd, data, len);
- return cyw_ioctl_wait();
- }
- static bool cyw_ioctl_iovar_get_(unsigned int ifc, char *var, void *data,
- size_t len) {
- cyw_ioctl_send_iovar_get(ifc, var, len);
- return cyw_ioctl_waitdata(data, len);
- }
- static bool cyw_ioctl_iovar_set2_(unsigned int ifc, char *var, void *data1,
- size_t len1, void *data2, size_t len2) {
- cyw_ioctl_send_iovar_set2(ifc, var, data1, len1, data2, len2);
- return cyw_ioctl_wait();
- }
- static bool cyw_ioctl_iovar_set_(unsigned int ifc, char *var, void *data,
- size_t len) {
- return cyw_ioctl_iovar_set2_(ifc, var, data, len, NULL, 0);
- }
- // CYW43 chipset specifics. All values read and written are in little endian
- // format
- #pragma pack(push, 1)
- // all little endian
- struct cyw_country {
- uint32_t a;
- int32_t rev;
- uint32_t c;
- };
- struct clm_hdr {
- uint16_t flag;
- uint16_t type;
- uint32_t len;
- uint32_t crc;
- };
- #pragma pack(pop)
- // worlwide rev0, TODO(): try rev 17 for 4343W
- static const uint32_t country_code = 'X' + ('X' << 8) + (0 << 16);
- static bool cyw_bus_specific_init();
- static bool cyw_load_clmll(void *data, size_t len);
- static bool cyw_load_clm(struct mg_tcpip_driver_cyw_firmware *fw) {
- return cyw_load_clmll((void *) fw->clm_addr, fw->clm_len);
- }
- // clang-format off
- static bool cyw_init(uint8_t *mac) {
- struct mg_tcpip_driver_cyw_data *d = (struct mg_tcpip_driver_cyw_data *) s_ifp->driver_data;
- uint32_t val = 0;
- if (!cyw_bus_specific_init()) return false;
- if (!cyw_load_clm(d->fw)) return false; // Load CLM blob
- // BT-ENABLED DEPENDENCY
- // set Wi-Fi up
- val = 0 /* disable */; cyw_ioctl_iovar_set("bus:txglom", (uint8_t *)&val, sizeof(val));
- val = 1 /* on */; cyw_ioctl_iovar_set("apsta", (uint8_t *)&val, sizeof(val));
- // CHIP DEPENDENCY
- val = 8 ; cyw_ioctl_iovar_set("ampdu_ba_wsize", (uint8_t *)&val, sizeof(val));
- val = 4 ; cyw_ioctl_iovar_set("ampdu_mpdu", (uint8_t *)&val, sizeof(val));
- val = 0 /* 8K */; cyw_ioctl_iovar_set("ampdu_rx_factor", (uint8_t *)&val, sizeof(val));
- //
- {
- struct cyw_country c;
- unsigned int rev = (unsigned int) (country_code >> 16) & 0xffff;
- c.c = c.a = country_code & 0xffff;
- c.rev = rev == 0 ? -1 : (int32_t) rev; // if rev is 0, set it to -1, the chip will use any NVRAM/OTP configured aggregate or default to rev 0
- cyw_ioctl_iovar_set("country", (void *)&c, sizeof(c));
- } // this takes some time to process
- { // so do some retries while enabling events of interest
- // we care for SET_SSID(0), JOIN(1), AUTH(3), DEAUTH(5), DISASSOC_IND(12), LINK(16), PSK_SUP(46), SCAN_RESULT(69); all < 128
- uint32_t data[128/8/4 + 1];
- data[0] = 0; // bss index: 0 = STA
- memset(&data[1], 0, 128/8); // mark all as not desired
- data[1] = MG_BIT(0) | MG_BIT(1) | MG_BIT(3) | MG_BIT(5) | MG_BIT(12) | MG_BIT(16); // events 0 to 31
- data[2] = MG_BIT(46 - 32); // events 32 to 63
- data[3] = MG_BIT(69 - 64); // events 64 to 95
- unsigned int times = 100;
- while (times --)
- if (cyw_ioctl_iovar_set("bsscfg:event_msgs", (uint8_t *)data, sizeof(data))) break;
- if (times == (unsigned int) ~0) return false;
- }
- val = 0; if (!cyw_ioctl_set(64 /* SET_ANTDIV */, (uint8_t *)&val, sizeof(val))) return false;
- if (!cyw_ioctl_set(2 /* UP, interface up */, NULL, 0)) return false;
- // use PM2 power saving for max throughput
- val = 200 /* ms */; if (!cyw_ioctl_iovar_set("pm2_sleep_ret", (uint8_t *)&val, sizeof(val))) return false;
- // set beacon intervals to reduce power consumption while associated to an AP but idle
- val = 1; if (!cyw_ioctl_iovar_set("bcn_li_bcn", (uint8_t *)&val, sizeof(val))) return false;
- val = 1; if (!cyw_ioctl_iovar_set("bcn_li_dtim", (uint8_t *)&val, sizeof(val))) return false;
- val = 10; if (!cyw_ioctl_iovar_set("assoc_listen", (uint8_t *)&val, sizeof(val))) return false;
- val = 1 /* auto */; if (!cyw_ioctl_set(110 /* SET_GMODE */, (uint8_t *)&val, sizeof(val))) return false;
- val = 0 /* any */; if (!cyw_ioctl_set(142 /* SET_BAND */, (uint8_t *)&val, sizeof(val))) return false;
- if (mg_log_level >= MG_LL_DEBUG) {
- char text[256]; // this is huge, but we're just starting up
- if (cyw_ioctl_iovar_get("ver", (uint8_t *)text, sizeof(text))) {
- unsigned int len = strnlen(text, sizeof(text));
- MG_DEBUG(("Firmware:\n%.*s", len, text));
- }
- text[0] = '\0';
- if (cyw_ioctl_iovar_get("clmver", (uint8_t *)text, sizeof(text)) && text[0] != '\0') {
- unsigned int len = strnlen(text, sizeof(text));
- MG_DEBUG(("CLM:\n%.*s", len, text));
- }
- }
- {
- if(cyw_ioctl_iovar_get("cur_etheraddr", mac, 6)) {
- MG_DEBUG(("MAC: %M", mg_print_mac, mac));
- } else {
- MG_ERROR(("read MAC failed"));
- }
- }
- return true;
- }
- // clang-format on
- static bool cyw_load_fwll(void *fwdata, size_t fwlen, void *nvramdata,
- size_t nvramlen);
- static bool cyw_load_firmware(struct mg_tcpip_driver_cyw_firmware *fw) {
- return cyw_load_fwll((void *) fw->code_addr, fw->code_len,
- (void *) fw->nvram_addr, fw->nvram_len);
- }
- // clang-format off
- static bool cyw_load_clmll(void *data, size_t len) {
- unsigned int sent = 0, offset = 0;
- struct clm_hdr hdr = {
- .flag = 1 << 12 /* DLOAD_HANDLER_VER */ | MG_BIT(1) /* DL_BEGIN */,
- .type = 2,
- .crc = 0};
- while (sent < len) {
- unsigned int bytes = len - sent;
- if (bytes > 1024) bytes = 1024;
- if (sent + bytes >= len) hdr.flag |= MG_BIT(2); // DL_END;
- hdr.len = bytes;
- if (!cyw_ioctl_iovar_set2_(0, "clmload", (void *) &hdr, sizeof(hdr), (uint8_t *) data + offset, bytes))
- break;
- sent += bytes;
- offset += bytes;
- hdr.flag &= (uint16_t)~MG_BIT(1); // DL_BEGIN
- }
- return sent >= len;
- }
- // clang-format on
- static void cyw_update_hash_table(void) {
- // TODO(): read database, rebuild hash table
- uint32_t val = 0;
- val = 1;
- cyw_ioctl_iovar_set2_(0, "mcast_list", (uint8_t *) &val, sizeof(val),
- (uint8_t *) mcast_addr, sizeof(mcast_addr));
- mg_delayms(50);
- }
- // CYW43 chip backplane specifics. All values read and written are in little
- // endian format
- // Access to chip backplane is done windowed in 32KB banks
- // - addr = area base address + register offset
- // - set the window address to addr & ~ADDRMSK
- // - access addr & ADDRMSK for non-32-bit quantities
- // - if accesing 32-bit quantities, do it on (addr & ADDRMSK) | ACCSS4B
- #define CYW_CHIP_CHIPCOMMON 0x18000000
- #define CYW_CHIP_BCKPLN_WINSZ 0x8000
- #define CYW_CHIP_BCKPLN_ADDRMSK 0x7fff
- #define CYW_CHIP_BCKPLN_ACCSS4B MG_BIT(15)
- #define CYW_CHIP_BCKPLN_WRAPPOFF 0x100000
- // BUS DEPENDENCY: max bus to backplane transfer size, bus function id
- #define CYW_CHIP_BCKPLN_SPIMAX 64
- #define CYW_CHIP_BCKPLN_SDIOMAX 1536
- #if MG_ENABLE_DRIVER_CYW_SDIO
- #define CYW_CHIP_BCKPLN_BUSMAX CYW_CHIP_BCKPLN_SDIOMAX
- #define CYW_BUS_FUNC_CHIP CYW_SDIO_FUNC_CHIP
- #else
- #define CYW_CHIP_BCKPLN_BUSMAX CYW_CHIP_BCKPLN_SPIMAX
- #define CYW_BUS_FUNC_CHIP CYW_SPID_FUNC_CHIP
- #endif
- // CHIP DEPENDENCY
- #define CYW_CHIP_ARMCORE_BASE (CYW_CHIP_CHIPCOMMON + 0x3000)
- #define CYW_CHIP_SOCSRAM_BASE (CYW_CHIP_CHIPCOMMON + 0x4000)
- #define CYW_CHIP_ARMCORE (CYW_CHIP_ARMCORE_BASE + CYW_CHIP_BCKPLN_WRAPPOFF)
- #define CYW_CHIP_SOCSRAM (CYW_CHIP_SOCSRAM_BASE + CYW_CHIP_BCKPLN_WRAPPOFF)
- #define CYW_CHIP_ATCMRAM_BASE 0
- #define CYW_CHIP_RAM_SIZE 0x80000
- //
- #define CYW_CHIP_ADDRLOW 0x1000a
- #define CYW_CHIP_ADDRMID 0x1000b
- #define CYW_CHIP_ADDRHIGH 0x1000c
- #define CYW_CHIP_SPIFRCTRL 0x1000d
- #define CYW_CHIP_CLOCKCSR 0x1000e
- #define CYW_CHIP_PULLUP 0x1000f
- #define CYW_CHIP_WAKEUPCTL 0x1001e
- #define CYW_CHIP_SLEEPCSR 0x1001f
- #define CYW_CHIP_SOCSRAM_BANKXIDX 0x010
- #define CYW_CHIP_SOCSRAM_BANKXPDA 0x044
- #define CYW_CHIP_AI_IOCTRL 0x408
- #define CYW_CHIP_AI_RESETCTRL 0x800
- static bool cyw_bus_write(unsigned int f, uint32_t addr, void *data,
- uint16_t len);
- static bool cyw_bus_read(unsigned int f, uint32_t addr, void *data,
- uint16_t len);
- // clang-format off
- // set backplane window to requested area.
- static void cyw_set_backplane_window(uint32_t addr) {
- uint32_t val;
- addr &= ~CYW_CHIP_BCKPLN_ADDRMSK;
- val = (addr >> 24) & 0xff; cyw_bus_write(CYW_BUS_FUNC_CHIP, CYW_CHIP_ADDRHIGH, &val, 1);
- val = (addr >> 16) & 0xff; cyw_bus_write(CYW_BUS_FUNC_CHIP, CYW_CHIP_ADDRMID, &val, 1);
- val = (addr >> 8) & 0xff; cyw_bus_write(CYW_BUS_FUNC_CHIP, CYW_CHIP_ADDRLOW, &val, 1);
- }
- static bool cyw_core_reset(uint32_t core_base, bool check) {
- uint32_t val = 0;
- // core disabled after chip reset
- cyw_set_backplane_window(core_base); // set backplane window for requested area; we do know offsets fall within that window
- // possible CHIP DEPENDENCY: AI_RESETSTATUS check and wait (instead of these cool reads) to ensure backplane operations end
- cyw_bus_read(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_IOCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1); // ensure backplane operations end
- val = MG_BIT(1) | MG_BIT(0) /* SICF_FGC | SICF_CLOCK_EN */; cyw_bus_write(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_IOCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1); // reset
- cyw_bus_read(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_IOCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1); // ensure backplane operations end
- val = 0x00; cyw_bus_write(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_RESETCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1); // release reset
- mg_delayms(1);
- val = MG_BIT(0) /* SICF_CLOCK_EN */; cyw_bus_write(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_IOCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1);
- cyw_bus_read(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_IOCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1); // ensure backplane operations end
- mg_delayms(1);
- if (check) {
- // Verify only clock is enabled
- cyw_bus_read(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_IOCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1);
- if ((val & (MG_BIT(1) | MG_BIT(0)) /* SICF_FGC | SICF_CLOCK_EN) */) != MG_BIT(0)) return false;
- // Verify it is not in reset state
- cyw_bus_read(CYW_BUS_FUNC_CHIP, (core_base + CYW_CHIP_AI_RESETCTRL) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 1);
- if (val & MG_BIT(0)) return false; // AIRC_RESET
- }
- return true;
- }
- static void cyw_socram_init(void) {
- uint32_t val;
- // CHIP DEPENDENCY: disable remap for SRAM_3: 43430 and 43439 only
- cyw_set_backplane_window(CYW_CHIP_SOCSRAM_BASE); // set backplane window for requested area; we do know offsets fall within that window
- val = 0x03; cyw_bus_write(CYW_BUS_FUNC_CHIP, ((CYW_CHIP_SOCSRAM_BASE + CYW_CHIP_SOCSRAM_BANKXIDX) & CYW_CHIP_BCKPLN_ADDRMSK), &val, sizeof(val));
- val = 0x00; cyw_bus_write(CYW_BUS_FUNC_CHIP, ((CYW_CHIP_SOCSRAM_BASE + CYW_CHIP_SOCSRAM_BANKXPDA) & CYW_CHIP_BCKPLN_ADDRMSK), &val, sizeof(val));
- }
- // transfer is fractioned in bus-to-backplane-size units within backplane windows
- static void cyw_load_data(uint32_t dest, void *data, size_t len) {
- size_t sent = 0, offset = 0;
- uint32_t last_addr = (uint32_t) ~0;
- while (sent < len) {
- size_t bytes = len - sent, avail;
- uint32_t addr = dest + offset;
- if (addr - last_addr >= CYW_CHIP_BCKPLN_WINSZ || last_addr == (uint32_t) ~0) {
- cyw_set_backplane_window(addr); // set backplane window for requested area
- last_addr = addr & ~CYW_CHIP_BCKPLN_ADDRMSK;
- }
- addr &= CYW_CHIP_BCKPLN_ADDRMSK;
- avail = CYW_CHIP_BCKPLN_WINSZ - (unsigned int) addr; // internal backplane limit
- if (bytes > avail) bytes = avail;
- if (bytes > CYW_CHIP_BCKPLN_BUSMAX) bytes = CYW_CHIP_BCKPLN_BUSMAX; // bus to backplane transfer limit
- cyw_bus_write(CYW_BUS_FUNC_CHIP, addr, (uint8_t *)data + offset, (uint16_t) bytes);
- sent += bytes;
- offset += bytes;
- }
- }
- // CHIP DEPENDENCY: no SOCSRAM base address; start address in fwdata image (Cortex-R4 chips)
- static bool cyw_load_fwll(void *fwdata, size_t fwlen, void *nvramdata, size_t nvramlen) {
- uint32_t val = ((~(nvramlen / 4) & 0xffff) << 16) | (nvramlen / 4); // ~len len in 32-bit words
- cyw_core_reset(CYW_CHIP_SOCSRAM, false); // cores were disabled at chip reset
- cyw_socram_init();
- cyw_load_data(CYW_CHIP_ATCMRAM_BASE, fwdata, fwlen);
- mg_delayms(5); // TODO(scaprile): CHECK IF THIS IS ACTUALLY NEEDED
- // Load NVRAM and place 'length ~length' at the end; end of chip RAM
- {
- const uint32_t start = CYW_CHIP_RAM_SIZE - 4 - nvramlen;
- cyw_load_data(start, nvramdata, nvramlen); // nvramlen must be a multiple of 4
- // RAM_SIZE is a multiple of WINSZ, so the place for len ~len will be at the end of the window
- cyw_bus_write(CYW_BUS_FUNC_CHIP, (CYW_CHIP_BCKPLN_WINSZ - 4), &val, sizeof(val));
- }
- // Reset ARM core and check it starts
- if (!cyw_core_reset(CYW_CHIP_ARMCORE, true)) return false;
- return true;
- }
- // clang-format on
- #if !MG_ENABLE_DRIVER_CYW_SDIO
- // CYW43 SPI bus specifics
- #define CYW_BUS_SPI_BUSCTRL 0x00 // 4 regs, 0 to 3
- #define CYW_BUS_SPI_INT 0x04 // 2 regs, 4 to 5
- #define CYW_BUS_SPI_INTEN 0x06 // 16-bit register
- #define CYW_BUS_SPI_STATUS 0x08 // 32-bit register
- #define CYW_BUS_SPI_TEST 0x14 // 32-bit register
- #define CYW_BUS_SPI_RESPDLY_F1 0x1d // 8-bit register, F1: chip
- #define CYW_BUS_STS_LEN(x) ((x >> 9) & 0x7ff)
- static bool cyw_spi_write(unsigned int f, uint32_t addr, void *data,
- uint16_t len);
- static void cyw_spi_read(unsigned int f, uint32_t addr, void *data,
- uint16_t len);
- // clang-format off
- static size_t cyw_spi_poll(uint8_t *response) {
- size_t len;
- uint32_t res;
- // SPI poll
- cyw_spi_read(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_STATUS, &res, sizeof(res));
- if (res == (uint32_t) ~0 || !(res & MG_BIT(8) /* packet available */ )) return 0;
- len = CYW_BUS_STS_LEN(res);
- if (len == 0) { // just ack IRQ
- uint16_t val = 1;
- cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_SPIFRCTRL, &val, 1);
- cyw_spi_read(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_INT, &val, sizeof(val));
- cyw_spi_write(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_INT, &val, sizeof(val));
- return 0;
- }
- cyw_spi_read(CYW_SPID_FUNC_WLAN, 0, response, (uint16_t)len);
- return len;
- }
- static size_t cyw_spi_tx(uint32_t *data, uint16_t len) {
- while (len & 3) ((uint8_t *)data)[len++] = 0; // SPI 32-bit padding
- return cyw_spi_write(CYW_SPID_FUNC_WLAN, 0, data, len) ? len: 0;
- }
- // this can be integrated in lowest level SPI read/write _driver_ functions
- // (those calling hal SPI transaction functions), though is only used at start
- uint32_t sw16_2(uint32_t data) {
- return ((uint32_t)mg_htons((uint16_t)(data >> 16)) << 16) + mg_htons((uint16_t)data);
- }
- // DS 4.2.2 Table 6: signal we're working in 16-bit mode
- #define CYW_SPI_16bMODE MG_BIT(2) // arbitrary bit out of the FUNC space
- static bool cyw_spi_init() {
- struct mg_tcpip_driver_cyw_data *d = (struct mg_tcpip_driver_cyw_data *) s_ifp->driver_data;
- uint32_t val = 0;
- // DS 4.2.3 Boot-Up Sequence; WHD: other chips might require more effort
- unsigned int times = 51;
- while (times--) {
- cyw_spi_read(CYW_SPID_FUNC_BUS | CYW_SPI_16bMODE, CYW_BUS_SPI_TEST, &val, sizeof(val));
- if (sw16_2(val) == 0xFEEDBEAD) break;
- mg_delayms(1);
- }
- if (times == (unsigned int) ~0) return false;
- // DS 4.2.3 Table 6. Chip starts in 16-bit little-endian mode.
- // Configure SPI and switch to 32-bit big-endian mode:
- // - High-speed mode: d->hs true
- // - IRQ POLARITY high
- // - SPI RESPONSE DELAY 4 bytes time [not in DS] TODO(scaprile): logic ana
- // - Status not sent after command, IRQ with status
- val = sw16_2(0x000204a3 | (d->hs ? MG_BIT(4) : 0)); // 4 reg content
- cyw_spi_write(CYW_SPID_FUNC_BUS | CYW_SPI_16bMODE, CYW_BUS_SPI_BUSCTRL, &val, sizeof(val));
- mg_tcpip_call(s_ifp, MG_TCPIP_EV_DRIVER, NULL);
- cyw_spi_read(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_TEST, &val, sizeof(val));
- if (val != 0xFEEDBEAD) return false;
- val = 4; cyw_spi_write(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_RESPDLY_F1, &val, 1);
- val = 0x99; // clear error bits DATA_UNAVAILABLE, COMMAND_ERROR, DATA_ERROR, F1_OVERFLOW
- cyw_spi_write(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_INT, &val, 1);
- val = 0x00be; // Enable IRQs F2_F3_FIFO_RD_UNDERFLOW, F2_F3_FIFO_WR_OVERFLOW, COMMAND_ERROR, DATA_ERROR, F2_PACKET_AVAILABLE, F1_OVERFLOW
- // BT-ENABLED DEPENDENCY: add F1_INTR (bit 13)
- cyw_spi_write(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_INTEN, &val, sizeof(uint16_t));
- // chip backplane is ready, initialize it
- // request ALP (Active Low Power) clock
- val = MG_BIT(3) /* ALP_REQ */; cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1);
- // BT-ENABLED DEPENDENCY
- times = 10;
- while (times--) {
- cyw_spi_read(CYW_SPID_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1);
- if (val & MG_BIT(6)) break; // ALP_AVAIL
- mg_delayms(1);
- }
- if (times == (unsigned int) ~0) return false;
- // clear request
- val = 0; cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1);
- cyw_set_backplane_window(CYW_CHIP_CHIPCOMMON); // set backplane window to start of CHIPCOMMON area
- cyw_spi_read(CYW_SPID_FUNC_CHIP, (CYW_CHIP_CHIPCOMMON + 0x00) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 2);
- if (val == 43430) val = 4343;
- MG_INFO(("WLAN chip is CYW%u%c", val, val == 4343 ? 'W' : ' '));
- // Load firmware (code and NVRAM)
- if (!cyw_load_firmware(d->fw)) return false;
- // Wait for High Throughput (HT) clock ready
- times = 50;
- while (times--) {
- cyw_spi_read(CYW_SPID_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1);
- if (val & MG_BIT(7)) break; // HT_AVAIL
- mg_delayms(1);
- }
- if (times == (unsigned int) ~0) return false;
- // Wait for backplane ready
- times = 1000;
- while (times--) {
- cyw_spi_read(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_STATUS, &val, sizeof(val));
- if (val & MG_BIT(5)) break; // F2_RX_READY
- mg_delayms(1);
- }
- if (times == (unsigned int) ~0) return false;
- // CHIP DEPENDENCY
- // Enable save / restore
- // Configure WakeupCtrl, set HT_AVAIL in CLOCK_CSR
- cyw_spi_read(CYW_SPID_FUNC_CHIP, CYW_CHIP_WAKEUPCTL, &val, 1);
- val |= MG_BIT(1) /* WAKE_TILL_HT_AVAIL */; cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_WAKEUPCTL, &val, 1);
- #if 0
- // Set BRCM_CARDCAP to CMD_NODEC. NOTE(): This is probably only necessary for SDIO, not SPI
- val = MG_BIT(3); cyw_spi_write(CYW_SPID_FUNC_BUS, 0xf0 /* SDIOD_CCCR_BRCM_CARDCAP */, &val, 1);
- #endif
- // Force HT request to chip backplane
- val = MG_BIT(1) /* FORCE_HT */; cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1);
- // Enable Keep SDIO On (KSO)
- cyw_spi_read(CYW_SPID_FUNC_CHIP, CYW_CHIP_SLEEPCSR, &val, 1);
- if (!(val & MG_BIT(0))) {
- val |= MG_BIT(0); cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_SLEEPCSR, &val, 1);
- }
- // The SPI bus can be configured for sleep (KSO controls wlan block sleep)
- cyw_spi_read(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_BUSCTRL, &val, sizeof(val));
- val &= ~MG_BIT(7) /* WAKE_UP */; cyw_spi_write(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_BUSCTRL, &val, sizeof(val));
- // Set SPI bus sleep
- val = 0x0f; cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_PULLUP, &val, 1);
- // Clear pullups. NOTE(): ?
- val = 0x00; cyw_spi_write(CYW_SPID_FUNC_CHIP, CYW_CHIP_PULLUP, &val, 1);
- cyw_spi_read(CYW_SPID_FUNC_CHIP, CYW_CHIP_PULLUP, &val, 1);
- // Clear possible data unavailable error
- cyw_spi_read(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_INTEN, &val, sizeof(uint16_t));
- if (val & MG_BIT(0)) cyw_spi_write(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_INTEN, &val, sizeof(uint16_t));
- return true;
- }
- // clang-format on
- // gSPI, CYW43439 DS 4.2.1 Fig.12
- #define CYW_SPID_LEN(x) ((x) &0x7FF) // bits 0-10
- #define CYW_SPID_ADDR(x) (((x) &0x1FFFF) << 11) // bits 11-27,
- #define CYW_SPID_FUNC(x) (((x) &3) << 28) // bits 28-29
- #define CYW_SPID_INC MG_BIT(30)
- #define CYW_SPID_WR MG_BIT(31)
- static bool cyw_spi_write(unsigned int f, uint32_t addr, void *data,
- uint16_t len) {
- struct mg_tcpip_driver_cyw_data *d =
- (struct mg_tcpip_driver_cyw_data *) s_ifp->driver_data;
- struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) d->bus;
- uint32_t hdr = CYW_SPID_WR | CYW_SPID_INC | CYW_SPID_FUNC(f) |
- CYW_SPID_ADDR(addr) | CYW_SPID_LEN(len); // gSPI header
- // TODO(scaprile): check spin in between and timeout values, return false
- if (f == CYW_SPID_FUNC_WLAN) {
- uint32_t val = 0;
- while ((val & MG_BIT(5)) != MG_BIT(5)) // F2 rx ready (FIFO ready)
- cyw_spi_read(CYW_SPID_FUNC_BUS, CYW_BUS_SPI_STATUS, &val, sizeof(val));
- }
- if (f & CYW_SPI_16bMODE)
- hdr = sw16_2(hdr); // swap half-words in 16-bit little-endian mode
- s->begin(NULL);
- s->txn(NULL, (uint8_t *) &hdr, NULL, sizeof(hdr));
- if (len <= 4) {
- uint32_t pad = 0;
- memcpy(&pad, data, len);
- s->txn(NULL, (uint8_t *) &pad, NULL, sizeof(pad));
- } else {
- s->txn(NULL, (uint8_t *) data, NULL, len);
- }
- s->end(NULL);
- return true;
- }
- // will write 32-bit aligned quantities to data if f == CYW_SPID_FUNC_WLAN
- static void cyw_spi_read(unsigned int f, uint32_t addr, void *data,
- uint16_t len) {
- struct mg_tcpip_driver_cyw_data *d =
- (struct mg_tcpip_driver_cyw_data *) s_ifp->driver_data;
- struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) d->bus;
- uint32_t padding =
- f == CYW_SPID_FUNC_CHIP
- ? 4
- : 0; // add padding to chip backplane reads as a response delay
- uint32_t hdr = CYW_SPID_INC | CYW_SPID_FUNC(f) | CYW_SPID_ADDR(addr) |
- CYW_SPID_LEN(len + padding); // gSPI header
- if (f == CYW_SPID_FUNC_WLAN && (len & 3))
- len = (len + 4) & ~3; // align WLAN transfers to 32-bit
- if (f & CYW_SPI_16bMODE)
- hdr = sw16_2(hdr); // swap half-words in 16-bit little-endian mode
- s->begin(NULL);
- s->txn(NULL, (uint8_t *) &hdr, NULL, sizeof(hdr));
- if (f == CYW_SPID_FUNC_CHIP) {
- uint32_t pad;
- s->txn(NULL, NULL, (uint8_t *) &pad, 4); // read padding back and discard
- }
- s->txn(NULL, NULL, (uint8_t *) data, len);
- s->end(NULL);
- }
- static bool cyw_bus_specific_init(void) {
- return cyw_spi_init();
- }
- static size_t cyw_bus_specific_poll(uint32_t *response) {
- return cyw_spi_poll((uint8_t *) response);
- }
- static size_t cyw_bus_specific_tx(uint32_t *data, uint16_t len) {
- return cyw_spi_tx(data, len);
- }
- static bool cyw_bus_write(unsigned int f, uint32_t addr, void *data,
- uint16_t len) {
- if (f == CYW_SPID_FUNC_CHIP && len >= 4) addr |= CYW_CHIP_BCKPLN_ACCSS4B;
- return cyw_spi_write(f, addr, data, len);
- }
- static bool cyw_bus_read(unsigned int f, uint32_t addr, void *data,
- uint16_t len) {
- cyw_spi_read(f, addr, data, len);
- return true;
- }
- #else // MG_ENABLE_DRIVER_CYW_SDIO
- // CYW43 SDIO bus specifics
- // CYW4343W and CYW43439 DS 4.1 SDIO v2.0:
- //- F0: max block size is 32 bytes
- //- F1: max block size is 64 bytes
- //- F2: max block size is 512 bytes
- // clang-format off
- static bool cyw_sdio_transfer(bool write, unsigned int f, uint32_t addr, void *data, uint32_t len) {
- struct mg_tcpip_driver_cyw_data *d = (struct mg_tcpip_driver_cyw_data *) s_ifp->driver_data;
- struct mg_tcpip_sdio *s = (struct mg_tcpip_sdio *) d->bus;
- uint32_t *ptr = (uint32_t *) data; // assume 32-bit aligned data (all except firmware)
- if (write && (size_t) data & 3) { // missed, source data is not 32-bit aligned
- memcpy(txdata, data, len); // copy to an aligned buffer, we know it fits
- ptr = txdata;
- } // all possible read destinations are 32-bit aligned
- // mg_sdio_transfer requires 32-bit alignment for > 1 byte transfers
- return mg_sdio_transfer(s, write, f, addr, ptr, len);
- }
- static size_t cyw_sdio_poll(uint32_t *response) {
- uint32_t res;
- uint16_t *len = (uint16_t *)&res;
- // WHD: internal docs, "tag" hinting a possible packet.
- // This is actually the len / ~len field of a possible struct sdpcm_hdr, if there is a packet available, or 0 if there is none.
- cyw_sdio_transfer(false, CYW_SDIO_FUNC_WLAN, 0, &res, sizeof(res)); // read "the tag"
- if ((len[0] | len[1]) == 0 || (len[0] ^ len[1]) != 0xffff || *len <= 4) return 0;
- response[0] = res; // copy what we already read, then read the rest
- cyw_sdio_transfer(false, CYW_SDIO_FUNC_WLAN, 0, response + 1, *len - sizeof(res));
- return (size_t)*len;
- }
- static size_t cyw_sdio_tx(uint32_t *data, uint16_t len) {
- return cyw_sdio_transfer(true, CYW_SDIO_FUNC_WLAN, 0, data, len) ? len: 0;
- }
- static bool cyw_sdio_init() {
- struct mg_tcpip_driver_cyw_data *d = (struct mg_tcpip_driver_cyw_data *) s_ifp->driver_data;
- struct mg_tcpip_sdio *s = (struct mg_tcpip_sdio *) d->bus;
- uint32_t val = 0;
- if (!mg_sdio_init(s)) return false;
- // no block transfers on F0. if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_BUS, 32)) return false;
- if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_CHIP, 64)) return false;
- if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_WLAN, 64)) return false;
- // TODO(scaprile): we don't handle SDIO interrupts, study CCCR INTEN and SDIO support (SDIO 6.3, 8)
- // Enable chip backplane (F1)
- if (!mg_sdio_enable_f(s, CYW_SDIO_FUNC_CHIP)) return false;
- // Wait for F1 to be ready
- if (!mg_sdio_waitready_f(s, CYW_SDIO_FUNC_CHIP)) return false;
- // chip backplane is ready, initialize it
- // request ALP (Active Low Power) clock
- val = MG_BIT(5) | MG_BIT(3) | MG_BIT(0); // HW_CLKREQ_OFF ALP_REQ FORCE_ALP
- cyw_sdio_transfer(true, CYW_SDIO_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1);
- // BT-ENABLED DEPENDENCY
- unsigned int times = 10;
- while (times--) {
- if(!cyw_sdio_transfer(false, CYW_SDIO_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1)) return false;
- if (val & MG_BIT(6)) break; // ALP_AVAIL
- mg_delayms(1);
- }
- if (times == (unsigned int) ~0) return false;
- // clear request
- val = 0; cyw_sdio_transfer(true, CYW_SDIO_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1);
- // Enable WLAN (F2)
- if (!mg_sdio_enable_f(s, CYW_SDIO_FUNC_WLAN)) return false;
- // Clear pullups. NOTE(): ?
- val = 0x00; cyw_sdio_transfer(true, CYW_SDIO_FUNC_CHIP, CYW_CHIP_PULLUP, &val, 1);
- // we don't handle wake nor OOB interrupts; SEP_INT_CTL is a vendor specific SDIO register
- // SDIO interrupts: enable F2 interrupt only
- cyw_set_backplane_window(CYW_CHIP_CHIPCOMMON); // set backplane window to start of CHIPCOMMON area
- cyw_sdio_transfer(false, CYW_SDIO_FUNC_CHIP, (CYW_CHIP_CHIPCOMMON + 0x00) & CYW_CHIP_BCKPLN_ADDRMSK, &val, 2);
- if (val == 43430) val = 4343;
- MG_INFO(("WLAN chip is CYW%u%c", val, val == 4343 ? 'W' : ' '));
- // Load firmware (code and NVRAM)
- if (!cyw_load_firmware(d->fw)) return false;
- // Wait for High Throughput (HT) clock ready
- times = 50;
- while (times--) {
- if(!cyw_sdio_transfer(false, CYW_SDIO_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1)) return false;
- if (val & MG_BIT(7)) break; // HT_AVAIL
- mg_delayms(1);
- }
- if (times == (unsigned int) ~0) return false;
- // Wait for WLAN ready
- if (!mg_sdio_waitready_f(s, CYW_SDIO_FUNC_WLAN)) return false;
- // CHIP DEPENDENCY
- // Enable save / restore
- // Configure WakeupCtrl, set HT_AVAIL in CLOCK_CSR
- if(!cyw_sdio_transfer(false, CYW_SDIO_FUNC_CHIP, CYW_CHIP_WAKEUPCTL, &val, 1)) return false;
- val |= MG_BIT(1) /* WAKE_TILL_HT_AVAIL */; cyw_sdio_transfer(true, CYW_SDIO_FUNC_CHIP, CYW_CHIP_WAKEUPCTL, &val, 1);
- #if 0 // TODO(scaprile): Check if this is actually necessary
- // Set BRCM_CARDCAP to CMD_NODEC. This is a vendor specific SDIO register
- val = MG_BIT(3); cyw_sdio_transfer(true, CYW_SDIO_FUNC_BUS, 0xf0 /* SDIOD_CCCR_BRCM_CARDCAP */, &val, 1);
- #endif
- // Force HT request to chip backplane
- val = MG_BIT(1) /* FORCE_HT */; if(!cyw_sdio_transfer(true, CYW_SDIO_FUNC_CHIP, CYW_CHIP_CLOCKCSR, &val, 1)) return false;
- // Enable Keep SDIO On (KSO)
- cyw_sdio_transfer(false, CYW_SDIO_FUNC_CHIP, CYW_CHIP_SLEEPCSR, &val, 1);
- if (!(val & MG_BIT(0))) {
- val |= MG_BIT(0); cyw_sdio_transfer(true, CYW_SDIO_FUNC_CHIP, CYW_CHIP_SLEEPCSR, &val, 1);
- }
- return true;
- }
- // clang-format on
- static bool cyw_bus_specific_init(void) {
- return cyw_sdio_init();
- }
- static size_t cyw_bus_specific_poll(uint32_t *response) {
- return cyw_sdio_poll(response);
- }
- static size_t cyw_bus_specific_tx(uint32_t *data, uint16_t len) {
- return cyw_sdio_tx(data, len);
- }
- static bool cyw_bus_write(unsigned int f, uint32_t addr, void *data,
- uint16_t len) {
- if (f == CYW_SDIO_FUNC_CHIP && len == 4) addr |= CYW_CHIP_BCKPLN_ACCSS4B;
- return cyw_sdio_transfer(true, f, addr, data, (uint32_t) len);
- }
- static bool cyw_bus_read(unsigned int f, uint32_t addr, void *data,
- uint16_t len) {
- return cyw_sdio_transfer(false, f, addr, data, (uint32_t) len);
- }
- #endif
- // Mongoose Wi-Fi API functions
- bool mg_wifi_scan(void) {
- return cyw_wifi_scan();
- }
- bool mg_wifi_connect(struct mg_wifi_data *wifi) {
- s_ifp->ip = s_ip;
- s_ifp->mask = s_mask;
- if (s_ifp->ip == 0) s_ifp->enable_dhcp_client = true;
- s_ifp->enable_dhcp_server = false;
- MG_DEBUG(("Connecting to '%s'", wifi->ssid));
- return cyw_wifi_connect(wifi->ssid, wifi->pass);
- }
- bool mg_wifi_disconnect(void) {
- return cyw_wifi_disconnect();
- }
- bool mg_wifi_ap_start(struct mg_wifi_data *wifi) {
- MG_DEBUG(("Starting AP '%s' (%u)", wifi->apssid, wifi->apchannel));
- return cyw_wifi_ap_start(wifi->apssid, wifi->appass, wifi->apchannel);
- }
- bool mg_wifi_ap_stop(void) {
- return cyw_wifi_ap_stop();
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/imxrt.c"
- #endif
- #if MG_ENABLE_TCPIP && \
- (defined(MG_ENABLE_DRIVER_IMXRT10) && MG_ENABLE_DRIVER_IMXRT10) || \
- (defined(MG_ENABLE_DRIVER_IMXRT11) && MG_ENABLE_DRIVER_IMXRT11) || \
- (defined(MG_ENABLE_DRIVER_MCXE) && MG_ENABLE_DRIVER_MCXE)
- struct imxrt_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
- #if defined(MG_ENABLE_DRIVER_IMXRT11) && MG_ENABLE_DRIVER_IMXRT11
- #define ENET ((struct imxrt_enet *) (uintptr_t) 0x40424000U)
- #define ETH_DESC_CNT 5 // Descriptors count
- #elif defined(MG_ENABLE_DRIVER_IMXRT10) && MG_ENABLE_DRIVER_IMXRT10
- #define ENET ((struct imxrt_enet *) (uintptr_t) 0x402D8000U)
- #define ETH_DESC_CNT 4 // Descriptors count
- #else // MG_ENABLE_DRIVER_MCXE
- #define ENET ((struct imxrt_enet *) (uintptr_t) 0x40079000U)
- #define ETH_DESC_CNT 4 // Descriptor count
- #endif
- #define ETH_PKT_SIZE 1536 // Max frame size, 64-bit aligned
- struct enet_desc {
- uint16_t length; // Data length
- uint16_t control; // Control and status
- uint32_t *buffer; // Data ptr
- };
- // Descriptors: in non-cached area (TODO(scaprile)), (37.5.1.22.2 37.5.1.23.2)
- // Buffers: 64-byte aligned (37.3.14)
- static volatile struct enet_desc s_rxdesc[ETH_DESC_CNT] MG_ETH_RAM MG_64BYTE_ALIGNED;
- static volatile struct enet_desc s_txdesc[ETH_DESC_CNT] MG_ETH_RAM MG_64BYTE_ALIGNED;
- static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_64BYTE_ALIGNED;
- static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_64BYTE_ALIGNED;
- static struct mg_tcpip_if *s_ifp; // MIP interface
- static uint16_t enet_read_phy(uint8_t addr, uint8_t reg) {
- ENET->EIR |= MG_BIT(23); // MII interrupt clear
- ENET->MMFR = (1 << 30) | (2 << 28) | (addr << 23) | (reg << 18) | (2 << 16);
- while ((ENET->EIR & MG_BIT(23)) == 0) (void) 0;
- return ENET->MMFR & 0xffff;
- }
- static void enet_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
- ENET->EIR |= MG_BIT(23); // MII interrupt clear
- ENET->MMFR =
- (1 << 30) | (1 << 28) | (addr << 23) | (reg << 18) | (2 << 16) | val;
- while ((ENET->EIR & MG_BIT(23)) == 0) (void) 0;
- }
- // MDC clock is generated from IPS Bus clock (ipg_clk); as per 802.3,
- // it must not exceed 2.5MHz
- // The PHY receives the PLL6-generated 50MHz clock
- static bool mg_tcpip_driver_imxrt_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_imxrt_data *d =
- (struct mg_tcpip_driver_imxrt_data *) ifp->driver_data;
- s_ifp = ifp;
- // Init RX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_rxdesc[i].control = MG_BIT(15); // Own (E)
- s_rxdesc[i].buffer = (uint32_t *) s_rxbuf[i]; // Point to data buffer
- }
- s_rxdesc[ETH_DESC_CNT - 1].control |= MG_BIT(13); // Wrap last descriptor
- // Init TX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- // s_txdesc[i].control = MG_BIT(10); // Own (TC)
- s_txdesc[i].buffer = (uint32_t *) s_txbuf[i];
- }
- s_txdesc[ETH_DESC_CNT - 1].control |= MG_BIT(13); // Wrap last descriptor
- ENET->ECR = MG_BIT(0); // Software reset, disable
- while ((ENET->ECR & MG_BIT(0))) (void) 0; // Wait until done
- // Set MDC clock divider. If user told us the value, use it.
- // TODO(): Otherwise, guess (currently assuming max freq)
- int cr = (d == NULL || d->mdc_cr < 0) ? 24 : d->mdc_cr;
- ENET->MSCR = (1 << 8) | ((cr & 0x3f) << 1); // HOLDTIME 2 clks
- struct mg_phy phy = {enet_read_phy, enet_write_phy};
- mg_phy_init(&phy, d->phy_addr, MG_PHY_LEDS_ACTIVE_HIGH); // MAC clocks PHY
- // Select RMII mode, 100M, keep CRC, set max rx length, disable loop
- ENET->RCR = (1518 << 16) | MG_BIT(8) | MG_BIT(2);
- // ENET->RCR |= MG_BIT(3); // Receive all
- ENET->TCR = MG_BIT(2); // Full-duplex
- ENET->RDSR = (uint32_t) (uintptr_t) s_rxdesc;
- ENET->TDSR = (uint32_t) (uintptr_t) s_txdesc;
- ENET->MRBR[0] = ETH_PKT_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];
- ENET->ECR = MG_BIT(8) | MG_BIT(1); // Little-endian CPU, Enable
- ENET->EIMR = MG_BIT(25); // Set interrupt mask
- ENET->RDAR = MG_BIT(24); // Receive Descriptors have changed
- ENET->TDAR = MG_BIT(24); // Transmit Descriptors have changed
- // ENET->OPD = 0x10014;
- ENET->IAUR = 0;
- ENET->IALR = 0;
- ENET->GAUR = 0;
- ENET->GALR = 0;
- return true;
- }
- // Transmit frame
- static size_t mg_tcpip_driver_imxrt_tx(const void *buf, size_t len,
- struct mg_tcpip_if *ifp) {
- static int s_txno; // Current descriptor index
- if (len > sizeof(s_txbuf[ETH_DESC_CNT])) {
- MG_ERROR(("Frame too big, %ld", (long) len));
- len = (size_t) -1; // fail
- } else if ((s_txdesc[s_txno].control & MG_BIT(15))) {
- ifp->nerr++;
- MG_ERROR(("No descriptors available"));
- len = 0; // retry later
- } else {
- memcpy(s_txbuf[s_txno], buf, len); // Copy data
- s_txdesc[s_txno].length = (uint16_t) len; // Set data len
- // Table 37-34, R, L, TC (Ready, last, transmit CRC after frame
- s_txdesc[s_txno].control |=
- (uint16_t) (MG_BIT(15) | MG_BIT(11) | MG_BIT(10));
- ENET->TDAR = MG_BIT(24); // Descriptor ring updated
- if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
- }
- (void) ifp;
- return len;
- }
- static void mg_tcpip_driver_imxrt_update_hash_table(struct mg_tcpip_if *ifp) {
- // TODO(): read database, rebuild hash table
- // RM 37.3.4.3.2
- uint32_t hash_table[2] = {0, 0};
- // uint8_t hash64 = ((~mg_crc32(0, mcast_addr, 6)) >> 26) & 0x3f;
- // hash_table[((uint8_t)hash64) >> 5] |= (1 << (hash64 & 0x1f));
- hash_table[1] = MG_BIT(1); // above reduces to this for mDNS addr
- ENET->GAUR = hash_table[1];
- ENET->GALR = hash_table[0];
- (void) ifp;
- }
- static bool mg_tcpip_driver_imxrt_poll(struct mg_tcpip_if *ifp, bool s1) {
- if (ifp->update_mac_hash_table) {
- mg_tcpip_driver_imxrt_update_hash_table(ifp);
- ifp->update_mac_hash_table = false;
- }
- if (!s1) return false;
- struct mg_tcpip_driver_imxrt_data *d =
- (struct mg_tcpip_driver_imxrt_data *) ifp->driver_data;
- uint8_t speed = MG_PHY_SPEED_10M;
- bool up = false, full_duplex = false;
- struct mg_phy phy = {enet_read_phy, enet_write_phy};
- up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed);
- if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
- // tmp = reg with flags set to the most likely situation: 100M full-duplex
- // if(link is slow or half) set flags otherwise
- // reg = tmp
- uint32_t tcr = ENET->TCR | MG_BIT(2); // Full-duplex
- uint32_t rcr = ENET->RCR & ~MG_BIT(9); // 100M
- if (speed == MG_PHY_SPEED_10M) rcr |= MG_BIT(9); // 10M
- if (full_duplex == false) tcr &= ~MG_BIT(2); // Half-duplex
- ENET->TCR = tcr; // IRQ handler does not fiddle with these registers
- ENET->RCR = rcr;
- MG_DEBUG(("Link is %uM %s-duplex", rcr & MG_BIT(9) ? 10 : 100,
- tcr & MG_BIT(2) ? "full" : "half"));
- }
- return up;
- }
- static uint32_t s_rxno;
- #if !defined(MG_ENABLE_DRIVER_MCXE)
- void ENET_IRQHandler(void);
- void ENET_IRQHandler(void) {
- #else
- void ENET_Receive_IRQHandler(void);
- void ENET_Receive_IRQHandler(void) {
- #endif
- ENET->EIR = MG_BIT(25); // Ack IRQ
- // Frame received, loop
- for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
- uint32_t r = s_rxdesc[s_rxno].control;
- if (r & MG_BIT(15)) break; // exit when done
- // skip partial/errored frames (Table 37-32)
- if ((r & MG_BIT(11)) &&
- !(r & (MG_BIT(5) | MG_BIT(4) | MG_BIT(2) | MG_BIT(1) | MG_BIT(0)))) {
- size_t len = s_rxdesc[s_rxno].length;
- mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
- }
- s_rxdesc[s_rxno].control |= MG_BIT(15);
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
- }
- ENET->RDAR = MG_BIT(24); // Receive Descriptors have changed
- // If b24 == 0, descriptors were exhausted and probably frames were dropped
- }
- struct mg_tcpip_driver mg_tcpip_driver_imxrt = {mg_tcpip_driver_imxrt_init,
- mg_tcpip_driver_imxrt_tx, NULL,
- mg_tcpip_driver_imxrt_poll};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/nxp_wifi.c"
- #endif
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_NXP_WIFI) && \
- MG_ENABLE_DRIVER_NXP_WIFI
- bool __attribute__((weak)) netif_init(struct mg_tcpip_if *ifp) {
- (void) ifp;
- MG_ERROR(("Please link wifi/port/net contents"));
- return false;
- }
- size_t __attribute__((weak))
- netif_tx(const void *bfr, size_t len, struct mg_tcpip_if *ifp) {
- (void) bfr;
- (void) len;
- netif_init(ifp);
- return 0;
- }
- bool __attribute__((weak)) netif_connect(struct mg_wifi_data *wifi) {
- (void) wifi;
- return netif_init(NULL);
- }
- bool __attribute__((weak))
- netif_poll(struct mg_tcpip_if *ifp, bool s1, mg_tcpip_event_handler_t evcb) {
- (void) ifp;
- (void) s1;
- (void) evcb;
- return false;
- }
- static struct mg_tcpip_if *s_ifp;
- static uint32_t s_ip, s_mask;
- static void wifi_cb(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
- struct mg_wifi_data *wifi =
- &((struct mg_tcpip_driver_nxp_wifi_data *) ifp->driver_data)->wifi;
- if (wifi->apmode && ev == MG_TCPIP_EV_STATE_CHANGE &&
- *(uint8_t *) ev_data == MG_TCPIP_STATE_UP) {
- MG_DEBUG(("Access Point started"));
- s_ip = ifp->ip, ifp->ip = wifi->apip;
- s_mask = ifp->mask, ifp->mask = wifi->apmask;
- ifp->enable_dhcp_client = false;
- ifp->enable_dhcp_server = true;
- }
- }
- static bool nxp_wifi_init(struct mg_tcpip_if *ifp) {
- struct mg_wifi_data *wifi =
- &((struct mg_tcpip_driver_nxp_wifi_data *) ifp->driver_data)->wifi;
- s_ifp = ifp;
- s_ip = ifp->ip;
- s_mask = ifp->mask;
- ifp->pfn = wifi_cb;
- if (!netif_init(ifp)) return false;
- if (wifi->apmode) {
- return mg_wifi_ap_start(wifi);
- } else if (wifi->ssid != NULL && wifi->pass != NULL) {
- return mg_wifi_connect(wifi);
- }
- return true;
- }
- bool nxp_wifi_poll(struct mg_tcpip_if *ifp, bool s1) {
- return netif_poll(ifp, s1, mg_tcpip_call);
- }
- struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi = {nxp_wifi_init, netif_tx,
- NULL, nxp_wifi_poll};
- bool mg_wifi_connect(struct mg_wifi_data *wifi) {
- s_ifp->ip = s_ip;
- s_ifp->mask = s_mask;
- if (s_ifp->ip == 0) s_ifp->enable_dhcp_client = true;
- s_ifp->enable_dhcp_server = false;
- return netif_connect(wifi);
- }
- bool __attribute__((weak)) mg_wifi_scan(void) {
- return netif_init(NULL);
- }
- bool __attribute__((weak)) mg_wifi_disconnect(void) {
- return netif_init(NULL);
- }
- bool __attribute__((weak)) mg_wifi_ap_start(struct mg_wifi_data *wifi) {
- (void) wifi;
- return netif_init(NULL);
- }
- bool __attribute__((weak)) mg_wifi_ap_stop(void) {
- return netif_init(NULL);
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/phy.c"
- #endif
- enum { // ID1 ID2
- MG_PHY_KSZ8x = 0x22, // 0022 156x - KSZ8081RNB, KSZ8091RNB
- MG_PHY_DP83x = 0x2000,
- MG_PHY_DP83867 = 0xa231, // 2000 a231 - TI DP83867I
- MG_PHY_DP83825 = 0xa140, // 2000 a140 - TI DP83825I
- MG_PHY_DP83848 = 0x5ca2, // 2000 5ca2 - TI DP83848I
- MG_PHY_LAN87x = 0x7, // 0007 c0fx - LAN8720
- MG_PHY_RTL82x = 0x1c,
- MG_PHY_RTL8201 = 0xc816, // 001c c816 - RTL8201F
- MG_PHY_RTL8211 = 0xc916, // 001c c916 - RTL8211F
- MG_PHY_ICS1894x = 0x15,
- MG_PHY_ICS189432 = 0xf450 // 0015 f450 - ICS1894
- };
- enum {
- MG_PHY_REG_BCR = 0,
- MG_PHY_REG_BSR = 1,
- MG_PHY_REG_ID1 = 2,
- MG_PHY_REG_ID2 = 3,
- MG_PHY_DP83x_REG_PHYSTS = 16,
- MG_PHY_DP83867_REG_PHYSTS = 17,
- MG_PHY_DP83x_REG_RCSR = 23,
- MG_PHY_DP83x_REG_LEDCR = 24,
- MG_PHY_KSZ8x_REG_PC1R = 30,
- MG_PHY_KSZ8x_REG_PC2R = 31,
- MG_PHY_LAN87x_REG_SCSR = 31,
- MG_PHY_RTL82x_REG_PAGESEL = 31,
- MG_PHY_RTL8201_REG_RMSR = 16, // in page 7
- MG_PHY_RTL8211_REG_PHYSR = 26, // in page a43
- MG_PHY_ICS189432_REG_POLL = 17
- };
- static const char *mg_phy_id_to_str(uint16_t id1, uint16_t id2) {
- switch (id1) {
- case MG_PHY_DP83x:
- switch (id2) {
- case MG_PHY_DP83867:
- return "DP83867";
- case MG_PHY_DP83848:
- return "DP83848";
- case MG_PHY_DP83825:
- return "DP83825";
- default:
- return "DP83x";
- }
- case MG_PHY_KSZ8x:
- return "KSZ8x";
- case MG_PHY_LAN87x:
- return "LAN87x";
- case MG_PHY_RTL82x:
- switch (id2) {
- case MG_PHY_RTL8201:
- return "RTL8201";
- case MG_PHY_RTL8211:
- return "RTL8211";
- default:
- return "RTL82x";
- }
- case MG_PHY_ICS1894x:
- return "ICS1894x";
- default:
- return "unknown";
- }
- }
- void mg_phy_init(struct mg_phy *phy, uint8_t phy_addr, uint8_t config) {
- uint16_t id1, id2;
- phy->write_reg(phy_addr, MG_PHY_REG_BCR, MG_BIT(15)); // Reset PHY
- while (phy->read_reg(phy_addr, MG_PHY_REG_BCR) & MG_BIT(15)) (void) 0;
- // MG_PHY_REG_BCR[12]: Autonegotiation is default unless hw says otherwise
- id1 = phy->read_reg(phy_addr, MG_PHY_REG_ID1);
- id2 = phy->read_reg(phy_addr, MG_PHY_REG_ID2);
- MG_INFO(("PHY ID: %#04x %#04x (%s)", id1, id2, mg_phy_id_to_str(id1, id2)));
- if (id1 == MG_PHY_DP83x && id2 == MG_PHY_DP83867) {
- phy->write_reg(phy_addr, 0x0d, 0x1f); // write 0x10d to IO_MUX_CFG (0x0170)
- phy->write_reg(phy_addr, 0x0e, 0x170);
- phy->write_reg(phy_addr, 0x0d, 0x401f);
- phy->write_reg(phy_addr, 0x0e, 0x10d);
- }
- if (config & MG_PHY_CLOCKS_MAC) {
- // Use PHY crystal oscillator (preserve defaults)
- // nothing to do
- } else { // MAC clocks PHY, PHY has no xtal
- // Enable 50 MHz external ref clock at XI (preserve defaults)
- if (id1 == MG_PHY_DP83x && id2 != MG_PHY_DP83867 && id2 != MG_PHY_DP83848) {
- phy->write_reg(phy_addr, MG_PHY_DP83x_REG_RCSR, MG_BIT(7) | MG_BIT(0));
- } else if (id1 == MG_PHY_KSZ8x) {
- // Disable isolation (override hw, it doesn't make sense at this point)
- // - #2848, some NXP boards set ISO, even though docs say they don't
- phy->write_reg(phy_addr, MG_PHY_REG_BCR,
- (uint16_t) (phy->read_reg(phy_addr, MG_PHY_REG_BCR) &
- (uint16_t) ~MG_BIT(10)));
- // now do clock stuff
- phy->write_reg(phy_addr, MG_PHY_KSZ8x_REG_PC2R,
- (uint16_t) (MG_BIT(15) | MG_BIT(8) | MG_BIT(7)));
- } else if (id1 == MG_PHY_LAN87x) {
- // nothing to do
- } else if (id1 == MG_PHY_RTL82x && id2 == MG_PHY_RTL8201) {
- // assume PHY has been hardware strapped properly
- #if 0
- phy->write_reg(phy_addr, MG_PHY_RTL82x_REG_PAGESEL, 7); // Select page 7
- phy->write_reg(phy_addr, MG_PHY_RTL8201_REG_RMSR, 0x1ffa);
- phy->write_reg(phy_addr, MG_PHY_RTL82x_REG_PAGESEL, 0); // Select page 0
- #endif
- } else if (id1 == MG_PHY_RTL82x && id2 == MG_PHY_RTL8211) {
- // assume PHY has been hardware strapped properly
- }
- }
- if (config & MG_PHY_LEDS_ACTIVE_HIGH && id1 == MG_PHY_DP83x) {
- phy->write_reg(phy_addr, MG_PHY_DP83x_REG_LEDCR,
- MG_BIT(9) | MG_BIT(7)); // LED status, active high
- } // Other PHYs do not support this feature
- }
- bool mg_phy_up(struct mg_phy *phy, uint8_t phy_addr, bool *full_duplex,
- uint8_t *speed) {
- bool up = false;
- uint16_t bsr = phy->read_reg(phy_addr, MG_PHY_REG_BSR);
- if ((bsr & MG_BIT(5)) && !(bsr & MG_BIT(2))) // some PHYs latch down events
- bsr = phy->read_reg(phy_addr, MG_PHY_REG_BSR); // read again
- up = bsr & MG_BIT(2);
- if (up && full_duplex != NULL && speed != NULL) {
- uint16_t id1 = phy->read_reg(phy_addr, MG_PHY_REG_ID1);
- if (id1 == MG_PHY_DP83x) {
- uint16_t id2 = phy->read_reg(phy_addr, MG_PHY_REG_ID2);
- if (id2 == MG_PHY_DP83867) {
- uint16_t physts = phy->read_reg(phy_addr, MG_PHY_DP83867_REG_PHYSTS);
- *full_duplex = physts & MG_BIT(13);
- *speed = (physts & MG_BIT(15)) ? MG_PHY_SPEED_1000M
- : (physts & MG_BIT(14)) ? MG_PHY_SPEED_100M
- : MG_PHY_SPEED_10M;
- } else {
- uint16_t physts = phy->read_reg(phy_addr, MG_PHY_DP83x_REG_PHYSTS);
- *full_duplex = physts & MG_BIT(2);
- *speed = (physts & MG_BIT(1)) ? MG_PHY_SPEED_10M : MG_PHY_SPEED_100M;
- }
- } else if (id1 == MG_PHY_KSZ8x) {
- uint16_t pc1r = phy->read_reg(phy_addr, MG_PHY_KSZ8x_REG_PC1R);
- *full_duplex = pc1r & MG_BIT(2);
- *speed = (pc1r & 3) == 1 ? MG_PHY_SPEED_10M : MG_PHY_SPEED_100M;
- } else if (id1 == MG_PHY_LAN87x) {
- uint16_t scsr = phy->read_reg(phy_addr, MG_PHY_LAN87x_REG_SCSR);
- *full_duplex = scsr & MG_BIT(4);
- *speed = (scsr & MG_BIT(3)) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M;
- } else if (id1 == MG_PHY_RTL82x) {
- uint16_t id2 = phy->read_reg(phy_addr, MG_PHY_REG_ID2);
- if (id2 == MG_PHY_RTL8211) {
- uint16_t physr;
- phy->write_reg(phy_addr, MG_PHY_RTL82x_REG_PAGESEL, 0xa43);
- physr = phy->read_reg(phy_addr, MG_PHY_RTL8211_REG_PHYSR);
- phy->write_reg(phy_addr, MG_PHY_RTL82x_REG_PAGESEL, 0);
- *full_duplex = physr & MG_BIT(3);
- *speed = (physr & MG_BIT(5)) ? MG_PHY_SPEED_1000M
- : (physr & MG_BIT(4)) ? MG_PHY_SPEED_100M
- : MG_PHY_SPEED_10M;
- } else {
- uint16_t bcr = phy->read_reg(phy_addr, MG_PHY_REG_BCR);
- *full_duplex = bcr & MG_BIT(8);
- *speed = (bcr & MG_BIT(13)) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M;
- }
- } else if (id1 == MG_PHY_ICS1894x) {
- uint16_t poll_reg = phy->read_reg(phy_addr, MG_PHY_ICS189432_REG_POLL);
- *full_duplex = poll_reg & MG_BIT(14);
- *speed = (poll_reg & MG_BIT(15)) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M;
- }
- }
- return up;
- }
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/pico-w.c"
- #endif
- #if MG_ENABLE_TCPIP && MG_ARCH == MG_ARCH_PICOSDK && \
- defined(MG_ENABLE_DRIVER_PICO_W) && MG_ENABLE_DRIVER_PICO_W
- static struct mg_tcpip_if *s_ifp;
- static uint32_t s_ip, s_mask;
- static bool s_aplink = false, s_scanning = false;
- static bool s_stalink = false, s_connecting = false;
- static void wifi_cb(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
- struct mg_wifi_data *wifi =
- &((struct mg_tcpip_driver_pico_w_data *) ifp->driver_data)->wifi;
- if (wifi->apmode && ev == MG_TCPIP_EV_STATE_CHANGE &&
- *(uint8_t *) ev_data == MG_TCPIP_STATE_UP) {
- MG_DEBUG(("Access Point started"));
- s_ip = ifp->ip, ifp->ip = wifi->apip;
- s_mask = ifp->mask, ifp->mask = wifi->apmask;
- ifp->enable_dhcp_client = false;
- ifp->enable_dhcp_server = true;
- }
- }
- static bool mg_tcpip_driver_pico_w_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_pico_w_data *d =
- (struct mg_tcpip_driver_pico_w_data *) ifp->driver_data;
- struct mg_wifi_data *wifi = &d->wifi;
- s_ifp = ifp;
- s_ip = ifp->ip;
- s_mask = ifp->mask;
- ifp->pfn = wifi_cb;
- if (cyw43_arch_init() != 0)
- return false; // initialize async_context and WiFi chip
- if (wifi->apmode && wifi->apssid != NULL) {
- if (!mg_wifi_ap_start(wifi)) return false;
- cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, ifp->mac); // same MAC
- } else {
- cyw43_arch_enable_sta_mode();
- cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, ifp->mac);
- if (wifi->ssid != NULL) {
- return mg_wifi_connect(wifi);
- } else {
- cyw43_arch_disable_sta_mode();
- }
- }
- return true;
- }
- static size_t mg_tcpip_driver_pico_w_tx(const void *buf, size_t len,
- struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_pico_w_data *d =
- (struct mg_tcpip_driver_pico_w_data *) ifp->driver_data;
- return cyw43_send_ethernet(&cyw43_state,
- d->wifi.apmode ? CYW43_ITF_AP : CYW43_ITF_STA, len,
- buf, false) == 0
- ? len
- : 0;
- }
- static bool mg_tcpip_driver_pico_w_poll(struct mg_tcpip_if *ifp, bool s1) {
- cyw43_arch_poll(); // not necessary, except when IRQs are disabled (OTA)
- if (s_scanning && !cyw43_wifi_scan_active(&cyw43_state)) {
- MG_VERBOSE(("scan complete"));
- s_scanning = 0;
- mg_tcpip_call(ifp, MG_TCPIP_EV_WIFI_SCAN_END, NULL);
- }
- if (ifp->update_mac_hash_table) {
- // first call to _poll() is after _init(), so this is safe
- cyw43_wifi_update_multicast_filter(&cyw43_state, (uint8_t *) mcast_addr,
- true);
- ifp->update_mac_hash_table = false;
- }
- if (!s1) return false;
- struct mg_tcpip_driver_pico_w_data *d =
- (struct mg_tcpip_driver_pico_w_data *) ifp->driver_data;
- if (d->wifi.apmode) return s_aplink;
- int sdkstate = cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA);
- MG_VERBOSE(("conn: %c state: %d", s_connecting ? '1' : '0', sdkstate));
- if (sdkstate < 0 && s_connecting) {
- mg_tcpip_call(ifp, MG_TCPIP_EV_WIFI_CONNECT_ERR, &sdkstate);
- s_connecting = false;
- }
- return s_stalink;
- }
- struct mg_tcpip_driver mg_tcpip_driver_pico_w = {
- mg_tcpip_driver_pico_w_init,
- mg_tcpip_driver_pico_w_tx,
- NULL,
- mg_tcpip_driver_pico_w_poll,
- };
- // Called once per outstanding frame by async_context
- void cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len,
- const uint8_t *buf) {
- mg_tcpip_qwrite((void *) buf, len, s_ifp);
- (void) cb_data;
- }
- // Called by async_context
- void cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) {
- if (itf == CYW43_ITF_AP) {
- s_aplink = true;
- } else {
- s_stalink = true;
- s_connecting = false;
- }
- }
- void cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) {
- if (itf == CYW43_ITF_AP) {
- s_aplink = false;
- } else {
- s_stalink = false;
- // SDK calls this before we check status, don't clear s_connecting here
- }
- }
- // there's life beyond lwIP
- void pbuf_copy_partial(void) {
- (void) 0;
- }
- static int result_cb(void *arg, const cyw43_ev_scan_result_t *data) {
- struct mg_wifi_scan_bss_data bss;
- bss.SSID = mg_str_n(data->ssid, data->ssid_len);
- bss.BSSID = (char *) data->bssid;
- bss.RSSI = (int8_t) data->rssi;
- bss.has_n = 0; // SDK ignores this
- bss.channel = (uint8_t) data->channel;
- bss.band = MG_WIFI_BAND_2G;
- // SDK-internal dependency, 2.1.0
- bss.security = data->auth_mode & MG_BIT(0) ? MG_WIFI_SECURITY_WEP
- : MG_WIFI_SECURITY_OPEN;
- if (data->auth_mode & MG_BIT(1)) bss.security |= MG_WIFI_SECURITY_WPA;
- if (data->auth_mode & MG_BIT(2)) bss.security |= MG_WIFI_SECURITY_WPA2;
- MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf,
- bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI,
- bss.security));
- mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_RESULT, &bss);
- return 0;
- }
- bool mg_wifi_scan(void) {
- cyw43_wifi_scan_options_t opts;
- memset(&opts, 0, sizeof(opts));
- bool res = (cyw43_wifi_scan(&cyw43_state, &opts, NULL, result_cb) == 0);
- if (res) s_scanning = true;
- return res;
- }
- bool mg_wifi_connect(struct mg_wifi_data *wifi) {
- s_ifp->ip = s_ip;
- s_ifp->mask = s_mask;
- if (s_ifp->ip == 0) s_ifp->enable_dhcp_client = true;
- s_ifp->enable_dhcp_server = false;
- cyw43_arch_enable_sta_mode();
- MG_DEBUG(("Connecting to '%s'", wifi->ssid));
- int res = cyw43_arch_wifi_connect_async(wifi->ssid, wifi->pass,
- CYW43_AUTH_WPA2_AES_PSK);
- MG_VERBOSE(("res: %d", res));
- if (res == 0) s_connecting = true;
- return (res == 0);
- }
- bool mg_wifi_disconnect(void) {
- cyw43_arch_disable_sta_mode();
- s_connecting = false;
- return true;
- }
- bool mg_wifi_ap_start(struct mg_wifi_data *wifi) {
- MG_DEBUG(("Starting AP '%s' (%u)", wifi->apssid, wifi->apchannel));
- cyw43_wifi_ap_set_channel(&cyw43_state, wifi->apchannel);
- cyw43_arch_enable_ap_mode(wifi->apssid, wifi->appass,
- CYW43_AUTH_WPA2_AES_PSK);
- return true;
- }
- bool mg_wifi_ap_stop(void) {
- cyw43_arch_disable_ap_mode();
- return true;
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/ra.c"
- #endif
- #if MG_ENABLE_TCPIP && \
- (defined(MG_ENABLE_DRIVER_RA6) && MG_ENABLE_DRIVER_RA6) || \
- (defined(MG_ENABLE_DRIVER_RA8) && MG_ENABLE_DRIVER_RA8)
- struct ra_etherc {
- volatile uint32_t ECMR, RESERVED, RFLR, RESERVED1, ECSR, RESERVED2, ECSIPR,
- RESERVED3, PIR, RESERVED4, PSR, RESERVED5[5], RDMLR, RESERVED6[3], IPGR,
- APR, MPR, RESERVED7, RFCF, TPAUSER, TPAUSECR, BCFRR, RESERVED8[20], MAHR,
- RESERVED9, MALR, RESERVED10, TROCR, CDCR, LCCR, CNDCR, RESERVED11, CEFCR,
- FRECR, TSFRCR, TLFRCR, RFCR, MAFCR;
- };
- struct ra_edmac {
- volatile uint32_t EDMR, RESERVED, EDTRR, RESERVED1, EDRRR, RESERVED2, TDLAR,
- RESERVED3, RDLAR, RESERVED4, EESR, RESERVED5, EESIPR, RESERVED6, TRSCER,
- RESERVED7, RMFCR, RESERVED8, TFTR, RESERVED9, FDR, RESERVED10, RMCR,
- RESERVED11[2], TFUCR, RFOCR, IOSR, FCFTR, RESERVED12, RPADIR, TRIMD,
- RESERVED13[18], RBWAR, RDFAR, RESERVED14, TBRAR, TDFAR;
- };
- #undef ETHERC
- #undef EDMAC
- #undef RASYSC
- #undef ICU_IELSR
- #if defined(MG_ENABLE_DRIVER_RA8) && MG_ENABLE_DRIVER_RA8
- #define ETHERC ((struct ra_etherc *) (uintptr_t) 0x40354100U)
- #define EDMAC ((struct ra_edmac *) (uintptr_t) 0x40354000U)
- #define RASYSC ((uint32_t *) (uintptr_t) 0x4001E000U)
- #define ICU_IELSR ((uint32_t *) (uintptr_t) 0x4000C300U)
- #else
- #define ETHERC ((struct ra_etherc *) (uintptr_t) 0x40114100U)
- #define EDMAC ((struct ra_edmac *) (uintptr_t) 0x40114000U)
- #define RASYSC ((uint32_t *) (uintptr_t) 0x4001E000U)
- #define ICU_IELSR ((uint32_t *) (uintptr_t) 0x40006300U)
- #endif
- #define ETH_PKT_SIZE 1536 // Max frame size, multiple of 32
- #define ETH_DESC_CNT 4 // Descriptors count
- // Descriptors: 16-byte aligned
- // Buffers: 32-byte aligned (27.3.1)
- static volatile uint32_t s_rxdesc[ETH_DESC_CNT][4] MG_ETH_RAM MG_16BYTE_ALIGNED;
- static volatile uint32_t s_txdesc[ETH_DESC_CNT][4] MG_ETH_RAM MG_16BYTE_ALIGNED;
- static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_32BYTE_ALIGNED;
- static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_32BYTE_ALIGNED;
- static struct mg_tcpip_if *s_ifp; // MIP interface
- // fastest is 3 cycles (SUB + BNE) on a 3-stage pipeline or equivalent
- static inline void raspin(volatile uint32_t count) {
- while (count--) (void) 0;
- }
- // count to get the 200ns SMC semi-cycle period (2.5MHz) calling raspin():
- // SYS_FREQUENCY * 200ns / 3 = SYS_FREQUENCY / 15000000
- static uint32_t s_smispin;
- // Bit-banged SMI
- static void smi_preamble(void) {
- unsigned int i = 32;
- uint32_t pir = MG_BIT(1) | MG_BIT(2); // write, mdio = 1, mdc = 0
- ETHERC->PIR = pir;
- while (i--) {
- pir &= ~MG_BIT(0); // mdc = 0
- ETHERC->PIR = pir;
- raspin(s_smispin);
- pir |= MG_BIT(0); // mdc = 1
- ETHERC->PIR = pir;
- raspin(s_smispin);
- }
- }
- static void smi_wr(uint16_t header, uint16_t data) {
- uint32_t word = (header << 16) | data;
- smi_preamble();
- unsigned int i = 32;
- while (i--) {
- uint32_t pir = MG_BIT(1) |
- (word & 0x80000000 ? MG_BIT(2) : 0); // write, mdc = 0, data
- ETHERC->PIR = pir;
- raspin(s_smispin);
- pir |= MG_BIT(0); // mdc = 1
- ETHERC->PIR = pir;
- raspin(s_smispin);
- word <<= 1;
- }
- }
- static uint16_t smi_rd(uint16_t header) {
- smi_preamble();
- unsigned int i = 16; // 2 LSb as turnaround
- uint32_t pir;
- while (i--) {
- pir = (i > 1 ? MG_BIT(1) : 0) |
- (header & 0x8000
- ? MG_BIT(2)
- : 0); // mdc = 0, header, set read direction at turnaround
- ETHERC->PIR = pir;
- raspin(s_smispin);
- pir |= MG_BIT(0); // mdc = 1
- ETHERC->PIR = pir;
- raspin(s_smispin);
- header <<= 1;
- }
- i = 16;
- uint16_t data = 0;
- while (i--) {
- data <<= 1;
- pir = 0; // read, mdc = 0
- ETHERC->PIR = pir;
- raspin(s_smispin / 2); // 1/4 clock period, 300ns max access time
- data |= (uint16_t) (ETHERC->PIR & MG_BIT(3) ? 1 : 0); // read mdio
- raspin(s_smispin / 2); // 1/4 clock period
- pir |= MG_BIT(0); // mdc = 1
- ETHERC->PIR = pir;
- raspin(s_smispin);
- }
- return data;
- }
- static uint16_t raeth_read_phy(uint8_t addr, uint8_t reg) {
- return smi_rd(
- (uint16_t) ((1 << 14) | (2 << 12) | (addr << 7) | (reg << 2) | (2 << 0)));
- }
- static void raeth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
- smi_wr(
- (uint16_t) ((1 << 14) | (1 << 12) | (addr << 7) | (reg << 2) | (2 << 0)),
- val);
- }
- // MDC clock is generated manually; as per 802.3, it must not exceed 2.5MHz
- static bool mg_tcpip_driver_ra_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_ra_data *d =
- (struct mg_tcpip_driver_ra_data *) ifp->driver_data;
- s_ifp = ifp;
- // Init SMI clock timing. If user told us the clock value, use it.
- // TODO(): Otherwise, guess
- s_smispin = d->clock / 15000000;
- // Init RX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_rxdesc[i][0] = MG_BIT(31); // RACT
- s_rxdesc[i][1] = ETH_PKT_SIZE << 16; // RBL
- s_rxdesc[i][2] = (uint32_t) s_rxbuf[i]; // Point to data buffer
- }
- s_rxdesc[ETH_DESC_CNT - 1][0] |= MG_BIT(30); // Wrap last descriptor
- // Init TX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- // TACT = 0
- s_txdesc[i][2] = (uint32_t) s_txbuf[i];
- }
- s_txdesc[ETH_DESC_CNT - 1][0] |= MG_BIT(30); // Wrap last descriptor
- EDMAC->EDMR = MG_BIT(0); // Software reset, wait 64 PCLKA clocks (27.2.1)
- uint32_t sckdivcr = RASYSC[8]; // get divisors from SCKDIVCR (8.2.2)
- uint32_t ick = 1 << ((sckdivcr >> 24) & 7); // sys_clock div
- uint32_t pcka = 1 << ((sckdivcr >> 12) & 7); // pclka div
- raspin((64U * pcka) / (3U * ick));
- EDMAC->EDMR = MG_BIT(6); // Initialize, little-endian (27.2.1)
- MG_DEBUG(("PHY addr: %d, smispin: %d", d->phy_addr, s_smispin));
- struct mg_phy phy = {raeth_read_phy, raeth_write_phy};
- mg_phy_init(&phy, d->phy_addr, MG_PHY_CLOCKS_MAC);
- // Select RMII mode,
- ETHERC->ECMR = MG_BIT(2) | MG_BIT(1); // 100M, Full-duplex, CRC
- // ETHERC->ECMR |= MG_BIT(0); // Receive all
- ETHERC->RFLR = 1518; // Set max rx length
- EDMAC->RDLAR = (uint32_t) (uintptr_t) s_rxdesc;
- EDMAC->TDLAR = (uint32_t) (uintptr_t) s_txdesc;
- // MAC address filtering (bytes in reversed order)
- ETHERC->MAHR = (uint32_t) (ifp->mac[0] << 24U) |
- ((uint32_t) ifp->mac[1] << 16U) |
- ((uint32_t) ifp->mac[2] << 8U) | ifp->mac[3];
- ETHERC->MALR = ((uint32_t) ifp->mac[4] << 8U) | ifp->mac[5];
- EDMAC->TFTR = 0; // Store and forward (27.2.10)
- EDMAC->FDR = 0x070f; // (27.2.11)
- EDMAC->RMCR = MG_BIT(0); // (27.2.12)
- ETHERC->ECMR |= MG_BIT(6) | MG_BIT(5); // TE RE
- EDMAC->EESIPR = MG_BIT(18); // FR: Enable Rx (frame) IRQ
- EDMAC->EDRRR = MG_BIT(0); // Receive Descriptors have changed
- EDMAC->EDTRR = MG_BIT(0); // Transmit Descriptors have changed
- return true;
- }
- // Transmit frame
- static size_t mg_tcpip_driver_ra_tx(const void *buf, size_t len,
- struct mg_tcpip_if *ifp) {
- static int s_txno; // Current descriptor index
- if (len > sizeof(s_txbuf[ETH_DESC_CNT])) {
- MG_ERROR(("Frame too big, %ld", (long) len));
- len = (size_t) -1; // fail
- } else if ((s_txdesc[s_txno][0] & MG_BIT(31))) {
- ifp->nerr++;
- MG_ERROR(("No descriptors available"));
- len = 0; // retry later
- } else {
- memcpy(s_txbuf[s_txno], buf, len); // Copy data
- s_txdesc[s_txno][1] = len << 16; // Set data len
- s_txdesc[s_txno][0] |= MG_BIT(31) | 3 << 28; // (27.3.1.1) mark valid
- EDMAC->EDTRR = MG_BIT(0); // Transmit request
- if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
- }
- return len;
- }
- static bool mg_tcpip_driver_ra_poll(struct mg_tcpip_if *ifp, bool s1) {
- if (ifp->update_mac_hash_table) {
- EDMAC->EESIPR = MG_BIT(18) | MG_BIT(7); // FR, RMAF: Frame and mcast IRQ
- ifp->update_mac_hash_table = false;
- }
- if (!s1) return false;
- struct mg_tcpip_driver_ra_data *d =
- (struct mg_tcpip_driver_ra_data *) ifp->driver_data;
- uint8_t speed = MG_PHY_SPEED_10M;
- bool up = false, full_duplex = false;
- struct mg_phy phy = {raeth_read_phy, raeth_write_phy};
- up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed);
- if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
- // tmp = reg with flags set to the most likely situation: 100M full-duplex
- // if(link is slow or half) set flags otherwise
- // reg = tmp
- uint32_t ecmr = ETHERC->ECMR | MG_BIT(2) | MG_BIT(1); // 100M Full-duplex
- if (speed == MG_PHY_SPEED_10M) ecmr &= ~MG_BIT(2); // 10M
- if (full_duplex == false) ecmr &= ~MG_BIT(1); // Half-duplex
- ETHERC->ECMR = ecmr; // IRQ handler does not fiddle with these registers
- MG_DEBUG(("Link is %uM %s-duplex", ecmr & MG_BIT(2) ? 100 : 10,
- ecmr & MG_BIT(1) ? "full" : "half"));
- }
- return up;
- }
- void EDMAC_IRQHandler(void);
- static uint32_t s_rxno;
- void EDMAC_IRQHandler(void) {
- struct mg_tcpip_driver_ra_data *d =
- (struct mg_tcpip_driver_ra_data *) s_ifp->driver_data;
- EDMAC->EESR = MG_BIT(18) | MG_BIT(7); // Ack IRQ in EDMAC 1st
- ICU_IELSR[d->irqno] &= ~MG_BIT(16); // Ack IRQ in ICU last
- // Frame received, loop
- for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
- uint32_t r = s_rxdesc[s_rxno][0];
- if (r & MG_BIT(31)) break; // exit when done
- // skip partial/errored frames (27.3.1.2)
- if ((r & (MG_BIT(29) | MG_BIT(28)) && !(r & MG_BIT(27)))) {
- size_t len = s_rxdesc[s_rxno][1] & 0xffff;
- mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp); // CRC already stripped
- }
- s_rxdesc[s_rxno][0] |= MG_BIT(31);
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
- }
- EDMAC->EDRRR = MG_BIT(0); // Receive Descriptors have changed
- // If b0 == 0, descriptors were exhausted and probably frames were dropped,
- // (27.2.9 RMFCR counts them)
- }
- struct mg_tcpip_driver mg_tcpip_driver_ra = {mg_tcpip_driver_ra_init,
- mg_tcpip_driver_ra_tx, NULL,
- mg_tcpip_driver_ra_poll};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/rw612.c"
- #endif
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612) && MG_ENABLE_DRIVER_RW612
- struct ENET_Type {
- volatile uint32_t RESERVED_0[1], EIR, EIMR, RESERVED_1[1], RDAR, TDAR,
- RESERVED_2[3], ECR, RESERVED_3[6], MMFR, MSCR, RESERVED_4[7], MIBC,
- RESERVED_5[7], RCR, RESERVED_6[15], TCR, RESERVED_7[7], PALR, PAUR, OPD,
- TXIC[1], RESERVED_8[3], RXIC[1], RESERVED_9[5], IAUR, IALR, GAUR, GALR,
- RESERVED_10[7], TFWR, RESERVED_11[14], RDSR, TDSR, MRBR, RESERVED_12[1],
- RSFL, RSEM, RAEM, RAFL, TSEM, TAEM, TAFL, TIPG, FTRL, RESERVED_13[3],
- TACC, RACC, RESERVED_14[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_P1024TO2047, RMON_T_P_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, RESERVED_15[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,
- RESERVED_16[1], RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255,
- RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, RMON_R_P_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, RESERVED_17[71], ATCR,
- ATVR, ATOFF, ATPER, ATCOR, ATINC, ATSTMP, RESERVED_18[122], TGSR,
- CHANNEL_TCSR[4], CHANNEL_TCCR[4];
- };
- #undef ENET
- #define ENET ((struct ENET_Type *) 0x40138000)
- #define ETH_PKT_SIZE 1536 // Max frame size
- #define ETH_DESC_CNT 4 // Descriptors count
- #define ETH_DS 2 // Descriptor size (words)
- static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_8BYTE_ALIGNED;
- static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM MG_8BYTE_ALIGNED;
- static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM MG_8BYTE_ALIGNED;
- static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM MG_8BYTE_ALIGNED;
- 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
- static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) {
- ENET->MMFR = MG_BIT(30) | // Start of frame delimiter
- MG_BIT(29) | // Opcode
- ((addr & 0x1f) << 23) | ((reg & 0x1f) << 18) | MG_BIT(17);
- while ((ENET->EIR & MG_BIT(23)) == 0) (void) 0;
- ENET->EIR |= MG_BIT(23);
- return ENET->MMFR & 0xffff;
- }
- static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
- ENET->MMFR = MG_BIT(30) | // Start of frame delimiter
- MG_BIT(28) | // Opcode
- ((addr & 0x1f) << 23) | ((reg & 0x1f) << 18) | MG_BIT(17) | val;
- while ((ENET->EIR & MG_BIT(23)) == 0) (void) 0;
- ENET->EIR |= MG_BIT(23);
- }
- static bool mg_tcpip_driver_rw612_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_rw612_data *d =
- (struct mg_tcpip_driver_rw612_data *) ifp->driver_data;
- s_ifp = ifp;
- ENET->MSCR = ((d->mdc_cr & 0x3f) << 1) | ((d->mdc_holdtime & 7) << 8);
- struct mg_phy phy = {eth_read_phy, eth_write_phy};
- mg_phy_init(&phy, d->phy_addr, 0);
- ENET->ECR |= MG_BIT(0); // reset ETH
- // initialize descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_rxdesc[i][1] = (uint32_t) s_rxbuf[i];
- s_rxdesc[i][0] = MG_BIT(31); // OWN
- if (i == ETH_DESC_CNT - 1) {
- s_rxdesc[i][0] |= MG_BIT(29); // mark last descriptor
- }
- s_txdesc[i][1] = (uint32_t) s_txbuf[i];
- if (i == ETH_DESC_CNT - 1) {
- s_txdesc[i][0] |= MG_BIT(29); // mark last descriptor
- }
- }
- ENET->RCR = (ENET->RCR & (0xffff << 16)) | MG_BIT(14) | MG_BIT(8) | MG_BIT(2);
- ENET->TCR = MG_BIT(2); // full duplex
- ENET->TDSR = (uint32_t) &s_txdesc[0][0];
- ENET->RDSR = (uint32_t) &s_rxdesc[0][0];
- ENET->MRBR = ETH_PKT_SIZE;
- ENET->PALR =
- ifp->mac[0] << 24 | ifp->mac[1] << 16 | ifp->mac[2] << 8 | ifp->mac[3];
- ENET->PAUR |= (ifp->mac[4] << 24 | ifp->mac[5] << 16);
- ENET->IALR = 0;
- ENET->IAUR = 0;
- ENET->GALR = 0;
- ENET->GAUR = 0;
- ENET->MSCR = ((d->mdc_cr & 0x3f) << 1) | ((d->mdc_holdtime & 7) << 8);
- ENET->EIMR = MG_BIT(25); // Enable RX interrupt
- ENET->ECR |= MG_BIT(8) | MG_BIT(1); // DBSWP, Enable
- ENET->RDAR = 0; // activate RX descriptors ring
- return true;
- }
- static size_t mg_tcpip_driver_rw612_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] & MG_BIT(31)) != 0)) {
- ifp->nerr++;
- MG_ERROR(("No free descriptors"));
- len = 0; // All descriptors are busy, fail
- } else {
- memcpy(s_txbuf[s_txno], buf, len);
- s_txdesc[s_txno][0] = len | MG_BIT(27) | MG_BIT(26); // last buffer, crc
- if (s_txno == ETH_DESC_CNT - 1) {
- s_txdesc[s_txno][0] |= MG_BIT(29); // wrap
- }
- s_txdesc[s_txno][0] |= MG_BIT(31); // release ownership
- MG_DSB();
- ENET->TDAR = 0;
- // MG_INFO(("s_txdesc[%d][0]: 0x%x", s_txno, s_txdesc[s_txno][0]));
- if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
- }
- return len;
- }
- static void mg_tcpip_driver_rw612_update_hash_table(struct mg_tcpip_if *ifp) {
- // TODO(): read database, rebuild hash table
- ENET->GAUR = MG_BIT(1); // see imxrt, it reduces to this for mDNS
- (void) ifp;
- }
- static bool mg_tcpip_driver_rw612_poll(struct mg_tcpip_if *ifp, bool s1) {
- if (ifp->update_mac_hash_table) {
- mg_tcpip_driver_rw612_update_hash_table(ifp);
- ifp->update_mac_hash_table = false;
- }
- if (!s1) return false;
- struct mg_tcpip_driver_rw612_data *d =
- (struct mg_tcpip_driver_rw612_data *) ifp->driver_data;
- uint8_t speed = MG_PHY_SPEED_10M;
- bool up = false, full_duplex = false;
- struct mg_phy phy = {eth_read_phy, eth_write_phy};
- up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed);
- if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
- // tmp = reg with flags set to the most likely situation: 100M full-duplex
- // if(link is slow or half) set flags otherwise
- // reg = tmp
- if (speed == MG_PHY_SPEED_100M && (ENET->RCR & MG_BIT(9))) {
- ENET->RCR &= ~MG_BIT(9);
- } else if (speed == MG_PHY_SPEED_10M && (ENET->RCR & MG_BIT(9)) == 0) {
- ENET->RCR |= MG_BIT(9);
- }
- if (full_duplex && (ENET->TCR & MG_BIT(2)) == 0) {
- ENET->ECR &= ~MG_BIT(1);
- ENET->TCR |= MG_BIT(2);
- ENET->ECR |= MG_BIT(1);
- } else if (!full_duplex && (ENET->TCR & MG_BIT(2))) {
- ENET->ECR &= ~MG_BIT(1);
- ENET->TCR &= ~MG_BIT(2);
- ENET->ECR |= MG_BIT(1);
- }
- MG_INFO(("Link is %uM %s-duplex",
- speed == MG_PHY_SPEED_10M
- ? 10
- : (speed == MG_PHY_SPEED_100M ? 100 : 1000),
- full_duplex ? "full" : "half"));
- }
- return up;
- }
- void ENET_IRQHandler(void) {
- if (ENET->EIR & MG_BIT(25)) {
- ENET->EIR = MG_BIT(25); // Ack RX
- for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
- if ((s_rxdesc[s_rxno][0] & MG_BIT(31)) != 0) break; // exit when done
- // skip partial/errored frames
- if ((s_rxdesc[s_rxno][0] & MG_BIT(27)) &&
- !(s_rxdesc[s_rxno][0] &
- (MG_BIT(21) | MG_BIT(20) | MG_BIT(18) | MG_BIT(17) | MG_BIT(16)))) {
- size_t len = s_rxdesc[s_rxno][0] & 0xffff;
- mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp);
- }
- s_rxdesc[s_rxno][0] |= MG_BIT(31); // OWN bit: handle control to DMA
- MG_DSB();
- ENET->RDAR = 0;
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
- }
- }
- }
- struct mg_tcpip_driver mg_tcpip_driver_rw612 = {mg_tcpip_driver_rw612_init,
- mg_tcpip_driver_rw612_tx, NULL,
- mg_tcpip_driver_rw612_poll};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/same54.c"
- #endif
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_SAME54) && \
- MG_ENABLE_DRIVER_SAME54
- struct GMAC_REGS_ {
- uint32_t GMAC_NCR, GMAC_NCFGR, GMAC_NSR, GMAC_UR, GMAC_DCFGR, GMAC_TSR,
- GMAC_RBQB, GMAC_TBQB, GMAC_RSR, GMAC_ISR, GMAC_IER, GMAC_IDR, GMAC_IMR,
- GMAC_MAN, GMAC_RPQ, GMAC_TPQ, GMAC_TPSF, GMAC_RPSF, GMAC_RJFML,
- Reserved1[13], GMAC_HRB, GMAC_HRT, GMAC_SAB0, GMAC_SAT0, GMAC_SAB1,
- GMAC_SAT1, GMAC_SAB2, GMAC_SAT2, GMAC_SAB3, GMAC_SAT3, GMAC_TIDM[4],
- GMAC_WOL, GMAC_IPGS, GMAC_SVLAN, GMAC_TPFCP, GMAC_SAMB1, GMAC_SAMT1,
- Reserved2[3], GMAC_NSC, GMAC_SCL, GMAC_SCH, GMAC_EFTSH, GMAC_EFRSH,
- GMAC_PEFTSH, GMAC_PEFRSH, Reserved3[2], GMAC_OTLO, GMAC_OTHI, GMAC_FT,
- GMAC_BCFT, GMAC_MFT, GMAC_PFT, GMAC_BFT64, GMAC_TBFT127, GMAC_TBFT255,
- GMAC_TBFT511, GMAC_TBFT1023, GMAC_TBFT1518, GMAC_GTBFT1518, GMAC_TUR,
- GMAC_SCF, GMAC_MCF, GMAC_EC, GMAC_LC, GMAC_DTF, GMAC_CSE, GMAC_ORLO,
- GMAC_ORHI, GMAC_FR, GMAC_BCFR, GMAC_MFR, GMAC_PFR, GMAC_BFR64,
- GMAC_TBFR127, GMAC_TBFR255, GMAC_TBFR511, GMAC_TBFR1023, GMAC_TBFR1518,
- GMAC_TMXBFR, GMAC_UFR, GMAC_OFR, GMAC_JR, GMAC_FCSE, GMAC_LFFE, GMAC_RSE,
- GMAC_AE, GMAC_RRE, GMAC_ROE, GMAC_IHCE, GMAC_TCE, GMAC_UCE, Reserved4[2],
- GMAC_TISUBN, GMAC_TSH, Reserved5, GMAC_TSSSL, GMAC_TSSN, GMAC_TSL,
- GMAC_TN, GMAC_TA, GMAC_TI, GMAC_EFTSL, GMAC_EFTN, GMAC_EFRSL, GMAC_EFRN,
- GMAC_PEFTSL, GMAC_PEFTN, GMAC_PEFRSL, GMAC_PEFRN, Reserved6[28],
- GMAC_RLPITR, GMAC_RLPITI, GMAC_TLPITR, GMAC_TLPITI;
- };
- struct GCLK_REGS_ {
- uint32_t GCLK_CTRLA_RESERVED, GCLK_SYNCBUSY, Reserved2[6], GCLK_GENCTRL[12],
- Reserved3[12], GCLK_PCHCTRL[48];
- };
- #define GMAC_REGS ((struct GMAC_REGS_ *) 0x42000800)
- #define GCLK_REGS ((struct GCLK_REGS_ *) 0x40001c00)
- #define ETH_PKT_SIZE 1536 // Max frame size
- #define ETH_DESC_CNT 4 // Descriptors count
- #define ETH_DS 2 // Descriptor size (words)
- static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE];
- static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE];
- 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_txno; // Current TX descriptor
- static uint8_t s_rxno; // Current RX descriptor
- static struct mg_tcpip_if *s_ifp; // MIP interface
- static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) {
- GMAC_REGS->GMAC_MAN = MG_BIT(30) | // Clause 22
- MG_BIT(29) | // Setting the read operation
- MG_BIT(17) | ((addr & 0x1f) << 23) | // PHY address
- ((reg & 0x1f) << 18); // Setting the register
- while (!(GMAC_REGS->GMAC_NSR & MG_BIT(2))) (void) 0;
- return GMAC_REGS->GMAC_MAN & 0xffff; // Getting the read value
- }
- static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
- GMAC_REGS->GMAC_MAN = MG_BIT(30) |
- MG_BIT(28) | // Setting the write operation
- MG_BIT(17) | ((addr & 0x1f) << 23) | // PHY address
- ((reg & 0x1f) << 18) | val; // Setting the register
- while (!(GMAC_REGS->GMAC_NSR & MG_BIT(2)))
- ; // Waiting until the write op is complete
- }
- uint32_t get_clock_rate(struct mg_tcpip_driver_same54_data *d) {
- if (d && d->mdc_cr >= 0 && d->mdc_cr <= 5) {
- return d->mdc_cr;
- } else {
- // get MCLK from GCLK_GENERATOR 0
- uint32_t div = 512;
- uint32_t mclk;
- if (!(GCLK_REGS->GCLK_GENCTRL[0] & MG_BIT(12))) {
- div = ((GCLK_REGS->GCLK_GENCTRL[0] & 0x00FF0000) >> 16);
- if (div == 0) div = 1;
- }
- switch (GCLK_REGS->GCLK_GENCTRL[0] & 0xF) {
- case 0: // GCLK_GENCTRL_SRC_XOSC0_Val
- mclk = 32000000UL; // 32MHz
- break;
- case 1: // GCLK_GENCTRL_SRC_XOSC1_Val
- mclk = 32000000UL; // 32MHz
- break;
- case 4: // GCLK_GENCTRL_SRC_OSCULP32K_Val
- mclk = 32000UL; // 32Khz
- break;
- case 5: // GCLK_GENCTRL_SRC_XOSC32K_Val
- mclk = 32000UL; // 32Khz
- break;
- case 6: // GCLK_GENCTRL_SRC_DFLL_Val
- mclk = 48000000UL; // 48MHz
- break;
- case 7: // GCLK_GENCTRL_SRC_DPLL0_Val:
- mclk = 200000000UL; // 200MHz
- break;
- case 8: // GCLK_GENCTRL_SRC_DPLL1_Val
- mclk = 200000000UL; // 200MHz
- break;
- default:
- mclk = 200000000UL; // 200MHz
- }
- mclk /= div;
- uint8_t crs[] = {0, 1, 2, 3, 4, 5}; // GMAC->NCFGR::CLK values
- uint8_t dividers[] = {8, 16, 32, 48, 64, 96}; // Respective CLK dividers
- for (int i = 0; i < 6; i++) {
- if (mclk / dividers[i] <= 2375000UL) { // 2.5MHz - 5%
- return crs[i];
- }
- }
- return 5;
- }
- }
- static bool mg_tcpip_driver_same54_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_same54_data *d =
- (struct mg_tcpip_driver_same54_data *) ifp->driver_data;
- s_ifp = ifp;
- GMAC_REGS->GMAC_NCFGR = get_clock_rate(d) << 18; // Set MDC divider
- GMAC_REGS->GMAC_NCR = 0; // Disable RX & TX
- GMAC_REGS->GMAC_NCR |= MG_BIT(4); // Enable MDC & MDIO
- struct mg_phy phy = {eth_read_phy, eth_write_phy};
- mg_phy_init(&phy, d->phy_addr, 0);
- for (int i = 0; i < ETH_DESC_CNT; i++) { // Init TX descriptors
- s_txdesc[i][0] = (uint32_t) s_txbuf[i]; // Point to data buffer
- s_txdesc[i][1] = MG_BIT(31); // OWN bit
- }
- s_txdesc[ETH_DESC_CNT - 1][1] |= MG_BIT(30); // Last tx descriptor - wrap
- GMAC_REGS->GMAC_DCFGR = (0x18 << 16) | // DMA recv buf 1536
- (3 << 8) | // RXBMS
- MG_BIT(10); // See #2487
- for (int i = 0; i < ETH_DESC_CNT; i++) { // Init RX descriptors
- s_rxdesc[i][0] = (uint32_t) s_rxbuf[i]; // Address of the data buffer
- s_rxdesc[i][1] = 0; // Clear status
- }
- s_rxdesc[ETH_DESC_CNT - 1][0] |= MG_BIT(1); // Last rx descriptor - wrap
- GMAC_REGS->GMAC_TBQB = (uint32_t) s_txdesc; // about the descriptor addresses
- GMAC_REGS->GMAC_RBQB = (uint32_t) s_rxdesc; // Let the controller know
- GMAC_REGS->GMAC_SAB0 =
- MG_U32(ifp->mac[3], ifp->mac[2], ifp->mac[1], ifp->mac[0]);
- GMAC_REGS->GMAC_SAT0 = MG_U32(0, 0, ifp->mac[5], ifp->mac[4]);
- GMAC_REGS->GMAC_UR &= ~MG_BIT(0); // Disable MII, use RMII
- GMAC_REGS->GMAC_NCFGR |= MG_BIT(8) | MG_BIT(6) | // MAXFX, MTIHEN
- MG_BIT(25) | MG_BIT(4); // EFRHD, CAF
- GMAC_REGS->GMAC_TSR = 0x17f; // all transmit statuses
- GMAC_REGS->GMAC_RSR = 0xf; // all recv statuses
- GMAC_REGS->GMAC_IDR = ~0U; // Disable interrupts, then enable required
- GMAC_REGS->GMAC_IER = MG_BIT(11) | MG_BIT(10) | // HRESP, ROVR
- MG_BIT(7) | MG_BIT(6) | // TCOMP, TFC
- MG_BIT(5) | MG_BIT(4) | // RLEX, TUR
- MG_BIT(2) | MG_BIT(1); // RXUBR, RCOMP
- GMAC_REGS->GMAC_NCR |= MG_BIT(3) | MG_BIT(2); // TXEN, RXEN
- return true;
- }
- static size_t mg_tcpip_driver_same54_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][1] & MG_BIT(31)) == 0) {
- ifp->nerr++;
- MG_ERROR(("No free descriptors"));
- len = 0; // All descriptors are busy, fail
- } else {
- uint32_t status = len | MG_BIT(15); // Frame length, last chunk
- if (s_txno == ETH_DESC_CNT - 1) status |= MG_BIT(30); // wrap
- memcpy(s_txbuf[s_txno], buf, len); // Copy data
- s_txdesc[s_txno][1] = status;
- if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
- }
- MG_DSB(); // Ensure descriptors have been written
- GMAC_REGS->GMAC_NCR |= MG_BIT(9); // Enable transmission
- return len;
- }
- static void mg_tcpip_driver_same54_update_hash_table(struct mg_tcpip_if *ifp) {
- // TODO(): read database, rebuild hash table
- // Setting Hash Index for 01:00:5e:00:00:fb (multicast)
- // 24.6.9 Hash addressing
- // computed hash is 55, which means bit 23 (55 - 32) in
- // HRT register must be set
- GMAC_REGS->GMAC_HRT = MG_BIT(23);
- GMAC_REGS->GMAC_NCFGR |= MG_BIT(6); // enable multicast hash filtering
- (void) ifp;
- }
- static bool mg_tcpip_driver_same54_poll(struct mg_tcpip_if *ifp, bool s1) {
- if (ifp->update_mac_hash_table) {
- mg_tcpip_driver_same54_update_hash_table(ifp);
- ifp->update_mac_hash_table = false;
- }
- bool up = false;
- if (s1) {
- uint8_t speed = MG_PHY_SPEED_10M;
- bool full_duplex = false;
- struct mg_phy phy = {eth_read_phy, eth_write_phy};
- up = mg_phy_up(&phy, 0, &full_duplex, &speed);
- // If PHY is ready, update NCFGR accordingly
- if (ifp->state == MG_TCPIP_STATE_DOWN && up) {
- GMAC_REGS->GMAC_NCFGR =
- (GMAC_REGS->GMAC_NCFGR & ~(MG_BIT(0) | MG_BIT(1))) | (speed & 1) |
- (full_duplex << 1);
- }
- }
- return up;
- }
- void GMAC_Handler(void);
- void GMAC_Handler(void) {
- uint32_t isr = GMAC_REGS->GMAC_ISR;
- uint32_t rsr = GMAC_REGS->GMAC_RSR;
- uint32_t tsr = GMAC_REGS->GMAC_TSR;
- if (isr & MG_BIT(1)) {
- if (rsr & MG_BIT(1)) {
- for (uint8_t i = 0; i < ETH_DESC_CNT; i++) {
- if ((s_rxdesc[s_rxno][0] & MG_BIT(0)) == 0) break;
- size_t len = s_rxdesc[s_rxno][1] & (MG_BIT(13) - 1);
- mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp);
- s_rxdesc[s_rxno][0] &= ~MG_BIT(0); // Disown
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
- }
- }
- }
- if (tsr != 0) {
- // MG_INFO((" --> %#x %#x", s_txdesc[s_txno][1], tsr));
- if (!(s_txdesc[s_txno][1] & MG_BIT(31))) s_txdesc[s_txno][1] |= MG_BIT(31);
- }
- GMAC_REGS->GMAC_RSR = rsr;
- GMAC_REGS->GMAC_TSR = tsr;
- }
- struct mg_tcpip_driver mg_tcpip_driver_same54 = {
- mg_tcpip_driver_same54_init, mg_tcpip_driver_same54_tx, NULL,
- mg_tcpip_driver_same54_poll};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/sdio.c"
- #endif
- #if MG_ENABLE_TCPIP && \
- (defined(MG_ENABLE_DRIVER_CYW_SDIO) && MG_ENABLE_DRIVER_CYW_SDIO)
- // SDIO 6.9 Table 6-1 CCCR (Common Card Control Registers)
- #define MG_SDIO_CCCR_SDIOREV 0x000
- #define MG_SDIO_CCCR_SDREV 0x001
- #define MG_SDIO_CCCR_IOEN 0x002
- #define MG_SDIO_CCCR_IORDY 0x003
- #define MG_SDIO_CCCR_INTEN 0x004
- #define MG_SDIO_CCCR_BIC 0x007
- #define MG_SDIO_CCCR_CCAP 0x008
- #define MG_SDIO_CCCR_CCIS 0x009 // 3 registers
- #define MG_SDIO_CCCR_F0BLKSZ 0x010 // 2 registers
- #define MG_SDIO_CCCR_HISPD 0x013
- // SDIO 6.10 Table 6-3 FBR (Function Basic Registers)
- #define MG_SDIO_FBR_FnBLKSZ(n) (((n) &7) * 0x100 + 0x10) // 2 registers
- // SDIO 5.1 IO_RW_DIRECT Command (CMD52)
- #define MG_SDIO_DATA(x) ((x) &0xFF) // bits 0-7
- #define MG_SDIO_ADDR(x) (((x) &0x1FFFF) << 9) // bits 9-25
- #define MG_SDIO_FUNC(x) (((x) &3) << 28) // bits 28-30 (30 unused here)
- #define MG_SDIO_WR MG_BIT(31)
- // SDIO 5.3 IO_RW_EXTENDED Command (CMD53)
- #define MG_SDIO_LEN(x) ((x) &0x1FF) // bits 0-8
- #define MG_SDIO_OPINC MG_BIT(26)
- #define MG_SDIO_BLKMODE MG_BIT(27)
- // - Drivers set blocksize, drivers request transfers. Requesting a read
- // transfer > blocksize means block transfer will be used.
- // - To simplify the use of DMA transfers and avoid intermediate buffers,
- // drivers must have room to accomodate a whole block transfer, e.g.: blocksize
- // = 64, read 65 => 2 blocks = 128 bytes
- // - Transfers of more than 1 byte assume (uint32_t *) data. 1-byte transfers
- // use (uint8_t *) data
- // - 'len' is the number of _bytes_ to transfer
- bool mg_sdio_transfer(struct mg_tcpip_sdio *sdio, bool write, unsigned int f,
- uint32_t addr, void *data, uint32_t len) {
- uint32_t arg, val = 0;
- unsigned int blksz = 64; // TODO(): mg_sdio_set_blksz() stores in an array,
- // index on f, skip if 0
- if (len == 1) {
- arg = (write ? MG_SDIO_WR : 0) | MG_SDIO_FUNC(f) | MG_SDIO_ADDR(addr) |
- (write ? MG_SDIO_DATA(*(uint8_t *) data) : 0);
- bool res = sdio->txn(sdio, 52, arg, &val); // IO_RW_DIRECT
- if (!write) *(uint8_t *) data = (uint8_t) val;
- return res;
- }
- // IO_RW_EXTENDED
- arg = (write ? MG_SDIO_WR : 0) | MG_SDIO_OPINC | MG_SDIO_FUNC(f) |
- MG_SDIO_ADDR(addr);
- if (len > 512 || (blksz != 0 && len > blksz)) { // SDIO 5.3 512 -> len=0
- unsigned int blkcnt;
- if (blksz == 0) return false; // > 512 requires block size set
- blkcnt = (len + blksz - 1) / blksz;
- if (blkcnt > 511) return false; // we don't support "infinite" blocks
- arg |= MG_SDIO_BLKMODE | MG_SDIO_LEN(blkcnt); // block transfer
- len = blksz * blkcnt;
- } else {
- arg |= MG_SDIO_LEN(len); // multi-byte transfer
- }
- return sdio->xfr(sdio, write, arg,
- (arg & MG_SDIO_BLKMODE) ? (uint16_t) blksz : 0,
- (uint32_t *) data, len, &val);
- }
- bool mg_sdio_set_blksz(struct mg_tcpip_sdio *sdio, unsigned int f,
- uint16_t blksz) {
- uint32_t val = blksz & 0xff;
- if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_FBR_FnBLKSZ(f), &val, 1))
- return false;
- val = (blksz >> 8) & 0x0f; // SDIO 6.10 Table 6-4, max 2048
- if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_FBR_FnBLKSZ(f) + 1, &val, 1))
- return false;
- // TODO(): store in an array, index on f. Static 8-element array
- MG_VERBOSE(("F%c block size set", (f & 7) + '0'));
- return true;
- }
- // Enable Fx
- bool mg_sdio_enable_f(struct mg_tcpip_sdio *sdio, unsigned int f) {
- uint8_t bit = 1U << (f & 7), bits;
- uint32_t val = 0;
- if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_IOEN, &val, 1))
- return false;
- bits = (uint8_t) val | bit;
- unsigned int times = 501;
- while (times--) {
- val = bits; /* IOEf */
- ;
- if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_CCCR_IOEN, &val, 1))
- return false;
- mg_delayms(1);
- val = 0;
- if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_IOEN, &val, 1))
- return false;
- if (val & bit) break;
- }
- if (times == (unsigned int) ~0) return false;
- MG_VERBOSE(("F%c enabled", (f & 7) + '0'));
- return true;
- }
- // Wait for Fx to be ready
- bool mg_sdio_waitready_f(struct mg_tcpip_sdio *sdio, unsigned int f) {
- uint8_t bit = 1U << (f & 7);
- unsigned int times = 501;
- while (times--) {
- uint32_t val;
- if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_IORDY, &val, 1))
- return false;
- if (val & bit) break; // IORf
- mg_delayms(1);
- }
- if (times == (unsigned int) ~0) return false;
- MG_VERBOSE(("F%c ready", (f & 7) + '0'));
- return true;
- }
- // SDIO 6.14 Bus State Diagram
- bool mg_sdio_init(struct mg_tcpip_sdio *sdio) {
- uint32_t val = 0;
- if (!sdio->txn(sdio, 0, 0, NULL)) return false; // GO_IDLE_STATE
- sdio->txn(sdio, 5, 0, &val); // IO_SEND_OP_COND, no CRC
- MG_VERBOSE(("IO Functions: %u, Memory: %c", 1 + ((val >> 28) & 7),
- (val & MG_BIT(27)) ? 'Y' : 'N'));
- if (!sdio->txn(sdio, 3, 0, &val)) return false; // SEND_RELATIVE_ADDR
- val = ((uint32_t) val) >> 16; // RCA
- if (!sdio->txn(sdio, 7, val << 16, &val))
- return false; // SELECT/DESELECT_CARD
- mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_SDIOREV, &val, 1);
- MG_DEBUG(("CCCR: %u.%u, SDIO: %u.%u", 1 + ((val >> 2) & 3), (val >> 0) & 3,
- 1 + ((val >> 6) & 3), (val >> 4) & 3));
- mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_SDREV, &val, 1);
- MG_VERBOSE(("SD: %u.%u", 1 + ((val >> 2) & 3), (val >> 0) & 3));
- mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_BIC, &val, 1);
- MG_SET_BITS(val, 3,
- MG_BIT(7) | MG_BIT(1)); // SDIO 6.9 Tables 6-1 6-2, 4-bit bus
- mg_sdio_transfer(sdio, true, 0, MG_SDIO_CCCR_BIC, &val, 1);
- // All Full-Speed SDIO cards support a 4-bit bus. Skip for Low-Speed SDIO
- // cards, we don't provide separate low-level functions for width and speed
- sdio->cfg(sdio, 0); // set DS;
- if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_HISPD, &val, 1))
- return false;
- if (val & MG_BIT(0) /* SHS */) {
- val = MG_BIT(1); /* EHS */
- if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_CCCR_HISPD, &val, 1))
- return false;
- sdio->cfg(sdio, 1); // set HS;
- MG_VERBOSE(("Bus set to 4-bit @50MHz"));
- } else {
- MG_VERBOSE(("Bus set to 4-bit @25MHz"));
- }
- return true;
- }
- // - 6.11 Card Information Structure (CIS): 0x0001000-0x017FF; for card common
- // and all functions
- // - 16.5 SDIO Card Metaformat
- // - 16.7.2 CISTPL_FUNCE (0x22): Function Extension Tuple, provides standard
- // information about the card (common) and each individual function. One
- // CISTPL_FUNCE in each function’s CIS, immediately following the CISTPL_FUNCID
- // tuple
- // - 16.7.3 CISTPL_FUNCE Tuple for Function 0 (common)
- // - 16.7.4 CISTPL_FUNCE Tuple for Function 1-7
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/st67w6.c"
- #endif
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_ST67W6) && \
- MG_ENABLE_DRIVER_ST67W6
- static struct mg_tcpip_if *s_ifp;
- static uint32_t s_ip, s_mask;
- static bool s_link = false, s_connecting = false;
- static void wifi_cb(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
- struct mg_wifi_data *wifi =
- &((struct mg_tcpip_driver_st67w6_data *) ifp->driver_data)->wifi;
- if (wifi->apmode && ev == MG_TCPIP_EV_STATE_CHANGE &&
- *(uint8_t *) ev_data == MG_TCPIP_STATE_UP) {
- MG_DEBUG(("Access Point started"));
- s_ip = ifp->ip, ifp->ip = wifi->apip;
- s_mask = ifp->mask, ifp->mask = wifi->apmask;
- ifp->enable_dhcp_client = false;
- ifp->enable_dhcp_server = true;
- }
- }
- static bool st67w6_init(uint8_t *mac);
- static void st67w6_poll(bool is_at);
- static bool mg_tcpip_driver_st67w6_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_st67w6_data *d =
- (struct mg_tcpip_driver_st67w6_data *) ifp->driver_data;
- struct mg_wifi_data *wifi = &d->wifi;
- if (MG_BIG_ENDIAN) {
- MG_ERROR(("Big-endian host"));
- return false;
- }
- if (d->is_ready == NULL) return false;
- s_ifp = ifp;
- s_ip = ifp->ip;
- s_mask = ifp->mask;
- s_link = false;
- ifp->pfn = wifi_cb;
- if (!st67w6_init(ifp->mac)) return false;
- if (d->send_queue.size == 0) d->send_queue.size = 8192;
- d->send_queue.buf = (char *) mg_calloc(1, d->send_queue.size);
- if (d->send_queue.buf == NULL) {
- MG_ERROR(("OOM"));
- return false;
- }
- if (wifi->apmode) {
- return mg_wifi_ap_start(wifi);
- } else if (wifi->ssid != NULL && wifi->pass != NULL) {
- return mg_wifi_connect(wifi);
- }
- return true;
- }
- // Decouple; module access depends on it being RDY. See st67w6_poll()
- size_t mg_tcpip_driver_st67w6_output(const void *buf, size_t len,
- struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_st67w6_data *d =
- (struct mg_tcpip_driver_st67w6_data *) ifp->driver_data;
- char *p;
- if (mg_queue_book(&d->send_queue, &p, len) < len) return 0;
- memcpy(p, buf, len);
- mg_queue_add(&d->send_queue, len);
- return len;
- }
- static bool mg_tcpip_driver_st67w6_poll(struct mg_tcpip_if *ifp, bool s1) {
- struct mg_tcpip_driver_st67w6_data *d =
- (struct mg_tcpip_driver_st67w6_data *) ifp->driver_data;
- if (d->is_ready == NULL) return false;
- st67w6_poll(false);
- if (!s1) return false;
- return s_link;
- }
- struct mg_tcpip_driver mg_tcpip_driver_st67w6 = {
- mg_tcpip_driver_st67w6_init, mg_tcpip_driver_st67w6_output, NULL,
- mg_tcpip_driver_st67w6_poll};
- // AT | STA | AP | HCI | OT
- // --------------------------
- // framing <-- includes rx stall indication
- // --------------------------
- // SPI <-- padded to 32-bit
- //
- // Transactions take place when the module signals RDY, 'tx' and 'write'
- // functions actually just write to memory
- // - AT: handles configuration and events (unsolicited +foo:). Responses may
- // come as:
- // - 1 frame: just OK or ERROR
- // - 2 frames: 1 text line each, a response and OK
- // - 3 frames: 2 text lines: 1 frame with text and no CR/LF, 1 frame with
- // binary and CR/LF, 1 text line with OK
- // - STA and AP contain plain Ethernet frames to/from the STA and AP networks,
- // respectively
- // - HCI: BLE stuff
- // - OT: ?
- #pragma pack(push, 1)
- // little endian
- struct spi_hdr {
- uint16_t magic;
- uint16_t len;
- uint8_t vflags; // version :2, rx_stall :1, flags :5
- uint8_t type;
- uint16_t reserved;
- };
- #define ST67W6_SPI_TYPE_AT 0
- #define ST67W6_SPI_TYPE_STA 1
- #define ST67W6_SPI_TYPE_AP 2
- #define ST67W6_SPI_TYPE_HCI 3
- #define ST67W6_SPI_TYPE_OT 4
- #pragma pack(pop)
- static uint32_t txdata[2048 / 4], rxdata[2048 / 4];
- static uint8_t at_resp[300];
- static bool s_at_ok, s_at_err;
- static size_t s_at_resp_len;
- static void st67w6_handle_wifi_evnt(char *, size_t len);
- static void st67w6_handle_scan_result(char *, size_t len);
- static size_t st67w6_spi_poll(uint8_t *write, uint8_t *read);
- static void st67w6_write(unsigned int f, void *data, uint16_t len);
- static void st67w6_update_hash_table(void);
- // High-level comm stuff
- static void st67w6_poll(bool is_at) {
- struct mg_tcpip_driver_st67w6_data *d =
- (struct mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
- struct spi_hdr *h = (struct spi_hdr *) rxdata;
- unsigned int type;
- uint8_t *txsource = (uint8_t *) txdata;
- if (s_ifp->update_mac_hash_table) {
- // first call to _poll() is after _init(), so this is safe
- st67w6_update_hash_table();
- s_ifp->update_mac_hash_table = false;
- }
- if (!is_at) { // send outstanding WLAN frames in the queue
- char *buf;
- size_t len;
- // NOTE(): have traffic-dependent queues or queue traffic type with data
- if ((len = mg_queue_next(&d->send_queue, &buf)) > 0) {
- st67w6_write(d->wifi.apmode ? ST67W6_SPI_TYPE_AP : ST67W6_SPI_TYPE_STA,
- buf, (uint16_t) len);
- mg_queue_del(&d->send_queue, len);
- } else { // nothing to send
- txsource = NULL;
- }
- }
- if (st67w6_spi_poll(txsource, (uint8_t *) rxdata) == 0) return;
- if (h->len == 0) return;
- type = h->type;
- if (type == ST67W6_SPI_TYPE_AT) {
- char *p = (char *) (h + 1);
- size_t len = h->len;
- if (len > 7 && strncmp(p, "+CWLAP:", 7) == 0) { // scan result
- st67w6_handle_scan_result(p + 7, len - 7);
- } else if (len > 4 && strncmp(p, "+CW:", 4) == 0) {
- st67w6_handle_wifi_evnt(p + 4, len - 4);
- } else {
- MG_VERBOSE(("AT partial: %.*s", (int) len, p));
- if (s_at_resp_len + len >= sizeof(at_resp))
- s_at_resp_len = 0; // truncate response, error will be caught later
- memcpy(at_resp + s_at_resp_len, p, len);
- s_at_resp_len += len;
- if (mg_match(mg_str_n((char *) at_resp, s_at_resp_len),
- mg_str_n("*ERROR*", 7), NULL)) {
- s_at_err = true;
- } else if (mg_match(mg_str_n((char *) at_resp, s_at_resp_len),
- mg_str_n("*OK*", 4), NULL)) {
- s_at_ok = true;
- }
- }
- } else if (type == ST67W6_SPI_TYPE_STA || type == ST67W6_SPI_TYPE_AP) {
- // WLAN frame reception
- mg_tcpip_qwrite(h + 1, h->len, s_ifp);
- } // else silently discard
- }
- // WLAN event handling
- // - Do not call any AT functions here, otherwise revise st67w6_at_wait()
- // - The module likes to send ERROR along with other events
- static void st67w6_handle_wifi_evnt(char *p, size_t len) {
- struct mg_str data[2];
- MG_VERBOSE(("event: %.*s", (int) len, p));
- if (len > 9 && strncmp(p, "CONNECTED", 9) == 0) {
- s_link = true;
- s_connecting = false;
- } else if (len > 12 && strncmp(p, "DISCONNECTED", 12) == 0) {
- s_link = false;
- s_connecting = false; // should not be needed
- } else if (s_connecting && len > 6 &&
- mg_match(mg_str_n(p, len), mg_str_n("ERROR,*\r\n*", 10), data)) {
- size_t reason = 0;
- bool ok = mg_to_size_t(data[0], &reason);
- s_connecting = false;
- MG_ERROR(("CONNECT FAILED"));
- mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_CONNECT_ERR, ok ? &reason : NULL);
- } else if (len > 9 && strncmp(p, "SCAN_DONE", 9) == 0) {
- MG_VERBOSE(("scan complete"));
- mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_END, NULL);
- } // else silently discard: CONNECTING; STA_CONNECTED,"MAC";
- // STA_DISCONNECTED,"MAC"; DIST_STA_IP,"MAC","IP"
- }
- static bool st67w6_at_cmd(char *cmd, size_t len);
- // Wi-Fi network stuff
- static bool st67w6_wifi_connect(char *ssid, char *pass) {
- char cmd[90]; // ssid + pass + AT
- size_t cmd_len;
- if (!st67w6_at_cmd("AT+CWMODE=1,0\r\n", 15)) return false;
- cmd_len = mg_snprintf(cmd, sizeof(cmd), "AT+CWJAP=\"%s\",\"%s\",,0\r\n", ssid,
- pass); // takes >700ms
- if (!st67w6_at_cmd(cmd, cmd_len)) return false;
- st67w6_at_cmd("AT+CWRECONNCFG=0,0\r\n", 20); // disregard error, connecting
- s_connecting = true;
- return true;
- }
- static bool st67w6_wifi_disconnect(void) {
- s_connecting = false;
- if (!st67w6_at_cmd("AT+CWQAP=0\r\n", 12)) return false; // takes >800ms
- return st67w6_at_cmd("AT+CWMODE=0,0\r\n", 15); // takes >550ms
- }
- static bool st67w6_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
- char cmd[90]; // ssid + pass + AT
- size_t cmd_len;
- if (!st67w6_at_cmd("AT+CWMODE=2,0\r\n", 15)) return false; // takes >800ms
- cmd_len = mg_snprintf(
- cmd, sizeof(cmd), "AT+CWSAP=\"%s\",\"%s\",%u,3,2,0\r\n", ssid, pass,
- channel); // 3: WPA2_PSK; 2: max stations // takes >350ms
- s_link = true;
- return st67w6_at_cmd(cmd, cmd_len);
- }
- static bool st67w6_wifi_ap_stop(void) {
- s_link = false;
- return st67w6_at_cmd("AT+CWMODE=0,0\r\n", 15); // takes >550ms
- }
- // WLAN scan handling
- // +CWLAP:(security,"SSID",RSSI,"BSSID",channel,cipher,proto,wps)\r\n
- // security: OPEN, WEP, WPA, WPA2, WPA-WPA2, WPA-EAP, WPA3-SAE, WPA2-WPA3-SAE
- // cipher: NONE, WEP, AES/CCMP, TKIP, TKIP and AES/CCMP
- // proto: 4-bit bitmap AX,N,G,B; all set from right to left
- static bool st67w6_wifi_scan(void) {
- return st67w6_at_cmd("AT+CWLAPOPT=1,1695,-100,255,50\r\n", 32) &&
- st67w6_at_cmd("AT+CWLAP=0,,,0\r\n", 16);
- }
- static void st67w6_handle_scan_result(char *data, size_t len) {
- struct mg_wifi_scan_bss_data bss;
- struct mg_str fields[2];
- char mac[6];
- uint8_t val;
- unsigned int i;
- MG_VERBOSE(("scan result event: %.*s", (int) len, data));
- ++data, --len; // skip '('
- if (!mg_span(mg_str_n(data, len), &fields[0], &fields[1], ',') ||
- !mg_str_to_num(fields[0], 10, &val, 1))
- return;
- bss.security =
- (val == 0) ? MG_WIFI_SECURITY_OPEN : (uint8_t) MG_WIFI_SECURITY_WEP;
- if (val == 2 || val == 4) bss.security |= MG_WIFI_SECURITY_WPA;
- if (val == 3 || val == 4 || val == 7) bss.security |= MG_WIFI_SECURITY_WPA2;
- if (val == 6 || val == 7) bss.security |= MG_WIFI_SECURITY_WPA3;
- if (val == 5) bss.security |= MG_WIFI_SECURITY_WPA_ENTERPRISE;
- if (!mg_span(fields[1], &fields[0], &fields[1], ',')) return;
- bss.SSID.buf = fields[0].buf + 1, bss.SSID.len = fields[0].len - 2;
- if (!mg_span(fields[1], &fields[0], &fields[1], ',')) return;
- while (fields[0].buf[0] == ' ') ++fields[0].buf, --fields[0].len;
- if (fields[0].buf[0] != '-') return; // positive RSSI would be great
- ++fields[0].buf, --fields[0].len;
- if (!mg_str_to_num(fields[0], 10, &val, 1)) return;
- bss.RSSI = (int8_t) - (int8_t) val;
- if (!mg_span(fields[1], &fields[0], &fields[1], ',')) return;
- if (fields[0].len < 19) return;
- ++fields[0].buf, --fields[0].len; // skip '"'
- for (i = 0; i < 6; i++) {
- struct mg_str str;
- str.buf = fields[0].buf + 3 * i;
- str.len = 2;
- if (!mg_str_to_num(str, 16, &mac[i], 1)) return;
- }
- bss.BSSID = mac;
- if (!mg_span(fields[1], &fields[0], &fields[1], ',') ||
- !mg_str_to_num(fields[0], 10, &bss.channel, 1))
- return;
- if (!mg_span(fields[1], &fields[0], &fields[1], ',') ||
- !mg_str_to_num(fields[0], 10, &val, 1))
- return;
- // ignore cypher
- if (!mg_span(fields[1], &fields[0], &fields[1], ',') ||
- !mg_str_to_num(fields[0], 10, &val, 1))
- return;
- bss.has_n = (val & 4) != 0;
- bss.has_ax = (val & 8) != 0;
- bss.band = MG_WIFI_BAND_2G; // NOT INFORMED with default options, no docs
- MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf,
- bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI,
- bss.security));
- mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_RESULT, &bss);
- }
- // AT stuff
- static inline bool delayms(unsigned int ms) {
- mg_delayms(ms);
- return true;
- }
- // send AT command, wait for a response or timeout, meanwhile delivering
- // received frames and events
- static bool st67w6_at_cmd(char *cmd, size_t len) {
- bool is_at = true;
- unsigned int times = 1000;
- s_at_resp_len = 0;
- st67w6_write(ST67W6_SPI_TYPE_AT, cmd, (uint16_t) len);
- s_at_ok = false, s_at_err = false;
- do { // AT response processing does not call any other AT function
- st67w6_poll(is_at); // otherwise we can't allow them to pile up here
- is_at = false; // avoid repeating, allow queued WLAN frames to be sent
- // network frames will be pushed to the queue so that is safe
- } while (!s_at_ok && !s_at_err && times-- > 0 && delayms(1));
- MG_VERBOSE(("AT response:\n%.*s", s_at_resp_len, at_resp));
- MG_VERBOSE(("ok: %c, err: %c, times: %d", s_at_ok ? '1' : '0',
- s_at_err ? '1' : '0', (int) times));
- return s_at_ok;
- }
- static bool st67w6_spi_init(void);
- bool mg_to_size_t(struct mg_str str, size_t *val);
- static bool st67w6_init(uint8_t *mac) {
- // struct mg_tcpip_driver_st67w6_data *d = (struct
- // mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
- struct mg_str data[3];
- size_t val;
- bool is_b = false;
- if (!st67w6_spi_init()) return false;
- if (!st67w6_at_cmd("AT\r\n", 4)) return false;
- if (!st67w6_at_cmd("AT+CWNETMODE?\r\n", 15) ||
- !mg_match(mg_str_n((char *) at_resp, s_at_resp_len),
- mg_str_n("*:*\r\n*", 6), data) ||
- !mg_to_size_t(data[1], &val))
- return false;
- if (val != 0) {
- MG_ERROR(("Wrong firmware, T02 is needed"));
- return false;
- }
- // set clock, who cares ???
- if (!st67w6_at_cmd("AT+GET_CLOCK\r\n", 14)) return false;
- MG_DEBUG(("%.*s", s_at_resp_len, at_resp)); // TODO(scaprile): --> VERBOSE
- // BT-ENABLED DEPENDENCY
- // MODULE DEPENDENCY
- if (!st67w6_at_cmd("AT+EFUSE-R=24,\"0x100\"\r\n", 23) ||
- !mg_match(mg_str_n((char *) at_resp, s_at_resp_len), mg_str_n("*,*", 3),
- data))
- return false;
- if (data[1].buf[0] == 'C' && data[1].buf[1] == '6') is_b = true;
- MG_DEBUG(("WLAN module is %sB type", is_b ? "" : "not")); // --> VERBOSE
- if (is_b) {
- // Disable the antenna diversity pin
- if (!st67w6_at_cmd("AT+IORST=0\r\n", 12)) return false;
- // Apparently they intend to disable some antenna ...
- if (!st67w6_at_cmd("AT+CWANTENABLE?\r\n", 17)) return false;
- MG_DEBUG(("%.*s", s_at_resp_len, at_resp)); // --> VERBOSE
- }
- // Do not set wake-up pin (AT+SLWKIO)
- // Disable power save mode
- // NOTE(scaprile): (no response if in hibernate mode, though I guess we
- // wouldn't have reached this point in that case either)
- if (!st67w6_at_cmd("AT+PWR=0\r\n", 12)) return false;
- // set Wi-Fi
- // set country code
- if (!st67w6_at_cmd("AT+CWCOUNTRY=0,\"00\"\r\n", 21)) return false;
- #if 0
- // set DTIM
- if (!st67w6_at_cmd("AT+SLWKDTIM=1\r\n", 15)) return false;
- #endif
- // Read only default MAC, ignore set bit count (data[6] & 0x3F). Custom MACs
- // reside at @0x64 and 0x70
- if (st67w6_at_cmd("AT+EFUSE-R=7,\"0x014\"\r\n", 22) &&
- mg_match(mg_str_n((char *) at_resp, s_at_resp_len), mg_str_n("*,*", 3),
- data)) {
- int i;
- for (i = 0; i < 6; i++) mac[i] = data[1].buf[5 - i];
- MG_DEBUG(("MAC: %M", mg_print_mac, mac));
- } else {
- MG_ERROR(("read MAC failed"));
- }
- return true;
- }
- static void st67w6_update_hash_table(void) {
- // TODO(): read database, rebuild hash table
- // uint32_t val = 0;
- // val = 1;
- // st67w6_at_iovar_set2_(0, "mcast_list", (uint8_t *) &val, sizeof(val),
- // (uint8_t *) mcast_addr, sizeof(mcast_addr)); mg_delayms(50);
- }
- // SPI specifics
- #define ST67W6_SPI_MAGIC 0x55AA
- // #define ST67W6_SPI_VERSION(x), IS_STALL, FLAGS(x), ...
- static const uint8_t idlehdr[sizeof(struct spi_hdr)] = {0xaa, 0x55, 0, 0,
- 0, 0, 0, 0};
- static void st67w6_write(unsigned int f, void *data, uint16_t len) {
- struct spi_hdr *h = (struct spi_hdr *) txdata;
- h->magic = ST67W6_SPI_MAGIC;
- h->type = (uint8_t) f;
- h->vflags = 0;
- h->len = len;
- h->reserved = 0;
- memmove(h + 1, data, len);
- }
- static size_t st67w6_spi_poll(uint8_t *write, uint8_t *read) {
- struct mg_tcpip_driver_st67w6_data *d =
- (struct mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
- struct spi_hdr *th, *rh = (struct spi_hdr *) read;
- struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) d->spi;
- size_t padded;
- unsigned int times;
- th = (write != NULL) ? (struct spi_hdr *) write : (struct spi_hdr *) idlehdr;
- s->begin(s->spi);
- times = 50;
- while (times--) {
- if (d->is_ready()) break;
- if (times == 0) {
- MG_ERROR(("RDY TIMEOUT"));
- s->end(s->spi);
- return 0;
- }
- mg_delayms(1);
- }
- padded = (th->len + 3) & ~3;
- s->txn(s->spi, (uint8_t *) th, (uint8_t *) rh, sizeof(*th) + padded);
- if (rh->magic == ST67W6_SPI_MAGIC && rh->len > padded) {
- size_t remaining_padded = (rh->len - padded + 3) & ~3;
- if (remaining_padded > (2048 - sizeof(*rh) - padded))
- remaining_padded = 2048 - sizeof(*rh) - padded;
- s->txn(s->spi, NULL, read + sizeof(*rh) + padded, remaining_padded);
- }
- times = 50;
- while (times--) {
- if (!d->is_ready()) break;
- if (times == 0) {
- MG_ERROR(("!RDY TIMEOUT"));
- break;
- }
- mg_delayms(1);
- }
- s->end(s->spi);
- return (size_t) rh->len;
- }
- static bool st67w6_spi_init(void) {
- struct mg_tcpip_driver_st67w6_data *d =
- (struct mg_tcpip_driver_st67w6_data *) s_ifp->driver_data;
- size_t len;
- unsigned int times = 1000;
- while (times--) {
- if (d->is_ready()) break;
- if (times == 0) return false;
- mg_delayms(1);
- }
- if (((len = st67w6_spi_poll(NULL, (uint8_t *) rxdata)) == 0) ||
- !mg_match(mg_str_n(((char *) rxdata) + sizeof(struct spi_hdr), len),
- mg_str_n("*ready*", 7), NULL))
- return false;
- return true;
- }
- // Mongoose Wi-Fi API functions
- bool mg_wifi_scan(void) {
- return st67w6_wifi_scan();
- }
- bool mg_wifi_connect(struct mg_wifi_data *wifi) {
- s_ifp->ip = s_ip;
- s_ifp->mask = s_mask;
- if (s_ifp->ip == 0) s_ifp->enable_dhcp_client = true;
- s_ifp->enable_dhcp_server = false;
- MG_DEBUG(("Connecting to '%s'", wifi->ssid));
- return st67w6_wifi_connect(wifi->ssid, wifi->pass);
- }
- bool mg_wifi_disconnect(void) {
- return st67w6_wifi_disconnect();
- }
- bool mg_wifi_ap_start(struct mg_wifi_data *wifi) {
- MG_DEBUG(("Starting AP '%s' (%u)", wifi->apssid, wifi->apchannel));
- return st67w6_wifi_ap_start(wifi->apssid, wifi->appass, wifi->apchannel);
- }
- bool mg_wifi_ap_stop(void) {
- return st67w6_wifi_ap_stop();
- }
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/stm32f.c"
- #endif
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32F) && \
- MG_ENABLE_DRIVER_STM32F
- struct stm32f_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 stm32f_eth *) (uintptr_t) 0x40028000)
- #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] MG_ETH_RAM; // RX descriptors
- static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM; // TX descriptors
- static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM; // RX ethernet buffers
- static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM; // 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
- static uint16_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 |= MG_BIT(0);
- while (ETH->MACMIIAR & MG_BIT(0)) (void) 0;
- return ETH->MACMIIDR & 0xffff;
- }
- static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
- ETH->MACMIIDR = val;
- ETH->MACMIIAR &= (7 << 2);
- ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | MG_BIT(1);
- ETH->MACMIIAR |= MG_BIT(0);
- while (ETH->MACMIIAR & MG_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_stm32f_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_stm32f_data *d =
- (struct mg_tcpip_driver_stm32f_data *) ifp->driver_data;
- uint8_t phy_addr = d == NULL ? 0 : d->phy_addr;
- s_ifp = ifp;
- // Init RX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_rxdesc[i][0] = MG_BIT(31); // Own
- s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | MG_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 |= MG_BIT(0); // Software reset
- while ((ETH->DMABMR & MG_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 = MG_BIT(13) | MG_BIT(16) | MG_BIT(22) | MG_BIT(23) |
- // MG_BIT(25);
- ETH->MACIMR = MG_BIT(3) | MG_BIT(9); // Mask timestamp & PMT IT
- ETH->MACFCR = MG_BIT(7); // Disable zero quarta pause
- ETH->MACFFR = MG_BIT(10); // Perfect filtering
- struct mg_phy phy = {eth_read_phy, eth_write_phy};
- mg_phy_init(&phy, phy_addr, MG_PHY_CLOCKS_MAC);
- ETH->DMARDLAR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors
- ETH->DMATDLAR = (uint32_t) (uintptr_t) s_txdesc; // RX descriptors
- ETH->DMAIER = MG_BIT(6) | MG_BIT(16); // RIE, NISE
- ETH->MACCR =
- MG_BIT(2) | MG_BIT(3) | MG_BIT(11) | MG_BIT(14); // RE, TE, Duplex, Fast
- ETH->DMAOMR =
- MG_BIT(1) | MG_BIT(13) | MG_BIT(21) | MG_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_stm32f_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] & MG_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] = MG_BIT(20) | MG_BIT(28) | MG_BIT(29); // Chain,FS,LS
- s_txdesc[s_txno][0] |= MG_BIT(31); // Set OWN bit - let DMA take over
- if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
- }
- MG_DSB(); // ensure descriptors have been written
- ETH->DMASR = MG_BIT(2) | MG_BIT(5); // Clear any prior TBUS/TUS
- ETH->DMATPDR = 0; // and resume
- return len;
- }
- static void mg_tcpip_driver_stm32f_update_hash_table(struct mg_tcpip_if *ifp) {
- // TODO(): read database, rebuild hash table
- ETH->MACA1LR = (uint32_t) mcast_addr[3] << 24 |
- (uint32_t) mcast_addr[2] << 16 |
- (uint32_t) mcast_addr[1] << 8 | (uint32_t) mcast_addr[0];
- ETH->MACA1HR = (uint32_t) mcast_addr[5] << 8 | (uint32_t) mcast_addr[4];
- ETH->MACA1HR |= MG_BIT(31); // AE
- (void) ifp;
- }
- static bool mg_tcpip_driver_stm32f_poll(struct mg_tcpip_if *ifp, bool s1) {
- if (ifp->update_mac_hash_table) {
- mg_tcpip_driver_stm32f_update_hash_table(ifp);
- ifp->update_mac_hash_table = false;
- }
- if (!s1) return false;
- struct mg_tcpip_driver_stm32f_data *d =
- (struct mg_tcpip_driver_stm32f_data *) ifp->driver_data;
- uint8_t phy_addr = d == NULL ? 0 : d->phy_addr;
- uint8_t speed = MG_PHY_SPEED_10M;
- bool up = false, full_duplex = false;
- struct mg_phy phy = {eth_read_phy, eth_write_phy};
- up = mg_phy_up(&phy, phy_addr, &full_duplex, &speed);
- if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
- // tmp = reg with flags set to the most likely situation: 100M full-duplex
- // if(link is slow or half) set flags otherwise
- // reg = tmp
- uint32_t maccr = ETH->MACCR | MG_BIT(14) | MG_BIT(11); // 100M, Full-duplex
- if (speed == MG_PHY_SPEED_10M) maccr &= ~MG_BIT(14); // 10M
- if (full_duplex == false) maccr &= ~MG_BIT(11); // Half-duplex
- ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
- MG_DEBUG(("Link is %uM %s-duplex", maccr & MG_BIT(14) ? 100 : 10,
- maccr & MG_BIT(11) ? "full" : "half"));
- }
- return up;
- }
- #ifdef __riscv
- __attribute__((interrupt())) // For RISCV CH32V307, which share the same MAC
- #endif
- void ETH_IRQHandler(void);
- void ETH_IRQHandler(void) {
- if (ETH->DMASR & MG_BIT(6)) { // Frame received, loop
- ETH->DMASR = MG_BIT(16) | MG_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] & MG_BIT(31)) break; // exit when done
- if (((s_rxdesc[s_rxno][0] & (MG_BIT(8) | MG_BIT(9))) ==
- (MG_BIT(8) | MG_BIT(9))) &&
- !(s_rxdesc[s_rxno][0] & MG_BIT(15))) { // skip partial/errored frames
- uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (MG_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] = MG_BIT(31);
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
- }
- }
- // Cleanup flags
- ETH->DMASR = MG_BIT(16) // NIS, normal interrupt summary
- | MG_BIT(7); // Clear possible RBUS while processing
- ETH->DMARPDR = 0; // and resume RX
- }
- struct mg_tcpip_driver mg_tcpip_driver_stm32f = {
- mg_tcpip_driver_stm32f_init, mg_tcpip_driver_stm32f_tx, NULL,
- mg_tcpip_driver_stm32f_poll};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/stm32h.c"
- #endif
- #if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN || \
- MG_ENABLE_DRIVER_STM32N)
- // STM32H: vendor modded single-queue Synopsys v4.2
- // STM32N: dual-queue GbE Synopsys v5.2 with no hash table option, 64-bit AXI
- // MCXNx4x: dual-queue Synopsys v5.2 with no hash table option
- // RT1170: ENET_QOS: quad-queue Synopsys v5.1
- #if MG_ENABLE_DRIVER_STM32H
- #define SYNOPSYS_ENET_V5 0
- #define SYNOPSYS_ENET_SINGLEQ 1
- #define SYNOPSYS_ENET_NOHASHTABLE 0
- #define SYNOPSYS_ENET_GbE 0
- #elif MG_ENABLE_DRIVER_STM32N
- #define SYNOPSYS_ENET_V5 1
- #define SYNOPSYS_ENET_SINGLEQ 0
- #define SYNOPSYS_ENET_NOHASHTABLE 1
- #define SYNOPSYS_ENET_GbE 1
- #elif MG_ENABLE_DRIVER_MCXN
- #define SYNOPSYS_ENET_V5 1
- #define SYNOPSYS_ENET_SINGLEQ 0
- #define SYNOPSYS_ENET_NOHASHTABLE 1
- #define SYNOPSYS_ENET_GbE 0
- #endif
- struct synopsys_enet_qos {
- 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
- #if MG_ENABLE_DRIVER_STM32H
- #define ETH ((struct synopsys_enet_qos *) (uintptr_t) 0x40028000UL)
- #elif MG_ENABLE_DRIVER_STM32N
- #define ETH ((struct synopsys_enet_qos *) (uintptr_t) 0x48036000UL)
- #elif MG_ENABLE_DRIVER_MCXN
- #define ETH ((struct synopsys_enet_qos *) (uintptr_t) 0x40100000UL)
- #endif
- #define ETH_PKT_SIZE 1540 // Max frame size
- #define ETH_DESC_CNT 4 // Descriptors count
- #define ETH_DS 4 // Descriptor size (words)
- #if MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_STM32N
- #define CACHE_LINESZ 32 // must be a whole number of (d)words (see DESC_SZW)
- #ifndef SCB
- struct m7_scb {
- volatile uint32_t CPUID, RESERVED1[4], CCR, RESERVED2[145], DCIMVAC, DCISW,
- DCCMVAU, DCCMVAC, DCCSW, DCCIMVAC, DCCISW, RESERVED3[10], CACR,
- RESERVED4[3];
- };
- #define SCB ((struct m7_scb *) (uintptr_t) 0xE000ED00UL)
- #endif
- // ending ISB is not needed because we don't cache instructions in data space
- static inline void MG_CACHE_INVAL(uint8_t *addr, int32_t len) {
- #if MG_ENABLE_DRIVER_STM32H
- if ((SCB->CPUID & 0xfff0) != 0xc270) return; // not a Cortex-M7 => not an H7
- #endif
- if ((SCB->CCR & MG_BIT(16)) == 0) return; // cache not enabled
- MG_DSB();
- while (len > 0) {
- SCB->DCIMVAC = (uint32_t) addr;
- addr += CACHE_LINESZ;
- len -= CACHE_LINESZ;
- }
- MG_DSB();
- }
- static inline void MG_CACHE_FLUSH(uint8_t *addr, int32_t len) {
- #if MG_ENABLE_DRIVER_STM32H
- if ((SCB->CPUID & 0xfff0) != 0xc270) return; // not a Cortex-M7 => not an H7
- #endif
- if ((SCB->CCR & MG_BIT(16)) == 0) return; // cache not enabled
- MG_DSB();
- while (len > 0) {
- SCB->DCCMVAC = (uint32_t) addr;
- addr += CACHE_LINESZ;
- len -= CACHE_LINESZ;
- }
- MG_DSB();
- }
- #define ETH_RAM_ALIGNED MG_32BYTE_ALIGNED // depends on CACHE_LINESZ and ETH
- #define CACHE_ALIGN(x) \
- ((((size_t) (x)) + CACHE_LINESZ - 1) & ~(CACHE_LINESZ - 1))
- #define DESC_SZ CACHE_ALIGN(4 * ETH_DS) // grow descriptors to fit a line
- #define DESC_SZW (DESC_SZ / 4)
- #define BUFF_SZ CACHE_ALIGN(ETH_PKT_SIZE) // grow buffers to fit n lines
- #if MG_ENABLE_DRIVER_STM32H
- #define DESC_SKIPW (DESC_SZW - ETH_DS) // tell DMA the descriptor size, words
- #else
- // MG_ENABLE_DRIVER_STM32N, DMA is AXI and specs skip in 64-bit double-words
- #define DESC_SKIPW ((DESC_SZW - ETH_DS) / 2)
- #endif
- #else
- #define MG_CACHE_FLUSH(a, b)
- #define MG_CACHE_INVAL(a, b)
- #define ETH_RAM_ALIGNED MG_8BYTE_ALIGNED // depends on ETH DMA alone
- #define DESC_SZ 0
- #define DESC_SZW ETH_DS
- #define BUFF_SZ ETH_PKT_SIZE
- #define DESC_SKIPW 0 // no need to skip, as we're not aligning to cache lines
- #endif
- // array[rows][cols] = coldata coldata ... for all rows, keeps alignment
- static volatile uint32_t s_rxdesc[ETH_DESC_CNT][DESC_SZW] MG_ETH_RAM
- ETH_RAM_ALIGNED;
- static volatile uint32_t s_txdesc[ETH_DESC_CNT][DESC_SZW] MG_ETH_RAM
- ETH_RAM_ALIGNED;
- static uint8_t s_rxbuf[ETH_DESC_CNT][BUFF_SZ] MG_ETH_RAM ETH_RAM_ALIGNED;
- static uint8_t s_txbuf[ETH_DESC_CNT][BUFF_SZ] MG_ETH_RAM ETH_RAM_ALIGNED;
- static struct mg_tcpip_if *s_ifp; // MIP interface
- static uint16_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 |= MG_BIT(0);
- while (ETH->MACMDIOAR & MG_BIT(0)) (void) 0;
- return (uint16_t) ETH->MACMDIODR;
- }
- static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
- ETH->MACMDIODR = val;
- ETH->MACMDIOAR &= (0xF << 8);
- ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 1 << 2;
- ETH->MACMDIOAR |= MG_BIT(0);
- while (ETH->MACMDIOAR & MG_BIT(0)) (void) 0;
- }
- 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;
- uint8_t phy_addr = d == NULL ? 0 : d->phy_addr;
- uint8_t phy_conf = d == NULL ? MG_PHY_CLOCKS_MAC : d->phy_conf;
- // Init RX descriptors
- memset((char *) s_rxdesc, 0, sizeof(s_rxdesc)); // manual init
- 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] = MG_BIT(31) | MG_BIT(30) | MG_BIT(24); // OWN, IOC, BUF1V
- }
- MG_CACHE_FLUSH((uint8_t *) s_rxdesc, sizeof(s_rxdesc));
- // Init TX descriptors
- memset((char *) s_txdesc, 0, sizeof(s_txdesc)); // manual init
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
- }
- MG_CACHE_FLUSH((uint8_t *) s_txdesc, sizeof(s_txdesc));
- ETH->DMAMR |= MG_BIT(0); // Software reset
- for (int i = 0; i < 4; i++)
- (void) 0; // wait at least 4 clocks before reading
- while ((ETH->DMAMR & MG_BIT(0)) != 0) (void) 0; // Wait until done
- // Set MDC clock divider. Get user value, else, assume max freq
- int cr = (d == NULL || d->mdc_cr < 0) ? 7 : 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 |= MG_BIT(12); // AAL NOTE(scaprile): is this actually needed
- ETH->MACIER = 0; // Do not enable additional irq sources (reset value)
- ETH->MACTFCR = MG_BIT(7); // Disable zero-quanta pause
- #if !SYNOPSYS_ENET_V5
- ETH->MACPFR = MG_BIT(10); // Perfect filtering
- #endif
- struct mg_phy phy = {eth_read_phy, eth_write_phy};
- mg_phy_init(&phy, phy_addr, phy_conf);
- 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 = DESC_SKIPW << 18; // DSL (contiguous/sparse descriptor table)
- #if SYNOPSYS_ENET_V5
- MG_SET_BITS(ETH->DMACTCR, 0x3F << 16, MG_BIT(16));
- MG_SET_BITS(ETH->DMACRCR, 0x3F << 16, MG_BIT(16));
- #endif
- ETH->DMACIER = MG_BIT(6) | MG_BIT(15); // RIE, NIE
- ETH->MACCR = MG_BIT(0) | MG_BIT(1) | MG_BIT(13) | MG_BIT(14) |
- MG_BIT(15); // RE, TE, Duplex, Fast, (10/100)/Reserved
- #if SYNOPSYS_ENET_SINGLEQ
- ETH->MTLTQOMR |= MG_BIT(1); // TSF
- ETH->MTLRQOMR |= MG_BIT(5); // RSF
- #else
- ETH->MTLTQOMR |= (7 << 16) | MG_BIT(3) | MG_BIT(1); // 2KB Q0, TSF
- ETH->MTLRQOMR |= (7 << 20) | MG_BIT(5); // 2KB Q, RSF
- MG_SET_BITS(ETH->RESERVED6[3], 3, 2); // Enable RxQ0 (MAC_RXQ_CTRL0)
- #endif
- ETH->DMACTCR |= MG_BIT(0); // ST
- ETH->DMACRCR |= MG_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));
- return 0; // Frame is too big
- }
- MG_CACHE_INVAL((uint8_t *) &s_txdesc[s_txno], DESC_SZ);
- if ((s_txdesc[s_txno][3] & MG_BIT(31))) {
- ifp->nerr++;
- MG_ERROR(("No free descriptors: %u %08X %08X %08X", s_txno,
- s_txdesc[s_txno][3], ETH->DMACSR, ETH->DMACTCR));
- MG_CACHE_INVAL((uint8_t *) s_txdesc, sizeof(s_txdesc));
- 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
- MG_CACHE_FLUSH((uint8_t *) &s_txbuf[s_txno], BUFF_SZ);
- s_txdesc[s_txno][2] = (uint32_t) len; // Set data len
- s_txdesc[s_txno][3] = MG_BIT(28) | MG_BIT(29); // FD, LD
- s_txdesc[s_txno][3] |= MG_BIT(31); // Set OWN bit - let DMA take over
- MG_CACHE_FLUSH((uint8_t *) &s_txdesc[s_txno], DESC_SZ);
- if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
- }
- ETH->DMACSR |= MG_BIT(2) | MG_BIT(1); // Clear any prior TBU, TPS
- ETH->DMACTDTPR = (uint32_t) (uintptr_t) &s_txdesc[s_txno]; // and resume
- return len;
- (void) ifp;
- }
- static void mg_tcpip_driver_stm32h_update_hash_table(struct mg_tcpip_if *ifp) {
- #if SYNOPSYS_ENET_NOHASHTABLE
- ETH->MACPFR = MG_BIT(4); // Pass Multicast (pass all multicast frames)
- #else
- // TODO(): read database, rebuild hash table
- // add mDNS / DNS-SD multicast address
- ETH->MACA1LR = (uint32_t) mcast_addr[3] << 24 |
- (uint32_t) mcast_addr[2] << 16 |
- (uint32_t) mcast_addr[1] << 8 | (uint32_t) mcast_addr[0];
- ETH->MACA1HR = (uint32_t) mcast_addr[5] << 8 | (uint32_t) mcast_addr[4];
- ETH->MACA1HR |= MG_BIT(31); // AE
- #endif
- (void) ifp;
- }
- static bool mg_tcpip_driver_stm32h_poll(struct mg_tcpip_if *ifp, bool s1) {
- if (ifp->update_mac_hash_table) {
- mg_tcpip_driver_stm32h_update_hash_table(ifp);
- ifp->update_mac_hash_table = false;
- }
- if (!s1) return false;
- struct mg_tcpip_driver_stm32h_data *d =
- (struct mg_tcpip_driver_stm32h_data *) ifp->driver_data;
- uint8_t phy_addr = d == NULL ? 0 : d->phy_addr;
- uint8_t speed = MG_PHY_SPEED_10M;
- bool up = false, full_duplex = false;
- struct mg_phy phy = {eth_read_phy, eth_write_phy};
- up = mg_phy_up(&phy, phy_addr, &full_duplex, &speed);
- if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
- // tmp = reg with flags set to the most likely situation: 100M full-duplex
- // if(link is slow or half) set flags otherwise
- // reg = tmp
- uint32_t maccr = ETH->MACCR | MG_BIT(14) | MG_BIT(13); // 100M, Full-duplex
- #if SYNOPSYS_ENET_GbE
- if (speed == MG_PHY_SPEED_1000M) maccr &= ~MG_BIT(15); // 1000M
- #endif
- if (speed == MG_PHY_SPEED_10M) maccr &= ~MG_BIT(14); // 10M
- if (full_duplex == false) maccr &= ~MG_BIT(13); // Half-duplex
- ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
- MG_DEBUG(("Link is %uM %s-duplex", maccr & MG_BIT(14) ? 100 : 10,
- maccr & MG_BIT(13) ? "full" : "half"));
- }
- return up;
- }
- static uint32_t s_rxno;
- #if MG_ENABLE_DRIVER_MCXN
- void ETHERNET_IRQHandler(void);
- void ETHERNET_IRQHandler(void) {
- #elif MG_ENABLE_DRIVER_STM32H
- void ETH_IRQHandler(void);
- void ETH_IRQHandler(void) {
- #else
- void ETH1_IRQHandler(void);
- void ETH1_IRQHandler(void) {
- #endif
- if (ETH->DMACSR & MG_BIT(6)) { // Frame received, loop
- ETH->DMACSR = MG_BIT(15) | MG_BIT(6); // Clear flag
- for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
- MG_CACHE_INVAL((uint8_t *) &s_rxdesc[s_rxno], DESC_SZ);
- if (s_rxdesc[s_rxno][3] & MG_BIT(31)) break; // exit when done
- if (((s_rxdesc[s_rxno][3] & (MG_BIT(28) | MG_BIT(29))) ==
- (MG_BIT(28) | MG_BIT(29))) &&
- !(s_rxdesc[s_rxno][3] & MG_BIT(15))) { // skip partial/errored frames
- uint32_t len = s_rxdesc[s_rxno][3] & (MG_BIT(15) - 1);
- // MG_DEBUG(("%lx %lu %lx %08lx", s_rxno, len, s_rxdesc[s_rxno][3],
- // ETH->DMACSR));
- MG_CACHE_INVAL((uint8_t *) &s_rxbuf[s_rxno], BUFF_SZ);
- mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
- }
- s_rxdesc[s_rxno][3] =
- MG_BIT(31) | MG_BIT(30) | MG_BIT(24); // OWN, IOC, BUF1V
- MG_CACHE_FLUSH((uint8_t *) &s_rxdesc[s_rxno], DESC_SZ);
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
- }
- }
- ETH->DMACSR =
- MG_BIT(7) | MG_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_poll};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/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)
- #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 |= MG_BIT(0);
- while (EMAC->EMACMIIADDR & MG_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) | MG_BIT(1);
- EMAC->EMACMIIADDR |= MG_BIT(0);
- while (EMAC->EMACMIIADDR & MG_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 i, result = -1; // Invalid CR value
- if (sysclk < 25000000) {
- MG_ERROR(("SYSCLK too low"));
- } else {
- for (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;
- int i;
- s_ifp = ifp;
- // Init RX descriptors
- for (i = 0; i < ETH_DESC_CNT; i++) {
- s_rxdesc[i][0] = MG_BIT(31); // Own
- s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | MG_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 (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 |= MG_BIT(0); // Software reset
- while ((EMAC->EMACDMABUSMOD & MG_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 = MG_BIT(13) | MG_BIT(16) | MG_BIT(22) | MG_BIT(23) |
- // MG_BIT(25);
- EMAC->EMACIM = MG_BIT(3) | MG_BIT(9); // Mask timestamp & PMT IT
- EMAC->EMACFLOWCTL = MG_BIT(7); // Disable zero-quanta pause
- EMAC->EMACFRAMEFLTR = MG_BIT(10); // Perfect filtering
- // EMAC->EMACPC defaults to internal PHY (EPHY) in MMI mode
- emac_write_phy(EPHY_ADDR, EPHYBMCR, MG_BIT(15)); // Reset internal PHY (EPHY)
- emac_write_phy(EPHY_ADDR, EPHYBMCR, MG_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 = MG_BIT(6) | MG_BIT(16); // RIE, NIE
- EMAC->EMACCFG =
- MG_BIT(2) | MG_BIT(3) | MG_BIT(11) | MG_BIT(14); // RE, TE, Duplex, Fast
- EMAC->EMACDMAOPMODE =
- MG_BIT(1) | MG_BIT(13) | MG_BIT(21) | MG_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];
- 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] & MG_BIT(31))) {
- ifp->nerr++;
- 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] =
- MG_BIT(20) | MG_BIT(28) | MG_BIT(29) | MG_BIT(30); // Chain,FS,LS,IC
- s_txdesc[s_txno][0] |= MG_BIT(31); // Set OWN bit - let DMA take over
- if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
- }
- EMAC->EMACDMARIS = MG_BIT(2) | MG_BIT(5); // Clear any prior TU/UNF
- EMAC->EMACTXPOLLD = 0; // and resume
- return len;
- }
- static void mg_tcpip_driver_tm4c_update_hash_table(struct mg_tcpip_if *ifp) {
- // TODO(): read database, rebuild hash table
- // add mDNS / DNS-SD multicast address
- EMAC->EMACADDR1L = (uint32_t) mcast_addr[3] << 24 |
- (uint32_t) mcast_addr[2] << 16 |
- (uint32_t) mcast_addr[1] << 8 | (uint32_t) mcast_addr[0];
- EMAC->EMACADDR1H = (uint32_t) mcast_addr[5] << 8 | (uint32_t) mcast_addr[4];
- EMAC->EMACADDR1H |= MG_BIT(31); // AE
- (void) ifp;
- }
- static bool mg_tcpip_driver_tm4c_poll(struct mg_tcpip_if *ifp, bool s1) {
- if (ifp->update_mac_hash_table) {
- mg_tcpip_driver_tm4c_update_hash_table(ifp);
- ifp->update_mac_hash_table = false;
- }
- if (!s1) return false;
- uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR);
- bool up = (bmsr & MG_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);
- // tmp = reg with flags set to the most likely situation: 100M full-duplex
- // if(link is slow or half) set flags otherwise
- // reg = tmp
- uint32_t emaccfg =
- EMAC->EMACCFG | MG_BIT(14) | MG_BIT(11); // 100M, Full-duplex
- if (sts & MG_BIT(1)) emaccfg &= ~MG_BIT(14); // 10M
- if ((sts & MG_BIT(2)) == 0) emaccfg &= ~MG_BIT(11); // Half-duplex
- EMAC->EMACCFG = emaccfg; // IRQ handler does not fiddle with this register
- MG_DEBUG(("Link is %uM %s-duplex", emaccfg & MG_BIT(14) ? 100 : 10,
- emaccfg & MG_BIT(11) ? "full" : "half"));
- }
- return up;
- }
- void EMAC0_IRQHandler(void);
- static uint32_t s_rxno;
- void EMAC0_IRQHandler(void) {
- int i;
- if (EMAC->EMACDMARIS & MG_BIT(6)) { // Frame received, loop
- EMAC->EMACDMARIS = MG_BIT(16) | MG_BIT(6); // Clear flag
- for (i = 0; i < 10; i++) { // read as they arrive but not forever
- if (s_rxdesc[s_rxno][0] & MG_BIT(31)) break; // exit when done
- if (((s_rxdesc[s_rxno][0] & (MG_BIT(8) | MG_BIT(9))) ==
- (MG_BIT(8) | MG_BIT(9))) &&
- !(s_rxdesc[s_rxno][0] & MG_BIT(15))) { // skip partial/errored frames
- uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (MG_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] = MG_BIT(31);
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
- }
- }
- EMAC->EMACDMARIS = MG_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_poll};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/tms570.c"
- #endif
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TMS570) && MG_ENABLE_DRIVER_TMS570
- struct tms570_emac_ctrl {
- volatile uint32_t REVID, SOFTRESET, RESERVED1[1], INTCONTROL, C0RXTHRESHEN,
- C0RXEN, C0TXEN, C0MISCEN, RESERVED2[8],
- C0RXTHRESHSTAT, C0RXSTAT, C0TXSTAT, C0MISCSTAT,
- RESERVED3[8],
- C0RXIMAX, C0TXIMAX;
- };
- struct tms570_emac {
- volatile uint32_t TXREVID, TXCONTROL, TXTEARDOWN, RESERVED1[1], RXREVID,
- RXCONTROL, RXTEARDOWN, RESERVED2[25], TXINTSTATRAW,TXINTSTATMASKED,
- TXINTMASKSET, TXINTMASKCLEAR, MACINVECTOR, MACEOIVECTOR, RESERVED8[2], RXINTSTATRAW,
- RXINTSTATMASKED, RXINTMASKSET, RXINTMASKCLEAR, MACINTSTATRAW, MACINTSTATMASKED,
- MACINTMASKSET, MACINTMASKCLEAR, RESERVED3[16], RXMBPENABLE, RXUNICASTSET,
- RXUNICASTCLEAR, RXMAXLEN, RXBUFFEROFFSET, RXFILTERLOWTHRESH, RESERVED9[2], RXFLOWTHRESH[8],
- RXFREEBUFFER[8], MACCONTROL, MACSTATUS, EMCONTROL, FIFOCONTROL, MACCONFIG,
- SOFTRESET, RESERVED4[22], MACSRCADDRLO, MACSRCADDRHI, MACHASH1, MACHASH2,
- BOFFTEST, TPACETEST, RXPAUSE, TXPAUSE, RESERVED5[4], RXGOODFRAMES, RXBCASTFRAMES,
- RXMCASTFRAMES, RXPAUSEFRAMES, RXCRCERRORS, RXALIGNCODEERRORS, RXOVERSIZED,
- RXJABBER, RXUNDERSIZED, RXFRAGMENTS, RXFILTERED, RXQOSFILTERED, RXOCTETS,
- TXGOODFRAMES, TXBCASTFRAMES, TXMCASTFRAMES, TXPAUSEFRAMES, TXDEFERRED,
- TXCOLLISION, TXSINGLECOLL, TXMULTICOLL, TXEXCESSIVECOLL, TXLATECOLL,
- TXUNDERRUN, TXCARRIERSENSE, TXOCTETS, FRAME64, FRAME65T127, FRAME128T255,
- FRAME256T511, FRAME512T1023, FRAME1024TUP, NETOCTETS, RXSOFOVERRUNS,
- RXMOFOVERRUNS, RXDMAOVERRUNS, RESERVED6[156], MACADDRLO, MACADDRHI,
- MACINDEX, RESERVED7[61], TXHDP[8], RXHDP[8], TXCP[8], RXCP[8];
- };
- struct tms570_mdio {
- volatile uint32_t REVID, CONTROL, ALIVE, LINK, LINKINTRAW, LINKINTMASKED,
- RESERVED1[2], USERINTRAW, USERINTMASKED, USERINTMASKSET, USERINTMASKCLEAR,
- RESERVED2[20], USERACCESS0, USERPHYSEL0, USERACCESS1, USERPHYSEL1;
- };
- #define SWAP32(x) ( (((x) & 0x000000FF) << 24) | \
- (((x) & 0x0000FF00) << 8) | \
- (((x) & 0x00FF0000) >> 8) | \
- (((x) & 0xFF000000) >> 24) )
- #undef EMAC
- #undef EMAC_CTRL
- #undef MDIO
- #define EMAC ((struct tms570_emac *) (uintptr_t) 0xFCF78000)
- #define EMAC_CTRL ((struct tms570_emac_ctrl *) (uintptr_t) 0xFCF78800)
- #define MDIO ((struct tms570_mdio *) (uintptr_t) 0xFCF78900)
- #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_txdesc[ETH_DESC_CNT][ETH_DS]
- __attribute__((section(".ETH_CPPI"), aligned(4))); // TX descriptors
- static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]
- __attribute__((section(".ETH_CPPI"), aligned(4))); // RX descriptors
- static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]
- __attribute__((aligned(4))); // RX ethernet buffers
- static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]
- __attribute__((aligned(4))); // TX ethernet buffers
- static struct mg_tcpip_if *s_ifp; // MIP interface
- static uint16_t emac_read_phy(uint8_t addr, uint8_t reg) {
- while(MDIO->USERACCESS0 & MG_BIT(31)) (void) 0;
- MDIO->USERACCESS0 = MG_BIT(31) | ((reg & 0x1f) << 21) |
- ((addr & 0x1f) << 16);
- while(MDIO->USERACCESS0 & MG_BIT(31)) (void) 0;
- return MDIO->USERACCESS0 & 0xffff;
- }
- static void emac_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
- while(MDIO->USERACCESS0 & MG_BIT(31)) (void) 0;
- MDIO->USERACCESS0 = MG_BIT(31) | MG_BIT(30) | ((reg & 0x1f) << 21) |
- ((addr & 0x1f) << 16) | (val & 0xffff);
- while(MDIO->USERACCESS0 & MG_BIT(31)) (void) 0;
- }
- static bool mg_tcpip_driver_tms570_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_tms570_data *d =
- (struct mg_tcpip_driver_tms570_data *) ifp->driver_data;
- s_ifp = ifp;
- EMAC_CTRL->SOFTRESET = MG_BIT(0); // Reset the EMAC Control Module
- while(EMAC_CTRL->SOFTRESET & MG_BIT(0)) (void) 0; // wait
- EMAC->SOFTRESET = MG_BIT(0); // Reset the EMAC Module
- while(EMAC->SOFTRESET & MG_BIT(0)) (void) 0;
- EMAC->MACCONTROL = 0;
- EMAC->RXCONTROL = 0;
- EMAC->TXCONTROL = 0;
- // Initialize all the header descriptor pointer registers
- uint32_t i;
- for(i = 0; i < ETH_DESC_CNT; i++) {
- EMAC->RXHDP[i] = 0;
- EMAC->TXHDP[i] = 0;
- EMAC->RXCP[i] = 0;
- EMAC->TXCP[i] = 0;
- ///EMAC->RXFREEBUFFER[i] = 0xff;
- }
- // Clear the interrupt enable for all the channels
- EMAC->TXINTMASKCLEAR = 0xff;
- EMAC->RXINTMASKCLEAR = 0xff;
- EMAC->MACHASH1 = 0;
- EMAC->MACHASH2 = 0;
- EMAC->RXBUFFEROFFSET = 0;
- EMAC->RXUNICASTCLEAR = 0xff;
- EMAC->RXUNICASTSET = 0;
- EMAC->RXMBPENABLE = 0;
- // init MDIO
- // MDIO_CLK frequency = VCLK3/(CLKDIV + 1). (MDIO must be between 1.0 - 2.5Mhz)
- uint32_t clkdiv = 75; // VCLK is configured to 75Mhz
- // CLKDIV, ENABLE, PREAMBLE, FAULTENB
- MDIO->CONTROL = (clkdiv - 1) | MG_BIT(30) | MG_BIT(20) | MG_BIT(18);
- volatile int delay = 0xfff;
- while (delay-- != 0) (void) 0;
- struct mg_phy phy = {emac_read_phy, emac_write_phy};
- mg_phy_init(&phy, d->phy_addr, MG_PHY_CLOCKS_MAC);
- uint32_t channel;
- for (channel = 0; channel < 8; channel++) {
- EMAC->MACINDEX = channel;
- EMAC->MACADDRHI = ifp->mac[0] | (ifp->mac[1] << 8) | (ifp->mac[2] << 16) |
- (ifp->mac[3] << 24);
- EMAC->MACADDRLO = ifp->mac[4] | (ifp->mac[5] << 8) | MG_BIT(20) |
- MG_BIT(19) | (channel << 16);
- }
- EMAC->RXUNICASTSET = 1; // accept unicast frames;
- EMAC->RXMBPENABLE |= MG_BIT(30) | MG_BIT(13); // CRC, broadcast
- // Initialize the descriptors
- for (i = 0; i < ETH_DESC_CNT; i++) {
- if (i < ETH_DESC_CNT - 1) {
- s_txdesc[i][0] = 0;
- s_rxdesc[i][0] = SWAP32(((uint32_t) &s_rxdesc[i + 1][0]));
- }
- s_txdesc[i][1] = SWAP32(((uint32_t) s_txbuf[i]));
- s_rxdesc[i][1] = SWAP32(((uint32_t) s_rxbuf[i]));
- s_txdesc[i][2] = 0;
- s_rxdesc[i][2] = SWAP32(ETH_PKT_SIZE);
- s_txdesc[i][3] = 0;
- s_rxdesc[i][3] = SWAP32(MG_BIT(29)); // OWN
- }
- s_txdesc[ETH_DESC_CNT - 1][0] = 0;
- s_rxdesc[ETH_DESC_CNT - 1][0] = 0;
-
- EMAC->MACCONTROL = MG_BIT(5) | MG_BIT(0); // Enable MII, Full-duplex
- //EMAC->TXINTMASKSET = 1; // Enable TX interrupt
- EMAC->RXINTMASKSET = 1; // Enable RX interrupt
- //EMAC_CTRL->C0TXEN = 1; // TX completion interrupt
- EMAC_CTRL->C0RXEN = 1; // RX completion interrupt
- EMAC->TXCONTROL = 1; // TXEN
- EMAC->RXCONTROL = 1; // RXEN
- EMAC->RXHDP[0] = (uint32_t) &s_rxdesc[0][0];
- return true;
- }
- static uint32_t s_txno;
- static size_t mg_tcpip_driver_tms570_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][3] & SWAP32(MG_BIT(29)))) {
- ifp->nerr++;
- MG_ERROR(("No descriptors available"));
- len = 0; // fail
- } else {
- memcpy(s_txbuf[s_txno], buf, len); // Copy data
- if (len < 128) {
- memset(s_txbuf[s_txno] + len, 0, 128 - len);
- len = 128;
- }
- s_txdesc[s_txno][2] = SWAP32((uint32_t) len); // Set data len
- s_txdesc[s_txno][3] =
- SWAP32(MG_BIT(31) | MG_BIT(30) | MG_BIT(29) | len); // SOP, EOP, OWN, length
-
- while(EMAC->TXHDP[0] != 0) (void) 0;
- EMAC->TXHDP[0] = (uint32_t) &s_txdesc[s_txno][0];
- if(++s_txno == ETH_DESC_CNT) {
- s_txno = 0;
- }
- }
- return len;
- (void) ifp;
- }
- static void mg_tcpip_driver_tms570_update_hash_table(struct mg_tcpip_if *ifp) {
- // TODO(): read database, rebuild hash table
- // Setting Hash Index for 01:00:5e:00:00:fb (multicast)
- // using TMS570 XOR method (32.5.37).
- // computed hash is 55, which means bit 23 (55 - 32) in
- // HASH2 register must be set
- EMAC->MACHASH2 = MG_BIT(23);
- EMAC->RXMBPENABLE = MG_BIT(5); // enable hash filtering
- (void) ifp;
- }
- static bool mg_tcpip_driver_tms570_poll(struct mg_tcpip_if *ifp, bool s1) {
- if (ifp->update_mac_hash_table) {
- mg_tcpip_driver_tms570_update_hash_table(ifp);
- ifp->update_mac_hash_table = false;
- }
- if (!s1) return false;
- struct mg_tcpip_driver_tms570_data *d =
- (struct mg_tcpip_driver_tms570_data *) ifp->driver_data;
- uint8_t speed = MG_PHY_SPEED_10M;
- bool up = false, full_duplex = false;
- struct mg_phy phy = {emac_read_phy, emac_write_phy};
- if (!s1) return false;
- up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed);
- if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) {
- // link state just went up
- MG_DEBUG(("Link is %uM %s-duplex", speed == MG_PHY_SPEED_10M ? 10 : 100,
- full_duplex ? "full" : "half"));
- }
- return up;
- }
- #pragma CODE_STATE(EMAC_TX_IRQHandler, 32)
- #pragma INTERRUPT(EMAC_TX_IRQHandler, IRQ)
- void EMAC_TX_IRQHandler(void) {
- uint32_t status = EMAC_CTRL->C0TXSTAT;
- if (status & 1) { // interrupt caused on channel 0
- while(s_txdesc[s_txno][3] & SWAP32(MG_BIT(29))) (void) 0;
- EMAC->TXCP[0] = (uint32_t) &s_txdesc[s_txno][0];
- }
- //Write the DMA end of interrupt vector
- EMAC->MACEOIVECTOR = 2;
- }
- static uint32_t s_rxno;
- #pragma CODE_STATE(EMAC_RX_IRQHandler, 32)
- #pragma INTERRUPT(EMAC_RX_IRQHandler, IRQ)
- void EMAC_RX_IRQHandler(void) {
- uint32_t status = EMAC_CTRL->C0RXSTAT;
- if (status & 1) { // Frame received, loop
- uint32_t i;
- //MG_INFO(("RX interrupt"));
- for (i = 0; i < 10; i++) { // read as they arrive but not forever
- if (s_rxdesc[s_rxno][3] & SWAP32(MG_BIT(29))) break;
- uint32_t len = SWAP32(s_rxdesc[s_rxno][3]) & 0xffff;
- //MG_INFO(("recv len: %d", len));
- //mg_hexdump(s_rxbuf[s_rxno], len);
- mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
- uint32_t flags = s_rxdesc[s_rxno][3];
- s_rxdesc[s_rxno][3] = SWAP32(MG_BIT(29));
- s_rxdesc[s_rxno][2] = SWAP32(ETH_PKT_SIZE);
- EMAC->RXCP[0] = (uint32_t) &s_rxdesc[s_rxno][0];
- if (flags & SWAP32(MG_BIT(28))) {
- //MG_INFO(("EOQ detected"));
- EMAC->RXHDP[0] = (uint32_t) &s_rxdesc[0][0];
- }
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
- }
- }
- //Write the DMA end of interrupt vector
- EMAC->MACEOIVECTOR = 1;
- }
- struct mg_tcpip_driver mg_tcpip_driver_tms570 = {mg_tcpip_driver_tms570_init,
- mg_tcpip_driver_tms570_tx, NULL,
- mg_tcpip_driver_tms570_poll};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/w5100.c"
- #endif
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_W5100) && MG_ENABLE_DRIVER_W5100
- static void w5100_txn(struct mg_tcpip_spi *s, uint16_t addr, bool wr, void *buf,
- size_t len) {
- uint8_t control = wr ? 0xF0 : 0x0F;
- uint8_t cmd[] = {control, (uint8_t) (addr >> 8), (uint8_t) (addr & 255)};
- s->begin(s->spi);
- s->txn(s->spi, cmd, NULL, sizeof(cmd));
- if (wr) s->txn(s->spi, (uint8_t *) buf, NULL, len);
- if (!wr) s->txn(s->spi, NULL, (uint8_t *) buf, len);
- s->end(s->spi);
- }
- // clang-format off
- static void w5100_wn(struct mg_tcpip_spi *s, uint16_t addr, void *buf, size_t len) { w5100_txn(s, addr, true, buf, len); }
- static void w5100_w1(struct mg_tcpip_spi *s, uint16_t addr, uint8_t val) { w5100_wn(s, addr, &val, 1); }
- static void w5100_w2(struct mg_tcpip_spi *s, uint16_t addr, uint16_t val) { uint8_t buf[2] = {(uint8_t) (val >> 8), (uint8_t) (val & 255)}; w5100_wn(s, addr, buf, sizeof(buf)); }
- static void w5100_rn(struct mg_tcpip_spi *s, uint16_t addr, void *buf, size_t len) { w5100_txn(s, addr, false, buf, len); }
- static uint8_t w5100_r1(struct mg_tcpip_spi *s, uint16_t addr) { uint8_t r = 0; w5100_rn(s, addr, &r, 1); return r; }
- static uint16_t w5100_r2(struct mg_tcpip_spi *s, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5100_rn(s, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); }
- // clang-format on
- static size_t w5100_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 = w5100_r2(s, 0x426)) > n) n = n2; // Until it is stable
- if (n > 0) {
- uint16_t ptr = w5100_r2(s, 0x428); // Get read pointer
- if (n <= len + 2 && n > 1) {
- r = (uint16_t) (n - 2);
- }
- uint16_t rxbuf_size = (1 << (w5100_r1(s, 0x1a) & 3)) * 1024;
- uint16_t rxbuf_addr = 0x6000;
- uint16_t ptr_ofs = (ptr + 2) & (rxbuf_size - 1);
- if (ptr_ofs + r < rxbuf_size) {
- w5100_rn(s, rxbuf_addr + ptr_ofs, buf, r);
- } else {
- uint16_t remaining_len = rxbuf_size - ptr_ofs;
- w5100_rn(s, rxbuf_addr + ptr_ofs, buf, remaining_len);
- w5100_rn(s, rxbuf_addr, buf + remaining_len, r - remaining_len);
- }
- w5100_w2(s, 0x428, (uint16_t) (ptr + n));
- w5100_w1(s, 0x401, 0x40); // Sock0 CR -> RECV
- }
- return r;
- }
- static size_t w5100_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 i, n = 0, ptr = 0, len = (uint16_t) buflen;
- while (n < len) n = w5100_r2(s, 0x420); // Wait for space
- ptr = w5100_r2(s, 0x424); // Get write pointer
- uint16_t txbuf_size = (1 << (w5100_r1(s, 0x1b) & 3)) * 1024;
- uint16_t ptr_ofs = ptr & (txbuf_size - 1);
- uint16_t txbuf_addr = 0x4000;
- if (ptr_ofs + len > txbuf_size) {
- uint16_t size = txbuf_size - ptr_ofs;
- w5100_wn(s, txbuf_addr + ptr_ofs, (char *) buf, size);
- w5100_wn(s, txbuf_addr, (char *) buf + size, len - size);
- } else {
- w5100_wn(s, txbuf_addr + ptr_ofs, (char *) buf, len);
- }
- w5100_w2(s, 0x424, (uint16_t) (ptr + len)); // Advance write pointer
- w5100_w1(s, 0x401, 0x20); // Sock0 CR -> SEND
- for (i = 0; i < 40; i++) {
- uint8_t ir = w5100_r1(s, 0x402); // Read S0 IR
- if (ir == 0) continue;
- // printf("IR %d, len=%d, free=%d, ptr %d\n", ir, (int) len, (int) n, ptr);
- w5100_w1(s, 0x402, 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 w5100_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
- s->end(s->spi);
- w5100_w1(s, 0, 0x80); // Reset chip: CR -> 0x80
- w5100_w1(s, 0x72, 0x53); // CR PHYLCKR -> unlock PHY
- w5100_w1(s, 0x46, 0); // CR PHYCR0 -> autonegotiation
- w5100_w1(s, 0x47, 0); // CR PHYCR1 -> reset
- w5100_w1(s, 0x72, 0x00); // CR PHYLCKR -> lock PHY
- w5100_wn(s, 0x09, ifp->mac, 6); // SHAR
- w5100_w1(s, 0x1a, 6); // Sock0 RX buf size - 4KB
- w5100_w1(s, 0x1b, 6); // Sock0 TX buf size - 4KB
- w5100_w1(s, 0x400, 0x44); // Sock0 MR -> MACRAW, MAC filter
- w5100_w1(s, 0x401, 1); // Sock0 CR -> OPEN
- return w5100_r1(s, 0x403) == 0x42; // Sock0 SR == MACRAW
- }
- static bool w5100_poll(struct mg_tcpip_if *ifp, bool s1) {
- struct mg_tcpip_spi *spi = (struct mg_tcpip_spi *) ifp->driver_data;
- return s1 ? w5100_r1(spi, 0x3c /* PHYSR */) & 1
- : false; // Bit 0 of PHYSR is LNK (0 - down, 1 - up)
- }
- struct mg_tcpip_driver mg_tcpip_driver_w5100 = {w5100_init, w5100_tx, w5100_rx,
- w5100_poll};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/w5500.c"
- #endif
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_W5500) && MG_ENABLE_DRIVER_W5500
- 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 cmd[] = {(uint8_t) (addr >> 8), (uint8_t) (addr & 255),
- (uint8_t) ((block << 3) | (wr ? 4 : 0))};
- s->begin(s->spi);
- s->txn(s->spi, cmd, NULL, sizeof(cmd));
- if (wr) s->txn(s->spi, (uint8_t *) buf, NULL, len);
- if (!wr) s->txn(s->spi, NULL, (uint8_t *) buf, len);
- 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 i, ptr, n = 0, len = (uint16_t) buflen;
- while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space
- 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 (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_poll(struct mg_tcpip_if *ifp, bool s1) {
- struct mg_tcpip_spi *spi = (struct mg_tcpip_spi *) ifp->driver_data;
- return s1 ? w5500_r1(spi, W5500_CR, 0x2e /* PHYCFGR */) & 1
- : false; // 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_poll};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/xmc.c"
- #endif
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC
- struct ETH_GLOBAL_TypeDef {
- volatile uint32_t MAC_CONFIGURATION, MAC_FRAME_FILTER, HASH_TABLE_HIGH,
- HASH_TABLE_LOW, GMII_ADDRESS, GMII_DATA, FLOW_CONTROL, VLAN_TAG, VERSION,
- DEBUG, REMOTE_WAKE_UP_FRAME_FILTER, PMT_CONTROL_STATUS, RESERVED[2],
- INTERRUPT_STATUS, INTERRUPT_MASK, MAC_ADDRESS0_HIGH, MAC_ADDRESS0_LOW,
- MAC_ADDRESS1_HIGH, MAC_ADDRESS1_LOW, MAC_ADDRESS2_HIGH, MAC_ADDRESS2_LOW,
- MAC_ADDRESS3_HIGH, MAC_ADDRESS3_LOW, RESERVED1[40], MMC_CONTROL,
- MMC_RECEIVE_INTERRUPT, MMC_TRANSMIT_INTERRUPT, MMC_RECEIVE_INTERRUPT_MASK,
- MMC_TRANSMIT_INTERRUPT_MASK, TX_STATISTICS[26], RESERVED2,
- RX_STATISTICS_1[26], RESERVED3[6], MMC_IPC_RECEIVE_INTERRUPT_MASK,
- RESERVED4, MMC_IPC_RECEIVE_INTERRUPT, RESERVED5, RX_STATISTICS_2[30],
- RESERVED7[286], TIMESTAMP_CONTROL, SUB_SECOND_INCREMENT,
- SYSTEM_TIME_SECONDS, SYSTEM_TIME_NANOSECONDS, SYSTEM_TIME_SECONDS_UPDATE,
- SYSTEM_TIME_NANOSECONDS_UPDATE, TIMESTAMP_ADDEND, TARGET_TIME_SECONDS,
- TARGET_TIME_NANOSECONDS, SYSTEM_TIME_HIGHER_WORD_SECONDS,
- TIMESTAMP_STATUS, PPS_CONTROL, RESERVED8[564], BUS_MODE,
- TRANSMIT_POLL_DEMAND, RECEIVE_POLL_DEMAND,
- RECEIVE_DESCRIPTOR_LIST_ADDRESS, TRANSMIT_DESCRIPTOR_LIST_ADDRESS, STATUS,
- OPERATION_MODE, INTERRUPT_ENABLE,
- MISSED_FRAME_AND_BUFFER_OVERFLOW_COUNTER,
- RECEIVE_INTERRUPT_WATCHDOG_TIMER, RESERVED9, AHB_STATUS, RESERVED10[6],
- CURRENT_HOST_TRANSMIT_DESCRIPTOR, CURRENT_HOST_RECEIVE_DESCRIPTOR,
- CURRENT_HOST_TRANSMIT_BUFFER_ADDRESS, CURRENT_HOST_RECEIVE_BUFFER_ADDRESS,
- HW_FEATURE;
- };
- #undef ETH0
- #define ETH0 ((struct ETH_GLOBAL_TypeDef *) 0x5000C000UL)
- #define ETH_PKT_SIZE 1536 // Max frame size
- #define ETH_DESC_CNT 4 // Descriptors count
- #define ETH_DS 4 // Descriptor size (words)
- static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM;
- static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM;
- static uint32_t s_rxdesc[ETH_DESC_CNT]
- [ETH_DS] MG_ETH_RAM; // RX descriptors
- static uint32_t s_txdesc[ETH_DESC_CNT]
- [ETH_DS] MG_ETH_RAM; // TX descriptors
- 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 { MG_PHY_ADDR = 0, MG_PHYREG_BCR = 0, MG_PHYREG_BSR = 1 };
- static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) {
- ETH0->GMII_ADDRESS = (ETH0->GMII_ADDRESS & 0x3c) | ((uint32_t) addr << 11) |
- ((uint32_t) reg << 6) | 1;
- while ((ETH0->GMII_ADDRESS & 1) != 0) (void) 0;
- return (uint16_t) (ETH0->GMII_DATA & 0xffff);
- }
- static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
- ETH0->GMII_DATA = val;
- ETH0->GMII_ADDRESS = (ETH0->GMII_ADDRESS & 0x3c) | ((uint32_t) addr << 11) |
- ((uint32_t) reg << 6) | 3;
- while ((ETH0->GMII_ADDRESS & 1) != 0) (void) 0;
- }
- static uint32_t get_clock_rate(struct mg_tcpip_driver_xmc_data *d) {
- if (d->mdc_cr == -1) {
- // assume ETH clock is 60MHz by default
- // then according to 13.2.8.1, we need to set value 3
- return 3;
- }
- return d->mdc_cr;
- }
- static bool mg_tcpip_driver_xmc_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_xmc_data *d =
- (struct mg_tcpip_driver_xmc_data *) ifp->driver_data;
- s_ifp = ifp;
- // reset MAC
- ETH0->BUS_MODE |= 1;
- while (ETH0->BUS_MODE & 1) (void) 0;
- // set clock rate
- ETH0->GMII_ADDRESS = get_clock_rate(d) << 2;
- // init phy
- struct mg_phy phy = {eth_read_phy, eth_write_phy};
- mg_phy_init(&phy, d->phy_addr, MG_PHY_CLOCKS_MAC);
- // configure MAC: DO, DM, FES, TC
- ETH0->MAC_CONFIGURATION = MG_BIT(13) | MG_BIT(11) | MG_BIT(14) | MG_BIT(24);
- // set the MAC address
- ETH0->MAC_ADDRESS0_HIGH = MG_U32(0, 0, ifp->mac[5], ifp->mac[4]);
- ETH0->MAC_ADDRESS0_LOW =
- MG_U32(ifp->mac[3], ifp->mac[2], ifp->mac[1], ifp->mac[0]);
- // Configure the receive filter
- ETH0->MAC_FRAME_FILTER = MG_BIT(10); // Perfect filter
- // Disable flow control
- ETH0->FLOW_CONTROL = 0;
- // Enable store and forward mode
- ETH0->OPERATION_MODE = MG_BIT(25) | MG_BIT(21); // RSF, TSF
- // Configure DMA bus mode (AAL, USP, RPBL, PBL)
- ETH0->BUS_MODE = MG_BIT(25) | MG_BIT(23) | (32 << 17) | (32 << 8);
- // init RX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_rxdesc[i][0] = MG_BIT(31); // OWN descriptor
- s_rxdesc[i][1] = MG_BIT(14) | ETH_PKT_SIZE;
- s_rxdesc[i][2] = (uint32_t) s_rxbuf[i];
- if (i == ETH_DESC_CNT - 1) {
- s_rxdesc[i][3] = (uint32_t) &s_rxdesc[0][0];
- } else {
- s_rxdesc[i][3] = (uint32_t) &s_rxdesc[i + 1][0];
- }
- }
- ETH0->RECEIVE_DESCRIPTOR_LIST_ADDRESS = (uint32_t) &s_rxdesc[0][0];
- // init TX descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_txdesc[i][0] = MG_BIT(30) | MG_BIT(20);
- s_txdesc[i][2] = (uint32_t) s_txbuf[i];
- if (i == ETH_DESC_CNT - 1) {
- s_txdesc[i][3] = (uint32_t) &s_txdesc[0][0];
- } else {
- s_txdesc[i][3] = (uint32_t) &s_txdesc[i + 1][0];
- }
- }
- ETH0->TRANSMIT_DESCRIPTOR_LIST_ADDRESS = (uint32_t) &s_txdesc[0][0];
- // Clear interrupts
- ETH0->STATUS = 0xFFFFFFFF;
- // Disable MAC interrupts
- ETH0->MMC_TRANSMIT_INTERRUPT_MASK = 0xFFFFFFFF;
- ETH0->MMC_RECEIVE_INTERRUPT_MASK = 0xFFFFFFFF;
- ETH0->MMC_IPC_RECEIVE_INTERRUPT_MASK = 0xFFFFFFFF;
- ETH0->INTERRUPT_MASK = MG_BIT(9) | MG_BIT(3); // TSIM, PMTIM
- // Enable interrupts (NIE, RIE, TIE)
- ETH0->INTERRUPT_ENABLE = MG_BIT(16) | MG_BIT(6) | MG_BIT(0);
- // Enable MAC transmission and reception (TE, RE)
- ETH0->MAC_CONFIGURATION |= MG_BIT(3) | MG_BIT(2);
- // Enable DMA transmission and reception (ST, SR)
- ETH0->OPERATION_MODE |= MG_BIT(13) | MG_BIT(1);
- return true;
- }
- static size_t mg_tcpip_driver_xmc_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] & MG_BIT(31))) {
- ifp->nerr++;
- MG_ERROR(("No free descriptors"));
- len = 0; // All descriptors are busy, fail
- } else {
- memcpy(s_txbuf[s_txno], buf, len);
- s_txdesc[s_txno][1] = len;
- // Table 13-19 Transmit Descriptor Word 0 (IC, LS, FS, TCH)
- s_txdesc[s_txno][0] = MG_BIT(30) | MG_BIT(29) | MG_BIT(28) | MG_BIT(20);
- s_txdesc[s_txno][0] |= MG_BIT(31); // OWN bit: handle control to DMA
- if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
- }
- // Resume processing
- ETH0->STATUS = MG_BIT(2); // clear Transmit unavailable
- ETH0->TRANSMIT_POLL_DEMAND = 0;
- return len;
- }
- static void mg_tcpip_driver_xmc_update_hash_table(struct mg_tcpip_if *ifp) {
- // TODO(): read database, rebuild hash table
- // set the multicast address filter
- ETH0->MAC_ADDRESS1_HIGH =
- MG_U32(0, 0, mcast_addr[5], mcast_addr[4]) | MG_BIT(31);
- ETH0->MAC_ADDRESS1_LOW =
- MG_U32(mcast_addr[3], mcast_addr[2], mcast_addr[1], mcast_addr[0]);
- (void) ifp;
- }
- static bool mg_tcpip_driver_xmc_poll(struct mg_tcpip_if *ifp, bool s1) {
- if (ifp->update_mac_hash_table) {
- mg_tcpip_driver_xmc_update_hash_table(ifp);
- ifp->update_mac_hash_table = false;
- }
- if (!s1) return false;
- struct mg_tcpip_driver_xmc_data *d =
- (struct mg_tcpip_driver_xmc_data *) ifp->driver_data;
- uint8_t speed = MG_PHY_SPEED_10M;
- bool up = false, full_duplex = false;
- struct mg_phy phy = {eth_read_phy, eth_write_phy};
- up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed);
- if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
- MG_DEBUG(("Link is %uM %s-duplex", speed == MG_PHY_SPEED_10M ? 10 : 100,
- full_duplex ? "full" : "half"));
- }
- return up;
- }
- void ETH0_0_IRQHandler(void);
- void ETH0_0_IRQHandler(void) {
- uint32_t irq_status = ETH0->STATUS;
- // check if a frame was received
- if (irq_status & MG_BIT(6)) {
- for (uint8_t i = 0; i < 10; i++) { // read as they arrive, but not forever
- if (s_rxdesc[s_rxno][0] & MG_BIT(31)) break;
- size_t len = (s_rxdesc[s_rxno][0] & 0x3fff0000) >> 16;
- mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp);
- s_rxdesc[s_rxno][0] = MG_BIT(31); // OWN bit: handle control to DMA
- // Resume processing
- ETH0->STATUS = MG_BIT(7) | MG_BIT(6); // clear RU and RI
- ETH0->RECEIVE_POLL_DEMAND = 0;
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
- }
- ETH0->STATUS = MG_BIT(6);
- }
- // clear Successful transmission interrupt
- if (irq_status & 1) {
- ETH0->STATUS = 1;
- }
- // clear normal interrupt
- if (irq_status & MG_BIT(16)) {
- ETH0->STATUS = MG_BIT(16);
- }
- }
- struct mg_tcpip_driver mg_tcpip_driver_xmc = {mg_tcpip_driver_xmc_init,
- mg_tcpip_driver_xmc_tx, NULL,
- mg_tcpip_driver_xmc_poll};
- #endif
- #ifdef MG_ENABLE_LINES
- #line 1 "src/drivers/xmc7.c"
- #endif
- #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7
- struct ETH_Type {
- volatile uint32_t CTL, STATUS, RESERVED[1022], NETWORK_CONTROL,
- NETWORK_CONFIG, NETWORK_STATUS, USER_IO_REGISTER, DMA_CONFIG,
- TRANSMIT_STATUS, RECEIVE_Q_PTR, TRANSMIT_Q_PTR, RECEIVE_STATUS,
- INT_STATUS, INT_ENABLE, INT_DISABLE, INT_MASK, PHY_MANAGEMENT, PAUSE_TIME,
- TX_PAUSE_QUANTUM, PBUF_TXCUTTHRU, PBUF_RXCUTTHRU, JUMBO_MAX_LENGTH,
- EXTERNAL_FIFO_INTERFACE, RESERVED1, AXI_MAX_PIPELINE, RSC_CONTROL,
- INT_MODERATION, SYS_WAKE_TIME, RESERVED2[7], HASH_BOTTOM, HASH_TOP,
- SPEC_ADD1_BOTTOM, SPEC_ADD1_TOP, SPEC_ADD2_BOTTOM, SPEC_ADD2_TOP,
- SPEC_ADD3_BOTTOM, SPEC_ADD3_TOP, SPEC_ADD4_BOTTOM, SPEC_ADD4_TOP,
- SPEC_TYPE1, SPEC_TYPE2, SPEC_TYPE3, SPEC_TYPE4, WOL_REGISTER,
- STRETCH_RATIO, STACKED_VLAN, TX_PFC_PAUSE, MASK_ADD1_BOTTOM,
- MASK_ADD1_TOP, DMA_ADDR_OR_MASK, RX_PTP_UNICAST, TX_PTP_UNICAST,
- TSU_NSEC_CMP, TSU_SEC_CMP, TSU_MSB_SEC_CMP, TSU_PTP_TX_MSB_SEC,
- TSU_PTP_RX_MSB_SEC, TSU_PEER_TX_MSB_SEC, TSU_PEER_RX_MSB_SEC,
- DPRAM_FILL_DBG, REVISION_REG, OCTETS_TXED_BOTTOM, OCTETS_TXED_TOP,
- FRAMES_TXED_OK, BROADCAST_TXED, MULTICAST_TXED, PAUSE_FRAMES_TXED,
- FRAMES_TXED_64, FRAMES_TXED_65, FRAMES_TXED_128, FRAMES_TXED_256,
- FRAMES_TXED_512, FRAMES_TXED_1024, FRAMES_TXED_1519, TX_UNDERRUNS,
- SINGLE_COLLISIONS, MULTIPLE_COLLISIONS, EXCESSIVE_COLLISIONS,
- LATE_COLLISIONS, DEFERRED_FRAMES, CRS_ERRORS, OCTETS_RXED_BOTTOM,
- OCTETS_RXED_TOP, FRAMES_RXED_OK, BROADCAST_RXED, MULTICAST_RXED,
- PAUSE_FRAMES_RXED, FRAMES_RXED_64, FRAMES_RXED_65, FRAMES_RXED_128,
- FRAMES_RXED_256, FRAMES_RXED_512, FRAMES_RXED_1024, FRAMES_RXED_1519,
- UNDERSIZE_FRAMES, EXCESSIVE_RX_LENGTH, RX_JABBERS, FCS_ERRORS,
- RX_LENGTH_ERRORS, RX_SYMBOL_ERRORS, ALIGNMENT_ERRORS, RX_RESOURCE_ERRORS,
- RX_OVERRUNS, RX_IP_CK_ERRORS, RX_TCP_CK_ERRORS, RX_UDP_CK_ERRORS,
- AUTO_FLUSHED_PKTS, RESERVED3, TSU_TIMER_INCR_SUB_NSEC, TSU_TIMER_MSB_SEC,
- TSU_STROBE_MSB_SEC, TSU_STROBE_SEC, TSU_STROBE_NSEC, TSU_TIMER_SEC,
- TSU_TIMER_NSEC, TSU_TIMER_ADJUST, TSU_TIMER_INCR, TSU_PTP_TX_SEC,
- TSU_PTP_TX_NSEC, TSU_PTP_RX_SEC, TSU_PTP_RX_NSEC, TSU_PEER_TX_SEC,
- TSU_PEER_TX_NSEC, TSU_PEER_RX_SEC, TSU_PEER_RX_NSEC, PCS_CONTROL,
- PCS_STATUS, RESERVED4[2], PCS_AN_ADV, PCS_AN_LP_BASE, PCS_AN_EXP,
- PCS_AN_NP_TX, PCS_AN_LP_NP, RESERVED5[6], PCS_AN_EXT_STATUS, RESERVED6[8],
- TX_PAUSE_QUANTUM1, TX_PAUSE_QUANTUM2, TX_PAUSE_QUANTUM3, RESERVED7,
- RX_LPI, RX_LPI_TIME, TX_LPI, TX_LPI_TIME, DESIGNCFG_DEBUG1,
- DESIGNCFG_DEBUG2, DESIGNCFG_DEBUG3, DESIGNCFG_DEBUG4, DESIGNCFG_DEBUG5,
- DESIGNCFG_DEBUG6, DESIGNCFG_DEBUG7, DESIGNCFG_DEBUG8, DESIGNCFG_DEBUG9,
- DESIGNCFG_DEBUG10, RESERVED8[22], SPEC_ADD5_BOTTOM, SPEC_ADD5_TOP,
- RESERVED9[60], SPEC_ADD36_BOTTOM, SPEC_ADD36_TOP, INT_Q1_STATUS,
- INT_Q2_STATUS, INT_Q3_STATUS, RESERVED10[11], INT_Q15_STATUS, RESERVED11,
- TRANSMIT_Q1_PTR, TRANSMIT_Q2_PTR, TRANSMIT_Q3_PTR, RESERVED12[11],
- TRANSMIT_Q15_PTR, RESERVED13, RECEIVE_Q1_PTR, RECEIVE_Q2_PTR,
- RECEIVE_Q3_PTR, RESERVED14[3], RECEIVE_Q7_PTR, RESERVED15,
- DMA_RXBUF_SIZE_Q1, DMA_RXBUF_SIZE_Q2, DMA_RXBUF_SIZE_Q3, RESERVED16[3],
- DMA_RXBUF_SIZE_Q7, CBS_CONTROL, CBS_IDLESLOPE_Q_A, CBS_IDLESLOPE_Q_B,
- UPPER_TX_Q_BASE_ADDR, TX_BD_CONTROL, RX_BD_CONTROL, UPPER_RX_Q_BASE_ADDR,
- RESERVED17[2], HIDDEN_REG0, HIDDEN_REG1, HIDDEN_REG2, HIDDEN_REG3,
- RESERVED18[2], HIDDEN_REG4, HIDDEN_REG5;
- };
- #define ETH0 ((struct ETH_Type *) 0x40490000)
- #define ETH_PKT_SIZE 1536 // Max frame size
- #define ETH_DESC_CNT 4 // Descriptors count
- #define ETH_DS 2 // Descriptor size (words)
- static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM;
- static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_ETH_RAM;
- static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM MG_8BYTE_ALIGNED;
- static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] MG_ETH_RAM MG_8BYTE_ALIGNED;
- 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 { MG_PHY_ADDR = 0, MG_PHYREG_BCR = 0, MG_PHYREG_BSR = 1 };
- static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) {
- // WRITE1, READ OPERATION, PHY, REG, WRITE10
- ETH0->PHY_MANAGEMENT = MG_BIT(30) | MG_BIT(29) | ((addr & 0xf) << 24) |
- ((reg & 0x1f) << 18) | MG_BIT(17);
- while ((ETH0->NETWORK_STATUS & MG_BIT(2)) == 0) (void) 0;
- return ETH0->PHY_MANAGEMENT & 0xffff;
- }
- static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) {
- ETH0->PHY_MANAGEMENT = MG_BIT(30) | MG_BIT(28) | ((addr & 0xf) << 24) |
- ((reg & 0x1f) << 18) | MG_BIT(17) | val;
- while ((ETH0->NETWORK_STATUS & MG_BIT(2)) == 0) (void) 0;
- }
- static uint32_t get_clock_rate(struct mg_tcpip_driver_xmc7_data *d) {
- // see ETH0 -> NETWORK_CONFIG register
- (void) d;
- return 3;
- }
- static bool mg_tcpip_driver_xmc7_init(struct mg_tcpip_if *ifp) {
- struct mg_tcpip_driver_xmc7_data *d =
- (struct mg_tcpip_driver_xmc7_data *) ifp->driver_data;
- s_ifp = ifp;
- // enable controller, set RGMII mode
- ETH0->CTL = MG_BIT(31) | (4 << 8) | 2;
- uint32_t cr = get_clock_rate(d);
- // set NSP change, ignore RX FCS, data bus width, clock rate
- // frame length 1536, full duplex, speed
- ETH0->NETWORK_CONFIG = MG_BIT(29) | MG_BIT(26) | MG_BIT(21) |
- ((cr & 7) << 18) | MG_BIT(8) | MG_BIT(1) | MG_BIT(0);
- // config DMA settings: Force TX burst, Discard on Error, set RX buffer size
- // to 1536, TX_PBUF_SIZE, RX_PBUF_SIZE, AMBA_BURST_LENGTH
- ETH0->DMA_CONFIG =
- MG_BIT(26) | MG_BIT(24) | (0x18 << 16) | MG_BIT(10) | (3 << 8) | 4;
- // initialize descriptors
- for (int i = 0; i < ETH_DESC_CNT; i++) {
- s_rxdesc[i][0] = (uint32_t) s_rxbuf[i];
- if (i == ETH_DESC_CNT - 1) {
- s_rxdesc[i][0] |= MG_BIT(1); // mark last descriptor
- }
- s_txdesc[i][0] = (uint32_t) s_txbuf[i];
- s_txdesc[i][1] = MG_BIT(31); // OWN descriptor
- if (i == ETH_DESC_CNT - 1) {
- s_txdesc[i][1] |= MG_BIT(30); // mark last descriptor
- }
- }
- ETH0->RECEIVE_Q_PTR = (uint32_t) s_rxdesc;
- ETH0->TRANSMIT_Q_PTR = (uint32_t) s_txdesc;
- // disable other queues
- ETH0->TRANSMIT_Q2_PTR = 1;
- ETH0->TRANSMIT_Q1_PTR = 1;
- ETH0->RECEIVE_Q2_PTR = 1;
- ETH0->RECEIVE_Q1_PTR = 1;
- // enable interrupts (RX complete)
- ETH0->INT_ENABLE = MG_BIT(1);
- // set MAC address
- ETH0->SPEC_ADD1_BOTTOM =
- ifp->mac[3] << 24 | ifp->mac[2] << 16 | ifp->mac[1] << 8 | ifp->mac[0];
- ETH0->SPEC_ADD1_TOP = ifp->mac[5] << 8 | ifp->mac[4];
- // enable MDIO, TX, RX
- ETH0->NETWORK_CONTROL = MG_BIT(4) | MG_BIT(3) | MG_BIT(2);
- // start transmission
- ETH0->NETWORK_CONTROL |= MG_BIT(9);
- // init phy
- struct mg_phy phy = {eth_read_phy, eth_write_phy};
- mg_phy_init(&phy, d->phy_addr, MG_PHY_CLOCKS_MAC);
- (void) d;
- return true;
- }
- static size_t mg_tcpip_driver_xmc7_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][1] & MG_BIT(31)) == 0)) {
- ifp->nerr++;
- MG_ERROR(("No free descriptors"));
- len = 0; // All descriptors are busy, fail
- } else {
- memcpy(s_txbuf[s_txno], buf, len);
- s_txdesc[s_txno][1] = (s_txno == ETH_DESC_CNT - 1 ? MG_BIT(30) : 0) |
- MG_BIT(15) | len; // Last buffer and length
- ETH0->NETWORK_CONTROL |= MG_BIT(9); // enable transmission
- if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
- }
- MG_DSB();
- ETH0->TRANSMIT_STATUS = ETH0->TRANSMIT_STATUS;
- ETH0->NETWORK_CONTROL |= MG_BIT(9); // enable transmission
- return len;
- }
- static void mg_tcpip_driver_xmc7_update_hash_table(struct mg_tcpip_if *ifp) {
- // TODO(): read database, rebuild hash table
- // set multicast MAC address
- ETH0->SPEC_ADD2_BOTTOM = mcast_addr[3] << 24 | mcast_addr[2] << 16 |
- mcast_addr[1] << 8 | mcast_addr[0];
- ETH0->SPEC_ADD2_TOP = mcast_addr[5] << 8 | mcast_addr[4];
- (void) ifp;
- }
- static bool mg_tcpip_driver_xmc7_poll(struct mg_tcpip_if *ifp, bool s1) {
- if (ifp->update_mac_hash_table) {
- mg_tcpip_driver_xmc7_update_hash_table(ifp);
- ifp->update_mac_hash_table = false;
- }
- if (!s1) return false;
- struct mg_tcpip_driver_xmc7_data *d =
- (struct mg_tcpip_driver_xmc7_data *) ifp->driver_data;
- uint8_t speed = MG_PHY_SPEED_10M;
- bool up = false, full_duplex = false;
- struct mg_phy phy = {eth_read_phy, eth_write_phy};
- up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed);
- if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
- // tmp = reg with flags set to the most likely situation: 100M full-duplex
- // if(link is slow or half) set flags otherwise
- // reg = tmp
- uint32_t netconf = ETH0->NETWORK_CONFIG;
- MG_SET_BITS(netconf, MG_BIT(10),
- MG_BIT(1) | MG_BIT(0)); // 100M, Full-duplex
- uint32_t ctl = ETH0->CTL;
- MG_SET_BITS(ctl, 0xFF00, 4 << 8); // /5 for 25M clock
- if (speed == MG_PHY_SPEED_1000M) {
- netconf |= MG_BIT(10); // 1000M
- MG_SET_BITS(ctl, 0xFF00, 0); // /1 for 125M clock TODO() IS THIS NEEDED ?
- } else if (speed == MG_PHY_SPEED_10M) {
- netconf &= ~MG_BIT(0); // 10M
- MG_SET_BITS(ctl, 0xFF00, 49); // /50 for 2.5M clock
- }
- if (full_duplex == false) netconf &= ~MG_BIT(1); // Half-duplex
- ETH0->NETWORK_CONFIG = netconf; // IRQ handler does not fiddle with these
- ETH0->CTL = ctl;
- MG_DEBUG(("Link is %uM %s-duplex",
- speed == MG_PHY_SPEED_10M
- ? 10
- : (speed == MG_PHY_SPEED_100M ? 100 : 1000),
- full_duplex ? "full" : "half"));
- }
- return up;
- }
- void ETH_IRQHandler(void) {
- uint32_t irq_status = ETH0->INT_STATUS;
- if (irq_status & MG_BIT(1)) {
- for (uint8_t i = 0; i < 10; i++) { // read as they arrive, but not forever
- if ((s_rxdesc[s_rxno][0] & MG_BIT(0)) == 0) break;
- size_t len = s_rxdesc[s_rxno][1] & (MG_BIT(13) - 1);
- mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp);
- s_rxdesc[s_rxno][0] &= ~MG_BIT(0); // OWN bit: handle control to DMA
- if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
- }
- }
- ETH0->INT_STATUS = irq_status;
- }
- struct mg_tcpip_driver mg_tcpip_driver_xmc7 = {mg_tcpip_driver_xmc7_init,
- mg_tcpip_driver_xmc7_tx, NULL,
- mg_tcpip_driver_xmc7_poll};
- #endif
|