Remote Administration Daemon Developer Guide

Exit Print View

Updated: July 2014
 
 

Validating adr_data_t Values

libadr provides a rich environment for examining and manipulating typed data. However, unlike C's native typing system, the compiler is unaware of libadr type relationships and is therefore unable to perform static type-checking at compile time. All type checking must be performed at runtime.

The most useful of the type-checking tools provided by libadr is adr_data_verify:

boolean_t adr_data_verify(adr_data_t *data, adr_type_t *type, boolean_t recursive);
 

adr_data_verify takes an adr_data_t to type-check and an adr_type_t to type-check against. It can be instructed to check only the adr_data_t data or data and the transitive closure of every adr_data_t it references. adr_data_verify returns B_TRUE if data matches type, and B_FALSE if not. If type is NULL, data is tested against the type it claims to be. Although this method is not a good idea for input validation, it can be useful for error handling.

In order for data to be verified as type type, the following must be true:

  • Data must not be NULL.

  • Data must claim to be of type type.

  • If type is an enumeration, data must be a value in that enumeration.

  • If data is an array, it must be not have been marked invalid by a failed adr_array_add or adr_array_vset operation.

  • If data is an array, it must have no NULL elements.

  • If data is an array and recursive is true, each element of the array must satisfy these criteria given the array's element type.

  • If data is a structure, every non-nullable field must have a value, that is, be non-NULL.

  • If data is a structure and recursive is true, every non-NULL field value must satisfy these criteria considering the field's type.

The adr_data_verify is useful when validating input from an untrusted source. A second, less obvious application of adr_data_verify is as a powerful error-handling tool. Suppose you are writing a function that needs to return a complex data value. A traditional way of implementing it would be to check each call for failure individually, as shown in the following example.

Example 5-1  Traditional Error Handling
adr_data_t *tmp, *name, *result;
if ((name = adr_data_new_struct(name_type)) == NULL) {
/* handle failure */
}
if ((tmp = adr_data_new_string("Jack")) == NULL) {
/* handle failure */
}
adr_struct_set(name, "first", tmp);
if ((tmp = adr_data_new_string("O'Neill")) == NULL) {
/* handle failure */
}
adr_struct_set(name, "last", tmp);
if ((record = adr_data_new_struct(record_type)) == NULL) {
/* handle failure */
}
adr_struct_set(record, "name", name);
/* ...and so on */

This approach is difficult to implement and difficult to maintain. It is more likely to have a flaw in it than the allocations it is testing are to fail. Instead, using adr_data_verify and the error handling behaviors described in adr_data_t Type, the entire non-truncated function can be reduced to the method shown in the following example.

Example 5-2  Error Handling With adr_data_verify
adr_data_t *name = adr_data_new_struct(name_type);
adr_struct_set(name, "first", adr_data_new_string("Jack"));
adr_struct_set(name, "last", adr_data_new_string("O'Neill"));
adr_data_t *record = adr_data_new_struct(record_type);
adr_struct_set(record, "name", name);
adr_struct_set(record, "rank", adr_data_new_enum_byname("COLONEL"));
adr_struct_set(record, "l_count", adr_data_new_integer(2));

if (!adr_data_verify(record, NULL, B_TRUE)) { /* Recursive type check */
			adr_data_free(record);
			return (NULL); /* NULL means something failed */
}

return (record); /* Non-NULL means success */

An important limitation to this technique is the possibility for structure fields to be nullable, and the NULL indicating that the field has no value is indistinguishable from the NULL that indicates that the allocation of that field's value failed. In such cases, explicitly testing each nullable value's allocation is necessary. Even with such explicit checks, however, the net savings in complexity can be substantial.