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 */