diff options
author | Julien Puydt <jpuydt@debian.org> | 2023-07-05 14:55:18 +0200 |
---|---|---|
committer | Julien Puydt <jpuydt@debian.org> | 2023-07-05 14:55:18 +0200 |
commit | 586db5ab5d6c34b4fd31ea4984549f5d842a17fe (patch) | |
tree | 0751deec2e3f2aa95d279b633c4d398764b3a0a1 | |
parent | eb1e703994d7f040e98432b34355feb37bb1cc88 (diff) | |
parent | cae8675a199f94e2ca672ee11c4b35f5c87d3c73 (diff) |
Record ocaml-ipaddr (5.5.0-1) in archive suite sid
-rw-r--r-- | .ocamlformat | 2 | ||||
-rw-r--r-- | CHANGES.md | 9 | ||||
-rw-r--r-- | debian/changelog | 9 | ||||
-rw-r--r-- | debian/control | 2 | ||||
-rwxr-xr-x | debian/rules | 8 | ||||
-rw-r--r-- | debian/watch | 2 | ||||
-rw-r--r-- | dune-project | 1 | ||||
-rw-r--r-- | ipaddr-cstruct.opam | 3 | ||||
-rw-r--r-- | ipaddr-sexp.opam | 3 | ||||
-rw-r--r-- | ipaddr.opam | 3 | ||||
-rw-r--r-- | lib/ipaddr.ml | 529 | ||||
-rw-r--r-- | lib/ipaddr.mli | 15 | ||||
-rw-r--r-- | lib/ipaddr_unix.mli | 2 | ||||
-rw-r--r-- | lib/macaddr.mli | 2 | ||||
-rw-r--r-- | lib_test/test_ipaddr.ml | 16 | ||||
-rw-r--r-- | lib_test/test_ipaddr_b128.ml | 184 | ||||
-rw-r--r-- | macaddr-cstruct.opam | 3 | ||||
-rw-r--r-- | macaddr-sexp.opam | 3 | ||||
-rw-r--r-- | macaddr.opam | 3 |
19 files changed, 531 insertions, 268 deletions
diff --git a/.ocamlformat b/.ocamlformat index 3bcdafe..e767a63 100644 --- a/.ocamlformat +++ b/.ocamlformat @@ -1,4 +1,4 @@ -version = 0.20.0 +version = 0.25.1 profile = conventional break-infix = fit-or-vertical parse-docstrings = true @@ -1,3 +1,12 @@ +## v5.5.0 (2023-03-31) + +* add `Ipaddr` `of_octet` functions (#117, @ryangibb). + +## v5.4.0 (2023-03-13) + +* Use Bytes.t for IPv6 addresses (#115 @verbosemode, fixes #16 @dsheets) +* Also fixes `V6.to_int64` (reported by @RyanGibb in #113) + ## v5.3.1 (2022-07-04) * Remove stdlib-shims dependency, require OCaml 4.08+ (@hannesm, #112) diff --git a/debian/changelog b/debian/changelog index a6184b7..f323c76 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +ocaml-ipaddr (5.5.0-1) unstable; urgency=medium + + * Fix compilation with recent dune. + * Fix d/watch + * Bump standards-version to 4.6.2 + * New upstream release. + + -- Julien Puydt <jpuydt@debian.org> Wed, 05 Jul 2023 14:55:18 +0200 + ocaml-ipaddr (5.3.1-1) unstable; urgency=medium * New upstream release diff --git a/debian/control b/debian/control index 2200f08..6f20db4 100644 --- a/debian/control +++ b/debian/control @@ -16,7 +16,7 @@ Build-Depends: libcstruct-ocaml-dev (>= 6.0.0), libsexplib0-ocaml-dev, dh-ocaml -Standards-Version: 4.6.0 +Standards-Version: 4.6.2 Rules-Requires-Root: no Homepage: https://github.com/mirage/ocaml-ipaddr Vcs-Git: https://salsa.debian.org/ocaml-team/ocaml-ipaddr.git diff --git a/debian/rules b/debian/rules index fb52ad5..cfc7ad5 100755 --- a/debian/rules +++ b/debian/rules @@ -16,13 +16,13 @@ override_dh_auto_clean: override_dh_auto_build: dune build -p macaddr - opam-installer --prefix=$(CURDIR)/_tmp/usr --libdir=..$(OCAML_STDLIB_DIR) macaddr.install + dune install --destdir=$(CURDIR)/_tmp --prefix=/usr --libdir=$(OCAML_STDLIB_DIR) macaddr OCAMLPATH=$(CURDIR)/_tmp$(OCAML_STDLIB_DIR) dune build -p ipaddr - opam-installer --prefix=$(CURDIR)/_tmp/usr --libdir=..$(OCAML_STDLIB_DIR) ipaddr.install + dune install --destdir=$(CURDIR)/_tmp --prefix=/usr --libdir=$(OCAML_STDLIB_DIR) ipaddr OCAMLPATH=$(CURDIR)/_tmp$(OCAML_STDLIB_DIR) dune build -p ipaddr-cstruct - opam-installer --prefix=$(CURDIR)/_tmp/usr --libdir=..$(OCAML_STDLIB_DIR) ipaddr-cstruct.install + dune install --destdir=$(CURDIR)/_tmp --prefix=/usr --libdir=$(OCAML_STDLIB_DIR) ipaddr-cstruct OCAMLPATH=$(CURDIR)/_tmp$(OCAML_STDLIB_DIR) dune build -p ipaddr-sexp - opam-installer --prefix=$(CURDIR)/_tmp/usr --libdir=..$(OCAML_STDLIB_DIR) ipaddr-sexp.install + dune install --destdir=$(CURDIR)/_tmp --prefix=/usr --libdir=$(OCAML_STDLIB_DIR) ipaddr-sexp override_dh_auto_test: ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) diff --git a/debian/watch b/debian/watch index a8fdc8d..da41eb6 100644 --- a/debian/watch +++ b/debian/watch @@ -1,2 +1,2 @@ version=4 -https://github.com/mirage/ocaml-ipaddr/releases .*-v?([\d\.]+)\.tbz +https://github.com/mirage/ocaml-ipaddr/tags .*/v?([\d\.]+)\.tar\.gz diff --git a/dune-project b/dune-project index 3760fcf..2e59a02 100644 --- a/dune-project +++ b/dune-project @@ -1,5 +1,4 @@ (lang dune 1.9) (name ipaddr) -(version v5.3.1) (allow_approximate_merlin) (using fmt 1.1) diff --git a/ipaddr-cstruct.opam b/ipaddr-cstruct.opam index 0006c25..3118833 100644 --- a/ipaddr-cstruct.opam +++ b/ipaddr-cstruct.opam @@ -1,4 +1,3 @@ -version: "5.3.1" opam-version: "2.0" maintainer: "anil@recoil.org" authors: ["David Sheets" "Anil Madhavapeddy" "Hugo Heuzard"] @@ -22,4 +21,4 @@ build: [ dev-repo: "git+https://github.com/mirage/ocaml-ipaddr.git" description: """ Cstruct convertions for macaddr -"""
\ No newline at end of file +""" diff --git a/ipaddr-sexp.opam b/ipaddr-sexp.opam index 7ac05eb..f9addcb 100644 --- a/ipaddr-sexp.opam +++ b/ipaddr-sexp.opam @@ -1,4 +1,3 @@ -version: "5.3.1" opam-version: "2.0" maintainer: "anil@recoil.org" authors: ["David Sheets" "Anil Madhavapeddy" "Hugo Heuzard"] @@ -26,4 +25,4 @@ build: [ ["dune" "build" "-p" name "-j" jobs] ["dune" "runtest" "-p" name "-j" jobs] {with-test} ] -dev-repo: "git+https://github.com/mirage/ocaml-ipaddr.git"
\ No newline at end of file +dev-repo: "git+https://github.com/mirage/ocaml-ipaddr.git" diff --git a/ipaddr.opam b/ipaddr.opam index 3da6fa4..606fd26 100644 --- a/ipaddr.opam +++ b/ipaddr.opam @@ -1,4 +1,3 @@ -version: "5.3.1" opam-version: "2.0" maintainer: "anil@recoil.org" authors: ["David Sheets" "Anil Madhavapeddy" "Hugo Heuzard"] @@ -40,4 +39,4 @@ build: [ ["dune" "build" "-p" name "-j" jobs] ["dune" "runtest" "-p" name "-j" jobs] {with-test} ] -dev-repo: "git+https://github.com/mirage/ocaml-ipaddr.git"
\ No newline at end of file +dev-repo: "git+https://github.com/mirage/ocaml-ipaddr.git" diff --git a/lib/ipaddr.ml b/lib/ipaddr.ml index 5a50e02..70805f4 100644 --- a/lib/ipaddr.ml +++ b/lib/ipaddr.ml @@ -108,27 +108,6 @@ let reject_octal s i = if s.[!i] == '0' && is_number 10 (int_of_char s.[!i + 1]) then raise (octal_notation s) -let hex_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") - -let hex_string_of_int32 i = String.make 1 (hex_char_of_int (Int32.to_int i)) - module V4 = struct type t = int32 @@ -442,109 +421,242 @@ module V4 = struct end module B128 = struct - type t = int32 * int32 * int32 * int32 - - let of_int64 (a, b) = - Int64. - ( to_int32 (shift_right_logical a 32), - to_int32 a, - to_int32 (shift_right_logical b 32), - to_int32 b ) + let int_of_hex_char c = + match c with + | '0' .. '9' -> Char.code c - 48 + | 'a' .. 'f' -> Char.code c - 87 + | 'A' .. 'F' -> Char.code c - 55 + | _ -> invalid_arg "char is not a valid hex digit" + + exception Overflow + + type t = Bytes.t + + let zero () = Bytes.make 16 '\x00' + let max_int () = Bytes.make 16 '\xff' + let compare = Bytes.compare + + let fold_left f a b = + let a' = ref a in + for i = 0 to 15 do + let x' = Bytes.get_uint8 b i in + a' := f !a' x' + done; + !a' + + let iteri_right2 f x y = + for i = 15 downto 0 do + let x' = Bytes.get_uint8 x i in + let y' = Bytes.get_uint8 y i in + f i x' y' + done - let to_int64 (a, b, c, d) = - Int64. - ( logor (shift_left (of_int32 a) 32) (of_int32 b), - logor (shift_left (of_int32 c) 32) (of_int32 d) ) + let of_string_exn s = + let l = String.length s in + if l != 32 then invalid_arg "not 32 chars long" + else + let b = zero () in + let bi = ref 15 in + let i = ref (l - 1) in + while !i >= 0 do + let x = int_of_hex_char (String.get s !i) in + let y = int_of_hex_char (String.get s (!i - 1)) in + Bytes.set_uint8 b !bi ((y lsl 4) + x); + i := !i - 2; + bi := !bi - 1 + done; + b + + let to_string b = + let l = ref [] in + for i = 15 downto 0 do + l := Printf.sprintf "%.2x" (Bytes.get_uint8 b i) :: !l + done; + String.concat "" !l + [@@ocaml.warning "-32"] + (* used in the tests *) - let of_int32 x = x - let to_int32 x = x + let of_int64 (a, b) = + let b' = zero () in + Bytes.set_int64_be b' 0 a; + Bytes.set_int64_be b' 8 b; + b' + + let to_int64 b = (Bytes.get_int64_be b 0, Bytes.get_int64_be b 8) + + let of_int32 (a, b, c, d) = + let b' = zero () in + Bytes.set_int32_be b' 0 a; + Bytes.set_int32_be b' 4 b; + Bytes.set_int32_be b' 8 c; + Bytes.set_int32_be b' 12 d; + b' + + let to_int32 b = + ( Bytes.get_int32_be b 0, + Bytes.get_int32_be b 4, + Bytes.get_int32_be b 8, + Bytes.get_int32_be b 12 ) let of_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) ) - - let to_int16 (x, y, z, t) = - let a, b = V4.to_int16 x - and c, d = V4.to_int16 y - and e, f = V4.to_int16 z - and g, h = V4.to_int16 t in - (a, b, c, d, e, f, g, h) - - let write_octets_exn ?(off = 0) (a, b, c, d) byte = - V4.write_octets_exn ~off a byte; - V4.write_octets_exn ~off:(off + 4) b byte; - V4.write_octets_exn ~off:(off + 8) c byte; - V4.write_octets_exn ~off:(off + 12) d byte - - let compare (a1, b1, c1, d1) (a2, b2, c2, d2) = - match V4.compare a1 a2 with - | 0 -> ( - match V4.compare b1 b2 with - | 0 -> ( match V4.compare c1 c2 with 0 -> V4.compare d1 d2 | n -> n) - | n -> n) - | n -> n - - let logand (a1, b1, c1, d1) (a2, b2, c2, d2) = - (a1 &&& a2, b1 &&& b2, c1 &&& c2, d1 &&& d2) - - let logor (a1, b1, c1, d1) (a2, b2, c2, d2) = - (a1 ||| a2, b1 ||| b2, c1 ||| c2, d1 ||| d2) - - let lognot (a, b, c, d) = Int32.(lognot a, lognot b, lognot c, lognot d) - - let succ (a, b, c, d) = - let cb (n, tl) v = - match n with - | 0l -> (0l, v :: tl) - | n -> - let n = if Int32.equal v 0xFF_FF_FF_FFl then n else 0l in - (n, Int32.succ v :: tl) - in - match List.fold_left cb (1l, []) [ d; c; b; a ] with - | 0l, [ a; b; c; d ] -> Ok (of_int32 (a, b, c, d)) - | n, [ _; _; _; _ ] when n > 0l -> - Error (`Msg "Ipaddr: highest address has been reached") - | _ -> Error (`Msg "Ipaddr: unexpected error with B128") - - let pred (a, b, c, d) = - let cb (n, tl) v = - match n with - | 0l -> (0l, v :: tl) - | n -> - let n = if v = 0x00_00_00_00l then n else 0l in - (n, Int32.pred v :: tl) - in - match List.fold_left cb (-1l, []) [ d; c; b; a ] with - | 0l, [ a; b; c; d ] -> Ok (of_int32 (a, b, c, d)) - | n, [ _; _; _; _ ] when n < 0l -> - Error (`Msg "Ipaddr: lowest address has been reached") - | _ -> Error (`Msg "Ipaddr: unexpected error with B128") - - (* result is unspecified if sz < 0 *) - let shift_right (a, b, c, d) sz = - if sz < 0 || sz > 128 then - Error (`Msg "Ipaddr: unexpected argument sz (must be >= 0 and < 128)") + let b' = zero () in + Bytes.set_uint16_be b' 0 a; + Bytes.set_uint16_be b' 2 b; + Bytes.set_uint16_be b' 4 c; + Bytes.set_uint16_be b' 6 d; + Bytes.set_uint16_be b' 8 e; + Bytes.set_uint16_be b' 10 f; + Bytes.set_uint16_be b' 12 g; + Bytes.set_uint16_be b' 14 h; + b' + + let to_int16 b = + ( Bytes.get_uint16_be b 0, + Bytes.get_uint16_be b 2, + Bytes.get_uint16_be b 4, + Bytes.get_uint16_be b 6, + Bytes.get_uint16_be b 8, + Bytes.get_uint16_be b 10, + Bytes.get_uint16_be b 12, + Bytes.get_uint16_be b 14 ) + + let add_exn x y = + let b = zero () in + let carry = ref 0 in + iteri_right2 + (fun i x' y' -> + let sum = x' + y' + !carry in + if sum >= 256 then ( + carry := 1; + Bytes.set_uint8 b i (sum - 256)) + else ( + carry := 0; + Bytes.set_uint8 b i sum)) + x y; + if !carry <> 0 then raise Overflow else b + + let sub_exn x y = + if Bytes.compare x y = -1 then raise Overflow else - let rec loop (a, b, c, d) sz = - if sz < 32 then (sz, (a, b, c, d)) else loop (0l, a, b, c) (sz - 32) - in - let sz, (a, b, c, d) = loop (a, b, c, d) sz in - let fn (saved, tl) part = - let new_saved = Int32.logand part (0xFF_FF_FF_FFl >|> sz) in - let new_part = part >|> sz ||| (saved <|< 32 - sz) in - (new_saved, new_part :: tl) - in - match List.fold_left fn (0l, []) [ a; b; c; d ] with - | _, [ d; c; b; a ] -> Ok (of_int32 (a, b, c, d)) - | _ -> Error (`Msg "Ipaddr: unexpected error with B128.shift_right") + let b = zero () in + let carry = ref 0 in + iteri_right2 + (fun i x' y' -> + if x' < y' then ( + Bytes.set_uint8 b i (256 + x' - y' - !carry); + carry := 1) + else ( + Bytes.set_uint8 b i (x' - y' - !carry); + carry := 0)) + x y; + if !carry <> 0 then raise Overflow else b + + let logand x y = + let b = zero () in + iteri_right2 (fun i x y -> Bytes.set_uint8 b i (x land y)) x y; + b + + let logor x y = + let b = zero () in + iteri_right2 (fun i x y -> Bytes.set_uint8 b i (x lor y)) x y; + b + + let lognot x = + let b = zero () in + Bytes.iteri (fun i _ -> Bytes.set_uint8 b i (lnot (Bytes.get_uint8 x i))) x; + b + + module Byte = struct + (* Extract the [n] least significant bits from [i] *) + let get_lsbits n i = + if n <= 0 || n > 8 then invalid_arg "out of bounds"; + i land ((1 lsl n) - 1) + + (* Extract the [n] most significant bits from [i] *) + let get_msbits n i = + if n <= 0 || n > 8 then invalid_arg "out of bounds"; + (i land (255 lsl (8 - n))) lsr (8 - n) + + (* Set value [x] in [i]'s [n] most significant bits *) + let set_msbits n x i = + if n < 0 || n > 8 then raise (Invalid_argument "n must be >= 0 && <= 8") + else if n = 0 then i + else if n = 8 then x + else (x lsl (8 - n)) lor i + + (* set bits are represented as true *) + let fold_left f a i = + let bitmask = ref 0b1000_0000 in + let a' = ref a in + for _ = 0 to 7 do + a' := f !a' (i land !bitmask > 0); + bitmask := !bitmask lsr 1 + done; + !a' + end + + let shift_right x n = + match n with + | 0 -> x + | 128 -> zero () + | n when n > 0 && n < 128 -> + let b = zero () in + let shift_bytes, shift_bits = (n / 8, n mod 8) in + (if shift_bits = 0 then Bytes.blit x 0 b shift_bytes (16 - shift_bytes) + else + let carry = ref 0 in + for i = 0 to 15 - shift_bytes do + let x' = Bytes.get_uint8 x i in + let new_carry = Byte.get_lsbits shift_bits x' in + let shifted_value = x' lsr shift_bits in + let new_value = Byte.set_msbits shift_bits !carry shifted_value in + Bytes.set_uint8 b (i + shift_bytes) new_value; + carry := new_carry + done); + b + | _ -> raise (Invalid_argument "n must be >= 0 && <= 128") + + let shift_left x n = + match n with + | 0 -> x + | 128 -> zero () + | n when n > 0 && n < 128 -> + let b = zero () in + let shift_bytes, shift_bits = (n / 8, n mod 8) in + (if shift_bits = 0 then Bytes.blit x shift_bytes b 0 (16 - shift_bytes) + else + let carry = ref 0 in + for i = 15 downto 0 + shift_bytes do + let x' = Bytes.get_uint8 x i in + let new_carry = Byte.get_msbits shift_bits x' in + let shifted_value = x' lsl shift_bits in + let new_value = shifted_value lor !carry in + Bytes.set_uint8 b (i - shift_bytes) new_value; + carry := new_carry + done); + b + | _ -> raise (Invalid_argument "n must be >= 0 && <= 128") + + let write_octets_exn ?(off = 0) b' byte = + if Bytes.length b' + off > Bytes.length byte then + raise + (Parse_error + ("larger including offset than target bytes", Bytes.to_string b')) + else Bytes.blit b' 0 byte off (Bytes.length b') + + let succ b = + try Ok (add_exn b (of_string_exn "00000000000000000000000000000001")) + with Overflow -> Error (`Msg "Ipaddr: highest address has been reached") + + let pred b = + try Ok (sub_exn b (of_string_exn "00000000000000000000000000000001")) + with Overflow | Invalid_argument _ -> + Error (`Msg "Ipaddr: lowest address has been reached") end module V6 = struct include B128 - (* TODO: Perhaps represent with bytestring? *) let make a b c d e f g h = of_int16 (a, b, c, d, e, f, g, h) (* parsing *) @@ -716,102 +828,75 @@ module V6 = struct (* byte conversion *) let of_octets_exn ?(off = 0) bs = - (* TODO : from cstruct *) - let hihi = V4.of_octets_exn ~off bs in - let hilo = V4.of_octets_exn ~off:(off + 4) bs in - let lohi = V4.of_octets_exn ~off:(off + 8) bs in - let lolo = V4.of_octets_exn ~off:(off + 12) bs in - of_int32 (hihi, hilo, lohi, lolo) + if String.length bs - off < 16 then raise (need_more bs) + else + let b = B128.zero () in + Bytes.blit_string bs off b 0 16; + b let of_octets ?off bs = try_with_result (of_octets_exn ?off) bs let write_octets ?off i bs = try_with_result (write_octets_exn ?off i) bs - - let to_octets i = - let b = Bytes.create 16 in - write_octets_exn i b; - Bytes.to_string b + let to_octets = Bytes.to_string (* MAC *) (* {{:https://tools.ietf.org/html/rfc2464#section-7}RFC 2464}. *) - let multicast_to_mac i = - let _, _, _, i = to_int32 i in - let macb = Bytes.create 6 in - Bytes.set macb 0 (Char.chr 0x33); - Bytes.set macb 1 (Char.chr 0x33); - Bytes.set macb 2 (Char.chr (( |~ ) (i >! 24))); - Bytes.set macb 3 (Char.chr (( |~ ) (i >! 16))); - Bytes.set macb 4 (Char.chr (( |~ ) (i >! 8))); - Bytes.set macb 5 (Char.chr (( |~ ) (i >! 0))); + let multicast_to_mac b = + let macb = Bytes.make 6 (Char.chr 0x33) in + Bytes.blit b 12 macb 2 4; Macaddr.of_octets_exn (Bytes.to_string macb) (* Host *) - let to_domain_name (a, b, c, d) = - let name = - [ - 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"; - ] + let to_domain_name b = + let hexstr_of_int = Printf.sprintf "%x" in + let rec aux_fold_left a i = + if i = 16 then a + else + let x = hexstr_of_int (Bytes.get_uint8 b i land ((1 lsl 4) - 1)) in + let y = hexstr_of_int (Bytes.get_uint8 b i lsr 4) in + aux_fold_left (x :: y :: a) (i + 1) in + let name = aux_fold_left [ "ip6"; "arpa" ] 0 in Domain_name.(host_exn (of_strings_exn name)) let of_domain_name n = - let open Domain_name in - if count_labels n = 34 then - let ip6 = get_label_exn n 32 and arpa = get_label_exn n 33 in - if equal_label ip6 "ip6" && equal_label arpa "arpa" then - let rev = true in - let n' = drop_label_exn ~rev ~amount:2 n in - let d = drop_label_exn ~rev ~amount:24 n' - and c = drop_label_exn ~amount:8 (drop_label_exn ~rev ~amount:16 n') - and b = drop_label_exn ~amount:16 (drop_label_exn ~rev ~amount:8 n') - and a = drop_label_exn ~amount:24 n' in - let t b d = - let v = Int32.of_int (parse_hex_int d (ref 0)) in - if v > 0xFl then raise (Parse_error ("number in label too big", d)) - else v <|< b - in - let f d = - List.fold_left - (fun (acc, b) d -> (Int32.add acc (t b d), b + 4)) - (0l, 0) (to_strings d) - in - try - let a', _ = f a and b', _ = f b and c', _ = f c and d', _ = f d in - Some (a', b', c', d') - with Parse_error _ -> None - else None + let int_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" + in + let labels = Domain_name.to_array n in + if + Array.length labels = 34 + && Domain_name.equal_label labels.(0) "arpa" + && Domain_name.equal_label labels.(1) "ip6" + then + let b = B128.zero () in + let bi = ref 0 in + let i = ref 2 in + try + while !i <= 32 do + let x = int_of_char_string labels.(!i) in + let y = int_of_char_string labels.(!i + 1) in + Bytes.set_uint8 b !bi (Int.logor (Int.shift_left x 4) y); + bi := !bi + 1; + i := !i + 2 + done; + Some b + with Failure _ -> None else None (* constant *) @@ -833,14 +918,7 @@ module V6 = struct if c = 0 then Stdlib.compare sz sz' else c let ip = make - - let _full = - let f = 0x0_FFFF_FFFF_l in - (f, f, f, f) - - let mask sz = - V4.Prefix.(mask (sz - 0), mask (sz - 32), mask (sz - 64), mask (sz - 96)) - + let mask sz = shift_left (max_int ()) (128 - sz) let prefix (pre, sz) = (logand pre (mask sz), sz) let make sz pre = (pre, sz) @@ -871,19 +949,23 @@ module V6 = struct let of_string s = try_with_result of_string_exn s let _of_netmask_exn ~netmask address = - let nm = - let bits netmask = - V4.Prefix.bits (V4.Prefix.of_netmask_exn ~netmask ~address:V4.any) - in - match netmask with - | 0_l, 0_l, 0_l, 0_l -> 0 - | lsw, 0_l, 0_l, 0_l -> bits lsw - | -1_l, lsw, 0_l, 0_l -> bits lsw + 32 - | -1_l, -1_l, lsw, 0_l -> bits lsw + 64 - | -1_l, -1_l, -1_l, lsw -> bits lsw + 96 - | _ -> raise (Parse_error ("invalid netmask", to_string netmask)) + let count_bits bits is_last_bit_set i = + B128.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_string netmask))) + (bits, is_last_bit_set) i + in + let nm_bits_set, _ = + B128.fold_left + (fun (a, is_last_bit_set) e -> count_bits a is_last_bit_set e) + (0, true) netmask in - make nm address + make nm_bits_set address let of_netmask_exn ~netmask ~address = _of_netmask_exn ~netmask address @@ -923,8 +1005,8 @@ module V6 = struct if sz > 126 then network cidr else network cidr |> succ |> failwith_msg let last ((_, sz) as cidr) = - let ffff = ip 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff in - logor (network cidr) (shift_right ffff sz |> failwith_msg) + let ffff = B128.max_int () in + logor (network cidr) (B128.shift_right ffff sz) end (* TODO: This could be optimized with something trie-like *) @@ -1055,6 +1137,15 @@ let with_port_of_string ~default s = Ok (ipv6, default)) with Parse_error (msg, _) -> Error (`Msg ("Ipaddr: " ^ msg)) +let of_octets_exn bs = + match String.length bs with + | 4 -> V4 (V4.of_octets_exn bs) + | 16 -> V6 (V6.of_octets_exn bs) + | _ -> raise (Parse_error ("octets must be of length 4 or 16", bs)) + +let of_octets bs = try_with_result of_octets_exn bs +let to_octets i = match i with V4 p -> V4.to_octets p | V6 p -> V6.to_octets p + let v6_of_v4 v4 = V6.(Prefix.(network_address ipv4_mapped (of_int32 (0l, 0l, 0l, v4)))) diff --git a/lib/ipaddr.mli b/lib/ipaddr.mli index 522c642..d1f088f 100644 --- a/lib/ipaddr.mli +++ b/lib/ipaddr.mli @@ -18,7 +18,7 @@ (** A library for manipulation of IP address representations. - {e v5.3.1 - {{:https://github.com/mirage/ocaml-ipaddr} homepage}} *) + {e %%VERSION%% - {{:%%PKG_HOMEPAGE%%} homepage}} *) exception Parse_error of string * string (** [Parse_error (err,packet)] is raised when parsing of the IP address syntax @@ -638,6 +638,19 @@ val with_port_of_string : - [::1:8080] returns the IPv6 [::1:8080] with the [default] port - [0:0:0:0:0:0:0:1:8080] returns [::1] with the port [8080]. *) +val of_octets_exn : string -> t +(** [of_octets_exn octets] is the address {!t} represented by [octets]. The + [octets] must be 4 bytes long for a {!V4} or 16 if a {!V6}. Raises + {!Parse_error} if [octets] is not a valid representation of an address. *) + +val of_octets : string -> (t, [> `Msg of string ]) result +(** Same as {!of_octets_exn} but returns a result type instead of raising an + exception. *) + +val to_octets : t -> string +(** [to_octets addr] returns the bytes representing the [addr] octets, which + will be 4 bytes long if addr is a {!V4} or 16 if a {!V6}. *) + val v4_of_v6 : V6.t -> V4.t option (** [v4_of_v6 ipv6] is the IPv4 representation of the IPv6 address [ipv6]. If [ipv6] is not an IPv4-mapped address, None is returned. *) diff --git a/lib/ipaddr_unix.mli b/lib/ipaddr_unix.mli index 78f6291..ad0008b 100644 --- a/lib/ipaddr_unix.mli +++ b/lib/ipaddr_unix.mli @@ -17,7 +17,7 @@ (** Convert to and from [Unix] to [Ipaddr] representations - {e v5.3.1 - {{:https://github.com/mirage/ocaml-ipaddr} homepage}} *) + {e %%VERSION%% - {{:%%PKG_HOMEPAGE%%} homepage}} *) val to_inet_addr : Ipaddr.t -> Unix.inet_addr (** [to_inet_addr ip] is the {!Unix.inet_addr} equivalent of the IPv4 or IPv6 diff --git a/lib/macaddr.mli b/lib/macaddr.mli index 184dbe4..ba9d2f0 100644 --- a/lib/macaddr.mli +++ b/lib/macaddr.mli @@ -16,7 +16,7 @@ (** A library for manipulation of MAC address representations. - {e v5.3.1 - {{:https://github.com/mirage/ocaml-ipaddr} homepage}} *) + {e %%VERSION%% - {{:%%PKG_HOMEPAGE%%} homepage}} *) exception Parse_error of string * string (** [Parse_error (err,packet)] is raised when parsing of the MAC address syntax diff --git a/lib_test/test_ipaddr.ml b/lib_test/test_ipaddr.ml index b87a77f..0d980bf 100644 --- a/lib_test/test_ipaddr.ml +++ b/lib_test/test_ipaddr.ml @@ -633,6 +633,21 @@ module Test_v6 = struct V6.(to_int32 (of_int32 addr)) addr + let test_int64_rt () = + let tests = + [ + (0x2a01_04f9_c011_87adL, 0x0_0_0_0L); + (0x0000_0000_8000_0000L, 0x0_0_0_0L); + ] + in + List.iter + (fun ((a, b) as addr) -> + assert_equal + ~msg:(Printf.sprintf "%016Lx %016Lx" a b) + V6.(to_int64 (of_int64 addr)) + addr) + tests + let test_prefix_string_rt () = let subnets = [ @@ -917,6 +932,7 @@ module Test_v6 = struct "cstruct_rt" >:: test_cstruct_rt; "cstruct_rt_bad" >:: test_cstruct_rt_bad; "int32_rt" >:: test_int32_rt; + "int64_rt" >:: test_int64_rt; "prefix_string_rt" >:: test_prefix_string_rt; "prefix_string_rt_bad" >:: test_prefix_string_rt_bad; "network_address_rt" >:: test_network_address_rt; diff --git a/lib_test/test_ipaddr_b128.ml b/lib_test/test_ipaddr_b128.ml index 19c9491..425e54d 100644 --- a/lib_test/test_ipaddr_b128.ml +++ b/lib_test/test_ipaddr_b128.ml @@ -16,35 +16,167 @@ *) open OUnit +module B128 = Ipaddr_internal.B128 + +(* copied from test_ipaddr.ml *) +let assert_raises ~msg exn test_fn = + assert_raises ~msg exn (fun () -> + try test_fn () + with rtexn -> + if exn <> rtexn then ( + Printf.eprintf "Stacktrace for '%s':\n%!" msg; + Printexc.print_backtrace stderr); + raise rtexn) + +let assert_equal = assert_equal ~printer:Ipaddr_internal.B128.to_string + +let test_addition () = + (* simple addition *) + let d1 = B128.zero () in + let d2 = B128.of_string_exn "00000000000000000000000000000001" in + assert_equal ~msg:"adding one to zero is one" d2 (B128.add_exn d1 d2); + + (* addition carry *) + let d1 = B128.of_string_exn "000000000000000000ff000000000000" in + let d2 = B128.of_string_exn "00000000000000000001000000000000" in + let d3 = B128.of_string_exn "00000000000000000100000000000000" in + assert_equal ~msg:"test addition carry over" d3 (B128.add_exn d1 d2); + + (* adding one to max_int overflows *) + let d1 = B128.max_int () in + let d2 = B128.of_string_exn "00000000000000000000000000000001" in + assert_raises ~msg:"adding one to max_int overflows" B128.Overflow (fun () -> + B128.add_exn d1 d2) + +let test_subtraction () = + (* simple subtraction *) + let d1 = B128.of_string_exn "00000000000000000000000000000001" in + let d2 = B128.of_string_exn "00000000000000000000000000000001" in + let d3 = B128.zero () in + assert_equal ~msg:"subtracting one from one is zero" d3 (B128.sub_exn d1 d2); + + (* subtract carry *) + let d1 = B128.of_string_exn "00000000000000000000000000000300" in + let d2 = B128.of_string_exn "0000000000000000000000000000002a" in + let d3 = B128.of_string_exn "000000000000000000000000000002d6" in + assert_equal ~msg:"test subtraction carry over" d3 (B128.sub_exn d1 d2); + + (* subtracting one from zero overflows *) + let d1 = B128.zero () in + let d2 = B128.of_string_exn "00000000000000000000000000000001" in + assert_raises ~msg:"subtracting one from min_int overflows" B128.Overflow + (fun () -> B128.sub_exn d1 d2) + +let test_of_to_string () = + let s = "ff000000000000004200000000000001" in + OUnit.assert_equal ~msg:"input of of_string is equal to output of to_string" s + (B128.of_string_exn s |> B128.to_string) + +let test_lognot () = + let d1 = B128.of_string_exn "00000000000000000000000000000001" in + let d2 = B128.of_string_exn "fffffffffffffffffffffffffffffffe" in + assert_equal ~msg:"lognot inverts bits" d2 (B128.lognot d1) + +let test_shift_left () = + (* bit shift count, input, expected output *) + let test_shifts = + [ + (1, "f0000000000000000000000000000000", "e0000000000000000000000000000000"); + (1, "0000000000000000000000000000000f", "0000000000000000000000000000001e"); + (1, "00000000000000000000000000000001", "00000000000000000000000000000002"); + (2, "f0000000000000000000000000000000", "c0000000000000000000000000000000"); + (2, "0000000000000000000000000000ffff", "0000000000000000000000000003fffc"); + (8, "00000000000000000000000000000100", "00000000000000000000000000010000"); + (9, "f0000000000000000000000000000000", "00000000000000000000000000000000"); + ( 64, + "00000000000000000000000000000001", + "00000000000000010000000000000000" ); + ( 127, + "00000000000000000000000000000001", + "80000000000000000000000000000000" ); + ( 128, + "00000000000000000000000000000001", + "00000000000000000000000000000000" ); + ] + in + List.iter + (fun (bits, input_value, expected_output) -> + assert_equal + ~msg:(Printf.sprintf "shift left by %i" bits) + (B128.of_string_exn expected_output) + (B128.shift_left (B128.of_string_exn input_value) bits)) + test_shifts let test_shift_right () = - let open Ipaddr_internal in - let open V6 in - let printer = function - | Ok v -> Printf.sprintf "Ok %s" (to_string v) - | Error (`Msg e) -> Printf.sprintf "Error `Msg \"%s\"" e + (* (bit shift count, input, expected output) *) + let test_shifts = + [ + (1, "f0000000000000000000000000000000", "78000000000000000000000000000000"); + (2, "f0000000000000000000000000000000", "3c000000000000000000000000000000"); + (2, "0000000000000000000000000000ffff", "00000000000000000000000000003fff"); + (2, "000000000000000000000000000ffff0", "0000000000000000000000000003fffc"); + (8, "00000000000000000000000000000100", "00000000000000000000000000000001"); + (9, "f0000000000000000000000000000000", "00780000000000000000000000000000"); + ( 32, + "000000000000000000000000ffffffff", + "00000000000000000000000000000000" ); + ( 32, + "0000000000000000aaaabbbbffffffff", + "000000000000000000000000aaaabbbb" ); + ( 40, + "0000000000000000aaaabbbbffffffff", + "00000000000000000000000000aaaabb" ); + ( 64, + "01000000000000000000000000000000", + "00000000000000000100000000000000" ); + ( 120, + "aaaabbbbccccdddd0000000000000000", + "000000000000000000000000000000aa" ); + ( 127, + "80000000000000000000000000000000", + "00000000000000000000000000000001" ); + ( 128, + "ffff0000000000000000000000000000", + "00000000000000000000000000000000" ); + ] in - let assert_equal = assert_equal ~printer in - assert_equal ~msg:":: >> 32" (of_string "::") - (B128.shift_right (of_string_exn "::ffff:ffff") 32); - assert_equal ~msg:"::aaaa:bbbb:ffff:ffff >> 32" (of_string "::aaaa:bbbb") - (B128.shift_right (of_string_exn "::aaaa:bbbb:ffff:ffff") 32); - assert_equal ~msg:"::aaaa:bbbb:ffff:ffff >> 40" (of_string "::aa:aabb") - (B128.shift_right (of_string_exn "::aaaa:bbbb:ffff:ffff") 40); - assert_equal ~msg:"::ffff >> 2" (of_string "::3fff") - (B128.shift_right (of_string_exn "::ffff") 2); - assert_equal ~msg:"ffff:: >> 128" (of_string "::") - (B128.shift_right (of_string_exn "ffff::") 128); - assert_equal ~msg:"aaaa:bbbb:cccc:dddd:: >> 120" (of_string "::aa") - (B128.shift_right (of_string_exn "aaaa:bbbb:cccc:dddd::") 120); - assert_equal ~msg:"ffff:: >> 140" - (Error (`Msg "Ipaddr: unexpected argument sz (must be >= 0 and < 128)")) - (B128.shift_right (of_string_exn "ffff::") 140); - assert_equal ~msg:"::ffff:ffff >> -8" - (Error (`Msg "Ipaddr: unexpected argument sz (must be >= 0 and < 128)")) - (B128.shift_right (of_string_exn "::ffff:ffff") (-8)) - -let suite = "Test B128 module" >::: [ "shift_right" >:: test_shift_right ];; + List.iter + (fun (bits, input_value, expected_output) -> + assert_equal + ~msg:(Printf.sprintf "shift right by %i" bits) + (B128.of_string_exn expected_output) + (B128.shift_right (B128.of_string_exn input_value) bits)) + test_shifts + +let test_byte_module () = + let assert_equal = OUnit2.assert_equal ~printer:(Printf.sprintf "0x%x") in + assert_equal ~msg:"get 3 lsb" 0x00 (B128.Byte.get_lsbits 3 0x00); + assert_equal ~msg:"get 4 lsb" 0x0f (B128.Byte.get_lsbits 4 0xff); + assert_equal ~msg:"get 5 lsb" 0x10 (B128.Byte.get_lsbits 5 0x10); + assert_equal ~msg:"get 8 lsb" 0xff (B128.Byte.get_lsbits 8 0xff); + + assert_equal ~msg:"get 3 msb" 0x0 (B128.Byte.get_msbits 3 0x00); + assert_equal ~msg:"get 4 msb" 0xf (B128.Byte.get_msbits 4 0xff); + assert_equal ~msg:"get 5 msb" 0x2 (B128.Byte.get_msbits 5 0x10); + assert_equal ~msg:"get 8 msb" 0xff (B128.Byte.get_msbits 8 0xff); + + assert_equal ~msg:"set 3 msb" 0x20 (B128.Byte.set_msbits 3 0x1 0x00); + assert_equal ~msg:"set 4 msb" 0xa0 (B128.Byte.set_msbits 4 0xa 0x00); + assert_equal ~msg:"set 5 msb" 0x98 (B128.Byte.set_msbits 5 0x13 0x00); + assert_equal ~msg:"set 8 msb" 0xff (B128.Byte.set_msbits 8 0xff 0x00) + +let suite = + "Test B128 module" + >::: [ + "addition" >:: test_addition; + "subtraction" >:: test_subtraction; + "of_to_string" >:: test_of_to_string; + "lognot" >:: test_lognot; + "shift_left" >:: test_shift_left; + "shift_right" >:: test_shift_right; + "byte_module" >:: test_byte_module; + ] +;; let _results = run_test_tt_main suite in () diff --git a/macaddr-cstruct.opam b/macaddr-cstruct.opam index 6f45751..0314b6d 100644 --- a/macaddr-cstruct.opam +++ b/macaddr-cstruct.opam @@ -1,4 +1,3 @@ -version: "5.3.1" opam-version: "2.0" maintainer: "anil@recoil.org" authors: ["David Sheets" "Anil Madhavapeddy" "Hugo Heuzard"] @@ -22,4 +21,4 @@ build: [ dev-repo: "git+https://github.com/mirage/ocaml-ipaddr.git" description: """ Cstruct convertions for macaddr -"""
\ No newline at end of file +""" diff --git a/macaddr-sexp.opam b/macaddr-sexp.opam index 03807d5..c45186d 100644 --- a/macaddr-sexp.opam +++ b/macaddr-sexp.opam @@ -1,4 +1,3 @@ -version: "5.3.1" opam-version: "2.0" maintainer: "anil@recoil.org" authors: ["David Sheets" "Anil Madhavapeddy" "Hugo Heuzard"] @@ -26,4 +25,4 @@ build: [ dev-repo: "git+https://github.com/mirage/ocaml-ipaddr.git" description: """ Sexp convertions for macaddr -"""
\ No newline at end of file +""" diff --git a/macaddr.opam b/macaddr.opam index 40ea230..6edfd59 100644 --- a/macaddr.opam +++ b/macaddr.opam @@ -1,4 +1,3 @@ -version: "5.3.1" opam-version: "2.0" maintainer: "anil@recoil.org" authors: ["David Sheets" "Anil Madhavapeddy" "Hugo Heuzard"] @@ -30,4 +29,4 @@ Features: * MAC-48 (Ethernet) address support * `Macaddr` is a `Map.OrderedType` * All types have sexplib serializers/deserializers optionally via the `Macaddr_sexp` library. - """
\ No newline at end of file + """ |