2009-09-29 48 views
10

Tôi đang tìm một giải pháp để phân tích cú pháp các tệp spec và tạo bộ giải mã từ những tệp đó. Lý tưởng nhất là tôi muốn làm việc với các mô-đun Python, nhưng nếu không có gì, tôi sẽ sử dụng các thư viện C/C++ và giao tiếp chúng với Python với rất nhiều giải pháp trên đó.trình phân tích cú pháp asn.1 trong C/Python

Trong quá khứ tôi đã sử dụng pyasn1 và xây dựng mọi thứ bằng tay nhưng điều đó đã trở nên quá khó sử dụng.

Tôi cũng đã xem bề ngoài để libtasn1 và asn1c. Việc đầu tiên có vấn đề phân tích cú pháp ngay cả những tập tin đơn giản nhất. Thứ hai có một trình phân tích cú pháp tốt nhưng việc tạo mã C để giải mã có vẻ quá phức tạp; giải pháp hoạt động tốt với thông số kỹ thuật đơn giản nhưng bị nghẹt thở trên các thông số phức tạp.

Bất kỳ lựa chọn thay thế tốt nào khác mà tôi có thể đã bỏ qua?

Trả lời

4

Không bao giờ cố gắng họ nhưng:

Cả hai dường như để làm những gì bạn muốn (C , không phải Python).

+0

snacc đã đi theo radar cho tôi. Tôi sẽ kiểm tra nó. Cảm ơn! – elventear

+0

asn1c đã là lựa chọn tốt nhất cho đến nay. – elventear

2

Có một số ANTLR ASN.1 grammar; bằng cách sử dụng ANTLR, bạn sẽ có thể làm cho một trình phân tích cú pháp ASN.1 ra khỏi nó. Đang tạo mã cho pyasn1 là trái như một bài tập cho các poster :-)

+0

Trích dẫn từ liên kết: "Ngữ pháp là chưa hoàn chỉnh. Tôi không có kinh nghiệm trong ANTLR". – jfs

+0

liên kết chết ngay bây giờ! – monojohnny

1

Tôi đã thực hiện một công việc tương tự bằng cách sử dụng asn1c và xây dựng xung quanh nó một phần mở rộng Pyrex. Cấu trúc bọc được mô tả trong 3GPP TS 32.401.

Với Pyrex bạn có thể viết một trình bao bọc đủ dày để chuyển đổi giữa các kiểu dữ liệu Python gốc và biểu diễn ASN.1 chính xác (trình tạo bao bọc, SWIG, có xu hướng không thực hiện các thao tác phức tạp trên loại). Trình bao bọc mà tôi đã viết cũng theo dõi quyền sở hữu của các cấu trúc dữ liệu C bên dưới (ví dụ: truy cập vào một cấu trúc con, một đối tượng Python đã được trả về, nhưng không có bản sao của dữ liệu cơ bản, chỉ chia sẻ tham chiếu).

Trình bao bọc cuối cùng được viết theo cách bán tự động, nhưng vì đó là công việc duy nhất của tôi với ASN.1, tôi chưa bao giờ thực hiện bước tự động hóa hoàn toàn việc tạo mã.

Bạn có thể thử sử dụng trình bao bọc Python-C khác và thực hiện chuyển đổi hoàn toàn tự động: công việc sẽ ít hơn, nhưng sau đó bạn sẽ di chuyển các thao tác phức tạp (và lặp lại lỗi) tới người dùng cấu trúc. ưa thích cách Pyrex. asn1c chắc chắn là một lựa chọn tốt.

+0

Định nghĩa asn1 tôi có khá dài và có vẻ phức tạp. Mã mà asn1c tạo ra dường như có vấn đề với tệp này và ý tưởng là không phải gỡ lỗi một công cụ khác. – elventear

2

Tôi có kinh nghiệm với pyasn1 và đủ để phân tích cú pháp khá phức tạp. Một ngữ pháp được thể hiện với cấu trúc python, vì vậy không cần chạy trình tạo mã.

+1

Vấn đề với pyasn1 là các cấu trúc dữ liệu phải được viết thủ công. Điều này là ok cho các định nghĩa asn.1 nhỏ. Nhưng không phải cho những người lớn. – elventear

2

Tôi là tác giả của LEPL, một trình phân tích cú pháp được viết bằng Python và những gì bạn muốn làm là một trong những điều trong danh sách "TODO" của tôi.

tôi sẽ không được làm điều này sớm, nhưng bạn có thể xem xét sử dụng LEPL để xây dựng giải pháp của bạn bởi vì:

1 - đó là một giải pháp Python tinh khiết (mà làm cho cuộc sống đơn giản hơn)

2 - nó có thể đã phân tích cú pháp dữ liệu nhị phân cũng như văn bản, vì vậy bạn chỉ cần sử dụng một công cụ duy nhất - cùng một trình phân tích cú pháp mà bạn sẽ sử dụng để phân tích cú pháp của thông số nhị phân sau đó được sử dụng để phân tích cú pháp dữ liệu nhị phân

Nhược điểm chính là:

1 - đó là một gói khá mới, vì vậy nó có thể bị lỗi hơn một số, và cộng đồng hỗ trợ không phải là lớn

2 - nó bị giới hạn ở Python 2.6 trở lên (và trình phân tích cú pháp nhị phân chỉ hoạt động với Python 3 trở lên).

Để biết thêm thông tin, vui lòng xem http://www.acooke.org/lepl - đặc biệt là đối với phân tích nhị phân xem phần liên quan của hướng dẫn (tôi không thể liên kết trực tiếp đến đó như Stack Overflow dường như nghĩ rằng tôi đang gửi thư rác)

Andrew

PS Lý do chính này không phải là điều tôi đã bắt đầu là các thông số kỹ thuật ASN 1 không có sẵn miễn phí, theo như tôi biết. Nếu bạn có quyền truy cập vào chúng, và nó không phải là bất hợp pháp (!), Một bản sao sẽ được đánh giá rất nhiều (rất tiếc là tôi hiện đang làm việc cho một dự án khác, vì vậy điều này vẫn cần thời gian để thực hiện, nhưng nó sẽ giúp tôi làm việc này sớm hơn ...).

+0

LEPL trông chắc chắn thú vị, nhưng về phần ngắn hạn nhu cầu của tôi là khả năng tương thích với Python 2.4. – elventear

11

Tôi đã viết trình phân tích cú pháp như vậy vài năm trước. Nó tạo ra các lớp python cho thư viện pyasn1. Tôi đã sử dụng tài liệu ericsson để tạo trình phân tích cú pháp cho CDR của họ.

Tôi sẽ thử đăng mã tại đây ngay bây giờ.

import sys 
from pyparsing import * 

OpenBracket = Regex("[({]").suppress() 
CloseBracket = Regex("[)}]").suppress() 

def Enclose(val): 
    return OpenBracket + val + CloseBracket 

def SetDefType(typekw): 
    def f(a, b, c): 
    c["defType"] = typekw 
    return f 

def NoDashes(a, b, c): 
    return c[0].replace("-", "_") 

def DefineTypeDef(typekw, typename, typedef): 
    return typename.addParseAction(SetDefType(typekw)).setResultsName("definitionType") - \ 
    Optional(Enclose(typedef).setResultsName("definition")) 



SizeConstraintBodyOpt = Word(nums).setResultsName("minSize") - \ 
    Optional(Suppress(Literal("..")) - Word(nums + "n").setResultsName("maxSize")) 

SizeConstraint = Group(Keyword("SIZE").suppress() - Enclose(SizeConstraintBodyOpt)).setResultsName("sizeConstraint") 

Constraints = Group(delimitedList(SizeConstraint)).setResultsName("constraints") 

DefinitionBody = Forward() 

TagPrefix = Enclose(Word(nums).setResultsName("tagID")) - Keyword("IMPLICIT").setResultsName("tagFormat") 

OptionalSuffix = Optional(Keyword("OPTIONAL").setResultsName("isOptional")) 
JunkPrefix = Optional("--F--").suppress() 
AName = Word(alphanums + "-").setParseAction(NoDashes).setResultsName("name") 

SingleElement = Group(JunkPrefix - AName - Optional(TagPrefix) - DefinitionBody.setResultsName("typedef") - OptionalSuffix) 

NamedTypes = Dict(delimitedList(SingleElement)).setResultsName("namedTypes") 

SetBody = DefineTypeDef("Set", Keyword("SET"), NamedTypes) 
SequenceBody = DefineTypeDef("Sequence", Keyword("SEQUENCE"), NamedTypes) 
ChoiceBody = DefineTypeDef("Choice", Keyword("CHOICE"), NamedTypes) 

SetOfBody = (Keyword("SET") + Optional(SizeConstraint) + Keyword("OF")).setParseAction(SetDefType("SetOf")) + Group(DefinitionBody).setResultsName("typedef") 
SequenceOfBody = (Keyword("SEQUENCE") + Optional(SizeConstraint) + Keyword("OF")).setParseAction(SetDefType("SequenceOf")) + Group(DefinitionBody).setResultsName("typedef") 

CustomBody = DefineTypeDef("constructed", Word(alphanums + "-").setParseAction(NoDashes), Constraints) 
NullBody = DefineTypeDef("Null", Keyword("NULL"), Constraints) 

OctetStringBody = DefineTypeDef("OctetString", Regex("OCTET STRING"), Constraints) 
IA5StringBody = DefineTypeDef("IA5String", Keyword("IA5STRING"), Constraints) 

EnumElement = Group(Word(printables).setResultsName("name") - Enclose(Word(nums).setResultsName("value"))) 
NamedValues = Dict(delimitedList(EnumElement)).setResultsName("namedValues") 
EnumBody = DefineTypeDef("Enum", Keyword("ENUMERATED"), NamedValues) 

BitStringBody = DefineTypeDef("BitString", Keyword("BIT") + Keyword("STRING"), NamedValues) 

DefinitionBody << (OctetStringBody | SetOfBody | SetBody | ChoiceBody | SequenceOfBody | SequenceBody | EnumBody | BitStringBody | IA5StringBody | NullBody | CustomBody) 

Definition = AName - Literal("::=").suppress() - Optional(TagPrefix) - DefinitionBody 

Definitions = Dict(ZeroOrMore(Group(Definition))) 

pf = Definitions.parseFile(sys.argv[1]) 

TypeDeps = {} 
TypeDefs = {} 

def SizeConstraintHelper(size): 
    s2 = s1 = size.get("minSize") 
    s2 = size.get("maxSize", s2) 
    try: 
    return("constraint.ValueSizeConstraint(%s, %s)" % (int(s1), int(s2))) 
    except ValueError: 
    pass 

ConstraintMap = { 
    'sizeConstraint' : SizeConstraintHelper, 
} 

def ConstraintHelper(c): 
    result = [] 
    for key, value in c.items(): 
    r = ConstraintMap[key](value) 
    if r: 
     result.append(r) 
    return result 

def GenerateConstraints(c, ancestor, element, level=1): 
    result = ConstraintHelper(c) 
    if result: 
    return [ "subtypeSpec = %s" % " + ".join(["%s.subtypeSpec" % ancestor] + result) ] 
    return [] 

def GenerateNamedValues(definitions, ancestor, element, level=1): 
    result = [ "namedValues = namedval.NamedValues(" ] 
    for kw in definitions: 
    result.append(" ('%s', %s)," % (kw["name"], kw["value"])) 
    result.append(")") 
    return result 

OptMap = { 
    False: "", 
    True: "Optional", 
} 

def GenerateNamedTypesList(definitions, element, level=1): 
    result = [] 
    for val in definitions: 
    name = val["name"] 
    typename = None 

    isOptional = bool(val.get("isOptional")) 

    subtype = [] 
    constraints = val.get("constraints") 
    if constraints: 
     cg = ConstraintHelper(constraints) 
     subtype.append("subtypeSpec=%s" % " + ".join(cg)) 
    tagId = val.get("tagID") 
    if tagId: 
     subtype.append("implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, %s)" % tagId) 

    if subtype: 
     subtype = ".subtype(%s)" % ", ".join(subtype) 
    else: 
     subtype = "" 

    cbody = [] 
    if val["defType"] == "constructed": 
     typename = val["typedef"] 
     element["_d"].append(typename) 
    elif val["defType"] == "Null": 
     typename = "univ.Null" 
    elif val["defType"] == "SequenceOf": 
     typename = "univ.SequenceOf" 
     print val.items() 
     cbody = [ " componentType=%s()" % val["typedef"]["definitionType"] ] 
    elif val["defType"] == "Choice": 
     typename = "univ.Choice" 
     indef = val.get("definition") 
     if indef: 
     cbody = [ " %s" % x for x in GenerateClassDefinition(indef, name, typename, element) ] 
    construct = [ "namedtype.%sNamedType('%s', %s(" % (OptMap[isOptional], name, typename), ")%s)," % subtype ] 
    if not cbody: 
     result.append("%s%s%s" % (" " * level, construct[0], construct[1])) 
    else: 
     result.append(" %s" % construct[0]) 
     result.extend(cbody) 
     result.append(" %s" % construct[1]) 
    return result 



def GenerateNamedTypes(definitions, ancestor, element, level=1): 
    result = [ "componentType = namedtype.NamedTypes(" ] 
    result.extend(GenerateNamedTypesList(definitions, element)) 
    result.append(")") 
    return result 


defmap = { 
    'constraints' : GenerateConstraints, 
    'namedValues' : GenerateNamedValues, 
    'namedTypes' : GenerateNamedTypes, 
} 

def GenerateClassDefinition(definition, name, ancestor, element, level=1): 
    result = [] 
    for defkey, defval in definition.items(): 
    if defval: 
     fn = defmap.get(defkey) 
     if fn: 
     result.extend(fn(defval, ancestor, element, level)) 
    return [" %s" % x for x in result] 

def GenerateClass(element, ancestor): 
    name = element["name"] 

    top = "class %s(%s):" % (name, ancestor) 
    definition = element.get("definition") 
    body = [] 
    if definition: 
    body = GenerateClassDefinition(definition, name, ancestor, element) 
    else: 
    typedef = element.get("typedef") 
    if typedef: 
     element["_d"].append(typedef["definitionType"]) 
     body.append(" componentType = %s()" % typedef["definitionType"]) 
     szc = element.get('sizeConstraint') 
     if szc: 
     body.extend(GenerateConstraints({ 'sizeConstraint' : szc }, ancestor, element)) 

    if not body: 
    body.append(" pass") 

    TypeDeps[name] = list(frozenset(element["_d"])) 

    return "\n".join([top] + body) 

StaticMap = { 
    "Null" : "univ.Null", 
    "Enum" : "univ.Enumerated", 
    "OctetString" : "univ.OctetString", 
    "IA5String" : "char.IA5String", 
    "Set" : "univ.Set", 
    "Sequence" : "univ.Sequence", 
    "Choice" : "univ.Choice", 
    "SetOf" : "univ.SetOf", 
    "BitString" : "univ.BitString", 
    "SequenceOf" : "univ.SequenceOf", 
} 

def StaticConstructor(x): 
    x["_d"] = [] 
    if x["defType"] == "constructed": 
    dt = x["definitionType"] 
    x["_d"].append(dt) 
    else: 
    dt = StaticMap[x["defType"]] 
    return GenerateClass(x, dt) 


for element in pf: 
    TypeDefs[element["name"]] = StaticConstructor(element) 

while TypeDefs: 
    ready = [ k for k, v in TypeDeps.items() if len(v) == 0 ] 
    if not ready: 
    x = list() 
    for a in TypeDeps.values(): 
     x.extend(a) 
    x = frozenset(x) - frozenset(TypeDeps.keys()) 

    print TypeDefs 

    raise ValueError, sorted(x) 

    for t in ready: 
    for v in TypeDeps.values(): 
     try: 
     v.remove(t) 
     except ValueError: 
     pass 

    del TypeDeps[t] 
    print TypeDefs[t] 
    print 
    print 

    del TypeDefs[t] 

này sẽ mất một tập tin với cú pháp, tương tự như thế này:

CarrierInfo ::= OCTET STRING (SIZE(2..3)) 
ChargeAreaCode ::= OCTET STRING (SIZE(3)) 
ChargeInformation ::= OCTET STRING (SIZE(2..33)) 
ChargedParty ::= ENUMERATED 

(chargingOfCallingSubscriber (0), 
    chargingOfCalledSubscriber (1), 
    noCharging     (2)) 
ChargingOrigin ::= OCTET STRING (SIZE(1)) 
Counter ::= OCTET STRING (SIZE(1..4)) 
Date ::= OCTET STRING (SIZE(3..4)) 

Bạn sẽ cần phải thêm dòng này trên đầu trang của các tập tin được tạo ra:

from pyasn1.type import univ, namedtype, namedval, constraint, tag, char 

Và tên kết quả defs.py. Sau đó, tôi gắn một loạt các prettyprinters đến defs (nếu bạn không phải chỉ cần bỏ qua nó)

import defs, parsers 

def rplPrettyOut(self, value): 
    return repr(self.decval(value)) 

for name in dir(parsers): 
    if (not name.startswith("_")) and hasattr(defs, name): 
    target = getattr(defs, name) 
    target.prettyOut = rplPrettyOut 
    target.decval = getattr(parsers, name) 

Sau đó, nó xuống:

def ParseBlock(self, block): 
    while block and block[0] != '\x00': 
     result, block = pyasn1.codec.ber.decoder.decode(block, asn1Spec=parserimp.defs.CallDataRecord()) 
     yield result 

Nếu bạn vẫn quan tâm đến tôi sẽ đặt mã ở đâu đó. Trong thực tế, tôi sẽ đặt nó ở đâu đó trong mọi trường hợp - nhưng nếu bạn quan tâm chỉ cho tôi biết và tôi sẽ chỉ cho bạn ở đó.

+4

Tôi có thể quan tâm đến việc sử dụng điều này trong một dự án nguồn mở (http://packages.python.org/DendroPy/). Đây có phải là miền công cộng hoặc có sẵn theo giấy phép nguồn mở tương đối thoải mái như BSD, MIT, v.v. không? – Jeet

0

Gần đây tôi đã tạo gói Python có tên là asn1tools, biên dịch một đặc tả ASN.1 thành các đối tượng Python, có thể được sử dụng để mã hóa và giải mã các thông báo.

>>> import asn1tools 
>>> foo = asn1tools.compile_file('tests/files/foo.asn') 
>>> encoded = foo.encode('Question', {'id': 1, 'question': 'Is 1+1=3?'}) 
>>> encoded 
bytearray(b'0\x0e\x02\x01\x01\x16\x09Is 1+1=3?') 
>>> foo.decode('Question', encoded) 
{'id': 1, 'question': 'Is 1+1=3?'} 
Các vấn đề liên quan