/** * Appends `n_bytes' to the pmsg_t buffer. If the last pmsg_t is writable * it is filled with as much data as space is still available. Otherwise * or if this space is not sufficient another pmsg_t is created and * appendded to the list. */ void pmsg_slist_append(slist_t *slist, const void *data, size_t n_bytes) { pmsg_t *mb; g_assert(slist); if (0 == n_bytes) return; g_assert(NULL != data); mb = slist_tail(slist); if (mb && pmsg_is_writable(mb)) { size_t n; n = pmsg_write(mb, data, n_bytes); data = (const char *) data + n; n_bytes -= n; } if (n_bytes > 0) { mb = pmsg_new(PMSG_P_DATA, NULL, MAX(n_bytes, PMSG_SLIST_GROW_MIN)); pmsg_write(mb, data, n_bytes); slist_append(slist, mb); } }
/** * 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); }
/** * Create new message holding serialized tree. * * @param t the tree to serialize * @param prio priority of the message * @param freecb if non-NULL, the free routine to attach to message * @param arg additional argument for the free routine * * @return a message containing the serialized tree. */ static pmsg_t * g2_build_pmsg_prio(const g2_tree_t *t, int prio, pmsg_free_t freecb, void *arg) { size_t len; pmsg_t *mb; len = g2_frame_serialize(t, NULL, 0); if (NULL == freecb) mb = pmsg_new(prio, NULL, len); else mb = pmsg_new_extend(prio, NULL, len, freecb, arg); g2_frame_serialize(t, pmsg_start(mb), len); pmsg_seek(mb, len); g_assert(UNSIGNED(pmsg_size(mb)) == len); return mb; }
/** * Split a buffer at given offset: the data before that offset are left in * the original buffer whilst the data starting at the offset (included) * are moved to a new buffer. The original buffer no longer holds the data * starting at the offset. * * @return new message block containing the data starting at the offset. */ pmsg_t * pmsg_split(pmsg_t *mb, int offset) { int slen; /* Split length */ const char *start; g_assert(offset >= 0); g_assert(offset < pmsg_size(mb)); pmsg_check_consistency(mb); start = mb->m_rptr + offset; slen = mb->m_wptr - start; g_assert(slen > 0); mb->m_wptr -= slen; /* Logically removed */ return pmsg_new(mb->m_prio, start, slen); /* Copies data */ }
/** * Create a new DBM wrapper over already created DB map. * * If value_data_size is 0, the length for value_size is used. * * @param dm The database (already opened) * @param name Database name, for logs * @param value_size Maximum value size, in bytes (structure) * @param value_data_size Maximum value size, in bytes (serialized form) * @param pack Serialization routine for values * @param unpack Deserialization routine for values * @param valfree Free routine for value (or NULL if none needed) * @param cache_size Amount of items to cache (0 = no cache, 1 = default) * @param hash_func Key hash function * @param eq_func Key equality test function * * If serialization and deserialization routines are NULL pointers, data * will be stored and retrieved as-is. In that case, they must be both * NULL. */ dbmw_t * dbmw_create(dbmap_t *dm, const char *name, size_t value_size, size_t value_data_size, dbmw_serialize_t pack, dbmw_deserialize_t unpack, dbmw_free_t valfree, size_t cache_size, GHashFunc hash_func, GEqualFunc eq_func) { dbmw_t *dw; g_assert(pack == NULL || value_size); g_assert((pack != NULL) == (unpack != NULL)); g_assert(valfree == NULL || unpack != NULL); g_assert(dm); WALLOC0(dw); dw->magic = DBMW_MAGIC; dw->dm = dm; dw->name = name; dw->key_size = dbmap_key_size(dm); dw->key_len = dbmap_key_length(dm); dw->value_size = value_size; dw->value_data_size = 0 == value_data_size ? value_size : value_data_size; /* Make sure we do not violate the SDBM constraint */ g_assert(sdbm_is_storable(dw->key_size, dw->value_data_size)); /* * There must be a serialization routine if the serialized length is not * the same as the structure length. */ g_assert(dw->value_size == dw->value_data_size || pack != NULL); /* * For a small amount of items, a PATRICIA tree is more efficient * than a hash table although it uses more memory. */ if ( NULL == dw->key_len && dw->key_size * 8 <= PATRICIA_MAXBITS && cache_size <= DBMW_CACHE ) { dw->values = map_create_patricia(dw->key_size * 8); } else { dw->values = map_create_hash(hash_func, eq_func); } dw->keys = hash_list_new(hash_func, eq_func); dw->pack = pack; dw->unpack = unpack; dw->valfree = valfree; /* * If a serialization routine is provided, we'll also have a need for * deserialization. Allocate the message in/out streams. * * We're allocating one more byte than necessary to be able to check * whether serialization stays within the imposed boundaries. */ if (dw->pack) { dw->bs = bstr_create(); dw->mb = pmsg_new(PMSG_P_DATA, NULL, dw->value_data_size + 1); } /* * If cache_size is zero, we won't cache anything but the latest * value requested, in deserialized form. If modified, it will be * written back immediately. * * If cache_size is one, use the default (DBMW_CACHE). * * Any other value is used as-is. */ if (0 == cache_size) dw->max_cached = 1; /* No cache, only keep latest around */ else if (cache_size == 1) dw->max_cached = DBMW_CACHE; else dw->max_cached = cache_size; if (common_dbg) g_debug("DBMW created \"%s\" with %s back-end " "(max cached = %lu, key=%lu bytes, value=%lu bytes, " "%lu max serialized)", dw->name, dbmw_map_type(dw) == DBMAP_SDBM ? "sdbm" : "map", (gulong) dw->max_cached, (gulong) dw->key_size, (gulong) dw->value_size, (gulong) dw->value_data_size); return dw; }
/** * Initiate a SOAP remote procedure call. * * Call will be launched asynchronously, not immediately upon return so that * callbacks are never called on the same stack frame and to allow further * options to be set on the handle before the call begins. * * Initially the request is sent as a regular POST. It is possible to force * the usage of the HTTP Extension Framework by using the SOAP_RPC_O_MAN_FORCE * option, in which case an M-POST will be sent with the proper SOAP Man: * header. Finally, automatic retry of the request can be requested via the * SOAP_RPC_O_MAN_RETRY option: it will start with POST and switch to M-POST * on 405 or 510 errors. * * @param url the HTTP URL to contact for the RPC * @param action the SOAP action to perform * @param maxlen maximum length of data we accept to receive * @param options user-supplied options * @param xn SOAP RPC data payload (XML tree root, will be freed) * @param soap_ns requested SOAP namespace prefix, NULL to use default * @param reply_cb callback to invoke when we get a reply * @param error_cb callback to invoke on error * @param arg additional user-defined callback parameter * * @return a SOAP RPC handle, NULL if the request cannot be initiated (XML * payload too large). In any case, the XML tree is freed. */ soap_rpc_t * soap_rpc(const char *url, const char *action, size_t maxlen, guint32 options, xnode_t *xn, const char *soap_ns, soap_reply_cb_t reply_cb, soap_error_cb_t error_cb, void *arg) { soap_rpc_t *sr; xnode_t *root, *body; pmsg_t *mb; ostream_t *os; gboolean failed = FALSE; g_assert(url != NULL); g_assert(action != NULL); /* * Create the SOAP XML request. */ root = xnode_new_element(NULL, SOAP_NAMESPACE, SOAP_X_ENVELOPE); xnode_add_namespace(root, soap_ns ? soap_ns : "SOAP", SOAP_NAMESPACE); xnode_prop_ns_set(root, SOAP_NAMESPACE, SOAP_X_ENC_STYLE, SOAP_ENCODING); body = xnode_new_element(root, SOAP_NAMESPACE, SOAP_X_BODY); xnode_add_child(body, xn); /* * Serialize the XML tree to a PDU message buffer. */ mb = pmsg_new(PMSG_P_DATA, NULL, SOAP_MAX_PAYLOAD); os = ostream_open_pmsg(mb); xfmt_tree(root, os, XFMT_O_NO_INDENT); if (!ostream_close(os)) { failed = TRUE; g_warning("SOAP unable to serialize payload within %d bytes", SOAP_MAX_PAYLOAD); if (GNET_PROPERTY(soap_debug) > 1) xfmt_tree_dump(root, stderr); } /* * Free the XML tree, including the supplied user nodes. */ xnode_tree_free(root); if (failed) { pmsg_free(mb); return NULL; } /* * Serialization of the XML payload was successful, prepare the * asynchronous SOAP request. */ sr = soap_rpc_alloc(); sr->url = atom_str_get(url); sr->action = atom_str_get(action); sr->maxlen = maxlen; sr->content_len = maxlen; /* Until we see a Content-Length */ sr->options = options; sr->mb = mb; sr->reply_cb = reply_cb; sr->error_cb = error_cb; sr->arg = arg; /* * Make sure the error callback is not called synchronously, and give * them time to supply other options after creating the request before * it starts. */ sr->delay_ev = cq_main_insert(1, soap_rpc_launch, sr); return sr; }