* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * $Id: bitmatch.mli,v 1.12 2008-04-02 13:59:37 rjones Exp $
+ * $Id: bitmatch.mli,v 1.16 2008-04-25 12:55:39 rjones Exp $
*)
(**
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*)
- | 4 : 4; hdrlen : 4; tos : 8; length : 16;
- identification : 16; flags : 3; fragoffset : 13;
- ttl : 8; protocol : 8; checksum : 16;
- source : 32;
- dest : 32;
- options : (hdrlen-5)*32 : bitstring;
- payload : -1 : bitstring ->
+ | { 4 : 4; hdrlen : 4; tos : 8; length : 16;
+ identification : 16; flags : 3; fragoffset : 13;
+ ttl : 8; protocol : 8; checksum : 16;
+ source : 32;
+ dest : 32;
+ options : (hdrlen-5)*32 : bitstring;
+ payload : -1 : bitstring } ->
printf "IPv4:\n";
printf " header length: %d * 32 bit words\n" hdrlen;
printf " packet payload:\n";
Bitmatch.hexdump_bitstring stdout payload
- | version : 4 ->
+ | { version : 4 } ->
eprintf "unknown IP version %d\n" version;
exit 1
- | _ as pkt ->
+ | { _ } as pkt ->
eprintf "data is smaller than one nibble:\n";
Bitmatch.hexdump_bitstring stderr pkt;
exit 1
let () =
bitmatch bits with
- | s_inodes_count : 32 : littleendian; (* Inodes count *)
- s_blocks_count : 32 : littleendian; (* Blocks count *)
- s_r_blocks_count : 32 : littleendian; (* Reserved blocks count *)
- s_free_blocks_count : 32 : littleendian; (* Free blocks count *)
- s_free_inodes_count : 32 : littleendian; (* Free inodes count *)
- s_first_data_block : 32 : littleendian; (* First Data Block *)
- s_log_block_size : 32 : littleendian; (* Block size *)
- s_log_frag_size : 32 : littleendian; (* Fragment size *)
- s_blocks_per_group : 32 : littleendian; (* # Blocks per group *)
- s_frags_per_group : 32 : littleendian; (* # Fragments per group *)
- s_inodes_per_group : 32 : littleendian; (* # Inodes per group *)
- s_mtime : 32 : littleendian; (* Mount time *)
- s_wtime : 32 : littleendian; (* Write time *)
- s_mnt_count : 16 : littleendian; (* Mount count *)
- s_max_mnt_count : 16 : littleendian; (* Maximal mount count *)
- 0xef53 : 16 : littleendian -> (* Magic signature *)
+ | { s_inodes_count : 32 : littleendian; (* Inodes count *)
+ s_blocks_count : 32 : littleendian; (* Blocks count *)
+ s_r_blocks_count : 32 : littleendian; (* Reserved blocks count *)
+ s_free_blocks_count : 32 : littleendian; (* Free blocks count *)
+ s_free_inodes_count : 32 : littleendian; (* Free inodes count *)
+ s_first_data_block : 32 : littleendian; (* First Data Block *)
+ s_log_block_size : 32 : littleendian; (* Block size *)
+ s_log_frag_size : 32 : littleendian; (* Fragment size *)
+ s_blocks_per_group : 32 : littleendian; (* # Blocks per group *)
+ s_frags_per_group : 32 : littleendian; (* # Fragments per group *)
+ s_inodes_per_group : 32 : littleendian; (* # Inodes per group *)
+ s_mtime : 32 : littleendian; (* Mount time *)
+ s_wtime : 32 : littleendian; (* Write time *)
+ s_mnt_count : 16 : littleendian; (* Mount count *)
+ s_max_mnt_count : 16 : littleendian; (* Maximal mount count *)
+ 0xef53 : 16 : littleendian } -> (* Magic signature *)
printf "ext3 superblock:\n";
printf " s_inodes_count = %ld\n" s_inodes_count;
printf " s_free_inodes_count = %ld\n" s_free_inodes_count;
printf " s_free_blocks_count = %ld\n" s_free_blocks_count
- | _ ->
+ | { _ } ->
eprintf "not an ext3 superblock!\n%!";
exit 2
]}
*)
let make_message typ subtype param =
- (BITSTRING
+ (BITSTRING {
typ : 16;
subtype : 16;
- param : 32) ;;
+ param : 32
+ }) ;;
]}
{2 Loading, creating bitstrings}
The general form of [bitmatch] is:
- [bitmatch] {i bitstring-expression} [with]
+ [bitmatch {] {i bitstring-expression} [} with]
- [|] {i pattern} [->] {i code}
+ [| {] {i pattern} [} ->] {i code}
- [|] {i pattern} [->] {i code}
+ [| {] {i pattern} [} ->] {i code}
[|] ...
{[
bitmatch bits with
-| version : 8; name : 8; param : 8 -> ...
+| { version : 8; name : 8; param : 8 } -> ...
(* Bitstring of at least 3 bytes. First byte is the version
number, second byte is a field called name, third byte is
a field called parameter. *)
-| flag : 1 ->
+| { flag : 1 } ->
printf "flag is %b\n" flag
(* A single flag bit (mapped into an OCaml boolean). *)
-| len : 4; data : 1+len ->
+| { len : 4; data : 1+len } ->
printf "len = %d, data = 0x%Lx\n" len data
(* A 4-bit length, followed by 1-16 bits of data, where the
length of the data is computed from len. *)
-| ipv6_source : 128 : bitstring;
- ipv6_dest : 128 : bitstring -> ...
+| { ipv6_source : 128 : bitstring;
+ ipv6_dest : 128 : bitstring } -> ...
(* IPv6 source and destination addresses. Each is 128 bits
and is mapped into a bitstring type which will be a substring
You can also add conditional when-clauses:
{[
-| version : 4
+| { version : 4 }
when version = 4 || version = 6 -> ...
(* Only match and run the code when version is 4 or 6. If
length is zero in the when-clause:
{[
-| n : 4;
- rest : -1 : bitstring
+| { n : 4;
+ rest : -1 : bitstring }
when Bitmatch.bitstring_length rest = 0 -> ...
(* Only matches exactly 4 bits. *)
but you can also match a constant, as in:
{[
-| 6 : 4 -> ...
+| { (4|6) : 4 } -> ...
- (* Only matches if the first 4 bits contain the integer 6. *)
+ (* Only matches if the first 4 bits contain either
+ the integer 4 or the integer 6. *)
+]}
+
+ One may also match on strings:
+
+{[
+| { "MAGIC" : 5*8 : string } -> ...
+
+ (* Only matches if the string "MAGIC" appears at the start
+ of the input. *)
]}
{3:patternfieldreference Pattern field reference}
signedness and endianness of the field. Permissible qualifiers are:
- [int] (field has an integer type)
+ - [string] (field is a string type)
- [bitstring] (field is a bitstring type)
- [signed] (field is signed)
- [unsigned] (field is unsigned)
bitstring and/or have a default match case:
{[
-| _ -> ...
+| { _ } -> ...
(* Default match case. *)
-| _ as pkt -> ...
+| { _ } as pkt -> ...
(* Default match case, with 'pkt' bound to the whole bitstring. *)
]}
let version = 1 ;;
let data = 10 ;;
let bits =
- BITSTRING
+ BITSTRING {
version : 4;
- data : 12 ;;
+ data : 12
+ } ;;
(* Constructs a 16-bit bitstring with the first four bits containing
the integer 1, and the following 12 bits containing the integer 10,
overflows. In addition to OCaml's normal bounds checks, we check
that field lengths are >= 0, and many additional checks.
- Denial of service attacks are more problematic although we still
- believe that the library is robust. We only work forwards through
- the bitstring, thus computation will eventually terminate. As for
- computed lengths, code such as this is thought to be secure:
+ Denial of service attacks are more problematic. We only work
+ forwards through the bitstring, thus computation will eventually
+ terminate. As for computed lengths, code such as this is thought
+ to be secure:
-{[
-bitmatch bits with
-| len : 64;
- buffer : Int64.to_int len : bitstring ->
-]}
+ {[
+ bitmatch bits with
+ | { len : 64;
+ buffer : Int64.to_int len : bitstring } ->
+ ]}
The [len] field can be set arbitrarily large by an attacker, but
when pattern-matching against the [buffer] field this merely causes
allocation of sub-bitstrings is efficient and doesn't involve an
arbitary-sized allocation or any copying.
- The main protection against attackers should therefore be to ensure
- that the main program will only read input bitstrings up to a
- certain length, which is outside the scope of this library.
+ However the above does not necessarily apply to strings used in
+ matching, since they may cause the library to use the
+ {!Bitmatch.string_of_bitstring} function, which allocates a string.
+ So you should take care if you use the [string] type particularly
+ with a computed length that is derived from external input.
+
+ The main protection against attackers should be to ensure that the
+ main program will only read input bitstrings up to a certain
+ length, which is outside the scope of this library.
{3 Security on output}
As with the input side, computed lengths are believed to be
safe. For example:
-{[
-let len = read_untrusted_source () in
-let buffer = allocate_bitstring () in
-BITSTRING
- buffer : len : bitstring
-]}
+ {[
+ let len = read_untrusted_source () in
+ let buffer = allocate_bitstring () in
+ BITSTRING {
+ buffer : len : bitstring
+ }
+ ]}
This code merely causes a check that buffer's length is the same as
[len]. However the program function [allocate_bitstring] must
(** [bitstring_length bitstring] returns the length of
the bitstring in bits. *)
+val string_of_bitstring : bitstring -> string
+(** [string_of_bitstring bitstring] converts a bitstring to a string
+ (eg. to allow comparison).
+
+ This function is inefficient. In the best case when the bitstring
+ is nicely byte-aligned we do a [String.sub] operation. If the
+ bitstring isn't aligned then this involves a lot of bit twiddling
+ and is particularly inefficient. *)
+
(** {3 Bitstring buffer} *)
module Buffer : sig