/** * Build a mapping message. * * This is used for mapping and unmapping request: in the latter case, the * external port and the lease time are set to 0 in the message. * * @param op the NAT-PMP mapping operation * @param port internal port, to be mapped to same external port * @param lease requested lease time (0 for deletions) */ static pmsg_t * natpmp_build_mapping(enum natpmp_op op, uint16 port, time_delta_t lease) { pmsg_t *mb; /* * The framing is done thusly: * * 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 = x | Reserved (MUST be zero) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Internal Port | Requested External Port | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Requested Port Mapping Lifetime in Seconds | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * x = 1 for TCP, x = 2 for UDP. */ mb = pmsg_new(PMSG_P_DATA, NULL, 12); pmsg_write_u8(mb, NATPMP_VERSION); pmsg_write_u8(mb, op & 0xff); pmsg_write_be16(mb, 0); pmsg_write_be16(mb, port); if (lease != 0) { pmsg_write_be16(mb, port); /* Request same port */ } else { pmsg_write_be16(mb, 0); /* Must be zero on deletions */ } pmsg_write_be32(mb, lease); return mb; }
/** * Start a "discovery rpc" sequence. * * @param np existing NAT-PMP gateway (NULL if unknown yet) * @param retries amount of retries before timeouting * @param cb callback to invoke on completion / timeout * @param arg user-defined callback argument */ static void natpmp_rpc_discover(natpmp_t *np, unsigned retries, natpmp_discover_cb_t cb, void *arg) { struct natpmp_rpc *rd; host_addr_t addr; pmsg_t *mb; if (np != NULL) { natpmp_check(np); addr = np->gateway; } else { /* * If we can't determine the default gateway, we can't go much further. * We notify of the discovery failure synchronously. */ if (0 != getgateway(&addr)) { if (GNET_PROPERTY(natpmp_debug)) g_warning("NATPMP cannot find default gateway"); (*cb)(FALSE, NULL, arg); return; } else { if (GNET_PROPERTY(natpmp_debug)) { g_info("NATPMP gateway is %s", host_addr_to_string(addr)); } } } /* * Build the discovery request: * * 0 1 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Vers = 0 | OP = 0 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ mb = pmsg_new(PMSG_P_DATA, NULL, 2); pmsg_write_u8(mb, NATPMP_VERSION); pmsg_write_u8(mb, NATPMP_OP_DISCOVERY); /* * Initiate asynchronous iteration discovery. */ rd = natpmp_rpc_alloc(np, addr, NATPMP_OP_DISCOVERY, mb); rd->cb.discovery = cb; rd->arg = arg; if (retries != 0) rd->retries = MIN(retries, rd->retries); cq_main_insert(1, natpmp_rpc_iterate, rd); }
/** * Serialization routine for keydata. */ static void serialize_keydata(pmsg_t *mb, const void *data) { const struct keydata *kd = data; int i; g_assert(kd->values <= MAX_VALUES); pmsg_write_u8(mb, KEYS_KEYDATA_VERSION); pmsg_write_u8(mb, kd->values); for (i = 0; i < kd->values; i++) { pmsg_write(mb, &kd->creators[i], sizeof(kd->creators[i])); pmsg_write(mb, &kd->dbkeys[i], sizeof(kd->dbkeys[i])); pmsg_write_time(mb, kd->expire[i]); } }
/** * Serialization routine for lifedata. */ static void serialize_lifedata(pmsg_t *mb, gconstpointer data) { const struct lifedata *ld = data; pmsg_write_u8(mb, LIFEDATA_STRUCT_VERSION); pmsg_write_time(mb, ld->first_seen); pmsg_write_time(mb, ld->last_seen); }
/** * Serialization routine for tokdata. */ static void serialize_tokdata(pmsg_t *mb, const void *data) { const struct tokdata *td = data; pmsg_write_time(mb, td->last_update); pmsg_write_u8(mb, td->length); pmsg_write(mb, td->token, td->length); }
/** * Serialization routine for guiddata. */ static void serialize_guiddata(pmsg_t *mb, const void *data) { const struct guiddata *gd = data; pmsg_write_u8(mb, GUID_DATA_VERSION); pmsg_write_time(mb, gd->create_time); pmsg_write_time(mb, gd->last_time); }
/** * Write an IPv4 or IPv6 address. */ void pmsg_write_ipv4_or_ipv6_addr(pmsg_t *mb, host_addr_t addr) { g_assert(pmsg_is_writable(mb)); /* Not shared, or would corrupt data */ g_assert(pmsg_available(mb) >= 17); switch (host_addr_net(addr)) { case NET_TYPE_IPV4: pmsg_write_u8(mb, 4); pmsg_write_be32(mb, host_addr_ipv4(addr)); break; case NET_TYPE_IPV6: pmsg_write_u8(mb, 16); pmsg_write(mb, host_addr_ipv6(&addr), 16); break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: g_error("unexpected address in pmsg_write_ipv4_or_ipv6_addr(): %s", host_addr_to_string(addr)); } }
/** * Write an unsigned 64-bit quantity using variable length encoding (little * endian). Each serialized byte contains 7 bits, the highest bit is set when * this is the last byte of the encoded value. */ void pmsg_write_ule64(pmsg_t *mb, uint64 v) { uint64 value = v; g_assert(pmsg_is_writable(mb)); /* Not shared, or would corrupt data */ g_assert(pmsg_available(mb) >= 10); /* Will need 10 bytes at most */ do { uint8 byt = (uint8) (value & 0x7f); /* Lowest 7 bits */ value >>= 7; if (0 == value) { byt |= 0x80; /* Last byte emitted */ } pmsg_write_u8(mb, byt); } while (value != 0); }
/** * Serialization routine for pubdata. */ static void serialize_pubdata(pmsg_t *mb, const void *data) { const struct pubdata *pd = data; pmsg_write_time(mb, pd->next_enqueue); pmsg_write_time(mb, pd->expiration); /* * Because this is persistent, version the structure so that changes * can be processed efficiently after an upgrade. * * This is done here and not at the beginning of the serialized data * because I forgot to plan for it before. * --RAM, 2009-10-18 */ pmsg_write_u8(mb, PUBDATA_STRUCT_VERSION); }