| #ifndef _SPOP_FUNCTIONS_H |
| #define _SPOP_FUNCTIONS_H |
| |
| #include <inttypes.h> |
| #include <string.h> |
| #include <spoe_types.h> |
| |
| |
| #ifndef MIN |
| #define MIN(a, b) (((a) < (b)) ? (a) : (b)) |
| #endif |
| |
| #ifndef MAX |
| #define MAX(a, b) (((a) > (b)) ? (a) : (b)) |
| #endif |
| |
| |
| /* Encode the integer <i> into a varint (variable-length integer). The encoded |
| * value is copied in <*buf>. Here is the encoding format: |
| * |
| * 0 <= X < 240 : 1 byte (7.875 bits) [ XXXX XXXX ] |
| * 240 <= X < 2288 : 2 bytes (11 bits) [ 1111 XXXX ] [ 0XXX XXXX ] |
| * 2288 <= X < 264432 : 3 bytes (18 bits) [ 1111 XXXX ] [ 1XXX XXXX ] [ 0XXX XXXX ] |
| * 264432 <= X < 33818864 : 4 bytes (25 bits) [ 1111 XXXX ] [ 1XXX XXXX ]*2 [ 0XXX XXXX ] |
| * 33818864 <= X < 4328786160 : 5 bytes (32 bits) [ 1111 XXXX ] [ 1XXX XXXX ]*3 [ 0XXX XXXX ] |
| * ... |
| * |
| * On success, it returns the number of written bytes and <*buf> is moved after |
| * the encoded value. Otherwise, it returns -1. */ |
| static inline int |
| encode_varint(uint64_t i, char **buf, char *end) |
| { |
| unsigned char *p = (unsigned char *)*buf; |
| int r; |
| |
| if (p >= (unsigned char *)end) |
| return -1; |
| |
| if (i < 240) { |
| *p++ = i; |
| *buf = (char *)p; |
| return 1; |
| } |
| |
| *p++ = (unsigned char)i | 240; |
| i = (i - 240) >> 4; |
| while (i >= 128) { |
| if (p >= (unsigned char *)end) |
| return -1; |
| *p++ = (unsigned char)i | 128; |
| i = (i - 128) >> 7; |
| } |
| |
| if (p >= (unsigned char *)end) |
| return -1; |
| *p++ = (unsigned char)i; |
| |
| r = ((char *)p - *buf); |
| *buf = (char *)p; |
| return r; |
| } |
| |
| /* Decode a varint from <*buf> and save the decoded value in <*i>. See |
| * 'spoe_encode_varint' for details about varint. |
| * On success, it returns the number of read bytes and <*buf> is moved after the |
| * varint. Otherwise, it returns -1. */ |
| static inline int |
| decode_varint(char **buf, char *end, uint64_t *i) |
| { |
| unsigned char *p = (unsigned char *)*buf; |
| int r; |
| |
| if (p >= (unsigned char *)end) |
| return -1; |
| |
| *i = *p++; |
| if (*i < 240) { |
| *buf = (char *)p; |
| return 1; |
| } |
| |
| r = 4; |
| do { |
| if (p >= (unsigned char *)end) |
| return -1; |
| *i += (uint64_t)*p << r; |
| r += 7; |
| } while (*p++ >= 128); |
| |
| r = ((char *)p - *buf); |
| *buf = (char *)p; |
| return r; |
| } |
| |
| /* Encode a buffer. Its length <len> is encoded as a varint, followed by a copy |
| * of <str>. It must have enough space in <*buf> to encode the buffer, else an |
| * error is triggered. |
| * On success, it returns <len> and <*buf> is moved after the encoded value. If |
| * an error occurred, it returns -1. */ |
| static inline int |
| spoe_encode_buffer(const char *str, size_t len, char **buf, char *end) |
| { |
| char *p = *buf; |
| int ret; |
| |
| if (p >= end) |
| return -1; |
| |
| if (!len) { |
| *p++ = 0; |
| *buf = p; |
| return 0; |
| } |
| |
| ret = encode_varint(len, &p, end); |
| if (ret == -1 || p + len > end) |
| return -1; |
| |
| memcpy(p, str, len); |
| *buf = p + len; |
| return len; |
| } |
| |
| /* Encode a buffer, possibly partially. It does the same thing than |
| * 'spoe_encode_buffer', but if there is not enough space, it does not fail. |
| * On success, it returns the number of copied bytes and <*buf> is moved after |
| * the encoded value. If an error occurred, it returns -1. */ |
| static inline int |
| spoe_encode_frag_buffer(const char *str, size_t len, char **buf, char *end) |
| { |
| char *p = *buf; |
| int ret; |
| |
| if (p >= end) |
| return -1; |
| |
| if (!len) { |
| *p++ = 0; |
| *buf = p; |
| return 0; |
| } |
| |
| ret = encode_varint(len, &p, end); |
| if (ret == -1 || p >= end) |
| return -1; |
| |
| ret = (p+len < end) ? len : (end - p); |
| memcpy(p, str, ret); |
| *buf = p + ret; |
| return ret; |
| } |
| |
| /* Decode a buffer. The buffer length is decoded and saved in <*len>. <*str> |
| * points on the first byte of the buffer. |
| * On success, it returns the buffer length and <*buf> is moved after the |
| * encoded buffer. Otherwise, it returns -1. */ |
| static inline int |
| spoe_decode_buffer(char **buf, char *end, char **str, uint64_t *len) |
| { |
| char *p = *buf; |
| uint64_t sz; |
| int ret; |
| |
| *str = NULL; |
| *len = 0; |
| |
| ret = decode_varint(&p, end, &sz); |
| if (ret == -1 || p + sz > end) |
| return -1; |
| |
| *str = p; |
| *len = sz; |
| *buf = p + sz; |
| return sz; |
| } |
| |
| /* Encode a typed data using value in <data> and type <type>. On success, it |
| * returns the number of copied bytes and <*buf> is moved after the encoded |
| * value. If an error occurred, it returns -1. |
| * |
| * If the value is too big to be encoded, depending on its type, then encoding |
| * failed or the value is partially encoded. Only strings and binaries can be |
| * partially encoded. In this case, the offset <*off> is updated to known how |
| * many bytes has been encoded. If <*off> is zero at the end, it means that all |
| * data has been encoded. */ |
| static inline int |
| spoe_encode_data(union spoe_data *data, enum spoe_data_type type, unsigned int *off, char **buf, char *end) |
| { |
| char *p = *buf; |
| int ret; |
| |
| if (p >= end) |
| return -1; |
| |
| if (data == NULL) { |
| *p++ = SPOE_DATA_T_NULL; |
| goto end; |
| } |
| |
| *p++ = type; |
| switch (type) { |
| case SPOE_DATA_T_BOOL: |
| p[-1] |= (data->boolean ? SPOE_DATA_FL_TRUE : SPOE_DATA_FL_FALSE); |
| break; |
| |
| case SPOE_DATA_T_INT32: |
| if (encode_varint(data->int32, &p, end) == -1) |
| return -1; |
| break; |
| |
| case SPOE_DATA_T_UINT32: |
| if (encode_varint(data->uint32, &p, end) == -1) |
| return -1; |
| break; |
| |
| case SPOE_DATA_T_INT64: |
| if (encode_varint(data->int64, &p, end) == -1) |
| return -1; |
| break; |
| |
| case SPOE_DATA_T_UINT64: |
| if (encode_varint(data->uint64, &p, end) == -1) |
| return -1; |
| break; |
| |
| case SPOE_DATA_T_IPV4: |
| if (p + 4 > end) |
| return -1; |
| memcpy(p, &data->ipv4, 4); |
| p += 4; |
| break; |
| |
| case SPOE_DATA_T_IPV6: |
| if (p + 16 > end) |
| return -1; |
| memcpy(p, &data->ipv6, 16); |
| p += 16; |
| break; |
| |
| case SPOE_DATA_T_STR: |
| case SPOE_DATA_T_BIN: { |
| /* Here, we need to know if the sample has already been |
| * partially encoded. If yes, we only need to encode the |
| * remaining, <*off> reprensenting the number of bytes |
| * already encoded. */ |
| if (!*off) { |
| /* First evaluation of the sample : encode the |
| * type (string or binary), the buffer length |
| * (as a varint) and at least 1 byte of the |
| * buffer. */ |
| ret = spoe_encode_frag_buffer(data->chk.ptr, data->chk.len, &p, end); |
| if (ret == -1) |
| return -1; |
| } |
| else { |
| /* The sample has been fragmented, encode remaining data */ |
| ret = MIN(data->chk.len - *off, end - p); |
| memcpy(p, data->chk.ptr + *off, ret); |
| p += ret; |
| } |
| /* Now update <*off> */ |
| if (ret + *off != data->chk.len) |
| *off += ret; |
| else |
| *off = 0; |
| break; |
| } |
| /* |
| case SMP_T_METH: { |
| char *m; |
| size_t len; |
| |
| *p++ = SPOE_DATA_T_STR; |
| switch (smp->data.u.meth.meth) { |
| case HTTP_METH_OPTIONS: m = "OPTIONS"; len = 7; break; |
| case HTTP_METH_GET : m = "GET"; len = 3; break; |
| case HTTP_METH_HEAD : m = "HEAD"; len = 4; break; |
| case HTTP_METH_POST : m = "POST"; len = 4; break; |
| case HTTP_METH_PUT : m = "PUT"; len = 3; break; |
| case HTTP_METH_DELETE : m = "DELETE"; len = 6; break; |
| case HTTP_METH_TRACE : m = "TRACE"; len = 5; break; |
| case HTTP_METH_CONNECT: m = "CONNECT"; len = 7; break; |
| |
| default : |
| m = smp->data.u.meth.str.str; |
| len = smp->data.u.meth.str.len; |
| } |
| if (spoe_encode_buffer(m, len, &p, end) == -1) |
| return -1; |
| break; |
| } |
| */ |
| |
| default: |
| /* send type NULL for unknown types */ |
| p[-1] = SPOE_DATA_T_NULL; |
| break; |
| } |
| |
| end: |
| ret = (p - *buf); |
| *buf = p; |
| return ret; |
| } |
| |
| /* Skip a typed data. If an error occurred, -1 is returned, otherwise the number |
| * of skipped bytes is returned and the <*buf> is moved after skipped data. |
| * |
| * A types data is composed of a type (1 byte) and corresponding data: |
| * - boolean: non additional data (0 bytes) |
| * - integers: a variable-length integer (see decode_varint) |
| * - ipv4: 4 bytes |
| * - ipv6: 16 bytes |
| * - binary and string: a buffer prefixed by its size, a variable-length |
| * integer (see spoe_decode_buffer) */ |
| static inline int |
| spoe_skip_data(char **buf, char *end) |
| { |
| char *str, *p = *buf; |
| int type, ret; |
| uint64_t v, sz; |
| |
| if (p >= end) |
| return -1; |
| |
| type = *p++; |
| switch (type & SPOE_DATA_T_MASK) { |
| case SPOE_DATA_T_BOOL: |
| break; |
| case SPOE_DATA_T_INT32: |
| case SPOE_DATA_T_INT64: |
| case SPOE_DATA_T_UINT32: |
| case SPOE_DATA_T_UINT64: |
| if (decode_varint(&p, end, &v) == -1) |
| return -1; |
| break; |
| case SPOE_DATA_T_IPV4: |
| if (p+4 > end) |
| return -1; |
| p += 4; |
| break; |
| case SPOE_DATA_T_IPV6: |
| if (p+16 > end) |
| return -1; |
| p += 16; |
| break; |
| case SPOE_DATA_T_STR: |
| case SPOE_DATA_T_BIN: |
| /* All the buffer must be skipped */ |
| if (spoe_decode_buffer(&p, end, &str, &sz) == -1) |
| return -1; |
| break; |
| } |
| |
| ret = (p - *buf); |
| *buf = p; |
| return ret; |
| } |
| |
| /* Decode a typed data and fill <smp>. If an error occurred, -1 is returned, |
| * otherwise the number of read bytes is returned and <*buf> is moved after the |
| * decoded data. See spoe_skip_data for details. */ |
| static inline int |
| spoe_decode_data(char **buf, char *end, union spoe_data *data, enum spoe_data_type *type) |
| { |
| char *str, *p = *buf; |
| int v, r = 0; |
| uint64_t sz; |
| |
| if (p >= end) |
| return -1; |
| |
| v = *p++; |
| *type = v & SPOE_DATA_T_MASK; |
| |
| switch (*type) { |
| case SPOE_DATA_T_BOOL: |
| data->boolean = ((v & SPOE_DATA_FL_MASK) == SPOE_DATA_FL_TRUE); |
| break; |
| case SPOE_DATA_T_INT32: |
| if (decode_varint(&p, end, &sz) == -1) |
| return -1; |
| data->int32 = sz; |
| break; |
| case SPOE_DATA_T_INT64: |
| if (decode_varint(&p, end, &sz) == -1) |
| return -1; |
| data->int64 = sz; |
| break; |
| case SPOE_DATA_T_UINT32: |
| if (decode_varint(&p, end, &sz) == -1) |
| return -1; |
| data->uint32 = sz; |
| break; |
| case SPOE_DATA_T_UINT64: |
| if (decode_varint(&p, end, &sz) == -1) |
| return -1; |
| data->uint64 = sz; |
| break; |
| case SPOE_DATA_T_IPV4: |
| if (p+4 > end) |
| return -1; |
| memcpy(&data->ipv4, p, 4); |
| p += 4; |
| break; |
| case SPOE_DATA_T_IPV6: |
| if (p+16 > end) |
| return -1; |
| memcpy(&data->ipv6, p, 16); |
| p += 16; |
| break; |
| case SPOE_DATA_T_STR: |
| case SPOE_DATA_T_BIN: |
| /* All the buffer must be decoded */ |
| if (spoe_decode_buffer(&p, end, &str, &sz) == -1) |
| return -1; |
| data->chk.ptr = str; |
| data->chk.len = sz; |
| break; |
| default: /* SPOE_DATA_T_NULL, unknown */ |
| break; |
| } |
| |
| r = (p - *buf); |
| *buf = p; |
| return r; |
| } |
| |
| |
| #endif |