├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── certs ├── server.cert └── server.key ├── client.c ├── lsquic_utils.c ├── lsquic_utils.h ├── net.c ├── net.h └── server.c /.gitignore: -------------------------------------------------------------------------------- 1 | # intelliJ 2 | .idea/ 3 | *.iml 4 | 5 | # gdb 6 | .gdb_history 7 | 8 | # binaries 9 | client 10 | server 11 | 12 | # cmake 13 | cmake-build-debug/ 14 | cmake_install.cmake 15 | CMakeCache.txt 16 | CMakeFiles/ 17 | *.cbp 18 | Makefile 19 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "boringssl"] 2 | path = boringssl 3 | url = https://github.com/google/boringssl 4 | [submodule "lsquic"] 5 | path = lsquic 6 | ; url = https://github.com/litespeedtech/lsquic.git 7 | url = https://github.com/dtikhonov/lsquic 8 | branch = 202002141134-tutorial-mods 9 | [submodule "libev"] 10 | path = libev 11 | url = https://github.com/enki/libev.git 12 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.5) 2 | PROJECT(lsquic-demo C) 3 | 4 | IF(CMAKE_BUILD_TYPE STREQUAL "") 5 | SET(CMAKE_BUILD_TYPE Debug) 6 | ENDIF() 7 | MESSAGE(STATUS "Build type: ${CMAKE_BUILD_TYPE}") 8 | IF (CMAKE_BUILD_TYPE STREQUAL Debug) 9 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0") 10 | ELSEIF (CMAKE_BUILD_TYPE STREQUAL Debug) 11 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2") 12 | ENDIF() 13 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} $ENV{EXTRA_CFLAGS}") 14 | 15 | MESSAGE(STATUS "Compiler flags: ${CMAKE_C_FLAGS}") 16 | 17 | ADD_SUBDIRECTORY(boringssl) 18 | 19 | INCLUDE_DIRECTORIES(boringssl/include) 20 | INCLUDE_DIRECTORIES(lsquic/include) 21 | INCLUDE_DIRECTORIES(lsquic/src/liblsquic) # For lsquic_xxhash.h 22 | ADD_SUBDIRECTORY(lsquic/src/liblsquic) 23 | 24 | INCLUDE_DIRECTORIES(libev) 25 | add_executable(client client.c net.c net.h lsquic_utils.c lsquic_utils.h) 26 | add_executable(server server.c net.c net.h lsquic_utils.c lsquic_utils.h) 27 | 28 | SET(LIBS lsquic crypto ssl z m ev) 29 | IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") 30 | # If using older glibc, need to link with -lrt. See clock_getres(2). 31 | EXECUTE_PROCESS( 32 | COMMAND ${PROJECT_SOURCE_DIR}/print-glibc-version.sh ${CMAKE_C_COMPILER} 33 | OUTPUT_VARIABLE GLIBC_VERSION) 34 | IF(NOT GLIBC_VERSION EQUAL "" AND GLIBC_VERSION VERSION_LESS 2.17) 35 | SET(LIBS ${LIBS} rt) 36 | ENDIF() 37 | ENDIF() 38 | TARGET_LINK_LIBRARIES(client ${LIBS}) 39 | TARGET_LINK_LIBRARIES(server ${LIBS}) 40 | -------------------------------------------------------------------------------- /certs/server.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDazCCAlOgAwIBAgIUTSiKIYGf+WqeuJNMMDiZ6g4WaqgwDQYJKoZIhvcNAQEL 3 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDExMTcyMzMwMTlaFw0yMDEy 5 | MTcyMzMwMTlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 6 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 7 | AQUAA4IBDwAwggEKAoIBAQCo6jZr7HnioMd8jDSk0iJU8RCnedMCJl+RJWQ1PKdl 8 | o3C93A2Ra2a5Qz7CNnuVuDUCCeIC2+YjlQbponQ54SQd/ZBharA7bC7OK9G18WMi 9 | 8ZUIOsM6pm1mSPEfSdmZ7k+N4sSzyu5baSmnLJvytDyeaX9oqNZCPRg8w8krTIRp 10 | emCZ9ejUegZuKbiQ6h2+ywIxdRDY0jcNL8YGsa/GFo0hyDMSoTE8wVYlmLq0h9/O 11 | kscoIXQbprDRSFhPvyLn6Igjpprbhbxthp4OXY/0iUGzbhIuIHjhzTqckiDLU8VX 12 | Py2EZsUAwV88drI68tg8LyA6FnVm1czs9DxqhWTc4TWFAgMBAAGjUzBRMB0GA1Ud 13 | DgQWBBQe7sycR1ErAWp2qoRBbDeXKA4vvTAfBgNVHSMEGDAWgBQe7sycR1ErAWp2 14 | qoRBbDeXKA4vvTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCA 15 | wKgJQ/3iKPEurPR6krEQIeuB+ZarbFEWLL+ixD8zr9GiGwdBHAJpe3BHTQ4kwHsK 16 | cwRYZSOi5dFdSJFykwEEX2jeCDn+m5jsQ6WJdfTGMWBGnfHqjk9JGdLzFzNkLedk 17 | ku8kyenGpbKSPvKz4BY3MGUTuUJ0WcRk18S2Nu/N1MQ4HzWIpE7q42rJa3D0cQZN 18 | F8GbELFwlRExRJ507AjPCrDqA29xmNGAYsejAcn4QMwd8TqRPPAbLWhxFteoue5e 19 | ZiuaS1ViFvy2YIltb+ymrampPWBjuw4WoBzTV/a53KT+whngNxUFUUP4QdXDL9DI 20 | OhKrrZCiKe6kW6H/rrtX 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /certs/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCo6jZr7HnioMd8 3 | jDSk0iJU8RCnedMCJl+RJWQ1PKdlo3C93A2Ra2a5Qz7CNnuVuDUCCeIC2+YjlQbp 4 | onQ54SQd/ZBharA7bC7OK9G18WMi8ZUIOsM6pm1mSPEfSdmZ7k+N4sSzyu5baSmn 5 | LJvytDyeaX9oqNZCPRg8w8krTIRpemCZ9ejUegZuKbiQ6h2+ywIxdRDY0jcNL8YG 6 | sa/GFo0hyDMSoTE8wVYlmLq0h9/OkscoIXQbprDRSFhPvyLn6Igjpprbhbxthp4O 7 | XY/0iUGzbhIuIHjhzTqckiDLU8VXPy2EZsUAwV88drI68tg8LyA6FnVm1czs9Dxq 8 | hWTc4TWFAgMBAAECggEBAIKko3LVc2/U6nVp+01/OQ55ZGgj0Q/YnhGk2dxTDSWP 9 | Q6OUjunqJ4kieWe8u9kQn+7ztdqAOX9LxAzvmPUrLMQFWaX+3k8XXRwYyBnwmLxu 10 | HNZAnL6/+d0ijVulwqdLqjwTKA/m5HPO4mhqGDOfM0NSVTidXRneJuDYjL7wb3kj 11 | 68TbqamQGmlE25fBp52WoHpirao7fY1wQ1uAP9DJo0O0dHf9LnQMsXmYCPF6F8ew 12 | 35Rb6aOkImSyOHBr2ClMbvHds/wONYAuhKO5qbw35sJ+W0dQq4wTgE0HJQAcfbOb 13 | srhcakX5VDCRu4EytAK20i4VRMb5rUEsFLgebOXlSAECgYEA1hpx6hyqk3ICuu3u 14 | qEM+ALREGSf7EAOwKCUCw0g9M9o8JeBCkCIxPdByHQHdYtJQUqEoarat12TojQAY 15 | oyG7Z0J9ayS9XO0X49GX4RryCtbBucEqpiPFokRbSh3Vv/f5vwx7xr8jEOUUyjhY 16 | YDrQEPwfe0XQCRbgMmXbc12e7B8CgYEAyfgLMcMnq+hG91nItvDknI37wVKSbSIn 17 | 75mVkt7FXmJBbKYox1HcMDhVy4yCXFR1U9q2dgCzUuoB6N+g9fhqa44Gu4sioLjQ 18 | 9zx1IVNHhcJNz6tyAsI8iu2Ci6ayZUp0wMqWxyIVxRRIJfmkiyEnrmjBYwlvMrsf 19 | PeA0GY5b6dsCgYEAxROAidOrO591XeWHicCjgPhtuo4vrlUGwF0ZBvImy+T0+pRM 20 | EOo4U0Lc11CRHNakcvq+/kN7D0pmOOu+weJZ4zmzwn0GBVSEagLSf7VS2KFHnxls 21 | jZNd5vhu43FFALhcXfw6rv3fZDzJfz6QMrvQK+I3hDwFu2ggJKPXdYDWuVcCgYBU 22 | MlSwXRQXxVB4Y3SaV1hmr4YpdLvYRSny58sEHFyZWUq++ZH8bfLmI0YZAi2CQi9b 23 | Us1H6kjXovhC1Z9rizEKP34tBvbbm9B5kTmfN/GUIImBro5r+f91R0hp4YdzVJPR 24 | n2M2Bs6ehzMAqc7ftWWufrtIBzHVK0794460rLVCiwKBgQDAPbojMflx8zQSvzFr 25 | +waLCPFIafAMe5zh3xO4pdXO4Bug+ywsxVSuZ+BcTTYIqytT4g+dwZZYY7+Q1J4Q 26 | XTzuXmt3AHSXPSvlL48AAYZ4Xhe/mytqBituT+y4q0Kz9lJ0nqn4RpxrdwDCL3e7 27 | DNrWW4lscBWsfKc7UzwbAjABNQ== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "ev.h" 8 | #include "net.h" 9 | #include "lsquic_utils.h" 10 | 11 | static lsquic_conn_ctx_t *on_new_conn_cb (void *ea_stream_if_ctx, lsquic_conn_t *conn); 12 | static void on_conn_closed_cb (lsquic_conn_t *conn); 13 | static lsquic_stream_ctx_t *on_new_stream_cb (void *ea_stream_if_ctx, lsquic_stream_t *stream); 14 | static void on_read_cb (lsquic_stream_t *stream, lsquic_stream_ctx_t *h); 15 | static void on_write_cb (lsquic_stream_t *stream, lsquic_stream_ctx_t *h); 16 | static void on_hsk_done (lsquic_conn_t *c, enum lsquic_hsk_status s); 17 | 18 | typedef struct State { 19 | // event loop 20 | struct ev_loop *loop; 21 | ev_io sock_watcher; 22 | ev_io stdin_watcher; 23 | ev_timer conn_watcher; 24 | 25 | // lsquic 26 | int sockfd; 27 | struct sockaddr_storage local_sas; 28 | lsquic_engine_t *engine; 29 | lsquic_conn_t *conn; 30 | lsquic_stream_t *stream; 31 | 32 | // msg to send 33 | char *buf; 34 | int size; 35 | } State; 36 | 37 | static void process_conns(State *state); 38 | 39 | const struct lsquic_stream_if stream_if = { 40 | .on_new_conn = on_new_conn_cb, 41 | .on_conn_closed = on_conn_closed_cb, 42 | .on_new_stream = on_new_stream_cb, 43 | .on_read = on_read_cb, 44 | .on_write = on_write_cb, 45 | .on_hsk_done = on_hsk_done 46 | }; 47 | 48 | 49 | static int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) 50 | { 51 | struct msghdr msg; 52 | int *sockfd; 53 | unsigned n; 54 | 55 | memset(&msg, 0, sizeof(msg)); 56 | sockfd = (int *) ctx; 57 | 58 | for (n = 0; n < n_specs; ++n) 59 | { 60 | msg.msg_name = (void *) specs[n].dest_sa; 61 | msg.msg_namelen = sizeof(struct sockaddr_in); 62 | msg.msg_iov = specs[n].iov; 63 | msg.msg_iovlen = specs[n].iovlen; 64 | if (sendmsg(*sockfd, &msg, 0) < 0) { 65 | perror("cannot send\n"); 66 | break; 67 | } 68 | } 69 | 70 | return (int) n; 71 | } 72 | 73 | static void read_sock(EV_P_ ev_io *w, int revents) { 74 | State *state = w->data; 75 | ssize_t nread; 76 | struct sockaddr_storage peer_sas; 77 | unsigned char buf[0x1000]; 78 | struct iovec vec[1] = {{ buf, sizeof(buf) }}; 79 | 80 | struct msghdr msg = { 81 | .msg_name = &peer_sas, 82 | .msg_namelen = sizeof(peer_sas), 83 | .msg_iov = vec, 84 | .msg_iovlen = 1, 85 | }; 86 | nread = recvmsg(w->fd, &msg, 0); 87 | if (-1 == nread) { 88 | return; 89 | } 90 | 91 | // TODO handle ECN properly 92 | int ecn = 0; 93 | 94 | (void) lsquic_engine_packet_in(state->engine, buf, nread, 95 | (struct sockaddr *) &state->local_sas, 96 | (struct sockaddr *) &peer_sas, 97 | (void *) (uintptr_t) w->fd, ecn); 98 | 99 | process_conns(state); 100 | } 101 | 102 | static void process_conns_cb(EV_P_ ev_timer *conn_watcher, int revents) { 103 | process_conns(conn_watcher->data); 104 | } 105 | 106 | void process_conns(State *state) { 107 | int diff; 108 | ev_tstamp timeout; 109 | 110 | ev_timer_stop(state->loop, &state->conn_watcher); 111 | lsquic_engine_process_conns(state->engine); 112 | if (lsquic_engine_earliest_adv_tick(state->engine, &diff)) { 113 | if (diff > 0) { 114 | timeout = (ev_tstamp) diff / 1000000; 115 | } else { 116 | timeout = 0; 117 | } 118 | ev_timer_init(&state->conn_watcher, process_conns_cb, timeout, 0.); 119 | ev_timer_start(state->loop, &state->conn_watcher); 120 | } 121 | } 122 | 123 | static lsquic_conn_ctx_t *on_new_conn_cb(void *ea_stream_if_ctx, lsquic_conn_t *conn) { 124 | printf("On new connection\n"); 125 | State *state = ea_stream_if_ctx; 126 | fflush(stdout); 127 | return (void *) state; 128 | } 129 | 130 | static void on_conn_closed_cb(lsquic_conn_t *conn) { 131 | printf("On connection close\n"); 132 | char errbuf[2048]; 133 | enum LSQUIC_CONN_STATUS status = lsquic_conn_status(conn, errbuf, 2048); 134 | printf("errbuf: %s\n", errbuf); 135 | printf("conn status: %s\n", get_conn_status_str(status)); 136 | fflush(stdout); 137 | } 138 | 139 | static void on_hsk_done(lsquic_conn_t *conn, enum lsquic_hsk_status status) { 140 | State *state = (void *) lsquic_conn_get_ctx(conn); 141 | 142 | switch (status) 143 | { 144 | case LSQ_HSK_OK: 145 | case LSQ_HSK_RESUMED_OK: 146 | printf("handshake successful, start stdin watcher\n"); 147 | fflush(stdout); 148 | lsquic_conn_make_stream(state->conn); 149 | break; 150 | default: 151 | printf("handshake failed\n"); 152 | fflush(stdout); 153 | break; 154 | } 155 | } 156 | 157 | static lsquic_stream_ctx_t *on_new_stream_cb(void *ea_stream_if_ctx, lsquic_stream_t *stream) { 158 | printf("On new stream\n"); 159 | fflush(stdout); 160 | State *state = ea_stream_if_ctx; 161 | state->stream = stream; 162 | ev_io_start(state->loop, &state->stdin_watcher); 163 | return (void *) state; 164 | } 165 | 166 | static void on_read_cb(lsquic_stream_t *stream, lsquic_stream_ctx_t *h) { 167 | lsquic_conn_t *conn = lsquic_stream_conn(stream); 168 | State *state = (void *) lsquic_conn_get_ctx(conn); 169 | 170 | unsigned char buf[256] = {0}; 171 | 172 | ssize_t nr = lsquic_stream_read(stream, buf, sizeof(buf)); 173 | 174 | buf[nr] = '\0'; 175 | printf("recv %zd bytes: %s\n", nr, buf); 176 | 177 | lsquic_stream_wantread(stream, 0); 178 | ev_io_start(state->loop, &state->stdin_watcher); 179 | } 180 | 181 | static void on_write_cb(lsquic_stream_t *stream, lsquic_stream_ctx_t *h) { 182 | lsquic_conn_t *conn = lsquic_stream_conn(stream); 183 | State *state = (void *) lsquic_conn_get_ctx(conn); 184 | 185 | lsquic_stream_write(stream, state->buf, state->size); 186 | lsquic_stream_wantwrite(stream, 0); 187 | lsquic_stream_flush(stream); 188 | lsquic_stream_wantread(stream, 1); 189 | } 190 | 191 | void read_stdin(EV_P_ ev_io *w, int revents) { 192 | State *state = w->data; 193 | char *lineptr = NULL; 194 | size_t n = 0; 195 | ssize_t read_bytes = getline(&lineptr, &n, stdin); 196 | if (read_bytes == -1) { 197 | printf("getline error\n"); 198 | fflush(stdout); 199 | exit(EXIT_FAILURE); 200 | } 201 | state->buf = lineptr; 202 | state->size = read_bytes; 203 | ev_io_stop(state->loop, w); 204 | lsquic_stream_wantwrite(state->stream, 1); 205 | process_conns(state); 206 | } 207 | 208 | void create_event_loop(State *state) { 209 | state->loop = EV_DEFAULT; 210 | state->sock_watcher.data = state; 211 | state->stdin_watcher.data = state; 212 | state->conn_watcher.data = state; 213 | ev_io_init (&state->sock_watcher, read_sock, state->sockfd, EV_READ); 214 | ev_io_start (state->loop, &state->sock_watcher); 215 | ev_io_init (&state->stdin_watcher, read_stdin, 0 /* STDIN*/, EV_READ); 216 | ev_init(&state->conn_watcher, process_conns_cb); 217 | } 218 | 219 | int main(int argc, char **argv) { 220 | State state; 221 | state.sockfd = create_sock(argv[1], atoi(argv[2]), &state.local_sas); 222 | create_event_loop(&state); 223 | 224 | struct sockaddr_in peer_addr = new_addr(argv[3], atoi(argv[4])); 225 | 226 | if (0 != lsquic_global_init(LSQUIC_GLOBAL_CLIENT)) { 227 | printf("Cannot init\n"); 228 | fflush(stdout); 229 | exit(EXIT_FAILURE); 230 | } 231 | init_logger("info"); 232 | 233 | 234 | struct lsquic_engine_api engine_api = { 235 | .ea_packets_out = send_packets_out, 236 | .ea_packets_out_ctx = (void *) &state.sockfd, 237 | .ea_stream_if = &stream_if, 238 | .ea_stream_if_ctx = (void *) &state, 239 | }; 240 | 241 | state.engine = lsquic_engine_new(0, &engine_api); 242 | state.conn = lsquic_engine_connect(state.engine, N_LSQVER, 243 | (struct sockaddr *) &state.local_sas, 244 | (struct sockaddr *) &peer_addr, (void *) &state.sockfd, NULL, 245 | NULL, 0, NULL, 0, NULL, 0); 246 | 247 | if(!state.conn) { 248 | printf("Cannot create connection\n"); 249 | fflush(stdout); 250 | exit(EXIT_FAILURE); 251 | } 252 | 253 | lsquic_engine_process_conns(state.engine); 254 | 255 | ev_run (state.loop, 0); 256 | 257 | lsquic_global_cleanup(); 258 | return 0; 259 | } 260 | -------------------------------------------------------------------------------- /lsquic_utils.c: -------------------------------------------------------------------------------- 1 | #include "lsquic_utils.h" 2 | 3 | char *get_conn_status_str(enum LSQUIC_CONN_STATUS status) { 4 | switch(status) { 5 | case LSCONN_ST_HSK_IN_PROGRESS: 6 | return "LSCONN_ST_HSK_IN_PROGRESS"; 7 | case LSCONN_ST_CONNECTED: 8 | return "LSCONN_ST_CONNECTED"; 9 | case LSCONN_ST_HSK_FAILURE: 10 | return "LSCONN_ST_HSK_FAILURE"; 11 | case LSCONN_ST_GOING_AWAY: 12 | return "LSCONN_ST_GOING_AWAY"; 13 | case LSCONN_ST_TIMED_OUT: 14 | return "LSCONN_ST_TIMED_OUT"; 15 | case LSCONN_ST_RESET: 16 | return "LSCONN_ST_RESET"; 17 | case LSCONN_ST_USER_ABORTED: 18 | return "LSCONN_ST_USER_ABORTED"; 19 | case LSCONN_ST_ERROR: 20 | return "LSCONN_ST_ERROR"; 21 | case LSCONN_ST_CLOSED: 22 | return "LSCONN_ST_CLOSED"; 23 | case LSCONN_ST_PEER_GOING_AWAY: 24 | return "LSCONN_ST_PEER_GOING_AWAY"; 25 | default: 26 | return "UNKNOWN"; 27 | } 28 | } 29 | 30 | static int log_buf(void *logger_ctx, const char *buf, size_t len) { 31 | (void) logger_ctx; 32 | (void) len; 33 | printf("%s", buf); 34 | fflush(stdout); 35 | return 0; 36 | } 37 | 38 | struct lsquic_logger_if logger_if = { 39 | .log_buf = log_buf 40 | }; 41 | 42 | void init_logger(char *level) { 43 | lsquic_logger_init(&logger_if, NULL, LLTS_HHMMSSMS); 44 | if(lsquic_set_log_level(level) != 0) { 45 | printf("Cannot set logger to %s level\n", level); 46 | fflush(stdout); 47 | exit(EXIT_FAILURE); 48 | } 49 | printf("Logger initialized\n"); 50 | fflush(stdout); 51 | } 52 | -------------------------------------------------------------------------------- /lsquic_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef LSQUIC_DEMO_LSQUIC_UTILS_H 4 | #define LSQUIC_DEMO_LSQUIC_UTILS_H 5 | 6 | #endif //LSQUIC_DEMO_LSQUIC_UTILS_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | char *get_conn_status_str(enum LSQUIC_CONN_STATUS status); 13 | 14 | void init_logger(char *level); 15 | -------------------------------------------------------------------------------- /net.c: -------------------------------------------------------------------------------- 1 | #include "net.h" 2 | #include "string.h" 3 | 4 | struct sockaddr_in new_addr(char *ip, unsigned int port) { 5 | struct sockaddr_in addr; 6 | addr.sin_family = AF_INET; 7 | addr.sin_port = htons(port); 8 | addr.sin_addr.s_addr = inet_addr(ip); 9 | return addr; 10 | } 11 | 12 | int create_sock(char *ip, unsigned int port, struct sockaddr_storage *local_sas) { 13 | int sockfd = socket(AF_INET, SOCK_DGRAM, 0); 14 | if (sockfd == -1) { 15 | printf("Error creating socket\n"); 16 | fflush(stdout); 17 | exit(EXIT_FAILURE); 18 | } 19 | 20 | struct sockaddr_in local_addr = new_addr(ip, port); 21 | if(bind(sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr)) != 0) { 22 | printf("Cannot bind"); 23 | fflush(stdout); 24 | exit(EXIT_FAILURE); 25 | } 26 | 27 | if(!memcpy(local_sas, &local_addr, sizeof(local_addr))) { 28 | printf("memcpy local_sas error\n"); 29 | fflush(stdout); 30 | exit(EXIT_FAILURE); 31 | } 32 | return sockfd; 33 | } 34 | -------------------------------------------------------------------------------- /net.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef LSQUIC_DEMO_NET_H 4 | #define LSQUIC_DEMO_NET_H 5 | 6 | #endif //LSQUIC_DEMO_NET_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | struct sockaddr_in new_addr(char *ip, uint port); 15 | int create_sock(char *ip, unsigned int port, struct sockaddr_storage *local_sas); -------------------------------------------------------------------------------- /server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "ev.h" 10 | 11 | #include "net.h" 12 | #include "lsquic_utils.h" 13 | 14 | 15 | static lsquic_conn_ctx_t *on_new_conn_cb(void *ea_stream_if_ctx, lsquic_conn_t *conn); 16 | 17 | static void on_conn_closed_cb(lsquic_conn_t *conn); 18 | 19 | static lsquic_stream_ctx_t *on_new_stream_cb(void *ea_stream_if_ctx, lsquic_stream_t *stream); 20 | 21 | static void on_read_cb(lsquic_stream_t *stream, lsquic_stream_ctx_t *h); 22 | 23 | static void on_write_cb(lsquic_stream_t *stream, lsquic_stream_ctx_t *h); 24 | 25 | typedef struct State { 26 | // event loop 27 | struct ev_loop *loop; 28 | ev_io sock_watcher; 29 | ev_timer conn_watcher; 30 | 31 | // lsquic 32 | int sockfd; 33 | struct sockaddr_storage local_sas; 34 | lsquic_engine_t *engine; 35 | 36 | // SSL 37 | SSL_CTX *ssl_ctx; 38 | 39 | // response 40 | char *response; 41 | int size; 42 | } State; 43 | 44 | void process_conns(State *state); 45 | 46 | 47 | const struct lsquic_stream_if stream_if = { 48 | .on_new_conn = on_new_conn_cb, 49 | .on_conn_closed = on_conn_closed_cb, 50 | .on_new_stream = on_new_stream_cb, 51 | .on_read = on_read_cb, 52 | .on_write = on_write_cb, 53 | }; 54 | 55 | 56 | static int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { 57 | struct msghdr msg; 58 | int *sockfd; 59 | unsigned n; 60 | 61 | memset(&msg, 0, sizeof(msg)); 62 | sockfd = (int *) ctx; 63 | 64 | for (n = 0; n < n_specs; ++n) { 65 | msg.msg_name = (void *) specs[n].dest_sa; 66 | msg.msg_namelen = sizeof(struct sockaddr_in); 67 | msg.msg_iov = specs[n].iov; 68 | msg.msg_iovlen = specs[n].iovlen; 69 | if (sendmsg(*sockfd, &msg, 0) < 0) { 70 | perror("sendmsg"); 71 | break; 72 | } 73 | } 74 | 75 | return (int) n; 76 | } 77 | 78 | static lsquic_conn_ctx_t *on_new_conn_cb(void *ea_stream_if_ctx, lsquic_conn_t *conn) { 79 | printf("On new connection\n"); 80 | fflush(stdout); 81 | State *state = ea_stream_if_ctx; 82 | return (void *) state; 83 | } 84 | 85 | static void on_conn_closed_cb(lsquic_conn_t *conn) { 86 | printf("On connection close\n"); 87 | fflush(stdout); 88 | } 89 | 90 | static lsquic_stream_ctx_t *on_new_stream_cb(void *ea_stream_if_ctx, lsquic_stream_t *stream) { 91 | printf("On new stream\n"); 92 | fflush(stdout); 93 | lsquic_stream_wantread(stream, 1); 94 | return NULL; 95 | } 96 | 97 | static void on_read_cb(lsquic_stream_t *stream, lsquic_stream_ctx_t *h) { 98 | lsquic_conn_t *conn = lsquic_stream_conn(stream); 99 | State *state = (void *) lsquic_conn_get_ctx(conn); 100 | 101 | unsigned char buf[256] = {0}; 102 | ssize_t nr = lsquic_stream_read(stream, buf, sizeof(buf)); 103 | buf[nr] = '\0'; 104 | printf("recv %zd bytes: %s\n", nr, buf); 105 | fflush(stdout); 106 | 107 | char *response = (char *) malloc(sizeof(char) * nr + 2); 108 | char *server_prefix = "s:"; 109 | 110 | int response_size = snprintf(response, nr + strlen(server_prefix), "%s%s", server_prefix, buf); 111 | state->response = response; 112 | state->size = response_size; 113 | 114 | lsquic_stream_wantread(stream, 0); 115 | lsquic_stream_wantwrite(stream, 1); 116 | } 117 | 118 | static void on_write_cb(lsquic_stream_t *stream, lsquic_stream_ctx_t *h) { 119 | lsquic_conn_t *conn = lsquic_stream_conn(stream); 120 | State *state = (void *) lsquic_conn_get_ctx(conn); 121 | 122 | lsquic_stream_write(stream, state->response, state->size); 123 | lsquic_stream_wantwrite(stream, 0); 124 | lsquic_stream_wantread(stream, 1); 125 | lsquic_stream_flush(stream); 126 | } 127 | 128 | 129 | SSL_CTX *ssl_ctx; 130 | 131 | struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx) { 132 | // TODO pass ssl_ctx in peer_ctx 133 | return ssl_ctx; 134 | } 135 | 136 | void create_ssl_ctx(State *state) { 137 | SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); 138 | if (SSL_CTX_use_certificate_chain_file(ssl_ctx, "./certs/server.cert") != 1) { 139 | printf("Cannot load cert\n"); 140 | fflush(stdout); 141 | exit(EXIT_FAILURE); 142 | } 143 | if (SSL_CTX_use_PrivateKey_file(ssl_ctx, "./certs/server.key", SSL_FILETYPE_PEM) != 1) { 144 | printf("Cannot load key\n"); 145 | fflush(stdout); 146 | exit(EXIT_FAILURE); 147 | } 148 | SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); 149 | SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); 150 | 151 | state->ssl_ctx = ssl_ctx; 152 | } 153 | 154 | static void read_sock(EV_P_ ev_io *w, int revents) { 155 | State *state = w->data; 156 | ssize_t nread; 157 | struct sockaddr_storage peer_sas; 158 | unsigned char buf[0x1000]; 159 | struct iovec vec[1] = {{buf, sizeof(buf)}}; 160 | 161 | struct msghdr msg = { 162 | .msg_name = &peer_sas, 163 | .msg_namelen = sizeof(peer_sas), 164 | .msg_iov = vec, 165 | .msg_iovlen = 1, 166 | }; 167 | nread = recvmsg(w->fd, &msg, 0); 168 | if (-1 == nread) { 169 | return; 170 | } 171 | 172 | // TODO handle ECN properly 173 | int ecn = 0; 174 | 175 | (void) lsquic_engine_packet_in(state->engine, buf, nread, 176 | (struct sockaddr *) &state->local_sas, 177 | (struct sockaddr *) &peer_sas, 178 | (void *) (uintptr_t) w->fd, ecn); 179 | 180 | process_conns(state); 181 | } 182 | 183 | static void process_conns_cb(EV_P_ ev_timer *conn_watcher, int revents) { 184 | process_conns(conn_watcher->data); 185 | } 186 | 187 | void process_conns(State *state) { 188 | int diff; 189 | ev_tstamp timeout; 190 | 191 | ev_timer_stop(state->loop, &state->conn_watcher); 192 | lsquic_engine_process_conns(state->engine); 193 | if (lsquic_engine_earliest_adv_tick(state->engine, &diff)) { 194 | if (diff > 0) { 195 | timeout = (ev_tstamp) diff / 1000000; 196 | } else { 197 | timeout = 0; 198 | } 199 | ev_timer_init(&state->conn_watcher, process_conns_cb, timeout, 0.); 200 | ev_timer_start(state->loop, &state->conn_watcher); 201 | } 202 | } 203 | 204 | void create_event_loop(State *state) { 205 | state->loop = EV_DEFAULT; 206 | state->sock_watcher.data = state; 207 | state->conn_watcher.data = state; 208 | ev_io_init (&state->sock_watcher, read_sock, state->sockfd, EV_READ); 209 | ev_io_start(state->loop, &state->sock_watcher); 210 | ev_init(&state->conn_watcher, process_conns_cb); 211 | } 212 | 213 | int main(int argc, char **argv) { 214 | State state; 215 | create_ssl_ctx(&state); 216 | ssl_ctx = state.ssl_ctx; 217 | state.sockfd = create_sock(argv[1], atoi(argv[2]), &state.local_sas); 218 | create_event_loop(&state); 219 | 220 | if (0 != lsquic_global_init(LSQUIC_GLOBAL_SERVER)) { 221 | exit(EXIT_FAILURE); 222 | } 223 | 224 | init_logger("info"); 225 | 226 | struct lsquic_engine_api engine_api = { 227 | .ea_packets_out = send_packets_out, 228 | .ea_packets_out_ctx = (void *) &state.sockfd, 229 | .ea_stream_if = &stream_if, 230 | .ea_stream_if_ctx = (void *) &state, 231 | .ea_get_ssl_ctx = get_ssl_ctx 232 | }; 233 | 234 | state.engine = lsquic_engine_new(LSENG_SERVER, &engine_api); 235 | 236 | ev_run(state.loop, 0); 237 | 238 | lsquic_global_cleanup(); 239 | 240 | return 0; 241 | } 242 | --------------------------------------------------------------------------------