123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377(*
* Copyright (c) 2013-2015 David Sheets <sheets@alum.mit.edu>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*)exceptionParse_errorofstring*stringtypescope=Point|Interface|Link|Admin|Site|Organization|Globallettry_with_resultfna=tryOk(fna)withParse_error(msg,_)->Error(`Msg("Ipaddr: "^msg))letfailwith_msg=functionOkx->x|Error(`Msgm)->failwithmletstring_of_scope=function|Point->"point"|Interface->"interface"|Link->"link"|Admin->"admin"|Site->"site"|Organization->"organization"|Global->"global"letscope_of_string=function|"point"->OkPoint|"interface"->OkInterface|"link"->OkLink|"admin"->OkAdmin|"site"->OkSite|"organization"->OkOrganization|"global"->OkGlobal|s->Error(`Msg("unknown scope: "^s))letpp_scopefmts=Format.pp_print_stringfmt(string_of_scopes)let(~|)=Int32.of_intlet(|~)=Int32.to_intlet(&&&)xy=Int32.logandxylet(|||)xy=Int32.logorxylet(<|<)xy=Int32.shift_leftxylet(>|>)xy=Int32.shift_right_logicalxylet(>!)xy=x>|>y&&&0xFF_llet(<!)xy=x&&&0xFF_l<|<yletneed_morex=Parse_error("not enough data",x)letchar_0=int_of_char'0'letchar_a=int_of_char'a'letchar_A=int_of_char'A'letint_of_charc=matchcwith|'0'..'9'->Stdlib.int_of_charc-char_0|'a'..'f'->10+Stdlib.int_of_charc-char_a|'A'..'F'->10+Stdlib.int_of_charc-char_A|_->-1letbad_charis=letmsg=Printf.sprintf"invalid character '%c' at %d"s.[i]iinParse_error(msg,s)letoctal_notations=letmsg=Printf.sprintf"octal notation disallowed"inParse_error(msg,s)letis_numberbasen=n>=0&&n<baseletparse_intbasesi=letlen=String.lengthsinletrecnextprev=letj=!iinifj>=lenthenprevelseletc=s.[j]inletk=int_of_charcinifis_numberbasekthen(incri;next((prev*base)+k))elseprevinleti=!iinifi<lenthenifis_numberbase(int_of_chars.[i])thennext0elseraise(bad_charis)elseraise(need_mores)letparse_dec_intsi=parse_int10siletparse_hex_intsi=parse_int16siletexpect_charsic=if!i<String.lengthsthenifs.[!i]<>cthenraise(bad_char!is)elseincrielseraise(need_mores)letexpect_endsi=ifString.lengths<=!ithen()elseraise(bad_char!is)letreject_octalsi=if!i+1<String.lengthsthenifs.[!i]=='0'&&is_number10(int_of_chars.[!i+1])thenraise(octal_notations)moduleV4=structtypet=int32letcompare=Int32.unsigned_compareletmakeabcd=~|a<!24|||(~|b<!16)|||(~|c<!8|||(~|d<!0))(* parsing *)letparse_dotted_quadsi=reject_octalsi;leta=parse_dec_intsiinexpect_charsi'.';reject_octalsi;letb=parse_dec_intsiinexpect_charsi'.';reject_octalsi;letc=parse_dec_intsiinexpect_charsi'.';reject_octalsi;letd=parse_dec_intsiinletvalida=aland0xff<>ainifvalidathenraise(Parse_error("first octet out of bounds",s))elseifvalidbthenraise(Parse_error("second octet out of bounds",s))elseifvalidcthenraise(Parse_error("third octet out of bounds",s))elseifvaliddthenraise(Parse_error("fourth octet out of bounds",s))elsemakeabcd(* string conversion *)letof_string_raw=parse_dotted_quadletof_string_exns=leto=ref0inletx=of_string_rawsoinexpect_endso;xletof_strings=try_with_resultof_string_exnsletwith_port_of_string~defaults=tryletlen=String.lengthsando=ref0inletipv4=of_string_rawsoinif!o<len&&s.[!o]=':'then(incro;letport=parse_dec_intsoinexpect_endso;Ok(ipv4,port))else(expect_endso;Ok(ipv4,default))withParse_error(msg,_)->Error(`Msg("Ipaddr: "^msg))letto_bufferbi=Printf.bprintfb"%ld.%ld.%ld.%ld"(i>!24)(i>!16)(i>!8)(i>!0)letto_stringi=letb=Buffer.create15into_bufferbi;Buffer.contentsbletppppfi=Format.fprintfppf"%s"(to_stringi)(* Octets conversion *)letof_octets_exn?(off=0)bs=trymake(Char.codebs.[0+off])(Char.codebs.[1+off])(Char.codebs.[2+off])(Char.codebs.[3+off])with_->raise(need_morebs)letof_octets?offbs=try_with_result(of_octets_exn?off)bsletwrite_octets_exn?(off=0)ib=tryBytes.setb(0+off)(Char.chr((|~)(i>!24)));Bytes.setb(1+off)(Char.chr((|~)(i>!16)));Bytes.setb(2+off)(Char.chr((|~)(i>!8)));Bytes.setb(3+off)(Char.chr((|~)(i>!0)))with_->raise(need_more(Bytes.to_stringb))letwrite_octets?offibs=try_with_result(write_octets_exn?offi)bsletto_octetsi=String.init4(function|0->Char.chr((|~)(i>!24))|1->Char.chr((|~)(i>!16))|2->Char.chr((|~)(i>!8))|3->Char.chr((|~)(i>!0))|_->assertfalse)(* Int32 *)letof_int32i=iletto_int32i=i(* Int16 *)letof_int16(a,b)=~|a<|<16|||~|bletto_int16a=((|~)(a>|>16),(|~)(a&&&0xFF_FF_l))(* MAC *)(* {{:http://tools.ietf.org/html/rfc1112#section-6.2}RFC 1112}. *)letmulticast_to_maci=letmacb=Bytes.create6inBytes.setmacb0(Char.chr0x01);Bytes.setmacb1(Char.chr0x00);Bytes.setmacb2(Char.chr0x5E);Bytes.setmacb3(Char.chr((|~)(i>|>16&&&0x7F_l)));Bytes.setmacb4(Char.chr((|~)(i>!8)));Bytes.setmacb5(Char.chr((|~)(i>!0)));Macaddr.of_octets_exn(Bytes.to_stringmacb)(* Host *)letto_domain_namei=letname=[Int32.to_string(i>!0);Int32.to_string(i>!8);Int32.to_string(i>!16);Int32.to_string(i>!24);"in-addr";"arpa";]inDomain_name.(host_exn(of_strings_exnname))letof_domain_namen=matchDomain_name.to_stringsnwith|[a;b;c;d;in_addr;arpa]whenDomain_name.(equal_labelarpa"arpa"&&equal_labelin_addr"in-addr")->(letconvbitsdata=leti=Int32.of_int(parse_dec_intdata(ref0))inifi>0xFFlthenraise(Parse_error("label with a too big number",data))elsei<!bitsintrylet(+)=Int32.addinSome(conv0a+conv8b+conv16c+conv24d)withParse_error_->None)|_->Noneletsucct=ifInt32.equalt0xFF_FF_FF_FFlthenError(`Msg"Ipaddr: highest address has been reached")elseOk(Int32.succt)letpredt=ifInt32.equalt0x00_00_00_00lthenError(`Msg"Ipaddr: lowest address has been reached")elseOk(Int32.predt)(* constant *)letany=make0000letunspecified=make0000letbroadcast=make255255255255letlocalhost=make127001letnodes=make224001letrouters=make224002modulePrefix=structtypeaddr=ttypet=addr*intletcompare(pre,sz)(pre',sz')=letc=compareprepre'inifc=0thenStdlib.compareszsz'elsecletip=makeletmasksz=ifsz<=0then0_lelseifsz>=32then0x0_FF_FF_FF_FF_lelse0x0_FF_FF_FF_FF_l<|<32-szletprefix(pre,sz)=(pre&&&masksz,sz)letmakeszpre=(pre,sz)letnetwork_address(pre,sz)addr=pre&&&masksz|||(addr&&&Int32.lognot(masksz))(* string conversion *)let_of_string_rawsi=letquad=of_string_rawsiinexpect_charsi'/';letp=parse_dec_intsiinifp>32||p<0thenraise(Parse_error("invalid prefix size",s));(p,quad)letof_string_rawsi=letp,quad=_of_string_rawsiinmakepquadlet_of_string_exns=leti=ref0inletres=_of_string_rawsiinexpect_endsi;resletof_string_exns=letp,quad=_of_string_exnsinmakepquadletof_strings=try_with_resultof_string_exnslet_of_netmask_exn~netmaskaddress=letrecfind_greatest_onebitsi=ifbits=0_ltheni-1elsefind_greatest_one(bits>|>1)(i+1)inletone=netmask&&&Int32.negnetmaskinletsz=32-find_greatest_oneone(ifone=0_lthen33else0)inifnetmask<>maskszthenraise(Parse_error("invalid netmask",to_stringnetmask))elsemakeszaddressletof_netmask_exn~netmask~address=_of_netmask_exn~netmaskaddressletof_netmask~netmask~address=try_with_result(_of_netmask_exn~netmask)addressletto_bufferbuf(pre,sz)=Printf.bprintfbuf"%a/%d"to_bufferpreszletto_stringsubnet=letb=Buffer.create18into_bufferbsubnet;Buffer.contentsbletppppfi=Format.fprintfppf"%s"(to_stringi)letmemip(pre,sz)=letm=maskszinip&&&m=(pre&&&m)letsubset~subnet:(pre1,sz1)~network:(pre2,sz2)=sz1>=sz2&&mempre1(pre2,sz2)letof_addrip=make32ipletglobal=make0(ip0000)letrelative=make8(ip0000)letloopback=make8(ip127000)letlink=make16(ip16925400)letmulticast=make4(ip224000)letmulticast_org=make14(ip23919200)letmulticast_admin=make16(ip23925500)letmulticast_link=make24(ip224000)(* http://tools.ietf.org/html/rfc2365 *)letprivate_10=make8(ip10000)letprivate_172=make12(ip1721600)letprivate_192=make16(ip19216800)letprivate_blocks=[loopback;link;private_10;private_172;private_192]letbroadcast(pre,sz)=Int32.logorpre(Int32.logxor(masksz)0xFF_FF_FF_FFl)letnetwork(pre,sz)=pre&&&maskszletaddress(addr,_)=addrletbits(_,sz)=szletnetmasksubnet=mask(bitssubnet)lethostmaskcidr=Int32.logxor(netmaskcidr)0xFF_FF_FF_FFlletfirst((_,sz)ascidr)=ifsz>30thennetworkcidrelsenetworkcidr|>succ|>failwith_msgletlast((_,sz)ascidr)=ifsz>30thenbroadcastcidrelsebroadcastcidr|>pred|>failwith_msglethosts?(usable=true)((_,sz)ascidr)=letreciter_seqstartstop=ifcompare(start,32)(stop,32)>0thenSeq.Nilelsematchsuccstartwith|Okstart_succ->Seq.Cons(start,fun()->iter_seqstart_succstop)|Error_->Seq.Cons(start,fun()->Seq.Nil)inifusable&&sz=32thenfun()->Seq.Nilelseletstart,stop=ifusablethen(firstcidr,lastcidr)else(networkcidr,broadcastcidr)infun()->iter_seqstartstopletsubnetsn((_,sz)ascidr)=letreciter_seqstartstopsteps=ifcompare(start,32)(stop,32)>0thenSeq.Nilelseletprefix=makenstartinletstart_succ=Int32.addstartstepsinifstart_succ=0lthenSeq.Cons(prefix,fun()->Seq.Nil)elseSeq.Cons(prefix,fun()->iter_seqstart_succstopsteps)inifsz>n||n>32thenfun()->Seq.Nilelseletstart=networkcidrinletstop=broadcastcidrinletsteps=Int32.add(hostmaskcidr)1l>|>n-szinfun()->iter_seqstartstopstepsend(* TODO: this could be optimized with something trie-like *)letscopei=letmem=Prefix.memiinifmemPrefix.loopbackthenInterfaceelseifmemPrefix.linkthenLinkelseifList.existsmemPrefix.private_blocksthenOrganizationelseifi=unspecifiedthenPointelseifi=broadcastthenAdminelseifmemPrefix.relativethenAdminelseifmemPrefix.multicastthenifmemPrefix.multicast_orgthenOrganizationelseifmemPrefix.multicast_adminthenAdminelseifmemPrefix.multicast_linkthenLinkelseGlobalelseGloballetis_globali=scopei=Globalletis_multicasti=Prefix.(memimulticast)letis_privatei=scopei<>GlobalmoduleSet=Set.Make(structtypenonrect=tletcompare(a:t)(b:t)=compareabend)moduleMap=Map.Make(structtypenonrect=tletcompare(a:t)(b:t)=compareabend)endmoduleS128:sigexceptionOverflowtypetvalzero:t[@@ocaml.warning"-32"]valmax_int:tvalcompare:t->t->intvalequal:t->t->boolvalfold_left:('a->int->'a)->'a->t->'avalof_octets_exn:string->tvalto_octets:t->stringvalof_int64:int64*int64->tvalto_int64:t->int64*int64valof_int32:int32*int32*int32*int32->tvalto_int32:t->int32*int32*int32*int32valof_int16:int*int*int*int*int*int*int*int->tvalto_int16:t->int*int*int*int*int*int*int*intvaladd_exn:t->t->t[@@ocaml.warning"-32"]valpred_exn:t->t[@@ocaml.warning"-32"]valadd:t->t->toptionvallogand:t->t->tvallogor:t->t->tvallogxor:t->t->tvallognot:t->tmoduleByte:sigvalget_lsbits:int->int->int[@@ocaml.warning"-32"]valget_msbits:int->int->int[@@ocaml.warning"-32"]valset_msbits:int->int->int->int[@@ocaml.warning"-32"]valfold_left:('a->bool->'a)->'a->int->'aendvalshift_right:t->int->tvalshift_left:t->int->tvalwrite_octets_exn:?off:int->t->bytes->unitvalsucc_exn:t->tvalsucc:t->(t,[>`Msgofstring])resultvalpred:t->(t,[>`Msgofstring])resultend=structexceptionOverflowtypet=stringletmk_zero()=Bytes.make16'\x00'letzero=Bytes.unsafe_to_string(mk_zero())letmax_int=String.make16'\xff'letcompare=String.compareletequal=String.equalletfold_leftfinits=(* With OCaml>=4.13.0:
[String.fold_left (fun acc c -> f acc (Char.code c)) init s] *)leta=refinitinfori=0to15doa:=f!a(Char.code(String.getsi))done;!aletiteri_right2fxy=fori=15downto0doletx'=Char.code(String.getxi)inlety'=Char.code(String.getyi)infix'y'doneletof_octets_exns=ifString.lengths<>16theninvalid_arg"not 16 bytes long";sletto_octets=Fun.idletof_int64(a,b)=letb'=mk_zero()inBytes.set_int64_beb'0a;Bytes.set_int64_beb'8b;Bytes.unsafe_to_stringb'letto_int64s=(* with OCaml>=4.13, use String.get_int64_be *)letb=Bytes.unsafe_of_stringsin(Bytes.get_int64_beb0,Bytes.get_int64_beb8)letof_int32(a,b,c,d)=letb'=mk_zero()inBytes.set_int32_beb'0a;Bytes.set_int32_beb'4b;Bytes.set_int32_beb'8c;Bytes.set_int32_beb'12d;Bytes.unsafe_to_stringb'letto_int32s=(* with OCaml>=4.13, use String.get_int32_be *)letb=Bytes.unsafe_of_stringsin(Bytes.get_int32_beb0,Bytes.get_int32_beb4,Bytes.get_int32_beb8,Bytes.get_int32_beb12)letof_int16(a,b,c,d,e,f,g,h)=letb'=mk_zero()inBytes.set_uint16_beb'0a;Bytes.set_uint16_beb'2b;Bytes.set_uint16_beb'4c;Bytes.set_uint16_beb'6d;Bytes.set_uint16_beb'8e;Bytes.set_uint16_beb'10f;Bytes.set_uint16_beb'12g;Bytes.set_uint16_beb'14h;Bytes.unsafe_to_stringb'letto_int16s=(* with OCaml>=4.13, use String.get_uint16_be *)letb=Bytes.unsafe_of_stringsin(Bytes.get_uint16_beb0,Bytes.get_uint16_beb2,Bytes.get_uint16_beb4,Bytes.get_uint16_beb6,Bytes.get_uint16_beb8,Bytes.get_uint16_beb10,Bytes.get_uint16_beb12,Bytes.get_uint16_beb14)letadd_exnxy=letb=mk_zero()inletcarry=ref0initeri_right2(funix'y'->letsum=x'+y'+!carryinifsum>=256then(carry:=1;Bytes.set_uint8bi(sum-256))else(carry:=0;Bytes.set_uint8bisum))xy;if!carry<>0thenraiseOverflowelseBytes.unsafe_to_stringbletaddxy=trySome(add_exnxy)withOverflow->Noneletpred_exnx=ifequalxzerothenraiseOverflow;letb=Bytes.of_stringxinletrecgoi=Bytes.set_uint8bi(Char.code(String.getxi)-1);ifChar.code(String.getxi)=0thengo(Stdlib.predi)ingo15;Bytes.unsafe_to_stringbletlogandxy=letb=mk_zero()initeri_right2(funixy->Bytes.set_uint8bi(xlandy))xy;Bytes.unsafe_to_stringbletlogorxy=letb=mk_zero()initeri_right2(funixy->Bytes.set_uint8bi(xlory))xy;Bytes.unsafe_to_stringbletlogxorxy=letb=mk_zero()initeri_right2(funixy->Bytes.set_uint8bi(xlxory))xy;Bytes.unsafe_to_stringbletlognotx=letb=mk_zero()inString.iteri(funi_->Bytes.set_uint8bi(lnot(Char.code(String.getxi))))x;Bytes.unsafe_to_stringbmoduleByte=struct(* Extract the [n] least significant bits from [i] *)letget_lsbitsni=ifn<=0||n>8theninvalid_arg"out of bounds";iland((1lsln)-1)(* Extract the [n] most significant bits from [i] *)letget_msbitsni=ifn<=0||n>8theninvalid_arg"out of bounds";(iland(255lsl(8-n)))lsr(8-n)(* Set value [x] in [i]'s [n] most significant bits *)letset_msbitsnxi=ifn<0||n>8thenraise(Invalid_argument"n must be >= 0 && <= 8")elseifn=0thenielseifn=8thenxelse(xlsl(8-n))lori(* set bits are represented as true *)letfold_leftfai=letbitmask=ref0b1000_0000inleta'=refainfor_=0to7doa':=f!a'(iland!bitmask>0);bitmask:=!bitmasklsr1done;!a'endletshift_rightxn=matchnwith|0->x|128->zero|nwhenn>0&&n<128->letb=mk_zero()inletshift_bytes,shift_bits=(n/8,nmod8)in(ifshift_bits=0thenBytes.blit_stringx0bshift_bytes(16-shift_bytes)elseletcarry=ref0infori=0to15-shift_bytesdoletx'=Char.code(String.getxi)inletnew_carry=Byte.get_lsbitsshift_bitsx'inletshifted_value=x'lsrshift_bitsinletnew_value=Byte.set_msbitsshift_bits!carryshifted_valueinBytes.set_uint8b(i+shift_bytes)new_value;carry:=new_carrydone);Bytes.unsafe_to_stringb|_->raise(Invalid_argument"n must be >= 0 && <= 128")letshift_leftxn=matchnwith|0->x|128->zero|nwhenn>0&&n<128->letb=mk_zero()inletshift_bytes,shift_bits=(n/8,nmod8)in(ifshift_bits=0thenBytes.blit_stringxshift_bytesb0(16-shift_bytes)elseletcarry=ref0infori=15downto0+shift_bytesdoletx'=Char.code(String.getxi)inletnew_carry=Byte.get_msbitsshift_bitsx'inletshifted_value=x'lslshift_bitsinletnew_value=shifted_valuelor!carryinBytes.set_uint8b(i-shift_bytes)new_value;carry:=new_carrydone);Bytes.unsafe_to_stringb|_->raise(Invalid_argument"n must be >= 0 && <= 128")letwrite_octets_exn?(off=0)sdest=ifBytes.lengthdest-off<16thenraise(Parse_error("larger including offset than target bytes",s))elseBytes.blit_strings0destoff(String.lengths)letsucc_exnx=add_exnx(of_int64(0L,1L))letsuccx=tryOk(succ_exnx)withOverflow->Error(`Msg"Ipaddr: highest address has been reached")letpredx=tryOk(pred_exnx)withOverflow|Invalid_argument_->Error(`Msg"Ipaddr: lowest address has been reached")endmoduleV6=structincludeS128letmakeabcdefgh=of_int16(a,b,c,d,e,f,g,h)(* parsing *)letparse_ipv6si=letcompressed=reffalsein(* :: *)letlen=String.lengthsiniflen<!i+1thenraise(need_mores);letuse_bracket=s.[!i]='['inifuse_bracketthenincri;iflen<!i+2thenraise(need_mores);(* check if it starts with :: *)letl=ifs.[!i]=':'then(incri;ifs.[!i]=':'then(compressed:=true;incri;[-1])elseraise(bad_char!is))else[]inletrecloopnbacc=ifnb>=8thenaccelseif!i>=lenthenaccelseletpos=!iinletx=tryparse_hex_intsiwith_->-1inifx<0thenaccelseifnb=7thenx::accelseif!i<len&&s.[!i]=':'then(incri;if!i<lenthenifs.[!i]=':'thenif!compressedthen(decri;x::acc(* trailing :: *))else(compressed:=true;incri;loop(nb+2)(-1::x::acc))elseifis_number16(int_of_chars.[!i])thenloop(nb+1)(x::acc)elseraise(bad_char!is)elseraise(need_mores))elseif!i<len&&s.[!i]='.'then(i:=pos;letv4=V4.of_string_rawsiinlethi,lo=V4.to_int16v4inlo::hi::acc)elsex::accinletres=loop(List.lengthl)linletres_len=List.lengthresinifres_len>8thenraise(Parse_error("too many components",s))elseifres_len=0thenraise(need_mores)elseleta=Array.make80inletmissing=if!compressedthen8-(res_len-1)elseifres_len<>8thenif!i<lenthenraise(bad_char!is)elseraise(need_mores)else0inlet_=List.fold_left(funix->ifx=-1theni-missingelse(ifxland0xffff<>xthenraise(Parse_error(Printf.sprintf"component %d out of bounds"i,s));a.(i)<-x;i-1))7resinifuse_bracketthenexpect_charsi']';a(* string conversion *)letof_string_rawsoffset=leta=parse_ipv6soffsetinmakea.(0)a.(1)a.(2)a.(3)a.(4)a.(5)a.(6)a.(7)letof_string_exns=leto=ref0inletx=of_string_rawsoinexpect_endso;xletof_strings=try_with_resultof_string_exnsletwith_port_of_string~defaults=letlen=String.lengthsando=ref0intryletipv6=of_string_rawsoinif!o<len&&s.[!o]=':'then(incro;letport=parse_dec_intsoinexpect_endso;Ok(ipv6,port))else(expect_endso;Ok(ipv6,default))withParse_error(msg,_)->Error(`Msg("Ipaddr: "^msg))(* http://tools.ietf.org/html/rfc5952 *)letto_bufferbufaddr=let((a,b,c,d,e,f,g,h)ascomp)=to_int16addrinletv4=matchcompwith0,0,0,0,0,0xffff,_,_->true|_->falseinletrecloopelidezerosacc=function|0::xs->loopelide(zeros-1)accxs|n::xswhenzeros=0->loopelide0(n::acc)xs|n::xs->loop(minelidezeros)0(n::zeros::acc)xs|[]->letelide=minelidezerosin((ifelide<-1thenSomeelideelseNone),ifzeros=0thenaccelsezeros::acc)inletelide,l=loop00[][h;g;f;e;d;c;b;a]inassert(matchelidewithSomexwhenx<-8->false|_->true);letreccons_zeroslx=ifx>=0thenlelsecons_zeros(Some0::l)(x+1)inlet_,lrev=List.fold_left(fun(patt,l)x->ifSomex=pattthen(None,None::l)elseifx<0then(patt,cons_zeroslx)else(patt,Somex::l))(elide,[])linletrecfill=function|[Somehi;Somelo]whenv4->letaddr=V4.of_int16(hi,lo)inV4.to_bufferbufaddr|None::xs->Buffer.add_stringbuf"::";fillxs|[Somen]->Printf.bprintfbuf"%x"n|Somen::None::xs->Printf.bprintfbuf"%x::"n;fillxs|Somen::xs->Printf.bprintfbuf"%x:"n;fillxs|[]->()infill(List.revlrev)letto_stringl=letbuf=Buffer.create39into_bufferbufl;Buffer.contentsbufletppppfi=Format.fprintfppf"%s"(to_stringi)(* byte conversion *)letof_octets_exn?(off=0)bs=ifString.lengthbs-off<16thenraise(need_morebs)elseS128.of_octets_exn(String.subbsoff16)letof_octets?offbs=try_with_result(of_octets_exn?off)bsletwrite_octets?offibs=try_with_result(write_octets_exn?offi)bsletto_octets=S128.to_octets(* MAC *)(* {{:https://tools.ietf.org/html/rfc2464#section-7}RFC 2464}. *)letmulticast_to_macs=letmacb=Bytes.make6(Char.chr0x33)inBytes.blit_string(S128.to_octetss)12macb24;Macaddr.of_octets_exn(Bytes.to_stringmacb)(* Host *)letto_domain_nameb=lethexstr_of_int=Printf.sprintf"%x"inletname=S128.fold_left(funaccb->letx=hexstr_of_int(bland((1lsl4)-1))inlety=hexstr_of_int(blsr4)inx::y::acc)["ip6";"arpa"]binDomain_name.(host_exn(of_strings_exnname))letof_domain_namen=letint_of_char_string=function|"0"->0|"1"->1|"2"->2|"3"->3|"4"->4|"5"->5|"6"->6|"7"->7|"8"->8|"9"->9|"a"->10|"b"->11|"c"->12|"d"->13|"e"->14|"f"->15|_->failwith"int_of_char_string: invalid hexadecimal string"inletlabels=Domain_name.to_arrayninifArray.lengthlabels=34&&Domain_name.equal_labellabels.(0)"arpa"&&Domain_name.equal_labellabels.(1)"ip6"thenletb=Bytes.create16intryforbi=0to15doleti=2*Int.succbiinletx=int_of_char_stringlabels.(i)inlety=int_of_char_stringlabels.(i+1)inBytes.set_uint8bbi(Int.logor(Int.shift_leftx4)y)done;Some(S128.of_octets_exn(Bytes.unsafe_to_stringb))withFailure_->NoneelseNone(* constant *)letunspecified=make00000000letlocalhost=make00000001letinterface_nodes=make0xff010000001letlink_nodes=make0xff020000001letinterface_routers=make0xff010000002letlink_routers=make0xff020000002letsite_routers=make0xff050000002modulePrefix=structtypeaddr=ttypet=addr*intletcompare(pre,sz)(pre',sz')=letc=compareprepre'inifc=0thenStdlib.compareszsz'elsecletip=makeletmasksz=shift_leftmax_int(128-sz)letprefix(pre,sz)=(logandpre(masksz),sz)letmakeszpre=(pre,sz)letnetwork_address(pre,sz)addr=logor(logandpre(masksz))(logandaddr(lognot(masksz)))let_of_string_rawsi=letv6=of_string_rawsiinexpect_charsi'/';letp=parse_dec_intsiinifp>128||p<0thenraise(Parse_error("invalid prefix size",s));(p,v6)letof_string_rawsi=letp,v6=_of_string_rawsiinmakepv6let_of_string_exns=leti=ref0inletres=_of_string_rawsiinexpect_endsi;resletof_string_exns=letp,v6=_of_string_exnsinmakepv6letof_strings=try_with_resultof_string_exnslet_of_netmask_exn~netmaskaddress=letcount_bitsbitsis_last_bit_seti=S128.Byte.fold_left(fun(a,is_last_bit_set)e->match(is_last_bit_set,e)with|true,false|false,false->(a,false)|true,true->(a+1,true)|false,true->(* netmask is not contiguous *)raise(Parse_error("invalid netmask",to_stringnetmask)))(bits,is_last_bit_set)iinletnm_bits_set,_=S128.fold_left(fun(a,is_last_bit_set)e->count_bitsais_last_bit_sete)(0,true)netmaskinmakenm_bits_setaddressletof_netmask_exn~netmask~address=_of_netmask_exn~netmaskaddressletof_netmask~netmask~address=try_with_result(_of_netmask_exn~netmask)addressletto_bufferbuf(pre,sz)=Printf.bprintfbuf"%a/%d"to_bufferpreszletto_stringsubnet=letbuf=Buffer.create43into_bufferbufsubnet;Buffer.contentsbufletppppfi=Format.fprintfppf"%s"(to_stringi)letmemip(pre,sz)=letm=maskszinlogandipm=logandpremletsubset~subnet:(pre1,sz1)~network:(pre2,sz2)=sz1>=sz2&&mempre1(pre2,sz2)letof_addrip=make128ipletglobal_unicast_001=make3(ip0x20000000000)letlink=make64(ip0xfe800000000)letunique_local=make7(ip0xfc000000000)letmulticast=make8(ip0xff000000000)letipv4_mapped=make96(ip000000xffff00)letnoneui64_interface=make3(ip0x00000000000)letsolicited_node=make104(ip0xff02000010xff000)letnetwork(pre,sz)=logandpre(masksz)letaddress(addr,_)=addrletbits(_,sz)=szletnetmasksubnet=mask(bitssubnet)lethostmaskcidr=S128.logxor(netmaskcidr)S128.max_intletfirst((_,sz)ascidr)=ifsz>126thennetworkcidrelsenetworkcidr|>succ|>failwith_msgletlast((_,sz)ascidr)=letffff=S128.max_intinlogor(networkcidr)(S128.shift_rightffffsz)lethosts?(usable=true)((_,sz)ascidr)=letreciter_seqstartstop=ifS128.comparestartstop>0thenSeq.Nilelsematchsuccstartwith|Okstart_succ->Seq.Cons(start,fun()->iter_seqstart_succstop)|Error_->Seq.Cons(start,fun()->Seq.Nil)inifusable&&sz=128thenfun()->Seq.Nilelseletstart,stop=ifusablethen(firstcidr,lastcidr)else(networkcidr,lastcidr)infun()->iter_seqstartstopletsubnetsn((_,sz)ascidr)=letreciter_seqstartstopsteps=ifS128.comparestartstop>0thenSeq.Nilelseletprefix=makenstartinifS128.equalstartstopthenSeq.Cons(prefix,fun()->Seq.Nil)elsematchS128.addstartstepswith|None->Seq.Cons(prefix,fun()->Seq.Nil)|Somestart_succ->Seq.Cons(prefix,fun()->iter_seqstart_succstopsteps)inifsz>n||n>128thenfun()->Seq.Nilelseletstart=networkcidrinletstop=lastcidrinletsteps=S128.(succ_exn(shift_right(hostmaskcidr)(n-sz)))infun()->iter_seqstartstopstepsend(* TODO: This could be optimized with something trie-like *)letscopei=letmem=Prefix.memiinifmemPrefix.global_unicast_001thenGlobalelseifmemPrefix.ipv4_mapped(* rfc says they are technically global but... *)thenV4.scope(let_,_,_,v4=to_int32iinV4.of_int32v4)elseifmemPrefix.multicastthenletx,_,_,_,_,_,_,_=to_int16iinmatchxland0xfwith|0->Point|1->Interface|2|3->Link|4->Admin|5|6|7->Site|8|9|10|11|12|13->Organization|14|15->Global|_->assertfalseelseifmemPrefix.linkthenLinkelseifmemPrefix.unique_localthenGlobalelseifi=localhostthenInterfaceelseifi=unspecifiedthenPointelseGloballetlink_address_of_mac=letcbi=Char.codeb.[i]infunmac->letbmac=Macaddr.to_octetsmacinletc_0=cbmac0lxor2inletaddr=make0000((c_0lsl8)+cbmac1)((cbmac2lsl8)+0xff)(0xfe00+cbmac3)((cbmac4lsl8)+cbmac5)inPrefix.(network_addresslinkaddr)letis_globali=scopei=Globalletis_multicasti=Prefix.(memimulticast)letis_privatei=scopei<>GlobalmoduleSet=Set.Make(structtypenonrect=tletcompare(a:t)(b:t)=compareabend)moduleMap=Map.Make(structtypenonrect=tletcompare(a:t)(b:t)=compareabend)endtype('v4,'v6)v4v6=V4of'v4|V6of'v6typet=(V4.t,V6.t)v4v6letcompareab=match(a,b)with|V4a,V4b->V4.compareab|V6a,V6b->V6.compareab|V4_,V6_->-1|V6_,V4_->1moduleSet=Set.Make(structtypenonrect=tletcompare(a:t)(b:t)=compareabend)moduleMap=Map.Make(structtypenonrect=tletcompare(a:t)(b:t)=compareabend)letto_string=functionV4x->V4.to_stringx|V6x->V6.to_stringxletto_bufferbuf=function|V4x->V4.to_bufferbufx|V6x->V6.to_bufferbufxletppppfi=Format.fprintfppf"%s"(to_stringi)letof_string_rawsoffset=letlen=String.lengthsiniflen<!offset+1thenraise(need_mores);matchs.[0]with|'['->V6(V6.of_string_rawsoffset)|_->(letpos=!offsetintryV4(V4.of_string_rawsoffset)withParse_error(v4_msg,_)->(offset:=pos;tryV6(V6.of_string_rawsoffset)withParse_error(v6_msg,s)->letmsg=Printf.sprintf"not an IPv4 address: %s\nnot an IPv6 address: %s"v4_msgv6_msginraise(Parse_error(msg,s))))letof_string_exns=leto=ref0inletx=of_string_rawsoinexpect_endso;xletof_strings=try_with_resultof_string_exnsletwith_port_of_string~defaults=letlen=String.lengthsando=ref0intryletipv6=of_string_rawsoinif!o<len&&s.[!o]=':'then(incro;letport=parse_dec_intsoinexpect_endso;Ok(ipv6,port))else(expect_endso;Ok(ipv6,default))withParse_error(msg,_)->Error(`Msg("Ipaddr: "^msg))letof_octets_exnbs=matchString.lengthbswith|4->V4(V4.of_octets_exnbs)|16->V6(V6.of_octets_exnbs)|_->raise(Parse_error("octets must be of length 4 or 16",bs))letof_octetsbs=try_with_resultof_octets_exnbsletto_octetsi=matchiwithV4p->V4.to_octetsp|V6p->V6.to_octetspletv6_of_v4v4=V6.(Prefix.(network_addressipv4_mapped(of_int32(0l,0l,0l,v4))))letv4_of_v6v6=ifV6.Prefix.(memv6ipv4_mapped)thenlet_,_,_,v4=V6.to_int32v6inSomeV4.(of_int32v4)elseNoneletto_v4=functionV4v4->Somev4|V6v6->v4_of_v6v6letto_v6=functionV4v4->v6_of_v4v4|V6v6->v6letscope=functionV4v4->V4.scopev4|V6v6->V6.scopev6letis_global=functionV4v4->V4.is_globalv4|V6v6->V6.is_globalv6letis_multicast=function|V4v4->V4.is_multicastv4|V6v6->V6.is_multicastv6letis_private=function|V4v4->V4.is_privatev4|V6v6->V6.is_privatev6letmulticast_to_mac=function|V4v4->V4.multicast_to_macv4|V6v6->V6.multicast_to_macv6letto_domain_name=function|V4v4->V4.to_domain_namev4|V6v6->V6.to_domain_namev6letof_domain_namen=matchDomain_name.count_labelsnwith|6->(matchV4.of_domain_namenwithNone->None|Somex->Some(V4x))|34->(matchV6.of_domain_namenwithNone->None|Somex->Some(V6x))|_->Noneletsucc=function|V4addr->Result.map(funv->V4v)(V4.succaddr)|V6addr->Result.map(funv->V6v)(V6.succaddr)letpred=function|V4addr->Result.map(funv->V4v)(V4.predaddr)|V6addr->Result.map(funv->V6v)(V6.predaddr)modulePrefix=structmoduleAddr=structletto_v6=to_v6endtypeaddr=ttypet=(V4.Prefix.t,V6.Prefix.t)v4v6letcompareab=match(a,b)with|V4a,V4b->V4.Prefix.compareab|V6a,V6b->V6.Prefix.compareab|V4_,V6_->-1|V6_,V4_->1letof_string_rawsoffset=letlen=String.lengthsiniflen<!offset+1thenraise(need_mores);matchs.[0]with|'['->V6(V6.Prefix.of_string_rawsoffset)|_->(letpos=!offsetintryV4(V4.Prefix.of_string_rawsoffset)withParse_error(v4_msg,_)->(offset:=pos;tryV6(V6.Prefix.of_string_rawsoffset)withParse_error(v6_msg,s)->letmsg=Printf.sprintf"not an IPv4 prefix: %s\nnot an IPv6 prefix: %s"v4_msgv6_msginraise(Parse_error(msg,s))))letof_string_exns=leto=ref0inletx=of_string_rawsoinexpect_endso;xletof_strings=try_with_resultof_string_exnsletv6_of_v4v4=V6.Prefix.make(96+V4.Prefix.bitsv4)(v6_of_v4(V4.Prefix.networkv4))letv4_of_v6v6=matchv4_of_v6(V6.Prefix.networkv6)with|Somev4->Some(V4.Prefix.make(V6.Prefix.bitsv6-96)v4)|None->Noneletto_v4=functionV4v4->Somev4|V6v6->v4_of_v6v6letto_v6=functionV4v4->v6_of_v4v4|V6v6->v6letmemipprefix=V6.Prefix.mem(Addr.to_v6ip)(to_v6prefix)letsubset~subnet~network=V6.Prefix.subset~subnet:(to_v6subnet)~network:(to_v6network)letof_addr=function|V4p->V4(V4.Prefix.of_addrp)|V6p->V6(V6.Prefix.of_addrp)letto_string=function|V4p->V4.Prefix.to_stringp|V6p->V6.Prefix.to_stringpletto_bufferbuf=function|V4p->V4.Prefix.to_bufferbufp|V6p->V6.Prefix.to_bufferbufpletnetwork=function|V4p->V4(V4.Prefix.networkp)|V6p->V6(V6.Prefix.networkp)letnetmask=function|V4p->V4(V4.Prefix.netmaskp)|V6p->V6(V6.Prefix.netmaskp)letaddress=function|V4p->V4(V4.Prefix.addressp)|V6p->V6(V6.Prefix.addressp)letppppfi=Format.fprintfppf"%s"(to_stringi)letfirst=function|V4p->V4(V4.Prefix.firstp)|V6p->V6(V6.Prefix.firstp)letlast=function|V4p->V4(V4.Prefix.lastp)|V6p->V6(V6.Prefix.lastp)lethosts?(usable=true)=function|V4p->V4(V4.Prefix.hosts~usablep)|V6p->V6(V6.Prefix.hosts~usablep)letsubnetsn=function|V4p->V4(V4.Prefix.subnetsnp)|V6p->V6(V6.Prefix.subnetsnp)end