1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378(*
* 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)letbits=functionV4p->V4.Prefix.bitsp|V6p->V6.Prefix.bitspletppppfi=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