/src/open5gs/lib/sbi/nghttp2-server.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2019,2024 by Sukchan Lee <acetcom@gmail.com> |
3 | | * |
4 | | * This file is part of Open5GS. |
5 | | * |
6 | | * This program is free software: you can redistribute it and/or modify |
7 | | * it under the terms of the GNU Affero General Public License as published by |
8 | | * the Free Software Foundation, either version 3 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | #include "ogs-sbi.h" |
21 | | #include "yuarel.h" |
22 | | |
23 | | #include <netinet/tcp.h> |
24 | | #include <nghttp2/nghttp2.h> |
25 | | |
26 | | #define USE_SEND_DATA_WITH_NO_COPY 1 |
27 | | |
28 | | static void server_init(int num_of_session_pool, int num_of_stream_pool); |
29 | | static void server_final(void); |
30 | | |
31 | | static int server_start(ogs_sbi_server_t *server, |
32 | | int (*cb)(ogs_sbi_request_t *request, void *data)); |
33 | | static void server_graceful_shutdown(ogs_sbi_server_t *server); |
34 | | static void server_stop(ogs_sbi_server_t *server); |
35 | | |
36 | | static bool server_send_rspmem_persistent( |
37 | | ogs_sbi_stream_t *stream, ogs_sbi_response_t *response); |
38 | | static bool server_send_response( |
39 | | ogs_sbi_stream_t *stream, ogs_sbi_response_t *response); |
40 | | |
41 | | static ogs_sbi_server_t *server_from_stream(ogs_sbi_stream_t *stream); |
42 | | |
43 | | static ogs_pool_id_t id_from_stream(ogs_sbi_stream_t *stream); |
44 | | static void *stream_find_by_id(ogs_pool_id_t id); |
45 | | |
46 | | static void xact_attach(ogs_sbi_stream_t *stream, ogs_sbi_xact_t *xact); |
47 | | static void xact_detach(ogs_sbi_xact_t *xact); |
48 | | |
49 | | const ogs_sbi_server_actions_t ogs_nghttp2_server_actions = { |
50 | | server_init, |
51 | | server_final, |
52 | | |
53 | | server_start, |
54 | | server_graceful_shutdown, |
55 | | server_stop, |
56 | | |
57 | | server_send_rspmem_persistent, |
58 | | server_send_response, |
59 | | |
60 | | server_from_stream, |
61 | | |
62 | | id_from_stream, |
63 | | stream_find_by_id, |
64 | | |
65 | | xact_attach, |
66 | | xact_detach, |
67 | | }; |
68 | | |
69 | | struct h2_settings { |
70 | | uint32_t max_concurrent_streams; |
71 | | bool enable_push; |
72 | | }; |
73 | | |
74 | | typedef struct ogs_sbi_session_s { |
75 | | ogs_lnode_t lnode; |
76 | | |
77 | | ogs_sock_t *sock; |
78 | | ogs_sockaddr_t *addr; |
79 | | struct { |
80 | | ogs_poll_t *read; |
81 | | ogs_poll_t *write; |
82 | | } poll; |
83 | | |
84 | | nghttp2_session *session; |
85 | | ogs_list_t write_queue; |
86 | | |
87 | | ogs_sbi_server_t *server; |
88 | | ogs_list_t stream_list; |
89 | | int32_t last_stream_id; |
90 | | |
91 | | struct h2_settings settings; |
92 | | SSL* ssl; |
93 | | } ogs_sbi_session_t; |
94 | | |
95 | | typedef struct ogs_sbi_stream_s { |
96 | | ogs_lnode_t lnode; |
97 | | |
98 | | ogs_pool_id_t id; |
99 | | |
100 | | int32_t stream_id; |
101 | | ogs_sbi_request_t *request; |
102 | | bool memory_overflow; |
103 | | |
104 | | ogs_sbi_session_t *session; |
105 | | |
106 | | /* |
107 | | * Outbound SBI transactions originated by this inbound stream. |
108 | | * Populated automatically when ogs_sbi_discover_and_send() sees |
109 | | * xact->assoc_stream_id pointing at this stream, and drained at |
110 | | * stream close so response timers are freed promptly rather |
111 | | * than lingering until the SBI client wait timeout. |
112 | | */ |
113 | | ogs_list_t xact_list; |
114 | | } ogs_sbi_stream_t; |
115 | | |
116 | | static void session_remove(ogs_sbi_session_t *sbi_sess); |
117 | | static void session_remove_all(ogs_sbi_server_t *server); |
118 | | |
119 | | static void stream_remove(ogs_sbi_stream_t *stream); |
120 | | |
121 | | static void accept_handler(short when, ogs_socket_t fd, void *data); |
122 | | static void recv_handler(short when, ogs_socket_t fd, void *data); |
123 | | |
124 | | static int session_set_callbacks(ogs_sbi_session_t *sbi_sess); |
125 | | static int session_send_preface(ogs_sbi_session_t *sbi_sess); |
126 | | static int session_send(ogs_sbi_session_t *sbi_sess); |
127 | | static void session_write_to_buffer( |
128 | | ogs_sbi_session_t *sbi_sess, ogs_pkbuf_t *pkbuf); |
129 | | |
130 | | static OGS_POOL(session_pool, ogs_sbi_session_t); |
131 | | static OGS_POOL(stream_pool, ogs_sbi_stream_t); |
132 | | |
133 | | static void server_init(int num_of_session_pool, int num_of_stream_pool) |
134 | 0 | { |
135 | 0 | ogs_pool_init(&session_pool, num_of_session_pool); |
136 | 0 | ogs_pool_init(&stream_pool, num_of_stream_pool); |
137 | 0 | } |
138 | | |
139 | | static void server_final(void) |
140 | 0 | { |
141 | 0 | ogs_pool_final(&stream_pool); |
142 | 0 | ogs_pool_final(&session_pool); |
143 | 0 | } |
144 | | |
145 | | #ifndef OPENSSL_NO_NEXTPROTONEG |
146 | | static int next_proto_cb(SSL *ssl, const unsigned char **data, |
147 | | unsigned int *len, void *arg) |
148 | 0 | { |
149 | 0 | static unsigned char next_proto_list[256]; |
150 | 0 | (void)ssl; |
151 | 0 | (void)arg; |
152 | |
|
153 | 0 | next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN; |
154 | 0 | memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID, NGHTTP2_PROTO_VERSION_ID_LEN); |
155 | |
|
156 | 0 | *data = next_proto_list; |
157 | 0 | *len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN; |
158 | 0 | return SSL_TLSEXT_ERR_OK; |
159 | 0 | } |
160 | | #endif |
161 | | |
162 | | #if OPENSSL_VERSION_NUMBER >= 0x10002000L |
163 | | static int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, |
164 | | unsigned char *outlen, const unsigned char *in, |
165 | | unsigned int inlen, void *arg) |
166 | 0 | { |
167 | 0 | int rv; |
168 | 0 | (void)ssl; |
169 | 0 | (void)arg; |
170 | |
|
171 | 0 | rv = nghttp2_select_next_protocol((unsigned char **)out, outlen, in, inlen); |
172 | 0 | if (rv != 1) { |
173 | 0 | return SSL_TLSEXT_ERR_NOACK; |
174 | 0 | } |
175 | | |
176 | 0 | return SSL_TLSEXT_ERR_OK; |
177 | 0 | } |
178 | | #endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ |
179 | | |
180 | | static int ssl_ctx_set_proto_versions(SSL_CTX *ssl_ctx, int min, int max) |
181 | 0 | { |
182 | 0 | #if OPENSSL_VERSION_NUMBER >= 0x1010000fL |
183 | 0 | if (SSL_CTX_set_min_proto_version(ssl_ctx, min) != 1 || |
184 | 0 | SSL_CTX_set_max_proto_version(ssl_ctx, max) != 1) { |
185 | 0 | return -1; |
186 | 0 | } |
187 | 0 | return 0; |
188 | | #else /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) */ |
189 | | long int opts = 0; |
190 | | |
191 | | // TODO We depends on the ordering of protocol version macro in |
192 | | // OpenSSL. |
193 | | if (min > TLS1_VERSION) { |
194 | | opts |= SSL_OP_NO_TLSv1; |
195 | | } |
196 | | if (min > TLS1_1_VERSION) { |
197 | | opts |= SSL_OP_NO_TLSv1_1; |
198 | | } |
199 | | if (min > TLS1_2_VERSION) { |
200 | | opts |= SSL_OP_NO_TLSv1_2; |
201 | | } |
202 | | |
203 | | if (max < TLS1_2_VERSION) { |
204 | | opts |= SSL_OP_NO_TLSv1_2; |
205 | | } |
206 | | if (max < TLS1_1_VERSION) { |
207 | | opts |= SSL_OP_NO_TLSv1_1; |
208 | | } |
209 | | |
210 | | SSL_CTX_set_options(ssl_ctx, opts); |
211 | | |
212 | | return 0; |
213 | | #endif /* OPENSSL_VERSION_NUMBER >= 0x1010000fL */ |
214 | 0 | } |
215 | | |
216 | | static SSL_CTX *create_ssl_ctx( |
217 | | const char *key_file, const char *cert_file, |
218 | | const char *sslkeylog_file) |
219 | 0 | { |
220 | 0 | SSL_CTX *ssl_ctx; |
221 | 0 | uint64_t ssl_opts; |
222 | |
|
223 | 0 | ogs_assert(key_file); |
224 | 0 | ogs_assert(cert_file); |
225 | | |
226 | 0 | ssl_ctx = SSL_CTX_new(TLS_server_method()); |
227 | 0 | if (!ssl_ctx) { |
228 | 0 | ogs_error("Could not create SSL/TLS context: %s", ERR_error_string(ERR_get_error(), NULL)); |
229 | 0 | return NULL; |
230 | 0 | } |
231 | | |
232 | | /* Set key log files for each SSL_CTX */ |
233 | 0 | if (sslkeylog_file) { |
234 | | /* Ensure app data is set for SSL objects */ |
235 | 0 | SSL_CTX_set_app_data(ssl_ctx, sslkeylog_file); |
236 | 0 | #if OPENSSL_VERSION_NUMBER >= 0x10101000L |
237 | | /* Set the SSL Key Log callback */ |
238 | 0 | SSL_CTX_set_keylog_callback(ssl_ctx, ogs_sbi_keylog_callback); |
239 | 0 | #endif |
240 | 0 | } |
241 | |
|
242 | 0 | ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | |
243 | 0 | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | |
244 | 0 | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | |
245 | 0 | SSL_OP_SINGLE_ECDH_USE | SSL_OP_SINGLE_DH_USE | |
246 | 0 | SSL_OP_CIPHER_SERVER_PREFERENCE |
247 | 0 | #if OPENSSL_VERSION_NUMBER >= 0x10101000L |
248 | | // The reason for disabling built-in anti-replay in |
249 | | // OpenSSL is that it only works if client gets back |
250 | | // to the same server. The freshness check |
251 | | // described in |
252 | | // https://tools.ietf.org/html/rfc8446#section-8.3 |
253 | | // is still performed. |
254 | 0 | | SSL_OP_NO_ANTI_REPLAY |
255 | 0 | #endif /* OPENSSL_VERSION_NUMBER >= 0x10101000L */ |
256 | 0 | ; |
257 | | |
258 | |
|
259 | 0 | SSL_CTX_set_options(ssl_ctx, ssl_opts); |
260 | |
|
261 | | #if OPENSSL_VERSION_NUMBER >= 0x30000000L |
262 | | if (SSL_CTX_set1_curves_list(ssl_ctx, "P-256") != 1) { |
263 | | ogs_error("SSL_CTX_set1_curves_list failed: %s", ERR_error_string(ERR_get_error(), NULL)); |
264 | | return NULL; |
265 | | } |
266 | | #endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ |
267 | |
|
268 | 0 | SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); |
269 | 0 | SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); |
270 | |
|
271 | 0 | if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) { |
272 | 0 | ogs_warn("Could not load system trusted ca certificates: %s", |
273 | 0 | ERR_error_string(ERR_get_error(), NULL)); |
274 | 0 | } |
275 | |
|
276 | 0 | #define OGS_TLS_MIN_VERSION TLS1_VERSION |
277 | 0 | #ifdef TLS1_3_VERSION |
278 | 0 | #define OGS_TLS_MAX_VERSION TLS1_3_VERSION |
279 | | #else /* !TLS1_3_VERSION */ |
280 | | #define OGS_TLS_MAX_VERSION TLS1_2_VERSION |
281 | | #endif /* TLS1_3_VERSION */ |
282 | 0 | if (ssl_ctx_set_proto_versions( |
283 | 0 | ssl_ctx, OGS_TLS_MIN_VERSION, OGS_TLS_MAX_VERSION) != 0) { |
284 | 0 | ogs_error("Could not set TLS versions [%d:%d]", |
285 | 0 | OGS_TLS_MIN_VERSION, OGS_TLS_MAX_VERSION); |
286 | 0 | return NULL; |
287 | 0 | } |
288 | | |
289 | 0 | #define DEFAULT_CIPHER_LIST \ |
290 | 0 | "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-" \ |
291 | 0 | "AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-" \ |
292 | 0 | "POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-" \ |
293 | 0 | "AES256-GCM-SHA384" |
294 | 0 | if (SSL_CTX_set_cipher_list(ssl_ctx, DEFAULT_CIPHER_LIST) == 0) { |
295 | 0 | ogs_error("%s", ERR_error_string(ERR_get_error(), NULL)); |
296 | 0 | return NULL; |
297 | 0 | } |
298 | | |
299 | 0 | if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) { |
300 | 0 | ogs_error("Could not read private key file - key_file=%s", key_file); |
301 | 0 | return NULL; |
302 | 0 | } |
303 | 0 | if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) { |
304 | 0 | ogs_error("Could not read certificate file - cert_file=%s ", cert_file); |
305 | 0 | return NULL; |
306 | 0 | } |
307 | 0 | if (SSL_CTX_check_private_key(ssl_ctx) != 1) { |
308 | 0 | ogs_error("SSL_CTX_check_private_key failed: %s", |
309 | 0 | ERR_error_string(ERR_get_error(), NULL)); |
310 | 0 | return NULL; |
311 | 0 | } |
312 | | |
313 | 0 | #ifndef OPENSSL_NO_NEXTPROTONEG |
314 | 0 | SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL); |
315 | 0 | #endif /* !OPENSSL_NO_NEXTPROTONEG */ |
316 | |
|
317 | 0 | #if OPENSSL_VERSION_NUMBER >= 0x10002000L |
318 | 0 | SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, NULL); |
319 | 0 | #endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ |
320 | |
|
321 | 0 | return ssl_ctx; |
322 | 0 | } |
323 | | |
324 | | static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) |
325 | 0 | { |
326 | 0 | if (!preverify_ok) { |
327 | 0 | int err = X509_STORE_CTX_get_error(ctx); |
328 | 0 | int depth = X509_STORE_CTX_get_error_depth(ctx); |
329 | 0 | if (err == X509_V_ERR_CERT_HAS_EXPIRED && depth == 0) { |
330 | 0 | ogs_error("The client certificate has expired, but is accepted by " |
331 | 0 | "configuration"); |
332 | 0 | return 1; |
333 | 0 | } |
334 | 0 | ogs_error("client certificate verify error:num=%d:%s:depth=%d", |
335 | 0 | err, X509_verify_cert_error_string(err), depth); |
336 | 0 | } |
337 | 0 | return preverify_ok; |
338 | 0 | } |
339 | | |
340 | | static int server_start(ogs_sbi_server_t *server, |
341 | | int (*cb)(ogs_sbi_request_t *request, void *data)) |
342 | 0 | { |
343 | 0 | char buf[OGS_ADDRSTRLEN]; |
344 | 0 | ogs_sock_t *sock = NULL; |
345 | 0 | ogs_sockaddr_t *addr = NULL; |
346 | 0 | char *hostname = NULL; |
347 | |
|
348 | 0 | addr = server->node.addr; |
349 | 0 | ogs_assert(addr); |
350 | | |
351 | | /* Create SSL CTX */ |
352 | 0 | if (server->scheme == OpenAPI_uri_scheme_https) { |
353 | |
|
354 | 0 | server->ssl_ctx = create_ssl_ctx( |
355 | 0 | server->private_key, server->cert, server->sslkeylog); |
356 | 0 | if (!server->ssl_ctx) { |
357 | 0 | ogs_error("Cannot create SSL CTX"); |
358 | 0 | return OGS_ERROR; |
359 | 0 | } |
360 | | |
361 | 0 | if (server->verify_client_cacert) { |
362 | 0 | char *context = NULL; |
363 | 0 | STACK_OF(X509_NAME) *cert_names = NULL; |
364 | |
|
365 | 0 | if (SSL_CTX_load_verify_locations( |
366 | 0 | server->ssl_ctx, |
367 | 0 | server->verify_client_cacert, NULL) != 1) { |
368 | 0 | ogs_error("Could not load trusted ca certificates from %s:%s", |
369 | 0 | server->verify_client_cacert, |
370 | 0 | ERR_error_string(ERR_get_error(), NULL)); |
371 | |
|
372 | 0 | SSL_CTX_free(server->ssl_ctx); |
373 | |
|
374 | 0 | return OGS_ERROR; |
375 | 0 | } |
376 | | |
377 | | /* |
378 | | * It is heard that SSL_CTX_load_verify_locations() may leave |
379 | | * error even though it returns success. See |
380 | | * http://forum.nginx.org/read.php?29,242540 |
381 | | */ |
382 | 0 | cert_names = SSL_load_client_CA_file(server->verify_client_cacert); |
383 | 0 | if (!cert_names) { |
384 | 0 | ogs_error("Could not load ca certificates from %s:%s", |
385 | 0 | server->verify_client_cacert, |
386 | 0 | ERR_error_string(ERR_get_error(), NULL)); |
387 | |
|
388 | 0 | SSL_CTX_free(server->ssl_ctx); |
389 | |
|
390 | 0 | return OGS_ERROR; |
391 | 0 | } |
392 | 0 | SSL_CTX_set_client_CA_list(server->ssl_ctx, cert_names); |
393 | |
|
394 | 0 | if (server->verify_client) |
395 | 0 | SSL_CTX_set_verify( |
396 | 0 | server->ssl_ctx, |
397 | 0 | SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | |
398 | 0 | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, |
399 | 0 | verify_callback); |
400 | |
|
401 | 0 | ogs_assert(server->id >= OGS_MIN_POOL_ID && |
402 | 0 | server->id <= OGS_MAX_POOL_ID); |
403 | 0 | context = ogs_msprintf("%d", server->id); |
404 | 0 | if (!context) { |
405 | 0 | ogs_error("ogs_sbi_server_id_context() failed"); |
406 | |
|
407 | 0 | SSL_CTX_free(server->ssl_ctx); |
408 | |
|
409 | 0 | return OGS_ERROR; |
410 | 0 | } |
411 | | |
412 | 0 | if (!SSL_CTX_set_session_id_context( |
413 | 0 | server->ssl_ctx, |
414 | 0 | (unsigned char *)context, strlen(context))) { |
415 | 0 | ogs_error("SSL_CTX_set_session_id_context() failed"); |
416 | |
|
417 | 0 | ogs_free(context); |
418 | 0 | SSL_CTX_free(server->ssl_ctx); |
419 | |
|
420 | 0 | return OGS_ERROR; |
421 | 0 | } |
422 | | |
423 | 0 | ogs_free(context); |
424 | 0 | } |
425 | 0 | } |
426 | | |
427 | 0 | sock = ogs_tcp_server(addr, server->node.option); |
428 | 0 | if (!sock) { |
429 | 0 | ogs_error("Cannot start SBI server"); |
430 | |
|
431 | 0 | if (server->ssl_ctx) |
432 | 0 | SSL_CTX_free(server->ssl_ctx); |
433 | |
|
434 | 0 | return OGS_ERROR; |
435 | 0 | } |
436 | | |
437 | 0 | server->node.sock = sock; |
438 | | |
439 | | /* Setup callback function */ |
440 | 0 | server->cb = cb; |
441 | | |
442 | | /* Setup poll for server listening socket */ |
443 | 0 | server->node.poll = ogs_pollset_add(ogs_app()->pollset, |
444 | 0 | OGS_POLLIN, sock->fd, accept_handler, server); |
445 | 0 | ogs_assert(server->node.poll); |
446 | | |
447 | 0 | hostname = ogs_gethostname(addr); |
448 | 0 | if (hostname) |
449 | 0 | ogs_info("nghttp2_server(%s) [%s://%s]:%d", |
450 | 0 | server->interface ? server->interface : "", |
451 | 0 | server->ssl_ctx ? "https" : "http", |
452 | 0 | hostname, OGS_PORT(addr)); |
453 | 0 | else |
454 | 0 | ogs_info("nghttp2_server(%s) [%s://%s]:%d", |
455 | 0 | server->interface ? server->interface : "", |
456 | 0 | server->ssl_ctx ? "https" : "http", |
457 | 0 | OGS_ADDR(addr, buf), OGS_PORT(addr)); |
458 | |
|
459 | 0 | return OGS_OK; |
460 | 0 | } |
461 | | |
462 | | /* Gracefully shutdown the server by sending GOAWAY to each session. */ |
463 | | static void server_graceful_shutdown(ogs_sbi_server_t *server) |
464 | 0 | { |
465 | 0 | ogs_sbi_session_t *sbi_sess = NULL; |
466 | 0 | ogs_sbi_session_t *next_sbi_sess = NULL; |
467 | 0 | int rv; |
468 | | |
469 | | /* Iterate over all active sessions in the server. */ |
470 | 0 | ogs_list_for_each_safe(&server->session_list, next_sbi_sess, sbi_sess) { |
471 | | /* Submit a GOAWAY frame using the last stream ID. */ |
472 | 0 | rv = nghttp2_submit_goaway(sbi_sess->session, |
473 | 0 | NGHTTP2_FLAG_NONE, |
474 | 0 | sbi_sess->last_stream_id, |
475 | 0 | NGHTTP2_NO_ERROR, |
476 | 0 | NULL, 0); |
477 | 0 | if (rv != 0) { |
478 | 0 | ogs_error("nghttp2_submit_goaway() failed (%d:%s)", |
479 | 0 | rv, nghttp2_strerror(rv)); |
480 | 0 | } |
481 | | |
482 | | /* Send the GOAWAY frame to the client. */ |
483 | 0 | if (session_send(sbi_sess) != OGS_OK) { |
484 | 0 | ogs_error("session_send() failed during graceful shutdown"); |
485 | 0 | } |
486 | 0 | } |
487 | 0 | } |
488 | | |
489 | | static void server_stop(ogs_sbi_server_t *server) |
490 | 0 | { |
491 | 0 | ogs_assert(server); |
492 | | |
493 | | /* Free SSL CTX */ |
494 | 0 | if (server->ssl_ctx) |
495 | 0 | SSL_CTX_free(server->ssl_ctx); |
496 | |
|
497 | 0 | if (server->node.poll) |
498 | 0 | ogs_pollset_remove(server->node.poll); |
499 | |
|
500 | 0 | if (server->node.sock) |
501 | 0 | ogs_sock_destroy(server->node.sock); |
502 | |
|
503 | 0 | session_remove_all(server); |
504 | 0 | } |
505 | | |
506 | | static void add_header(nghttp2_nv *nv, const char *key, const char *value) |
507 | 0 | { |
508 | 0 | nv->name = (uint8_t *)key; |
509 | 0 | nv->namelen = strlen(key); |
510 | 0 | nv->value = (uint8_t *)value; |
511 | 0 | nv->valuelen = strlen(value); |
512 | 0 | nv->flags = NGHTTP2_NV_FLAG_NONE; |
513 | 0 | } |
514 | | |
515 | | static char status_string[600][4] = { |
516 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
517 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
518 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
519 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
520 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
521 | | "100", "101", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
522 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
523 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
524 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
525 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
526 | | "200", "201", "202", "203", "204", "205", "206", "", "", "", "", "", "", "", "", "", "", "", "", "", |
527 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
528 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
529 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
530 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
531 | | "300", "301", "302", "303", "304", "305", "306", "307", "308", "", "", "", "", "", "", "", "", "", "", "", |
532 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
533 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
534 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
535 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
536 | | "400", "401", "402", "403", "404", "405", "406", "407", "408", "409", |
537 | | "410", "411", "412", "413", "414", "415", "416", "417", "", "", |
538 | | "", "421", "", "", "", "", "426", "", "428", "429", "", "431", "", "", "", "", "", "", "", "", |
539 | | "", "", "", "", "", "", "", "", "", "", "", "451", "", "", "", "", "", "", "", "", |
540 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
541 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
542 | | "500", "501", "502", "503", "504", "505", "", "", "", "", "", "511", "", "", "", "", "", "", "", "", |
543 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
544 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
545 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", |
546 | | "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" |
547 | | }; |
548 | | |
549 | 0 | #define DATE_STRLEN 128 |
550 | | static char *get_date_string(char *date) |
551 | 0 | { |
552 | 0 | static const char *const days[] = { |
553 | 0 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" |
554 | 0 | }; |
555 | 0 | static const char *const mons[] = { |
556 | 0 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
557 | 0 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
558 | 0 | }; |
559 | |
|
560 | 0 | ogs_assert(date); |
561 | | |
562 | 0 | struct tm tm; |
563 | 0 | ogs_gmtime(ogs_time_sec(ogs_time_now()), &tm); |
564 | |
|
565 | 0 | ogs_snprintf(date, DATE_STRLEN, "%3s, %02u %3s %04u %02u:%02u:%02u GMT", |
566 | 0 | days[tm.tm_wday % 7], |
567 | 0 | (unsigned int)tm.tm_mday, |
568 | 0 | mons[tm.tm_mon % 12], |
569 | 0 | (unsigned int)(1900 + tm.tm_year), |
570 | 0 | (unsigned int)tm.tm_hour, |
571 | 0 | (unsigned int)tm.tm_min, |
572 | 0 | (unsigned int)tm.tm_sec); |
573 | |
|
574 | 0 | return date; |
575 | 0 | } |
576 | | |
577 | | static ssize_t response_read_callback(nghttp2_session *session, |
578 | | int32_t stream_id, |
579 | | uint8_t *buf, size_t length, |
580 | | uint32_t *data_flags, |
581 | | nghttp2_data_source *source, |
582 | | void *user_data) |
583 | 0 | { |
584 | 0 | #if USE_SEND_DATA_WITH_NO_COPY |
585 | 0 | int rv; |
586 | 0 | #endif |
587 | |
|
588 | 0 | ogs_sbi_response_t *response = NULL; |
589 | 0 | ogs_sbi_stream_t *stream = NULL; |
590 | |
|
591 | 0 | ogs_assert(session); |
592 | | |
593 | 0 | stream = nghttp2_session_get_stream_user_data(session, stream_id); |
594 | 0 | if (!stream) { |
595 | 0 | ogs_error("no stream [%d]", stream_id); |
596 | 0 | return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; |
597 | 0 | } |
598 | | |
599 | 0 | ogs_assert(source); |
600 | 0 | response = source->ptr; |
601 | 0 | ogs_assert(response); |
602 | | |
603 | 0 | ogs_assert(response->http.content); |
604 | 0 | ogs_assert(response->http.content_length); |
605 | | |
606 | 0 | #if USE_SEND_DATA_WITH_NO_COPY |
607 | 0 | *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; |
608 | | #else |
609 | | memcpy(buf, response->http.content, response->http.content_length); |
610 | | #endif |
611 | |
|
612 | 0 | *data_flags |= NGHTTP2_DATA_FLAG_EOF; |
613 | |
|
614 | 0 | #if USE_SEND_DATA_WITH_NO_COPY |
615 | 0 | rv = nghttp2_session_get_stream_remote_close(session, stream_id); |
616 | 0 | if (rv == 0) { |
617 | 0 | ogs_warn("nghttp2_session_get_stream_remote_close() failed"); |
618 | 0 | nghttp2_submit_rst_stream( |
619 | 0 | session, NGHTTP2_FLAG_NONE, stream_id, NGHTTP2_NO_ERROR); |
620 | 0 | } else if (rv != 1) { |
621 | 0 | ogs_error("nghttp2_session_get_stream_remote_close() failed[%d]", rv); |
622 | 0 | } |
623 | 0 | #endif |
624 | |
|
625 | 0 | return response->http.content_length; |
626 | 0 | } |
627 | | |
628 | | static bool server_send_rspmem_persistent( |
629 | | ogs_sbi_stream_t *stream, ogs_sbi_response_t *response) |
630 | 0 | { |
631 | 0 | ogs_sbi_session_t *sbi_sess = NULL; |
632 | 0 | ogs_sock_t *sock = NULL; |
633 | 0 | ogs_socket_t fd = INVALID_SOCKET; |
634 | |
|
635 | 0 | ogs_hash_index_t *hi; |
636 | 0 | nghttp2_nv *nva; |
637 | 0 | size_t nvlen; |
638 | 0 | int i, rv; |
639 | 0 | char datebuf[DATE_STRLEN]; |
640 | 0 | char srv_version[128]; |
641 | 0 | char clen[128]; |
642 | |
|
643 | 0 | ogs_assert(response); |
644 | | |
645 | 0 | if (response->status >= 600) { |
646 | 0 | ogs_error("Invalid response status [%d]", response->status); |
647 | 0 | return false; |
648 | 0 | } |
649 | | |
650 | 0 | ogs_assert(stream); |
651 | 0 | sbi_sess = stream->session; |
652 | 0 | ogs_assert(sbi_sess); |
653 | 0 | ogs_assert(sbi_sess->session); |
654 | | |
655 | 0 | sock = sbi_sess->sock; |
656 | 0 | ogs_assert(sock); |
657 | 0 | fd = sock->fd; |
658 | 0 | ogs_assert(fd != INVALID_SOCKET); /* Check if session is removed */ |
659 | | |
660 | 0 | nvlen = 3; /* :status && server && date */ |
661 | |
|
662 | 0 | for (hi = ogs_hash_first(response->http.headers); |
663 | 0 | hi; hi = ogs_hash_next(hi)) |
664 | 0 | nvlen++; |
665 | |
|
666 | 0 | if (response->http.content && response->http.content_length) |
667 | 0 | nvlen++; |
668 | |
|
669 | 0 | nva = ogs_calloc(nvlen, sizeof(nghttp2_nv)); |
670 | 0 | if (!nva) { |
671 | 0 | ogs_error("ogs_calloc() failed"); |
672 | 0 | return false; |
673 | 0 | } |
674 | | |
675 | 0 | i = 0; |
676 | |
|
677 | 0 | if (strlen(status_string[response->status]) != 3) { |
678 | 0 | ogs_fatal("response status [%d]", response->status); |
679 | 0 | ogs_fatal("status string [%s]", status_string[response->status]); |
680 | 0 | ogs_assert_if_reached(); |
681 | 0 | return false; |
682 | 0 | } |
683 | | |
684 | 0 | add_header(&nva[i++], ":status", status_string[response->status]); |
685 | |
|
686 | 0 | ogs_snprintf(srv_version, sizeof(srv_version), |
687 | 0 | "Open5GS %s", ogs_app()->version ? ogs_app()->version : "TEST"); |
688 | 0 | add_header(&nva[i++], "server", srv_version); |
689 | 0 | add_header(&nva[i++], "date", get_date_string(datebuf)); |
690 | |
|
691 | 0 | if (response->http.content && response->http.content_length) { |
692 | 0 | ogs_snprintf(clen, sizeof(clen), |
693 | 0 | "%d", (int)response->http.content_length); |
694 | 0 | add_header(&nva[i++], "content-length", clen); |
695 | 0 | } |
696 | |
|
697 | 0 | for (hi = ogs_hash_first(response->http.headers); |
698 | 0 | hi; hi = ogs_hash_next(hi)) { |
699 | 0 | add_header(&nva[i++], ogs_hash_this_key(hi), ogs_hash_this_val(hi)); |
700 | 0 | } |
701 | |
|
702 | 0 | ogs_debug("STATUS [%d]", response->status); |
703 | |
|
704 | 0 | if (response->http.content && response->http.content_length) { |
705 | 0 | nghttp2_data_provider data_prd; |
706 | |
|
707 | 0 | data_prd.source.ptr = response; |
708 | 0 | data_prd.read_callback = response_read_callback; |
709 | |
|
710 | 0 | ogs_debug("SENDING...: %d", (int)response->http.content_length); |
711 | 0 | ogs_debug("%s", response->http.content); |
712 | |
|
713 | 0 | rv = nghttp2_submit_response(sbi_sess->session, |
714 | 0 | stream->stream_id, nva, nvlen, &data_prd); |
715 | 0 | } else { |
716 | 0 | rv = nghttp2_submit_response(sbi_sess->session, |
717 | 0 | stream->stream_id, nva, nvlen, NULL); |
718 | 0 | } |
719 | |
|
720 | 0 | if (rv != OGS_OK) { |
721 | 0 | ogs_error("nghttp2_submit_response(%d) failed (%d:%s)", |
722 | 0 | (int)response->http.content_length, |
723 | 0 | rv, nghttp2_strerror(rv)); |
724 | 0 | nghttp2_submit_rst_stream( |
725 | 0 | sbi_sess->session, NGHTTP2_FLAG_NONE, stream->stream_id, rv); |
726 | 0 | } |
727 | |
|
728 | 0 | if (session_send(sbi_sess) != OGS_OK) { |
729 | 0 | ogs_error("session_send() failed"); |
730 | 0 | session_remove(sbi_sess); |
731 | 0 | } |
732 | |
|
733 | 0 | ogs_free(nva); |
734 | |
|
735 | 0 | return true; |
736 | 0 | } |
737 | | |
738 | | static bool server_send_response( |
739 | | ogs_sbi_stream_t *stream, ogs_sbi_response_t *response) |
740 | 0 | { |
741 | 0 | bool rc; |
742 | |
|
743 | 0 | ogs_assert(response); |
744 | | |
745 | 0 | rc = server_send_rspmem_persistent(stream, response); |
746 | |
|
747 | 0 | ogs_sbi_response_free(response); |
748 | |
|
749 | 0 | return rc; |
750 | 0 | } |
751 | | |
752 | | static ogs_sbi_server_t *server_from_stream(ogs_sbi_stream_t *stream) |
753 | 0 | { |
754 | 0 | ogs_sbi_session_t *sbi_sess = NULL; |
755 | |
|
756 | 0 | ogs_assert(stream); |
757 | 0 | sbi_sess = stream->session; |
758 | 0 | ogs_assert(sbi_sess); |
759 | 0 | ogs_assert(sbi_sess->server); |
760 | | |
761 | 0 | return sbi_sess->server; |
762 | 0 | } |
763 | | |
764 | | static ogs_sbi_stream_t *stream_add( |
765 | | ogs_sbi_session_t *sbi_sess, int32_t stream_id) |
766 | 0 | { |
767 | 0 | ogs_sbi_stream_t *stream = NULL; |
768 | |
|
769 | 0 | ogs_assert(sbi_sess); |
770 | | |
771 | 0 | ogs_pool_id_calloc(&stream_pool, &stream); |
772 | 0 | if (!stream) { |
773 | 0 | ogs_error("ogs_pool_id_calloc() failed"); |
774 | 0 | return NULL; |
775 | 0 | } |
776 | | |
777 | 0 | stream->request = ogs_sbi_request_new(); |
778 | 0 | if (!stream->request) { |
779 | 0 | ogs_error("ogs_sbi_request_new() failed"); |
780 | 0 | ogs_pool_id_free(&stream_pool, stream); |
781 | 0 | return NULL; |
782 | 0 | } |
783 | | |
784 | 0 | stream->stream_id = stream_id; |
785 | 0 | sbi_sess->last_stream_id = stream_id; |
786 | |
|
787 | 0 | stream->session = sbi_sess; |
788 | |
|
789 | 0 | ogs_list_init(&stream->xact_list); |
790 | |
|
791 | 0 | ogs_list_add(&sbi_sess->stream_list, stream); |
792 | |
|
793 | 0 | return stream; |
794 | 0 | } |
795 | | |
796 | | static void xact_attach(ogs_sbi_stream_t *stream, ogs_sbi_xact_t *xact) |
797 | 0 | { |
798 | 0 | ogs_assert(stream); |
799 | 0 | ogs_assert(xact); |
800 | | |
801 | | /* |
802 | | * Invariant: the server-layer wrapper (ogs_sbi_server_attach_xact) |
803 | | * already filtered out the already-attached case. Reaching the |
804 | | * backend with to_stream_list set is a programming error. |
805 | | */ |
806 | 0 | ogs_assert(!xact->to_stream_list); |
807 | | |
808 | | /* |
809 | | * Cache the list head so xact_detach() can unlink in O(1) |
810 | | * without re-resolving the stream from assoc_stream_id. |
811 | | * to_stream_list serves as both attachment flag and cached head. |
812 | | */ |
813 | 0 | ogs_list_add(&stream->xact_list, &xact->to_stream_node); |
814 | 0 | xact->to_stream_list = &stream->xact_list; |
815 | 0 | } |
816 | | |
817 | | static void xact_detach(ogs_sbi_xact_t *xact) |
818 | 0 | { |
819 | 0 | ogs_assert(xact); |
820 | | |
821 | | /* |
822 | | * Invariant: the server-layer wrapper (ogs_sbi_server_detach_xact) |
823 | | * already filtered out the not-attached case. Reaching the |
824 | | * backend with to_stream_list cleared is a programming error. |
825 | | */ |
826 | 0 | ogs_assert(xact->to_stream_list); |
827 | | |
828 | 0 | ogs_list_remove(xact->to_stream_list, &xact->to_stream_node); |
829 | |
|
830 | 0 | xact->to_stream_list = NULL; |
831 | 0 | xact->assoc_stream_id = OGS_INVALID_POOL_ID; |
832 | 0 | } |
833 | | |
834 | | /* |
835 | | * Cancel every outbound SBI transaction that was triggered by this |
836 | | * stream. Without this, ogs_sbi_xact_remove() and its response timer |
837 | | * would only be reached when the upstream NF responds or the SBI |
838 | | * client wait timer expires; a peer that rapidly resets streams |
839 | | * while the upstream NF stalls would pile up those timers until the |
840 | | * pool is exhausted (the crash signature in issues #4472 / #4473). |
841 | | * |
842 | | * ogs_sbi_xact_remove() invokes xact_detach() internally, which is |
843 | | * what unlinks the node from this list. ogs_list_for_each_entry_safe |
844 | | * caches the next pointer before the body runs, so detach-during- |
845 | | * iteration is well-defined. |
846 | | */ |
847 | | static void stream_remove_xact_all(ogs_sbi_stream_t *stream) |
848 | 0 | { |
849 | 0 | ogs_sbi_xact_t *xact = NULL, *next_xact = NULL; |
850 | |
|
851 | 0 | ogs_assert(stream); |
852 | | |
853 | 0 | ogs_list_for_each_entry_safe( |
854 | 0 | &stream->xact_list, next_xact, xact, to_stream_node) { |
855 | | /* |
856 | | * Logged at error level so the cancellation shows up in |
857 | | * production traces alongside the upstream NF activity that |
858 | | * was abandoned. Useful for diagnosing #4472/#4473 style |
859 | | * patterns and any future regression where a peer resets |
860 | | * streams while upstream NFs are slow. |
861 | | */ |
862 | 0 | ogs_error("Canceling pending outbound SBI transaction " |
863 | 0 | "on HTTP/2 stream close [xact:%d,stream:%d,service:%s]", |
864 | 0 | (int)xact->id, stream->stream_id, |
865 | 0 | OpenAPI_service_name_ToString(xact->service_name)); |
866 | 0 | ogs_sbi_xact_remove(xact); |
867 | 0 | } |
868 | 0 | } |
869 | | |
870 | | static void stream_remove(ogs_sbi_stream_t *stream) |
871 | 0 | { |
872 | 0 | ogs_sbi_session_t *sbi_sess = NULL; |
873 | |
|
874 | 0 | ogs_assert(stream); |
875 | 0 | sbi_sess = stream->session; |
876 | 0 | ogs_assert(sbi_sess); |
877 | | |
878 | 0 | ogs_list_remove(&sbi_sess->stream_list, stream); |
879 | |
|
880 | 0 | stream_remove_xact_all(stream); |
881 | |
|
882 | 0 | ogs_assert(stream->request); |
883 | 0 | ogs_sbi_request_free(stream->request); |
884 | |
|
885 | 0 | ogs_pool_id_free(&stream_pool, stream); |
886 | 0 | } |
887 | | |
888 | | static void stream_remove_all(ogs_sbi_session_t *sbi_sess) |
889 | 0 | { |
890 | 0 | ogs_sbi_stream_t *stream = NULL, *next_stream = NULL; |
891 | |
|
892 | 0 | ogs_assert(sbi_sess); |
893 | | |
894 | 0 | ogs_list_for_each_safe(&sbi_sess->stream_list, next_stream, stream) |
895 | 0 | stream_remove(stream); |
896 | 0 | } |
897 | | |
898 | | static ogs_pool_id_t id_from_stream(ogs_sbi_stream_t *stream) |
899 | 0 | { |
900 | 0 | ogs_assert(stream); |
901 | 0 | return stream->id; |
902 | 0 | } |
903 | | |
904 | | static void *stream_find_by_id(ogs_pool_id_t id) |
905 | 0 | { |
906 | 0 | return ogs_pool_find_by_id(&stream_pool, id); |
907 | 0 | } |
908 | | |
909 | | static ogs_sbi_session_t *session_add( |
910 | | ogs_sbi_server_t *server, ogs_sock_t *sock) |
911 | 0 | { |
912 | 0 | ogs_sbi_session_t *sbi_sess = NULL; |
913 | |
|
914 | 0 | ogs_assert(server); |
915 | 0 | ogs_assert(sock); |
916 | | |
917 | 0 | ogs_pool_alloc(&session_pool, &sbi_sess); |
918 | 0 | if (!sbi_sess) { |
919 | 0 | ogs_error("ogs_pool_alloc() failed"); |
920 | 0 | return NULL; |
921 | 0 | } |
922 | 0 | memset(sbi_sess, 0, sizeof(ogs_sbi_session_t)); |
923 | |
|
924 | 0 | sbi_sess->server = server; |
925 | 0 | sbi_sess->sock = sock; |
926 | |
|
927 | 0 | sbi_sess->addr = ogs_calloc(1, sizeof(ogs_sockaddr_t)); |
928 | 0 | if (!sbi_sess->addr) { |
929 | 0 | ogs_error("ogs_calloc() failed"); |
930 | 0 | ogs_pool_free(&session_pool, sbi_sess); |
931 | 0 | return NULL; |
932 | 0 | } |
933 | 0 | memcpy(sbi_sess->addr, &sock->remote_addr, sizeof(ogs_sockaddr_t)); |
934 | |
|
935 | 0 | if (server->ssl_ctx) { |
936 | 0 | char *context = NULL; |
937 | |
|
938 | 0 | sbi_sess->ssl = SSL_new(server->ssl_ctx); |
939 | 0 | if (!sbi_sess->ssl) { |
940 | 0 | ogs_error("SSL_new() failed"); |
941 | 0 | ogs_free(sbi_sess->addr); |
942 | 0 | ogs_pool_free(&session_pool, sbi_sess); |
943 | 0 | return NULL; |
944 | 0 | } |
945 | | |
946 | 0 | context = ogs_msprintf("%d", |
947 | 0 | (int)ogs_pool_index(&session_pool, sbi_sess)); |
948 | 0 | if (!context) { |
949 | 0 | ogs_error("No memory for session id context"); |
950 | 0 | SSL_free(sbi_sess->ssl); |
951 | 0 | ogs_free(sbi_sess->addr); |
952 | 0 | ogs_pool_free(&session_pool, sbi_sess); |
953 | 0 | return NULL; |
954 | 0 | } |
955 | | |
956 | 0 | if (!SSL_set_session_id_context( |
957 | 0 | sbi_sess->ssl, (unsigned char *)context, strlen(context))) { |
958 | 0 | ogs_error("SSL_set_session_id_context() failed"); |
959 | 0 | ogs_free(context); |
960 | 0 | ogs_free(sbi_sess->addr); |
961 | 0 | SSL_free(sbi_sess->ssl); |
962 | 0 | ogs_pool_free(&session_pool, sbi_sess); |
963 | 0 | return NULL; |
964 | 0 | } |
965 | | |
966 | 0 | ogs_free(context); |
967 | 0 | } |
968 | | |
969 | 0 | ogs_list_add(&server->session_list, sbi_sess); |
970 | |
|
971 | 0 | return sbi_sess; |
972 | 0 | } |
973 | | |
974 | | static void session_remove(ogs_sbi_session_t *sbi_sess) |
975 | 0 | { |
976 | 0 | ogs_sbi_server_t *server = NULL; |
977 | 0 | ogs_pkbuf_t *pkbuf = NULL, *next_pkbuf = NULL; |
978 | |
|
979 | 0 | ogs_assert(sbi_sess); |
980 | 0 | server = sbi_sess->server; |
981 | 0 | ogs_assert(server); |
982 | | |
983 | 0 | ogs_list_remove(&server->session_list, sbi_sess); |
984 | |
|
985 | 0 | if (sbi_sess->ssl) |
986 | 0 | SSL_free(sbi_sess->ssl); |
987 | |
|
988 | 0 | stream_remove_all(sbi_sess); |
989 | 0 | nghttp2_session_del(sbi_sess->session); |
990 | |
|
991 | 0 | if (sbi_sess->poll.read) |
992 | 0 | ogs_pollset_remove(sbi_sess->poll.read); |
993 | |
|
994 | 0 | if (sbi_sess->poll.write) |
995 | 0 | ogs_pollset_remove(sbi_sess->poll.write); |
996 | |
|
997 | 0 | ogs_list_for_each_safe(&sbi_sess->write_queue, next_pkbuf, pkbuf) { |
998 | 0 | ogs_list_remove(&sbi_sess->write_queue, pkbuf); |
999 | 0 | ogs_pkbuf_free(pkbuf); |
1000 | 0 | } |
1001 | |
|
1002 | 0 | ogs_assert(sbi_sess->addr); |
1003 | 0 | ogs_free(sbi_sess->addr); |
1004 | |
|
1005 | 0 | ogs_assert(sbi_sess->sock); |
1006 | 0 | ogs_sock_destroy(sbi_sess->sock); |
1007 | |
|
1008 | 0 | ogs_pool_free(&session_pool, sbi_sess); |
1009 | 0 | } |
1010 | | |
1011 | | static void session_remove_all(ogs_sbi_server_t *server) |
1012 | 0 | { |
1013 | 0 | ogs_sbi_session_t *sbi_sess = NULL, *next_sbi_sess = NULL; |
1014 | |
|
1015 | 0 | ogs_assert(server); |
1016 | | |
1017 | 0 | ogs_list_for_each_safe(&server->session_list, next_sbi_sess, sbi_sess) |
1018 | 0 | session_remove(sbi_sess); |
1019 | 0 | } |
1020 | | |
1021 | | static void accept_handler(short when, ogs_socket_t fd, void *data) |
1022 | 0 | { |
1023 | 0 | ogs_sbi_server_t *server = data; |
1024 | 0 | ogs_sbi_session_t *sbi_sess = NULL; |
1025 | 0 | ogs_sock_t *sock = NULL; |
1026 | 0 | ogs_sock_t *new = NULL; |
1027 | |
|
1028 | 0 | int on; |
1029 | |
|
1030 | 0 | ogs_assert(data); |
1031 | 0 | ogs_assert(fd != INVALID_SOCKET); |
1032 | | |
1033 | 0 | sock = server->node.sock; |
1034 | |
|
1035 | 0 | new = ogs_sock_accept(sock); |
1036 | 0 | if (!new) { |
1037 | 0 | ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, "accept() failed"); |
1038 | 0 | return; |
1039 | 0 | } |
1040 | 0 | ogs_assert(new->fd != INVALID_SOCKET); |
1041 | | |
1042 | 0 | on = 1; |
1043 | 0 | if (setsockopt(new->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) != 0) { |
1044 | 0 | ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, |
1045 | 0 | "setsockopt() for SCTP_NODELAY failed"); |
1046 | 0 | ogs_sock_destroy(new); |
1047 | 0 | return; |
1048 | 0 | } |
1049 | | |
1050 | 0 | sbi_sess = session_add(server, new); |
1051 | 0 | ogs_assert(sbi_sess); |
1052 | | |
1053 | 0 | if (sbi_sess->ssl) { |
1054 | 0 | int err; |
1055 | 0 | SSL_set_fd(sbi_sess->ssl, new->fd); |
1056 | 0 | SSL_set_accept_state(sbi_sess->ssl); |
1057 | 0 | err = SSL_accept(sbi_sess->ssl); |
1058 | 0 | if (err <= 0) { |
1059 | 0 | ogs_error("SSL_accept failed [%s]", ERR_error_string(ERR_get_error(), NULL)); |
1060 | 0 | session_remove(sbi_sess); |
1061 | 0 | return; |
1062 | 0 | } |
1063 | 0 | } |
1064 | | |
1065 | 0 | sbi_sess->poll.read = ogs_pollset_add(ogs_app()->pollset, |
1066 | 0 | OGS_POLLIN, new->fd, recv_handler, sbi_sess); |
1067 | 0 | ogs_assert(sbi_sess->poll.read); |
1068 | | |
1069 | 0 | if (session_set_callbacks(sbi_sess) != OGS_OK || |
1070 | 0 | session_send_preface(sbi_sess) != OGS_OK) { |
1071 | 0 | ogs_error("session_add() failed"); |
1072 | 0 | session_remove(sbi_sess); |
1073 | 0 | } |
1074 | 0 | } |
1075 | | |
1076 | | static void recv_handler(short when, ogs_socket_t fd, void *data) |
1077 | 0 | { |
1078 | 0 | char buf[OGS_ADDRSTRLEN]; |
1079 | 0 | ogs_sockaddr_t *addr = NULL; |
1080 | |
|
1081 | 0 | ogs_sbi_session_t *sbi_sess = data; |
1082 | 0 | ogs_pkbuf_t *pkbuf = NULL; |
1083 | 0 | ssize_t readlen; |
1084 | 0 | int n; |
1085 | |
|
1086 | 0 | ogs_assert(sbi_sess); |
1087 | 0 | ogs_assert(fd != INVALID_SOCKET); |
1088 | 0 | addr = sbi_sess->addr; |
1089 | 0 | ogs_assert(addr); |
1090 | | |
1091 | 0 | pkbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); |
1092 | 0 | ogs_assert(pkbuf); |
1093 | | |
1094 | 0 | if (sbi_sess->ssl) |
1095 | 0 | n = SSL_read(sbi_sess->ssl, pkbuf->data, OGS_MAX_SDU_LEN); |
1096 | 0 | else |
1097 | 0 | n = ogs_recv(fd, pkbuf->data, OGS_MAX_SDU_LEN, 0); |
1098 | |
|
1099 | 0 | if (n > 0) { |
1100 | 0 | ogs_pkbuf_put(pkbuf, n); |
1101 | |
|
1102 | 0 | ogs_assert(sbi_sess->session); |
1103 | 0 | readlen = nghttp2_session_mem_recv( |
1104 | 0 | sbi_sess->session, pkbuf->data, pkbuf->len); |
1105 | 0 | if (readlen < 0) { |
1106 | 0 | ogs_error("nghttp2_session_mem_recv() failed (%d:%s)", |
1107 | 0 | (int)readlen, nghttp2_strerror((int)readlen)); |
1108 | 0 | session_remove(sbi_sess); |
1109 | 0 | } else { |
1110 | | /* |
1111 | | * Issues #2385 |
1112 | | * |
1113 | | * Nokia AMF is sending GOAWAY because it didn't get |
1114 | | * ACK SETTINGS packet for the SETTINGS it set, |
1115 | | * this is according to http2 RFC, all settings must be |
1116 | | * ACK or connection will be dropped. |
1117 | | * |
1118 | | * Open5GS is not ACKing pure settings packets, |
1119 | | * looks like it is waiting for a header |
1120 | | * like POST/GET first to trigger |
1121 | | * sending settings ACK and then headers reply. |
1122 | | */ |
1123 | | |
1124 | | /* |
1125 | | * [SOLVED] |
1126 | | * |
1127 | | * Whether or not to send a Setting ACK is determined |
1128 | | * by the nghttp2 library. Therefore, when nghttp2 informs us |
1129 | | * that it want to send an SETTING frame with ACK |
1130 | | * by nghttp2_session_want_write(), we need to call session_send() |
1131 | | * directly to send it. |
1132 | | */ |
1133 | 0 | if (nghttp2_session_want_write(sbi_sess->session)) |
1134 | 0 | session_send(sbi_sess); |
1135 | 0 | } |
1136 | 0 | } else { |
1137 | 0 | if (n < 0) { |
1138 | 0 | if (errno != OGS_ECONNRESET) |
1139 | 0 | ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, |
1140 | 0 | "lost connection [%s]:%d", |
1141 | 0 | OGS_ADDR(addr, buf), OGS_PORT(addr)); |
1142 | 0 | } else if (n == 0) { |
1143 | 0 | ogs_debug("connection closed [%s]:%d", |
1144 | 0 | OGS_ADDR(addr, buf), OGS_PORT(addr)); |
1145 | 0 | } |
1146 | |
|
1147 | 0 | session_remove(sbi_sess); |
1148 | 0 | } |
1149 | | |
1150 | 0 | ogs_pkbuf_free(pkbuf); |
1151 | 0 | } |
1152 | | |
1153 | | static int on_frame_recv(nghttp2_session *session, |
1154 | | const nghttp2_frame *frame, void *user_data); |
1155 | | static int on_stream_close(nghttp2_session *session, int32_t stream_id, |
1156 | | uint32_t error_code, void *user_data); |
1157 | | static int on_header(nghttp2_session *session, |
1158 | | const nghttp2_frame *frame, |
1159 | | nghttp2_rcbuf *name, nghttp2_rcbuf *value, |
1160 | | uint8_t flags, void *user_data); |
1161 | | static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, |
1162 | | int32_t stream_id, const uint8_t *data, |
1163 | | size_t len, void *user_data); |
1164 | | static int error_callback(nghttp2_session *session, |
1165 | | const char *msg, size_t len, void *user_data); |
1166 | | static int on_invalid_frame_recv(nghttp2_session *session, |
1167 | | const nghttp2_frame *frame, |
1168 | | int error_code, void *user_data); |
1169 | | static int on_invalid_header(nghttp2_session *session, |
1170 | | const nghttp2_frame *frame, |
1171 | | const uint8_t *name, size_t namelen, |
1172 | | const uint8_t *value, size_t valuelen, |
1173 | | uint8_t flags, void *user_data); |
1174 | | static int on_begin_frame(nghttp2_session *session, |
1175 | | const nghttp2_frame_hd *hd, void *user_data); |
1176 | | static int on_begin_headers(nghttp2_session *session, |
1177 | | const nghttp2_frame *frame, |
1178 | | void *user_data); |
1179 | | #if USE_SEND_DATA_WITH_NO_COPY |
1180 | | static int on_send_data(nghttp2_session *session, nghttp2_frame *frame, |
1181 | | const uint8_t *framehd, size_t length, |
1182 | | nghttp2_data_source *source, void *user_data); |
1183 | | #else |
1184 | | static ssize_t send_callback(nghttp2_session *session, const uint8_t *data, |
1185 | | size_t length, int flags, void *user_data); |
1186 | | #endif |
1187 | | |
1188 | | static int session_set_callbacks(ogs_sbi_session_t *sbi_sess) |
1189 | 0 | { |
1190 | 0 | int rv; |
1191 | 0 | nghttp2_session_callbacks *callbacks = NULL; |
1192 | |
|
1193 | 0 | ogs_assert(sbi_sess); |
1194 | | |
1195 | 0 | rv = nghttp2_session_callbacks_new(&callbacks); |
1196 | 0 | if (rv != 0) { |
1197 | 0 | ogs_error("nghttp2_session_callbacks_new() failed (%d:%s)", |
1198 | 0 | rv, nghttp2_strerror(rv)); |
1199 | 0 | return OGS_ERROR; |
1200 | 0 | } |
1201 | | |
1202 | 0 | nghttp2_session_callbacks_set_on_frame_recv_callback( |
1203 | 0 | callbacks, on_frame_recv); |
1204 | |
|
1205 | 0 | nghttp2_session_callbacks_set_on_stream_close_callback( |
1206 | 0 | callbacks, on_stream_close); |
1207 | |
|
1208 | 0 | nghttp2_session_callbacks_set_on_header_callback2(callbacks, on_header); |
1209 | |
|
1210 | 0 | nghttp2_session_callbacks_set_on_data_chunk_recv_callback( |
1211 | 0 | callbacks, on_data_chunk_recv); |
1212 | |
|
1213 | 0 | nghttp2_session_callbacks_set_error_callback(callbacks, error_callback); |
1214 | |
|
1215 | 0 | nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( |
1216 | 0 | callbacks, on_invalid_frame_recv); |
1217 | |
|
1218 | 0 | nghttp2_session_callbacks_set_on_invalid_header_callback( |
1219 | 0 | callbacks, on_invalid_header); |
1220 | |
|
1221 | 0 | nghttp2_session_callbacks_set_on_begin_frame_callback( |
1222 | 0 | callbacks, on_begin_frame); |
1223 | |
|
1224 | 0 | nghttp2_session_callbacks_set_on_begin_headers_callback( |
1225 | 0 | callbacks, on_begin_headers); |
1226 | |
|
1227 | 0 | #if USE_SEND_DATA_WITH_NO_COPY |
1228 | 0 | nghttp2_session_callbacks_set_send_data_callback(callbacks, on_send_data); |
1229 | | #else |
1230 | | nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); |
1231 | | #endif |
1232 | |
|
1233 | 0 | rv = nghttp2_session_server_new(&sbi_sess->session, callbacks, sbi_sess); |
1234 | 0 | if (rv != 0) { |
1235 | 0 | ogs_error("nghttp2_session_callbacks_new() failed (%d:%s)", |
1236 | 0 | rv, nghttp2_strerror(rv)); |
1237 | 0 | return OGS_ERROR; |
1238 | 0 | } |
1239 | | |
1240 | 0 | nghttp2_session_callbacks_del(callbacks); |
1241 | |
|
1242 | 0 | return OGS_OK; |
1243 | 0 | } |
1244 | | |
1245 | | static int on_frame_recv(nghttp2_session *session, |
1246 | | const nghttp2_frame *frame, void *user_data) |
1247 | 0 | { |
1248 | 0 | int rv; |
1249 | 0 | ogs_sbi_session_t *sbi_sess = user_data; |
1250 | |
|
1251 | 0 | ogs_sbi_server_t *server = NULL; |
1252 | 0 | ogs_sbi_stream_t *stream = NULL; |
1253 | 0 | ogs_sbi_request_t *request = NULL; |
1254 | |
|
1255 | 0 | ogs_assert(sbi_sess); |
1256 | 0 | server = sbi_sess->server; |
1257 | 0 | ogs_assert(server); |
1258 | 0 | ogs_assert(server->cb); |
1259 | | |
1260 | 0 | ogs_assert(session); |
1261 | 0 | ogs_assert(frame); |
1262 | | |
1263 | 0 | switch (frame->hd.type) { |
1264 | 0 | case NGHTTP2_HEADERS: |
1265 | 0 | stream = nghttp2_session_get_stream_user_data( |
1266 | 0 | session, frame->hd.stream_id); |
1267 | 0 | if (!stream) return 0; |
1268 | | |
1269 | 0 | request = stream->request; |
1270 | 0 | ogs_assert(request); |
1271 | | |
1272 | 0 | if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { |
1273 | 0 | const char *expect100 = |
1274 | 0 | ogs_sbi_header_get(request->http.headers, OGS_SBI_EXPECT); |
1275 | 0 | if (expect100 && ogs_strcasecmp(expect100, "100-continue") == 0) { |
1276 | 0 | nghttp2_nv nva; |
1277 | |
|
1278 | 0 | add_header(&nva, ":status", status_string[100]); |
1279 | 0 | rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, |
1280 | 0 | stream->stream_id, NULL, &nva, 1, NULL); |
1281 | 0 | if (rv != 0) { |
1282 | 0 | ogs_error("nghttp2_submit_headers() failed (%d:%s)", |
1283 | 0 | rv, nghttp2_strerror(rv)); |
1284 | 0 | nghttp2_submit_rst_stream( |
1285 | 0 | session, NGHTTP2_FLAG_NONE, stream->stream_id, rv); |
1286 | 0 | return 0; |
1287 | 0 | } |
1288 | 0 | } |
1289 | 0 | } |
1290 | | /* fallthrough */ |
1291 | 0 | OGS_GNUC_FALLTHROUGH; |
1292 | |
|
1293 | 0 | case NGHTTP2_DATA: |
1294 | 0 | stream = nghttp2_session_get_stream_user_data( |
1295 | 0 | session, frame->hd.stream_id); |
1296 | 0 | if (!stream) return 0; |
1297 | | |
1298 | 0 | request = stream->request; |
1299 | 0 | ogs_assert(request); |
1300 | | |
1301 | | /* HEADERS or DATA frame with +END_STREAM flag */ |
1302 | 0 | if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { |
1303 | 0 | ogs_log_level_e level = OGS_LOG_DEBUG; |
1304 | |
|
1305 | 0 | if (stream->memory_overflow == true) |
1306 | 0 | level = OGS_LOG_ERROR; |
1307 | |
|
1308 | 0 | ogs_log_message(level, 0, |
1309 | 0 | "[%s] %s", request->h.method, request->h.uri); |
1310 | |
|
1311 | 0 | if (request->http.content_length && request->http.content) { |
1312 | 0 | ogs_log_message(level, 0, |
1313 | 0 | "RECEIVED: %d", (int)request->http.content_length); |
1314 | 0 | ogs_log_message(level, 0, "%s", request->http.content); |
1315 | 0 | } |
1316 | |
|
1317 | 0 | if (stream->memory_overflow == true) { |
1318 | 0 | ogs_error("[DROP] Overflow"); |
1319 | 0 | break; |
1320 | 0 | } |
1321 | | |
1322 | 0 | if (server->cb(request, |
1323 | 0 | OGS_UINT_TO_POINTER(stream->id)) != OGS_OK) { |
1324 | 0 | ogs_warn("server callback error"); |
1325 | | |
1326 | | /* The callback may have sent a response and closed |
1327 | | * the stream. */ |
1328 | 0 | stream = nghttp2_session_get_stream_user_data( |
1329 | 0 | session, frame->hd.stream_id); |
1330 | 0 | if (!stream) { |
1331 | 0 | ogs_error("The server callback already sent a response " |
1332 | 0 | "but returned an error; it must return OGS_OK " |
1333 | 0 | "instead [%d]", frame->hd.stream_id); |
1334 | 0 | return 0; |
1335 | 0 | } |
1336 | | |
1337 | 0 | ogs_assert(true == |
1338 | 0 | ogs_sbi_server_send_error(stream, |
1339 | 0 | OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL, |
1340 | 0 | "server callback error", NULL, NULL)); |
1341 | | |
1342 | 0 | return 0; |
1343 | 0 | } |
1344 | 0 | } else { |
1345 | | /* TODO : Need to implement the timeouf of reading STREAM */ |
1346 | 0 | } |
1347 | 0 | break; |
1348 | 0 | case NGHTTP2_SETTINGS: |
1349 | 0 | ogs_debug("FLAGS(0x%x) [%s]", |
1350 | 0 | frame->hd.flags, |
1351 | 0 | frame->hd.flags & NGHTTP2_FLAG_ACK ? "ACK" : "NO-ACK"); |
1352 | |
|
1353 | 0 | if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { |
1354 | 0 | sbi_sess->settings.max_concurrent_streams = |
1355 | 0 | nghttp2_session_get_remote_settings( |
1356 | 0 | session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); |
1357 | 0 | sbi_sess->settings.enable_push = |
1358 | 0 | nghttp2_session_get_remote_settings( |
1359 | 0 | session, NGHTTP2_SETTINGS_ENABLE_PUSH); |
1360 | 0 | ogs_debug("MAX_CONCURRENT_STREAMS = %d", |
1361 | 0 | sbi_sess->settings.max_concurrent_streams); |
1362 | 0 | ogs_debug("ENABLE_PUSH = %s", |
1363 | 0 | sbi_sess->settings.enable_push ? "TRUE" : "false"); |
1364 | |
|
1365 | 0 | return 0; |
1366 | 0 | } |
1367 | | |
1368 | | /* |
1369 | | * TODO: |
1370 | | * |
1371 | | * if Setting ACK received, need to stop Timer. |
1372 | | * Otherwise, we need to send NGHTTP2_GOAWAY |
1373 | | */ |
1374 | 0 | break; |
1375 | 0 | case NGHTTP2_GOAWAY: |
1376 | | #if 0 /* It is not neeed in other nghttp2 example */ |
1377 | | rv = nghttp2_submit_goaway( |
1378 | | session, NGHTTP2_FLAG_NONE, sbi_sess->last_stream_id, |
1379 | | NGHTTP2_NO_ERROR, NULL, 0); |
1380 | | if (rv != 0) { |
1381 | | ogs_error("nghttp2_submit_goaway() failed (%d:%s)", |
1382 | | rv, nghttp2_strerror(rv)); |
1383 | | return OGS_ERROR; |
1384 | | } |
1385 | | |
1386 | | session_send(sbi_sess); |
1387 | | #endif |
1388 | 0 | ogs_info("GOAWAY received: last-stream-id=%d", |
1389 | 0 | frame->goaway.last_stream_id); |
1390 | 0 | ogs_info("error_code=%d", frame->goaway.error_code); |
1391 | 0 | break; |
1392 | 0 | case NGHTTP2_RST_STREAM: |
1393 | 0 | ogs_info("RST_STREAM received: stream_id=%d", frame->hd.stream_id); |
1394 | 0 | break; |
1395 | 0 | case NGHTTP2_PING: |
1396 | 0 | if (frame->hd.flags & NGHTTP2_FLAG_ACK) |
1397 | 0 | ogs_info("PING ACK received"); |
1398 | 0 | break; |
1399 | 0 | case NGHTTP2_PUSH_PROMISE: |
1400 | 0 | ogs_info("PUSH_PROMISE recieved: stream_id=%d", frame->hd.stream_id); |
1401 | 0 | ogs_info("promised_stream_id=%d", |
1402 | 0 | frame->push_promise.promised_stream_id); |
1403 | 0 | break; |
1404 | 0 | default: |
1405 | 0 | break; |
1406 | 0 | } |
1407 | | |
1408 | 0 | return 0; |
1409 | 0 | } |
1410 | | |
1411 | | static int on_stream_close(nghttp2_session *session, int32_t stream_id, |
1412 | | uint32_t error_code, void *user_data) |
1413 | 0 | { |
1414 | 0 | ogs_sbi_stream_t *stream = NULL; |
1415 | |
|
1416 | 0 | ogs_assert(session); |
1417 | | |
1418 | 0 | stream = nghttp2_session_get_stream_user_data(session, stream_id); |
1419 | 0 | if (!stream) { |
1420 | 0 | ogs_error("no stream [%d]", stream_id); |
1421 | 0 | return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; |
1422 | 0 | } |
1423 | | |
1424 | 0 | if (error_code) { |
1425 | 0 | ogs_error("on_stream_close_callback() failed (%d:%s)", |
1426 | 0 | error_code, nghttp2_http2_strerror(error_code)); |
1427 | 0 | nghttp2_submit_rst_stream( |
1428 | 0 | session, NGHTTP2_FLAG_NONE, stream_id, error_code); |
1429 | 0 | } |
1430 | |
|
1431 | 0 | ogs_debug("STREAM closed [%d]", stream_id); |
1432 | 0 | stream_remove(stream); |
1433 | 0 | return 0; |
1434 | 0 | } |
1435 | | |
1436 | | static int on_header(nghttp2_session *session, const nghttp2_frame *frame, |
1437 | | nghttp2_rcbuf *name, nghttp2_rcbuf *value, |
1438 | | uint8_t flags, void *user_data) |
1439 | 0 | { |
1440 | 0 | ogs_sbi_session_t *sbi_sess = user_data; |
1441 | 0 | ogs_sbi_stream_t *stream = NULL; |
1442 | 0 | ogs_sbi_request_t *request = NULL; |
1443 | |
|
1444 | 0 | const char PATH[] = ":path"; |
1445 | 0 | const char METHOD[] = ":method"; |
1446 | |
|
1447 | 0 | nghttp2_vec namebuf, valuebuf; |
1448 | 0 | char *namestr = NULL, *valuestr = NULL; |
1449 | |
|
1450 | 0 | ogs_assert(session); |
1451 | 0 | ogs_assert(frame); |
1452 | | |
1453 | 0 | if (frame->hd.type != NGHTTP2_HEADERS || |
1454 | 0 | frame->headers.cat != NGHTTP2_HCAT_REQUEST) { |
1455 | 0 | return 0; |
1456 | 0 | } |
1457 | | |
1458 | 0 | stream = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); |
1459 | 0 | if (!stream) { |
1460 | 0 | ogs_error("no stream [%d]", frame->hd.stream_id); |
1461 | 0 | return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; |
1462 | 0 | } |
1463 | | |
1464 | 0 | ogs_assert(sbi_sess); |
1465 | | |
1466 | 0 | request = stream->request; |
1467 | 0 | ogs_assert(request); |
1468 | | |
1469 | 0 | ogs_assert(name); |
1470 | 0 | namebuf = nghttp2_rcbuf_get_buf(name); |
1471 | 0 | ogs_assert(namebuf.base); |
1472 | 0 | ogs_assert(namebuf.len); |
1473 | | |
1474 | 0 | ogs_assert(value); |
1475 | 0 | valuebuf = nghttp2_rcbuf_get_buf(value); |
1476 | 0 | ogs_assert(valuebuf.base); |
1477 | | |
1478 | 0 | if (valuebuf.len == 0) return 0; |
1479 | | |
1480 | 0 | namestr = ogs_strndup((const char *)namebuf.base, namebuf.len); |
1481 | 0 | ogs_assert(namestr); |
1482 | | |
1483 | 0 | valuestr = ogs_strndup((const char *)valuebuf.base, valuebuf.len); |
1484 | 0 | ogs_assert(valuestr); |
1485 | | |
1486 | 0 | if (namebuf.len == sizeof(PATH) - 1 && |
1487 | 0 | memcmp(PATH, namebuf.base, namebuf.len) == 0) { |
1488 | 0 | char *saveptr = NULL, *query; |
1489 | 0 | #define MAX_NUM_OF_PARAM_IN_QUERY 16 |
1490 | 0 | struct yuarel_param params[MAX_NUM_OF_PARAM_IN_QUERY+2]; |
1491 | 0 | int j; |
1492 | |
|
1493 | 0 | ogs_assert(request->h.uri == NULL); |
1494 | 0 | request->h.uri = ogs_sbi_parse_uri(valuestr, "?", &saveptr); |
1495 | 0 | ogs_assert(request->h.uri); |
1496 | | |
1497 | 0 | memset(params, 0, sizeof(params)); |
1498 | |
|
1499 | 0 | query = ogs_sbi_parse_uri(NULL, "?", &saveptr); |
1500 | 0 | if (query && *query && strlen(query)) |
1501 | 0 | yuarel_parse_query(query, '&', params, MAX_NUM_OF_PARAM_IN_QUERY+1); |
1502 | |
|
1503 | 0 | j = 0; |
1504 | 0 | while(params[j].key && params[j].val) { |
1505 | 0 | if (j >= MAX_NUM_OF_PARAM_IN_QUERY) { |
1506 | 0 | ogs_error("Too many query params (max=%d)", |
1507 | 0 | MAX_NUM_OF_PARAM_IN_QUERY); |
1508 | 0 | ogs_sbi_server_send_error(stream, |
1509 | 0 | OGS_SBI_HTTP_STATUS_BAD_REQUEST, NULL, |
1510 | 0 | "Too many query parameters", NULL, NULL); |
1511 | |
|
1512 | 0 | ogs_free(query); |
1513 | |
|
1514 | 0 | ogs_free(namestr); |
1515 | 0 | ogs_free(valuestr); |
1516 | |
|
1517 | 0 | return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; |
1518 | 0 | } |
1519 | | |
1520 | 0 | if (strlen(params[j].key)) |
1521 | 0 | ogs_sbi_header_set(request->http.params, |
1522 | 0 | params[j].key, params[j].val); |
1523 | 0 | else |
1524 | 0 | ogs_warn("No KEY in Query-Parms"); |
1525 | |
|
1526 | 0 | j++; |
1527 | 0 | } |
1528 | | |
1529 | 0 | ogs_free(query); |
1530 | |
|
1531 | 0 | } else if (namebuf.len == sizeof(METHOD) - 1 && |
1532 | 0 | memcmp(METHOD, namebuf.base, namebuf.len) == 0) { |
1533 | |
|
1534 | 0 | ogs_assert(request->h.method == NULL); |
1535 | 0 | request->h.method = ogs_strdup(valuestr); |
1536 | 0 | ogs_assert(request->h.method); |
1537 | |
|
1538 | 0 | } else { |
1539 | |
|
1540 | 0 | ogs_sbi_header_set(request->http.headers, namestr, valuestr); |
1541 | |
|
1542 | 0 | } |
1543 | | |
1544 | 0 | ogs_free(namestr); |
1545 | 0 | ogs_free(valuestr); |
1546 | |
|
1547 | 0 | return 0; |
1548 | 0 | } |
1549 | | |
1550 | | static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, |
1551 | | int32_t stream_id, const uint8_t *data, |
1552 | | size_t len, void *user_data) |
1553 | 0 | { |
1554 | 0 | ogs_sbi_stream_t *stream = NULL; |
1555 | 0 | ogs_sbi_request_t *request = NULL; |
1556 | 0 | char *content = NULL; |
1557 | |
|
1558 | 0 | size_t offset = 0; |
1559 | |
|
1560 | 0 | ogs_assert(session); |
1561 | | |
1562 | 0 | stream = nghttp2_session_get_stream_user_data(session, stream_id); |
1563 | 0 | if (!stream) { |
1564 | 0 | ogs_error("no stream [%d]", stream_id); |
1565 | 0 | return 0; |
1566 | 0 | } |
1567 | | |
1568 | 0 | request = stream->request; |
1569 | 0 | ogs_assert(request); |
1570 | | |
1571 | 0 | ogs_assert(data); |
1572 | 0 | ogs_assert(len); |
1573 | | |
1574 | 0 | #define MAX_HTTP_CONTENT_LEN (256 * 1024 * 1024) /* 256MB */ |
1575 | 0 | if (request->http.content_length + len > MAX_HTTP_CONTENT_LEN) { |
1576 | 0 | stream->memory_overflow = true; |
1577 | |
|
1578 | 0 | ogs_error("Payload too large : Content-Length[%d], len[%d]", |
1579 | 0 | (int)request->http.content_length, (int)len); |
1580 | |
|
1581 | 0 | ogs_sbi_server_send_error(stream, |
1582 | 0 | OGS_SBI_HTTP_STATUS_PAYLOAD_TOO_LARGE, |
1583 | 0 | NULL, "Payload too large", NULL, NULL); |
1584 | |
|
1585 | 0 | return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; |
1586 | 0 | } |
1587 | | |
1588 | 0 | if (request->http.content == NULL) { |
1589 | 0 | ogs_assert(request->http.content_length == 0); |
1590 | 0 | ogs_assert(offset == 0); |
1591 | | |
1592 | 0 | content = (char*)ogs_malloc(len + 1); |
1593 | 0 | } else { |
1594 | 0 | ogs_assert(request->http.content_length != 0); |
1595 | | |
1596 | 0 | content = (char*)ogs_realloc( |
1597 | 0 | request->http.content, request->http.content_length + len + 1); |
1598 | 0 | } |
1599 | | |
1600 | 0 | if (!content) { |
1601 | 0 | stream->memory_overflow = true; |
1602 | |
|
1603 | 0 | ogs_error("Memory Overflow : Content-Length[%d], len[%d]", |
1604 | 0 | (int)request->http.content_length, (int)len); |
1605 | 0 | ogs_log_hexdump(OGS_LOG_ERROR, data, len); |
1606 | |
|
1607 | 0 | ogs_sbi_server_send_error(stream, |
1608 | 0 | OGS_SBI_HTTP_STATUS_SERVICE_UNAVAILABLE, |
1609 | 0 | NULL, "Memory Overflow", NULL, NULL); |
1610 | |
|
1611 | 0 | return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; |
1612 | 0 | } |
1613 | | |
1614 | 0 | request->http.content = content; |
1615 | |
|
1616 | 0 | offset = request->http.content_length; |
1617 | 0 | request->http.content_length += len; |
1618 | |
|
1619 | 0 | memcpy(request->http.content + offset, data, len); |
1620 | 0 | request->http.content[request->http.content_length] = '\0'; |
1621 | |
|
1622 | 0 | return 0; |
1623 | 0 | } |
1624 | | |
1625 | | static int error_callback(nghttp2_session *session, |
1626 | | const char *msg, size_t len, void *user_data) |
1627 | 0 | { |
1628 | 0 | char buf[OGS_ADDRSTRLEN]; |
1629 | 0 | ogs_sockaddr_t *addr = NULL; |
1630 | 0 | ogs_sbi_session_t *sbi_sess = user_data; |
1631 | |
|
1632 | 0 | ogs_assert(sbi_sess); |
1633 | 0 | addr = sbi_sess->addr; |
1634 | 0 | ogs_assert(addr); |
1635 | | |
1636 | 0 | ogs_assert(msg); |
1637 | | |
1638 | 0 | ogs_error("[%s]:%d http2 error: %.*s", |
1639 | 0 | OGS_ADDR(addr, buf), OGS_PORT(addr), (int)len, msg); |
1640 | |
|
1641 | 0 | return 0; |
1642 | 0 | } |
1643 | | |
1644 | | static int on_invalid_frame_recv(nghttp2_session *session, |
1645 | | const nghttp2_frame *frame, |
1646 | | int error_code, void *user_data) |
1647 | 0 | { |
1648 | 0 | char buf[OGS_ADDRSTRLEN]; |
1649 | 0 | ogs_sockaddr_t *addr = NULL; |
1650 | |
|
1651 | 0 | ogs_sbi_session_t *sbi_sess = user_data; |
1652 | |
|
1653 | 0 | ogs_assert(sbi_sess); |
1654 | 0 | addr = sbi_sess->addr; |
1655 | 0 | ogs_assert(addr); |
1656 | | |
1657 | 0 | ogs_error("[%s]:%d invalid frame (%d:%s)", |
1658 | 0 | OGS_ADDR(addr, buf), OGS_PORT(addr), |
1659 | 0 | error_code, nghttp2_strerror(error_code)); |
1660 | 0 | return 0; |
1661 | 0 | } |
1662 | | |
1663 | | static int on_invalid_header(nghttp2_session *session, |
1664 | | const nghttp2_frame *frame, |
1665 | | const uint8_t *name, size_t namelen, |
1666 | | const uint8_t *value, size_t valuelen, |
1667 | | uint8_t flags, void *user_data) |
1668 | 0 | { |
1669 | 0 | char buf[OGS_ADDRSTRLEN]; |
1670 | 0 | ogs_sockaddr_t *addr = NULL; |
1671 | 0 | char *namestr = NULL, *valuestr = NULL; |
1672 | |
|
1673 | 0 | ogs_sbi_session_t *sbi_sess = user_data; |
1674 | |
|
1675 | 0 | ogs_assert(sbi_sess); |
1676 | 0 | addr = sbi_sess->addr; |
1677 | 0 | ogs_assert(addr); |
1678 | | |
1679 | 0 | namestr = ogs_strndup((const char *)name, namelen); |
1680 | 0 | ogs_assert(namestr); |
1681 | | |
1682 | 0 | valuestr = ogs_strndup((const char *)value, valuelen); |
1683 | 0 | ogs_assert(valuestr); |
1684 | | |
1685 | 0 | ogs_error("[%s]:%d invalid header (%s:%s)", |
1686 | 0 | OGS_ADDR(addr, buf), OGS_PORT(addr), namestr, valuestr); |
1687 | |
|
1688 | 0 | ogs_free(namestr); |
1689 | 0 | ogs_free(valuestr); |
1690 | |
|
1691 | 0 | return 0; |
1692 | 0 | } |
1693 | | |
1694 | | static int on_begin_frame(nghttp2_session *session, const nghttp2_frame_hd *hd, |
1695 | | void *user_data) |
1696 | 0 | { |
1697 | 0 | char buf[OGS_ADDRSTRLEN]; |
1698 | 0 | ogs_sockaddr_t *addr = NULL; |
1699 | 0 | ogs_sbi_session_t *sbi_sess = user_data; |
1700 | |
|
1701 | 0 | ogs_assert(sbi_sess); |
1702 | 0 | addr = sbi_sess->addr; |
1703 | 0 | ogs_assert(addr); |
1704 | | |
1705 | 0 | ogs_assert(hd); |
1706 | | |
1707 | 0 | if ((hd->type == NGHTTP2_HEADERS) && |
1708 | 0 | (hd->stream_id < sbi_sess->last_stream_id)) { |
1709 | 0 | ogs_error("[%s]:%d invalid stream id(%d) >= last stream id(%d)", |
1710 | 0 | OGS_ADDR(addr, buf), OGS_PORT(addr), |
1711 | 0 | hd->stream_id, sbi_sess->last_stream_id); |
1712 | 0 | return NGHTTP2_ERR_CALLBACK_FAILURE; |
1713 | 0 | } |
1714 | | |
1715 | 0 | return 0; |
1716 | 0 | } |
1717 | | |
1718 | | static int on_begin_headers(nghttp2_session *session, |
1719 | | const nghttp2_frame *frame, void *user_data) |
1720 | 0 | { |
1721 | 0 | ogs_sbi_session_t *sbi_sess = user_data; |
1722 | 0 | ogs_sbi_stream_t *stream = NULL; |
1723 | 0 | int rv; |
1724 | |
|
1725 | 0 | ogs_assert(sbi_sess); |
1726 | 0 | ogs_assert(session); |
1727 | 0 | ogs_assert(frame); |
1728 | | |
1729 | 0 | if (frame->hd.type != NGHTTP2_HEADERS || |
1730 | 0 | frame->headers.cat != NGHTTP2_HCAT_REQUEST) { |
1731 | 0 | return 0; |
1732 | 0 | } |
1733 | | |
1734 | 0 | stream = stream_add(sbi_sess, frame->hd.stream_id); |
1735 | 0 | if (!stream) { |
1736 | 0 | ogs_error("stream_add() failed for stream [%d]", |
1737 | 0 | frame->hd.stream_id); |
1738 | |
|
1739 | 0 | rv = nghttp2_submit_rst_stream( |
1740 | 0 | session, NGHTTP2_FLAG_NONE, |
1741 | 0 | frame->hd.stream_id, NGHTTP2_REFUSED_STREAM); |
1742 | 0 | if (rv != 0) |
1743 | 0 | ogs_error("nghttp2_submit_rst_stream() failed (%d:%s)", |
1744 | 0 | rv, nghttp2_strerror(rv)); |
1745 | |
|
1746 | 0 | return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; |
1747 | 0 | } |
1748 | | |
1749 | 0 | ogs_debug("STREAM added [%d]", frame->hd.stream_id); |
1750 | |
|
1751 | 0 | nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, stream); |
1752 | |
|
1753 | 0 | return 0; |
1754 | 0 | } |
1755 | | |
1756 | | static int session_send_preface(ogs_sbi_session_t *sbi_sess) |
1757 | 0 | { |
1758 | 0 | int rv; |
1759 | 0 | nghttp2_settings_entry iv[1] = { |
1760 | 0 | { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, ogs_app()->pool.stream } |
1761 | 0 | }; |
1762 | |
|
1763 | 0 | ogs_assert(sbi_sess); |
1764 | 0 | ogs_assert(sbi_sess->session); |
1765 | | |
1766 | 0 | rv = nghttp2_submit_settings( |
1767 | 0 | sbi_sess->session, NGHTTP2_FLAG_NONE, iv, OGS_ARRAY_SIZE(iv)); |
1768 | 0 | if (rv != 0) { |
1769 | 0 | ogs_error("nghttp2_submit_settings() failed (%d:%s)", |
1770 | 0 | rv, nghttp2_strerror(rv)); |
1771 | 0 | return OGS_ERROR; |
1772 | 0 | } |
1773 | | |
1774 | 0 | return session_send(sbi_sess); |
1775 | 0 | } |
1776 | | |
1777 | | #if USE_SEND_DATA_WITH_NO_COPY |
1778 | | static int on_send_data(nghttp2_session *session, nghttp2_frame *frame, |
1779 | | const uint8_t *framehd, size_t length, |
1780 | | nghttp2_data_source *source, void *user_data) |
1781 | 0 | { |
1782 | 0 | ogs_sbi_session_t *sbi_sess = user_data; |
1783 | |
|
1784 | 0 | ogs_sbi_response_t *response = NULL; |
1785 | 0 | ogs_sbi_stream_t *stream = NULL; |
1786 | 0 | ogs_pkbuf_t *pkbuf = NULL; |
1787 | 0 | size_t padlen = 0; |
1788 | |
|
1789 | 0 | ogs_assert(session); |
1790 | 0 | ogs_assert(frame); |
1791 | | |
1792 | 0 | stream = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); |
1793 | 0 | if (!stream) { |
1794 | 0 | ogs_error("no stream [%d]", frame->hd.stream_id); |
1795 | 0 | return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; |
1796 | 0 | } |
1797 | | |
1798 | 0 | ogs_assert(sbi_sess); |
1799 | | |
1800 | 0 | ogs_assert(source); |
1801 | 0 | response = source->ptr; |
1802 | 0 | ogs_assert(response); |
1803 | | |
1804 | 0 | ogs_assert(response->http.content); |
1805 | 0 | ogs_assert(response->http.content_length); |
1806 | | |
1807 | 0 | ogs_assert(framehd); |
1808 | 0 | ogs_assert(length); |
1809 | | |
1810 | 0 | pkbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); |
1811 | 0 | ogs_assert(pkbuf); |
1812 | 0 | ogs_pkbuf_put_data(pkbuf, framehd, 9); |
1813 | |
|
1814 | 0 | padlen = frame->data.padlen; |
1815 | |
|
1816 | 0 | if (padlen > 0) { |
1817 | 0 | ogs_pkbuf_put_u8(pkbuf, padlen-1); |
1818 | 0 | } |
1819 | |
|
1820 | 0 | ogs_pkbuf_put_data(pkbuf, |
1821 | 0 | response->http.content, response->http.content_length); |
1822 | |
|
1823 | 0 | if (padlen > 0) { |
1824 | 0 | memset(pkbuf->tail, 0, padlen-1); |
1825 | 0 | ogs_pkbuf_put(pkbuf, padlen-1); |
1826 | 0 | } |
1827 | |
|
1828 | 0 | session_write_to_buffer(sbi_sess, pkbuf); |
1829 | |
|
1830 | 0 | return 0; |
1831 | 0 | } |
1832 | | #else |
1833 | | static ssize_t send_callback(nghttp2_session *session, const uint8_t *data, |
1834 | | size_t length, int flags, void *user_data) |
1835 | | { |
1836 | | ogs_sbi_session_t *sbi_sess = user_data; |
1837 | | ogs_sock_t *sock = NULL; |
1838 | | ogs_socket_t fd = INVALID_SOCKET; |
1839 | | |
1840 | | ogs_pkbuf_t *pkbuf = NULL; |
1841 | | |
1842 | | ogs_assert(sbi_sess); |
1843 | | sock = sbi_sess->sock; |
1844 | | ogs_assert(sock); |
1845 | | fd = sock->fd; |
1846 | | ogs_assert(fd != INVALID_SOCKET); |
1847 | | |
1848 | | ogs_assert(data); |
1849 | | ogs_assert(length); |
1850 | | |
1851 | | pkbuf = ogs_pkbuf_alloc(NULL, length); |
1852 | | ogs_assert(pkbuf); |
1853 | | ogs_pkbuf_put_data(pkbuf, data, length); |
1854 | | |
1855 | | session_write_to_buffer(sbi_sess, pkbuf); |
1856 | | |
1857 | | return length; |
1858 | | } |
1859 | | #endif |
1860 | | |
1861 | | static int session_send(ogs_sbi_session_t *sbi_sess) |
1862 | 0 | { |
1863 | 0 | #if USE_SEND_DATA_WITH_NO_COPY |
1864 | 0 | ogs_pkbuf_t *pkbuf = NULL; |
1865 | | #else |
1866 | | int rv; |
1867 | | #endif |
1868 | |
|
1869 | 0 | ogs_assert(sbi_sess); |
1870 | 0 | ogs_assert(sbi_sess->session); |
1871 | | |
1872 | 0 | #if USE_SEND_DATA_WITH_NO_COPY |
1873 | 0 | for (;;) { |
1874 | 0 | const uint8_t *data = NULL; |
1875 | 0 | ssize_t data_len; |
1876 | |
|
1877 | 0 | data_len = nghttp2_session_mem_send(sbi_sess->session, &data); |
1878 | 0 | if (data_len < 0) { |
1879 | 0 | ogs_error("nghttp2_session_mem_send() failed (%d:%s)", |
1880 | 0 | (int)data_len, nghttp2_strerror((int)data_len)); |
1881 | 0 | return OGS_ERROR; |
1882 | 0 | } |
1883 | | |
1884 | 0 | if (data_len == 0) { |
1885 | 0 | break; |
1886 | 0 | } |
1887 | | |
1888 | 0 | pkbuf = ogs_pkbuf_alloc(NULL, data_len); |
1889 | 0 | ogs_assert(pkbuf); |
1890 | 0 | ogs_pkbuf_put_data(pkbuf, data, data_len); |
1891 | |
|
1892 | 0 | session_write_to_buffer(sbi_sess, pkbuf); |
1893 | 0 | } |
1894 | | #else |
1895 | | rv = nghttp2_session_send(sbi_sess->session); |
1896 | | if (rv != 0) { |
1897 | | ogs_error("nghttp_session_send() failed (%d:%s)", |
1898 | | rv, nghttp2_strerror(rv)); |
1899 | | return OGS_ERROR; |
1900 | | } |
1901 | | #endif |
1902 | | |
1903 | 0 | return OGS_OK; |
1904 | 0 | } |
1905 | | |
1906 | | static void session_write_callback(short when, ogs_socket_t fd, void *data) |
1907 | 0 | { |
1908 | 0 | ogs_sbi_session_t *sbi_sess = data; |
1909 | 0 | ogs_pkbuf_t *pkbuf = NULL; |
1910 | |
|
1911 | 0 | ogs_assert(sbi_sess); |
1912 | | |
1913 | 0 | if (ogs_list_empty(&sbi_sess->write_queue) == true) { |
1914 | 0 | if (sbi_sess->poll.write) { |
1915 | 0 | ogs_pollset_remove(sbi_sess->poll.write); |
1916 | 0 | sbi_sess->poll.write = NULL; |
1917 | 0 | } else |
1918 | 0 | ogs_warn("poll.write has already been removed"); |
1919 | |
|
1920 | 0 | return; |
1921 | 0 | } |
1922 | | |
1923 | 0 | pkbuf = ogs_list_first(&sbi_sess->write_queue); |
1924 | 0 | ogs_assert(pkbuf); |
1925 | 0 | ogs_list_remove(&sbi_sess->write_queue, pkbuf); |
1926 | |
|
1927 | 0 | if (sbi_sess->ssl) |
1928 | 0 | SSL_write(sbi_sess->ssl, pkbuf->data, pkbuf->len); |
1929 | 0 | else |
1930 | 0 | ogs_send(fd, pkbuf->data, pkbuf->len, 0); |
1931 | |
|
1932 | 0 | ogs_log_hexdump(OGS_LOG_DEBUG, pkbuf->data, pkbuf->len); |
1933 | |
|
1934 | 0 | ogs_pkbuf_free(pkbuf); |
1935 | 0 | } |
1936 | | |
1937 | | static void session_write_to_buffer( |
1938 | | ogs_sbi_session_t *sbi_sess, ogs_pkbuf_t *pkbuf) |
1939 | 0 | { |
1940 | 0 | ogs_sock_t *sock = NULL; |
1941 | 0 | ogs_socket_t fd = INVALID_SOCKET; |
1942 | |
|
1943 | 0 | ogs_assert(pkbuf); |
1944 | | |
1945 | 0 | ogs_assert(sbi_sess); |
1946 | 0 | sock = sbi_sess->sock; |
1947 | 0 | ogs_assert(sock); |
1948 | 0 | fd = sock->fd; |
1949 | 0 | ogs_assert(fd != INVALID_SOCKET); |
1950 | | |
1951 | 0 | ogs_list_add(&sbi_sess->write_queue, pkbuf); |
1952 | |
|
1953 | 0 | if (!sbi_sess->poll.write) { |
1954 | 0 | sbi_sess->poll.write = ogs_pollset_add(ogs_app()->pollset, |
1955 | 0 | OGS_POLLOUT, fd, session_write_callback, sbi_sess); |
1956 | 0 | ogs_assert(sbi_sess->poll.write); |
1957 | 0 | } |
1958 | 0 | } |