int main(int argc, char *const *argv) { struct addrinfo hints, *ai_list, *ai; int r, sockfd = 0; ssize_t session_state_length = 0; uint8_t *session_state = NULL; /* Optional args */ const char *alpn_protocols = NULL; const char *server_name = NULL; const char *ca_file = NULL; const char *ca_dir = NULL; uint16_t mfl_value = 0; uint8_t insecure = 0; int reconnect = 0; uint8_t session_ticket = 1; s2n_status_request_type type = S2N_STATUS_REQUEST_NONE; uint32_t dyn_rec_threshold = 0; uint8_t dyn_rec_timeout = 0; /* required args */ const char *cipher_prefs = "default"; const char *host = NULL; struct verify_data unsafe_verify_data; const char *port = "443"; int echo_input = 0; int use_corked_io = 0; static struct option long_options[] = { {"alpn", required_argument, 0, 'a'}, {"ciphers", required_argument, 0, 'c'}, {"echo", required_argument, 0, 'e'}, {"help", no_argument, 0, 'h'}, {"name", required_argument, 0, 'n'}, {"status", no_argument, 0, 's'}, {"mfl", required_argument, 0, 'm'}, {"ca-file", required_argument, 0, 'f'}, {"ca-dir", required_argument, 0, 'd'}, {"insecure", no_argument, 0, 'i'}, {"reconnect", no_argument, 0, 'r'}, {"no-session-ticket", no_argument, 0, 'T'}, {"dynamic", required_argument, 0, 'D'}, {"timeout", required_argument, 0, 't'}, {"corked-io", no_argument, 0, 'C'}, }; while (1) { int option_index = 0; int c = getopt_long(argc, argv, "a:c:ehn:sf:d:D:t:irTC", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'a': alpn_protocols = optarg; break; case 'C': use_corked_io = 1; break; case 'c': cipher_prefs = optarg; break; case 'e': echo_input = 1; break; case 'h': usage(); break; case 'n': server_name = optarg; break; case 's': type = S2N_STATUS_REQUEST_OCSP; break; case 'm': mfl_value = (uint16_t) atoi(optarg); break; case 'f': ca_file = optarg; break; case 'd': ca_dir = optarg; break; case 'i': insecure = 1; break; case 'r': reconnect = 5; break; case 'T': session_ticket = 0; break; case 't': dyn_rec_timeout = (uint8_t) MIN(255, atoi(optarg)); break; case 'D': dyn_rec_threshold = strtoul(optarg, 0, 10); if (errno == ERANGE) { dyn_rec_threshold = 0; } break; case '?': default: usage(); break; } } if (optind < argc) { host = argv[optind++]; } if (optind < argc) { port = argv[optind++]; } if (!host) { usage(); } if (!server_name) { server_name = host; } memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { fprintf(stderr, "Error disabling SIGPIPE\n"); exit(1); } GUARD_EXIT(s2n_init(), "Error running s2n_init()"); if ((r = getaddrinfo(host, port, &hints, &ai_list)) != 0) { fprintf(stderr, "error: %s\n", gai_strerror(r)); exit(1); } do { int connected = 0; for (ai = ai_list; ai != NULL; ai = ai->ai_next) { if ((sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) { continue; } if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) == -1) { close(sockfd); continue; } connected = 1; /* connect() succeeded */ break; } if (connected == 0) { fprintf(stderr, "Failed to connect to %s:%s\n", host, port); exit(1); } struct s2n_config *config = s2n_config_new(); setup_s2n_config(config, cipher_prefs, type, &unsafe_verify_data, host, alpn_protocols, mfl_value); if (ca_file || ca_dir) { if (s2n_config_set_verification_ca_location(config, ca_file, ca_dir) < 0) { print_s2n_error("Error setting CA file for trust store."); } } else if (insecure) { GUARD_EXIT(s2n_config_disable_x509_verification(config), "Error disabling X.509 validation"); } if (session_ticket) { GUARD_EXIT(s2n_config_set_session_tickets_onoff(config, 1), "Error enabling session tickets"); } struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); if (conn == NULL) { print_s2n_error("Error getting new connection"); exit(1); } GUARD_EXIT(s2n_connection_set_config(conn, config), "Error setting configuration"); GUARD_EXIT(s2n_set_server_name(conn, server_name), "Error setting server name"); GUARD_EXIT(s2n_connection_set_fd(conn, sockfd) , "Error setting file descriptor"); if (use_corked_io) { GUARD_EXIT(s2n_connection_use_corked_io(conn), "Error setting corked io"); } /* Update session state in connection if exists */ if (session_state_length > 0) { GUARD_EXIT(s2n_connection_set_session(conn, session_state, session_state_length), "Error setting session state in connection"); } /* See echo.c */ int ret = negotiate(conn); if (ret != 0) { /* Error is printed in negotiate */ return -1; } printf("Connected to %s:%s\n", host, port); /* Save session state from connection if reconnect is enabled */ if (reconnect > 0) { if (!session_ticket && s2n_connection_get_session_id_length(conn) <= 0) { printf("Endpoint sent empty session id so cannot resume session\n"); exit(1); } free(session_state); session_state_length = s2n_connection_get_session_length(conn); session_state = calloc(session_state_length, sizeof(uint8_t)); if (s2n_connection_get_session(conn, session_state, session_state_length) != session_state_length) { print_s2n_error("Error getting serialized session state"); exit(1); } } if (dyn_rec_threshold > 0 && dyn_rec_timeout > 0) { s2n_connection_set_dynamic_record_threshold(conn, dyn_rec_threshold, dyn_rec_timeout); } if (echo_input == 1) { echo(conn, sockfd); } s2n_blocked_status blocked; s2n_shutdown(conn, &blocked); GUARD_EXIT(s2n_connection_free(conn), "Error freeing connection"); GUARD_EXIT(s2n_config_free(config), "Error freeing configuration"); close(sockfd); reconnect--; } while (reconnect >= 0); GUARD_EXIT(s2n_cleanup(), "Error running s2n_cleanup()"); free(session_state); freeaddrinfo(ai_list); return 0; }
int main(int argc, char * const *argv) { struct addrinfo hints, *ai_list, *ai; int r, sockfd = 0; /* Optional args */ const char *alpn_protocols = NULL; const char *server_name = NULL; s2n_status_request_type type = S2N_STATUS_REQUEST_NONE; /* required args */ const char *host = NULL; const char *port = "443"; static struct option long_options[] = { { "alpn", required_argument, 0, 'a' }, { "help", no_argument, 0, 'h' }, { "name", required_argument, 0, 'n' }, { "status", no_argument, 0, 's' }, }; while (1) { int option_index = 0; int c = getopt_long (argc, argv, "a:hn:s", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'a': alpn_protocols = optarg; break; case 'h': usage(); break; case 'n': server_name = optarg; break; case 's': type = S2N_STATUS_REQUEST_OCSP; break; case '?': default: usage(); break; } } if (optind < argc) { host = argv[optind++]; } if (optind < argc) { port = argv[optind++]; } if (!host) { usage(); } if (!server_name) { server_name = host; } if (memset(&hints, 0, sizeof(hints)) != &hints) { fprintf(stderr, "memset error: %s\n", strerror(errno)); exit(1); } hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { fprintf(stderr, "Error disabling SIGPIPE\n"); exit(1); } if ((r = getaddrinfo(host, port, &hints, &ai_list)) != 0) { fprintf(stderr, "error: %s\n", gai_strerror(r)); exit(1); } int connected = 0; for (ai = ai_list; ai != NULL; ai = ai->ai_next) { if ((sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) { continue; } if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) == -1) { close(sockfd); continue; } connected = 1; /* connect() succeeded */ break; } freeaddrinfo(ai_list); if (connected == 0) { fprintf(stderr, "Failed to connect to %s:%s\n", argv[1], port); close(sockfd); exit(1); } const char *error; if (s2n_init(&error) < 0) { fprintf(stderr, "Error running s2n_init(): '%s'\n", s2n_strerror(s2n_errno, "EN")); } struct s2n_config *config = s2n_config_new(); if (config == NULL) { fprintf(stderr, "Error getting new config: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } if (s2n_config_set_status_request_type(config, type) < 0) { fprintf(stderr, "Error setting status request type: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } if (alpn_protocols) { /* Count the number of commas, this tells us how many protocols there are in the list */ const char *ptr = alpn_protocols; int protocol_count = 1; while (*ptr) { if (*ptr == ',') { protocol_count++; } ptr++; } char **protocols = malloc(sizeof(char *) * protocol_count); if (!protocols) { fprintf(stderr, "Error allocating memory\n"); exit(1); } const char *next = alpn_protocols; int index = 0; int length = 0; ptr = alpn_protocols; while (*ptr) { if (*ptr == ',') { protocols[index] = malloc(length + 1); if (!protocols[index]) { fprintf(stderr, "Error allocating memory\n"); exit(1); } memcpy(protocols[index], next, length); protocols[index][length] = '\0'; length = 0; index++; ptr++; next = ptr; } else { length++; ptr++; } } if (ptr != next) { protocols[index] = malloc(length + 1); if (!protocols[index]) { fprintf(stderr, "Error allocating memory\n"); exit(1); } memcpy(protocols[index], next, length); protocols[index][length] = '\0'; } if (s2n_config_set_protocol_preferences(config, (const char * const *)protocols, protocol_count) < 0) { fprintf(stderr, "Failed to set protocol preferences: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } while(protocol_count) { protocol_count--; free(protocols[protocol_count]); } free(protocols); } struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); if (conn == NULL) { fprintf(stderr, "Error getting new connection: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } printf("Connected to %s:%s\n", host, port); if (s2n_connection_set_config(conn, config) < 0) { fprintf(stderr, "Error setting configuration: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } if (s2n_set_server_name(conn, server_name) < 0) { fprintf(stderr, "Error setting server name: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } if (s2n_connection_set_fd(conn, sockfd) < 0) { fprintf(stderr, "Error setting file descriptor: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } /* See echo.c */ echo(conn, sockfd); if (s2n_connection_free(conn) < 0) { fprintf(stderr, "Error freeing connection: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } if (s2n_config_free(config) < 0) { fprintf(stderr, "Error freeing configuration: '%s'\n", s2n_strerror(s2n_errno, "EN")); exit(1); } if (s2n_cleanup(&error) < 0) { fprintf(stderr, "Error running s2n_cleanup(): '%s'\n", s2n_strerror(s2n_errno, "EN")); } return 0; }
int main(int argc, char **argv) { if(argc != 2) { fprintf(stderr,"usage: %s hostname\n", argv[0]); return 1; } signal(SIGINT, sigint_handler); printf("Looking up addresses for %s ...\n", argv[1]); struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version hints.ai_socktype = SOCK_STREAM; struct addrinfo *dnsres; int status_1 = getaddrinfo(argv[1], PORT, &hints, &dnsres); if(status_1 != 0) { fprintf(stderr, "dns lookup failed: %s\n", gai_strerror(status_1)); return 2; } print_addrinfo(dnsres); printf("Connecting to %s ...\n", "the server"); int sockfd = socket(dnsres->ai_family, dnsres->ai_socktype, dnsres->ai_protocol); if(connect(sockfd, dnsres->ai_addr, dnsres->ai_addrlen) != 0) { perror("connect"); return 3; } printf("Connected.\n"); freeaddrinfo(dnsres); // frees the memory that was dynamically allocated for the linked lists by getaddrinfo s2n_init(); struct s2n_config *config = s2n_config_new(); s2n_status_request_type type = S2N_STATUS_REQUEST_NONE; s2n_config_set_status_request_type(config, type); struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); s2n_connection_set_config(conn, config); s2n_connection_set_fd(conn, sockfd); s2n_blocked_status blocked; do { if (s2n_negotiate(conn, &blocked) < 0) { fprintf(stderr, "Failed to negotiate: '%s' %d\n", s2n_strerror(s2n_errno, "EN"), s2n_connection_get_alert(conn)); exit(1); } } while (blocked); int client_hello_version; int client_protocol_version; int server_protocol_version; int actual_protocol_version; if ((client_hello_version = s2n_connection_get_client_hello_version(conn)) < 0) { fprintf(stderr, "Could not get client hello version\n"); exit(1); } if ((client_protocol_version = s2n_connection_get_client_protocol_version(conn)) < 0) { fprintf(stderr, "Could not get client protocol version\n"); exit(1); } if ((server_protocol_version = s2n_connection_get_server_protocol_version(conn)) < 0) { fprintf(stderr, "Could not get server protocol version\n"); exit(1); } if ((actual_protocol_version = s2n_connection_get_actual_protocol_version(conn)) < 0) { fprintf(stderr, "Could not get actual protocol version\n"); exit(1); } printf("Client hello version: %d\n", client_hello_version); printf("Client protocol version: %d\n", client_protocol_version); printf("Server protocol version: %d\n", server_protocol_version); printf("Actual protocol version: %d\n", actual_protocol_version); if (s2n_get_server_name(conn)) { printf("Server name: %s\n", s2n_get_server_name(conn)); } if (s2n_get_application_protocol(conn)) { printf("Application protocol: %s\n", s2n_get_application_protocol(conn)); } uint32_t length; const uint8_t *status = s2n_connection_get_ocsp_response(conn, &length); if (status && length > 0) { fprintf(stderr, "OCSP response received, length %d\n", length); } printf("Cipher negotiated: %s\n", s2n_connection_get_cipher(conn)); char buf[BUFFERSIZE + 1]; int bytes_read, bytes_written; // Make sure stdin is a terminal. if (!isatty(STDIN_FILENO)) { fprintf(stderr, "Not a terminal.\n"); exit(EXIT_FAILURE); } // Save the terminal attributes so we can restore them later. tcgetattr(STDIN_FILENO, &saved_attributes); atexit(reset_input_mode); // Set the funny terminal modes. struct termios tattr; tcgetattr(STDIN_FILENO, &tattr); tattr.c_lflag &= ~(ICANON | ECHO); // Clear ICANON and ECHO. tattr.c_cc[VMIN] = 1; tattr.c_cc[VTIME] = 0; tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr); fd_set master, readfds; FD_ZERO(&master); FD_SET(STDIN_FILENO, &master); FD_SET(sockfd, &master); for(;;) { readfds = master; select(sockfd + 1, &readfds, NULL, NULL, NULL); if(FD_ISSET(STDIN_FILENO, &readfds)) { bytes_read = read(STDIN_FILENO, buf, BUFFERSIZE); if(bytes_read < 1) break; char *buf_ptr = buf; int bytes_available = bytes_read; do { bytes_written = s2n_send(conn, buf_ptr, bytes_available, &blocked); if(bytes_written < 0) break; bytes_available -= bytes_written; buf_ptr += bytes_written; } while(bytes_available || blocked); } if(FD_ISSET(sockfd, &readfds)) { do { bytes_read = s2n_recv(conn, buf, BUFFERSIZE, &blocked); if(bytes_read < 1) break; write(STDOUT_FILENO, buf, bytes_read); } while(blocked); } //if(nbytes != mbytes) printf("nbytes [%d] != mbytes [%d] \n", nbytes, mbytes); } close(sockfd); s2n_connection_free(conn); s2n_config_free(config); s2n_cleanup(); printf("\nBYE!\n"); return 0; }