import sys import getopt import os.path def compressTokens(tokens): ret = [] for t in tokens: if t: ret.append(t) return ret class AsnField(object): ''' ''' def __init__(self, name, seqIndex, optional, asnType, subType): self.name = name self.seqIndex = seqIndex self.optional = optional self.asnType = asnType self.asnTypePointer = None self.subType = subType self.outputType = None self.lenFunc = None self.writeFunc = None self.readFunc = None self.cleanupFunc = None self.freeFunc = None self.options = [] if asnType.upper() in ['OCTET STRING', 'INTEGER']: self.asnTypePointer = self.asnType def __str__(self): return "{0} [{1}] {2}{3}".format(self.name, self.seqIndex, self.asnType, self.optional and " OPTIONAL" or "") class AsnSequence(object): ''' ''' def __init__(self, name): self.name = name self.fields = [] FIELD_OPTION = 'fieldOption' CHAR_TO_UNICODE = 'charInMemorySerializeToUnicode' UNICODE = "unicode" KNOWN_FIELD_OPTIONS = (CHAR_TO_UNICODE, UNICODE,) class AsnParser(object): KNOWN_OPTIONS = (FIELD_OPTION, 'prefix', ) STATE_ROOT, STATE_IN_ITEM, STATE_IN_OPTIONS = range(0, 3) def __init__(self): self.state = AsnParser.STATE_ROOT self.defs = {} self.currentItem = None self.currentName = None self.emitArraycode = [] # options self.options = { 'prefix': '', 'octetStringLen': { 'char': 'strlen(item->{fieldName})', 'WCHAR': '_wcslen(item->{fieldName}) * 2', CHAR_TO_UNICODE: 'strlen(item->{fieldName}) * 2', } } def parse(self, content): for l in content.split("\n"): if l.startswith('#'): continue tokens = compressTokens(l.lstrip().rstrip().split(' ')) if self.state == AsnParser.STATE_ROOT: if not len(tokens): continue if tokens[0] == "%options" and tokens[1] == "{": self.state = AsnParser.STATE_IN_OPTIONS continue if tokens[1] != "::=": continue if tokens[2] != "SEQUENCE": raise Exception("ERROR: not handling non sequence items for now") self.currentName = tokens[0] self.currentItem = AsnSequence(tokens[0]) self.state = AsnParser.STATE_IN_ITEM elif self.state == AsnParser.STATE_IN_ITEM: if tokens[0] == '}': self.defs[self.currentName] = self.currentItem self.state = AsnParser.STATE_ROOT continue optional = tokens[-1] in ["OPTIONAL", "OPTIONAL,"] fieldIndex = int(tokens[1][1:-1]) if optional: typeTokens = tokens[2:-1] else: typeTokens = tokens[2:] asnType = " ".join(typeTokens) if asnType[-1] == ',': asnType = asnType[0:-1] subType = None if asnType.startswith("SEQUENCE OF"): subType = typeTokens[-1] asnType = "SEQUENCE OF" self.currentItem.fields.append(AsnField(tokens[0], fieldIndex, optional, asnType, subType)) elif self.state == AsnParser.STATE_IN_OPTIONS: if not len(tokens) or l.startswith("#"): continue if tokens[0] == "}": self.state == AsnParser.STATE_ROOT continue option = tokens[0] if option not in AsnParser.KNOWN_OPTIONS: raise Exception("unknown option '{0}'".format(option)) if option == FIELD_OPTION: target = tokens[1] objName, fieldName = target.split(".", 2) obj = self.defs.get(objName, None) if not obj: raise Exception("object type {0} unknown".format(objName)) found = False for field in obj.fields: if field.name == fieldName: found = True break if not found: raise Exception("object {0} has no field {1}".format(objName, fieldName)) if tokens[2] not in KNOWN_FIELD_OPTIONS: raise Exception("unknown field option {0}".format(objName, tokens[2])) field.options.append(tokens[2]) elif option == "prefix": self.options['prefix'] = tokens[1] # try to resolve custom types in fields for typeDef in self.defs.values(): for field in typeDef.fields: if field.asnTypePointer is None: field.asnTypePointer = self.defs.get(field.asnType, None) if field.asnType == "SEQUENCE OF": self.emitArraycode.append(field.subType) # adjust AsnField fields for typeDef in self.defs.values(): for field in typeDef.fields: if field.asnType == "OCTET STRING": fieldType = field.outputType if not fieldType: fieldType = "WCHAR" if CHAR_TO_UNICODE in field.options: fieldType = "char" field.outputType = "char" field.writeFunc = "ber_write_contextual_char_to_unicode_octet_string(s, {fieldIndex}, item->{fieldName})" field.lenFunc = "ber_sizeof_contextual_octet_string(strlen(item->{fieldName}) * 2)" field.readFunc = "ber_read_char_from_unicode_octet_string({stream}, &item->{fieldName})" elif UNICODE in field.options: fieldType = "WCHAR" field.outputType = "WCHAR" field.writeFunc = "ber_write_contextual_unicode_octet_string(s, {fieldIndex}, item->{fieldName})" field.lenFunc = "ber_sizeof_contextual_octet_string(" + self.options['octetStringLen'][fieldType] + ")" field.readFunc = "ber_read_unicode_octet_string({stream}, &item->{fieldName})" else: field.writeFunc = "ber_write_contextual_octet_string(s, {fieldIndex}, item->{fieldName}, item->{fieldName}Len)" field.lenFunc = "ber_sizeof_contextual_octet_string(item->{fieldName}Len)" field.readFunc = "ber_read_octet_string({stream}, &item->{fieldName}, &item->{fieldName}Len)" field.cleanupFunc = "free(item->{fieldName});" elif field.asnType == "INTEGER": field.lenFunc = "ber_sizeof_contextual_integer(item->{fieldName})" field.writeFunc = "ber_write_contextual_integer(s, {fieldIndex}, item->{fieldName})" field.readFunc = "ber_read_integer({stream}, &item->{fieldName})" field.cleanupFunc = "" elif field.asnType == "SEQUENCE OF": field.lenFunc = "ber_sizeof_contextual_{prefix}{fieldSubType}_array(item->{fieldName}, item->{fieldName}Items)" field.writeFunc = "ber_write_contextual_{prefix}{fieldSubType}_array(s, {fieldIndex}, item->{fieldName}, item->{fieldName}Items)" field.readFunc = "ber_read_{prefix}{fieldSubType}_array({stream}, &item->{fieldName}, &item->{fieldName}Items)" field.cleanupFunc = "" else: field.lenFunc = "ber_sizeof_contextual_{prefix}{fieldType}(item->{fieldName})" field.writeFunc = "ber_write_contextual_{prefix}{fieldType}(s, {fieldIndex}, item->{fieldName})" field.readFunc = "ber_read_{prefix}{fieldType}({stream}, &item->{fieldName})" field.cleanupFunc = "{prefix}{fieldType}_free(&item->{fieldName});" return True def emitStructDefs(self): ret = '' for defName, seq in self.defs.items(): h = { 'prefix': self.options['prefix'], 'defName': defName } ret += 'typedef struct {\n' for field in seq.fields: if field.asnType == "INTEGER": ret += "\tUINT32 {fieldName};\n".format(fieldName=field.name) elif field.asnType == "OCTET STRING": fieldType = field.outputType if CHAR_TO_UNICODE in field.options: fieldType = 'char' elif fieldType == 'WCHAR': pass else: fieldType = 'BYTE' ret += "\tsize_t {fieldName}Len;\n".format(fieldName=field.name, fieldType=fieldType) ret += "\t{fieldType}* {fieldName};\n".format(fieldName=field.name, fieldType=fieldType) elif field.asnType == "SEQUENCE OF": ret += "\tsize_t {fieldName}Items;\n".format(fieldName=field.name, fieldType=field.subType) ret += "\t{fieldType}_t* {fieldName};\n".format(fieldName=field.name, fieldType=field.subType) else: ret += "\t{typeName}_t* {fieldName};\n".format(fieldName=field.name, typeName=field.asnType) ret += '}} {defName}_t;\n\n'.format(**h) return ret def emitPrototypes(self): ret = '' for defName, seq in self.defs.items(): h = { 'prefix': self.options['prefix'], 'defName': defName } ret += "size_t ber_sizeof_{prefix}{defName}_content(const {defName}_t* item);\n".format(**h) ret += "size_t ber_sizeof_{prefix}{defName}(const {defName}_t* item);\n".format(**h) ret += "size_t ber_sizeof_contextual_{prefix}{defName}(const {defName}_t* item);\n".format(**h) ret += "void {prefix}{defName}_free({defName}_t** pitem);\n".format(**h) ret += "size_t ber_write_{prefix}{defName}(wStream *s, const {defName}_t* item);\n".format(**h) ret += "size_t ber_write_contextual_{prefix}{defName}(wStream *s, BYTE tag, const {defName}_t* item);\n".format(**h) ret += 'BOOL ber_read_{prefix}{defName}(wStream *s, {defName}_t** pret);\n'.format(**h) if defName in self.emitArraycode: ret += "size_t ber_sizeof_{prefix}{defName}_array_content(const {defName}_t* item, size_t nitems);\n".format(**h) ret += "size_t ber_sizeof_{prefix}{defName}_array(const {defName}_t* item, size_t nitems);\n".format(**h) ret += "size_t ber_sizeof_contextual_{prefix}{defName}_array(const {defName}_t* item, size_t nitems);\n".format(**h) ret += "size_t ber_write_{prefix}{defName}_array(wStream* s, const {defName}_t* item, size_t nitems);\n".format(**h) ret += "size_t ber_write_contextual_{prefix}{defName}_array(wStream* s, BYTE tag, const {defName}_t* item, size_t nitems);\n".format(**h) ret += "BOOL ber_read_{prefix}{defName}_array(wStream* s, {defName}_t** item, size_t* nitems);\n".format(**h) ret += '\n' return ret def emitImpl(self): ret = '' for defName, seq in self.defs.items(): h = { 'prefix': self.options['prefix'], 'defName': defName } # ================= ber_sizeof_ ========================================= ret += "size_t ber_sizeof_{prefix}{defName}_content(const {defName}_t* item) {{\n".format(**h) ret += "\tsize_t ret = 0;\n\n" for field in seq.fields: h2 = {'fieldName': field.name, 'fieldIndex': field.seqIndex, 'fieldType':field.asnType, 'fieldSubType': field.subType } shift = '\t' ret += shift + "/* [{fieldIndex}] {fieldName} ({fieldType}){optional}*/\n".format(**h2, optional=field.optional and " OPTIONAL" or "") if field.optional: ret += shift + "if (item->{fieldName}) {{\n".format(fieldName=field.name) shift = '\t\t' ret += shift + "ret += " + field.lenFunc.format(**h2, **h) + ";\n" if field.optional: ret += "\t}\n" ret += '\n' ret += '\treturn ret;\n' ret += '}\n\n' ret += '''size_t ber_sizeof_{prefix}{defName}(const {defName}_t* item) {{ size_t ret = ber_sizeof_{prefix}{defName}_content(item); return ber_sizeof_sequence(ret); }} '''.format(**h) ret += "size_t ber_sizeof_contextual_{prefix}{defName}(const {defName}_t* item) {{\n".format(**h) ret += "\tsize_t innerSz = ber_sizeof_{prefix}{defName}(item);\n".format(**h) ret += "\treturn ber_sizeof_contextual_tag(innerSz) + innerSz;\n" ret += "}\n\n" # ================= free_ ========================================= ret += "void {prefix}{defName}_free({defName}_t** pitem) {{\n".format(**h) ret += "\t{defName}_t* item;\n\n".format(**h) ret += "\tWINPR_ASSERT(pitem);\n" ret += "\titem = *pitem;\n" ret += "\tif (!item)\n" ret += "\t\treturn;\n\n" for field in seq.fields: if field.cleanupFunc: h2 = { 'fieldName': field.name, 'fieldIndex': field.seqIndex, 'fieldType':field.asnType } ret += "\t" + field.cleanupFunc.format(**h2, **h) + "\n" ret += "\tfree(item);\n" ret += "\t*pitem = NULL;\n" ret += "}\n\n" # ================= ber_write_ ========================================= ret += '''size_t ber_write_{prefix}{defName}(wStream *s, const {defName}_t* item) {{ size_t content_size = ber_sizeof_{prefix}{defName}_content(item); size_t ret = 0; ret = ber_write_sequence_tag(s, content_size); '''.format(**h) for field in seq.fields: h2 = { 'fieldName': field.name, 'fieldIndex': field.seqIndex, 'fieldType':field.asnType, 'fieldSubType': field.subType } shift = " " ret += shift + "/* [{fieldIndex}] {fieldName} ({fieldType}){optional} */\n".format(**h2, optional=field.optional and " OPTIONAL" or "") if field.optional: ret += shift + "if (item->{fieldName}) {{\n" .format(**h2) shift += ' ' ret += shift + "if (!" + field.writeFunc.format(**h2, **h) + ")\n" ret += shift + ' return 0;\n' if field.optional: ret += " }\n" ret += "\n" ret += ' return ret + content_size;\n' ret += '}\n\n' ret += '''size_t ber_write_contextual_{prefix}{defName}(wStream *s, BYTE tag, const {defName}_t* item) {{ size_t ret; size_t inner = ber_sizeof_{prefix}{defName}(item); ret = ber_write_contextual_tag(s, tag, inner, TRUE); ber_write_{prefix}{defName}(s, item); return ret + inner; }} '''.format(**h) # ================= ber_read_ ========================================= ret += '''BOOL ber_read_{prefix}{defName}(wStream *s, {defName}_t** pret) {{ wStream seqstream; size_t seqLength; size_t inner_size; wStream fieldStream; {defName}_t* item; BOOL ret; if (!ber_read_sequence_tag(s, &seqLength) || Stream_GetRemainingLength(s) < seqLength) return FALSE; Stream_StaticInit(&seqstream, Stream_Pointer(s), seqLength); item = calloc(1, sizeof(*item)); if (!item) return FALSE; '''.format(**h) shiftLevel = 1 shift = ' ' * 4 * shiftLevel cleanupLabels = [] for field in seq.fields: h2 = { 'fieldName': field.name, 'fieldIndex': field.seqIndex, 'fieldType':field.asnType, 'stream': '&fieldStream', 'fieldSubType': field.subType } cleanupLabels.insert(0, "\t" + field.cleanupFunc.format(**h2, **h)) cleanupLabels.insert(1, "out_fail_{fieldName}:".format(**h2, **h)) ret += shift + "/* [{fieldIndex}] {fieldName} ({fieldType}){optional} */\n".format(**h2, optional=field.optional and " OPTIONAL" or "") ret += shift + 'ret = ber_read_contextual_tag(&seqstream, {fieldIndex}, &inner_size, TRUE);\n'.format(**h2) if not field.optional: ret += shift + "if (!ret) \n" ret += shift + '\tgoto out_fail_{fieldName};\n'.format(**h2) else: ret += shift + "if (ret) { \n" shiftLevel += 1 shift = ' ' * 4 * shiftLevel ret += shift + "Stream_StaticInit(&fieldStream, Stream_Pointer(&seqstream), inner_size);\n" ret += shift + "Stream_Seek(&seqstream, inner_size);\n" ret += '\n' ret += shift + "ret = " + field.readFunc.format(**h2, **h) + ";\n" ret += shift + "if (!ret)\n" ret += shift + '\tgoto out_fail_{fieldName};\n'.format(**h2) if field.optional: shiftLevel -= 1 shift = ' ' * 4 * shiftLevel ret += shift + '}' ret += '\n' ret += shift + "*pret = item;\n" ret += shift + "return TRUE;\n" ret += '\n' cleanupLabels = cleanupLabels[1:] ret += "\n".join(cleanupLabels) ret += '\n' ret += shift + 'free(item);\n' ret += shift + 'return FALSE;\n' ret += '}\n\n' # ====================== code for handling arrays ==================================== if defName in self.emitArraycode: ret += '''size_t ber_sizeof_{prefix}{defName}_array_content(const {defName}_t* item, size_t nitems) {{ size_t i, ret = 0; for (i = 0; i < nitems; i++, item++) ret += ber_sizeof_{prefix}{defName}(item); return ber_sizeof_sequence(ret); }} size_t ber_sizeof_{prefix}{defName}_array(const {defName}_t* item, size_t nitems) {{ return ber_sizeof_sequence( ber_sizeof_{prefix}{defName}_array_content(item, nitems) ); }} size_t ber_sizeof_contextual_{prefix}{defName}_array(const {defName}_t* item, size_t nitems) {{ size_t inner = ber_sizeof_{prefix}{defName}_array(item, nitems); return ber_sizeof_contextual_tag(inner) + inner; }} size_t ber_write_{prefix}{defName}_array(wStream* s, const {defName}_t* item, size_t nitems) {{ size_t i, r, ret; size_t inner_len = ber_sizeof_{prefix}{defName}_array_content(item, nitems); ret = ber_write_sequence_tag(s, inner_len); for (i = 0; i < nitems; i++, item++) {{ r = ber_write_{prefix}{defName}(s, item); if (!r) return 0; ret += r; }} return ret; }} size_t ber_write_contextual_{prefix}{defName}_array(wStream* s, BYTE tag, const {defName}_t* item, size_t nitems) {{ size_t ret; size_t inner = ber_sizeof_{prefix}{defName}_array(item, nitems); ret = ber_write_contextual_tag(s, tag, inner, TRUE); ber_write_{prefix}{defName}_array(s, item, nitems); return ret + inner; }} BOOL ber_read_{prefix}{defName}_array(wStream* s, {defName}_t** pitems, size_t* nitems) {{ size_t subLen; wStream subStream; {defName}_t* retItems = NULL; size_t ret = 0; if (!ber_read_sequence_tag(s, &subLen) || Stream_GetRemainingLength(s) < subLen) return FALSE; Stream_StaticInit(&subStream, Stream_Pointer(s), subLen); while (Stream_GetRemainingLength(&subStream)) {{ {defName}_t *item; {defName}_t* tmpRet; if (!ber_read_{prefix}{defName}(&subStream, &item)) {{ free(retItems); return FALSE; }} tmpRet = realloc(retItems, (ret+1) * sizeof({defName}_t)); if (!tmpRet) {{ free(retItems); return FALSE; }} memcpy(&retItems[ret], item, sizeof(*item)); free(item); ret++; }} *pitems = retItems; *nitems = ret; return TRUE; }} '''.format(**h) return ret if __name__ == '__main__': opts, extraArgs = getopt.getopt(sys.argv[1:], "hi:o:t:", ['input=', 'output=', 'output-kind=', 'help']) ALLOWED_OUTPUTS = ('headers', 'impls',) inputStream = sys.stdin outputStream = sys.stdout outputs = ALLOWED_OUTPUTS inputHeaderName = "ASN1_HEADER_H" headerFileName = "tscredentials.h" for option, value in opts: if option in ('-h', '--help',): print("usage: {0} [-i|--input]= [-o|--output]=") print("\t[-i|--input] : input file") print("\t[-o|--output] : output file") print("\t[-t|--output-kind] [header|impl]: the kind of output") elif option in ('-o', '--output',): outputStream = open(value, "w") # libfreerdp/core/credssp.c => credssp.h headerFileName = '.'.join( [os.path.splitext(os.path.basename(value))[0], 'h' ]) elif option in ('-i', '--input',): inputStream = open(value, "r") # libfreerdp/core/credssp.asn1 => LIBFREERDP_CORE_CREDSSP_ASN1_H inputHeaderName = os.path.normpath(value).replace(os.path.sep, '_').replace('.', '_').upper() + '_H' elif option in ('-t', '--output-kind',): if value not in ALLOWED_OUTPUTS: raise Exception("unknown output kind '{0}'".format(value)) outputs = value else: raise Exception("unknown option {0}".format(option)) input = inputStream.read() parser = AsnParser() parser.parse(input) h = {'programName': os.path.basename(sys.argv[0]), 'cmdLine': ' '.join(sys.argv), 'programPath': sys.argv[0], 'inputHeaderName': inputHeaderName, 'headerFileName': headerFileName } outputStream.write('''/* ============================================================================================================ * this file has been generated using * {cmdLine} * * /!\\ If you want to modify this file you'd probably better change {programName} or the corresponding ASN1 * definition file * * ============================================================================================================ */ '''.format(**h)) if outputs == 'headers': outputStream.write('''#ifndef {inputHeaderName} #define {inputHeaderName} #include '''.format(**h)) outputStream.write(parser.emitStructDefs()) outputStream.write(parser.emitPrototypes()) outputStream.write('#endif /* {inputHeaderName} */\n'.format(**h)) elif outputs == "impls": outputStream.write(''' #include #include #include "{headerFileName}" '''.format(**h)) outputStream.write(parser.emitImpl())