/** * Deserialization routine for keydata. */ static void deserialize_keydata(bstr_t *bs, void *valptr, size_t len) { struct keydata *kd = valptr; int i; uint8 version; g_assert(sizeof *kd == len); /* * Early returns will cause DBMW to complain that the value could not * be deserialized properly. We do not log anything here. */ if (!bstr_read_u8(bs, &version)) return; if (!bstr_read_u8(bs, &kd->values)) return; if (kd->values > G_N_ELEMENTS(kd->creators)) return; STATIC_ASSERT(G_N_ELEMENTS(kd->creators) == G_N_ELEMENTS(kd->dbkeys)); STATIC_ASSERT(G_N_ELEMENTS(kd->creators) == G_N_ELEMENTS(kd->expire)); for (i = 0; i < kd->values; i++) { bstr_read(bs, &kd->creators[i], sizeof(kd->creators[i])); bstr_read(bs, &kd->dbkeys[i], sizeof(kd->dbkeys[i])); bstr_read_time(bs, &kd->expire[i]); } }
/** * Deserialization routine for pubdata. */ static void deserialize_pubdata(bstr_t *bs, void *valptr, size_t len) { struct pubdata *pd = valptr; g_assert(sizeof *pd == len); bstr_read_time(bs, &pd->next_enqueue); bstr_read_time(bs, &pd->expiration); /* * Temporary, until 0.96.7 is out: we cannot blindly read the version * since it was lacking in previous experimental versions. Therefore * only do it if we have unread data. * * The test will be removed in versions after 0.96.7, when we can be * certain that the new data format was serialized. * --RAM, 2009-10-18 */ if (bstr_unread_size(bs)) bstr_read_u8(bs, &pd->version); else pd->version = 0; }
/** * Deserialization routine for lifedata. */ static void deserialize_lifedata(bstr_t *bs, gpointer valptr, size_t len) { struct lifedata *ld = valptr; g_assert(sizeof *ld == len); bstr_read_u8(bs, &ld->version); bstr_read_time(bs, &ld->first_seen); bstr_read_time(bs, &ld->last_seen); }
/** * Deserialization routine for guiddata. */ static void deserialize_guiddata(bstr_t *bs, void *valptr, size_t len) { struct guiddata *gd = valptr; uint8 version; g_assert(sizeof *gd == len); bstr_read_u8(bs, &version); bstr_read_time(bs, &gd->create_time); bstr_read_time(bs, &gd->last_time); }
/** * Deserialization routine for tokdata. */ static void deserialize_tokdata(bstr_t *bs, void *valptr, size_t len) { struct tokdata *td = valptr; g_assert(sizeof *td == len); bstr_read_time(bs, &td->last_update); bstr_read_u8(bs, &td->length); if (td->length != 0) { td->token = walloc(td->length); bstr_read(bs, td->token, td->length); } else { td->token = NULL; } }
/** * Handle reply to a mapping request. * * @param payload the received reply * @param len length of reply * @param rd the RPC request descriptor * * @return TRUE if we successfully processed the reply and notified the * user code about the outcome of the request, FALSE if we need to resend * the request. */ static bool natpmp_handle_mapping_reply( const void *payload, size_t len, struct natpmp_rpc *rd) { bstr_t *bs; uint8 version; uint8 code; uint16 result = 0; uint16 port; uint32 lifetime; natpmp_rpc_check(rd); /* * We expect the following reply to a mapping request: * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Vers = 0 | OP = 128 + x | Result Code | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Seconds Since Start of Epoch | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Internal Port | Mapped External Port | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Port Mapping Lifetime in Seconds | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ bs = bstr_open(payload, len, GNET_PROPERTY(natpmp_debug) ? BSTR_F_ERROR : 0); /* * Make sure we got a valid reply. */ bstr_read_u8(bs, &version); bstr_read_u8(bs, &code); bstr_read_be16(bs, &result); if (bstr_has_error(bs)) goto error; if (GNET_PROPERTY(natpmp_debug) > 5) { g_debug("NATPMP version=%u, code=%u, result_code=%u (%s)", version, code, result, natpmp_strerror(result)); } if (version != NATPMP_VERSION || code != NATPMP_REPLY_OFF + rd->op) goto error; if (NATPMP_E_OK != result) goto failed; /* * We're allowed to parse the remaining of the packet. */ bstr_read_be32(bs, &rd->sssoe); bstr_read_be16(bs, &port); /* Internal port */ if (port != rd->iport) goto error; bstr_read_be16(bs, &port); /* External port */ bstr_read_be32(bs, &lifetime); /* Lease time */ /* * Signal success, if needed. */ if (GNET_PROPERTY(natpmp_debug) > 1) { g_debug("NATPMP %spublished NAT-PMP mapping for %s port %u", 0 == lifetime ? "un-" : "", NATPMP_OP_MAP_TCP == rd->op ? "TCP" : "UDP", rd->iport); } if (rd->cb.map != NULL) (*rd->cb.map)(result, port, lifetime, rd->arg); bstr_free(&bs); return TRUE; /* OK */ failed: if (GNET_PROPERTY(natpmp_debug)) g_warning("NATPMP unable to publish NAT-PMP mapping: %s", natpmp_strerror(result)); if (rd->cb.map != NULL) (*rd->cb.map)(result, 0, 0, rd->arg); return TRUE; /* We're done for now */ error: if (GNET_PROPERTY(natpmp_debug)) { if (bstr_has_error(bs)) { g_warning("NATPMP parsing error while processing discovery reply " "(%zu byte%s): %s", len, 1 == len ? "" : "s", bstr_error(bs)); } else { g_warning("NATPMP inconsistent discovery reply (%zu byte%s)", len, 1 == len ? "" : "s"); } } bstr_free(&bs); return FALSE; }
/** * Handle reply to a discovery request. * * @param payload the received reply * @param len length of reply * @param rd the RPC request descriptor * * @return TRUE if we successfully processed the reply and notified the * user code about the outcome of the request, FALSE if we need to resend * the request. */ static bool natpmp_handle_discovery_reply( const void *payload, size_t len, struct natpmp_rpc *rd) { bstr_t *bs; uint8 version; uint8 code; uint16 result; uint32 ip; host_addr_t wan_ip; natpmp_t *np; natpmp_rpc_check(rd); /** * A NAT gateway will reply with the following message: * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Vers = 0 | OP = 128 + 0 | Result Code | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Seconds Since Start of Epoch | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | External IP Address (a.b.c.d) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * The first 32-bits are always present, the remaining of the packet * may or may not be there depending on the result code. */ bs = bstr_open(payload, len, GNET_PROPERTY(natpmp_debug) ? BSTR_F_ERROR : 0); /* * Make sure we got a valid reply. */ bstr_read_u8(bs, &version); bstr_read_u8(bs, &code); bstr_read_be16(bs, &result); if (bstr_has_error(bs)) goto error; if (GNET_PROPERTY(natpmp_debug) > 5) { g_debug("NATPMP version=%u, code=%u, result_code=%u (%s)", version, code, result, natpmp_strerror(result)); } if (version != NATPMP_VERSION || code != NATPMP_REPLY_OFF + rd->op) goto error; if (NATPMP_E_OK != result) goto failed; bstr_read_be32(bs, &rd->sssoe); bstr_read_be32(bs, &ip); if (bstr_has_error(bs)) goto error; wan_ip = host_addr_get_ipv4(ip); if (GNET_PROPERTY(natpmp_debug) > 5) { g_debug("NATPMP SSSOE=%u, WAN IP is %s", rd->sssoe, host_addr_to_string(wan_ip)); } if (!host_addr_is_routable(wan_ip)) goto failed; /* * Good, we got a valid reply from the gateway, with a routable WAN IP. */ if (rd->np != NULL) { natpmp_check(rd->np); np = rd->np; natpmp_update(np, rd->sssoe); np->wan_ip = wan_ip; } else { np = natpmp_alloc(rd->gateway, rd->sssoe, wan_ip); } (*rd->cb.discovery)(TRUE, np, rd->arg); bstr_free(&bs); return TRUE; /* OK */ failed: if (GNET_PROPERTY(natpmp_debug)) g_warning("NATPMP did not find any suitable NAT-PMP gateway"); (*rd->cb.discovery)(FALSE, rd->np, rd->arg); return TRUE; /* We're done for now */ error: if (GNET_PROPERTY(natpmp_debug)) { if (bstr_has_error(bs)) { g_warning("NATPMP parsing error while processing discovery reply " "(%zu byte%s): %s", len, 1 == len ? "" : "s", bstr_error(bs)); } else { g_warning("NATPMP inconsistent discovery reply (%zu byte%s)", len, 1 == len ? "" : "s"); } } bstr_free(&bs); return FALSE; }