(* * $Id: xdr.ml 1998 2014-08-24 20:41:09Z gerd $ *) (* This is an XDR implementation. * See RFC 1014 *) open Netnumber open Printf exception Propagate of string;; (**********************************************************************) (* auxiliary stuff: *) let aux_cmp (ha,sa) (hb,sb) = if ha = hb then compare sa sb else ha - hb ;; let all_distinct_q l = (* returns whether all elements of l are distinct *) let a = Array.map (fun s -> (Hashtbl.hash s), s) (Array.of_list l) in Array.sort aux_cmp a; let distinct = ref true in let k = ref 0 in while !distinct && !k < Array.length a - 1 do let (ha,sa) = a.( !k ) in let (hb,sb) = a.( !k + 1 ) in distinct := (ha != hb) && (sa <> sb); incr k done; !distinct ;; let all_distinct = function [] | [_] -> true | [a;b] -> a <> b | l -> all_distinct_q l ;; let sub_set_q l1 l2 = (* returns whether all elements of l1 occur in l2 *) let a1 = Array.map (fun s -> (Hashtbl.hash s), s) (Array.of_list l1) in let a2 = Array.map (fun s -> (Hashtbl.hash s), s) (Array.of_list l2) in Array.sort aux_cmp a1; Array.sort aux_cmp a2; let occurs = ref true in let k1 = ref 0 in let k2 = ref 0 in while !occurs && !k1 < Array.length a1 && !k2 < Array.length a2 do let (h1,s1) = a1.( !k1 ) in let found = ref false in while not !found && !k2 < Array.length a2 do let (h2,s2) = a2.( !k2 ) in found := (h1 == h2) && (s1 = s2); if not !found then incr k2 done; occurs := !found; incr k1 done; !occurs ;; let sub_set l1 l2 = match (l1,l2) with ([],_) -> true | ([x],_) -> List.mem x l2 | _ -> sub_set_q l1 l2 ;; (* (* currently unused! *) let equal_sets_q l1 l2 = (* returns whether all elements of l1 occur in l2, and vice versa *) let a1 = Array.map (fun s -> (Hashtbl.hash s), s) (Array.of_list l1) in let a2 = Array.map (fun s -> (Hashtbl.hash s), s) (Array.of_list l2) in Array.sort aux_cmp a1; Array.sort aux_cmp a2; let equal = ref true in let k1 = ref 0 in let k2 = ref 0 in let k2_match = ref false in (* can only be false when !k2 = 0 *) while !equal && !k1 < Array.length a1 && !k2 < Array.length a2 do let (h1,s1) = a1.( !k1 ) in let (h2,s2) = a2.( !k2 ) in if (h1 == h2) && (s1 = s2) then ( incr k1; k2_match := true; (* a match for the k2 element has been found *) ) else ( if !k2_match then ( incr k2; while !k2 < Array.length a2 && (h2,s2) = a2.( !k2 ) do incr k2 done; if !k2 < Array.length a2 then ( let (h2',s2') = a2.( !k2 ) in if (h1 == h2') && (s1 = s2') then ( incr k1; ) else equal := false ) else equal := false ) else equal := false ) done; if !equal && !k1 = Array.length a1 && !k2 < Array.length a2 && !k1 > 0 then ( (* !k1 > 0 ==> a1 is not empty && !k2_match, * !k2 < length a2 ==> a2 is not empty *) let (h2,s2) = a2.( !k2 ) in incr k2; while !k2 < Array.length a2 && (h2,s2) = a2.( !k2 ) do incr k2 done; ); !equal && !k1 = Array.length a1 && !k2 = Array.length a2 ;; let equal_sets l1 l2 = match (l1,l2) with ([],[]) -> true | ([x],[y]) -> x = y | _ -> equal_sets_q l1 l2 ;; *) (**********************************************************************) (* definition of XDR types and type systems *) (**********************************************************************) (* restriction: it is not allowed to have an X_param as enumerator type * in union_over_enum. There must always be a real X_enum or, in a * type system, a resolvable X_type at this position. *) type xdr_type_term = X_int | X_uint | X_hyper | X_uhyper | X_enum of (string * int4) list | X_float | X_double | X_opaque_fixed of uint4 | X_opaque of uint4 | X_string of uint4 | X_mstring of string * uint4 | X_array_fixed of xdr_type_term * uint4 | X_array of xdr_type_term * uint4 | X_struct of (string * xdr_type_term) list | X_union_over_int of (int4 * xdr_type_term) list * xdr_type_term option | X_union_over_uint of (uint4 * xdr_type_term) list * xdr_type_term option | X_union_over_enum of xdr_type_term * (string * xdr_type_term) list * xdr_type_term option | X_void | X_type of string | X_param of string | X_rec of (string * xdr_type_term) (* define a recursive type *) | X_refer of string (* refer to a recursive type *) | X_direct of xdr_type_term * (string -> int ref -> int -> exn) * (exn -> string -> int ref -> unit) * (exn -> int) ;; module StringSet = Set.Make(String) ;; type xdr_type0 = { mutable term : xdr_term; mutable params : StringSet.t; (* "params" is normally only non-empty in the top node *) mutable min_size : int (* min_size: the minimum number of bytes every element of the array will take in XDR form. This does not include any inner parameters. *) } and xdr_term = T_int | T_uint | T_hyper | T_uhyper | T_enum of (string * int32) array (* array must be sorted by ascending int32 *) | T_float | T_double | T_opaque_fixed of uint4 | T_opaque of uint4 | T_string of uint4 | T_mstring of string * uint4 | T_array_fixed of xdr_type0 * uint4 | T_array of xdr_type0 * (* max size: *) uint4 | T_struct of (string * xdr_type0) array | T_union_over_int of (int4, xdr_type0) Hashtbl.t * xdr_type0 option | T_union_over_uint of (uint4, xdr_type0) Hashtbl.t * xdr_type0 option | T_union_over_enum of xdr_type0 * xdr_type0 option array * xdr_type0 option (* The array corresponds to the T_enum array. None means that the * constant is not mapped. *) | T_void | T_param of string | T_rec of (string * xdr_type0) | T_refer of (string * xdr_type0) | T_direct of xdr_type0 * (string -> int ref -> int -> exn) * (exn -> string -> int ref -> unit) * (exn -> int) ;; type xdr_type = xdr_type0 * xdr_type0 (* left: includes T_rec and T_refer, right: does not include T_rec, T_refer *) type xdr_type_term_system = (string * xdr_type_term) list ;; type xdr_type_system = (string * xdr_type) list (* export xdr_type_system in an opaque manner *) let t_name = function | T_int -> "T_int" | T_uint -> "T_uint" | T_hyper -> "T_hyper" | T_uhyper -> "T_uhyper" | T_enum _ -> "T_enum" | T_float -> "T_float" | T_double -> "T_double" | T_opaque_fixed _ -> "T_opaque_fixed" | T_opaque _ -> "T_opaque" | T_string _ -> "T_string" | T_mstring(_,_) -> "T_mstring" | T_array_fixed (_,_) -> "T_array_fixed" | T_array (_,_) -> "T_array" | T_struct _ -> "T_struct" | T_union_over_int(_,_) -> "T_union_over_int" | T_union_over_uint(_,_) -> "T_union_over_uint" | T_union_over_enum(_,_,_) -> "T_union_over_enum" | T_void -> "T_void" | T_param _ -> "T_param" | T_rec _ -> "T_rec" | T_refer _ -> "T_refer" | T_direct _ -> "T_direct" let x_bool = X_enum ["FALSE", int4_of_int 0; "TRUE", int4_of_int 1] ;; let x_optional t = X_union_over_enum (x_bool, ["TRUE", t; "FALSE", X_void], None) ;; let x_opaque_max = X_opaque (mk_uint4 ('\255', '\255', '\255', '\255'));; let x_string_max = X_string (mk_uint4 ('\255', '\255', '\255', '\255'));; let x_mstring_max name = X_mstring (name, mk_uint4 ('\255', '\255', '\255', '\255'));; let x_array_max t = X_array (t, (mk_uint4 ('\255', '\255', '\255', '\255')));; (**********************************************************************) (* definition of XDR values *) (**********************************************************************) type xdr_value_version = [ `V1 | `V2 | `V3 | `V4 | `Ocamlrpcgen ] type xdr_value = XV_int of int4 | XV_uint of uint4 | XV_hyper of int8 | XV_uhyper of uint8 | XV_enum of string | XV_float of fp4 | XV_double of fp8 | XV_opaque of string | XV_string of string | XV_array of xdr_value array | XV_struct of (string * xdr_value) list | XV_union_over_int of (int4 * xdr_value) | XV_union_over_uint of (uint4 * xdr_value) | XV_union_over_enum of (string * xdr_value) | XV_void | XV_enum_fast of int | XV_struct_fast of xdr_value array | XV_union_over_enum_fast of (int * xdr_value) | XV_array_of_string_fast of string array | XV_mstring of Xdr_mstring.mstring | XV_direct of exn * int ;; let xv_true = XV_enum_fast 1 (* "TRUE" *);; let xv_false = XV_enum_fast 0 (* "FALSE" *);; let xv_none = XV_union_over_enum_fast (0,XV_void);; let xv_some v = XV_union_over_enum_fast (1,v);; exception Dest_failure let dest_xv_int v = match v with XV_int x -> x | _ -> raise Dest_failure;; let dest_xv_uint v = match v with XV_uint x -> x | _ -> raise Dest_failure;; let dest_xv_hyper v = match v with XV_hyper x -> x | _ -> raise Dest_failure;; let dest_xv_uhyper v = match v with XV_uhyper x -> x | _ -> raise Dest_failure;; let dest_xv_enum v = match v with XV_enum x -> x | _ -> raise Dest_failure;; let dest_xv_enum_fast v = match v with XV_enum_fast x -> x | _ -> raise Dest_failure;; let dest_xv_float v = match v with XV_float x -> x | _ -> raise Dest_failure;; let dest_xv_double v = match v with XV_double x -> x | _ -> raise Dest_failure;; let dest_xv_opaque v = match v with XV_opaque x -> x | _ -> raise Dest_failure;; let dest_xv_string v = match v with XV_string x -> x | _ -> raise Dest_failure;; let dest_xv_mstring v = match v with XV_mstring x -> x | _ -> raise Dest_failure;; let dest_xv_array v = match v with XV_array x -> x | _ -> raise Dest_failure;; let dest_xv_array_of_string_fast v = match v with XV_array_of_string_fast x -> x | _ -> raise Dest_failure;; let dest_xv_struct v = match v with XV_struct x -> x | _ -> raise Dest_failure;; let dest_xv_struct_fast v = match v with XV_struct_fast x -> x | _ -> raise Dest_failure;; let dest_xv_void v = match v with XV_void -> () | _ -> raise Dest_failure;; let dest_xv_union_over_int v = match v with XV_union_over_int x -> x | _ -> raise Dest_failure;; let dest_xv_union_over_uint v = match v with XV_union_over_uint x -> x | _ -> raise Dest_failure;; let dest_xv_union_over_enum v = match v with XV_union_over_enum x -> x | _ -> raise Dest_failure;; let dest_xv_union_over_enum_fast v = match v with XV_union_over_enum_fast x -> x | _ -> raise Dest_failure;; let fail_map_xv_enum_fast k = failwith ("Xdr.map_xv_enum_fast [" ^ string_of_int k ^ "]") ;; let map_xv_enum_fast0 t v = match t.term with T_enum l -> let m = Array.length l in ( match v with XV_enum_fast k -> if k >= 0 && k < m then snd(Array.unsafe_get l k) else fail_map_xv_enum_fast 1 | XV_enum name -> let k = ref 0 in while !k < m && (fst l.( !k ) <> name) do incr k done; if !k >= m then fail_map_xv_enum_fast 2; snd(l.( !k )) | _ -> fail_map_xv_enum_fast 3 ) | _ -> fail_map_xv_enum_fast 4 let map_xv_enum_fast (_,t) v = map_xv_enum_fast0 t v let fail_map_xv_struct_fast k = failwith ("Xdr.map_xv_struct_fast [" ^ string_of_int k ^ "]") ;; let map_xv_struct_fast0 t v = match t.term with T_struct decl -> let m = Array.length decl in ( match v with XV_struct_fast x -> let k = Array.length x in if k = m then x else fail_map_xv_struct_fast 1 | XV_struct l -> ( try Array.map (fun (name,y) -> List.assoc name l) decl with Not_found -> fail_map_xv_struct_fast 2 ) | _ -> fail_map_xv_struct_fast 3 ) | _ -> fail_map_xv_struct_fast 4 let map_xv_struct_fast (_,t) v = map_xv_struct_fast0 t v let fail_map_xv_union_over_enum_fast k = failwith ("Xdr.map_xv_union_over_enum_fast [" ^ string_of_int k ^ "]") ;; let map_xv_union_over_enum_fast0 t v = match t.term with T_union_over_enum( { term = T_enum e }, u, u_dfl ) -> let m = Array.length e in assert( m = Array.length u ); ( match v with XV_union_over_enum_fast(k, x) -> if k >= 0 && k < m then (k, (snd e.(k)), x) else fail_map_xv_union_over_enum_fast 1 | XV_union_over_enum(name, x) -> let k = ref 0 in while !k < m && fst(e.( !k )) <> name do incr k done; if !k >= m then fail_map_xv_union_over_enum_fast 2; (!k, (snd e.(!k)), x) | _ -> fail_map_xv_union_over_enum_fast 3; ) | _ -> fail_map_xv_union_over_enum_fast 4 let map_xv_union_over_enum_fast (_,t) v = map_xv_union_over_enum_fast0 t v exception Xdr_format of string;; exception Xdr_format_message_too_long of xdr_value;; (* raised in unpack_xdr_value if the byte stream does not match * the expected type. The string is an explanation and might be * useful while debugging. In the special case Xdr_format_message_too_long * there are more bytes than expected, but a prefix matches the type. * The prefix is returned as xdr_value. *) let () = Netexn.register_printer (Xdr_format "") (function | Xdr_format s -> sprintf "Xdr.Xdr_format(%S)" s | _ -> assert false ) exception Xdr_failure of string let safe_add x y = (* exported *) (* pre: x >= 0 && y >= 0 *) let s = x + y in if s < 0 then (* can only happen on 32 bit platforms *) raise(Xdr_failure "int overflow while computing size"); s let safe_mul x y = (* exported *) (* pre: x >= 0 && y >= 0 *) if x=0 || y=0 then 0 else let n = max_int / y in if x > n then raise(Xdr_failure "int overflow while computing size"); x * y (**********************************************************************) (* check if XDR types are well-formed *) (**********************************************************************) (* TODO: check on recursions without finite fix point. *) let rec validate_xdr_type_i1 (r:xdr_type_term -> xdr_type0) (b:(string * xdr_type0) list) (t:xdr_type_term) : xdr_type0 = (* r: function that resolves X_type references * t: the xdr_type_term to validate * b: list of recursive bindings * * raise Not_found on any error *) let mktype tm = { term = tm; params = StringSet.empty; min_size = (-1) } in (* min_size is calculated in a second pass *) match t with X_int -> mktype T_int | X_uint -> mktype T_uint | X_hyper -> mktype T_hyper | X_uhyper -> mktype T_uhyper | X_float -> mktype T_float | X_double -> mktype T_double | X_void -> mktype T_void | X_enum e -> let e_names, e_values = List.split e in if all_distinct e_names && all_distinct e_values then let ea = Array.map (fun (n,i) -> (n, Netnumber.int32_of_int4 i)) (Array.of_list e) in Array.sort (fun (_,i) (_,i') -> compare i i') ea; mktype (T_enum ea) else raise (Propagate "Bad enumeration type: double values") | X_opaque_fixed n -> mktype (T_opaque_fixed n) | X_opaque n -> mktype (T_opaque n) | X_string n -> mktype (T_string n) | X_mstring (name,n) -> mktype (T_mstring (name,n)) | X_array_fixed (s,n) -> let nL = int64_of_uint4 n in if nL > 0x3fff_ffff_ffffL then raise (Propagate "Bad fixed array: bound too high"); mktype (T_array_fixed(validate_xdr_type_i1 r b s, n)) | X_array (s,n) -> mktype (T_array (validate_xdr_type_i1 r b s, n)) | X_struct s -> let s_names, s_types = List.split s in if all_distinct s_names then mktype (T_struct (Array.of_list (List.map (fun (n,x) -> n,validate_xdr_type_i1 r b x) s))) else raise (Propagate "Bad struct type: components with same names found") | X_union_over_int (u, default) -> let u_values, u_types = List.split u in if all_distinct u_values then begin let default' = match default with Some d -> Some (validate_xdr_type_i1 r b d) | None -> None in let htbl = Hashtbl.create(List.length u) in List.iter (fun (n,x) -> let x' = validate_xdr_type_i1 r b x in Hashtbl.add htbl n x') u; mktype(T_union_over_int(htbl, default')) end else raise (Propagate "Bad union_over_int type: variants found with same tags") | X_union_over_uint (u,default) -> let u_values, u_types = List.split u in if all_distinct u_values then begin let default' = match default with Some d -> Some (validate_xdr_type_i1 r b d) | None -> None in let htbl = Hashtbl.create(List.length u) in List.iter (fun (n,x) -> let x' = validate_xdr_type_i1 r b x in Hashtbl.add htbl n x') u; mktype(T_union_over_uint(htbl, default')) end else raise (Propagate "Bad union_over_uint type: variants found with same tags") | X_union_over_enum (e,u,default) -> let e' = validate_xdr_type_i1 r b e in let u_values, u_types = List.split u in let el = match e'.term with T_enum x -> x | _ -> raise (Propagate "Bad union_over_enum type: discriminator is not enumerator") in let el_names, el_values = List.split (Array.to_list el) in if all_distinct u_values && sub_set u_values el_names then begin let default' = match default with Some d -> Some (validate_xdr_type_i1 r b d) | None -> None in mktype (T_union_over_enum (e', Array.map (fun (name, _) -> try Some(validate_xdr_type_i1 r b (List.assoc name u)) with Not_found -> default' ) el, default')) end else raise (Propagate "Bad union_over_enum type: variants found with identical tags") | X_type _ -> r t | X_param p -> mktype (T_param p) | X_rec (name, s) -> let node = mktype T_void in let t' = validate_xdr_type_i1 r ((name,node)::b) s in node.term <- T_rec (name, t'); node | X_refer name -> mktype (T_refer (name, List.assoc name b)) | X_direct(s, read, write, size) -> mktype (T_direct (validate_xdr_type_i1 r b s, read, write, size)) ;; let rec find_params (t:xdr_type0) : StringSet.t = (* collect all parameters *) match t.term with T_param p -> StringSet.singleton p | T_array_fixed (t',n) -> find_params t' | T_array (t',n) -> find_params t' | T_struct s -> Array.fold_left (fun set (s,t') -> StringSet.union (find_params t') set) StringSet.empty s | T_union_over_int (htbl,def_opt) -> Hashtbl.fold (fun n t' set -> StringSet.union (find_params t') set) htbl (match def_opt with None -> StringSet.empty | Some def -> find_params def) | T_union_over_uint (htbl,def_opt) -> Hashtbl.fold (fun n t' set -> StringSet.union (find_params t') set) htbl (match def_opt with None -> StringSet.empty | Some def -> find_params def) | T_union_over_enum (e,u,def_opt) -> Array.fold_left (fun set t' -> match t' with Some t'' -> StringSet.union (find_params t'') set | None -> set) (match def_opt with None -> StringSet.empty | Some def -> find_params def) u | T_rec (_,t') -> find_params t' | T_direct(t',_,_,_) -> find_params t' | _ -> StringSet.empty ;; (* Elimination of rec/refer *) let map_opt f o = match o with | None -> None | Some x -> Some(f x) let map_hashtbl f t = let acc = Hashtbl.create (Hashtbl.length t) in Hashtbl.iter (fun k v -> let v' = f k v in Hashtbl.add acc k v'; (* !!! reverses order of bindings !!! *) ) t; acc let rec elim_rec t = (* get rid of T_rec and T_refer *) match t.term with | T_int | T_uint | T_hyper | T_uhyper | T_enum _ | T_float | T_double | T_opaque_fixed _ | T_opaque _ | T_string _ | T_mstring _ | T_void | T_param _ -> t | T_array_fixed(t',n) -> { t with term = T_array_fixed(elim_rec t', n) } | T_array(t',n) -> { t with term = T_array(elim_rec t', n) } | T_struct s -> let s' = Array.map (fun (n,t') -> (n, elim_rec t')) s in { t with term = T_struct s' } | T_union_over_int(ht, dt) -> let ht' = map_hashtbl (fun c t' -> elim_rec t') ht in let dt' = map_opt elim_rec dt in { t with term = T_union_over_int(ht', dt') } | T_union_over_uint(ht, dt) -> let ht' = map_hashtbl (fun c t' -> elim_rec t') ht in let dt' = map_opt elim_rec dt in { t with term = T_union_over_uint(ht', dt') } | T_union_over_enum(et,ct,dt) -> let et' = elim_rec et in let ct' = Array.map (map_opt elim_rec) ct in let dt' = map_opt elim_rec dt in { t with term = T_union_over_enum(et',ct',dt') } | T_rec(n,t') -> elim_rec t' | T_refer(n,t') -> t' | T_direct(t',read,write,size) -> { t with term = T_direct(elim_rec t', read, write, size) } let rec calc_min_size t = let ( ++ ) x y = (* pre: x >= 0 && y >= 0 *) let s = x + y in if s < 0 then (* can only happen on 32 bit platforms *) raise(Propagate("Minimum size of type exceeds limit")); s in let calc_for_union u_snd default = ( match default with | None -> () | Some d -> calc_min_size d ); List.iter (fun t' -> calc_min_size t') u_snd; let l = (match default with | None -> [] | Some d -> [d] ) @ u_snd in assert(l <> []); 4 ++ (List.fold_left (fun acc x -> min acc x.min_size ) ((List.hd l).min_size) (List.tl l) ) in let hashtbl_vals h = Hashtbl.fold (fun _ v acc -> v :: acc) h [] in let optarray_elems a = Array.fold_left (fun acc x_opt -> match x_opt with | None -> acc | Some x -> x :: acc ) [] a in if t.min_size < 0 then ( t.min_size <- 0; (* for stopping recursions *) ( match t.term with T_int -> t.min_size <- 4 | T_uint -> t.min_size <- 4 | T_hyper -> t.min_size <- 8 | T_uhyper -> t.min_size <- 8 | T_float -> t.min_size <- 4 | T_double -> t.min_size <- 8 | T_void -> t.min_size <- 0 | T_enum e -> t.min_size <- 4 | T_opaque_fixed n -> let nL = int64_of_uint4 n in let min_size = if nL=0L then 0 else Int64.to_int(Int64.succ (Int64.div (Int64.pred nL) 4L)) in t.min_size <- min_size | T_opaque n -> t.min_size <- 4 | T_string n -> t.min_size <- 4 | T_mstring (name,n) -> t.min_size <- 4 | T_array_fixed (s,n) -> calc_min_size s; if s.min_size = 0 then raise(Propagate "Array elements must not have length 0"); let nL = int64_of_uint4 n in let n_max = max_int / s.min_size in if nL > Int64.of_int n_max then raise(Propagate "Minimum size of type exceeds limit"); let iL = Int64.of_int s.min_size in t.min_size <- Int64.to_int (Int64.mul nL iL) | T_array (s,n) -> calc_min_size s; if s.min_size = 0 then raise(Propagate "Array elements must not have length 0"); t.min_size <- 4 | T_struct s -> Array.iter (fun (_,t') -> calc_min_size t') s; t.min_size <- (Array.fold_left (fun acc (_,x) -> acc ++ x.min_size ) 0 s ) | T_union_over_int (u, default) -> t.min_size <- calc_for_union (hashtbl_vals u) default | T_union_over_uint (u, default) -> t.min_size <- calc_for_union (hashtbl_vals u) default | T_union_over_enum (e,u,default) -> t.min_size <- calc_for_union (optarray_elems u) default | T_param p -> (* not optimal, but we do not know it better at this point *) t.min_size <- 0 | T_direct(t',_,_,_) -> calc_min_size t'; t.min_size <- t'.min_size | T_rec (_,t') -> calc_min_size t'; t.min_size <- t'.min_size | T_refer (r,t') -> calc_min_size t'; t.min_size <- t'.min_size; (* eprintf "%s: " r*) ); (* eprintf "min_size(%s) = %d\n" (t_name t.term) t.min_size*) ) let rec validate_xdr_type (t:xdr_type_term) : xdr_type = let r n = raise (Propagate "Cannot resolve X_type element") in try let t0' = validate_xdr_type_i1 r [] t in let pl = find_params t0' in t0'.params <- pl; let t1' = elim_rec t0' in calc_min_size t0'; calc_min_size t1'; (t0', t1') with Not_found -> failwith "Xdr.validate_xdr_type: unspecified error" | Propagate s -> failwith ("Xdr.validate_xdr_type: " ^ s) ;; let params (t:xdr_type) = StringSet.fold (fun p acc -> p :: acc) (fst t).params [] let rec expand_X_type (s:xdr_type_system) (t:xdr_type_term) : xdr_type0 = match t with X_type n -> begin let rec r s1 s2 = match s2 with [] -> raise (Propagate ("Cannot resolve X_type " ^ n)) | (n',t') :: s2' -> if n = n' then fst t' else r (s1 @ [n',t']) s2' in r [] s end | _ -> raise (Propagate "Found X_type where it must not occur") ;; let validate_xdr_type_system (s:xdr_type_term_system) : xdr_type_system = let names = List.map fst s in if all_distinct names then begin let rec r (s1:xdr_type_system) (s2:xdr_type_term_system) = match s2 with [] -> [] | (n,t) :: s2' -> let t2 = begin try let t0' = validate_xdr_type_i1 (expand_X_type s1) [] t in let pl = find_params t0' in t0'.params <- pl; let t1' = elim_rec t0' in calc_min_size t0'; calc_min_size t1'; (t0',t1') with Not_found -> failwith "Xdr.validate_xdr_type_system: unspecified error" | Propagate s -> failwith ("Xdr.validate_xdr_type_system: " ^ s) end in (n,t2)::(r (s1 @ [n,t2]) s2') in r [] s end else failwith "Xdr.validate_xdr_type_system: type system has members with same names" ;; (**********************************************************************) (* the reverse way *) (**********************************************************************) let rec xdr_type_term0 (t:xdr_type0) : xdr_type_term = let conv_list l = List.map (fun (x, t') -> x, xdr_type_term0 t') l in let conv_htbl htbl = Hashtbl.fold (fun x t' l -> (x, xdr_type_term0 t') :: l) htbl [] in let conv_option p = match p with None -> None | Some t' -> Some (xdr_type_term0 t') in match t.term with T_int -> X_int | T_uint -> X_uint | T_hyper -> X_hyper | T_uhyper -> X_uhyper | T_enum l -> X_enum (Array.to_list (Array.map (fun (n,i) -> (n,Netnumber.int4_of_int32 i)) l)) | T_float -> X_float | T_double -> X_double | T_void -> X_void | T_param p -> X_param p | T_opaque_fixed n -> X_opaque_fixed n | T_opaque n -> X_opaque n | T_string n -> X_string n | T_mstring(name,n)-> X_mstring(name,n) | T_array_fixed (t', n) -> X_array_fixed (xdr_type_term0 t',n) | T_array (t', n) -> X_array (xdr_type_term0 t',n) | T_struct s -> X_struct (conv_list (Array.to_list s)) | T_rec (n, t') -> X_rec (n, xdr_type_term0 t') | T_refer (n, t') -> X_refer n | T_union_over_int (u,d) -> X_union_over_int (conv_htbl u, conv_option d) | T_union_over_uint (u,d) -> X_union_over_uint (conv_htbl u, conv_option d) | T_union_over_enum ( { term = T_enum e } as e_term ,u,d) -> let u' = List.flatten (Array.to_list (Array.mapi (fun k t'_opt -> match t'_opt with Some t' -> let name = fst(e.(k)) in [ name, xdr_type_term0 t' ] | None -> [] ) u ) ) in X_union_over_enum (xdr_type_term0 e_term, u', conv_option d) | T_direct (t', read, write, size) -> X_direct (xdr_type_term0 t',read, write, size) | _ -> assert false ;; let xdr_type_term (t:xdr_type) : xdr_type_term = xdr_type_term0 (fst t) let xdr_type_term_system (s:xdr_type_system) : xdr_type_term_system = List.map (fun (n,t) -> n,xdr_type_term t) s ;; (**********************************************************************) (* expand X_type members relative to given systems *) (**********************************************************************) (* The implementation of "expanded_xdr_type_term" repeats many phrases * that have been defined for "validate_xdr_type" in a very similar * way. * TODO: Currently many checks have been left out *) let rec expanded_xdr_type_term (s:xdr_type_term_system) (t:xdr_type_term) : xdr_type_term = match t with X_array_fixed (t',n) -> X_array_fixed ((expanded_xdr_type_term s t'), n) | X_array (t',n) -> X_array ((expanded_xdr_type_term s t'), n) | X_struct st -> let s_names, s_types = List.split st in X_struct (List.combine s_names (List.map (expanded_xdr_type_term s) s_types)) | X_union_over_int (u,default) -> let u_values, u_types = List.split u in let default' = match default with Some d -> Some (expanded_xdr_type_term s d) | None -> None in X_union_over_int (List.combine u_values (List.map (expanded_xdr_type_term s) u_types), default') | X_union_over_uint (u,default) -> let u_values, u_types = List.split u in let default' = match default with Some d -> Some (expanded_xdr_type_term s d) | None -> None in X_union_over_uint (List.combine u_values (List.map (expanded_xdr_type_term s) u_types), default') | X_union_over_enum (e,u,default) -> let u_values, u_types = List.split u in let default' = match default with Some d -> Some (expanded_xdr_type_term s d) | None -> None in X_union_over_enum ( (expanded_xdr_type_term s e), (List.combine u_values (List.map (expanded_xdr_type_term s) u_types)), default') | X_type n -> let rec r s1 s2 = match s2 with [] -> failwith ("Xdr.expanded_xdr_type_term: cannot resolve X_type " ^ n) | (n',t') :: s2' -> if n = n' then expanded_xdr_type_term s1 t' else r (s1 @ [n',t']) s2' in r [] s | X_rec (n, t') -> X_rec (n, expanded_xdr_type_term s t') | X_direct (t',read, write, size) -> X_direct ((expanded_xdr_type_term s t'), read, write, size) | _ -> t ;; let expanded_xdr_type (s:xdr_type_system) (t:xdr_type_term) : xdr_type = try let t0 = validate_xdr_type_i1 (expand_X_type s) [] t in let t1 = elim_rec t0 in calc_min_size t0; calc_min_size t1; (t0,t1) with Not_found -> failwith "Xdr.expanded_xdr_type: unspecified error" | Propagate s -> failwith ("Xdr.expanded_xdr_type: " ^ s) ;; (**********************************************************************) (* test on compatibility *) (**********************************************************************) let are_compatible (s1:xdr_type) (s2:xdr_type) : bool = (* implementation: * enum, struct and union members can be swapped *) failwith "Xdr.are_compatible: not implemented" ;; (**********************************************************************) (* common implementation of value_matches_type & pack_xdr_value *) (**********************************************************************) (* pack: interestingly, two loops over the value where one loop only determines the size of the final buffer are _faster_ than a single loop over the value doing everything. Whoever understands that. *) type encoder = Xdr_mstring.mstring list -> Xdr_mstring.mstring list type decoder = string -> int -> int -> (string * int) let overflow() = raise(Xdr_failure "overflow in ++") let ( ++ ) x y = (* pre: x >= 0 && y >= 0 *) let s = x + y in if s < 0 then overflow(); s let get_string_decoration_size x_len n = (* header field plus padding *) let x_len_u = uint4_of_int x_len in let x_len_mod_4 = x_len land 3 in if Netnumber.le_uint4 x_len_u n then begin (if x_len_mod_4 = 0 then 4 else 8 - x_len_mod_4 ) end else raise (Xdr_failure "string is longer than allowed") let sizefn_string n x = let x_len = String.length x in get_string_decoration_size x_len n + x_len let sizefn_mstring n x = let x_len = x#length in get_string_decoration_size x_len n + x_len let pack_size (v:xdr_value) (t:xdr_type0) (get_param:string->xdr_type) (get_encoder:string->encoder option) : int = (* returned size does not include mstrings! *) let rec get_size v t = match t.term with | T_int -> 4 | T_uint -> 4 | T_hyper -> 8 | T_uhyper -> 8 | T_enum e -> 4 | T_float -> 4 | T_double -> 8 | T_opaque_fixed n -> int_of_uint4 n | T_opaque n -> let x = dest_xv_opaque v in sizefn_string n x | T_string n -> let x = dest_xv_string v in sizefn_string n x | T_mstring(_,n) -> (* for an mstring we only count the length field plus padding *) let x = dest_xv_mstring v in let l = x#length in get_string_decoration_size l n | T_array_fixed (t',n) -> get_array_size v t' n (fun m n -> m=n) | T_array (t',n) -> 4 + get_array_size v t' n Netnumber.le_uint4 | T_struct s -> let v_array = map_xv_struct_fast0 t v in let sum = ref 0 in Array.iteri (fun k v_component -> sum := !sum ++ get_size v_component (snd s.(k))) v_array; !sum | T_union_over_int (u,default) -> let i,x = dest_xv_union_over_int v in let t' = try Hashtbl.find u i with Not_found -> match default with Some d -> d | None -> raise (Xdr_failure "T_union_over_int") in 4 ++ get_size x t' | T_union_over_uint (u,default) -> let i,x = dest_xv_union_over_uint v in let t' = try Hashtbl.find u i with Not_found -> match default with Some d -> d | None -> raise (Xdr_failure "T_union_over_uint") in 4 ++ get_size x t' | T_union_over_enum (et,u,default) -> let k,i,x = map_xv_union_over_enum_fast0 t v in let t' = match u.(k) with Some u_t -> u_t | None -> ( match default with Some d -> d | None -> raise (Xdr_failure "T_union_over_enum") ) in 4 ++ get_size x t' | T_void -> 0 | T_param n -> let t' = get_param n in let enc_opt = get_encoder n in if enc_opt = None then get_size v (snd t') else 0 | T_rec (n, t') -> get_size v t' | T_refer (n, t') -> get_size v t' | T_direct(t', _, _, _) -> ( match v with | XV_direct(_,size) -> size | _ -> get_size v t' ) and get_array_size v t' n cmp = (* w/o array header *) (* TODO: optimize arrays of types with fixed repr length *) match v with | XV_array x -> (* generic *) let m = uint4_of_int (Array.length x) in if cmp m n then ( let s = ref 0 in Array.iter (fun v' -> s := !s ++ get_size v' t') x; !s ) else raise (Xdr_failure "array length mismatch") | XV_array_of_string_fast x -> ( match t'.term with | T_string sn -> let m = uint4_of_int (Array.length x) in if cmp m n then ( let sum = ref 0 in Array.iter (fun s -> sum := !sum ++ sizefn_string sn s) x; !sum ) else raise (Xdr_failure "array length mismatch") | T_direct(t1, _, _, _) -> get_array_size v t1 n cmp | _ -> raise Dest_failure ) | _ -> raise Dest_failure in get_size v t let print_string_padding l buf pos = let n = 4-(l land 3) in if n < 4 then begin let p = !pos in if n >= 1 then String.unsafe_set buf p '\000'; if n >= 2 then String.unsafe_set buf (p + 1) '\000'; if n >= 3 then String.unsafe_set buf (p + 2) '\000'; pos := p + n end let rec pack_mstring (v:xdr_value) (t:xdr_type0) (get_param:string->xdr_type) (get_encoder:string->encoder option) : Xdr_mstring.mstring list = (* The recursion over pack_mstring is only used for encoded parameters *) let size = pack_size v t get_param get_encoder in (* all sanity checks are done here! Also, [size] does not include the size for mstrings (only the length field, and padding), and it does not include encoded parameters *) let buf = String.create size in let buf_start = ref 0 in let buf_pos = ref 0 in let result = ref [] in (* The resulting mstrings in reverse order *) let save_buf() = if !buf_pos > !buf_start then ( let x = Xdr_mstring.string_based_mstrings # create_from_string buf !buf_start (!buf_pos - !buf_start) false in result := x :: !result; buf_start := !buf_pos ) in let print_string s l = String.unsafe_blit s 0 buf !buf_pos l; buf_pos := !buf_pos + l; print_string_padding l buf buf_pos in let rec pack v t = match t.term with T_int -> let x = dest_xv_int v in Netnumber.BE.write_int4_unsafe buf !buf_pos x; buf_pos := !buf_pos + 4 | T_uint -> let x = dest_xv_uint v in Netnumber.BE.write_uint4_unsafe buf !buf_pos x; buf_pos := !buf_pos + 4 | T_hyper -> let x = dest_xv_hyper v in Netnumber.BE.write_int8_unsafe buf !buf_pos x; buf_pos := !buf_pos + 8 | T_uhyper -> let x = dest_xv_uhyper v in Netnumber.BE.write_uint8_unsafe buf !buf_pos x; buf_pos := !buf_pos + 8 | T_enum e -> let i = map_xv_enum_fast0 t v in Netnumber.BE.write_int4_unsafe buf !buf_pos (int4_of_int32 i); buf_pos := !buf_pos + 4 | T_float -> let x = dest_xv_float v in let s = Netnumber.BE.fp4_as_string x in String.unsafe_blit s 0 buf !buf_pos 4; buf_pos := !buf_pos + 4 | T_double -> let x = dest_xv_double v in let s = Netnumber.BE.fp8_as_string x in String.unsafe_blit s 0 buf !buf_pos 8; buf_pos := !buf_pos + 8 | T_opaque_fixed n -> let x = dest_xv_opaque v in print_string x (String.length x) | T_opaque n -> let x = dest_xv_opaque v in let x_len = String.length x in Netnumber.BE.write_uint4_unsafe buf !buf_pos (uint4_of_int x_len); buf_pos := !buf_pos + 4; print_string x x_len | T_string n -> let x = dest_xv_string v in let x_len = String.length x in Netnumber.BE.write_uint4_unsafe buf !buf_pos (uint4_of_int x_len); buf_pos := !buf_pos + 4; print_string x x_len | T_mstring(_,n) -> let x = dest_xv_mstring v in let x_len = x#length in Netnumber.BE.write_uint4_unsafe buf !buf_pos (uint4_of_int x_len); buf_pos := !buf_pos + 4; save_buf(); result := x :: !result; print_string_padding x_len buf buf_pos | T_array_fixed (t',n) -> pack_array v t' n false | T_array (t',n) -> pack_array v t' n true | T_struct s -> let v_array = map_xv_struct_fast0 t v in Array.iteri (fun k v_component -> pack v_component (snd s.(k))) v_array | T_union_over_int (u,default) -> let i,x = dest_xv_union_over_int v in let t' = try Hashtbl.find u i with Not_found -> match default with Some d -> d | None -> raise (Xdr_failure "T_union_over_int") in Netnumber.BE.write_int4_unsafe buf !buf_pos i; buf_pos := !buf_pos + 4; pack x t' | T_union_over_uint (u,default) -> let i,x = dest_xv_union_over_uint v in let t' = try Hashtbl.find u i with Not_found -> match default with Some d -> d | None -> raise (Xdr_failure "T_union_over_uint") in Netnumber.BE.write_uint4_unsafe buf !buf_pos i; buf_pos := !buf_pos + 4; pack x t' | T_union_over_enum (et,u,default) -> let k,i,x = map_xv_union_over_enum_fast0 t v in let t' = match u.(k) with Some u_t -> u_t | None -> ( match default with Some d -> d | None -> raise (Xdr_failure "T_union_over_enum") ) in Netnumber.BE.write_int4_unsafe buf !buf_pos (int4_of_int32 i); buf_pos := !buf_pos + 4; pack x t' | T_void -> () | T_param n -> let t' = get_param n in let enc_opt = get_encoder n in ( match enc_opt with | None -> pack v (snd t') | Some enc -> save_buf(); let l = pack_mstring v (snd t') (fun _ -> assert false) (fun _ -> assert false) in let e = enc l in result := List.rev e @ !result ) | T_rec (n, t') -> pack v t' | T_refer (n, t') -> pack v t' | T_direct(t', _, write, _) -> ( match v with | XV_direct(x,xv_size) -> let old = !buf_pos in write x buf buf_pos; (* Printf.eprintf "old=%d new=%d size=%d\n" old !buf_pos size; *) assert(!buf_pos = old + xv_size); | _ -> pack v t' ) and pack_array v t' n have_array_header = match v with | XV_array x -> (* generic *) if have_array_header then pack_array_header (Array.length x); Array.iter (fun v' -> pack v' t') x | XV_array_of_string_fast x -> ( match t'.term with | T_string n -> if have_array_header then pack_array_header (Array.length x); Array.iter (fun s -> let s_len = String.length s in Netnumber.BE.write_uint4_unsafe buf !buf_pos (uint4_of_int s_len); buf_pos := !buf_pos + 4; print_string s s_len ) x | T_direct(t1,_,_,_) -> pack_array v t1 n have_array_header | _ -> raise Dest_failure ) | _ -> raise Dest_failure and pack_array_header x_len = Netnumber.BE.write_uint4_unsafe buf !buf_pos (uint4_of_int x_len); buf_pos := !buf_pos + 4; in pack v t; save_buf(); List.rev !result ;; let write_string_fixed n x buf pos = (* exported *) let x_len = String.length x in if x_len <> n then raise (Xdr_failure "fixed string has bad length"); String.unsafe_blit x 0 buf !pos x_len; pos := !pos + x_len; print_string_padding x_len buf pos let write_string x buf pos = (* exported *) let x_len = String.length x in Netnumber.BE.write_uint4_unsafe buf !pos (uint4_of_int x_len); pos := !pos + 4; String.unsafe_blit x 0 buf !pos x_len; pos := !pos + x_len; print_string_padding x_len buf pos let value_matches_type (v:xdr_value) ((_,t):xdr_type) (p:(string * xdr_type) list) : bool = if StringSet.for_all (fun n -> List.mem_assoc n p) t.params && List.for_all (fun (n,t') -> StringSet.is_empty (fst t').params) p then try ignore(pack_size v t (fun n -> List.assoc n p) (fun _ -> None)); true with _ -> (* we assume here that no other errors can occur *) false else false ;; (**********************************************************************) (* pack and unpack values *) (**********************************************************************) let pack_xdr_value ?(encode = []) (v:xdr_value) ((_,t):xdr_type) (p:(string * xdr_type) list) (print:string->unit) : unit = (* DEBUG *) (* List.iter (fun pn -> prerr_endline ("param " ^ pn)) t.params; *) if StringSet.for_all (fun n -> List.mem_assoc n p) t.params && List.for_all (fun (n,t') -> StringSet.is_empty (fst t').params) p then try let mstrings = pack_mstring v t (fun n -> List.assoc n p) (fun n -> try Some(List.assoc n encode) with Not_found -> None) in List.iter (fun ms -> let (s,p) = ms#as_string in print (String.sub s p ms#length) ) mstrings with | Dest_failure -> raise(Xdr_failure "Xdr.pack_xdr_value [2]: XDR type mismatch") | Netnumber.Cannot_represent _ -> raise(Xdr_failure "Xdr.pack_xdr_value [3]: integer not representable") | Netnumber.Out_of_range -> raise(Xdr_failure "Xdr.pack_xdr_value [4]: index out of range") | Failure s -> raise(Xdr_failure ("Xdr.pack_xdr_value [5]: " ^ s)) else raise(Xdr_failure "Xdr.pack_xdr_value [1]") ;; let pack_xdr_value_as_string ?(rm = false) ?(encode = []) (v:xdr_value) ((_,t):xdr_type) (p:(string * xdr_type) list) : string = if StringSet.for_all (fun n -> List.mem_assoc n p) t.params && List.for_all (fun (n,t') -> StringSet.is_empty (fst t').params) p then try let mstrings0 = pack_mstring v t (fun n -> List.assoc n p) (fun n -> try Some(List.assoc n encode) with Not_found -> None) in let rm_prefix = if rm then let s = "\000\000\000\000" in [ Xdr_mstring.string_based_mstrings # create_from_string s 0 4 false ] else [] in let mstrings = rm_prefix @ mstrings0 in Xdr_mstring.concat_mstrings mstrings with | Dest_failure -> (*let bt = Printexc.get_backtrace() in eprintf "Backtrace: %s\n" bt; *) raise(Xdr_failure "Xdr.pack_xdr_value_as_string [2]: XDR type mismatch") | Netnumber.Cannot_represent _ -> raise(Xdr_failure "Xdr.pack_xdr_value_as_string [3]: integer not representable") | Netnumber.Out_of_range -> raise(Xdr_failure "Xdr.pack_xdr_value_as_string [4]: index out of range") | Failure s -> raise(Xdr_failure ("Xdr.pack_xdr_value_as_string [5]: " ^ s)) else raise(Xdr_failure "Xdr.pack_xdr_value_as_string [1]") ;; let pack_xdr_value_as_mstrings ?(encode = []) (v:xdr_value) ((_,t):xdr_type) (p:(string * xdr_type) list) = if StringSet.for_all (fun n -> List.mem_assoc n p) t.params && List.for_all (fun (n,t') -> StringSet.is_empty (fst t').params) p then try pack_mstring v t (fun n -> List.assoc n p) (fun n -> try Some(List.assoc n encode) with Not_found -> None) with | Dest_failure -> raise(Xdr_failure "Xdr.pack_xdr_value_as_mstring [2]: XDR type mismatch") | Netnumber.Cannot_represent _ -> raise (Xdr_failure "Xdr.pack_xdr_value_as_mstring [3]: integer not representable") | Netnumber.Out_of_range -> raise(Xdr_failure "Xdr.pack_xdr_value_as_mstring [4]: index out of range") | Failure s -> raise(Xdr_failure ("Xdr.pack_xdr_value_as_mstring [5]: " ^ s)) else raise(Xdr_failure "Xdr.pack_xdr_value_as_mstring [1]") ;; (* "let rec" prevents that these functions are inlined. This is wanted here, because these are error cases, and for a function call less code is generated than for raising an exception *) let rec raise_xdr_format_too_short () = raise (Xdr_format "message too short") let rec raise_xdr_format_value_not_included () = raise (Xdr_format "value not included in enumeration") let rec raise_xdr_format_maximum_length () = raise (Xdr_format "maximum length of field exceeded") let rec raise_xdr_format_undefined_descriminator() = raise (Xdr_format "undefined discriminator") let rec find_enum (e : (string * int32) array) (i : int32) = (* no inlining! *) let rec loop lb ub = (* The element is between lb and ub *) if lb > ub then raise_xdr_format_value_not_included (); let m = (ub + lb) lsr 1 in let x_m = snd(e.(m)) in if i = x_m then (* Found! *) m else if i < x_m then loop lb (m-1) else (* i > x_m *) loop (m+1) ub in loop 0 (Array.length e - 1) ;; (* DEBUG*) (* let hex_dump_s s pos len = let b = Buffer.create 100 in for k = 0 to len - 1 do let c = s.[pos+k] in bprintf b "%02x " (Char.code c) done; Buffer.contents b *) let read_string_fixed n str k k_end = (* exported *) let k0 = !k in let m = if n land 3 = 0 then n else n+4-(n land 3) in if k0 > k_end - m then raise_xdr_format_too_short (); let s = String.create n in String.unsafe_blit str k0 s 0 n; k := k0 + m; s let read_string n str k k_end = (* exported *) let k0 = !k in k := k0 + 4; if !k > k_end then raise_xdr_format_too_short(); let m = Netnumber.BE.read_uint4_unsafe str k0 in (* Test: n < m as unsigned int32: *) if Netnumber.lt_uint4 n m then raise_xdr_format_maximum_length (); read_string_fixed (int_of_uint4 m) str k k_end let empty_mf = Hashtbl.create 1 let rec unpack_term ?(pos = 0) ?len ?(fast = false) ?(prefix = false) ?(mstring_factories = empty_mf) ?(xv_version = if fast then `Ocamlrpcgen else `V1) (str:string) (t:xdr_type0) (get_param:string->xdr_type) (get_decoder:string->decoder option) : xdr_value * int = (* The recursion over unpack_term is only used for decoding encrypted parameters *) let xv_version = if xv_version = `Ocamlrpcgen then `V4 else xv_version in let v2 = (xv_version <> `V1) in (* meaning: at least v2 *) let v3 = v2 && (xv_version <> `V2) in let v4 = v3 && (xv_version <> `V3) in let len = match len with None -> String.length str - pos | Some l -> l in if pos < 0 || len < 0 || len > String.length str - pos then invalid_arg "Xdr.unpack_xdr_value"; let k_end = pos+len in let k = ref pos in let rec read_fp4 k0 = if k0 + 4 > k_end then raise_xdr_format_too_short(); k := !k + 4; Netnumber.BE.read_fp4 str k0 in let rec read_fp8 k0 = if k0 + 8 > k_end then raise_xdr_format_too_short(); k := !k + 8; Netnumber.BE.read_fp8 str k0 in let rec read_enum e k0 = k := k0 + 4; if !k > k_end then raise_xdr_format_too_short(); let i = Netnumber.int32_of_int4(Netnumber.BE.read_int4_unsafe str k0) in let j = find_enum e i in (* returns array position, or Xdr_format *) if v2 then XV_enum_fast j else XV_enum(fst(e.(j))) in let rec read_string_or_opaque n k0 = k := k0 + 4; if !k > k_end then raise_xdr_format_too_short(); let m = Netnumber.BE.read_uint4_unsafe str k0 in (* Test: n < m as unsigned int32: *) if Netnumber.lt_uint4 n m then raise_xdr_format_maximum_length (); read_string_fixed (int_of_uint4 m) str k k_end in let rec read_mstring name n k0 = let factory = try Hashtbl.find mstring_factories name with Not_found -> ( try Hashtbl.find mstring_factories "*" with Not_found -> failwith "read_mstring: no such factory" ) in k := k0 + 4; if !k > k_end then raise_xdr_format_too_short(); let m = Netnumber.BE.read_uint4_unsafe str k0 in (* Test: n < m as unsigned int32: *) if Netnumber.lt_uint4 n m then raise_xdr_format_maximum_length (); let m = int_of_uint4 m in let p = if m land 3 = 0 then m else m+4-(m land 3) in if !k > k_end - p then raise_xdr_format_too_short (); let ms = factory # create_from_string str !k m false in k := !k + p; ms in let rec unpack_array t' p = (* Estimate the maximum p *) (* eprintf "unpack_array: t' = %s\n" (t_name t'.term);*) assert(t'.min_size > 0); let p_max = (k_end - !k) / t'.min_size in if p > p_max then raise_xdr_format_too_short(); match t'.term with | T_string n -> let n' = Netnumber.logical_int32_of_uint4 n in let a = Array.make p "" in let k' = Netsys_xdr.s_read_string_array_unsafe str !k (k_end - !k) n' a in if k' = (-1) then raise_xdr_format_too_short(); if k' = (-2) then raise_xdr_format_maximum_length (); k := k'; if v3 then XV_array_of_string_fast a else XV_array(Array.map (fun s -> XV_string s) a) | _ -> let a = Array.make p XV_void in for i = 0 to p-1 do Array.unsafe_set a i (unpack t') done; XV_array a and unpack t = let k0 = !k in (* fprintf stderr "unpack k=%d t=%s\n%!" k0 (t_name t.term); *) match t.term with T_int -> k := k0 + 4; if !k > k_end then raise_xdr_format_too_short(); XV_int (Netnumber.BE.read_int4_unsafe str k0) | T_uint -> k := k0 + 4; if !k > k_end then raise_xdr_format_too_short(); XV_uint (Netnumber.BE.read_uint4_unsafe str k0) | T_hyper -> k := k0 + 8; if !k > k_end then raise_xdr_format_too_short(); XV_hyper (Netnumber.BE.read_int8_unsafe str k0) | T_uhyper -> k := !k + 8; if k0 > k_end then raise_xdr_format_too_short(); XV_uhyper (Netnumber.BE.read_uint8_unsafe str k0) | T_enum e -> read_enum e k0 | T_float -> XV_float (read_fp4 k0) | T_double -> XV_double (read_fp8 k0) | T_opaque_fixed n -> XV_opaque (read_string_fixed (int_of_uint4 n) str k k_end) | T_opaque n -> XV_opaque (read_string_or_opaque n k0) | T_string n -> XV_string (read_string_or_opaque n k0) | T_mstring(name,n) -> XV_mstring (read_mstring name n k0) | T_array_fixed (t',n) -> let p = int_of_uint4 n in unpack_array t' p | T_array (t',n) -> k := k0 + 4; let m = Netnumber.BE.read_uint4 str k0 in if Netnumber.lt_uint4 n m then raise_xdr_format_maximum_length (); unpack_array t' (int_of_uint4 m) | T_struct s -> if v2 then XV_struct_fast ( Array.map (fun (name,t') -> unpack t') s ) else XV_struct (List.map (fun (name,t') -> (name,unpack t')) (Array.to_list s) ) | T_union_over_int (u,default) -> unpack_union_over_int u default k0 | T_union_over_uint (u,default) -> unpack_union_over_uint u default k0 | T_union_over_enum ( { term = T_enum e },u,default) -> unpack_union_over_enum e u default k0 | T_void -> XV_void | T_param p -> let t' = get_param p in let dec_opt = get_decoder p in ( match dec_opt with | None -> unpack (snd t') | Some decoder -> let (dec_s, n) = decoder str k0 (k_end - k0) in k := !k + n; assert( !k <= k_end ); let (v, p) = unpack_term ~mstring_factories ~xv_version dec_s (snd t') (fun _ -> assert false) (fun _ -> None) in v ) | T_rec (_, t') | T_refer (_, t') -> unpack t' | T_direct(t', read, _, _) -> if v4 then let k0 = !k in let xv = read str k k_end in XV_direct(xv, !k-k0) else unpack t' | _ -> assert false and unpack_union_over_int u default k0 = k := k0 + 4; let n = Netnumber.BE.read_int4 str k0 in let t' = try Hashtbl.find u n with Not_found -> match default with None -> raise_xdr_format_undefined_descriminator() | Some d -> d in XV_union_over_int (n, unpack t') and unpack_union_over_uint u default k0 = k := k0 + 4; let n = Netnumber.BE.read_uint4 str k0 in let t' = try Hashtbl.find u n with Not_found -> match default with None -> raise_xdr_format_undefined_descriminator() | Some d -> d in XV_union_over_uint (n, unpack t') and unpack_union_over_enum e u default k0 = k := k0 + 4; let i = Netnumber.int32_of_int4 (Netnumber.BE.read_int4 str k0) in let j = find_enum e i (* returns array position, or Xdr_format *) in let t' = match u.(j) with Some u_t -> u_t | None -> ( match default with Some d -> d | None -> raise_xdr_format_undefined_descriminator() ) in if v2 then XV_union_over_enum_fast(j, unpack t') else let name = fst(e.(j)) in XV_union_over_enum(name, unpack t') in try let v = unpack t in if prefix || !k = k_end then (v, !k - pos) else ( (* fprintf stderr "Too LONG: k=%d k_end=%d\n%!" !k k_end; fprintf stderr "Dump: %s\n%!" (hex_dump_s str pos (k_end-pos)); *) raise (Xdr_format_message_too_long v) ) with Cannot_represent _ -> raise (Xdr_format "implementation restriction") | Out_of_range -> raise (Xdr_format "message too short") ;; let unpack_xdr_value ?pos ?len ?fast ?prefix ?mstring_factories ?xv_version ?(decode=[]) (str:string) ((_,t):xdr_type) (p:(string * xdr_type) list) : xdr_value = if StringSet.for_all (fun n -> List.mem_assoc n p) t.params && List.for_all (fun (n,t') -> StringSet.is_empty (fst t').params) p then fst(unpack_term ?pos ?len ?fast ?prefix ?mstring_factories ?xv_version str t (fun n -> List.assoc n p) (fun n -> try Some(List.assoc n decode) with Not_found -> None) ) else failwith "Xdr.unpack_xdr_value" ;; let unpack_xdr_value_l ?pos ?len ?fast ?prefix ?mstring_factories ?xv_version ?(decode=[]) (str:string) ((_,t):xdr_type) (p:(string * xdr_type) list) : xdr_value * int = if StringSet.for_all (fun n -> List.mem_assoc n p) t.params && List.for_all (fun (n,t') -> StringSet.is_empty (fst t').params) p then unpack_term ?pos ?len ?fast ?prefix ?mstring_factories ?xv_version str t (fun n -> List.assoc n p) (fun n -> try Some(List.assoc n decode) with Not_found -> None) else failwith "Xdr.unpack_xdr_value" ;;