aboutsummaryrefslogtreecommitdiff
path: root/src/molerat
diff options
context:
space:
mode:
authorShav Kinderlehrer <[email protected]>2024-07-23 17:48:28 -0400
committerShav Kinderlehrer <[email protected]>2024-07-23 17:48:28 -0400
commitdc0f2ce9ba97ebb47e05b80a511da6eb29818b63 (patch)
treedc83035069f5a015047be1ca3da6f65781eb4695 /src/molerat
parentf638f4bd1e3a03bc2bdd5f9dcd57d4830fd3c553 (diff)
downloadmolehole-ncurses.tar.gz
molehole-ncurses.zip
Merge old-moleholencurses
Diffstat (limited to 'src/molerat')
-rwxr-xr-xsrc/molerat/connect.c133
-rwxr-xr-xsrc/molerat/net.c68
-rwxr-xr-xsrc/molerat/request.c34
-rwxr-xr-xsrc/molerat/response.c215
-rwxr-xr-xsrc/molerat/url.c181
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;
+}