From ef5a1c48ba84cf08c6c780b2734767959c7813f7 Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Sun, 21 Dec 2008 10:19:16 +0000 Subject: [PATCH] A first implementation of AAIP 0.2 encoding and decoding --- test/aaip_0_2.c | 688 +++++++++++++++++++++++++++++++++++++++++++ test/aaip_0_2.h | 187 ++++++++++++ test/aaip_0_2_test.c | 179 +++++++++++ 3 files changed, 1054 insertions(+) create mode 100644 test/aaip_0_2.c create mode 100644 test/aaip_0_2.h create mode 100644 test/aaip_0_2_test.c diff --git a/test/aaip_0_2.c b/test/aaip_0_2.c new file mode 100644 index 00000000..1df3c58d --- /dev/null +++ b/test/aaip_0_2.c @@ -0,0 +1,688 @@ + +/* + + Arbitrary Attribute Interchange Protocol , AAIP version 0.2 + Demonstration program for encoding and decoding EA and ACL. + + See test/aaip_0_2.h + http://libburnia-project.org/wiki/AAIP + + >>> ACLs + +*/ + +#include +#include +#include +#include +#include +#include + +/* <<< */ +#define Aaip_encode_debuG 1 + +#include "aaip_0_2.h" + + +/* --------------------------------- Encoder ---------------------------- */ + + +static int aaip_encode_pair(char *name, size_t attr_length, char *attr, + unsigned int *num_recs, size_t *comp_size, + unsigned char *result, size_t result_fill, + int flag); + + +/* Convert an array of Arbitrary Attributes into a series of AAIP fields. + @param aa_name The 2 byte SUSP Signature Word of the fields + @param num_attrs Number of attributes + @param names Array of pointers to 0 terminated name strings + @param attr_lengths Array of byte lengths for each attribute payload + @param attrs Array of pointers to the attribute payload 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 Bitfield for control purposes + bit0= set CONTINUE bit of last AA field to 1 + @return >0 is the number of SUSP fields generated, + 0 means error +*/ +unsigned int aaip_encode(char aa_name[2], + unsigned int num_attrs, char **names, + size_t *attr_lengths, char **attrs, + size_t *result_len, unsigned char **result, int flag) +{ + size_t mem_size= 0, comp_size; + unsigned int number_of_fields, i, num_recs, total_recs= 0, ret; + + /* Predict memory needs, number of SUSP fields and component records */ + *result_len= 0; + for(i= 0; i < num_attrs; i++) { + ret= aaip_encode_pair(names[i], attr_lengths[i], attrs[i], + &num_recs, &comp_size, NULL, (size_t) 0, 1); + if(ret <= 0) + return(ret); + mem_size+= comp_size; + total_recs= num_recs; + } + number_of_fields= mem_size / 250 + !!(mem_size % 250); + mem_size+= number_of_fields * 5; + +#ifdef Aaip_encode_debuG + *result= (unsigned char *) calloc(1, mem_size + 1024000); + /* generous honeypot for overflows */ +#else + *result= (unsigned char *) calloc(1, mem_size); +#endif + + + /* Encode pairs into result */ + for(i= 0; i < num_attrs; i++) { + ret= aaip_encode_pair(names[i], attr_lengths[i], attrs[i], + &num_recs, &comp_size, *result, *result_len, 0); + if(ret <= 0) + return(ret); + (*result_len)+= comp_size; + } + + /* write the field headers */ + for(i= 0; i < number_of_fields; i++) { + (*result)[i * 255 + 0]= aa_name[0]; + (*result)[i * 255 + 1]= aa_name[1]; + if(i < number_of_fields - 1 || (mem_size % 255) == 0) + (*result)[i * 255 + 2]= 255; + else + (*result)[i * 255 + 2]= mem_size % 255; + (*result)[i * 255 + 3]= 1; + (*result)[i * 255 + 4]= (flag & 1) || (i < number_of_fields - 1); + } + (*result_len)+= number_of_fields * 5; + +#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); +} + + +static void aaip_encode_byte(unsigned char *result, size_t *result_fill, + unsigned char value) +{ + result[(*result_fill / 250) * 255 + 5 + (*result_fill % 250)]= value; + (*result_fill)++; +} + + +static int aaip_encode_comp(unsigned char *result, size_t *result_fill, + char *data, size_t l, int flag) +{ + size_t todo; + char *rpt, *comp_start; + + if(l == 0) { + aaip_encode_byte(result, result_fill, 0); + aaip_encode_byte(result, result_fill, 0); + return(1); + } + for(rpt= data; rpt - data < l;) { + todo= l - (rpt - data); + aaip_encode_byte(result, result_fill, (todo > 255)); + if(todo > 255) + todo= 255; + aaip_encode_byte(result, result_fill, todo); + for(comp_start= rpt; rpt - comp_start < todo; rpt++) + aaip_encode_byte(result, result_fill, *((unsigned char *) rpt)); + } + return(1); +} + + +/* Write the component records for name and attr. Skip the positions of + AA field headers. + @param flag bit0= only count but do not produce result +*/ +static int aaip_encode_pair(char *name, size_t attr_length, char *attr, + unsigned int *num_recs, size_t *comp_size, + unsigned char *result, size_t result_fill, + int flag) +{ + size_t l; + + l= strlen(name); + *num_recs= l / 255 + (!!(l % 255)) + (l == 0) + + attr_length / 255 + (!!(attr_length % 255)) + (attr_length == 0); + *comp_size= l + attr_length + 2 * *num_recs; + + if(flag & 1) + return(1); + + aaip_encode_comp(result, &result_fill, name, l, 0); + aaip_encode_comp(result, &result_fill, attr, attr_length, 0); + return(1); +} + + +/* >>> Encoder for ACLs */; + + +/* --------------------------------- Decoder ---------------------------- */ + +/* --- private --- */ + +/* Not less than 2 * 2048 */ +#define Aaip_buffer_sizE 4096 + +struct aaip_state { + + /* AA field status */ + unsigned char aa_name[2]; + int aa_head_missing; /* number of bytes needed to complete AA field header */ + int aa_missing; /* number of bytes needed to complete current AA field */ + int aa_ends; /* 0= still AA fields expected, 1= last AA being processed, + 2= all AA fields processed, 3= all is delivered */ + + /* Buffer for component records */ + int recs_invalid; /* number of components to skip */ + unsigned char recs[Aaip_buffer_sizE]; + size_t recs_fill; + int rec_head_missing; /* number of bytes needed to complete rec header */ + int rec_missing; /* number of bytes needed to complete current rec */ + int rec_ends; + + /* Counter for completed data */ + unsigned int num_recs; + size_t ready_bytes; + + /* Counter and meaning for completed components */ + unsigned int num_components; + int first_is_name; + + /* Last return value of aaip_decode_pair() */ + int pair_status; + unsigned int pairs_skipped; + +}; + + +/* ------- functions ------ */ + + +size_t aaip_sizeof_aaip_state(void) +{ + return((size_t) sizeof(struct aaip_state)); +} + + +int aaip_init(struct aaip_state *aaip, char aa_name[2], int flag) +{ + aaip->aa_name[0]= aa_name[0]; + aaip->aa_name[1]= aa_name[1]; + aaip->aa_head_missing= 5; + aaip->aa_missing= 0; + + aaip->recs_invalid= 0; + memset(aaip->recs, 0, Aaip_buffer_sizE); + aaip->recs_fill= 0; + aaip->rec_head_missing= 2; + aaip->rec_missing= 0; + aaip->rec_ends= 0; + + aaip->num_recs= 0; + aaip->ready_bytes= 0; + + aaip->num_components= 0; + aaip->first_is_name= 1; + + aaip->pair_status= 1; + aaip->pairs_skipped= 0; + + return(1); +} + + +static int aaip_consume_rec_head(struct aaip_state *aaip, + unsigned char **data, size_t *num_data, int flag) +{ + size_t todo, i; + + todo= *num_data; + if(todo > aaip->aa_missing) + todo= aaip->aa_missing; + if(todo >= aaip->rec_head_missing) + todo= aaip->rec_head_missing; + if(!aaip->recs_invalid) { + memcpy(aaip->recs + aaip->recs_fill, *data + i, todo); + aaip->recs_fill+= todo; + } + aaip->rec_head_missing-= todo; + if(aaip->rec_head_missing == 0) { + aaip->rec_missing= aaip->recs[aaip->recs_fill - 1]; + aaip->rec_ends= !(aaip->recs[aaip->recs_fill - 2] & 1); + } + aaip->aa_missing-= todo; + (*num_data)-= todo; + (*data)+= todo; + return(1); +} + + +static int aaip_consume_rec_data(struct aaip_state *aaip, + unsigned char **data, size_t *num_data, int flag) +{ + size_t todo, i; + + todo= *num_data; + if(todo > aaip->aa_missing) + todo= aaip->aa_missing; + if(todo > aaip->rec_missing) + todo= aaip->rec_missing; + if(!aaip->recs_invalid) { + memcpy(aaip->recs + aaip->recs_fill, *data + i, todo); + aaip->recs_fill+= todo; + aaip->ready_bytes+= todo; + } + aaip->rec_missing-= todo; + aaip->aa_missing-= todo; + (*num_data)-= todo; + (*data)+= todo; + if(aaip->rec_missing <= 0) { + if(aaip->recs_invalid > 0) { + if(aaip->rec_ends) + aaip->recs_invalid--; + } else { + aaip->num_recs++; + if(aaip->rec_ends) + aaip->num_components++; + } + aaip->rec_head_missing= 2; + } + return(0); +} + + +static int aaip_consume_aa_head(struct aaip_state *aaip, + unsigned char **data, size_t *num_data, int flag) +{ + size_t todo, i; + + todo= *num_data; + if(todo >= aaip->aa_head_missing) + todo= aaip->aa_head_missing; + for(i= 0; i < todo; i++) + aaip->recs[aaip->recs_fill++]= (*data)[i]; + aaip->aa_head_missing-= todo; + if(aaip->aa_head_missing == 0) { + if(aaip->recs[aaip->recs_fill - 5] != aaip->aa_name[0] || + aaip->recs[aaip->recs_fill - 4] != aaip->aa_name[1] || + aaip->recs[aaip->recs_fill - 2] != 1) + return(-1); + aaip->aa_missing= aaip->recs[aaip->recs_fill - 3]; + aaip->aa_ends= !(aaip->recs[aaip->recs_fill - 1] & 1); + aaip->recs_fill-= 5; /* AA heads do not get delivered */ + if(aaip->aa_missing >= 5) + aaip->aa_missing-= 5; + else + aaip->aa_missing= 0; + } + (*num_data)-= todo; + (*data)+= todo; + return(1); +} + + +static int aaip_consume_aa_data(struct aaip_state *aaip, + unsigned char **data, size_t *num_data, int flag) +{ + while(*num_data > 0 && aaip->aa_missing > 0) { + if(aaip->rec_head_missing > 0) { + aaip_consume_rec_head(aaip, data, num_data, 0); + if(*num_data == 0 || aaip->aa_missing <= 0) + return(1); + } + aaip_consume_rec_data(aaip, data, num_data, 0); + } + if(aaip->aa_missing <= 0) { + if(aaip->aa_ends) { + + /* >>> Check for incomplete component or pair, discard it */; + + aaip->aa_ends= 2; + } else + aaip->aa_head_missing= 5; + } + return(0); +} + + +/* Submit small data chunk for decoding. + The return value will tell whether data are pending for being fetched. + @param aaip The AAIP decoder context + @param data Not more than 2048 bytes input for the decoder + @parm num_data Number of bytes in data + 0 inquires the buffer status avoiding replies <= 0 + @param ready_bytes Number of decoded bytes ready for delivery + @param flag Bitfield for control purposes + @return -1= non-AA field detected + *ready_bytes gives number of consumed bytes in data + 0= cannot accept data because buffer full + 1= no component record complete, submit more data + 2= component record complete, may be delivered + 3= component complete, may be delivered + 4= no component available, no more data expected, done +*/ +int aaip_submit_data(struct aaip_state *aaip, + unsigned char *data, size_t num_data, + size_t *ready_bytes, int flag) +{ + int ret; + unsigned char *in_data; + + if(aaip->aa_ends == 3) + return(4); + in_data= data; + if(num_data == 0) + goto ex; + if(aaip->recs_fill + num_data > Aaip_buffer_sizE) + return(0); + + while(aaip->aa_missing == 0 && num_data > 0) { + if(aaip->aa_head_missing > 0) { + ret= aaip_consume_aa_head(aaip, &data, &num_data, 0); + if(ret < 0) { + *ready_bytes= data - in_data; + return(-1); + } + if(num_data == 0 || aaip->aa_missing <= 0) + goto ex; + } + aaip_consume_aa_data(aaip, &data, &num_data, 0); + } +ex:; + *ready_bytes= aaip->ready_bytes; + if(aaip->num_components > 0) + return(3); + if(aaip->num_recs > 0) + return(2); + if(aaip->aa_ends && aaip->aa_head_missing == 0 && aaip->aa_missing == 0) + aaip->aa_ends= 2; + if(aaip->aa_ends == 2 && aaip->num_recs == 0) + aaip->aa_ends= 3; + if(aaip->aa_ends == 3) + return(4); + return(1); +} + + +/* Fetch the available part of current component. + The return value will tell whether it belongs to name or to value and + whether that name or value is completed now. + @param aaip The AAIP decoder context + @param result Has to point to storage for the component data + @param result_size Gives the amount of provided result storage + @param num_result Will tell the number of fetched result bytes + @param flag Bitfield for control purposes + bit0= discard data rather than copying to result + @return -2 = insufficient result_size + -1 = no data ready for delivery + 0 = result holds the final part of a name + 1 = result holds an intermediate part of a name + 2 = result holds the final part of a value + 3 = result holds an intermediate part of a value +*/ +int aaip_fetch_data(struct aaip_state *aaip, + char *result, size_t result_size, size_t *num_result, + int flag) +{ + int ret= -1, complete= 0; + unsigned int i, num_bytes= 0, h; + + if(aaip->num_recs == 0) + return(-1); + + /* Copy data until end of buffer or end of component */ + h= 0; + for(i= 0; i < aaip->num_recs && !complete; i++) { + if(!(flag & 1)) { + if(num_bytes + aaip->recs[h + 1] > result_size) + return(-2); + memcpy(result + num_bytes, aaip->recs + h + 2, aaip->recs[h + 1]); + *num_result= num_bytes + aaip->recs[h + 1]; + } + num_bytes+= aaip->recs[h + 1]; + if(!(aaip->recs[h] & 1)) + complete= 1; + h+= aaip->recs[h + 1] + 2; + } + aaip->num_recs-= i; + aaip->ready_bytes-= num_bytes; + + /* Shift buffer */ + if(h < aaip->recs_fill) + memmove(aaip->recs, aaip->recs + h, aaip->recs_fill - h); + aaip->recs_fill-= h; + + /* Compute reply */ + ret= 2 * !aaip->first_is_name; + if(complete) { + aaip->first_is_name= !aaip->first_is_name; + if(aaip->num_components > 0) + aaip->num_components--; + } else + ret|= 1; + + return(ret); +} + + +/* Skip the current component and eventually the following value component. + This has to be called if fetching of a component shall be aborted + but the next component resp. pair shall be fetchable again. + aaip_submit_data() will not indicate readiness for fetching until all + bytes of the skipped components are submitted. Those bytes get discarded. + @param aaip The AAIP decoder context + @param flag Bitfield for control purposes + bit0= do not skip value if current component is name + @return <=0 error , 1= now in skip state, 2= not in skip state +*/ +int aaip_skip_component(struct aaip_state *aaip, int flag) +{ + int to_skip= 1; + + if(aaip->recs_invalid) { + aaip->recs_invalid+= 1 + (flag & 1); + return(1); + } + if(aaip->first_is_name && !(flag & 1)) + to_skip= 2; + + if(aaip->num_components) { + /* null-fetch */ + aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1); + to_skip--; + } + if(aaip->num_components && to_skip) { + /* null-fetch */ + aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1); + to_skip--; + } + if(to_skip) { + aaip->recs_fill= 0; + aaip->num_recs= 0; + aaip->ready_bytes= 0; + } + aaip->recs_invalid= to_skip; + if(aaip->aa_ends == 2 && aaip->num_recs == 0) + aaip->aa_ends= 3; + return(1 + (aaip->num_recs > 0)); +} + + +/* + @return see aaip_decode_pair +*/ +static int aaip_advance_pair(struct aaip_state *aaip, + char *name, size_t name_size, size_t *name_fill, + char *value, size_t value_size, size_t *value_fill, + int flag) +{ + int ret; + char *wpt; + size_t size, num; + +retry:; + if(aaip->first_is_name) { + wpt= name + *name_fill; + size= name_size - *name_fill; + } else { + wpt= value + *value_fill; + size= value_size - *value_fill; + } + ret= aaip_fetch_data(aaip, wpt, size, &num, 0); + if(ret == -2) { /* insufficient result size */ + ret= aaip_skip_component(aaip, 0); + *name_fill= *value_fill= 0; + aaip->pairs_skipped++; + if(ret == 2) /* Skip performed, valid data pending */ + goto retry; + } else if(ret == -1) { /* No data ready for delivery : may not happen */ + return(-1); + } else if(ret == 0) { /* result holds the final part of a name */ + (*name_fill)+= num; + /* peek for value data */ + ret= aaip_submit_data(aaip, NULL, (size_t) 0, &num, 0); + if(ret == 2 || ret == 3) { + /* fetch value data */; + ret= aaip_advance_pair(aaip, name, name_size, name_fill, + value, value_size, value_fill, flag); + return ret; + } else if(ret == 4) + return(5); + } else if(ret == 1) { /* result holds an intermediate part of a name */ + (*name_fill)+= num; + } else if(ret == 2) { /* result holds the final part of a value */ + (*value_fill)+= num; + if(aaip->num_components >= 2) + return(3); + if(aaip->aa_ends == 2 && aaip->num_recs == 0) + aaip->aa_ends= 3; + if(aaip->aa_ends == 3) + return(4); + return(2); + } else if(ret == 3) { + /* result holds an intermediate part of a value */; + (*value_fill)+= num; + } else { + return(-1); /* unknown reply from aaip_fetch_data() */ + } + return(1); +} + + +/* Accept raw input data and collect a pair of name and value. + The return value will indicate whether the pair is complete, whether more + pairs are complete or whether more data are desired. No input data will be + accepted as long as complete pairs are pending. The end of the attribute + list will be indicated. + @param aaip The AAIP decoder context + @param data The raw data to decode + @param num_data Number of data bytes provided + @param consumed Returns the number of consumed data bytes + @param name Buffer to build the name string + @param name_size Maximum number of bytes in name + @param name_fill Holds the current buffer fill of name + @param value Buffer to build the value string + @param value_size Maximum number of bytes in value + @param value_fill Holds the current buffer fill of value + @param flag Bitfield for control purposes - submit 0 for now + @return <0 error + 0 data not accepted, first fetch pending pairs with num_data == 0 + 1 name and value are not valid yet, submit more data + 2 name and value are valid, submit more data + 3 name and value are valid, pairs pending, fetch with num_data == 0 + 4 name and value are valid, no more data expected + 5 name and value are not valid, no more data expected +*/ +int aaip_decode_pair(struct aaip_state *aaip, + unsigned char *data, size_t num_data, size_t *consumed, + char *name, size_t name_size, size_t *name_fill, + char *value, size_t value_size, size_t *value_fill, + int flag) +{ + int ret; + size_t ready_bytes; + + *consumed= 0; + if(aaip->pair_status < 0 || aaip->pair_status == 4 || + aaip->pair_status == 5) { /* dead ends */ + ret= aaip->pair_status; + goto ex; + } else if(aaip->pair_status == 2 || aaip->pair_status == 3) { + if(aaip->pair_status == 3 && num_data > 0) + {ret= 0; goto ex;} + /* Start a new pair */ + if(!aaip->first_is_name) /* Eventually skip orphaned value */ + aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1); + *name_fill= *value_fill= 0; + } + + if(num_data > 0) { + ret= aaip_submit_data(aaip, data, num_data, &ready_bytes, 0); + } else { + ret= 1; + if(aaip->num_components) + ret= 3; + else if(aaip->num_recs) + ret= 2; + } + if(ret < 0) { /* non-AA field detected */ + *consumed= ready_bytes; + {ret= -1; goto ex;} + } else if(ret == 0) { /* buffer overflow */; + /* should not happen with correct usage */ + {ret= -2; goto ex;} + } else if(ret == 1) { /* no component record complete */ + goto ex; + } else if(ret == 2) { /* component record complete, may be delivered */ + ; + } else if(ret == 3) { /* component complete, may be delivered */ + ; + } else if(ret == 4) { /* no component available, no more data expected */ + {ret= 5; goto ex;} + } else + {ret= -1; goto ex;} /* unknown reply from aaip_submit_data() */ + + *consumed= num_data; + ret= aaip_advance_pair(aaip, name, name_size, name_fill, + value, value_size, value_fill, 0); + if(aaip->aa_ends == 3) { + if(ret >= 2 && ret <= 4) + ret= 4; + else + ret= 5; + } +ex:; + aaip->pair_status= ret; + return(ret); +} + + +unsigned int aaip_get_pairs_skipped(struct aaip_state *aaip, int flag) +{ + return(aaip->pairs_skipped); +} + + +/* >>> Decoder for ACLs */; + diff --git a/test/aaip_0_2.h b/test/aaip_0_2.h new file mode 100644 index 00000000..2e1647bc --- /dev/null +++ b/test/aaip_0_2.h @@ -0,0 +1,187 @@ + +/* + + Arbitrary Attribute Interchange Protocol , AAIP version 0.2 + Demonstration program for encoding and decoding EA and ACL. + + See http://libburnia-project.org/wiki/AAIP + + test/aaip_0.2.h - Public declarations + +*/ + +#ifndef Aaip_h_is_includeD +#define Aaip_h_is_includeD yes + + +/* --------------------------------- Encoder ---------------------------- */ + +/* Convert an array of Arbitrary Attributes into a series of AAIP fields. + @param aa_name The 2 byte SUSP Signature Word of the fields + @param num_attrs Number of attributes + @param names Array of pointers to 0 terminated name strings + @param attr_lengths Array of byte lengths for each attribute payload + @param attrs Array of pointers to the attribute payload 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 Bitfield for control purposes + bit0= set CONTINUE bit of last AA field to 1 + @return >0 is the number of SUSP fields generated, + 0 means error +*/ +unsigned int aaip_encode(char aa_name[2], + unsigned int num_attrs, char **names, + size_t *attr_lengths, char **attrs, + size_t *result_len, unsigned char **result, int flag); + + +/* >>> Encoder for ACLs */; + + +/* --------------------------------- Decoder ---------------------------- */ + +/* + The AAIP decoder avoids the use of dynamic memory. It rather provides a + stateful decoding context with a small buffer which delivers results to + caller provided memory locations. + This may be done stream-like via the Component Level Interface or to + fixly sized storage for name and value via the Pair Level Interface. +*/ + + +/* The AAIP decoder context. +*/ +struct aaip_state; + + +/* Obtain the size in bytes of an aaip_state object. +*/ +size_t aaip_sizeof_aaip_state(void); + + +/* Initialize a AAIP decoder context. + This has to be done before the first AA field of a node is processed. + The caller has to provide the storage of the struct aaip_state. +*/ +int aaip_init(struct aaip_state *aaip, char aa_name[2], int flag); + + +/* ------------------------------------------------------------------------- */ +/* Component Level Interface + Provides support for unlimited component size but demands the caller + to have a growing storage facility resp. to do own oversize handling. + + This interface expects moderatly sized input pieces and will hand out + moderately sized result pieces. The number of ransactions is virtually + unlimited. +*/ + +/* Submit small data chunk for decoding. + The return value will tell whether data are pending for being fetched. + @param aaip The AAIP decoder context + @param data Not more than 2048 bytes input for the decoder + @param num_data Number of bytes in data + 0 inquires the buffer status avoiding replies <= 0 + @param ready_bytes Number of decoded bytes ready for delivery + @param flag Bitfield for control purposes + @return -1= non-AA field detected + *ready_bytes gives number of consumed bytes in data + 0= cannot accept data because buffer full + 1= no component record complete, submit more data + 2= component record complete, may be delivered + 3= component complete, may be delivered + 4= no component available, no more data expected, done +*/ +int aaip_submit_data(struct aaip_state *aaip, + unsigned char *data, size_t num_data, + size_t *ready_bytes, int flag); + + +/* Fetch the available part of current component. + The return value will tell whether it belongs to name or to value and + whether that name or value is completed now. + @param aaip The AAIP decoder context + @param result Has to point to storage for the component data + @param result_size Gives the amount of provided result storage + @param num_result Will tell the number of fetched result bytes + @param flag Bitfield for control purposes + bit0= discard data rather than copying to result + @return -2 = insufficient result_size + -1 = no data ready for delivery + 0 = result holds the final part of a name + 1 = result holds an intermediate part of a name + 2 = result holds the final part of a value + 3 = result holds an intermediate part of a value +*/ +int aaip_fetch_data(struct aaip_state *aaip, + char *result, size_t result_size, size_t *num_result, + int flag); + + +/* Skip the current component and eventually the following value component. + This has to be called if fetching of a component shall be aborted + but the next component resp. pair shall be fetchable again. + aaip_submit_data() will not indicate readiness for fetching until all + bytes of the skipped components are submitted. Those bytes get discarded. + @param aaip The AAIP decoder context + @param flag Bitfield for control purposes + bit0= do not skip value if current component is name + @return <=0 error , 1= now in skip state, 2= not in skip state +*/ +int aaip_skip_component(struct aaip_state *aaip, int flag); + + +/* ------------------------------------------------------------------------- */ +/* Pair Level Interface + Provides support for names and values of limited size. The limits are + given by the caller who has to provide the storage for name and value. + + This interface expects moderatly sized input pieces. + The number of input transcations is virtually unlimited. + The number of pair transactions after aaip_init() should be limited + to 4 billion. +*/ + + +/* Accept raw input data and collect a pair of name and value. + The return value iwill indicate whether the pair is complete, whether more + pairs are complete or whether more data are desired. No input data will be + accepted as long as complete pairs are pending. The end of the attribute + list will be indicated. + @param aaip The AAIP decoder context + @param data The raw data to decode + @param num_data Number of data bytes provided + @param consumed Returns the number of consumed data bytes + @param name Buffer to build the name string + @param name_size Maximum number of bytes in name + @param name_fill Holds the current buffer fill of name + @param value Buffer to build the value string + @param value_size Maximum number of bytes in value + @param value_fill Holds the current buffer fill of value + @param flag Bitfield for control purposes - submit 0 for now + @return <0 error + 0 data not accepted, first fetch pending pairs with num_data == 0 + 1 name and value are not valid yet, submit more data + 2 name and value are valid, submit more data + 3 name and value are valid, pairs pending, fetch with num_data == 0 + 4 name and value are valid, no more data expected + 5 name and value are not valid, no more data expected +*/ +int aaip_decode_pair(struct aaip_state *aaip, + unsigned char *data, size_t num_data, size_t *consumed, + char *name, size_t name_size, size_t *name_fill, + char *value, size_t value_size, size_t *value_fill, + int flag); + + +/* Inquire the number of pairs which were skipped because being oversized. + @param aaip The AAIP decoder context + @param flag Bitfield for control purposes - submit 0 for now + @return The number of pairs skipped since aaip_init() +*/ +unsigned int aaip_get_pairs_skipped(struct aaip_state *aaip, int flag); + +#endif /* ! Aaip_h_is_includeD */ + diff --git a/test/aaip_0_2_test.c b/test/aaip_0_2_test.c new file mode 100644 index 00000000..92875f16 --- /dev/null +++ b/test/aaip_0_2_test.c @@ -0,0 +1,179 @@ + +/* + + Arbitrary Attribute Interchange Protocol , AAIP version 0.2 + Demonstration program for encoding and decoding EA and ACL. + + See http://libburnia-project.org/wiki/AAIP + + test/aaip_0_2_test.c - Main program for test binary + + Compile: cc -g -Wall -o test/aaip test/aaip_0_2.c test/aaip_0_2_test.c + + Usage: ./aaip name value + Long parameters ./aaip -name"x100" -value"x100" + + >>> ACLs + +*/ + +#include +#include +#include +#include +#include +#include + +#include "aaip_0_2.h" + + +int main(int argc, char **argv) +{ + int ret, l, mult= 0, k; + size_t result_len, i; + unsigned char *result= NULL; + char **names= NULL, **values= NULL, *cpt, **param; + size_t *value_lengths= NULL; + + struct aaip_state *aaip; + size_t consumed= 0; + char name[1025]; + size_t name_size= 4; + size_t name_fill; + char value[1025]; + size_t value_size= 1024; + size_t value_fill; + size_t todo; + int submit_data, is_done; + unsigned char *rpt; + unsigned int skipped, was_skipped= 0; + + if(argc < 3 || (argc % 2) == 0) { + fprintf(stderr, "usage: %s [-]name[xNNN] [-]value[xNNN] ...\n", argv[0]); + exit(1); + } + aaip= (struct aaip_state *) calloc(aaip_sizeof_aaip_state(), 1); + names= calloc(sizeof(char *), (argc - 1) / 2); + values= calloc(sizeof(char *), (argc - 1) / 2); + value_lengths= calloc(sizeof(size_t), (argc - 1) / 2); + + for(i= 0; i < argc - 1; i++) { + if(i % 2) + param= values + i / 2; + else + param= names + i / 2; + (*param)= 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) { + (*param)= calloc(1, l * mult + 1); + if((*param) != NULL) { + for(k= 0; k < mult; k++) + memcpy((*param) + k * l, argv[i + 1] + 1, l); + (*param)[mult * l]= 0; + } else + (*param)= argv[i + 1]; + } + } + } + if(i % 2) + value_lengths[i / 2]= strlen(values[i / 2]); + } + + ret= aaip_encode("AA", (unsigned int) ((argc - 1) / 2), names, + value_lengths, values, + &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"); + + + aaip_init(aaip, "AA", 0); + rpt= result; + submit_data= 1; + is_done= 0; + while(!is_done) { + if(submit_data) { + todo= result_len - (rpt - result); + if(todo > 2048) + todo= 2048; + if(todo == 0) { + fprintf(stderr, "%s : Out of data while still prompted to submit\n", + argv[0]); + exit(5); + } + } else + todo= 0; + ret= aaip_decode_pair(aaip, rpt, todo, &consumed, + name, name_size, &name_fill, + value, value_size, &value_fill, 0); + skipped= aaip_get_pairs_skipped(aaip, 0); + if(skipped > was_skipped) { + printf("- skipped %d pair%s -\n\n", skipped - was_skipped, + skipped - was_skipped > 1 ? "s" : ""); + was_skipped= skipped; + } + if(ret < 0) { + fprintf(stderr, "%s : aaip_decode_pair failed with ret= %d\n", + argv[0], ret); + exit(3); + } + rpt+= todo; + if(ret == 0) { + rpt-= todo; + submit_data= 0; + continue; + } else if(ret == 1) { + submit_data= 1; + continue; + } else if(ret == 2) { + submit_data= 1; + } else if(ret == 3) { + submit_data= 0; + } else if(ret == 4) { + is_done= 1; + } else if(ret == 5) { + is_done= 1; + break; + } else { + fprintf(stderr, "%s : Unknown return %d from aaip_decode_pair()\n", + argv[0], ret); + exit(4); + } + name[name_fill]= 0; + value[value_fill]= 0; + if((name_fill < 1000 && value_fill < 1000)) { + printf("name = '%s' (%lu)\n", name, (unsigned long) name_fill); + printf("value= '%s' (%lu)\n", value, (unsigned long) value_fill); + } else { + printf("name = (%lu)\n", (unsigned long) name_fill); + printf("value= (%lu)\n", (unsigned long) value_fill); + } + printf("\n"); + } + + exit(0); +} + +