コード例 #1
0
ファイル: search_cb.c プロジェクト: Longdengyu/gtk-gnutella
static void
selected_row_changed(GtkCTree *ctree)
{
	int row;
	
	if (selected_record) {
		search_gui_unref_record(selected_record);
		selected_record = NULL;
	}

	row = clist_get_cursor_row(GTK_CLIST(ctree));
	if (row >= 0) {
		GtkCTreeNode *node;

		node = gtk_ctree_node_nth(GTK_CTREE(ctree), row);
		selected_record = search_gui_get_record(ctree, GTK_CTREE_NODE(node));
		if (selected_record) {
			search_gui_ref_record(selected_record);
		}
	}

	if (row_selected_ev) {
		cq_resched(row_selected_ev, ROW_SELECT_TIMEOUT);
	} else {
		row_selected_ev = cq_main_insert(ROW_SELECT_TIMEOUT,
							row_selected_expire, NULL);
	}
}
コード例 #2
0
ファイル: tsync.c プロジェクト: MrJoe/gtk-gnutella
/**
 * Send time synchronization request to specified node.
 *
 * When node_id is non-zero, it refers to the connected node to which
 * we're sending the time synchronization request.
 */
void
tsync_send(struct gnutella_node *n, const struct nid *node_id)
{
	struct tsync *ts;

	g_return_if_fail(n->port != 0);
	if (!NODE_IS_WRITABLE(n))
		return;

	WALLOC(ts);
	ts->magic = TSYNC_MAGIC;
	tm_now_exact(&ts->sent);
	ts->sent.tv_sec = clock_loc2gmt(ts->sent.tv_sec);
	ts->node_id = nid_ref(node_id);
	ts->udp = booleanize(NODE_IS_UDP(n));

	/*
	 * As far as time synchronization goes, we must get the reply within
	 * the next TSYNC_EXPIRE_MS millisecs.
	 */

	ts->expire_ev = cq_main_insert(TSYNC_EXPIRE_MS, tsync_expire, ts);

	hevset_insert(tsync_by_time, ts);

	vmsg_send_time_sync_req(n, GNET_PROPERTY(ntp_detected), &ts->sent);
}
コード例 #3
0
ファイル: wd.c プロジェクト: MrJoe/gtk-gnutella
/**
 * Start watchdog timer.
 */
static void
wd_start(watchdog_t *wd)
{
	watchdog_check(wd);

	/* watchdog period given in seconds */
	wd->last_kick = 0;
	wd->ev = cq_main_insert(wd->period * 1000, wd_expired, wd);
}
コード例 #4
0
ファイル: natpmp.c プロジェクト: MrJoe/gtk-gnutella
/**
 * 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);
}
コード例 #5
0
ファイル: wq.c プロジェクト: gtk-gnutella/gtk-gnutella
/**
 * Callout queue callback fired when waiting event times out.
 */
static void
wq_timed_out(cqueue_t *cq, void *arg)
{
	wq_event_t *we = arg;
	hash_list_t *hl;
	wq_status_t status;

	wq_event_check(we);
	g_assert(we->tm != NULL);

	cq_zero(cq, &we->tm->timeout_ev);
	hl = htable_lookup(waitqueue, we->key);

	g_assert(hl != NULL);

	/*
	 * Invoke the callback with the sentinel data signalling a timeout.
	 */

	status = (*we->cb)(we->arg, WQ_TIMED_OUT);

	/*
	 * When the callback returns WQ_SLEEP, we re-instantiate the initial
	 * timeout.
	 *
	 * Otherwise the event is discarded (removed from the wait queue) and
	 * the callback will never be invoked again for this event.
	 */

	switch (status) {
	case WQ_SLEEP:
		we->tm->timeout_ev = cq_main_insert(we->tm->delay, wq_timed_out, we);
		return;
	case WQ_EXCLUSIVE:
		s_critical("weird status WQ_EXCLUSIVE on timeout invocation of %s()",
			stacktrace_function_name(we->cb));
		/* FALL THROUGH */
	case WQ_REMOVE:
		hash_list_remove(hl, we);

		/*
		 * Cleanup the table if it ends-up being empty.
		 */

		if (0 == hash_list_length(hl)) {
			hash_list_free(&hl);
			htable_remove(waitqueue, we->key);
		}

		wq_event_free(we);
		return;
	}

	g_assert_not_reached();
}
コード例 #6
0
ファイル: wq.c プロジェクト: gtk-gnutella/gtk-gnutella
/**
 * Record a waiting event, limited in time by a timeout.
 *
 * If no wq_wakeup() occurs before the timeout limit, a wq_wakeup() is forced
 * with the WQ_TIMED_OUT value.  The callback must be prepared to handle
 * this value explicitly.
 *
 * @param key		waiting key
 * @param delay		delay in ms before timeout occurs
 * @param cb		callback to invoke on wakeup
 * @param arg		additional callback argument
 *
 * @return the registered event, whose reference must be kept if it is meant
 * to be cancelled.
 */
wq_event_t *
wq_sleep_timeout(const void *key, int delay, wq_callback_t cb, void *arg)
{
	wq_event_t *we;

	we = wq_sleep(key, cb, arg);

	WALLOC(we->tm);
	we->tm->delay = delay;
	we->tm->timeout_ev = cq_main_insert(delay, wq_timed_out, we);

	return we;
}
コード例 #7
0
ファイル: clock.c プロジェクト: MrJoe/gtk-gnutella
/**
 * Create a value for the `used' table.
 */
static struct used_val *
val_create(const host_addr_t addr, int precision)
{
	struct used_val *v;

	g_assert(is_host_addr(addr));

	WALLOC(v);
	v->addr = addr;
	v->precision = precision;
	v->cq_ev = cq_main_insert(REUSE_DELAY * 1000, val_destroy, v);

	return v;
}
コード例 #8
0
ファイル: soap.c プロジェクト: Haxe/gtk-gnutella
/**
 * HTTP async callback, invoked on errors.
 */
static void
soap_error_ind(http_async_t *ha, http_errtype_t type, void *val)
{
	soap_rpc_t *sr = http_async_get_opaque(ha);
	soap_error_t err = SOAP_E_OK;

	soap_rpc_check(sr);

	if (GNET_PROPERTY(soap_debug)) {
		http_async_log_error_dbg(ha, type, val, "SOAP",
			GNET_PROPERTY(soap_debug) > 1);
	}

	if (HTTP_ASYNC_ERROR == type) {
		switch (GPOINTER_TO_INT(val)) {
		case HTTP_ASYNC_CANCELLED:
			/*
			 * Retry with M-POST if cancelled with sr->retry set to TRUE.
			 */
			if (sr->retry) {
				g_assert(NULL == sr->delay_ev);
				sr->delay_ev = cq_main_insert(1, soap_rpc_launch, sr);

				if (GNET_PROPERTY(soap_debug) > 1) {
					g_message("SOAP \"%s\" at \"%s\": retrying with M-POST",
						sr->action, sr->url);
				}
			}
			break;		/* No callback on explicit user cancel */
		case HTTP_ASYNC_DATA2BIG:
			err = SOAP_E_DATA2BIG;
			break;
		case HTTP_ASYNC_CONN_TIMEOUT:
		case HTTP_ASYNC_TIMEOUT:
			err = SOAP_E_TIMEOUT;
			break;
		default:
			err = SOAP_E_TRANSPORT;
			break;
		}
	} else {
		err = SOAP_E_TRANSPORT;
	}

	if (err != SOAP_E_OK)
		soap_error(sr, err);
}
コード例 #9
0
ファイル: sectoken.c プロジェクト: kyungmin4444/gtk-gnutella
/**
 * Token key rotating event.
 */
static void
sectoken_rotate(cqueue_t *cq, void *obj)
{
	size_t i;
	sectoken_gen_t *stg = obj;

	sectoken_gen_check(stg);

	cq_zero(cq, &stg->rotate_ev);
	stg->rotate_ev = cq_main_insert(stg->refresh * 1000, sectoken_rotate, stg);

	for (i = 0; i < stg->keycnt - 1; i++)
		stg->keys[i + 1] = stg->keys[i];

	/* 0 is most recent key */
	random_strong_bytes(&stg->keys[0], sizeof(stg->keys[0]));
}
コード例 #10
0
/**
 * Send an UDP ping to the host cache.
 */
static void
uhc_send_ping(void)
{
	g_assert(uhc_connecting);

	guid_random_muid(&uhc_ctx.muid);

	if (udp_send_ping(&uhc_ctx.muid, uhc_ctx.addr, uhc_ctx.port, TRUE)) {

		if (GNET_PROPERTY(bootstrap_debug) || GNET_PROPERTY(log_uhc_pings_tx)) {
			g_debug("BOOT sent UDP SCP ping #%s to %s:%u",
				guid_hex_str(&uhc_ctx.muid), uhc_ctx.host, uhc_ctx.port);
		}

		/*
		 * Give GUI feedback.
		 */
		{
			char msg[256];

			str_bprintf(msg, sizeof msg,
				_("Sent ping to UDP host cache %s:%u"),
				uhc_ctx.host, uhc_ctx.port);
			gcu_statusbar_message(msg);
		}

		/*
		 * Arm a timer to see whether we should not try to ping another
		 * host cache if we don't get a timely reply.
		 */

		g_assert(uhc_ctx.timeout_ev == NULL);

		uhc_ctx.timeout_ev = cq_main_insert(UHC_TIMEOUT,
			uhc_ping_timeout, NULL);
	} else {
		g_warning("BOOT failed to send UDP SCP to %s",
			host_addr_port_to_string(uhc_ctx.addr, uhc_ctx.port));
		uhc_try_next();
	}
}
コード例 #11
0
ファイル: sectoken.c プロジェクト: qgewfg/gtk-gnutella
/**
 * Create a new security token generator.
 */
sectoken_gen_t *
sectoken_gen_new(size_t keys, time_delta_t refresh)
{
	sectoken_gen_t *stg;
	size_t i;

	g_assert(size_is_positive(keys));

	WALLOC0(stg);
	stg->magic = SECTOKEN_GEN_MAGIC;
	stg->keys = walloc(keys * sizeof stg->keys[0]);
	stg->keycnt = keys;
	stg->refresh = refresh;

	for (i = 0; i < stg->keycnt; i++)
		random_bytes(&stg->keys[i], sizeof(stg->keys[0]));

	stg->rotate_ev = cq_main_insert(refresh * 1000, sectoken_rotate, stg);

	return stg;
}
コード例 #12
0
ファイル: natpmp.c プロジェクト: MrJoe/gtk-gnutella
/**
 * Request port mapping or deletion.
 *
 * @param np		the NAT-PMP gateway to which we publish the mapping
 * @param proto		protocol type (TCP or UDP)
 * @param port		internal port, to be mapped to same external port
 * @param lease		requested lease time (0 for deletion)
 * @param cb		completion callback
 * @param arg		additional callback argument
 */
static void
natpmp_rpc_map(natpmp_t *np, enum upnp_map_proto proto, uint16 port,
	time_delta_t lease, natpmp_map_cb_t cb, void *arg)
{
	pmsg_t *mb;
	enum natpmp_op op = NATPMP_OP_INVALID;
	struct natpmp_rpc *rd;

	natpmp_check(np);

	switch (proto) {
	case UPNP_MAP_TCP: op = NATPMP_OP_MAP_TCP; break;
	case UPNP_MAP_UDP: op = NATPMP_OP_MAP_UDP; break;
	case UPNP_MAP_MAX: g_assert_not_reached();
	}
	g_assert(NATPMP_OP_INVALID != op);

	/*
	 * Creating the mapping message.
	 */

	mb = natpmp_build_mapping(op, port, lease);

	/*
	 * Initiate asynchronous publishing only when there is a user callback.
	 */

	rd = natpmp_rpc_alloc(np, np->gateway, op, mb);
	rd->cb.map = cb;
	rd->arg = arg;
	rd->iport = port;	/* We only accept same internal and external ports */

	if (NULL == cb) {
		natpmp_rpc_iterate(NULL, rd);		/* Synchronous */
	} else {
		cq_main_insert(1, natpmp_rpc_iterate, rd);
	}
}
コード例 #13
0
ファイル: rpc.c プロジェクト: Eppo791906066/gtk-gnutella
/**
 * Start a G2 RPC with the specified host.
 *
 * @param host		the host to which message is sent
 * @param mb		the message to send
 * @param cb		if non-NULL, callback to invoke on reply or timeout
 * @param arg		additional callback argument
 * @param timeout	amount of seconds before timeout
 *
 * @return TRUE if we initiated the RPC, FALSE if another of the same
 * kind was already in progress with the host.
 */
bool
g2_rpc_launch(const gnet_host_t *host, pmsg_t *mb,
	g2_rpc_cb_t cb, void *arg, unsigned timeout)
{
	struct g2_rpc *gr;
	struct g2_rpc_key key;
	gnutella_node_t *n;

	key.type = g2_msg_type_mb(mb);
	key.addr = gnet_host_get_addr(host);

	/*
	 * Because there is no MUID in /PI and /QKR messages, we cannot use that
	 * as a key to detect the RPC reply.  Therefore, we use the message type
	 * and the IP address of the host.  When a /PO or /QKA comes back, we'll
	 * be able to see whether we had a pending RPC from that host for that
	 * type of transaction.
	 *
	 * The downside is that we can only have one pending RPC at a time of
	 * a given kind towards a given IP address.  We don't use the port in
	 * the key because we cannot assume the reply will come from the same port
	 * we sent the message to, if the remote host is behind NAT or does not
	 * use its listening UDP socket to reply.
	 */

	if (hevset_contains(g2_rpc_pending, &key)) {
		if (GNET_PROPERTY(g2_rpc_debug)) {
			g_debug("%s(): cannot issue /%s RPC to %s: concurrent request",
				G_STRFUNC, g2_msg_type_name(key.type),
				gnet_host_to_string(host));
		}

		return FALSE;
	}

	/*
	 * Make sure the node is valid.
	 */

	n = node_udp_g2_get_addr_port(key.addr, gnet_host_get_port(host));

	if (NULL == n) {
		if (GNET_PROPERTY(g2_rpc_debug)) {
			g_debug("%s(): cannot issue /%s RPC to %s: cannot get G2 node",
				G_STRFUNC, g2_msg_type_name(key.type),
				gnet_host_to_string(host));
		}

		return FALSE;		/* Invalid node, or G2 disabled */
	}

	/*
	 * Good, we can issue the RPC.
	 */

	WALLOC(gr);
	gr->magic = G2_RPC_MAGIC;
	gr->key = key;				/* struct copy */
	gr->cb = cb;
	gr->arg = arg;
	gr->timeout_ev = cq_main_insert(timeout * 1000, g2_rpc_timeout, gr);

	hevset_insert(g2_rpc_pending, gr);

	if (GNET_PROPERTY(g2_rpc_debug) > 1) {
		g_debug("%s(): issuing /%s RPC to %s, timeout %u sec%s",
			G_STRFUNC, g2_msg_type_name(key.type),
			gnet_host_to_string(host), timeout, plural(timeout));
	}

	/*
	 * Do not send RPCs reliably: this can cause problems if we don't receive
	 * the ACK backm yet the message was received and processed remotely: the
	 * remote host will send a reply back and the message will still appear to
	 * be "unsent" locally.
	 *
	 * Furthermore, this alleviates the need for the remote side to actually
	 * acknowledge the request: targeted hosts can be busy so it's best to
	 * make the RPC "unreliable" to limit processing and bandwidth requirements.
	 */

	g2_node_send(n, mb);

	return TRUE;
}
コード例 #14
0
ファイル: keys.c プロジェクト: lucab/gtk-gnutella
static void
install_periodic_kball(int period)
{
	kball_ev = cq_main_insert(period * 1000, keys_periodic_kball, NULL);
}
コード例 #15
0
ファイル: soap.c プロジェクト: Haxe/gtk-gnutella
/**
 * 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;
}
コード例 #16
0
ファイル: urpc.c プロジェクト: qgewfg/gtk-gnutella
/**
 * Initiate an UDP RPC transaction.
 *
 * The message held in ``data'' is sent to the specified address and port.
 * Upon reception of a reply from that host, the callback is invoked.
 * If no reply is received after some time, the callaback is also invoked.
 *
 * @param what		type of RPC, for logging (static string)
 * @param addr		address where RPC should be sent to
 * @param port		port where RPC should be sent to
 * @param data		message data to send
 * @param len		length of data to send
 * @param timeout	timeout in milliseconds to get a reply
 * @param cb		callback to invoke on reply or timeout
 * @param arg		additionnal callback argument
 *
 * @return 0 if OK, -1 if we could not initiate the RPC, with errno set.
 */
int
urpc_send(const char *what,
	host_addr_t addr, uint16 port, const void *data, size_t len,
	unsigned long timeout, urpc_cb_t cb, void *arg)
{
	struct urpc_cb *ucb;
	struct gnutella_socket *s;
	host_addr_t bind_addr = zero_host_addr;
	gnet_host_t to;
	ssize_t r;

	/*
	 * Create anonymous socket to send/receive the RPC.
	 */

	switch (host_addr_net(addr)) {
	case NET_TYPE_IPV4:
		bind_addr = ipv4_unspecified;
		break;
	case NET_TYPE_IPV6:
		bind_addr = ipv6_unspecified;
		break;
	case NET_TYPE_LOCAL:
	case NET_TYPE_NONE:
		g_assert_not_reached();
	}

	s = socket_udp_listen(bind_addr, 0, urpc_received);
	if (NULL == s) {
		if (GNET_PROPERTY(udp_debug)) {
			g_warning("unable to create anonymous UDP %s socket for %s RPC: %m",
				net_type_to_string(host_addr_net(bind_addr)), what);
		}
		return -1;
	}

	/*
	 * Send the message.
	 */

	gnet_host_set(&to, addr, port);
	r = (s->wio.sendto)(&s->wio, &to, data, len);

	/*
	 * Reset errno if there was no "real" error to prevent getting a
	 * bogus and possibly misleading error message later.
	 */

	if ((ssize_t) -1 == r) {
		if (GNET_PROPERTY(udp_debug)) {
			g_warning("unable to send UDP %s RPC to %s: %m",
				what, host_addr_port_to_string(addr, port));
		}
	} else {
		errno = 0;
	}

	if (len != UNSIGNED(r)) {
		if ((ssize_t) -1 != r) {
			if (GNET_PROPERTY(udp_debug)) {
				g_warning("unable to send whole %zu-byte UDP %s RPC to %s: "
					"only sent %zu byte%s",
					len, what, host_addr_port_to_string(addr, port),
					r, 1 == r ? "" : "s");
			}
		}
		socket_free_null(&s);
		errno = EIO;
		return -1;
	}

	/*
	 * Make sure socket_udp_event() will only process replies one at a time
	 * since we're going to close the anonymous UDP socket as soon as we
	 * get a reply.
	 */

	socket_set_single(s, TRUE);

	/*
	 * Message was sent, wait for the answer.
	 */

	WALLOC(ucb);
	ucb->magic = URPC_CB_MAGIC;
	ucb->addr = addr;
	ucb->port = port;
	ucb->s = s;
	ucb->cb = cb;
	ucb->arg = arg;
	ucb->timeout_ev = cq_main_insert(timeout, urpc_timed_out, ucb);
	ucb->what = what;

	htable_insert(pending, s, ucb);

	return 0;
}