123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229(*
* Copyright (c) 2012 Richard Mortier <mort@cantab.net>
*
* 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.
*)(* open Wire *)(* open Re_str *)openPrintfopenOperatorstypet=stringlisttypekey=stringmoduleOrdered=structtypex=ttypet=xletreccomparel1l2=match(l1,l2)with|[],[]->0|_::_,[]->1|[],_::_->-1|h1::t1,h2::t2->matchString.compareh1h2with|0->comparet1t2|i->iendmoduleMap=Map.Make(Ordered)moduleSet=Set.Make(Ordered)letempty=[]letappend=(@)letconsxxs=(String.lowercase_asciix)::xsletto_string_listdn=dnletof_string_list=List.mapString.lowercase_asciiletto_string=String.concat"."(* TODO: this looks wrong for the trailing dot case/we should ensure
we handle the trailing dot case consistently *)letof_string(s:string):t=Re.Str.split(Re.Str.regexp"\\.")(String.lowercase_asciis)letstring_to_domain_name=of_stringletof_ipaddrip=of_string_list(Ipaddr.to_domain_nameip|>Domain_name.to_strings)typelabel=|Lofstring*int(* string *)|Pofint*int(* pointer *)|Zofint(* zero; terminator *)letparse_labelbasebuf=(* NB. we're shifting buf for each call; offset is for the names Hashtbl *)matchCstruct.get_uint8buf0with|0->Zbase,1|vwhen((vland0b0_11000000)=0b0_11000000)->letptr=((vland0b0_00111111)lsl8)+Cstruct.get_uint8buf1inP(ptr,base),2|v->if((0<v)&&(v<64))then(letname=Cstruct.(subbuf1v|>to_string)inL(name,base),1+v)elsefailwith(sprintf"Name.parse_label: invalid length %d"v)letparsenamesbasebuf=(* what. a. mess. *)letrecauxoffsetsnamebasebufsize=matchparse_labelbasebufwith|(Zoaszero,offset)->Hashtbl.addnamesozero;name,base+offset,Cstruct.shiftbufoffset,(size+1)|(L(n,o)aslabel,offset)->Hashtbl.addnamesolabel;offsets|>List.iter(funoff->(Hashtbl.addnamesofflabel));aux(o::offsets)(n::name)(base+offset)(Cstruct.shiftbufoffset)(size+offset)|(P(p,_),offset)->(matchHashtbl.find_allnamespwith|[]->failwith(sprintf"Name.parse_pointer: Cannot dereference pointer to (%n) at position (%n)"pbase);|all->letlabels=(all|>List.filter(functionL_->true|_->false))in(* update the list of offsets-so-far to include current label *)(base::offsets)|>List.iter(funo->(List.revlabels)|>List.iter(funn->Hashtbl.addnameson));(* convert label list into string list *)letlabels_str=(labels||>(function|L(nm,_)->nm|_->failwith"Name.parse"))inletnb_labels=List.lengthlabels_strinletlabel_size=List.fold_left(funsizestr->size+(String.lengthstr))nb_labelslabels_strinlabels_str@name,base+offset,Cstruct.shiftbufoffset,(size+label_size))inletname,base,buf,size=aux[][]basebuf0inifsize>255thenfailwith(sprintf"Name.parse: invalid length %d"size)elseList.revname,(base,buf)letmarshal?(compress=true)namesbasebufname=letnot_compressednamesbasebufname=letbase,buf=List.fold_left(fun(base,buf)label->letlabel,llen=charstrlabelinCstruct.blit_from_stringlabel0buf0llen;base+llen,Cstruct.shiftbufllen)(base,buf)nameinnames,base+1,Cstruct.shiftbuf1inletcompressednamesbasebufname=letpointero=((0b11_l<|<14)+++(Int32.of_into))|>Int32.to_intinletlookupnamesn=trySome(Map.findnnames)withNot_found->Noneinletrecauxnamesoffsetlabels=matchlookupnameslabelswith|None->(matchlabelswith|[]->Cstruct.set_uint8bufoffset0;names,offset+1|(hd::tl)asls->letnames=Map.addls(base+offset)namesinletlabel,llen=charstrhdinCstruct.blit_from_stringlabel0bufoffsetllen;auxnames(offset+llen)tl)|Someo->Cstruct.BE.set_uint16bufoffset(pointero);names,offset+2inletnames,offset=auxnames0nameinnames,(base+offset),Cstruct.shiftbufoffsetinifcompressthencompressednamesbasebufnameelsenot_compressednamesbasebufname(* Hash-consing: character strings *)moduleCSH=Hashcons.Make(structtypet=stringletequalab=(a=b)lethashs=Hashtbl.hashsend)letcstr_hash=ref(CSH.create101)lethashcons_strings=CSH.hashcons!cstr_hashs(*
Hash-consing: domain names (string lists). This requires a little
more subtlety than the Hashcons module gives us directly: we want to
merge common suffixes, and we're downcasing everything.
N.B. RFC 4343 says we shouldn't do this downcasing.
*)moduleDNH=Hashcons.Make(structtypex=ttypet=xletequalab=(a=b)lethashs=Hashtbl.hashsend)letdn_hash=ref(DNH.create101)letrechashcons(x:t)=matchxwith|[]->DNH.hashcons!dn_hash[]|h::t->letth=hashconstinDNH.hashcons!dn_hash(((hashcons_string(String.lowercase_asciih)).Hashcons.node)::(th.Hashcons.node))letclear_cons_tables()=DNH.clear!dn_hash;CSH.clear!cstr_hash;dn_hash:=DNH.create1;cstr_hash:=CSH.create1exceptionBadDomainNameofstringletto_keydomain_name=letchecks=ifString.containss'\000'thenraise(BadDomainName"contains null character");ifString.lengths=0thenraise(BadDomainName"zero-length label");ifString.lengths>63thenraise(BadDomainName("label too long: "^s))inList.itercheckdomain_name;String.concat"\000"(List.rev_mapString.lowercase_asciidomain_name)letdnssec_compareab=match(a,b)with|[],[]->0|[],_->-1|_,[]->1|a::a_tl,b::b_tl->if(String.compareab=0)thencomparea_tlb_tlelse(if(String.lengtha)=(String.lengthb)thenString.compareabelsecompare(String.lengtha)(String.lengthb))letdnssec_compare_str=dnssec_compare