legacy/libisoburn/trunk/test/aaip.c

538 lines
17 KiB
C

/*
Arbitrary Attribute Interchange Protocol , AAIP
Demonstration program for encoding and decoding types PAIR and ACL1.
See http://libburnia-project.org/wiki/AAIP
Compile: cc -g -Wall -o test/aaip test/aaip.c
Usage: ./aaip name value
Long parameters ./aaip -name"x100" -value"x100"
*/
#include <ctype.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/* <<< */
#define Aaip_encode_debuG 1
/* --------------------------------- Encoder ---------------------------- */
static unsigned int aaip_encode_parameter(char aa_name[2],
char *field_start, unsigned int field_fill,
unsigned int num_payload, char *payload,
int flag);
/* Convert the given type and parameters into a series of AAIP fields.
@param aa_name The 2 byte SUSP Signature Word of the fields
(recommended is "AA")
@param type The 4 byte Attribute Type ("ATTR" or "ACL1" for now)
@param num_params Number of attribute parameters (>= 0)
@param param_lengths Array of byte lengths for each parameter
@param params Array of pointers to the parameter bytes
@param result_len Number of bytes in the resulting SUSP field string
@param result *result will point to the start of the result string.
This is malloc() memory which needs to be freed when
no longer needed
@param flag Unused yet. Submit 0
@return >0 is the number of SUSP fields generated,
<= 0 means error
*/
int aaip_encode(char aa_name[2], char type[4], unsigned int num_params,
unsigned int *param_lengths, char **params,
size_t *result_len, char **result, int flag)
{
size_t mem_size= 0;
char *wpt, *field_start;
unsigned char *upt;
int ret;
unsigned int fl= 0, follow_fields, i, payload= 0;
unsigned int number_of_fields;
*result_len= 0;
*result= NULL;
/* Predict memory needs and number of SUSP fields */
mem_size= 16; /* First field up to NUM_FIELDS */
number_of_fields= 1;
for(i= 0; i < num_params; i++) {
if(i == 0) {
if(i > 239)
fl= param_lengths[i] - 239; /* what exceeds the first field */
else
fl= 0;
} else
fl= param_lengths[i];
follow_fields= (fl / 251) + ((fl % 251) > 0);
number_of_fields+= follow_fields;
mem_size+= param_lengths[i] + (follow_fields ) * 5;
}
#ifdef Aaip_encode_debuG
*result= calloc(1, mem_size + 10240); /* generous honeypot for overflows */
#else
*result= calloc(1, mem_size);
#endif
if(*result == NULL)
return(-1);
wpt= *result;
/* ----- First SUSP field ----- */
field_start= wpt;
*(wpt++)= aa_name[0];
*(wpt++)= aa_name[1];
/* number of bytes in first field */
if(num_params > 0) {
if(param_lengths[0] > 239)
payload= 239;
else
payload= param_lengths[0];
*((unsigned *) (wpt++))= payload + 16;
} else
*(wpt++)= 16;
*(wpt++)= 1;
/* Attribute type */
memcpy(wpt, type, 4);
wpt+= 4;
/* Number of fields LSB and MSB */
upt= (unsigned char *) wpt;
for(i= 0; i < 4; i++)
upt[7 - i]= upt[i]= (number_of_fields >> (8 * i)) & 0xff;
wpt+= 8;
/* ----- Parameters ----- */
/* Eventual first parameter */
if(num_params > 0) {
ret= aaip_encode_parameter(aa_name, field_start,
(unsigned int) (wpt - field_start),
param_lengths[0], params[0], 0);
wpt+= ret;
}
/* Further parameters */
for(i= 1; i < num_params; i++) {
ret= aaip_encode_parameter(aa_name, wpt, 0, param_lengths[i], params[i], 0);
wpt+= ret;
}
*result_len= wpt - *result;
#ifdef Aaip_encode_debuG
if(*result_len != mem_size) {
fprintf(stderr, "aaip_encode(): MEMORY MISMATCH BY %d BYTES\n",
(int) (mem_size - *result_len));
}
ret= 0;
for(i= 0; i < *result_len; i+= ((unsigned char *) (*result))[i + 2])
ret++;
if(ret != number_of_fields) {
fprintf(stderr, "aaip_encode(): WRONG NUMBER OF FIELDS %d <> %d\n",
number_of_fields, ret);
}
#endif /* Aaip_encode_debuG */
return(number_of_fields);
}
/* >>> Encoder for ACL */
/* Helper function:
Write the parameter into an eventually partially written field and into
eventually necessary further fields.
@param aa_name The 2 byte SUSP Signature Word of the fields
@param field_start Points to the start of the current field
@param field_fill Number of bytes already written in field (0 or >5)
@param num_payload Number of parameter bytes
@param payload Points to parameter bytes
@param flag bit0= do not write further fields
bit1= with new field set FOLLOW bit 1
@return Number of bytes written (payload and overhead)
*/
static unsigned int aaip_encode_parameter(char aa_name[2],
char *field_start, unsigned int field_fill,
unsigned int num_payload, char *payload,
int flag)
{
int ret;
unsigned int num_head= 0, num_pay= 0;
char *wpt, *rpt;
if(field_fill < 5) {
field_start[0]= aa_name[0];
field_start[1]= aa_name[1];
field_start[2]= 5;
field_start[3]= 1;
field_start[4]= !!(flag & 2);
num_head= field_fill= 5;
}
if(num_payload <= 0)
return(num_head);
if(num_payload + field_fill > 255)
num_pay= 255 - field_fill;
else
num_pay= num_payload;
memcpy(field_start + field_fill, payload, num_pay);
/* Update field length */
((unsigned char *) field_start)[2]= field_fill + num_pay;
if(!(flag & 1)) {
/* Write further fields */
wpt= field_start + field_fill + num_pay;
rpt= payload + num_pay;
while(num_pay < num_payload) {
ret= aaip_encode_parameter(aa_name, wpt, 0, num_payload - num_pay, rpt,
3);
wpt+= ret;
num_head+= 5;
rpt+= ret - 5;
num_pay+= ret - 5;
}
}
return(num_head + num_pay);
}
/* --------------------------------- Decoder ---------------------------- */
/* A size limit for single parameters in order to prevent resource ehaustion.
*/
#define Aaip_param_memory_limiT 1024*1024
static int aaip_decode_parameter(char *start_field, unsigned int num_fields,
char **new_pos, unsigned int *field_idx,
size_t *result_len, char **result, int flag);
/* Checks whether an encoded attribute is complete yet, or how many more fields
need to be added, or whether the encoded bytes represent no valid attribute.
This function assumes to get fed with complete SUSP fields, though.
It also computes a pointer to the first byte after the fields of the
attribute if it is valid at all.
@param aa_name The 2 byte SUSP Signature Word of the fields as of
the ER field with signature "AAIP_2008A"
@param attr_start Points to the first first field of the attribute.
@param up_to Maximum number of bytes to be checked
@param num_params Will tell number of parameters of the attribute
@param num_fields Will tell number of fields of the attribute. See return.
@param new_pos Will point to the desired byte position if return is 1
@param flag Unused yet. Submit 0
@return 1 ok, *new_pos and *num_fields are valid
0 not ok, *num_fields tells number of missing fields
-1 not ok, not an attribute start
*/
int aaip_decode_check(char aa_name[2], char *attr_start, size_t up_to,
unsigned int *num_params, unsigned int *num_fields,
char **new_pos, int flag)
{
unsigned int i, number_of_fields;
char *rpt;
unsigned char *upt;
/* check signature */
if(up_to < 16)
return(-1);
rpt= attr_start;
upt= (unsigned char *) rpt;
if(rpt[0] != aa_name[0] || rpt[1] != aa_name[1] || upt[4] < 0x20)
return(-1);
number_of_fields= upt[8] | (upt[9] << 8) | (upt[10] << 16) | (upt[11] << 24);
*num_params= 1;
for(i= 0; i < number_of_fields; i++) {
if(i > 0) {
if(rpt[0] != aa_name[0] || rpt[1] != aa_name[1] || upt[4] >= 0x20)
return(-1);
if(!(upt[4] & 1))
(*num_params)++;
}
rpt+= upt[2];
upt= (unsigned char *) rpt;
if(rpt - attr_start >= up_to && i < number_of_fields - 1) {
*num_fields= number_of_fields - 1 - i;
return(0);
}
}
*new_pos= rpt;
*num_fields= number_of_fields;
return(1);
}
/* Check whether the given fields are an attribute of type PAIR and eventually
retrieve its name and value. *value and *name will be allocated memory which
has to be freed when no longer needed. Both will have a trailing 0 byte
which is not counted in their len values.
@param aa_name The 2 byte SUSP Signature Word of the fields as of
the ER field with signature "AAIP_2008A"
@param attr_start Points to the first first field of the attribute.
@param up_to Maximum number of bytes to be checked
@param new_pos Will point to the desired byte position if return is 1
@param name_len Will point to the number of bytes in *name
@param name Will point to the bytes of the pair name
@param value_len Will point to the number of bytes in *value
@param value Will point to the bytes of the pair value
@param flag Unused yet. Submit 0
@return 1 ok, result is valid
0 not ok, not a PAIR type attribute or not complete
-1 not ok, other error
*/
int aaip_decode_pair(char aa_name[2], char *attr_start, size_t up_to,
char **new_pos,
size_t *name_len, char **name,
size_t *value_len, char **value, int flag)
{
unsigned int num_fields, num_params, field_idx= 0;
int ret;
char *peek_pos;
*name_len= *value_len= 0;
*name= *value= NULL;
if(up_to < 9)
return(0);
if(strncmp(attr_start + 4, "PAIR", 4) != 0)
return(0);
ret= aaip_decode_check(aa_name, attr_start, up_to, &num_params, &num_fields,
&peek_pos, 0);
if(ret <= 0)
return(ret);
if(num_params != 2)
return(0);
ret= aaip_decode_parameter(attr_start, num_fields, &attr_start, &field_idx,
name_len, name, 0);
if(ret <= 0)
return(-1);
ret= aaip_decode_parameter(attr_start, num_fields, &attr_start, &field_idx,
value_len, value, 0);
if(ret <= 0)
return(-1);
*new_pos= peek_pos;
return(1);
}
/* Check whether the given fields are an attribute of type ACL1 and eventually
retrieve its long text form ready for acl_from_text(3). This text is
terminated by a 0 byte.
*text will be allocated memory which has to be freed when no longer needed.
@param aa_name The 2 byte SUSP Signature Word of the fields as of
the ER field with signature "AAIP_2008A"
@param attr_start Points to the first first field of the attribute.
@param up_to Maximum number of bytes to be checked
@param new_pos Will point to the desired byte position if return is 1
@param text Will point to the bytes of the ACL text form
@param flag Unused yet. Submit 0
@return 1 ok, result is valid
0 not ok, not a ACL1 type attribute or not complete
-1 not ok, other error
*/
int aaip_decode_acl1(char aa_name[2], char *attr_start, size_t up_to,
char **new_pos, char **text, int flag)
{
unsigned int num_fields, num_params, field_idx= 0;
int ret;
size_t data_len;
char *peek_pos, *data= NULL;
*text= NULL;
if(up_to < 9)
return(0);
if(strncmp(attr_start + 4, "ACL1", 4) != 0)
return(0);
ret= aaip_decode_check(aa_name, attr_start, up_to, &num_params, &num_fields,
&peek_pos, 0);
if(ret <= 0)
return(ret);
if(num_params != 1)
return(0);
ret= aaip_decode_parameter(attr_start, num_fields, &attr_start, &field_idx,
&data_len, &data, 0);
if(ret <= 0)
return(-1);
/* >>> convert parameter into long text form of ACL . see man 5 acl */;
*new_pos= peek_pos;
if(data != NULL)
free(data);
return(1);
}
/* Helper function:
Read a parameter from one or more fields and return the number of consumed
bytes. It is assumed that the completeness of the attribute has been
checked already.
@param start_field Points to the field where the parameter starts
@param num_fields Total number of fields in the attribute
@param new_pos Will point to the byte position after the parameter
@param field_idx Number of fields read so far. This will be updated
by this call.
@param result_len Will point to the number of bytes in result
@param result Will point to bytes of the parameter. A terminating 0
is added for convenience but not counted in result_len
@param flag Unused yet. Submit 0.
@return >0 Number of bytes consumed (payload and overhead)
This is eventually the start of the next field
after the parameter.
<=0 error
*/
static int aaip_decode_parameter(char *start_field, unsigned int num_fields,
char **new_pos, unsigned int *field_idx,
size_t *result_len, char **result, int flag)
{
int overhead, pass;
unsigned int fi;
char *rpt, *wpt;
unsigned char *upt;
size_t mem_size= 0;
*result_len= 0;
*result= NULL;
fi= *field_idx;
for(pass= 0; pass < 2; pass++) {
rpt= start_field;
upt= (unsigned char *) rpt;
for(fi= *field_idx; fi < num_fields; fi++) {
if(upt[4] >= 0x20)
overhead= 16;
else
overhead= 5;
if(pass == 0) {
mem_size+= upt[2] - overhead;
} else {
memcpy(wpt, rpt + overhead, upt[2] - overhead);
wpt+= upt[2] - overhead;
}
rpt+= upt[2];
upt= (unsigned char *) rpt;
if(fi < num_fields - 1)
if(upt[4] < 0x20 && !(upt[4] & 1))
break; /* start of next parameter found */
}
if(pass == 0) {
if(mem_size >= Aaip_param_memory_limiT)
return(-1);
(*result)= calloc(1, mem_size + 1);
if(*result == NULL)
return(-1);
*result_len= mem_size;
wpt= *result;
wpt[mem_size]= 0; /* a trailing zero cannot harm */
}
}
*field_idx= fi;
*new_pos= rpt;
return(1);
}
/* ------------------------- Test ------------------------ */
int main(int argc, char **argv)
{
int ret, l, mult= 0, k;
size_t result_len, i, name_len, value_len;
char *result= NULL, *new_pos, *name= NULL, *value= NULL, *params[2], *cpt;
unsigned int param_lengths[2];
if(argc != 3) {
fprintf(stderr, "usage: %s [-]name[xNNN] [-]value[xNNN]\n", argv[0]);
exit(1);
}
for(i= 0; i < 2; i++) {
params[i]= argv[i + 1];
if(argv[i + 1][0] == '-') {
cpt= strchr(argv[i + 1], 'x');
if(cpt != NULL) {
l= cpt - argv[i + 1] - 1;
if(l > 0)
sscanf(cpt + 1, "%d", &mult);
if(l > 0 && mult > 0) {
params[i]= calloc(1, l * mult + 1);
if(params[i] != NULL) {
for(k= 0; k < mult; k++)
memcpy(params[i] + k * l, argv[i + 1] + 1, l);
params[i][mult * l]= 0;
} else
params[i]= argv[i + 1];
}
}
}
}
param_lengths[0]= strlen(params[0]);
param_lengths[1]= strlen(params[1]);
ret= aaip_encode("AA", "PAIR", 2, param_lengths, params,
&result_len, &result, 0);
if(ret <= 0) {
fprintf(stderr, "%s : aaip_encode failed with ret= %d\n", argv[0], ret);
exit(2);
}
printf(
" - - - - - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9\n");
printf("\n");
printf("%4u : ", 0);
for(i= 0; i < result_len; i++) {
if(result[i] >= 32 && result[i] <= 126)
printf("'%c' ", result[i]);
else
printf("%3u ", (unsigned int) ((unsigned char *) result)[i]);
if((i % 10) == 9)
printf("\n%4u : ", (unsigned int) (i + 1));
}
printf("\n\n");
printf(
" - - - - - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9\n");
printf("\n");
ret= aaip_decode_pair("AA", result, result_len, &new_pos,
&name_len, &name, &value_len, &value, 0);
if(ret <= 0) {
fprintf(stderr, "%s : aaip_decode_pair failed with ret= %d\n",
argv[0], ret);
exit(3);
}
if(mult == 0 || (name_len < 1000 && value_len < 1000)) {
printf("name = '%s' (%lu)\n", name, (unsigned long) name_len);
printf("value= '%s' (%lu)\n", value, (unsigned long) value_len);
} else {
printf("name = (%lu)\n", (unsigned long) name_len);
printf("value= (%lu)\n", (unsigned long) value_len);
}
printf("\n");
if(result != NULL)
free(result);
if(name != NULL)
free(name);
if(value != NULL)
free(value);
exit(0);
}