~rcr/rirc

7bc8d185404a984f9218e2b4946f0dff6200ed6c — Richard Robbins 1 year, 4 months ago 54d28bc
fix disconnect callbacks on ping timeout
3 files changed, 104 insertions(+), 84 deletions(-)

M src/io.c
M src/io.h
M src/utils/utils.h
M src/io.c => src/io.c +67 -54
@@ 79,6 79,9 @@
		PT_UL(&cb_mutex);   \
	} while (0)

/* state transition */
#define ST_X(OLD, NEW) (((OLD) << 3) | (NEW))

#define io_cb_cxed(C)        PT_CB(IO_CB_CXED, (C)->obj)
#define io_cb_dxed(C)        PT_CB(IO_CB_DXED, (C)->obj)
#define io_cb_err(C, ...)    PT_CB(IO_CB_ERR, (C)->obj, __VA_ARGS__)


@@ 119,6 122,7 @@ struct connection
	mbedtls_ssl_context ssl_ctx;
	pthread_mutex_t mtx;
	pthread_t tid;
	unsigned ping;
	unsigned rx_sleep;
};



@@ 472,11 476,10 @@ io_state_cxng(struct connection *cx)
	if ((cert_ret = mbedtls_ssl_get_verify_result(&(cx->ssl_ctx))) != 0) {
		if (mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), "", cert_ret) <= 0) {
			io_cb_err(cx, " ... failed to verify cert: unknown failure");
			goto error_ssl;
		} else {
			io_cb_err(cx, " ... failed to verify cert: %s", vrfy_buf);
			goto error_ssl;
		}
		goto error_ssl;
	}

	io_cb_info(cx, " ... SSL connection established");


@@ 500,7 503,7 @@ static enum io_state_t
io_state_cxed(struct connection *cx)
{
	int ret;
	enum io_state_t st = IO_ST_RXNG;
	enum io_state_t st = IO_ST_CXNG;

	mbedtls_ssl_conf_read_timeout(&(cx->ssl_conf), SEC_IN_MS(IO_PING_MIN));



@@ 510,10 513,8 @@ io_state_cxed(struct connection *cx)
	switch (ret) {
		case MBEDTLS_ERR_SSL_WANT_READ:
		case MBEDTLS_ERR_SSL_WANT_WRITE:
			/* EINTR */
			break;
		case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
			/* Graceful termination */
			break;
		case MBEDTLS_ERR_SSL_TIMEOUT:
			return IO_ST_PING;


@@ 536,15 537,16 @@ io_state_cxed(struct connection *cx)
static enum io_state_t
io_state_ping(struct connection *cx)
{
	int ping = IO_PING_MIN;
	int ret;
	enum io_state_t st = IO_ST_RXNG;
	enum io_state_t st = IO_ST_CXNG;

	mbedtls_ssl_conf_read_timeout(&(cx->ssl_conf), SEC_IN_MS(IO_PING_REFRESH));

	while ((ret = io_cx_read(cx)) <= 0 && ret == MBEDTLS_ERR_SSL_TIMEOUT) {
		if ((ping += IO_PING_REFRESH) < IO_PING_MAX) {
			io_cb_ping_n(cx, ping);
		if ((cx->ping += IO_PING_REFRESH) < IO_PING_MAX) {
			io_cb_ping_n(cx, cx->ping);
		} else {
			break;
		}
	}



@@ 552,13 554,13 @@ io_state_ping(struct connection *cx)
		return IO_ST_CXED;

	switch (ret) {
		case MBEDTLS_ERR_SSL_WANT_READ:         /* io_dx EINTR */
		case MBEDTLS_ERR_SSL_WANT_WRITE:        /* io_dx EINTR */
		case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: /* Graceful termination */
			st = IO_ST_DXED;
		case MBEDTLS_ERR_SSL_WANT_READ:
		case MBEDTLS_ERR_SSL_WANT_WRITE:
			break;
		case MBEDTLS_ERR_SSL_TIMEOUT:
			io_cb_err(cx, "connection timeout (%u)", ping);
		case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
			break;
		case MBEDTLS_ERR_SSL_TIMEOUT: /* read timeout */
			io_cb_err(cx, "connection timeout (%u)", cx->ping);
			break;
		case MBEDTLS_ERR_NET_CONN_RESET:
		case 0:


@@ 589,65 591,76 @@ io_thread(void *arg)
	PT_CF(sigaddset(&sigset, SIGUSR1));
	PT_CF(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL));

	cx->st_new = IO_ST_CXNG;

	for (;;) {
	cx->st_cur = IO_ST_CXNG;

		enum io_state_t st_from;
		enum io_state_t st_to;
	do {
		enum io_state_t st_new;
		enum io_state_t st_old;

		switch (cx->st_cur) {
			case IO_ST_CXED: st_to = io_state_cxed(cx); break;
			case IO_ST_CXNG: st_to = io_state_cxng(cx); break;
			case IO_ST_PING: st_to = io_state_ping(cx); break;
			case IO_ST_RXNG: st_to = io_state_rxng(cx); break;
			case IO_ST_DXED: st_to = IO_ST_INVALID; break;
			case IO_ST_CXED: st_new = io_state_cxed(cx); break;
			case IO_ST_CXNG: st_new = io_state_cxng(cx); break;
			case IO_ST_PING: st_new = io_state_ping(cx); break;
			case IO_ST_RXNG: st_new = io_state_rxng(cx); break;
			default:
				fatal("invalid state: %d", cx->st_cur);
		}

		st_from = cx->st_cur;
		st_old = cx->st_cur;

		PT_LK(&(cx->mtx));

		/* New state set by io_cx/io_dx */
		if (cx->st_new != IO_ST_INVALID) {
			cx->st_cur = st_to = cx->st_new;
			cx->st_new = IO_ST_INVALID;
			cx->st_cur = st_new = cx->st_new;
		} else {
			cx->st_cur = st_to;
			cx->st_cur = st_new;
		}

		PT_UL(&(cx->mtx));

		if (st_from == IO_ST_CXNG && st_to == IO_ST_RXNG)
			io_cb_err(cx, " ... Connection failed -- retrying");

		if (st_from == IO_ST_CXNG && st_to == IO_ST_CXED) {
			io_cb_info(cx, " ... Connection successful");
			io_cb_cxed(cx);
			cx->rx_sleep = 0;
		/* State transitions */
		switch (ST_X(st_old, st_new)) {
			case ST_X(IO_ST_CXED, IO_ST_CXNG): /* F1 */
			case ST_X(IO_ST_PING, IO_ST_CXNG): /* F2 */
				io_cb_dxed(cx);
			case ST_X(IO_ST_DXED, IO_ST_CXNG): /* A1 */
			case ST_X(IO_ST_RXNG, IO_ST_CXNG): /* A2,C */
				break;
			case ST_X(IO_ST_RXNG, IO_ST_DXED): /* B1 */
			case ST_X(IO_ST_CXNG, IO_ST_DXED): /* B2 */
				io_cb_info(cx, "Connection cancelled");
				break;
			case ST_X(IO_ST_CXED, IO_ST_DXED): /* B3 */
			case ST_X(IO_ST_PING, IO_ST_DXED): /* B4 */
				io_cb_info(cx, "Connection closed");
				io_cb_dxed(cx);
				break;
			case ST_X(IO_ST_CXNG, IO_ST_CXED): /* D */
				io_cb_info(cx, " ... Connection successful");
				io_cb_cxed(cx);
				cx->rx_sleep = 0;
				break;
			case ST_X(IO_ST_CXNG, IO_ST_RXNG): /* E */
				io_cb_err(cx, " ... Connection failed -- retrying");
				break;
			case ST_X(IO_ST_CXED, IO_ST_PING): /* G */
				cx->ping = IO_PING_MIN;
				io_cb_ping_1(cx, cx->ping);
				break;
			case ST_X(IO_ST_PING, IO_ST_PING): /* H */
				io_cb_ping_n(cx, cx->ping);
				break;
			case ST_X(IO_ST_PING, IO_ST_CXED): /* I */
				cx->ping = 0;
				io_cb_ping_0(cx, cx->ping);
				break;
			default:
				fatal("BAD ST_X from: %d to: %d", st_old, st_new);
		}

		if ((st_from == IO_ST_RXNG || st_from == IO_ST_CXNG) && st_to == IO_ST_DXED)
			io_cb_info(cx, "Connection cancelled");

		if ((st_from == IO_ST_CXED || st_from == IO_ST_PING) && st_to == IO_ST_DXED)
			io_cb_info(cx, "Connection closed");

		if (st_from == IO_ST_PING && st_to == IO_ST_CXED)
			io_cb_ping_0(cx, 0);

		if (st_from == IO_ST_CXED && st_to == IO_ST_PING)
			io_cb_ping_1(cx, IO_PING_MIN);

		/* Exit the thread */
		if (cx->st_cur == IO_ST_DXED) {
			io_cb_dxed(cx);
			cx->rx_sleep = 0;
			break;
		}
	}
	} while (cx->st_cur != IO_ST_DXED);

	return NULL;
}

M src/io.h => src/io.h +33 -27
@@ 12,33 12,39 @@
 *  - cxed: connected    ~ Socket connected
 *  - ping: timing out   ~ Socket connected, network state in question
 *
 *                            +--------+
 *                 +----(B)-- |  rxng  |
 *                 |          +--------+
 *                 |           |      ^
 *   INIT          |         (A,C)    |
 *    v            |           |     (E)
 *    |            v           v      |
 *    |    +--------+ --(A)-> +--------+
 *    +--> |  dxed  |         |  cxng  | <--+
 *         +--------+ <-(B)-- +--------+    |
 *          ^      ^           |      ^    (F)
 *          |      |          (D)     |     |
 *          |      |           |     (F)    |
 *          |      |           v      |     |
 *          |      |          +--------+    |
 *          |      +----(B)-- |  cxed  |    |
 *          |                 +--------+    |
 *          |                  |      ^     |
 *          |                 (G)     |     |
 *          |                  |     (I)    |
 *          |                  v      |     |
 *          |                 +--------+    |
 *          +-----------(B)-- |  ping  | ---+
 *                            +--------+
 *                             v      ^
 *                             |      |
 *                             +--(H)-+
 *
 *    TODO: how to we label the difference between A, and (A,C) ?
 *          just for the sake of consistency should it be A1, (A2, C)
 *
 *          is the H transition necessary?
 *
 *                             +--------+
 *                 +----(B1)-- |  rxng  |
 *                 |           +--------+
 *                 |            |      ^
 *   INIT          |         (A2,C)    |
 *    v            |            |     (E)
 *    |            v            v      |
 *    |    +--------+ --(A1)-> +--------+
 *    +--> |  dxed  |          |  cxng  | <--+
 *         +--------+ <-(B2)-- +--------+    |
 *          ^      ^            |      ^   (F2)
 *          |      |           (D)     |     |
 *          |      |            |    (F1)    |
 *          |      |            v      |     |
 *          |      |           +--------+    |
 *          |      +----(B3)-- |  cxed  |    |
 *          |                  +--------+    |
 *          |                   |      ^     |
 *          |                  (G)     |     |
 *          |                   |     (I)    |
 *          |                   v      |     |
 *          |                  +--------+    |
 *          +-----------(B4)-- |  ping  | ---+
 *                             +--------+
 *                              v      ^
 *                              |      |
 *                              +--(H)-+
 *
 * This module exposes functions for explicitly directing network
 * state as well declaring callback functions for state transitions

M src/utils/utils.h => src/utils/utils.h +4 -3
@@ 18,15 18,16 @@
#define MESSAGE(TYPE, ...) \
	fprintf(stderr, "%s %s:%d:%s ", (TYPE), __FILE__, __LINE__, __func__); \
	fprintf(stderr, __VA_ARGS__); \
	fprintf(stderr, "\n");
	fprintf(stderr, "\n"); \
	fflush(stderr);

#if !(defined NDEBUG) && !(defined TESTING)
#define debug(...) \
	do { MESSAGE("DEBUG", __VA_ARGS__); } while (0)
#define debug_send(L, M) \
	do { fprintf(stderr, "DEBUG (--> %3zu) %s\n", (L), (M)); } while (0)
	do { fprintf(stderr, "DEBUG (--> %3zu) %s\n", (L), (M)); fflush(stderr); } while (0)
#define debug_recv(L, M) \
	do { fprintf(stderr, "DEBUG (<-- %3zu) %s\n", (L), (M)); } while (0)
	do { fprintf(stderr, "DEBUG (<-- %3zu) %s\n", (L), (M)); fflush(stderr); } while (0)
#else
#define debug(...) \
	do { ; } while (0)