ROSE stands for "Remote Operations Service Element" and defines a request/response transaction protocol in which requests to a conforming entity must be answered with the result or errors defined in operation definitions. Variations of this are used in a number of protocols in use today including CSTA and TCAP.
The definition of the ROSE OPERATION MACRO that is built into the ASN1C is as follows:
OPERATION MACRO ::= BEGIN TYPE NOTATION ::= Parameter Result Errors LinkedOperations VALUE NOTATION ::= value (VALUE INTEGER) Parameter ::= ArgKeyword NamedType | empty ArgKeyword ::= "ARGUMENT" | "PARAMETER" Result ::= "RESULT" ResultType | empty Errors ::= "ERRORS" "{"ErrorNames"}" | empty LinkedOperations ::= "LINKED" "{"LinkedOperationNames"}" | empty ResultType ::= NamedType | empty ErrorNames ::= ErrorList | empty ErrorList ::= Error | ErrorList "," Error Error ::= value(ERROR) -- shall reference an error value | type -- shall reference an error type -- if no value is specified LinkedOperationNames ::= OperationList | empty OperationList ::= Operation | OperationList "," Operation Operation ::= value(OPERATION) -- shall reference an op value | type -- shall reference an op type -- if no value is specified NamedType ::= identifier type | type END
This MACRO does not need to be defined in the ASN.1 specification to be parsed. In fact, any attempt to redefine this MACRO will be ignored. Its definition is hard-coded into the compiler.
The compiler uses this definition to parse types and values out of OPERATION definitions. An example of an OPERATION definition is as follows:
login OPERATION ARGUMENT SEQUENCE { username IA5String, password IA5String } RESULT SEQUENCE { ticket OCTET STRING, welcomeMessage IA5String } ERRORS { authenticationFailure, insufficientResources } ::= 1
In this case, there are two embedded types (an ARGUMENT type and a RESULT type) and an integer value (1) that identifies the OPERATION. There are also error definitions.
The ASN1C compiler generates two types of items for the OPERATION:
It extracts the type definitions from within the OPERATION definitions and generates equivalent C/C++ structures and encoders/decoders, and
It generates value constants for the value associated with the OPERATION (i.e., the value to the right of the '::=' in the definition).
The compiler does not generate any structures or code related to the OPERATION itself (for example, code to encode the body and header in a single step). The reason is because of the multi-layered nature of the protocol. It is assumed that the user of such a protocol would be most interested in doing the processing in multiple stages, hence no single function or structure is generated.
Therefore, to encode the login example the user would do the following:
At the application layer, the Login_ARGUMENT structure would be populated with the username and password to be encoded.
The encode function for Login_ARGUMENT would be called and the resulting message pointer and length would be passed down to the next layer (the ROSE layer).
At the ROSE layer, the Invoke structure would be populated with the OPERATION value, invoke identifier, and other header parameters. The parameter.numocts value would be populated with the length of the message passed in from step 2. The parameter.data field would be populated with the message pointer passed in from step 2.
The encode function for Invoke would be called resulting in a fully encoded ROSE Invoke message ready for transfer across the communications link.
The following is a picture showing this process:
On the decode side, the process would be reversed with the message flowing up the stack:
At the ROSE layer, the header would be decoded producing information on the OPERATION type (based on the MACRO definition) and message type (Invoke, Result, etc..). The invoke identifier would also be available for use in session management. In our example, we would know at this point that we got a login invoke request.
Based on the information from step 1, the ROSE layer would know that the Open Type field contains a pointer and length to an encoded Login_ARGUMENT component. It would then route this information to the appropriate processor within the Application Layer for handling this type of message.
The Application Layer would call the specific decoder associated with the Login_ARGUMENT. It would then have available to it the username/password the user is logging in with. It could then do whatever applicationspecific processing is required with this information (database lookup, etc.).
Finally, the Application Layer would begin the encoding process again in order to send back a Result or Error message to the Login Request.
A picture showing this is as follows:
The login OPERATION also contains references to ERROR definitions. These are defined using a separate MACRO that is built into the compiler. The definition of this MACRO is as follows:
ERROR MACRO ::= BEGIN TYPE NOTATION ::= Parameter VALUE NOTATION ::= value (VALUE INTEGER) Parameter ::= "PARAMETER" NamedType | empty NamedType ::= identifier type | type END
In this definition, an error is assigned an identifying number as
well as on optional parameter type to hold parameters associated with
the error. An example of a reference to this MACRO for the
authenticationFailure
error in the login operation
defined earlier would be as
follows:
applicationError ERROR PARAMETER SEQUENCE { errorText IA5String } } ::= 1
The ASN1C compiler will generate a type definition for the error parameter and a value constant for the error value. The format of the name of the type generated will be "<name>_PARAMETER" where <name> is the ERROR name (applicationError in this case) with the first letter set to uppercase. The name of the value will simply be the ERROR name.