Beispiel #1
0
/**
 * 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;
}
Beispiel #2
0
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;
}
Beispiel #3
0
/**
 * 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;
}