/** * Decode a base16 encoding of `len' bytes of `data' into the buffer `dst'. * * @param dst destination buffer * @param size length of destination * @param data start of data to decode * @param len amount of encoded data to decode * * @return the amount of bytes decoded into the destination. */ size_t base16_decode(char *dst, size_t size, const void *data, size_t len) { const unsigned char *p = data; char *q = dst; size_t i; if G_UNLIKELY(0 == hex2int_inline('a')) misc_init(); /* Auto-initialization of hex2int_inline() */ len /= 2; len = 2 * (size < len ? size : len); i = 0; while (i < len) { int high, low; high = hex2int_inline(p[i++]); if (high < 0) return (size_t) -1; low = hex2int_inline(p[i++]); if (low < 0) return (size_t) -1; *q++ = (high << 4) | low; } return q - dst; }
static uint32 parse_numeric_entity(const struct array entity) { size_t i = 0; if (i < entity.size && '#' == entity.data[i]) { unsigned base; uint32 v; i++; switch (entity.data[i]) { case 'x': case 'X': base = 16; i++; break; default: base = 10; } v = 0; while (i < entity.size) { unsigned d; d = hex2int_inline(entity.data[i++]); if (d >= base) goto error; v = v * base + d; if (v >= 0x10ffffU) goto error; } if (0 == utf8_encoded_len(v)) goto error; return v; } error: return -1; }
/** * Decodes "chunked" data. * * The function returns as soon as it needs more data to proceed, on * error, if the state CHUNK_STATE_END was reached, or if the state * CHUNK_STATE_DATA was reached. In the latter case the chunk payload * itself must be consumed and this function must not be called again * until the state CHUNK_STATE_DATA_CRLF is reached. * * @param rx the current RX driver. * @param src the chunk data. * @param size no document. * @param p_error_str if not NULL and parse_chunk() fails, it will point * to an informational error message. * * @return 0 on failure; non-zero amount of consumed bytes on success. */ static size_t parse_chunk(rxdrv_t *rx, const char *src, size_t size, const char **p_error_str) { struct attr *attr = rx->opaque; const char *error_str; size_t len; g_assert(attr); g_assert(src); g_assert(size > 0); g_assert(attr->state < NUM_CHUNK_STATES); g_assert(0 == attr->data_remain); len = size; do { switch (attr->state) { case CHUNK_STATE_DATA_CRLF: /* The chunk-data must be followed by a CRLF */ while (len > 0) { uchar c; len--; c = *src++; if ('\r' == c) { /* * This allows more than one CR but we must consume * some data or keep state over this otherwise. */ continue; } else if ('\n' == c) { attr->state = CHUNK_STATE_SIZE; break; } else { /* * Normally it is an error, there should be CRLF after * the chunk data. However, they might have forgotten * to send the '\n' or the whole sequence. * * If what follows looks like a valid chunk size, then * we should be able to resync properly: Unread the * character and move on to the chunk size decoding. */ if (!(attr->flags & IF_NO_CRLF)) { attr->flags |= IF_NO_CRLF; g_warning("Host %s forgot CRLF after data", gnet_host_to_string(&rx->host)); } len++; src--; attr->state = CHUNK_STATE_SIZE; break; } } break; case CHUNK_STATE_SIZE: g_assert(attr->hex_pos < sizeof attr->hex_buf); while (len > 0) { uchar c; len--; c = *src++; if (is_ascii_xdigit(c)) { if (attr->hex_pos >= sizeof attr->hex_buf) { error_str = "Overflow in chunk-size"; goto error; } /* Collect up to 16 hex characters */ attr->hex_buf[attr->hex_pos++] = c; } else { /* * There might be a chunk-extension after the * hexadecimal chunk-size but there shouldn't * anything else. */ if ( 0 == attr->hex_pos || (!is_ascii_space(c) && ';' != c) ) { error_str = "Bad chunk-size"; goto error; } attr->state = CHUNK_STATE_EXT; break; } } break; case CHUNK_STATE_EXT: /* Just skip over the chunk-extension */ while (len > 0) { len--; if ('\n' == *src++) { /* * Pick up the collected hex digits and * calculate the chunk-size. */ g_assert(attr->hex_pos > 0); g_assert(attr->hex_pos <= sizeof attr->hex_buf); { uint64 v = 0; uint i; for (i = 0; i < attr->hex_pos; i++) v = (v << 4) | hex2int_inline(attr->hex_buf[i]); attr->data_remain = v; attr->hex_pos = 0; } attr->state = 0 != attr->data_remain ? CHUNK_STATE_DATA : CHUNK_STATE_TRAILER_START; break; } } break; case CHUNK_STATE_TRAILER_START: /* We've reached another trailer line */ if (len < 1) break; if ('\r' == src[0]) { /* * This allows more than one CR but we must consume * some data or keep state over this otherwise. */ src++; len--; } if (len < 1) break; if ('\n' == src[0]) { /* An empty line means the end of all trailers was reached */ src++; len--; attr->state = CHUNK_STATE_END; break; } attr->state = CHUNK_STATE_TRAILER; /* FALL THROUGH */ case CHUNK_STATE_TRAILER: /* Just skip over the trailer line */ while (len > 0) { len--; if ('\n' == *src++) { /* * Now check whether there's another trailer * line or whether we've reached the end */ attr->state = CHUNK_STATE_TRAILER_START; break; } } break; case CHUNK_STATE_END: /* * We're not supposed to receive data after the chunk stream * has been ended. But if we do, it means either we * misinterpreted the chunk end stream or the other end is just * going berserk. */ error_str = "Remaining data after chunk end"; goto error; case CHUNK_STATE_DATA: case CHUNK_STATE_ERROR: case NUM_CHUNK_STATES: g_assert_not_reached(); break; } /* NB: Some data from ``src'' must have been consumed or an * infinite loop may occur. */ if (CHUNK_STATE_DATA == attr->state) { if (GNET_PROPERTY(rx_debug) > 9) g_debug("parse_chunk: chunk size %s bytes", uint64_to_string(attr->data_remain)); break; } } while (len > 0 && CHUNK_STATE_END != attr->state); if (p_error_str) *p_error_str = NULL; return size - len; error: if (p_error_str) *p_error_str = error_str; attr->state = CHUNK_STATE_ERROR; return 0; }