538 lines
17 KiB
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);
|
|
}
|
|
|