Пример #1
0
/**
 * Decode major/minor and release information from the specified two
 * contiguous GUID bytes.
 *
 * @param guid is the GUID considered
 * @param start is the offset of the markup (2 or 4) in the GUID
 * @param majp is filled with the major version if it's a GTKG markup
 * @param minp is filled with the minor version if it's a GTKG markup
 * @param relp is filled with the release status if it's a GTKG markup
 *
 * @return whether we recognized a GTKG markup.
 */
static bool
guid_extract_gtkg_info(const guid_t *guid, size_t start,
	uint8 *majp, uint8 *minp, bool *relp)
{
	uint8 major;
	uint8 minor;
	bool release;
	uint16 mark;
	uint16 xmark;
	uint8 product_major;

	g_assert(start < GUID_RAW_SIZE - 1);
	major = peek_u8(&guid->v[start]) & 0x0f;
	minor = peek_u8(&guid->v[start + 1]) & 0x7f;
	release = booleanize(0 == (peek_u8(&guid->v[start + 1]) & 0x80));

	mark = guid_gtkg_encode_version(major, minor, release);
	xmark = peek_be16(&guid->v[start]);

	if (mark != xmark)
		return FALSE;

	/*
	 * Even if by extraordinary the above check matches, make sure we're
	 * not distant from more than one major version.  Since GTKG versions
	 * expire every year, and I don't foresee more than one major version
	 * release per year, this strengthens the positive check.
	 */

	product_major = product_get_major();

	if (major != product_major) {
		int8 delta = product_major - major;
		if (delta < -1 || delta > 1)
			return FALSE;
	}

	/*
	 * We've validated the GUID: the HEC is correct and the version is
	 * consistently encoded, judging by the highest 4 bits of guid.v[4].
	 */

	if (majp) *majp = major;
	if (minp) *minp = minor;
	if (relp) *relp = release;

	return TRUE;
}
Пример #2
0
/**
 * Test whether GUID is that of GTKG, and extract version major/minor, along
 * with release status provided the `majp', `minp' and `relp' are non-NULL.
 */
static bool
guid_oob_is_gtkg(const guid_t *guid, uint8 *majp, uint8 *minp, bool *relp)
{
	uint8 hec;

	if (!guid_extract_gtkg_info(guid, 4, majp, minp, relp))
		return FALSE;		/* Marking incorrect, no need to compute HEC */

	/*
	 * The HEC for OOB queries was made of the first 15 bytes for versions
	 * up to 0.98.4u (legacy encoding).  Starting with 0.98.4, we have a
	 * different way of encoding the HEC to preserve its integrity even in
	 * the advent of OOB-proxying.
	 *
	 * Also bit 0 of the HEC is not significant (used to mark requeries)
	 * therefore it is masked out for comparison purposes.
	 */

	hec = peek_u8(&guid->v[15]) & ~GUID_REQUERY;

	if (*majp >0 || *minp >= 99)
		return booleanize((guid_hec_oob(guid) & ~GUID_REQUERY) == hec);

	/*
	 * Transition period for servents earlier than 0.99: try the legacy marking
	 * for 0.97 and earlier. For 0.98, try the legacy marking first, then the
	 * new marking.
	 */

	if (*minp <= 97)
		return booleanize((guid_hec_oob_legacy(guid) & ~GUID_REQUERY) == hec);

	return booleanize((guid_hec_oob_legacy(guid) & ~GUID_REQUERY) == hec) ||
		booleanize((guid_hec_oob(guid) & ~GUID_REQUERY) == hec);
}
Пример #3
0
/**
 * Test whether GUID is that of GTKG, and extract version major/minor, along
 * with release status provided the `majp', `minp' and `relp' are non-NULL.
 */
bool
guid_is_gtkg(const guid_t *guid, uint8 *majp, uint8 *minp, bool *relp)
{
	if (peek_u8(&guid->v[0]) != guid_hec(guid))
		return FALSE;

	return guid_extract_gtkg_info(guid, 2, majp, minp, relp);
}
Пример #4
0
/*
 * Compute HEC for GUID bytes ``start'' to ``end'', inclusive.
 */
static inline uint8
calculate_hec(const struct guid *guid, int start, int end)
{
	int i;
	uint8 hec = 0;

	g_assert(start >= 0);
	g_assert(end <= 15);

	for (i = start; i <= end; i++)
		hec = syndrome_table[hec ^ peek_u8(&guid->v[i])];

	return hec ^ HEC_GTKG_MASK;
}
Пример #5
0
/**
 * Called when Gnutella payload has been read, or when a G2 messsage is read.
 *
 * The actual payload size (effectively read) is expected to be found
 * in n->size for Gnutella messages and G2 messages.
 *
 * @param n			the node from which message was received
 * @param payload	start of Gnutella payload, or head of G2 frame
 */
void
gnet_stats_count_received_payload(const gnutella_node_t *n, const void *payload)
{
	uint8 f;
	uint t;
	uint i;
	gnet_stats_t *stats;
    uint32 size;
	uint8 hops, ttl;

	g_assert(thread_is_main());

	stats = NODE_USES_UDP(n) ? &gnet_udp_stats : &gnet_tcp_stats;
	size = n->size;

	/*
	 * Size is NOT read in the Gnutella header but in n->size, which
	 * reflects how much data we have in the payload, as opposed to the
	 * size in the header which may be wrong, or have highest bits set
	 * because they indicate flags.
	 *
	 * In particular, broken DHT messages often come with an invalid size in
	 * the header.
	 *		--RAM, 2010-10-30
	 */

	if (NODE_TALKS_G2(n)) {
		f = g2_msg_type(payload, size);
		if (f != G2_MSG_MAX) {
			f += MSG_G2_BASE;
		} else {
			f = G_N_ELEMENTS(stats_lut) - 1;	/* Last, holds MSG_UNKNOWN */
		}
		ttl = 1;
		hops = NODE_USES_UDP(n) ? 0 : 1;
		t = stats_lut[f];
		/*
		 * No header for G2, so count header reception now with a size of zero.
		 * This is required to update the other packet reception statistics.
		 */
		gnet_stats_count_received_header_internal(
			deconstify_pointer(n),
			0, t, ttl, hops);
	} else {
		f = gnutella_header_get_function(&n->header);
		hops = gnutella_header_get_hops(&n->header);
		ttl = gnutella_header_get_ttl(&n->header);
		t = stats_lut[f];
	}

	gnet_stats_randomness(n, f, size);

	/*
	 * If we're dealing with a Kademlia message, we need to do two things:
	 *
	 * We counted the Gnutella header for the GTA_MSG_DHT message, but we
	 * now need to undo that and count it as a Kademlia message.
	 *
	 * To access the proper entry in the array, we need to offset the
	 * Kademlia OpCode from the header with MSG_DHT_BASE to get the entry
	 * in the statistics that are associated with that particular message.
	 *		--RAM, 2010-11-01
	 */

	if (GTA_MSG_DHT == f && size + GTA_HEADER_SIZE >= KDA_HEADER_SIZE) {
		uint8 opcode = peek_u8(payload);	/* Kademlia Opcode */

		if (UNSIGNED(opcode + MSG_DHT_BASE) < G_N_ELEMENTS(stats_lut)) {
			t = stats_lut[opcode + MSG_DHT_BASE];
			gnet_stats_count_kademlia_header(n, t);
		}
	}

	g_assert(t < MSG_TOTAL);

    gnet_stats.byte.received[MSG_TOTAL] += size;
    gnet_stats.byte.received[t] += size;

    stats->byte.received[MSG_TOTAL] += size;
    stats->byte.received[t] += size;

	i = MIN(ttl, STATS_RECV_COLUMNS - 1);
    stats->byte.received_ttl[i][MSG_TOTAL] += size;
    stats->byte.received_ttl[i][t] += size;

	i = MIN(hops, STATS_RECV_COLUMNS - 1);
    stats->byte.received_hops[i][MSG_TOTAL] += size;
    stats->byte.received_hops[i][t] += size;
}
Пример #6
0
/**
 * Parse ``size'' bytes starting at ``data''  and fill-in the information
 * about the next record in ``header''.
 *
 * @return the length of the record successfully parsed, 0 on error (since
 * the minimal amount of bytes for an empty record would be DIME_HEADER_SIZE).
 */
static size_t
dime_parse_record_header(const char *data, size_t size,
	struct dime_record *header)
{
	const char * const data0 = data;
	size_t n;
	
	g_assert(data);
	g_assert(header);

	n = DIME_HEADER_SIZE;
	if (size < n) {
		goto failure;
	}
	
	header->version = peek_u8(&data[0]) >> 3;

	if (DIME_VERSION != header->version) {
		g_warning("dime_parse_record_header(): Cannot parse dime version %u, "
			"only version %u is supported",
			header->version, DIME_VERSION);
		goto failure;
	}

	header->flags = peek_u8(&data[0]) & (DIME_F_MB | DIME_F_ME | DIME_F_CF);
	header->type_t = peek_u8(&data[1]) >> 4;
	header->resrvd = peek_u8(&data[1]) & 0x0F;

	header->options_length	= peek_be16(&data[2]);
	header->id_length		= peek_be16(&data[4]);
	header->type_length		= peek_be16(&data[6]);
	header->data_length		= peek_be32(&data[8]);

	size -= n;
	data += n;
	header->options	= data;

	n = dime_ceil(header->options_length);
	if (size < n) {
		dime_log_truncated_record("options",
			header, header->options_length, size);
		goto failure;
	}
	size -= n;
	data += n;
	header->id = data;

	n = dime_ceil(header->id_length);
	if (size < n) {
		dime_log_truncated_record("ID", header, header->id_length, size);
		goto failure;
	}
	size -= n;
	data += n;
	header->type = data;

	n = dime_ceil(header->type_length);
	if (size < n) {
		dime_log_truncated_record("type", header, header->type_length, size);
		goto failure;
	}
	size -= n;
	data += n;
	header->data = data;

	n = dime_ceil(header->data_length);
	if (size < n) {
		dime_log_truncated_record("data", header, header->data_length, size);
		goto failure;
	}
	size -= n;
	data += n;

	return data - data0;

failure:
	return 0;
}
Пример #7
0
/**
 * Get next token, as delimited by one of the characters given in ``delim'' or
 * by the end of the string, whichever comes first.  Same as strtok_next(),
 * only we can specify whether we wish to ignore leading and/or trailing spaces
 * for this lookup.
 *
 * When ``looked'' is non-NULL, we're looking whether the token matches the
 * string, and we do not bother constructing the token as soon as we have
 * determined that the current token cannot match.  Therefore, the returned
 * token string is meaningless and forced to "", the empty string.
 *
 * @param s			the string tokenizing object
 * @param delim		the string containing one-character delimiters, e.g. ",;"
 * @param no_lead	whether leading spaces in token should be stripped
 * @param no_end	whether trailing spaces in token should be stripped
 * @param length	if non-NULL, gets filled with the returned token length
 * @param looked	the token which we're looking for, NULL if none
 * @param caseless	whether token matching is to be done case-insensitively
 * @param found		if non-NULL, gets filled with whether we found ``looked''
 *
 * @return pointer to the next token, which must be duplicated if it needs to
 * be perused, or NULL if there are no more tokens.  The token lifetime lasts
 * until the next call to one of the strtok_* functions on the same object.
 */
static const char *
strtok_next_internal(strtok_t *s, const char *delim,
	bool no_lead, bool no_end, size_t *length,
	const char *looked, bool caseless, bool *found)
{
	size_t tlen;
	int c;
	int d_min, d_max;
	const char *l = NULL;
	bool seen_non_blank = FALSE;
	int deferred_blank = 0;
	char *tstart;

	strtok_check(s);
	g_assert(delim != NULL);

	if (NULL == s->p)
		return NULL;		/* Finished parsing */

	/*
	 * Pre-compile delimiter string to see what are the min and max character
	 * codes on which we delimit tokens.  When handling a low amount of
	 * delimiters which are close enough in the 8-bit code space, this lowers
	 * significantly the amount of character comparisons we have to do.
	 */

	d_min = 256;
	d_max = 0;

	{
		const char *q = delim;
		int d;

		while ((d = peek_u8(q++))) {
			if (d < d_min)
				d_min = d;
			if (d > d_max)
				d_max = d;
		}
	}

	/*
	 * Now parse the string until we reach one of the delimiters or its end.
	 */

	s->t = s->token;
	tlen = 0;

	while ((c = peek_u8(s->p++))) {

		/* Have we reached one of the delimiters? */

		if (c >= d_min && c <= d_max) {
			const char *q = delim;
			int d;

			while ((d = peek_u8(q++))) {
				if (d == c)
					goto end_token;
			}
		}

		/* Check whether token can match the ``looked'' up string */

		if (looked != NULL) {
			if (!seen_non_blank && !is_ascii_blank(c))
				seen_non_blank = TRUE;

			if (!no_lead || seen_non_blank) {
				int x;

				if (l == NULL)
					l = looked;

				if (no_end) {
					if (is_ascii_blank(c)) {
						deferred_blank++;
						continue;
					} else {
						for (/**/; deferred_blank > 0; deferred_blank--) {
							/* All blanks deemed equal here */
							if (!is_ascii_blank(*l++))
								goto skip_until_delim;
						}
					}
				}

				x = peek_u8(l++);
				if (caseless) {
					if (ascii_tolower(c) != ascii_tolower(x))
						goto skip_until_delim;
				} else if (c != x) {
					goto skip_until_delim;
				}
			}
			continue;		/* No need to collect token when looking... */
		}

		/* Character was not a delimiter, add to token */

		if (tlen >= s->len)
			extend_token(s);

		g_assert(tlen < s->len);

		s->t = poke_u8(s->t, c);
		tlen++;
	}

	s->p = NULL;			/* Signals: reached end of string */

end_token:
	if (tlen >= s->len)
		extend_token(s);

	g_assert(tlen < s->len);
	g_assert(s->len > 0);

	/*
	 * Strip trailing white spaces if required.
	 */

	if (no_end) {
		while (s->t > s->token) {
			if (!is_ascii_blank(*(s->t - 1)))
				break;
			s->t--;
		}
	}
	*s->t = '\0';			/* End token string */

	/* Check whether token can match the ``looked'' up string */

	if (looked != NULL) {
		if (l == NULL)
			l = looked;
		if (*l != '\0')
			goto not_found;
		*s->token = '\0';		/* Always return empty string */
	}

	/*
	 * Leading white spaces are skipped if required.
	 */

	tstart = no_lead ? skip_ascii_blanks(s->token) : s->token;

	/* Fill to-be-returned information */

	if (found)  *found  = TRUE;
	if (length) *length = s->t - tstart;

	return tstart;

skip_until_delim:

	/*
	 * Looked-up string did not match the token we were constructing.
	 * Move to the next delimiter or the end of the string, skipping
	 * the token construction.
	 */

	while ((c = peek_u8(s->p++))) {
		if (c >= d_min && c <= d_max) {
			const char *q = delim;
			int d;

			while ((d = peek_u8(q++))) {
				if (d == c)
					goto not_found;		/* Found delimiter, not the string */
			}
		}
	}

	/* FALL THROUGH */

not_found:

	/*
	 * We did not find the looked-up string and reached either the next
	 * delimiter or the end of the parsed string.
	 */

	if (0 == s->len)
		extend_token(s);

	*s->token = '\0';		/* Always return empty string */

	if (length) *length = 0;
	if (found)  *found  = FALSE;

	return s->token;
}
Пример #8
0
/**
 * Test whether a GTKG MUID in a Query is marked as being a retry.
 */
bool
guid_is_requery(const struct guid *guid)
{
	return (peek_u8(&guid->v[15]) & GUID_REQUERY) ? TRUE : FALSE;
}