#ifndef GNUTLS_TESTS_EAGAIN_COMMON_H #define GNUTLS_TESTS_EAGAIN_COMMON_H #include #include #include #define min(x,y) ((x)<(y)?(x):(y)) extern const char *side; #ifdef USE_CMOCKA # define failure() fail() # define client_transfer_failure(r) {fprintf(stderr, "client transfer failure: %s\n", gnutls_strerror(r)); fail();} # define server_transfer_failure(r) {fprintf(stderr, "server transfer failure: %s\n", gnutls_strerror(r)); fail();} # define switch_side(str) #else # define failure() fail("Handshake failed\n") # define client_transfer_failure(r) fail("client transfer failure: %s\n", gnutls_strerror(r)) # define server_transfer_failure(r) fail("client transfer failure: %s\n", gnutls_strerror(r)) # define switch_side(str) side = str #endif #define HANDSHAKE_EXPECT(c, s, clierr, serverr) \ sret = cret = GNUTLS_E_AGAIN; \ do \ { \ if (cret == GNUTLS_E_AGAIN) \ { \ switch_side("client"); \ cret = gnutls_handshake (c); \ if (cret == GNUTLS_E_INTERRUPTED) cret = GNUTLS_E_AGAIN; \ } \ if (sret == GNUTLS_E_AGAIN) \ { \ switch_side("server"); \ sret = gnutls_handshake (s); \ if (sret == GNUTLS_E_INTERRUPTED) sret = GNUTLS_E_AGAIN; \ } \ } \ while ((cret == GNUTLS_E_AGAIN || (cret == 0 && sret == GNUTLS_E_AGAIN)) && (sret == GNUTLS_E_AGAIN || (sret == 0 && cret == GNUTLS_E_AGAIN))); \ if ((clierr != -1 && cret != clierr) || (serverr != -1 && sret != serverr)) \ { \ fprintf(stderr, "client[%d]: %s\n", cret, gnutls_strerror(cret)); \ fprintf(stderr, "server[%d]: %s\n", sret, gnutls_strerror(sret)); \ failure(); \ } #define HANDSHAKE(c, s) \ HANDSHAKE_EXPECT(c,s,0,0) #define HANDSHAKE_DTLS_EXPECT(c, s, clierr, serverr) \ sret = cret = GNUTLS_E_AGAIN; \ do \ { \ if (cret == GNUTLS_E_LARGE_PACKET) \ { \ unsigned int mtu = gnutls_dtls_get_mtu(s); \ gnutls_dtls_set_mtu(s, mtu/2); \ } \ if (cret < 0 && gnutls_error_is_fatal(cret) == 0) \ { \ switch_side("client"); \ cret = gnutls_handshake (c); \ } \ if (sret == GNUTLS_E_LARGE_PACKET) \ { \ unsigned int mtu = gnutls_dtls_get_mtu(s); \ gnutls_dtls_set_mtu(s, mtu/2); \ } \ if (sret < 0 && gnutls_error_is_fatal(sret) == 0) \ { \ switch_side("server"); \ sret = gnutls_handshake (s); \ } \ } \ while (((gnutls_error_is_fatal(cret) == 0 && gnutls_error_is_fatal(sret) == 0)) && (cret < 0 || sret < 0)); \ if (cret != clierr || sret != serverr) \ { \ fprintf(stderr, "client: %s\n", gnutls_strerror(cret)); \ fprintf(stderr, "server: %s\n", gnutls_strerror(sret)); \ failure(); \ } #define HANDSHAKE_DTLS(c, s) \ HANDSHAKE_DTLS_EXPECT(c,s,0,0) #define HANDSHAKE(c, s) \ HANDSHAKE_EXPECT(c,s,0,0) #define TRANSFER2(c, s, msg, msglen, buf, buflen, retry_send_with_null) { \ int _ret; \ switch_side("client"); \ _ret = record_send_loop (c, msg, msglen, retry_send_with_null); \ \ if (_ret < 0) client_transfer_failure(_ret); \ \ do \ { \ do \ { \ switch_side("server"); \ _ret = gnutls_record_recv (s, buf, buflen); \ } \ while(_ret == GNUTLS_E_AGAIN); \ if (_ret <= 0) \ { \ server_transfer_failure(_ret); \ } \ else \ { \ transferred += _ret; \ } \ switch_side("server"); \ _ret = record_send_loop (server, msg, msglen, retry_send_with_null); \ if (_ret < 0) server_transfer_failure(_ret); \ do \ { \ switch_side("client"); \ _ret = gnutls_record_recv (client, buf, buflen); \ } \ while(_ret == GNUTLS_E_AGAIN); \ if (_ret <= 0) \ { \ client_transfer_failure(_ret); \ } \ else \ { \ if (msglen != _ret || memcmp (buf, msg, msglen) != 0) \ { \ failure(); \ } \ /* echo back */ \ switch_side("client"); \ _ret = record_send_loop (client, buf, msglen, retry_send_with_null); \ if (_ret < 0) client_transfer_failure(_ret); \ transferred += _ret; \ } \ } \ while (transferred < 70000); \ } #define EMPTY_BUF(s, c, buf, buflen) \ { \ switch_side("client"); int _ret = 0; \ while((_ret == GNUTLS_E_AGAIN && to_server_len > 0) || to_server_len > 0) \ { \ switch_side("server"); \ _ret = gnutls_record_recv (s, buf, buflen); \ } \ if (_ret < 0 && _ret !=GNUTLS_E_AGAIN) \ { \ server_transfer_failure(_ret); \ } \ switch_side("server"); _ret = 0; \ while((to_client_len > 0 && _ret == GNUTLS_E_AGAIN) || to_client_len > 0) \ { \ switch_side("client"); \ _ret = gnutls_record_recv (client, buf, buflen); \ } \ if (_ret < 0 && _ret !=GNUTLS_E_AGAIN) \ { \ client_transfer_failure(_ret); \ } \ } #define TRANSFER(c, s, msg, msglen, buf, buflen) \ TRANSFER2(c, s, msg, msglen, buf, buflen, 0); \ TRANSFER2(c, s, msg, msglen, buf, buflen, 1) static char to_server[64 * 1024]; static size_t to_server_len = 0; static char to_client[64 * 1024]; static size_t to_client_len = 0; #ifdef RANDOMIZE #define RETURN_RND_EAGAIN(session) \ unsigned int rnd = time(0); \ if (rnd++ % 3 == 0) \ { \ gnutls_transport_set_errno (session, EAGAIN); \ return -1; \ } #else #define RETURN_RND_EAGAIN(session) #endif #ifndef IGNORE_PUSH static ssize_t client_push(gnutls_transport_ptr_t tr, const void *data, size_t len) { size_t newlen; RETURN_RND_EAGAIN(tr); len = min(len, sizeof(to_server) - to_server_len); newlen = to_server_len + len; memcpy(to_server + to_server_len, data, len); to_server_len = newlen; #ifdef EAGAIN_DEBUG fprintf(stderr, "eagain: pushed %d bytes to server (avail: %d)\n", (int) len, (int) to_server_len); #endif return len; } #endif static ssize_t client_pull(gnutls_transport_ptr_t tr, void *data, size_t len) { RETURN_RND_EAGAIN(tr); if (to_client_len == 0) { #ifdef EAGAIN_DEBUG fprintf(stderr, "eagain: Not enough data by server (asked for: %d, have: %d)\n", (int) len, (int) to_client_len); #endif gnutls_transport_set_errno((gnutls_session_t) tr, EAGAIN); return -1; } len = min(len, to_client_len); memcpy(data, to_client, len); memmove(to_client, to_client + len, to_client_len - len); to_client_len -= len; #ifdef EAGAIN_DEBUG fprintf(stderr, "eagain: pulled %d bytes by client (avail: %d)\n", (int) len, (int) to_client_len); #endif return len; } static ssize_t server_pull(gnutls_transport_ptr_t tr, void *data, size_t len) { //success ("server_pull len %d has %d\n", len, to_server_len); RETURN_RND_EAGAIN(tr); if (to_server_len == 0) { #ifdef EAGAIN_DEBUG fprintf(stderr, "eagain: Not enough data by client (asked for: %d, have: %d)\n", (int) len, (int) to_server_len); #endif gnutls_transport_set_errno((gnutls_session_t) tr, EAGAIN); return -1; } len = min(len, to_server_len); #ifdef EAGAIN_DEBUG fprintf(stderr, "eagain: pulled %d bytes by server (avail: %d)\n", (int) len, (int) to_server_len); #endif memcpy(data, to_server, len); memmove(to_server, to_server + len, to_server_len - len); to_server_len -= len; return len; } #ifndef IGNORE_PUSH static ssize_t server_push(gnutls_transport_ptr_t tr, const void *data, size_t len) { size_t newlen; RETURN_RND_EAGAIN(tr); // hexprint (data, len); len = min(len, sizeof(to_client) - to_client_len); newlen = to_client_len + len; memcpy(to_client + to_client_len, data, len); to_client_len = newlen; #ifdef EAGAIN_DEBUG fprintf(stderr, "eagain: pushed %d bytes to client (avail: %d)\n", (int) len, (int) to_client_len); #endif #ifdef SERVER_PUSH_ADD SERVER_PUSH_ADD #endif return len; } #endif /* inline is used to avoid a gcc warning if used in mini-eagain */ inline static int server_pull_timeout_func(gnutls_transport_ptr_t ptr, unsigned int ms) { int ret; if (to_server_len > 0) ret = 1; /* available data */ else ret = 0; /* timeout */ #ifdef EAGAIN_DEBUG fprintf(stderr, "eagain: server_pull_timeout: %d (avail: cli %d, serv %d)\n", ret, (int) to_client_len, (int) to_server_len); #endif return ret; } inline static int client_pull_timeout_func(gnutls_transport_ptr_t ptr, unsigned int ms) { int ret; if (to_client_len > 0) ret = 1; else ret = 0; #ifdef EAGAIN_DEBUG fprintf(stderr, "eagain: client_pull_timeout: %d (avail: cli %d, serv %d)\n", ret, (int) to_client_len, (int) to_server_len); #endif return ret; } inline static void reset_buffers(void) { to_server_len = 0; to_client_len = 0; } inline static int record_send_loop(gnutls_session_t session, const void *data, size_t sizeofdata, int use_null_on_retry) { int ret; const void *retry_data; size_t retry_sizeofdata; if (use_null_on_retry) { retry_data = 0; retry_sizeofdata = 0; } else { retry_data = data; retry_sizeofdata = sizeofdata; } ret = gnutls_record_send(session, data, sizeofdata); while (ret == GNUTLS_E_AGAIN) { ret = gnutls_record_send(session, retry_data, retry_sizeofdata); } return ret; } #endif /* GNUTLS_TESTS_EAGAIN_COMMON_H */