/** * Read value from database file, returning a pointer to the allocated * deserialized data. These data can be modified freely and stored back, * but their lifetime will not exceed that of the next call to a dbmw * operation on the same descriptor. * * User code does not need to bother with freeing the allocated data, this * is managed directly by the DBM wrapper. * * @param dw the DBM wrapper * @param key the key (constant-width, determined at open time) * @param lenptr if non-NULL, writes length of (deserialized) value * * @return pointer to value, or NULL if it was either not found or the * deserialization failed. */ G_GNUC_HOT gpointer dbmw_read(dbmw_t *dw, gconstpointer key, size_t *lenptr) { struct cached *entry; dbmap_datum_t dval; dbmw_check(dw); g_assert(key); dw->r_access++; entry = map_lookup(dw->values, key); if (entry) { dw->r_hits++; if (lenptr) *lenptr = entry->len; return entry->data; } /* * Not cached, must read from DB. */ dw->ioerr = FALSE; dval = dbmap_lookup(dw->dm, key); if (dbmap_has_ioerr(dw->dm)) { dw->ioerr = TRUE; dw->error = errno; g_warning("DBMW \"%s\" I/O error whilst reading entry: %s", dw->name, dbmap_strerror(dw->dm)); return NULL; } else if (NULL == dval.data) return NULL; /* Not found in DB */ /* * Value was found, allocate a cache entry object for it. */ WALLOC0(entry); /* * Deserialize data if needed. */ if (dw->unpack) { /* * Allocate cache entry arena to hold the deserialized version. */ entry->data = walloc(dw->value_size); entry->len = dw->value_size; bstr_reset(dw->bs, dval.data, dval.len, BSTR_F_ERROR); if (!dbmw_deserialize(dw, dw->bs, entry->data, dw->value_size)) { g_carp("DBMW \"%s\" deserialization error in %s(): %s", dw->name, stacktrace_routine_name(func_to_pointer(dw->unpack), FALSE), bstr_error(dw->bs)); /* Not calling value free routine on deserialization failures */ wfree(entry->data, dw->value_size); WFREE(entry); return NULL; } if (lenptr) *lenptr = dw->value_size; } else { g_assert(dw->value_size >= dval.len); if (dval.len) { entry->len = dval.len; entry->data = wcopy(dval.data, dval.len); } else { entry->data = NULL; entry->len = 0; } if (lenptr) *lenptr = dval.len; } g_assert((entry->len != 0) == (entry->data != NULL)); /* * Insert into cache. */ (void) allocate_entry(dw, key, entry); return entry->data; }
/** * Common code for dbmw_foreach_trampoline() and * dbmw_foreach_remove_trampoline(). */ static gboolean dbmw_foreach_common(gboolean removing, gpointer key, dbmap_datum_t *d, gpointer arg) { struct foreach_ctx *ctx = arg; dbmw_t *dw = ctx->dw; struct cached *entry; dbmw_check(dw); entry = map_lookup(dw->values, key); if (entry != NULL) { /* * Key / value pair is present in the cache. * * This affects us in two ways: * * - We may already know that the key was deleted, in which case * that entry is just skipped: no further access is possible * through DBMW until that key is recreated. We still return * TRUE to make sure the lower layers will delete the entry * physically, since deletion has not been flushed yet (that's * the reason we're still iterating on it). * * - Should the cached key need to be deleted (as determined by * the user callback, we make sure we delete the entry in the * cache upon callback return). */ entry->traversed = TRUE; /* Signal we iterated on cached value */ if (entry->absent) return TRUE; /* Key was already deleted, info cached */ if (removing) { gboolean status; status = (*ctx->u.cbr)(key, entry->data, entry->len, ctx->arg); if (status) { entry->removable = TRUE; /* Discard it after traversal */ } return status; } else { (*ctx->u.cb)(key, entry->data, entry->len, ctx->arg); return FALSE; } } else { gboolean status = FALSE; gpointer data = d->data; size_t len = d->len; /* * Deserialize data if needed, but do not cache this value. * Iterating over the map must not disrupt the cache. */ if (dw->unpack) { len = dw->value_size; data = walloc(len); bstr_reset(dw->bs, d->data, d->len, BSTR_F_ERROR); if (!dbmw_deserialize(dw, dw->bs, data, len)) { g_carp("DBMW \"%s\" deserialization error in %s(): %s", dw->name, stacktrace_routine_name(func_to_pointer(dw->unpack), FALSE), bstr_error(dw->bs)); /* Not calling value free routine on deserialization failures */ wfree(data, len); return FALSE; } } if (removing) { status = (*ctx->u.cbr)(key, data, len, ctx->arg); } else { (*ctx->u.cb)(key, data, len, ctx->arg); } if (dw->unpack) { if (dw->valfree) (*dw->valfree)(data, len); wfree(data, len); } return status; } }
/** * 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; }
/** * 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; }
/** * Read value from database file, returning a pointer to the allocated * deserialized data. These data can be modified freely and stored back, * but their lifetime will not exceed that of the next call to a dbmw * operation on the same descriptor. * * User code does not need to bother with freeing the allocated data, this * is managed directly by the DBM wrapper. * * @param dw the DBM wrapper * @param key the key (constant-width, determined at open time) * @param lenptr if non-NULL, writes length of (deserialized) value * * @return pointer to value, or NULL if it was either not found or the * deserialization failed. */ G_GNUC_HOT void * dbmw_read(dbmw_t *dw, const void *key, size_t *lenptr) { struct cached *entry; dbmap_datum_t dval; dbmw_check(dw); g_assert(key); dw->r_access++; entry = map_lookup(dw->values, key); if (entry) { if (dbg_ds_debugging(dw->dbg, 5, DBG_DSF_CACHING | DBG_DSF_ACCESS)) { dbg_ds_log(dw->dbg, dw, "%s: read cache hit on %s key=%s%s", G_STRFUNC, entry->dirty ? "dirty" : "clean", dbg_ds_keystr(dw->dbg, key, (size_t) -1), entry->absent ? " (absent)" : ""); } dw->r_hits++; if (lenptr) *lenptr = entry->len; return entry->data; } /* * Not cached, must read from DB. */ dw->ioerr = FALSE; dval = dbmap_lookup(dw->dm, key); if (dbmap_has_ioerr(dw->dm)) { dw->ioerr = TRUE; dw->error = errno; s_warning_once_per(LOG_PERIOD_SECOND, "DBMW \"%s\" I/O error whilst reading entry: %s", dw->name, dbmap_strerror(dw->dm)); return NULL; } else if (NULL == dval.data) return NULL; /* Not found in DB */ /* * Value was found, allocate a cache entry object for it. */ WALLOC0(entry); /* * Deserialize data if needed. */ if (dw->unpack) { /* * Allocate cache entry arena to hold the deserialized version. */ entry->data = walloc(dw->value_size); entry->len = dw->value_size; bstr_reset(dw->bs, dval.data, dval.len, BSTR_F_ERROR); if (!dbmw_deserialize(dw, dw->bs, entry->data, dw->value_size)) { s_critical("DBMW \"%s\" deserialization error in %s(): %s", dw->name, stacktrace_function_name(dw->unpack), bstr_error(dw->bs)); /* Not calling value free routine on deserialization failures */ wfree(entry->data, dw->value_size); WFREE(entry); return NULL; } if (lenptr) *lenptr = dw->value_size; } else { g_assert(dw->value_size >= dval.len); if (dval.len) { entry->len = dval.len; entry->data = wcopy(dval.data, dval.len); } else { entry->data = NULL; entry->len = 0; } if (lenptr) *lenptr = dval.len; } g_assert((entry->len != 0) == (entry->data != NULL)); /* * Insert into cache. */ (void) allocate_entry(dw, key, entry); if (dbg_ds_debugging(dw->dbg, 4, DBG_DSF_CACHING)) { dbg_ds_log(dw->dbg, dw, "%s: cached %s key=%s%s", G_STRFUNC, entry->dirty ? "dirty" : "clean", dbg_ds_keystr(dw->dbg, key, (size_t) -1), entry->absent ? " (absent)" : ""); } return entry->data; }