/* * Copyright (C) 2008-2012 Free Software Foundation, Inc. * Copyright (C) 2018-2020 Red Hat, Inc. * Copyright (C) 2010 Mike Blumenkrantz * * This file is part of GnuTLS. * * GnuTLS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GnuTLS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "../utils.h" #include #include #include #include static int done = 0; EV_P; ev_io remote_w; gnutls_session_t session; static const char *SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_description_t status) { return gnutls_handshake_description_get_name(status); } /* Connects to the peer and returns a socket * descriptor. */ static int tcp_connect(void) { const char *PORT = getenv("PORT"); const char *SERVER = "127.0.0.1"; //verisign.com int err, sd; int flag = 1, curstate = 0; struct sockaddr_in sa; /* sets some fd options such as nonblock */ sd = socket(AF_INET, SOCK_STREAM, 0); setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate, sizeof(curstate)); memset(&sa, '\0', sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(atoi(PORT)); inet_pton(AF_INET, SERVER, &sa.sin_addr); err = connect(sd, (struct sockaddr *)&sa, sizeof(sa)); if ((err < 0) && (errno != EINPROGRESS)) { fprintf(stderr, "Connect error\n"); exit(1); } /* lower the send buffers to force EAGAIN */ assert(setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) >= 0); assert(fcntl(sd, F_SETFL, O_NONBLOCK)>=0); return sd; } static void tcp_close(int sd) { shutdown(sd, SHUT_RDWR); /* no more receptions */ close(sd); } /* We provide this helper to ensure that we test EAGAIN while writing * even on a reliable connection */ static ssize_t _client_push(gnutls_transport_ptr_t tr, const void *data, size_t len) { struct timeval tv; assert(gettimeofday(&tv, NULL) >= 0); if (tv.tv_usec % 2 == 0) { errno = EAGAIN; return -1; } return send((unsigned long)tr, data, len, 0); } static ssize_t _client_pull(gnutls_transport_ptr_t tr, void *data, size_t len) { struct timeval tv; assert(gettimeofday(&tv, NULL) >= 0); if (tv.tv_usec % 2 == 0) { errno = EAGAIN; return -1; } return recv((unsigned long)tr, data, len, 0); } static int _client_pull_timeout(gnutls_transport_ptr_t ptr, unsigned int ms) { return gnutls_system_recv_timeout(ptr, ms); } static void _process_data(EV_P_ ev_io * w, int revents) { static int ret = -1, lastret = 0; static unsigned int count = 0; static int prev_direction; if (!done && (revents & (EV_WRITE|EV_READ))) { if (lastret == GNUTLS_E_AGAIN) { if (revents & EV_WRITE) { assert(prev_direction == 1); } if (revents & EV_READ) { assert(prev_direction == 0); } } lastret = ret; ret = gnutls_handshake(session); count++; if (gnutls_record_get_direction(session)) { ev_io_stop(EV_A_ &remote_w); ev_io_set(&remote_w, gnutls_transport_get_int(session), EV_WRITE); ev_io_start(EV_A_ &remote_w); prev_direction = 1; } else { ev_io_stop(EV_A_ &remote_w); ev_io_set(&remote_w, gnutls_transport_get_int(session), EV_READ); ev_io_start(EV_A_ &remote_w); prev_direction = 0; } /* avoid printing messages infinity times */ if (lastret != ret && ret != 0 && ret != GNUTLS_E_AGAIN) { fprintf(stderr, "gnutls returned with: %s - %s\n", gnutls_strerror_name(ret), gnutls_strerror(ret)); if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED) || (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)) fprintf(stderr, "Also received alert: %s\n", gnutls_alert_get_name (gnutls_alert_get(session))); fprintf(stderr, "last out: %s\n", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS (gnutls_handshake_get_last_out(session))); fprintf(stderr, "last in: %s\n", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS (gnutls_handshake_get_last_in(session))); } if (gnutls_error_is_fatal(ret)) { fprintf(stderr, "yarrr this be an error!"); exit(1); } } if (ret == GNUTLS_E_SUCCESS) { count = 0; ret = -1; done = 1; lastret = 0; ev_io_stop(EV_A_ & remote_w); } return; } static void try(const char *name, const char *prio) { gnutls_certificate_credentials_t c_certcred; int sd, i, ret; global_init(); gnutls_certificate_allocate_credentials(&c_certcred); printf("%s: testing priority %s\n", name, prio); loop = EV_DEFAULT; for (i = 0; i < 4; i++) { done = 0; assert(gnutls_init(&session, GNUTLS_CLIENT) >= 0); gnutls_transport_set_push_function(session, _client_push); gnutls_transport_set_pull_function(session, _client_pull); gnutls_transport_set_pull_timeout_function(session, _client_pull_timeout); gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); assert(gnutls_priority_set_direct(session, prio, NULL) >= 0); gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, c_certcred); gnutls_server_name_set(session, GNUTLS_NAME_DNS, "localhost", strlen("localhost")); sd = tcp_connect(); /* associate gnutls with socket */ gnutls_transport_set_int(session, sd); /* add a callback for data being available for send/receive on socket */ ev_io_init(&remote_w, _process_data, sd, EV_WRITE); ev_io_start(EV_A_ & remote_w); /* begin main loop */ ev_loop(EV_A_ 0); do { ret = gnutls_bye(session, GNUTLS_SHUT_RDWR); } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); gnutls_deinit(session); session = NULL; tcp_close(sd); } ev_loop_destroy(loop); gnutls_certificate_free_credentials(c_certcred); return; } int main(void) { try("tls 1.2 (dhe)", "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.2:-KX-ALL:+DHE-RSA"); try("tls 1.2 (rsa)", "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.2:-KX-ALL:+RSA"); try("tls 1.3", "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3"); try("default", "NORMAL"); return 0; }