/** * 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; }
/** * 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); }
/** * 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); }
/* * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }