123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999(*
* 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.
*
*)openSexplib.StdexceptionParse_errorofstring*string[@@derivingsexp]typescope=|Point|Interface|Link|Admin|Site|Organization|Global[@@derivingsexp]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)lettoo_muchx=Parse_error("too much 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'->Pervasives.int_of_charc-char_0|'a'..'f'->10+Pervasives.int_of_charc-char_a|'A'..'F'->10+Pervasives.int_of_charc-char_A|_->-1letbad_charis=letmsg=Printf.sprintf"invalid character '%c' at %d"s.[i]iinParse_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)lethex_char_of_int=function|0->'0'|1->'1'|2->'2'|3->'3'|4->'4'|5->'5'|6->'6'|7->'7'|8->'8'|9->'9'|10->'a'|11->'b'|12->'c'|13->'d'|14->'e'|15->'f'|_->raise(Invalid_argument"not a hex int")lethex_string_of_int32i=String.make1(hex_char_of_int(Int32.to_inti))moduleV4=structtypet=int32letcompareab=(* ignore the sign *)letc=Int32.compare(a>|>1)(b>|>1)inifc=0thenInt32.compare(a&&&1l)(b&&&1l)elsecletmakeabcd=((~|a<!24)|||(~|b<!16))|||((~|c<!8)|||(~|d<!0))(* parsing *)letparse_dotted_quadsi=leta=parse_dec_intsiinexpect_charsi'.';letb=parse_dec_intsiinexpect_charsi'.';letc=parse_dec_intsiinexpect_charsi'.';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=trySome(of_string_exns)with_->Noneletto_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)letpp_hum=ppletsexp_of_ti=Sexplib.Sexp.Atom(to_stringi)lett_of_sexpi=matchiwith|Sexplib.Sexp.Atomi->of_string_exni|_->raise(Failure"Ipaddr.V4.t: Unexpected non-atom in sexp")(* Byte conversion *)letof_bytes_rawbso=make(Char.codebs.[0+o])(Char.codebs.[1+o])(Char.codebs.[2+o])(Char.codebs.[3+o])letof_bytes_exnbs=letlen=String.lengthbsiniflen>4thenraise(too_muchbs);iflen<4thenraise(need_morebs);of_bytes_rawbs0letof_bytesbs=trySome(of_bytes_exnbs)with_->Noneletto_bytes_rawibo=Bytes.setb(0+o)(Char.chr((|~)(i>!24)));Bytes.setb(1+o)(Char.chr((|~)(i>!16)));Bytes.setb(2+o)(Char.chr((|~)(i>!8)));Bytes.setb(3+o)(Char.chr((|~)(i>!0)))letto_bytesi=letb=Bytes.create4into_bytes_rawib0;Bytes.to_stringb(* Int32*)letof_int32i=iletto_int32i=i(* Int16 *)letof_int16(a,b)=(~|a<|<16)|||(~|b)letto_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_bytes_exn(Bytes.to_stringmacb)(* Host *)letto_domain_namei=[Int32.to_string(i>!0);Int32.to_string(i>!8);Int32.to_string(i>!16);Int32.to_string(i>!24);"in-addr";"arpa";"";](* constant *)letany=make0000letunspecified=make0000letbroadcast=make255255255255letlocalhost=make127001letnodes=make224001letrouters=make224002modulePrefix=structtypeaddr=t[@@derivingsexp]typet=addr*int[@@derivingsexp]letcompare(pre,sz)(pre',sz')=letc=compareprepre'inifc=0thenPervasives.compareszsz'elsecletip=makeletmasksz=ifsz<=0then0_lelseifsz>=32then0x0_FF_FF_FF_FF_lelse0x0_FF_FF_FF_FF_l<|<(32-sz)letmakeszpre=(pre&&&(masksz),sz)letnetwork_address(pre,sz)addr=pre|||(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=let(p,quad)=_of_string_rawsiinmakepquadlet_of_string_exns=leti=ref0inletres=_of_string_rawsiinexpect_endsi;resletof_string_exns=let(p,quad)=_of_string_exnsinmakepquadletof_strings=trySome(of_string_exns)with_->Noneletof_address_string_exns=let(p,quad)=_of_string_exnsin(makepquad,quad)letof_address_strings=trySome(of_address_string_exns)with_->Noneletof_netmasknmaddr=letrecfind_greatest_onebitsi=ifbits=0_ltheni-1elsefind_greatest_one(bits>|>1)(i+1)inletone=nm&&&(Int32.negnm)inletsz=32-(find_greatest_oneone(ifone=0_lthen33else0))inifnm<>(masksz)thenraise(Parse_error("invalid netmask",to_stringnm))elsemakeszaddrletto_bufferbuf(pre,sz)=Printf.bprintfbuf"%a/%d"to_bufferpreszletto_stringsubnet=letb=Buffer.create18into_bufferbsubnet;Buffer.contentsbletppppfi=Format.fprintfppf"%s"(to_stringi)letpp_hum=ppletto_address_bufferbuf((_,sz)assubnet)addr=to_bufferbuf(network_addresssubnetaddr,sz)letto_address_stringsubnetaddr=letb=Buffer.create18into_address_bufferbsubnetaddr;Buffer.contentsbletmemip(pre,sz)=lethost=32-szin(ip>|>host)=(pre>|>host)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)=pre|||(0x0_FF_FF_FF_FF_l>|>sz)letnetwork(pre,_)=preletbits(_,sz)=szletnetmasksubnet=mask(bitssubnet)end(* TODO: this could be optimized with something trie-like *)letscopei=letmem=Prefix.memiinifmemPrefix.loopbackthenInterfaceelseifmemPrefix.linkthenLinkelseifList.existsmemPrefix.private_blocksthenOrganizationelseifi=unspecifiedthenPointelseifi=broadcastthenAdminelseifmemPrefix.relativethenAdminelseifmemPrefix.multicastthen(ifmemPrefix.multicast_orgthenOrganizationelseifmemPrefix.multicast_adminthenAdminelseifmemPrefix.multicast_linkthenLinkelseGlobal)elseGloballetis_globali=(scopei)=Globalletis_multicasti=Prefix.(memimulticast)letis_privatei=(scopei)<>GlobalendmoduleB128=structtypet=int32*int32*int32*int32letof_int64(a,b)=Int64.(to_int32(shift_right_logicala32),to_int32a,to_int32(shift_right_logicalb32),to_int32b)letto_int64(a,b,c,d)=Int64.(logor(shift_left(of_int32a)32)(of_int32b),logor(shift_left(of_int32c)32)(of_int32d))letof_int32x=xletto_int32x=xletof_int16(a,b,c,d,e,f,g,h)=V4.of_int16(a,b),V4.of_int16(c,d),V4.of_int16(e,f),V4.of_int16(g,h)letto_int16(x,y,z,t)=leta,b=V4.to_int16xandc,d=V4.to_int16yande,f=V4.to_int16zandg,h=V4.to_int16tin(a,b,c,d,e,f,g,h)letto_bytes_raw(a,b,c,d)byteo=V4.to_bytes_rawabyte(o+0);V4.to_bytes_rawbbyte(o+4);V4.to_bytes_rawcbyte(o+8);V4.to_bytes_rawdbyte(o+12)let_of_bytes_exnbs=(* TODO : from cstruct *)letlen=String.lengthbsiniflen>16thenraise(too_muchbs);iflen<16thenraise(need_morebs);lethihi=V4.of_bytes_rawbs0inlethilo=V4.of_bytes_rawbs4inletlohi=V4.of_bytes_rawbs8inletlolo=V4.of_bytes_rawbs12inof_int32(hihi,hilo,lohi,lolo)letcompare(a1,b1,c1,d1)(a2,b2,c2,d2)=matchV4.comparea1a2with|0->beginmatchV4.compareb1b2with|0->beginmatchV4.comparec1c2with|0->V4.compared1d2|n->nend|n->nend|n->nletlogand(a1,b1,c1,d1)(a2,b2,c2,d2)=(a1&&&a2,b1&&&b2,c1&&&c2,d1&&&d2)letlogor(a1,b1,c1,d1)(a2,b2,c2,d2)=(a1|||a2,b1|||b2,c1|||c2,d1|||d2)letlognot(a,b,c,d)=Int32.(lognota,lognotb,lognotc,lognotd)endmoduleV6=structincludeB128(* TODO: Perhaps represent with bytestring? *)letmakeabcdefgh=of_int16(a,b,c,d,e,f,g,h)(* parsing *)letparse_ipv6si=letcompressed=reffalsein(* :: *)letlen=String.lengthsiniflen<!i+2then(raise(need_mores));letuse_bracket=s.[!i]='[';inifuse_bracketthenincri;(* check if it starts with :: *)letl=ifs.[!i]=':'thenbeginincri;ifs.[!i]=':'thenbegincompressed:=true;incri;[-1]endelseraise(bad_char!is);endelse[]inletrecloopnbacc=ifnb>=8thenaccelseif!i>=lenthenaccelseletpos=!iinletx=tryparse_hex_intsiwith_->-1inifx<0thenaccelseifnb=7thenx::accelseif!i<len&&s.[!i]=':'thenbeginincri;if!i<lenthenifs.[!i]=':'thenif!compressedthen(decri;x::acc)(* trailing :: *)elsebegincompressed:=true;incri;loop(nb+2)(-1::x::acc)endelsebeginifis_number16(int_of_chars.[!i])thenloop(nb+1)(x::acc)elseraise(bad_char!is)endelseraise(need_mores)endelseif!i<len&&s.[!i]='.'thenbegini:=pos;letv4=V4.of_string_rawsiinlet(hi,lo)=V4.to_int16v4inlo::hi::accendelsex::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-missingelsebeginifxland0xffff<>xthenraise(Parse_error(Printf.sprintf"component %d out of bounds"i,s));a.(i)<-x;i-1end)7resin(ifuse_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=trySome(of_string_exns)with_->None(* http://tools.ietf.org/html/rfc5952 *)letto_buffer?(v4=false)bufaddr=let(a,b,c,d,e,f,g,h)ascomp=to_int16addrinletv4=matchcompwith|(0,0,0,0,0,0xffff,_,_)->true|_->v4inletrecloopelidezerosacc=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_string?v4l=letbuf=Buffer.create39into_buffer?v4bufl;Buffer.contentsbufletppppfi=Format.fprintfppf"%s"(to_stringi)letpp_hum=ppletsexp_of_ti=Sexplib.Sexp.Atom(to_stringi)lett_of_sexpi=matchiwith|Sexplib.Sexp.Atomi->of_string_exni|_->raise(Failure"Ipaddr.V6.t: Unexpected non-atom in sexp")(* byte conversion *)letof_bytes_rawbso=(* TODO : from cstruct *)lethihi=V4.of_bytes_rawbs(o+0)inlethilo=V4.of_bytes_rawbs(o+4)inletlohi=V4.of_bytes_rawbs(o+8)inletlolo=V4.of_bytes_rawbs(o+12)inof_int32(hihi,hilo,lohi,lolo)letof_bytes_exnbs=(* TODO : from cstruct *)letlen=String.lengthbsiniflen>16thenraise(too_muchbs);iflen<16thenraise(need_morebs);of_bytes_rawbs0letof_bytesbs=trySome(of_bytes_exnbs)with_->Noneletto_bytesi=letbs=Bytes.create16into_bytes_rawibs0;Bytes.to_stringbs(** MAC *)(** {{:https://tools.ietf.org/html/rfc2464#section-7}RFC 2464}. *)letmulticast_to_maci=let(_,_,_,i)=to_int32iinletmacb=Bytes.create6inBytes.setmacb0(Char.chr0x33);Bytes.setmacb1(Char.chr0x33);Bytes.setmacb2(Char.chr((|~)(i>!24)));Bytes.setmacb3(Char.chr((|~)(i>!16)));Bytes.setmacb4(Char.chr((|~)(i>!8)));Bytes.setmacb5(Char.chr((|~)(i>!0)));Macaddr.of_bytes_exn(Bytes.to_stringmacb)(* Host *)letto_domain_name(a,b,c,d)=[hex_string_of_int32((d>|>0)&&&0xF_l);hex_string_of_int32((d>|>4)&&&0xF_l);hex_string_of_int32((d>|>8)&&&0xF_l);hex_string_of_int32((d>|>12)&&&0xF_l);hex_string_of_int32((d>|>16)&&&0xF_l);hex_string_of_int32((d>|>20)&&&0xF_l);hex_string_of_int32((d>|>24)&&&0xF_l);hex_string_of_int32((d>|>28)&&&0xF_l);hex_string_of_int32((c>|>0)&&&0xF_l);hex_string_of_int32((c>|>4)&&&0xF_l);hex_string_of_int32((c>|>8)&&&0xF_l);hex_string_of_int32((c>|>12)&&&0xF_l);hex_string_of_int32((c>|>16)&&&0xF_l);hex_string_of_int32((c>|>20)&&&0xF_l);hex_string_of_int32((c>|>24)&&&0xF_l);hex_string_of_int32((c>|>28)&&&0xF_l);hex_string_of_int32((b>|>0)&&&0xF_l);hex_string_of_int32((b>|>4)&&&0xF_l);hex_string_of_int32((b>|>8)&&&0xF_l);hex_string_of_int32((b>|>12)&&&0xF_l);hex_string_of_int32((b>|>16)&&&0xF_l);hex_string_of_int32((b>|>20)&&&0xF_l);hex_string_of_int32((b>|>24)&&&0xF_l);hex_string_of_int32((b>|>28)&&&0xF_l);hex_string_of_int32((a>|>0)&&&0xF_l);hex_string_of_int32((a>|>4)&&&0xF_l);hex_string_of_int32((a>|>8)&&&0xF_l);hex_string_of_int32((a>|>12)&&&0xF_l);hex_string_of_int32((a>|>16)&&&0xF_l);hex_string_of_int32((a>|>20)&&&0xF_l);hex_string_of_int32((a>|>24)&&&0xF_l);hex_string_of_int32((a>|>28)&&&0xF_l);"ip6";"arpa";"";](* constant *)letunspecified=make00000000letlocalhost=make00000001letinterface_nodes=make0xff010000001letlink_nodes=make0xff020000001letinterface_routers=make0xff010000002letlink_routers=make0xff020000002letsite_routers=make0xff050000002modulePrefix=structtypeaddr=t[@@derivingsexp]typet=addr*int[@@derivingsexp]letcompare(pre,sz)(pre',sz')=letc=compareprepre'inifc=0thenPervasives.compareszsz'elsecletip=makelet_full=letf=0x0_FFFF_FFFF_linf,f,f,fletmasksz=V4.Prefix.(mask(sz-0),mask(sz-32),mask(sz-64),mask(sz-96))letmakeszpre=(logandpre(masksz),sz)letnetwork_address(pre,sz)addr=logorpre(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=let(p,v6)=_of_string_rawsiinmakepv6let_of_string_exns=leti=ref0inletres=_of_string_rawsiinexpect_endsi;resletof_string_exns=let(p,v6)=_of_string_exnsinmakepv6letof_strings=trySome(of_string_exns)with_->Noneletof_address_string_exns=let(p,v6)=_of_string_exnsin(makepv6,v6)letof_address_strings=trySome(of_address_string_exns)with_->Noneletof_netmasknmaddr=make(matchnmwith|(0_l,0_l,0_l,0_l)->0|(lsw,0_l,0_l,0_l)->V4.Prefix.(bits(of_netmasklswV4.any))|(-1_l,lsw,0_l,0_l)->V4.Prefix.(bits(of_netmasklswV4.any))+32|(-1_l,-1_l,lsw,0_l)->V4.Prefix.(bits(of_netmasklswV4.any))+64|(-1_l,-1_l,-1_l,lsw)->V4.Prefix.(bits(of_netmasklswV4.any))+96|_->raise(Parse_error("invalid netmask",to_stringnm)))addrletto_bufferbuf(pre,sz)=Printf.bprintfbuf"%a/%d"(to_buffer~v4:false)preszletto_stringsubnet=letbuf=Buffer.create43into_bufferbufsubnet;Buffer.contentsbufletppppfi=Format.fprintfppf"%s"(to_stringi)letpp_hum=ppletto_address_bufferbuf((_,sz)assubnet)addr=to_bufferbuf(network_addresssubnetaddr,sz)letto_address_stringsubnetaddr=letb=Buffer.create43into_address_bufferbsubnetaddr;Buffer.contentsbletmemip(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,_)=preletbits(_,sz)=szletnetmasksubnet=mask(bitssubnet)end(* 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.multicastthenlet(x,_,_,_,_,_,_,_)=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.code(String.getbi)infunmac->letbmac=Macaddr.to_bytesmacinletc_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)<>Globalendtype('v4,'v6)v4v6=V4of'v4|V6of'v6[@@derivingsexp]typet=(V4.t,V6.t)v4v6[@@derivingsexp]letcompareab=matcha,bwith|V4a,V4b->V4.compareab|V6a,V6b->V6.compareab|V4_,V6_->-1|V6_,V4_->1letto_string=function|V4x->V4.to_stringx|V6x->V6.to_stringxletto_bufferbuf=function|V4x->V4.to_bufferbufx|V6x->V6.to_bufferbufxletppppfi=Format.fprintfppf"%s"(to_stringi)letpp_hum=ppletof_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=of_string_raws(ref0)letof_strings=trySome(of_string_exns)with_->Noneletv6_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=function|V4v4->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_namev6modulePrefix=structmoduleAddr=structletto_v6=to_v6endtypeaddr=t[@@derivingsexp]typet=(V4.Prefix.t,V6.Prefix.t)v4v6[@@derivingsexp]letcompareab=matcha,bwith|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=of_string_raws(ref0)letof_strings=trySome(of_string_exns)with_->Noneletv6_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)letppppfi=Format.fprintfppf"%s"(to_stringi)letpp_hum=ppend