The Z39.50 ASN.1 ModuleIntroduction
The &asn; module provides you with a set of C struct definitions for the
various PDUs of the Z39.50 protocol, as well as for the complex types
appearing within the PDUs. For the primitive data types, the C
representation often takes the form of an ordinary C language type,
such as int. For ASN.1 constructs that have no direct
representation in C, such as general octet strings and bit strings,
the &odr; module (see section The ODR Module)
provides auxiliary definitions.
The &asn; module is located in sub directory z39.50.
There you'll find C files that implements encoders and decoders for the
Z39.50 types. You'll also find the protocol definitions:
z3950v3.asn, esupdate.asn,
and others.
Preparing PDUs
A structure representing a complex ASN.1 type doesn't in itself contain the
members of that type. Instead, the structure contains
pointers to the members of the type.
This is necessary, in part, to allow a mechanism for specifying which
of the optional structure (SEQUENCE) members are present, and which
are not. It follows that you will need to somehow provide space for
the individual members of the structure, and set the pointers to
refer to the members.
The conversion routines don't care how you allocate and maintain your
C structures - they just follow the pointers that you provide.
Depending on the complexity of your application, and your personal
taste, there are at least three different approaches that you may take
when you allocate the structures.
You can use static or automatic local variables in the function that
prepares the PDU. This is a simple approach, and it provides the most
efficient form of memory management. While it works well for flat
PDUs like the InitReqest, it will generally not be sufficient for say,
the generation of an arbitrarily complex RPN query structure.
You can individually create the structure and its members using the
malloc(2) function. If you want to ensure that
the data is freed when it is no longer needed, you will have to
define a function that individually releases each member of a
structure before freeing the structure itself.
You can use the odr_malloc() function (see
for details). When you use
odr_malloc(), you can release all of the
allocated data in a single operation, independent of any pointers and
relations between the data. odr_malloc() is based on a
"nibble-memory"
scheme, in which large portions of memory are allocated, and then
gradually handed out with each call to odr_malloc().
The next time you call odr_reset(), all of the
memory allocated since the last call is recycled for future use (actually,
it is placed on a free-list).
You can combine all of the methods described here. This will often be
the most practical approach. For instance, you might use
odr_malloc() to allocate an entire structure and
some of its elements, while you leave other elements pointing to global
or per-session default variables.
The &asn; module provides an important aid in creating new PDUs. For
each of the PDU types (say, Z_InitRequest), a
function is provided that allocates and initializes an instance of
that PDU type for you. In the case of the InitRequest, the function is
simply named zget_InitRequest(), and it sets up
reasonable default value for all of the mandatory members. The optional
members are generally initialized to null pointers. This last aspect
is very important: it ensures that if the PDU definitions are
extended after you finish your implementation (to accommodate
new versions of the protocol, say), you won't get into trouble with
uninitialized pointers in your structures. The functions use
odr_malloc() to
allocate the PDUs and its members, so you can free everything again with a
single call to odr_reset(). We strongly recommend
that you use the zget_*
functions whenever you are preparing a PDU (in a C++ API, the
zget_
functions would probably be promoted to constructors for the
individual types).
The prototype for the individual PDU types generally look like this:
Z_<type> *zget_<type>(ODR o);
eg.:
Z_InitRequest *zget_InitRequest(ODR o);
The &odr; handle should generally be your encoding stream, but it
needn't be.
As well as the individual PDU functions, a function
zget_APDU() is provided, which allocates
a top-level Z-APDU of the type requested:
Z_APDU *zget_APDU(ODR o, int which);
The which parameter is (of course) the discriminator
belonging to the Z_APDUCHOICE type.
All of the interface described here is provided by the &asn; module, and
you access it through the proto.h header file.
EXTERNAL Data
In order to achieve extensibility and adaptability to different
application domains, the new version of the protocol defines many
structures outside of the main ASN.1 specification, referencing them
through ASN.1 EXTERNAL constructs. To simplify the construction and
access to the externally referenced data, the &asn; module defines a
specialized version of the EXTERNAL construct, called
Z_External.It is defined thus:
typedef struct Z_External
{
Odr_oid *direct_reference;
int *indirect_reference;
char *descriptor;
enum
{
/* Generic types */
Z_External_single = 0,
Z_External_octet,
Z_External_arbitrary,
/* Specific types */
Z_External_SUTRS,
Z_External_explainRecord,
Z_External_resourceReport1,
Z_External_resourceReport2
...
} which;
union
{
/* Generic types */
Odr_any *single_ASN1_type;
Odr_oct *octet_aligned;
Odr_bitmask *arbitrary;
/* Specific types */
Z_SUTRS *sutrs;
Z_ExplainRecord *explainRecord;
Z_ResourceReport1 *resourceReport1;
Z_ResourceReport2 *resourceReport2;
...
} u;
} Z_External;
When decoding, the &asn; module will attempt to determine which
syntax describes the data by looking at the reference fields
(currently only the direct-reference). For ASN.1 structured data, you
need only consult the which field to determine the
type of data. You can the access the data directly through the union.
When constructing data for encoding, you set the union pointer to point
to the data, and set the which field accordingly.
Remember also to set the direct (or indirect) reference to the correct
OID for the data type.
For non-ASN.1 data such as MARC records, use the
octet_aligned arm of the union.
Some servers return ASN.1 structured data values (eg. database
records) as BER-encoded records placed in the
octet-aligned branch of the EXTERNAL CHOICE.
The ASN-module will not automatically decode
these records. To help you decode the records in the application, the
function
Z_ext_typeent *z_ext_gettypebyref(const oid *oid);
Can be used to retrieve information about the known, external data
types. The function return a pointer to a static area, or NULL, if no
match for the given direct reference is found. The
Z_ext_typeent
is defined as:
typedef struct Z_ext_typeent
{
int oid[OID_SIZE]; /* the direct-reference OID. */
int what; /* discriminator value for the external CHOICE */
Odr_fun fun; /* decoder function */
} Z_ext_typeent;
The what member contains the
Z_External union discriminator value for the
given type: For the SUTRS record syntax, the value would be
Z_External_sutrs.
The fun member contains a pointer to the
function which encodes/decodes the given type. Again, for the SUTRS
record syntax, the value of fun would be
z_SUTRS (a function pointer).
If you receive an EXTERNAL which contains an octet-string value that
you suspect of being an ASN.1-structured data value, you can use
z_ext_gettypebyref to look for the provided
direct-reference.
If the return value is different from NULL, you can use the provided
function to decode the BER string (see
).
If you want to send EXTERNALs containing
ASN.1-structured values in the occtet-aligned branch of the CHOICE, this
is possible too. However, on the encoding phase, it requires a somewhat
involved juggling around of the various buffers involved.
If you need to add new, externally defined data types, you must update
the struct above, in the source file prt-ext.h, as
well as the encoder/decoder in the file prt-ext.c.
When changing the latter, remember to update both the
arm arrary and the list
type_table, which drives the CHOICE biasing that
is necessary to tell the different, structured types apart
on decoding.
Eventually, the EXTERNAL processing will most likely
automatically insert the correct OIDs or indirect-refs. First,
however, we need to determine how application-context management
(specifically the presentation-context-list) should fit into the
various modules.
PDU Contents Table
We include, for reference, a listing of the fields of each top-level
PDU, as well as their default settings.
Default settings for PDU Initialize RequestFieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
protocolVersionOdr_bitmaskEmpty bitmask
optionsOdr_bitmaskEmpty bitmask
preferredMessageSizeint30*1024
maximumRecordSizeint30*1024
idAuthenticationZ_IdAuthenticationNULL
implementationIdchar*"81"
implementationNamechar*"YAZ"
implementationVersionchar*YAZ_VERSION
userInformationFieldZ_UserInformationNULL
otherInfoZ_OtherInformationNULL
Default settings for PDU Initialize
ResponseFieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
protocolVersionOdr_bitmaskEmpty bitmask
optionsOdr_bitmaskEmpty bitmask
preferredMessageSizeint30*1024
maximumRecordSizeint30*1024
resultbool_tTRUE
implementationIdchar*"id)"
implementationNamechar*"YAZ"
implementationVersionchar*YAZ_VERSION
userInformationFieldZ_UserInformationNULL
otherInfoZ_OtherInformationNULL
Default settings for PDU Search ResponseFieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
resultCountint0
numberOfRecordsReturnedint0
nextResultSetPositionint0
searchStatusbool_tTRUE
resultSetStatusintNULL
presentStatusintNULL
recordsZ_RecordsNULL
additionalSearchInfoZ_OtherInformationNULL
otherInfoZ_OtherInformationNULL
Default settings for PDU Present RequestFieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
resultSetIdchar*"default"
resultSetStartPointint1
numberOfRecordsRequestedint10
num_rangesint0
additionalRangesZ_RangeNULL
recordCompositionZ_RecordCompositionNULL
preferredRecordSyntaxOdr_oidNULL
maxSegmentCountintNULL
maxRecordSizeintNULL
maxSegmentSizeintNULL
otherInfoZ_OtherInformationNULL
Default settings for PDU Present ResponseFieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
numberOfRecordsReturnedint0
nextResultSetPositionint0
presentStatusintZ_PresentStatus_success
recordsZ_RecordsNULL
otherInfoZ_OtherInformationNULL
Default settings for Delete Result Set Request
FieldTypeDefault ValuereferenceId
Z_ReferenceIdNULL
deleteFunctionintZ_DeleteResultSetRequest_list
num_idsint0
resultSetListchar**NULL
otherInfoZ_OtherInformationNULL
Default settings for Delete Result Set Response
FieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
deleteOperationStatusintZ_DeleteStatus_success
num_statusesint0
deleteListStatusesZ_ListStatus**NULL
numberNotDeletedintNULL
num_bulkStatusesint0
bulkStatusesZ_ListStatusNUL
L
deleteMessagechar*NULL
otherInfoZ_OtherInformationNULL
Default settings for Scan Request
FieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
num_databaseNamesint0
databaseNameschar**NULL
attributeSetOdr_oidNULL
termListAndStartPointZ_AttributesPlus...
NULL
stepSizeintNULL
numberOfTermsRequestedint20
preferredPositionInResponseintNULL
otherInfoZ_OtherInformationNULL
Default settings for Scan Response
FieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
stepSizeintNULL
scanStatusintZ_Scan_success
numberOfEntriesReturnedint0
positionOfTermintNULL
entriesZ_ListEntrisNULL
attributeSetOdr_oidNULL
otherInfoZ_OtherInformationNULL
Default settings for Trigger Resource Control Request FieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
requestedActionint
Z_TriggerResourceCtrl_resou..
prefResourceReportFormatOdr_oidNULL
resultSetWantedbool_tNULL
otherInfoZ_OtherInformationNULL
Default settings for Resource Control RequestFieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
suspendedFlagbool_tNULL
resourceReportZ_ExternalNULL
partialResultsAvailableintNULL
responseRequiredbool_tFALSE
triggeredRequestFlagbool_tNULL
otherInfoZ_OtherInformationNULL
Default settings for Resource Control ResponseFieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
continueFlagbool_tTRUE
resultSetWantedbool_tNULL
otherInfoZ_OtherInformationNULL
Default settings for Access Control RequestFieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
whichenumZ_AccessRequest_simpleForm;
uunionNULL
otherInfoZ_OtherInformationNULL
Default settings for Access Control ResponseFieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
whichenumZ_AccessResponse_simpleForm
uunionNULL
diagnosticZ_DiagRecNULL
otherInfoZ_OtherInformationNULL
Default settings for SegmentFieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
numberOfRecordsReturnedintvalue=0
num_segmentRecordsint0
segmentRecordsZ_NamePlusRecordNULL
otherInfoZ_OtherInformationNULL
Default settings for CloseFieldTypeDefault Value
referenceIdZ_ReferenceIdNULL
closeReasonintZ_Close_finished
diagnosticInformationchar*NULL
resourceReportFormatOdr_oidNULL
resourceFormatZ_ExternalNULL
otherInfoZ_OtherInformationNULL