aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorturret <turret@duck.com>2024-03-30 16:04:45 -0500
committerturret <turret@duck.com>2024-03-30 16:04:45 -0500
commit80a67b7d20393a29aa5d2cb92197f3381be7fd96 (patch)
treef0c313da5ef509e79e5067972edd91976513ce0e /net
parentf01745a2ee84f11b8cc54e37c5f7f596184ab785 (diff)
downloaddiscord-bot-skeleton-80a67b7d20393a29aa5d2cb92197f3381be7fd96.tar.gz
discord-bot-skeleton-80a67b7d20393a29aa5d2cb92197f3381be7fd96.tar.bz2
discord-bot-skeleton-80a67b7d20393a29aa5d2cb92197f3381be7fd96.zip
*: directory changes
since this project is a skeleton and not meant to clutter up the code that will actually consume the bot, i've opted to consolidate the majority of files under a single directory and minimise extra files *: move code to util/ *: move include files to include/dbs/ net: consolidate net functions into single file config: remove config
Diffstat (limited to 'net')
-rw-r--r--net/api.c87
-rw-r--r--net/net.c203
-rw-r--r--net/ws.c67
3 files changed, 0 insertions, 357 deletions
diff --git a/net/api.c b/net/api.c
deleted file mode 100644
index be36866..0000000
--- a/net/api.c
+++ /dev/null
@@ -1,87 +0,0 @@
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <curl/curl.h>
-
-#define __API_INTERNAL
-#include <api.h>
-#include <log.h>
-
-extern char *token;
-
-int http_request(HTTPMethod method, char *url,
- struct curl_slist *_Nullable headers, char *writebuf, size_t bufsiz)
-{
- int inputpipe[2];
- int outputpipe[2];
-
- if(pipe(inputpipe) < 0)
- return -(errno << 8);
- if(pipe(outputpipe) < 0)
- return -(errno << 8);
-
- if(writebuf && bufsiz > 0)
- write(inputpipe[1], writebuf, bufsiz);
- close(inputpipe[1]);
-
- FILE *input_read = fdopen(inputpipe[0], "r");
- FILE *output_write = fdopen(outputpipe[1], "w");
-
- int ret = outputpipe[0];
-
- CURL *job = curl_easy_init();
- if(job == NULL)
- panic("api: curl_easy_init failed");
-
- curl_easy_setopt(job, CURLOPT_URL, url);
- curl_easy_setopt(job, CURLOPT_READDATA, input_read);
- curl_easy_setopt(job, CURLOPT_WRITEDATA, output_write);
- char *requestmethod = "GET";
- switch(method) {
- case HTTP_PATCH:
- requestmethod = "PATCH";
- break;
- case HTTP_DELETE:
- requestmethod = "DELETE";
- break;
- case HTTP_PUT:
- requestmethod = "PUT";
- break;
- case HTTP_POST:
- requestmethod = "POST";
- break;
- case HTTP_GET: /* fallthrough */
- default:
- break;
- }
- curl_easy_setopt(job, CURLOPT_CUSTOMREQUEST, requestmethod);
- if(headers)
- curl_easy_setopt(job, CURLOPT_HTTPHEADER, headers);
- CURLcode res = curl_easy_perform(job);
-
- if(res > 0) {
- close(outputpipe[0]);
- ret = -res;
- }
-
- curl_easy_cleanup(job);
- fclose(input_read);
- fclose(output_write);
- return ret;
-}
-
-int api_request(HTTPMethod method, char *url,
- struct curl_slist *_Nullable headers, char *writebuf, size_t bufsiz)
-{
- char *new_url = calloc((strlen("https://discord.com/api") + strlen(url) + 1),
- sizeof(char));
- strcpy(new_url, "https://discord.com/api");
- strcat(new_url, url);
- struct curl_slist *headers_auth = curl_slist_append(headers, token);
- int ret = http_request(method, new_url, headers_auth, writebuf, bufsiz);
- free(new_url);
- curl_slist_free_all(headers_auth);
- return ret;
-}
diff --git a/net/net.c b/net/net.c
deleted file mode 100644
index ee3b043..0000000
--- a/net/net.c
+++ /dev/null
@@ -1,203 +0,0 @@
-#include <errno.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/signalfd.h>
-#include <unistd.h>
-
-#include <cJSON.h>
-#include <curl/curl.h>
-
-#include <api.h>
-#include <init.h>
-#include <log.h>
-#include <subsys.h>
-
-extern void ws_handle_event(cJSON *event);
-extern void ws_send_heartbeat();
-CURL *ws_handle;
-char *gateway_url;
-
-int net_subsystem(void)
-{
- if(!gateway_url)
- panic("net: gateway url invalid");
-
- /* Initialise CURL */
- ws_handle = curl_easy_init();
-
- curl_easy_setopt(ws_handle, CURLOPT_URL, gateway_url);
- curl_easy_setopt(ws_handle, CURLOPT_CONNECT_ONLY, 2L);
-
- CURLcode ret = curl_easy_perform(ws_handle);
-
- if(ret > 0) {
- panic("net: cannot open websocket: %s", curl_easy_strerror(ret));
- }
-
- int ws_sockfd;
- if((ret = curl_easy_getinfo(ws_handle,
- CURLINFO_ACTIVESOCKET, &ws_sockfd)) != CURLE_OK)
- panic("net: curl cannot get active socket: "
- "%s", curl_easy_strerror(ret));
-
-
- /* Block ALRM */
- sigset_t *set = malloc(sizeof(sigset_t));
- sigemptyset(set);
- sigaddset(set, SIGALRM);
- sigprocmask(SIG_BLOCK, set, NULL);
- int alrmfd = signalfd(-1, set, 0);
- free(set);
-
- /* Prepare poll */
- struct pollfd pollarray[2] = {
- {
- .fd = ws_sockfd,
- .events = POLLIN,
- .revents = POLLIN
- },
- {
- .fd = alrmfd,
- .events = POLLIN,
- .revents = 0
- }
- };
-
- struct pollfd *sockpoll = &(pollarray[0]);
- struct pollfd *alrmpoll = &(pollarray[1]);
-
- /* Misc. variables */
- char *inbuf = malloc(1<<16 * sizeof(char));
- size_t rlen;
- const struct curl_ws_frame *meta;
-
- errno = 0;
- do {
- if((sockpoll->revents & POLLIN) == POLLIN) {
- ret = curl_ws_recv(ws_handle, inbuf, 1<<16, &rlen, &meta);
- /* sometimes only SSL information gets sent through, so no actual
- data is received. curl uses NONBLOCK internally so it lets us
- know if there is no more data remaining */
- if(ret == CURLE_AGAIN)
- continue;
- if(ret != CURLE_OK) {
- print(LOG_ERR "net: encountered error while reading socket: "
- "%s", curl_easy_strerror(ret));
- break;
- }
-
- /* TODO: partial frames */
- if((meta->offset | meta->bytesleft) > 0) {
- print(LOG_ERR "net: dropped partial frame");
- continue;
- }
-
- cJSON *event = cJSON_ParseWithLength(inbuf, rlen);
- if(!event) {
- print(LOG_ERR "net: dropped malformed frame");
- continue;
- }
- ws_handle_event(event);
- cJSON_Delete(event);
- } else if((sockpoll->revents &
- (POLLRDHUP | POLLERR | POLLHUP | POLLNVAL)) > 0) {
- break;
- }
-
- if((alrmpoll->revents & POLLIN) == POLLIN) {
- struct signalfd_siginfo siginfo;
- read(alrmfd, &siginfo, sizeof(struct signalfd_siginfo));
- ws_send_heartbeat();
- }
- } while(poll(pollarray, 2, -1) >= 0);
-
- if(errno > 0) {
- print(LOG_ERR "net: poll: %s", strerror(errno));
- }
-
- free(inbuf);
-
- curl_easy_cleanup(ws_handle);
-
- panic("net: websocket closed unexpectedly");
-
- return 0;
-}
-
-void net_get_gateway_url()
-{
- /* determine if websockets are supported */
- curl_version_info_data *curl_version =
- curl_version_info(CURLVERSION_NOW);
- const char * const* curl_protocols = curl_version->protocols;
- int wss_supported = 0;
- for(int i = 0; curl_protocols[i]; ++i) {
- if(strcmp(curl_protocols[i], "wss") == 0) {
- wss_supported = 1;
- break;
- }
- }
-
- if(!wss_supported)
- panic("net: wss not supported by libcurl");
-
- /* fetch preferred url from discord */
- int fd = api_get("/gateway/bot", NULL, NULL, 0);
- if(fd < 0) {
- print(LOG_ERR "net: cannot get gateway url: %s", curl_easy_strerror(-fd));
- goto assume;
- }
-
- char buf[512];
- int buf_length = read(fd, buf, 512);
- close(fd);
-
- cJSON *gateway_info = cJSON_ParseWithLength(buf, buf_length);
- cJSON *gateway_url_json =
- cJSON_GetObjectItemCaseSensitive(gateway_info, "url");
- if(!cJSON_IsString(gateway_url_json) ||
- gateway_url_json->valuestring == NULL) {
-
- cJSON *gateway_message =
- cJSON_GetObjectItemCaseSensitive(gateway_info, "message");
-
- if(cJSON_IsString(gateway_message)) {
- print(LOG_ERR "net: cannot get gateway url from api: "
- "%s: assuming url", cJSON_GetStringValue(gateway_message));
- } else {
- print(LOG_ERR "net: cannot get gateway url from api "
- "(unknown error): assuming url");
- }
- cJSON_Delete(gateway_info);
- goto assume;
- }
-
- /* curl requires websocket secure URLs to begin with WSS instead
- of wss, so we fix up the received url for curl */
- gateway_url = calloc(strlen(gateway_url_json->valuestring) + 1,
- sizeof(char));
- strcpy(gateway_url, gateway_url_json->valuestring);
- gateway_url[0] = 'W';
- gateway_url[1] = 'S';
- gateway_url[2] = 'S';
-
- cJSON_Delete(gateway_info);
- return;
-
-assume:
- gateway_url = calloc(strlen("WSS://gateway.discord.gg") + 1,
- sizeof(char));
- strcpy(gateway_url, "WSS://gateway.discord.gg");
- return;
-}
-l1_initcall(net_get_gateway_url);
-
-void net_initcall()
-{
- start_subsystem(net_subsystem);
-}
-l2_initcall(net_initcall);
diff --git a/net/ws.c b/net/ws.c
deleted file mode 100644
index 3a8d512..0000000
--- a/net/ws.c
+++ /dev/null
@@ -1,67 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/time.h>
-#include <unistd.h>
-
-#include <cJSON.h>
-#include <curl/curl.h>
-
-#include <log.h>
-
-extern CURL *ws_handle;
-long last_sequence = -1;
-struct timeval heartbeat_time;
-
-void ws_send_heartbeat()
-{
- char buf[128] = "{\"op\":1,\"d\":null}";
- if(last_sequence > 0)
- snprintf(buf, 128, "{\"op\":1,\"d\":%ld}", last_sequence);
- size_t sent;
- curl_ws_send(ws_handle, buf, strnlen(buf, 128), &sent, 0, CURLWS_TEXT);
-
- /* if we receive a heartbeat request from discord, we need to fix
- the itimer so we don't send another one before the desired
- heartbeat interval. if our itimer is off more than 2 seconds
- then we fix it up and reset it */
- struct itimerval itimer;
- getitimer(ITIMER_REAL, &itimer);
- if(itimer.it_value.tv_sec < heartbeat_time.tv_sec - 2) {
- itimer.it_value = heartbeat_time;
- setitimer(ITIMER_REAL, &itimer, NULL);
- }
-}
-
-void ws_handle_event(cJSON *event)
-{
- int op = cJSON_GetObjectItem(event, "op")->valueint;
- cJSON *data = cJSON_GetObjectItem(event, "d");
- switch(op) {
- case 1: /* Heartbeat request */
- ws_send_heartbeat();
- break;
- case 10: ; /* Hello */
- int heartbeat_wait = cJSON_GetObjectItem(data,
- "heartbeat_interval")->valueint;
- float jitter = (float)rand() / (RAND_MAX * 1.0f);
-
- heartbeat_time.tv_sec = heartbeat_wait / 1000;
- heartbeat_time.tv_usec = (heartbeat_wait % 1000) * 1000;
- struct timeval jitter_time = {
- .tv_sec = heartbeat_time.tv_sec * jitter,
- .tv_usec = heartbeat_time.tv_usec * jitter,
- };
- struct itimerval new_itimer = {
- .it_interval = heartbeat_time,
- .it_value = jitter_time
- };
- setitimer(ITIMER_REAL, &new_itimer, NULL);
- break;
- case 11: /* Heartbeat ACK */
- break;
- default:
- print(LOG_ERR "ws: received unknown WS opcode %d", op);
- break;
- }
-}