diff options
author | Shav Kinderlehrer <[email protected]> | 2024-07-23 17:48:28 -0400 |
---|---|---|
committer | Shav Kinderlehrer <[email protected]> | 2024-07-23 17:48:28 -0400 |
commit | dc0f2ce9ba97ebb47e05b80a511da6eb29818b63 (patch) | |
tree | dc83035069f5a015047be1ca3da6f65781eb4695 /src/molerat | |
parent | f638f4bd1e3a03bc2bdd5f9dcd57d4830fd3c553 (diff) | |
download | molehole-ncurses.tar.gz molehole-ncurses.zip |
Merge old-moleholencurses
Diffstat (limited to 'src/molerat')
-rwxr-xr-x | src/molerat/connect.c | 133 | ||||
-rwxr-xr-x | src/molerat/net.c | 68 | ||||
-rwxr-xr-x | src/molerat/request.c | 34 | ||||
-rwxr-xr-x | src/molerat/response.c | 215 | ||||
-rwxr-xr-x | src/molerat/url.c | 181 |
5 files changed, 631 insertions, 0 deletions
diff --git a/src/molerat/connect.c b/src/molerat/connect.c new file mode 100755 index 0000000..96c904b --- /dev/null +++ b/src/molerat/connect.c @@ -0,0 +1,133 @@ +#include <errno.h> +#include <netdb.h> +#include <openssl/ssl.h> +#include <stdbool.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include "connect.h" +#include "status.h" +#include "url.h" + +int connect_socket(struct config *conf, struct url url) { + struct addrinfo hints; + struct addrinfo *servinfo; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + char port[5]; + sprintf(port, "%d", url.port); + int rc = getaddrinfo(url.host, port, &hints, &servinfo); + if (rc != 0) { + error_status(conf, (char *)gai_strerror(rc)); + return ERR_GETADDRINFO; + } + + struct addrinfo *p; + int sockfd; + for (p = servinfo; p != NULL; p = p->ai_next) { + sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (sockfd == -1) { + continue; + } + + rc = connect(sockfd, p->ai_addr, p->ai_addrlen); + if (rc == -1) { + close(sockfd); + continue; + } + + break; + } + + if (p == NULL) { + error_status(conf, strerror(errno)); + return ERR_CONNECT; + } + update_status(conf, "Connected to socket"); + freeaddrinfo(servinfo); + + return sockfd; +} + +int tls_connect(struct config *conf, struct url url) { + char status_msg[strlen(url.host) + 128]; + sprintf(status_msg, "Connecting to %s...", url.host); + update_status(conf, status_msg); + + struct connection *conn = init_connection(); + conf->s.conn = conn; + + int sock; + int rc = connect_socket(conf, url); + if (rc < 0) { + return rc; + } else { + sock = rc; + } + + const SSL_METHOD *method; + method = TLS_method(); + + SSL_CTX *ctx; + ctx = SSL_CTX_new(method); + if (ctx == NULL) { + error_status(conf, "Failed to init SSL"); + return ERR_SSL_CTX; + } + + SSL *ssl; + ssl = SSL_new(ctx); + if (ssl == NULL) { + error_status(conf, "Failed to init SSL"); + return ERR_SSL_SSL; + } + + rc = SSL_set_fd(ssl, sock); + if (rc == 0) { + error_status(conf, "Failed to wrap socket"); + return ERR_SSL_SSL; + } + + rc = SSL_connect(ssl); + if (rc <= 0) { + error_status(conf, "Failed to connect SSL"); + return ERR_SSL_SSL; + } + + conn->ssl = ssl; + conn->sockfd = sock; + conn->used = true; + conf->s.conn = conn; + + update_status(conf, "Connected"); + + return 0; +} + +void tls_cleanup(struct connection *conn) { + if (conn->sockfd) { + shutdown(conn->sockfd, SHUT_RDWR); + close(conn->sockfd); + } + + if (conn->ssl) { + SSL_shutdown(conn->ssl); + SSL_CTX_free(SSL_get_SSL_CTX(conn->ssl)); + SSL_free(conn->ssl); + } + + free(conn); +} + +struct connection *init_connection(void) { + struct connection *conn = malloc(sizeof(struct connection)); + memset(conn, 0, sizeof(struct connection)); + + return conn; +} diff --git a/src/molerat/net.c b/src/molerat/net.c new file mode 100755 index 0000000..f3eef06 --- /dev/null +++ b/src/molerat/net.c @@ -0,0 +1,68 @@ +#include <openssl/ssl.h> +#include <string.h> +#include <time.h> + +#include "config.h" +#include "connect.h" +#include "net.h" +#include "request.h" +#include "response.h" +#include "status.h" + +int send_request(struct config *conf, struct request *req) { + char *req_string = request_to_string(req); + + int rc = SSL_write(conf->s.conn->ssl, req_string, strlen(req_string)); + + if (rc < 0) { + return SSL_SEND_ERROR; + } + + free(req_string); + return 0; +} + +int read_response(struct config *conf, struct response *res) { + struct timespec start_time; + clock_gettime(CLOCK_MONOTONIC, &start_time); + + int buf_len = 4096; + char *buf = malloc(buf_len); + + int bytes_read = 0; + + do { + bytes_read = SSL_read(conf->s.conn->ssl, buf, buf_len); + + if (bytes_read == buf_len) { + buf_len *= 2; + char *temp_buf = realloc(buf, buf_len); + if (temp_buf == NULL) + return ALLOC_ERROR; + + buf = temp_buf; + } + } while (bytes_read > 0); + + int rc = parse_response(res, buf); + if (rc < 0) { + return RESPONSE_PARSE_ERROR; + } + free(buf); + + struct timespec end_time; + clock_gettime(CLOCK_MONOTONIC, &end_time); + + float time_diff = (end_time.tv_sec - start_time.tv_sec) + + 1e-9 * (end_time.tv_nsec - start_time.tv_nsec); + + int msg_len = + snprintf(NULL, 0, "Received after %0.3f seconds", time_diff) + 1; + char *msg = malloc(msg_len); + snprintf(msg, msg_len, "Received after %.3f seconds", time_diff); + + update_status(conf, msg); + free(msg); + + return 0; +} diff --git a/src/molerat/request.c b/src/molerat/request.c new file mode 100755 index 0000000..1d59a09 --- /dev/null +++ b/src/molerat/request.c @@ -0,0 +1,34 @@ +#include <ncurses.h> +#include <stdlib.h> +#include <string.h> + +#include "request.h" +#include "url.h" + +char *get_request_kind(enum RequestKind kind) { + switch (kind) { + case GET: + return "get"; + case PUT: + return "put"; + case DEL: + return "del"; + } +} + +char *request_to_string(struct request *req) { + int len = sizeof(struct request) + + 6; // +1 for null terminator +5 for request whitespace + + char *buf = malloc(len); + + char *kind = get_request_kind(req->kind); + char *host = req->url.host != NULL ? req->url.host : ""; + char *path = req->url.path != NULL ? req->url.path : ""; + char *query = req->url.query != NULL ? req->url.query : ""; + char *fragment = req->url.fragment != NULL ? req->url.fragment : ""; + + snprintf(buf, len, "%s %s%s%s%s\r\n\r\n", kind, host, path, query, fragment); + + return buf; +} diff --git a/src/molerat/response.c b/src/molerat/response.c new file mode 100755 index 0000000..0fffe21 --- /dev/null +++ b/src/molerat/response.c @@ -0,0 +1,215 @@ +#include <stdlib.h> +#include <string.h> + +#include "response.h" + +#define SET_STR(segment) \ + (segment) = malloc(i - start + 1); \ + strncpy((segment), s + start, i - start); + +#define MOVE(amount) \ + i += (amount); \ + start = i; + +#define CHECK_AT_END() \ + if (cur == '\0') { \ + state = END; \ + } + +#define AT_DELIM (cur == '\t' && next == '\r' && next_next == '\n') + +enum state { + STATUS, + + MESSAGE, + MESSAGE_S, + + TYPE_S, + TYPE, + + LENGTH_S, + LENGTH, + + HASH, + HASH_S, + + CONTENT, + END +}; + +int parse_response(struct response *res, char *s) { + int i = 0; + int start = 0; + char cur; + char next; + char next_next; + + int s_len = strlen(s); + + enum state state = STATUS; + + while (state != END) { + cur = s[i]; + if (i < s_len) + next = s[i + 1]; + if (i + 1 < s_len) + next_next = s[i + 2]; + + switch (state) { + case STATUS: + if (cur == '\r' && next == '\n') { + char *status_str; + SET_STR(status_str); + int status = atoi(status_str); + if (status > 0) + res->status = status; + else + return INVALID_STATUS; + free(status_str); + + MOVE(2); + state = MESSAGE; + } + + break; + case MESSAGE: + if (cur == ':') { + char *message_str; + SET_STR(message_str); + if (strncmp(message_str, "message", 7) != 0) { + MOVE(strlen(message_str) * -1); + state = TYPE; + break; + } else { + MOVE(1); // skip ':' + state = MESSAGE_S; + } + + free(message_str); + } + break; + case MESSAGE_S: + if (AT_DELIM) { + char *message_str; + SET_STR(message_str); + res->message = message_str; + + MOVE(3); + state = TYPE; + } + + break; + case TYPE: + if (cur == ':') { + char *type_str; + SET_STR(type_str); + if (strncmp(type_str, "type", 4) != 0) { + MOVE(strlen(type_str) * -1); + state = LENGTH; + } else { + MOVE(1); // skip ':' + state = TYPE_S; + } + + free(type_str); + } + break; + case TYPE_S: + if (cur == '/') { + SET_STR(res->type.type); + MOVE(1); + } else if (AT_DELIM) { + SET_STR(res->type.subtype); + + MOVE(3); + state = LENGTH; + } + break; + + case LENGTH: + if (cur == ':') { + char *length_str; + SET_STR(length_str); + if (strncmp(length_str, "length", 6) != 0) { + MOVE(strlen(length_str) * -1); + state = HASH; + } else { + MOVE(1); // skip ':' + state = LENGTH_S; + } + free(length_str); + } + + case LENGTH_S: + if (AT_DELIM) { + char *length_str; + SET_STR(length_str); + res->length = atoi(length_str); + free(length_str); + + MOVE(3); + state = HASH; + } + break; + + case HASH: + if (cur == ':') { + char *hash_str; + SET_STR(hash_str); + if (strncmp(hash_str, "hash", 4) != 0) { + MOVE(strlen(hash_str) * -1); + free(hash_str); + return UNKNOWN_KEY; + } else { + MOVE(1); // skip ':' + state = HASH_S; + } + free(hash_str); + } + break; + case HASH_S: + if ((cur == '\r' && next == '\n') || AT_DELIM) { + char *hash_str; + SET_STR(hash_str); + res->hash = hash_str; + + MOVE(4); + state = CONTENT; + } + + case CONTENT: + if (res->length < 1) { + state = END; + break; + } + + if (i == s_len - 1) { + char *content_str; + SET_STR(content_str); + res->content = content_str; + + state = END; + } + + case END: + break; + } + + i++; + if (i == s_len) { + state = END; + } + } + + return 0; +} + +void free_response(struct response *res) { + free(res->message); + + free(res->type.type); + free(res->type.subtype); + + free(res->hash); + free(res->content); +} diff --git a/src/molerat/url.c b/src/molerat/url.c new file mode 100755 index 0000000..0fd99ea --- /dev/null +++ b/src/molerat/url.c @@ -0,0 +1,181 @@ +#include <stdlib.h> +#include <string.h> + +#include "url.h" + +#define SET_STR(segment) \ + (segment) = malloc(i - start + 1); \ + strncpy((segment), s + start, i - start); + +#define MOVE(amount) \ + i += (amount); \ + start = i; + +#define SET_AT_END(segment) \ + if (cur == '\0') { \ + state = END; \ + if (i - 1 > start) { \ + SET_STR(segment); \ + } \ + } + +enum state { SCHEME, HOST, PORT, PATH, QUERY, FRAGMENT, END }; + +int parse_url(struct url *url, char *s) { + int i = 0; // index into *s + int start = 0; // index of current mode start + enum state state = SCHEME; + char cur; + + while (i < MAX_URL_LENGTH) { + cur = s[i]; + + if (cur == ' ') + return INVALID_CHARACTER; + + switch (state) { + case SCHEME: + if (cur == ':') { + state = HOST; + SET_STR(url->scheme); + + MOVE(3); // skip the '://' + } + SET_AT_END(url->scheme); + break; + + case HOST: + if (cur == ':') { + state = PORT; + SET_STR(url->host); + + MOVE(1); + } + + if (cur == '/') { + state = PATH; + SET_STR(url->host); + + MOVE(0); + } + SET_AT_END(url->host); + break; + + case PORT: + if (cur == '/') { + state = PATH; + char *port; + SET_STR(port); + + url->port = atoi(port); + + MOVE(0); + } + if (cur == '\0') { + state = END; + + char *port; + SET_STR(port); + + url->port = atoi(port); + } + + break; + + case PATH: + if (cur == '?') { + state = QUERY; + SET_STR(url->path); + + MOVE(0); + } + + if (cur == '#') { + state = FRAGMENT; + SET_STR(url->path); + + MOVE(0); + } + SET_AT_END(url->path); + break; + + case QUERY: + if (cur == '#') { + state = FRAGMENT; + SET_STR(url->query); + + MOVE(0); + } + SET_AT_END(url->query); + break; + + case FRAGMENT: + if (cur == '\0') { + state = END; + SET_STR(url->fragment); + + MOVE(0); + } + break; + + case END: + break; + } + + i++; + + if (cur == '\0') + break; + if (state == END) + break; + } + + if (url->host == NULL) + return MISSING_HOST; + if (url->port == 0) + return MISSING_PORT; + + if (url->path == NULL) { + url->path = malloc(2); + strcpy(url->path, "/"); + } + + return 0; +} + +struct url *init_url(void) { + struct url *url = malloc(sizeof(struct url)); + + url->scheme = NULL; + url->host = NULL; + url->port = 2693; + + url->path = NULL; + + url->query = NULL; + url->fragment = NULL; + + return url; +} + +void free_url(struct url *url) { + free(url->scheme); + free(url->host); + free(url->path); + free(url->fragment); + free(url->query); + free(url); +} + +int len_url(struct url *url) { + int len = 0; + + len += url->scheme != NULL ? strlen(url->scheme) : 0; + len += url->host != NULL ? strlen(url->host) : 0; + len += sizeof(url->port); + len += url->path != NULL ? strlen(url->path) : 0; + len += url->query != NULL ? strlen(url->query) : 0; + len += url->fragment != NULL ? strlen(url->fragment) : 0; + + return len; +} |