Ejemplo n.º 1
0
/**
 * 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);
	}
}
Ejemplo n.º 2
0
/**
 * 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;
}
Ejemplo n.º 3
0
/**
 * 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);
}
Ejemplo n.º 4
0
/**
 * 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;
}
Ejemplo n.º 5
0
/**
 * 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 */
}
Ejemplo n.º 6
0
/**
 * 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;
}
Ejemplo n.º 7
0
/**
 * 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;
}