1 #include <sys/poll.h>
2 #include <openssl/ssl.h>
3 #include <uv.h>
4 #include <assert.h>
5 #ifdef USE_QUIC
6 # include <sys/time.h>
7 #endif
8
9 typedef struct app_conn_st APP_CONN;
10 typedef struct upper_write_op_st UPPER_WRITE_OP;
11 typedef struct lower_write_op_st LOWER_WRITE_OP;
12
13 typedef void (app_connect_cb)(APP_CONN *conn, int status, void *arg);
14 typedef void (app_write_cb)(APP_CONN *conn, int status, void *arg);
15 typedef void (app_read_cb)(APP_CONN *conn, void *buf, size_t buf_len, void *arg);
16
17 #ifdef USE_QUIC
18 static void set_timer(APP_CONN *conn);
19 #else
20 static void tcp_connect_done(uv_connect_t *tcp_connect, int status);
21 #endif
22 static void net_connect_fail_close_done(uv_handle_t *handle);
23 static int handshake_ssl(APP_CONN *conn);
24 static void flush_write_buf(APP_CONN *conn);
25 static void set_rx(APP_CONN *conn);
26 static int try_write(APP_CONN *conn, UPPER_WRITE_OP *op);
27 static void handle_pending_writes(APP_CONN *conn);
28 static int write_deferred(APP_CONN *conn, const void *buf, size_t buf_len, app_write_cb *cb, void *arg);
29 static void teardown_continued(uv_handle_t *handle);
30 static int setup_ssl(APP_CONN *conn, const char *hostname);
31
32 #ifdef USE_QUIC
timeval_to_ms(const struct timeval * t)33 static inline int timeval_to_ms(const struct timeval *t)
34 {
35 return t->tv_sec*1000 + t->tv_usec/1000;
36 }
37 #endif
38
39 /*
40 * Structure to track an application-level write request. Only created
41 * if SSL_write does not accept the data immediately, typically because
42 * it is in WANT_READ.
43 */
44 struct upper_write_op_st {
45 struct upper_write_op_st *prev, *next;
46 const uint8_t *buf;
47 size_t buf_len, written;
48 APP_CONN *conn;
49 app_write_cb *cb;
50 void *cb_arg;
51 };
52
53 /*
54 * Structure to track a network-level write request.
55 */
56 struct lower_write_op_st {
57 #ifdef USE_QUIC
58 uv_udp_send_t w;
59 #else
60 uv_write_t w;
61 #endif
62 uv_buf_t b;
63 uint8_t *buf;
64 APP_CONN *conn;
65 };
66
67 /*
68 * Application connection object.
69 */
70 struct app_conn_st {
71 SSL_CTX *ctx;
72 SSL *ssl;
73 BIO *net_bio;
74 #ifdef USE_QUIC
75 uv_udp_t udp;
76 uv_timer_t timer;
77 #else
78 uv_stream_t *stream;
79 uv_tcp_t tcp;
80 uv_connect_t tcp_connect;
81 #endif
82 app_connect_cb *app_connect_cb; /* called once handshake is done */
83 void *app_connect_arg;
84 app_read_cb *app_read_cb; /* application's on-RX callback */
85 void *app_read_arg;
86 const char *hostname;
87 char init_handshake, done_handshake, closed;
88 char *teardown_done;
89
90 UPPER_WRITE_OP *pending_upper_write_head, *pending_upper_write_tail;
91 };
92
93 /*
94 * The application is initializing and wants an SSL_CTX which it will use for
95 * some number of outgoing connections, which it creates in subsequent calls to
96 * new_conn. The application may also call this function multiple times to
97 * create multiple SSL_CTX.
98 */
create_ssl_ctx(void)99 SSL_CTX *create_ssl_ctx(void)
100 {
101 SSL_CTX *ctx;
102
103 #ifdef USE_QUIC
104 ctx = SSL_CTX_new(OSSL_QUIC_client_method());
105 #else
106 ctx = SSL_CTX_new(TLS_client_method());
107 #endif
108 if (ctx == NULL)
109 return NULL;
110
111 /* Enable trust chain verification. */
112 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
113
114 /* Load default root CA store. */
115 if (SSL_CTX_set_default_verify_paths(ctx) == 0) {
116 SSL_CTX_free(ctx);
117 return NULL;
118 }
119
120 return ctx;
121 }
122
123 /*
124 * The application wants to create a new outgoing connection using a given
125 * SSL_CTX. An outgoing TCP connection is started and the callback is called
126 * asynchronously when the TLS handshake is complete.
127 *
128 * hostname is a string like "openssl.org" used for certificate validation.
129 */
130
new_conn(SSL_CTX * ctx,const char * hostname,struct sockaddr * sa,socklen_t sa_len,app_connect_cb * cb,void * arg)131 APP_CONN *new_conn(SSL_CTX *ctx, const char *hostname,
132 struct sockaddr *sa, socklen_t sa_len,
133 app_connect_cb *cb, void *arg)
134 {
135 int rc;
136 APP_CONN *conn = NULL;
137
138 conn = calloc(1, sizeof(APP_CONN));
139 if (!conn)
140 return NULL;
141
142 #ifdef USE_QUIC
143 uv_udp_init(uv_default_loop(), &conn->udp);
144 conn->udp.data = conn;
145
146 uv_timer_init(uv_default_loop(), &conn->timer);
147 conn->timer.data = conn;
148 #else
149 uv_tcp_init(uv_default_loop(), &conn->tcp);
150 conn->tcp.data = conn;
151
152 conn->stream = (uv_stream_t *)&conn->tcp;
153 #endif
154
155 conn->app_connect_cb = cb;
156 conn->app_connect_arg = arg;
157 #ifdef USE_QUIC
158 rc = uv_udp_connect(&conn->udp, sa);
159 #else
160 conn->tcp_connect.data = conn;
161 rc = uv_tcp_connect(&conn->tcp_connect, &conn->tcp, sa, tcp_connect_done);
162 #endif
163 if (rc < 0) {
164 #ifdef USE_QUIC
165 uv_close((uv_handle_t *)&conn->udp, net_connect_fail_close_done);
166 #else
167 uv_close((uv_handle_t *)&conn->tcp, net_connect_fail_close_done);
168 #endif
169 return NULL;
170 }
171
172 conn->ctx = ctx;
173 conn->hostname = hostname;
174
175 #ifdef USE_QUIC
176 rc = setup_ssl(conn, hostname);
177 if (rc < 0) {
178 uv_close((uv_handle_t *)&conn->udp, net_connect_fail_close_done);
179 return NULL;
180 }
181 #endif
182
183 return conn;
184 }
185
186 /*
187 * The application wants to start reading from the SSL stream.
188 * The callback is called whenever data is available.
189 */
app_read_start(APP_CONN * conn,app_read_cb * cb,void * arg)190 int app_read_start(APP_CONN *conn, app_read_cb *cb, void *arg)
191 {
192 conn->app_read_cb = cb;
193 conn->app_read_arg = arg;
194 set_rx(conn);
195 return 0;
196 }
197
198 /*
199 * The application wants to write. The callback is called once the
200 * write is complete. The callback should free the buffer.
201 */
app_write(APP_CONN * conn,const void * buf,size_t buf_len,app_write_cb * cb,void * arg)202 int app_write(APP_CONN *conn, const void *buf, size_t buf_len, app_write_cb *cb, void *arg)
203 {
204 write_deferred(conn, buf, buf_len, cb, arg);
205 handle_pending_writes(conn);
206 return buf_len;
207 }
208
209 /*
210 * The application wants to close the connection and free bookkeeping
211 * structures.
212 */
teardown(APP_CONN * conn)213 void teardown(APP_CONN *conn)
214 {
215 char teardown_done = 0;
216
217 if (conn == NULL)
218 return;
219
220 BIO_free_all(conn->net_bio);
221 SSL_free(conn->ssl);
222
223 #ifndef USE_QUIC
224 uv_cancel((uv_req_t *)&conn->tcp_connect);
225 #endif
226
227 conn->teardown_done = &teardown_done;
228 #ifdef USE_QUIC
229 uv_close((uv_handle_t *)&conn->udp, teardown_continued);
230 uv_close((uv_handle_t *)&conn->timer, teardown_continued);
231 #else
232 uv_close((uv_handle_t *)conn->stream, teardown_continued);
233 #endif
234
235 /* Just wait synchronously until teardown completes. */
236 #ifdef USE_QUIC
237 while (teardown_done < 2)
238 #else
239 while (!teardown_done)
240 #endif
241 uv_run(uv_default_loop(), UV_RUN_DEFAULT);
242 }
243
244 /*
245 * The application is shutting down and wants to free a previously
246 * created SSL_CTX.
247 */
teardown_ctx(SSL_CTX * ctx)248 void teardown_ctx(SSL_CTX *ctx)
249 {
250 SSL_CTX_free(ctx);
251 }
252
253 /*
254 * ============================================================================
255 * Internal implementation functions.
256 */
enqueue_upper_write_op(APP_CONN * conn,UPPER_WRITE_OP * op)257 static void enqueue_upper_write_op(APP_CONN *conn, UPPER_WRITE_OP *op)
258 {
259 op->prev = conn->pending_upper_write_tail;
260 if (op->prev)
261 op->prev->next = op;
262
263 conn->pending_upper_write_tail = op;
264 if (conn->pending_upper_write_head == NULL)
265 conn->pending_upper_write_head = op;
266 }
267
dequeue_upper_write_op(APP_CONN * conn)268 static void dequeue_upper_write_op(APP_CONN *conn)
269 {
270 if (conn->pending_upper_write_head == NULL)
271 return;
272
273 if (conn->pending_upper_write_head->next == NULL) {
274 conn->pending_upper_write_head = NULL;
275 conn->pending_upper_write_tail = NULL;
276 } else {
277 conn->pending_upper_write_head = conn->pending_upper_write_head->next;
278 conn->pending_upper_write_head->prev = NULL;
279 }
280 }
281
net_read_alloc(uv_handle_t * handle,size_t suggested_size,uv_buf_t * buf)282 static void net_read_alloc(uv_handle_t *handle,
283 size_t suggested_size, uv_buf_t *buf)
284 {
285 #ifdef USE_QUIC
286 if (suggested_size < 1472)
287 suggested_size = 1472;
288 #endif
289
290 buf->base = malloc(suggested_size);
291 buf->len = suggested_size;
292 }
293
on_rx_push(APP_CONN * conn)294 static void on_rx_push(APP_CONN *conn)
295 {
296 int srd, rc;
297 int buf_len = 4096;
298
299 do {
300 if (!conn->app_read_cb)
301 return;
302
303 void *buf = malloc(buf_len);
304 if (!buf)
305 return;
306
307 srd = SSL_read(conn->ssl, buf, buf_len);
308 flush_write_buf(conn);
309 if (srd <= 0) {
310 rc = SSL_get_error(conn->ssl, srd);
311 if (rc == SSL_ERROR_WANT_READ) {
312 free(buf);
313 return;
314 }
315 }
316
317 conn->app_read_cb(conn, buf, srd, conn->app_read_arg);
318 } while (srd == buf_len);
319 }
320
net_error(APP_CONN * conn)321 static void net_error(APP_CONN *conn)
322 {
323 conn->closed = 1;
324 set_rx(conn);
325
326 if (conn->app_read_cb)
327 conn->app_read_cb(conn, NULL, 0, conn->app_read_arg);
328 }
329
handle_pending_writes(APP_CONN * conn)330 static void handle_pending_writes(APP_CONN *conn)
331 {
332 int rc;
333
334 if (conn->pending_upper_write_head == NULL)
335 return;
336
337 do {
338 UPPER_WRITE_OP *op = conn->pending_upper_write_head;
339 rc = try_write(conn, op);
340 if (rc <= 0)
341 break;
342
343 dequeue_upper_write_op(conn);
344 free(op);
345 } while (conn->pending_upper_write_head != NULL);
346
347 set_rx(conn);
348 }
349
350 #ifdef USE_QUIC
net_read_done(uv_udp_t * stream,ssize_t nr,const uv_buf_t * buf,const struct sockaddr * addr,unsigned int flags)351 static void net_read_done(uv_udp_t *stream, ssize_t nr, const uv_buf_t *buf,
352 const struct sockaddr *addr, unsigned int flags)
353 #else
354 static void net_read_done(uv_stream_t *stream, ssize_t nr, const uv_buf_t *buf)
355 #endif
356 {
357 int rc;
358 APP_CONN *conn = (APP_CONN *)stream->data;
359
360 if (nr < 0) {
361 free(buf->base);
362 net_error(conn);
363 return;
364 }
365
366 if (nr > 0) {
367 int wr = BIO_write(conn->net_bio, buf->base, nr);
368 assert(wr == nr);
369 }
370
371 free(buf->base);
372
373 if (!conn->done_handshake) {
374 rc = handshake_ssl(conn);
375 if (rc < 0) {
376 fprintf(stderr, "handshake error: %d\n", rc);
377 return;
378 }
379
380 if (!conn->done_handshake)
381 return;
382 }
383
384 handle_pending_writes(conn);
385 on_rx_push(conn);
386 }
387
set_rx(APP_CONN * conn)388 static void set_rx(APP_CONN *conn)
389 {
390 #ifdef USE_QUIC
391 if (!conn->closed)
392 uv_udp_recv_start(&conn->udp, net_read_alloc, net_read_done);
393 else
394 uv_udp_recv_stop(&conn->udp);
395 #else
396 if (!conn->closed && (conn->app_read_cb || (!conn->done_handshake && conn->init_handshake) || conn->pending_upper_write_head != NULL))
397 uv_read_start(conn->stream, net_read_alloc, net_read_done);
398 else
399 uv_read_stop(conn->stream);
400 #endif
401 }
402
403 #ifdef USE_QUIC
net_write_done(uv_udp_send_t * req,int status)404 static void net_write_done(uv_udp_send_t *req, int status)
405 #else
406 static void net_write_done(uv_write_t *req, int status)
407 #endif
408 {
409 LOWER_WRITE_OP *op = (LOWER_WRITE_OP *)req->data;
410 APP_CONN *conn = op->conn;
411
412 if (status < 0) {
413 fprintf(stderr, "UV write failed %d\n", status);
414 return;
415 }
416
417 free(op->buf);
418 free(op);
419
420 flush_write_buf(conn);
421 }
422
flush_write_buf(APP_CONN * conn)423 static void flush_write_buf(APP_CONN *conn)
424 {
425 int rc, rd;
426 LOWER_WRITE_OP *op;
427 uint8_t *buf;
428
429 buf = malloc(4096);
430 if (!buf)
431 return;
432
433 rd = BIO_read(conn->net_bio, buf, 4096);
434 if (rd <= 0) {
435 free(buf);
436 return;
437 }
438
439 op = calloc(1, sizeof(LOWER_WRITE_OP));
440 if (!op)
441 return;
442
443 op->buf = buf;
444 op->conn = conn;
445 op->w.data = op;
446 op->b.base = (char *)buf;
447 op->b.len = rd;
448
449 #ifdef USE_QUIC
450 rc = uv_udp_send(&op->w, &conn->udp, &op->b, 1, NULL, net_write_done);
451 #else
452 rc = uv_write(&op->w, conn->stream, &op->b, 1, net_write_done);
453 #endif
454 if (rc < 0) {
455 free(buf);
456 free(op);
457 fprintf(stderr, "UV write failed\n");
458 return;
459 }
460 }
461
handshake_done_ssl(APP_CONN * conn)462 static void handshake_done_ssl(APP_CONN *conn)
463 {
464 #ifdef USE_QUIC
465 set_timer(conn);
466 #endif
467 conn->app_connect_cb(conn, 0, conn->app_connect_arg);
468 }
469
handshake_ssl(APP_CONN * conn)470 static int handshake_ssl(APP_CONN *conn)
471 {
472 int rc, rcx;
473
474 conn->init_handshake = 1;
475
476 rc = SSL_do_handshake(conn->ssl);
477 if (rc > 0) {
478 conn->done_handshake = 1;
479 handshake_done_ssl(conn);
480 set_rx(conn);
481 return 0;
482 }
483
484 flush_write_buf(conn);
485 rcx = SSL_get_error(conn->ssl, rc);
486 if (rcx == SSL_ERROR_WANT_READ) {
487 set_rx(conn);
488 return 0;
489 }
490
491 fprintf(stderr, "Handshake error: %d\n", rcx);
492 return -rcx;
493 }
494
setup_ssl(APP_CONN * conn,const char * hostname)495 static int setup_ssl(APP_CONN *conn, const char *hostname)
496 {
497 BIO *internal_bio = NULL, *net_bio = NULL;
498 SSL *ssl = NULL;
499 #ifdef USE_QUIC
500 static const unsigned char alpn[] = {5, 'd', 'u', 'm', 'm', 'y'};
501 #endif
502
503 ssl = SSL_new(conn->ctx);
504 if (!ssl)
505 return -1;
506
507 SSL_set_connect_state(ssl);
508
509 #ifdef USE_QUIC
510 if (BIO_new_bio_dgram_pair(&internal_bio, 0, &net_bio, 0) <= 0) {
511 SSL_free(ssl);
512 return -1;
513 }
514 #else
515 if (BIO_new_bio_pair(&internal_bio, 0, &net_bio, 0) <= 0) {
516 SSL_free(ssl);
517 return -1;
518 }
519 #endif
520
521 SSL_set_bio(ssl, internal_bio, internal_bio);
522
523 if (SSL_set1_host(ssl, hostname) <= 0) {
524 SSL_free(ssl);
525 return -1;
526 }
527
528 if (SSL_set_tlsext_host_name(ssl, hostname) <= 0) {
529 SSL_free(ssl);
530 return -1;
531 }
532
533 #ifdef USE_QUIC
534 /* Configure ALPN, which is required for QUIC. */
535 if (SSL_set_alpn_protos(ssl, alpn, sizeof(alpn))) {
536 /* Note: SSL_set_alpn_protos returns 1 for failure. */
537 SSL_free(ssl);
538 return -1;
539 }
540 #endif
541
542 conn->net_bio = net_bio;
543 conn->ssl = ssl;
544 return handshake_ssl(conn);
545 }
546
547 #ifndef USE_QUIC
tcp_connect_done(uv_connect_t * tcp_connect,int status)548 static void tcp_connect_done(uv_connect_t *tcp_connect, int status)
549 {
550 int rc;
551 APP_CONN *conn = (APP_CONN *)tcp_connect->data;
552
553 if (status < 0) {
554 uv_stop(uv_default_loop());
555 return;
556 }
557
558 rc = setup_ssl(conn, conn->hostname);
559 if (rc < 0) {
560 fprintf(stderr, "cannot init SSL\n");
561 uv_stop(uv_default_loop());
562 return;
563 }
564 }
565 #endif
566
net_connect_fail_close_done(uv_handle_t * handle)567 static void net_connect_fail_close_done(uv_handle_t *handle)
568 {
569 APP_CONN *conn = (APP_CONN *)handle->data;
570
571 free(conn);
572 }
573
574 #ifdef USE_QUIC
575
timer_done(uv_timer_t * timer)576 static void timer_done(uv_timer_t *timer)
577 {
578 APP_CONN *conn = (APP_CONN *)timer->data;
579
580 SSL_handle_events(conn->ssl);
581 handle_pending_writes(conn);
582 flush_write_buf(conn);
583 set_rx(conn);
584 set_timer(conn); /* repeat timer */
585 }
586
set_timer(APP_CONN * conn)587 static void set_timer(APP_CONN *conn)
588 {
589 struct timeval tv;
590 int ms, is_infinite;
591
592 if (!SSL_get_event_timeout(conn->ssl, &tv, &is_infinite))
593 return;
594
595 ms = is_infinite ? -1 : timeval_to_ms(&tv);
596 if (ms > 0)
597 uv_timer_start(&conn->timer, timer_done, ms, 0);
598 }
599
600 #endif
601
try_write(APP_CONN * conn,UPPER_WRITE_OP * op)602 static int try_write(APP_CONN *conn, UPPER_WRITE_OP *op)
603 {
604 int rc, rcx;
605 size_t written = op->written;
606
607 while (written < op->buf_len) {
608 rc = SSL_write(conn->ssl, op->buf + written, op->buf_len - written);
609 if (rc <= 0) {
610 rcx = SSL_get_error(conn->ssl, rc);
611 if (rcx == SSL_ERROR_WANT_READ) {
612 op->written = written;
613 return 0;
614 } else {
615 if (op->cb != NULL)
616 op->cb(conn, -rcx, op->cb_arg);
617 return 1; /* op should be freed */
618 }
619 }
620
621 written += rc;
622 }
623
624 if (op->cb != NULL)
625 op->cb(conn, 0, op->cb_arg);
626
627 flush_write_buf(conn);
628 return 1; /* op should be freed */
629 }
630
write_deferred(APP_CONN * conn,const void * buf,size_t buf_len,app_write_cb * cb,void * arg)631 static int write_deferred(APP_CONN *conn, const void *buf, size_t buf_len, app_write_cb *cb, void *arg)
632 {
633 UPPER_WRITE_OP *op = calloc(1, sizeof(UPPER_WRITE_OP));
634 if (!op)
635 return -1;
636
637 op->buf = buf;
638 op->buf_len = buf_len;
639 op->conn = conn;
640 op->cb = cb;
641 op->cb_arg = arg;
642
643 enqueue_upper_write_op(conn, op);
644 set_rx(conn);
645 flush_write_buf(conn);
646 return buf_len;
647 }
648
teardown_continued(uv_handle_t * handle)649 static void teardown_continued(uv_handle_t *handle)
650 {
651 APP_CONN *conn = (APP_CONN *)handle->data;
652 UPPER_WRITE_OP *op, *next_op;
653 char *teardown_done = conn->teardown_done;
654
655 #ifdef USE_QUIC
656 if (++*teardown_done < 2)
657 return;
658 #endif
659
660 for (op=conn->pending_upper_write_head; op; op=next_op) {
661 next_op = op->next;
662 free(op);
663 }
664
665 free(conn);
666 #ifndef USE_QUIC
667 *teardown_done = 1;
668 #endif
669 }
670
671 /*
672 * ============================================================================
673 * Example driver for the above code. This is just to demonstrate that the code
674 * works and is not intended to be representative of a real application.
675 */
post_read(APP_CONN * conn,void * buf,size_t buf_len,void * arg)676 static void post_read(APP_CONN *conn, void *buf, size_t buf_len, void *arg)
677 {
678 if (!buf_len) {
679 free(buf);
680 uv_stop(uv_default_loop());
681 return;
682 }
683
684 fwrite(buf, 1, buf_len, stdout);
685 free(buf);
686 }
687
post_write_get(APP_CONN * conn,int status,void * arg)688 static void post_write_get(APP_CONN *conn, int status, void *arg)
689 {
690 if (status < 0) {
691 fprintf(stderr, "write failed: %d\n", status);
692 return;
693 }
694
695 app_read_start(conn, post_read, NULL);
696 }
697
698 char tx_msg[300];
699 int mlen;
700
post_connect(APP_CONN * conn,int status,void * arg)701 static void post_connect(APP_CONN *conn, int status, void *arg)
702 {
703 int wr;
704
705 if (status < 0) {
706 fprintf(stderr, "failed to connect: %d\n", status);
707 uv_stop(uv_default_loop());
708 return;
709 }
710
711 wr = app_write(conn, tx_msg, mlen, post_write_get, NULL);
712 if (wr < mlen) {
713 fprintf(stderr, "error writing request");
714 return;
715 }
716 }
717
main(int argc,char ** argv)718 int main(int argc, char **argv)
719 {
720 int rc = 1;
721 SSL_CTX *ctx = NULL;
722 APP_CONN *conn = NULL;
723 struct addrinfo hints = {0}, *result = NULL;
724
725 if (argc < 3) {
726 fprintf(stderr, "usage: %s host port\n", argv[0]);
727 goto fail;
728 }
729
730 mlen = snprintf(tx_msg, sizeof(tx_msg),
731 "GET / HTTP/1.0\r\nHost: %s\r\n\r\n", argv[1]);
732
733 ctx = create_ssl_ctx();
734 if (!ctx)
735 goto fail;
736
737 hints.ai_family = AF_INET;
738 hints.ai_socktype = SOCK_STREAM;
739 hints.ai_flags = AI_PASSIVE;
740 rc = getaddrinfo(argv[1], argv[2], &hints, &result);
741 if (rc < 0) {
742 fprintf(stderr, "cannot resolve\n");
743 goto fail;
744 }
745
746 conn = new_conn(ctx, argv[1], result->ai_addr, result->ai_addrlen, post_connect, NULL);
747 if (!conn)
748 goto fail;
749
750 uv_run(uv_default_loop(), UV_RUN_DEFAULT);
751
752 rc = 0;
753 fail:
754 teardown(conn);
755 freeaddrinfo(result);
756 uv_loop_close(uv_default_loop());
757 teardown_ctx(ctx);
758 }
759