#include "networking.h" #include #include #include #include #include #include #include #include #include #include "ssl.h" typedef int socket_fd; static SSL_CTX *ssl_ctx = NULL; char *strstr_fixn(const char *haystack, const char *needle, size_t len) { size_t needle_len; if (0 == (needle_len = strnlen(needle, len))) return (char *)haystack; for (size_t i = 0; i <= len - needle_len; i++) { if ((haystack[0] == needle[0]) && (0 == strncmp(haystack, needle, needle_len))) return (char *)haystack; haystack++; } return NULL; } static socket_fd connect_to_http(const char* hostname) { struct addrinfo resolve_hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, }; struct addrinfo* resolve_result; int result = getaddrinfo(hostname, "https", &resolve_hints, &resolve_result); if (result != 0) { fprintf(stderr, "Failed to get IP address of \"%s\" for an http connection. Error: %s\n", hostname, gai_strerror(result)); return -1; } socket_fd sock_desc = -1; for (struct addrinfo *it = resolve_result; it != NULL; it = it->ai_next) { sock_desc = socket(it->ai_family, it->ai_socktype, it->ai_protocol); if (sock_desc == -1) { continue; } if (connect(sock_desc, it->ai_addr, it->ai_addrlen) != -1) { uint16_t port = ntohs( (it->ai_family == AF_INET) ? ((struct sockaddr_in*) it->ai_addr)->sin_port : ((struct sockaddr_in6*) it->ai_addr)->sin6_port); printf("Connected to \"%s\" on port %d\n", hostname, port); break; } close(sock_desc); } freeaddrinfo(resolve_result); return sock_desc; } void tcp_get(const char* addr) { initialize_ssl(); printf("initialized ssl\n"); socket_fd sock_fd = connect_to_http(addr); printf("Got socket: %d\n", sock_fd); SSL* ssl_conn = SSL_new(ssl_ctx); if (!ssl_conn) { fprintf(stderr, "Failed to create an SSL connection provider.\n"); ERR_print_errors_fp(stderr); close(sock_fd); return; } SSL_set_fd(ssl_conn, sock_fd); if (!SSL_set_tlsext_host_name(ssl_conn, addr)) { fprintf(stderr, "Failed to set SNI hostname.\n"); ERR_print_errors_fp(stderr); SSL_free(ssl_conn); return; } if (!SSL_set1_host(ssl_conn, addr)) { fprintf(stderr, "Failed to set certificate hostname.\n"); ERR_print_errors_fp(stderr); SSL_free(ssl_conn); return; } if (SSL_connect(ssl_conn) < 1) { fprintf(stderr, "Failed to connect to server.\n"); if (SSL_get_verify_result(ssl_conn) != X509_V_OK) { fprintf(stderr, "Verification error: %s\n", X509_verify_cert_error_string(SSL_get_verify_result(ssl_conn))); } else { ERR_print_errors_fp(stderr); } SSL_free(ssl_conn); return; } printf("Connected to peer.\n"); const char* HTTP_HEAD = "GET / HTTP/1.1\r\n" "Connection: close\r\n" "Host: "; const char* HTTP_HEAD_END = "\r\n\r\n"; const char* msg[] = { HTTP_HEAD, addr, HTTP_HEAD_END }; for (size_t i = 0; i < sizeof(msg)/sizeof(msg[0]); ++i) { if (!SSL_write(ssl_conn, msg[i], strlen(msg[i]))) { fprintf(stderr, "Failed to send message: \"%s\"\n", msg[i]); ERR_print_errors_fp(stderr); SSL_free(ssl_conn); return; } } printf("Sent HTTP request to peer.\n"); size_t read_bytes; char read_buf[160]; enum { HTTP_CONTENT_LENGTH, HTTP_CONTENT_CHUNKED } http_content_transmit_type; size_t total_read_bytes; size_t expected_bytes = -1; bool body = false; char last_3_of_prev[3] = {}; printf("\n"); while (SSL_read_ex(ssl_conn, read_buf, sizeof(read_buf), &read_bytes)) { total_read_bytes += read_bytes; if ((http_content_transmit_type == HTTP_CONTENT_CHUNKED && read_buf[0] == '0') || http_content_transmit_type == HTTP_CONTENT_LENGTH && total_read_bytes == expected_bytes) { SSL_set_options(ssl_conn, SSL_OP_IGNORE_UNEXPECTED_EOF); } if (!body) { const char* content_length_str = "Content-Length: "; const char* transfer_encoding_str = "Transfer-Encoding: chunked"; char* content_length_start = strstr_fixn(read_buf, content_length_str, read_bytes); if (content_length_start != NULL) { http_content_transmit_type = HTTP_CONTENT_LENGTH; content_length_start += strlen(content_length_str); expected_bytes = strtol(content_length_start, NULL, 10); } if (strstr_fixn(read_buf, transfer_encoding_str, read_bytes)) { http_content_transmit_type = HTTP_CONTENT_CHUNKED; } char concat[] = { last_3_of_prev[0], last_3_of_prev[1], last_3_of_prev[2], read_buf[0], read_buf[1], read_buf[2], 0 }; char *concat_start; if ((concat_start = strstr(concat, HTTP_HEAD_END))) { body = true; total_read_bytes = read_bytes - (concat_start - concat + 1); } else { char* body_start = strstr_fixn(read_buf, HTTP_HEAD_END, read_bytes); if (body_start) { body = true; total_read_bytes = read_bytes - (body_start + strlen(HTTP_HEAD_END) - read_buf); } } } fwrite(read_buf, 1, read_bytes, stdout); } printf("\n"); if (SSL_get_error(ssl_conn, 0) != SSL_ERROR_ZERO_RETURN) { fprintf(stderr, "Failed to read response.\n"); ERR_print_errors_fp(stderr); } printf("Recieved %ld bytes", total_read_bytes); printf(expected_bytes != -1 ? ", expected: %ld bytes\n" : "\n", expected_bytes); printf("Ignore unexpected eof? %s\n", SSL_get_options(ssl_conn) & SSL_OP_IGNORE_UNEXPECTED_EOF ? "true" : "false"); if (SSL_shutdown(ssl_conn) < 1) { fprintf(stderr, "Failed to shut down connection gracefully.\n"); ERR_print_errors_fp(stderr); } SSL_free(ssl_conn); printf("end\n"); }