1 module asn1.tools.decoder_mixin; 2 3 mixin template Decoder(Element) 4 { 5 import asn1.constants; 6 import std.bigint : BigInt; 7 import std.conv : ConvException; 8 import std.stdio : write, writeln, writefln, stdin; 9 import std.utf : UTFException; 10 11 int main(string[] args) 12 { 13 enum ReturnValue : int 14 { 15 success = 0, 16 elementTerminatedPrematurely = 1, 17 invalidEncodedValue = 2, 18 unexpectedException = int.max 19 } 20 21 ubyte[] data = stdin.rawRead!ubyte(new ubyte[500000]); 22 23 if (args.length == 2u && data.length > 0u) 24 { 25 if (args[1] == "-n") 26 { 27 if (data[$-1] == '\n') 28 { 29 data = data[0 .. $-1]; 30 writeln("Removed a trailing LineFeed character from input."); 31 } 32 else writeln("There was no trailing LineFeed character to remove from input."); 33 } 34 else if (args[1] == "-r") 35 { 36 if (data[$-2 .. $] == "\r\n") 37 { 38 data = data[0 .. $-2]; 39 writeln("Removed a trailing CarriageReturn+LineFeed from input."); 40 } 41 else writeln("There was no trailing CarriageReturn+LineFeed to remove from input."); 42 } 43 else writeln("Ignoring unrecognized option or argument: '" ~ args[1] ~"'."); 44 } 45 46 Element[] tops; 47 48 try 49 { 50 while (data.length > 0u) 51 tops ~= new Element(data); 52 } 53 catch (ASN1ValueSizeException e) 54 { 55 writeln("\n", e.msg, "\n"); 56 return ReturnValue.elementTerminatedPrematurely; 57 } 58 catch (ASN1Exception e) 59 { 60 writeln("\n", e.msg, "\n"); 61 return ReturnValue.invalidEncodedValue; 62 } 63 catch (Exception e) 64 { 65 writeln("\n", e.msg, "\n"); 66 return ReturnValue.unexpectedException; 67 } 68 69 writeln(); 70 foreach (top; tops) 71 { 72 try 73 { 74 display(top, 0u); 75 } 76 catch (ASN1ValueSizeException e) 77 { 78 writeln("\n", e.msg, "\n"); 79 return ReturnValue.elementTerminatedPrematurely; 80 } 81 catch (ASN1Exception e) 82 { 83 writeln("\n", e.msg, "\n"); 84 return ReturnValue.invalidEncodedValue; 85 } 86 catch (Exception e) 87 { 88 writeln("\n", e.msg, "\n"); 89 return ReturnValue.unexpectedException; 90 } 91 } 92 93 writeln(); 94 return ReturnValue.success; 95 } 96 97 void display (Element element, ubyte indentation) 98 { 99 string tagClassString = ""; 100 bool universal = false; 101 switch (element.tagClass) 102 { 103 case (ASN1TagClass.universal): 104 { 105 tagClassString = "UNIV"; 106 universal = true; 107 break; 108 } 109 case (ASN1TagClass.application): 110 { 111 tagClassString = "APPL"; 112 break; 113 } 114 case (ASN1TagClass.contextSpecific): 115 { 116 tagClassString = "CTXT"; 117 break; 118 } 119 case (ASN1TagClass.privatelyDefined): 120 { 121 tagClassString = "PRIV"; 122 break; 123 } 124 default: assert(0, "Impossible tagClass encountered!"); 125 } 126 127 char[] indents; 128 indents.length = indentation; 129 indents[0 .. $] = ' '; 130 131 if (element.construction == ASN1Construction.primitive) 132 { 133 if (universal) 134 writefln("%s[ %s %d ] : %s", cast(string) indents, tagClassString, element.tagNumber, stringifyUniversalValue(element)); 135 else 136 writefln("%s[ %s %d ] : %(%02X %)", cast(string) indents, tagClassString, element.tagNumber, element.value); 137 } 138 else 139 { 140 writefln("%s[ %s %d ] :", cast(string) indents, tagClassString, element.tagNumber); 141 indentation += 4; 142 ubyte[] value = element.value.dup; 143 Element[] subs; 144 size_t i = 0u; 145 while (i < value.length) 146 subs ~= new Element(i, value); 147 148 foreach (sub; subs) 149 { 150 display(new Element(element.value), indentation); 151 } 152 } 153 indentation -= 4; 154 } 155 156 string stringifyBitString (in bool[] value) 157 { 158 char[] ret; 159 ret.length = value.length; 160 foreach(size_t index, bool boolean; value) 161 { 162 if (boolean) 163 ret[index] = '1'; 164 else 165 ret[index] = '0'; 166 } 167 return cast(string) ret; 168 } 169 170 string stringifyUniversalValue (Element element) 171 { 172 import std.conv : text; 173 import std.format : format; 174 175 switch (element.tagNumber) 176 { 177 case (0u): return "END OF CONTENT"; 178 case (1u): return (element.boolean ? "TRUE" : "FALSE"); 179 case (2u): return format("%d", element.integer!BigInt); 180 case (3u): return stringifyBitString(element.bitString); 181 case (4u): return format("%(%02X %)", element.octetString); 182 case (5u): return "NULL"; 183 case (6u): return element.objectIdentifier.toString(); 184 case (7u): return element.objectDescriptor; 185 case (8u): return "EXTERNAL"; // This should never be executed. 186 case (9u): return text(element.realNumber!real); 187 case (10u): return text(element.enumerated!ptrdiff_t); 188 case (11u): return "EmbeddedPDV"; // This should never be executed. 189 case (12u): return element.utf8String; 190 case (13u): return ("RELATIVE OID: " ~ text(element.value)); 191 case (14u): return "!!! INVALID TYPE : RESERVED 14 !!!"; 192 case (15u): return "!!! INVALID TYPE : RESERVED 15 !!!"; 193 case (16u): return "SEQUENCE"; // This should never be executed. 194 case (17u): return "SET"; // This should never be executed. 195 case (18u): return element.numericString; 196 case (19u): return element.printableString; 197 case (20u): return format("%(%02X %)", element.teletexString); 198 case (21u): return format("%(%02X %)", element.videotexString); 199 case (22u): return element.ia5String; 200 case (23u): return element.utcTime.toISOString(); 201 case (24u): return element.generalizedTime.toISOString(); 202 case (25u): return element.graphicString; 203 case (26u): return element.visibleString; 204 case (27u): return element.generalString; 205 case (28u): return cast(string) element.universalString; 206 case (29u): return "CharacterString"; // This should never be executed. 207 case (30u): return cast(string) element.bmpString; 208 case (31u): return "!!! INVALID TYPE : UNDEFINED 31 !!!"; 209 default: return "!!! INVALID TYPE : tagNumber above 31 !!!"; 210 } 211 } 212 }