diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/api.c | 87 | ||||
-rw-r--r-- | net/net.c | 203 | ||||
-rw-r--r-- | net/ws.c | 67 |
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; - } -} |