From 883fe7ff9fe842ad1ce7ba8078796dd59292bcb9 Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Tue, 18 Nov 2008 22:38:27 +0000 Subject: [PATCH] Demo of the emerging Arbitrary Attribute Interchange Protocol format --- test/aaip.c | 537 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 537 insertions(+) create mode 100644 test/aaip.c diff --git a/test/aaip.c b/test/aaip.c new file mode 100644 index 00000000..e90819ad --- /dev/null +++ b/test/aaip.c @@ -0,0 +1,537 @@ + +/* + + 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 +#include +#include +#include +#include +#include + +/* <<< */ +#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); +} +