libdiscord

libdiscord.git
git clone git://git.lenczewski.org/libdiscord.git
Log | Files | Refs | README | LICENSE

internal.h (7883B)


      1 #ifndef INTERNAL_H
      2 #define INTERNAL_H
      3 
      4 #define _GNU_SOURCE 1
      5 #define _POSIX_SOURCE 1
      6 #define _XOPEN_SOURCE 700
      7 
      8 #include <unistd.h>
      9 
     10 #include <liburing.h>
     11 
     12 #include <openssl/ssl.h>
     13 #include <openssl/err.h>
     14 
     15 #include <sys/types.h>
     16 #include <sys/socket.h>
     17 #include <netinet/ip.h>
     18 #include <netdb.h>
     19 
     20 #include "libdiscord.h"
     21 #include "utils.h"
     22 
     23 struct ioreq_connect {
     24 	int fd;
     25 	struct sockaddr *addr;
     26 	socklen_t addrlen;
     27 };
     28 
     29 struct ioreq_recv {
     30 	int fd;
     31 	void *buf;
     32 	size_t len;
     33 	int flags;
     34 };
     35 
     36 struct ioreq_recvfrom {
     37 	int fd;
     38 	void *buf;
     39 	size_t len;
     40 	int flags;
     41 	struct sockaddr *addr;
     42 	socklen_t addrlen;
     43 };
     44 
     45 struct ioreq_send {
     46 	int fd;
     47 	void *buf;
     48 	size_t len;
     49 	int flags;
     50 };
     51 
     52 struct ioreq_sendto {
     53 	int fd;
     54 	void *buf;
     55 	size_t len;
     56 	int flags;
     57 	struct sockaddr *addr;
     58 	socklen_t addrlen;
     59 };
     60 
     61 struct ioreq_close {
     62 	int fd;
     63 };
     64 
     65 union ioreq_tag {
     66 	struct ioreq_connect connect;
     67 	struct ioreq_recv recv;
     68 	struct ioreq_recvfrom recvfrom;
     69 	struct ioreq_send send;
     70 	struct ioreq_sendto sendto;
     71 	struct ioreq_close close;
     72 };
     73 
     74 struct conn;
     75 
     76 enum ioreq_type {
     77 	IOREQ_CONNECT,
     78 	IOREQ_RECV,
     79 	IOREQ_RECVFROM,
     80 	IOREQ_SEND,
     81 	IOREQ_SENDTO,
     82 	IOREQ_CLOSE,
     83 };
     84 
     85 struct ioreq {
     86 	enum ioreq_type type;
     87 	union ioreq_tag tag;
     88 };
     89 
     90 int
     91 queue_ioreqs(struct io_uring *uring, struct ioreq *reqs, size_t len);
     92 
     93 struct conn_ops {
     94 	int (*on_connect)(struct conn *conn, struct io_uring *uring);
     95 	int (*on_tls_handshake)(struct conn *conn, struct io_uring *uring);
     96 	int (*on_close)(struct conn *conn, struct io_uring *uring);
     97 
     98 	int (*on_recv)(struct conn *conn, struct io_uring *uring,
     99 		       struct discord_event *ev);
    100 
    101 	int (*on_send)(struct conn *conn, struct io_uring *uring,
    102 		       struct discord_event *ev);
    103 };
    104 
    105 struct conn {
    106 	int socket;
    107 	struct addrinfo *addrinfo, *ai_ptr;
    108 
    109 	SSL *ssl;
    110 	BIO *ssl_bio, *net_bio;
    111 
    112 	struct ioreq ioreq;
    113 
    114 	unsigned char *buf;
    115 	size_t cur, len, cap;
    116 
    117 	struct conn_ops *ops;
    118 };
    119 
    120 inline int
    121 conn_queue_connect(struct conn *conn, struct io_uring *uring)
    122 {
    123 	int sock = socket(conn->ai_ptr->ai_family,
    124 			  conn->ai_ptr->ai_socktype,
    125 			  conn->ai_ptr->ai_protocol);
    126 	if (sock < 0)
    127 		return -1;
    128 
    129 	conn->socket = sock;
    130 
    131 	conn->ioreq.type = IOREQ_CONNECT;
    132 	conn->ioreq.tag.connect.fd = conn->socket;
    133 	conn->ioreq.tag.connect.addr = conn->ai_ptr->ai_addr;
    134 	conn->ioreq.tag.connect.addrlen = conn->ai_ptr->ai_addrlen;
    135 
    136 	char host[NI_MAXHOST], serv[NI_MAXSERV];
    137 	getnameinfo(conn->ai_ptr->ai_addr, conn->ai_ptr->ai_addrlen,
    138 		    host, sizeof host, serv, sizeof serv, NI_NUMERICSERV);
    139 	printf("discord: connecting to %s (%s:%s)\n",
    140 			conn->ai_ptr->ai_canonname, host, serv);
    141 
    142 	return queue_ioreqs(uring, &conn->ioreq, 1);
    143 }
    144 
    145 inline void
    146 conn_finish_connect(struct conn *conn, int result)
    147 {
    148 	if (result < 0) {
    149 		close(conn->socket);
    150 		conn->socket = -1;
    151 	}
    152 }
    153 
    154 inline int
    155 conn_queue_recv(struct conn *conn, struct io_uring *uring)
    156 {
    157 	char *buf;
    158 	int len = BIO_nwrite0(conn->net_bio, &buf);
    159 
    160 	conn->ioreq.type = IOREQ_RECV;
    161 	conn->ioreq.tag.recv.fd = conn->socket;
    162 	conn->ioreq.tag.recv.buf = buf;
    163 	conn->ioreq.tag.recv.len = len;
    164 	conn->ioreq.tag.recv.flags = 0;
    165 
    166 	printf("discord: receiving %d bytes\n", len);
    167 
    168 	return queue_ioreqs(uring, &conn->ioreq, 1);
    169 }
    170 
    171 inline void
    172 conn_finish_recv(struct conn *conn, int result)
    173 {
    174 	printf("discord: received %d bytes\n", result);
    175 
    176 	BIO_nwrite(conn->net_bio, NULL, result);
    177 }
    178 
    179 inline int
    180 conn_queue_send(struct conn *conn, struct io_uring *uring)
    181 {
    182 	char *buf;
    183 	int len = BIO_nread0(conn->net_bio, &buf);
    184 
    185 	conn->ioreq.type = IOREQ_SEND;
    186 	conn->ioreq.tag.send.fd = conn->socket;
    187 	conn->ioreq.tag.send.buf = buf;
    188 	conn->ioreq.tag.send.len = len;
    189 	conn->ioreq.tag.send.flags = 0;
    190 
    191 	printf("discord: sending %d bytes\n", len);
    192 
    193 	return queue_ioreqs(uring, &conn->ioreq, 1);
    194 }
    195 
    196 inline void
    197 conn_finish_send(struct conn *conn, int result)
    198 {
    199 	printf("discord: sent %d bytes\n", result);
    200 
    201 	BIO_nread(conn->net_bio, NULL, result);
    202 }
    203 
    204 inline int
    205 conn_queue_close(struct conn *conn, struct io_uring *uring)
    206 {
    207 	conn->ioreq.type = IOREQ_CLOSE;
    208 	conn->ioreq.tag.close.fd = conn->socket;
    209 
    210 	printf("discord: closing socket\n");
    211 
    212 	return queue_ioreqs(uring, &conn->ioreq, 1);
    213 }
    214 
    215 inline void
    216 conn_finish_close(struct conn *conn, int result)
    217 {
    218 	printf("discord: closed socket\n");
    219 }
    220 
    221 inline int
    222 conn_do_tls_handshake(struct conn *conn, struct io_uring *uring)
    223 {
    224 	int ret = SSL_do_handshake(conn->ssl);
    225 	if (ret == 1) /* handshake completed */
    226 		return conn->ops->on_tls_handshake(conn, uring);
    227 
    228 	if (ret == 0) /* connection closed */
    229 		return -1;
    230 
    231 	int err = SSL_get_error(conn->ssl, ret);
    232 	if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
    233 		goto error;
    234 
    235 	int pending = BIO_ctrl_pending(conn->net_bio);
    236 	if (pending) /* need to send bytes to advance handshake */
    237 		return conn_queue_send(conn, uring);
    238 
    239 	int expecting = BIO_ctrl_get_read_request(conn->net_bio);
    240 	if (expecting) /* need to recv bytes to advance handshake */
    241 		return conn_queue_recv(conn, uring);
    242 
    243 error:
    244 	fprintf(stderr, "discord: failed tls handshake: %d\n", err);
    245 	ERR_print_errors_fp(stderr);
    246 	return -1;
    247 }
    248 
    249 inline int
    250 conn_do_io(struct conn *conn, struct io_uring *uring, int result,
    251 	   struct discord_event *ev)
    252 {
    253 	switch (conn->ioreq.type) {
    254 	case IOREQ_CONNECT: {
    255 		conn_finish_connect(conn, result);
    256 
    257 		return conn->ops->on_connect(conn, uring);
    258 	} break;
    259 
    260 	case IOREQ_RECV: {
    261 		conn_finish_recv(conn, result);
    262 
    263 		if (!SSL_is_init_finished(conn->ssl))
    264 			return conn_do_tls_handshake(conn, uring);
    265 
    266 		return conn->ops->on_recv(conn, uring, ev);
    267 	} break;
    268 
    269 	case IOREQ_RECVFROM: {
    270 		// TODO: simply use connect() + recv() for UDP?
    271 		assert(0);
    272 		return 0;
    273 	} break;
    274 
    275 	case IOREQ_SEND: {
    276 		conn_finish_send(conn, result);
    277 
    278 		if (!SSL_is_init_finished(conn->ssl))
    279 			return conn_do_tls_handshake(conn, uring);
    280 
    281 		return conn->ops->on_send(conn, uring, ev);
    282 	} break;
    283 
    284 	case IOREQ_SENDTO: {
    285 		// TODO: simply use connect() + send() for UDP?
    286 		assert(0);
    287 		return 0;
    288 	} break;
    289 
    290 	case IOREQ_CLOSE: {
    291 		conn_finish_close(conn, result);
    292 
    293 		return conn->ops->on_close(conn, uring);
    294 	} break;
    295 	}
    296 }
    297 
    298 #define GATEWAY_HOST "gateway.discord.gg"
    299 #define GATEWAY_PORT "443"
    300 
    301 struct discord_gateway {
    302 	struct conn conn;
    303 
    304 	enum { GATEWAY_CONN, GATEWAY_HTTP, GATEWAY_WS, } state;
    305 
    306 	struct {
    307 		int foo;
    308 	} http;
    309 
    310 	struct {
    311 		int foo;
    312 	} ws;
    313 
    314 	unsigned char buf[4096];
    315 };
    316 
    317 #define VOICE_HOST "voice.discord.com"
    318 #define VOICE_PORT "443"
    319 
    320 struct discord_voice {
    321 	struct conn conn;
    322 
    323 	unsigned char buf[4096];
    324 };
    325 
    326 #define DISCORD_HOST "discord.com"
    327 #define DISCORD_PORT "443"
    328 
    329 struct discord {
    330 	struct arena arena;
    331 
    332 	struct io_uring io_uring;
    333 
    334 	SSL_CTX *ssl_ctx;
    335 
    336 	struct discord_gateway gateway;
    337 	struct discord_voice voice;
    338 };
    339 
    340 int
    341 gateway_connect(struct conn *conn, struct io_uring *uring,
    342 		char const *host, char const *port);
    343 
    344 int
    345 gateway_on_connect(struct conn *conn, struct io_uring *uring);
    346 
    347 int
    348 gateway_on_tls_handshake(struct conn *conn, struct io_uring *uring);
    349 
    350 int
    351 gateway_on_close(struct conn *conn, struct io_uring *uring);
    352 
    353 int
    354 gateway_recv(struct conn *conn, struct io_uring *uring);
    355 
    356 int
    357 gateway_on_recv(struct conn *conn, struct io_uring *uring,
    358 		struct discord_event *ev);
    359 
    360 int
    361 gateway_send(struct conn *conn, struct io_uring *uring);
    362 
    363 int
    364 gateway_on_send(struct conn *conn, struct io_uring *uring,
    365 		struct discord_event *ev);
    366 
    367 int
    368 voice_connect(struct conn *conn, struct io_uring *uring,
    369 	      char const *host, char const *port);
    370 
    371 int
    372 voice_on_connect(struct conn *conn, struct io_uring *uring);
    373 
    374 int
    375 voice_on_tls_handshake(struct conn *conn, struct io_uring *uring);
    376 
    377 int
    378 voice_on_close(struct conn *conn, struct io_uring *uring);
    379 
    380 int
    381 voice_recv(struct conn *conn, struct io_uring *uring);
    382 
    383 int
    384 voice_on_recv(struct conn *conn, struct io_uring *uring,
    385 	      struct discord_event *ev);
    386 
    387 int
    388 voice_send(struct conn *conn, struct io_uring *uring);
    389 
    390 int
    391 voice_on_send(struct conn *conn, struct io_uring *uring,
    392 	      struct discord_event *ev);
    393 
    394 #endif /* INTERNAL_H */