#!/usr/bin/perl -w # # Build a static ASN.1 Object Identified (OID) registry # # Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. # Written by David Howells (dhowells@redhat.com) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public Licence # as published by the Free Software Foundation; either version # 2 of the Licence, or (at your option) any later version. # use strict; my @names = (); my @oids = (); if ($#ARGV != 1) { print STDERR "Format: ", $0, " <in-h-file> <out-c-file>\n"; exit(2); } # # Open the file to read from # open IN_FILE, "<$ARGV[0]" || die; while (<IN_FILE>) { chomp; if (m!\s+OID_([a-zA-z][a-zA-Z0-9_]+),\s+/[*]\s+([012][.0-9]*)\s+[*]/!) { push @names, $1; push @oids, $2; } } close IN_FILE || die; # # Open the files to write into # open C_FILE, ">$ARGV[1]" or die; print C_FILE "/*\n"; print C_FILE " * Automatically generated by ", $0, ". Do not edit\n"; print C_FILE " */\n"; # # Split the data up into separate lists and also determine the lengths of the # encoded data arrays. # my @indices = (); my @lengths = (); my $total_length = 0; print "Compiling ", $#names + 1, " OIDs\n"; for (my $i = 0; $i <= $#names; $i++) { my $name = $names[$i]; my $oid = $oids[$i]; my @components = split(/[.]/, $oid); # Determine the encoded length of this OID my $size = $#components; for (my $loop = 2; $loop <= $#components; $loop++) { my $c = $components[$loop]; # We will base128 encode the number my $tmp = ($c == 0) ? 0 : int(log($c)/log(2)); $tmp = int($tmp / 7); $size += $tmp; } push @lengths, $size; push @indices, $total_length; $total_length += $size; } # # Emit the look-up-by-OID index table # print C_FILE "\n"; if ($total_length <= 255) { print C_FILE "static const unsigned char oid_index[OID__NR + 1] = {\n"; } else { print C_FILE "static const unsigned short oid_index[OID__NR + 1] = {\n"; } for (my $i = 0; $i <= $#names; $i++) { print C_FILE "\t[OID_", $names[$i], "] = ", $indices[$i], ",\n" } print C_FILE "\t[OID__NR] = ", $total_length, "\n"; print C_FILE "};\n"; # # Encode the OIDs # my @encoded_oids = (); for (my $i = 0; $i <= $#names; $i++) { my @octets = (); my @components = split(/[.]/, $oids[$i]); push @octets, $components[0] * 40 + $components[1]; for (my $loop = 2; $loop <= $#components; $loop++) { my $c = $components[$loop]; # Base128 encode the number my $tmp = ($c == 0) ? 0 : int(log($c)/log(2)); $tmp = int($tmp / 7); for (; $tmp > 0; $tmp--) { push @octets, (($c >> $tmp * 7) & 0x7f) | 0x80; } push @octets, $c & 0x7f; } push @encoded_oids, \@octets; } # # Create a hash value for each OID # my @hash_values = (); for (my $i = 0; $i <= $#names; $i++) { my @octets = @{$encoded_oids[$i]}; my $hash = $#octets; foreach (@octets) { $hash += $_ * 33; } $hash = ($hash >> 24) ^ ($hash >> 16) ^ ($hash >> 8) ^ ($hash); push @hash_values, $hash & 0xff; } # # Emit the OID data # print C_FILE "\n"; print C_FILE "static const unsigned char oid_data[", $total_length, "] = {\n"; for (my $i = 0; $i <= $#names; $i++) { my @octets = @{$encoded_oids[$i]}; print C_FILE "\t"; print C_FILE $_, ", " foreach (@octets); print C_FILE "\t// ", $names[$i]; print C_FILE "\n"; } print C_FILE "};\n"; # # Build the search index table (ordered by length then hash then content) # my @index_table = ( 0 .. $#names ); @index_table = sort { my @octets_a = @{$encoded_oids[$a]}; my @octets_b = @{$encoded_oids[$b]}; return $hash_values[$a] <=> $hash_values[$b] if ($hash_values[$a] != $hash_values[$b]); return $#octets_a <=> $#octets_b if ($#octets_a != $#octets_b); for (my $i = $#octets_a; $i >= 0; $i--) { return $octets_a[$i] <=> $octets_b[$i] if ($octets_a[$i] != $octets_b[$i]); } return 0; } @index_table; # # Emit the search index and hash value table # print C_FILE "\n"; print C_FILE "static const struct {\n"; print C_FILE "\tunsigned char hash;\n"; if ($#names <= 255) { print C_FILE "\tenum OID oid : 8;\n"; } else { print C_FILE "\tenum OID oid : 16;\n"; } print C_FILE "} oid_search_table[OID__NR] = {\n"; for (my $i = 0; $i <= $#names; $i++) { my @octets = @{$encoded_oids[$index_table[$i]]}; printf(C_FILE "\t[%3u] = { %3u, OID_%-35s }, // ", $i, $hash_values[$index_table[$i]], $names[$index_table[$i]]); printf C_FILE "%02x", $_ foreach (@octets); print C_FILE "\n"; } print C_FILE "};\n"; # # Emit the OID debugging name table # #print C_FILE "\n"; #print C_FILE "const char *const oid_name_table[OID__NR + 1] = {\n"; # #for (my $i = 0; $i <= $#names; $i++) { # print C_FILE "\t\"", $names[$i], "\",\n" #} #print C_FILE "\t\"Unknown-OID\"\n"; #print C_FILE "};\n"; # # Polish off # close C_FILE or die;