OAuth2Lib/networking.c

222 lines
5.6 KiB
C

#include "networking.h"
#include <openssl/bio.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#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");
}