Mercurial > hg > freeDiameter
comparison freeDiameter/cnxctx.c @ 209:b9f48f2f2a22
Some cleanups in the code
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Tue, 16 Feb 2010 15:29:55 +0900 |
parents | e1da03ba112f |
children | 929513df9024 |
comparison
equal
deleted
inserted
replaced
208:e1da03ba112f | 209:b9f48f2f2a22 |
---|---|
133 char addrbuf[INET6_ADDRSTRLEN]; | 133 char addrbuf[INET6_ADDRSTRLEN]; |
134 int rc; | 134 int rc; |
135 rc = getnameinfo(sa, sizeof(sSS), addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); | 135 rc = getnameinfo(sa, sizeof(sSS), addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); |
136 if (rc) | 136 if (rc) |
137 snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); | 137 snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); |
138 snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Srv TCP [%s]:%hu (%d)", addrbuf, port, cnx->cc_socket); | 138 snprintf(cnx->cc_id, sizeof(cnx->cc_id), "TCP srv [%s]:%hu (%d)", addrbuf, port, cnx->cc_socket); |
139 } | 139 } |
140 | 140 |
141 cnx->cc_proto = IPPROTO_TCP; | 141 cnx->cc_proto = IPPROTO_TCP; |
142 | 142 |
143 return cnx; | 143 return cnx; |
168 | 168 |
169 /* Create the socket */ | 169 /* Create the socket */ |
170 CHECK_FCT_DO( fd_sctp_create_bind_server( &cnx->cc_socket, ep_list, port ), goto error ); | 170 CHECK_FCT_DO( fd_sctp_create_bind_server( &cnx->cc_socket, ep_list, port ), goto error ); |
171 | 171 |
172 /* Generate the name for the connection object */ | 172 /* Generate the name for the connection object */ |
173 snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Srv SCTP :%hu (%d)", port, cnx->cc_socket); | 173 snprintf(cnx->cc_id, sizeof(cnx->cc_id), "SCTP srv :%hu (%d)", port, cnx->cc_socket); |
174 | 174 |
175 cnx->cc_proto = IPPROTO_SCTP; | 175 cnx->cc_proto = IPPROTO_SCTP; |
176 | 176 |
177 return cnx; | 177 return cnx; |
178 | 178 |
215 struct fd_endpoint * ep; | 215 struct fd_endpoint * ep; |
216 | 216 |
217 TRACE_ENTRY("%p", serv); | 217 TRACE_ENTRY("%p", serv); |
218 CHECK_PARAMS_DO(serv, return NULL); | 218 CHECK_PARAMS_DO(serv, return NULL); |
219 | 219 |
220 /* Accept the new connection -- this is blocking until new client enters or cancellation */ | 220 /* Accept the new connection -- this is blocking until new client enters or until cancellation */ |
221 CHECK_SYS_DO( cli_sock = accept(serv->cc_socket, (sSA *)&ss, &ss_len), return NULL ); | 221 CHECK_SYS_DO( cli_sock = accept(serv->cc_socket, (sSA *)&ss, &ss_len), return NULL ); |
222 | 222 |
223 if (TRACE_BOOL(INFO)) { | 223 if (TRACE_BOOL(INFO)) { |
224 fd_log_debug("%s : accepted new client [", fd_cnx_getid(serv)); | 224 fd_log_debug("%s : accepted new client [", fd_cnx_getid(serv)); |
225 sSA_DUMP_NODE( &ss, NI_NUMERICHOST ); | 225 sSA_DUMP_NODE( &ss, NI_NUMERICHOST ); |
244 if (rc) { | 244 if (rc) { |
245 snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); | 245 snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); |
246 portbuf[0] = '\0'; | 246 portbuf[0] = '\0'; |
247 } | 247 } |
248 | 248 |
249 snprintf(cli->cc_id, sizeof(cli->cc_id), "Incoming %s [%s]:%s (%d) @ serv (%d)", | 249 snprintf(cli->cc_id, sizeof(cli->cc_id), "{%s} (%d) <- [%s]:%s (%d)", |
250 IPPROTO_NAME(cli->cc_proto), | 250 IPPROTO_NAME(cli->cc_proto), serv->cc_socket, |
251 addrbuf, portbuf, | 251 addrbuf, portbuf, cli->cc_socket); |
252 cli->cc_socket, serv->cc_socket); | |
253 | 252 |
254 /* Name for log messages */ | 253 /* Name for log messages */ |
255 rc = getnameinfo((sSA *)&ss, sizeof(sSS), cli->cc_remid, sizeof(cli->cc_remid), NULL, 0, 0); | 254 rc = getnameinfo((sSA *)&ss, sizeof(sSS), cli->cc_remid, sizeof(cli->cc_remid), NULL, 0, 0); |
256 if (rc) | 255 if (rc) |
257 snprintf(cli->cc_remid, sizeof(cli->cc_remid), "[err:%s]", gai_strerror(rc)); | 256 snprintf(cli->cc_remid, sizeof(cli->cc_remid), "[err:%s]", gai_strerror(rc)); |
313 if (rc) { | 312 if (rc) { |
314 snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); | 313 snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); |
315 portbuf[0] = '\0'; | 314 portbuf[0] = '\0'; |
316 } | 315 } |
317 | 316 |
318 snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Client of TCP server [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket); | 317 snprintf(cnx->cc_id, sizeof(cnx->cc_id), "{TCP} -> [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket); |
319 | 318 |
320 /* Name for log messages */ | 319 /* Name for log messages */ |
321 rc = getnameinfo(sa, addrlen, cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0); | 320 rc = getnameinfo(sa, addrlen, cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0); |
322 if (rc) | 321 if (rc) |
323 snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc)); | 322 snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc)); |
376 if (rc) { | 375 if (rc) { |
377 snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); | 376 snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); |
378 portbuf[0] = '\0'; | 377 portbuf[0] = '\0'; |
379 } | 378 } |
380 | 379 |
381 snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Client of SCTP server [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket); | 380 snprintf(cnx->cc_id, sizeof(cnx->cc_id), "{SCTP} -> [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket); |
382 | 381 |
383 /* Name for log messages */ | 382 /* Name for log messages */ |
384 rc = getnameinfo((sSA *)&primary, sizeof(sSS), cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0); | 383 rc = getnameinfo((sSA *)&primary, sizeof(sSS), cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0); |
385 if (rc) | 384 if (rc) |
386 snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc)); | 385 snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc)); |
417 | 416 |
418 /* Return the TLS state of a connection */ | 417 /* Return the TLS state of a connection */ |
419 int fd_cnx_getTLS(struct cnxctx * conn) | 418 int fd_cnx_getTLS(struct cnxctx * conn) |
420 { | 419 { |
421 CHECK_PARAMS_DO( conn, return 0 ); | 420 CHECK_PARAMS_DO( conn, return 0 ); |
422 return conn->cc_tls; | 421 return conn->cc_status & CC_STATUS_TLS; |
423 } | 422 } |
424 | 423 |
425 /* Get the list of endpoints (IP addresses) of the local and remote peers on this connection */ | 424 /* Get the list of endpoints (IP addresses) of the local and remote peers on this connection */ |
426 int fd_cnx_getendpoints(struct cnxctx * conn, struct fd_list * local, struct fd_list * remote) | 425 int fd_cnx_getendpoints(struct cnxctx * conn, struct fd_list * local, struct fd_list * remote) |
427 { | 426 { |
491 | 490 |
492 /**************************************/ | 491 /**************************************/ |
493 /* Use of a connection object */ | 492 /* Use of a connection object */ |
494 /**************************************/ | 493 /**************************************/ |
495 | 494 |
495 /* An error occurred on the socket */ | |
496 void fd_cnx_markerror(struct cnxctx * conn) | |
497 { | |
498 TRACE_ENTRY("%p", conn); | |
499 CHECK_PARAMS_DO( conn, goto fatal ); | |
500 | |
501 /* Mark the error */ | |
502 conn->cc_status |= CC_STATUS_ERROR; | |
503 | |
504 /* Report the error if not reported yet, and not closing */ | |
505 if ((!(conn->cc_status & CC_STATUS_CLOSING )) && (!(conn->cc_status & CC_STATUS_SIGNALED ))) { | |
506 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_ERROR, 0, NULL), goto fatal); | |
507 conn->cc_status |= CC_STATUS_SIGNALED; | |
508 } | |
509 | |
510 return; | |
511 fatal: | |
512 /* An unrecoverable error occurred, stop the daemon */ | |
513 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); | |
514 } | |
515 | |
496 /* Set the timeout option on the socket */ | 516 /* Set the timeout option on the socket */ |
497 void fd_cnx_s_setto(int sock) | 517 void fd_cnx_s_setto(int sock) |
498 { | 518 { |
499 struct timeval tv; | 519 struct timeval tv; |
500 | 520 |
501 /* Set a timeout on the socket so that in any case we are not stuck waiting for something */ | 521 /* Set a timeout on the socket so that in any case we are not stuck waiting for something */ |
502 memset(&tv, 0, sizeof(tv)); | 522 memset(&tv, 0, sizeof(tv)); |
503 tv.tv_sec = 3; /* allow 3 seconds timeout for TLS session cleanup */ | 523 tv.tv_sec = 3; /* allow 3 seconds timeout for TLS session cleanup */ |
504 CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), /* best effort only */ ); | 524 CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), /* best effort only */ ); |
505 CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), /* Also timeout for sending, to avoid waiting forever */ ); | 525 CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), /* Also timeout for sending, to avoid waiting forever */ ); |
506 } | 526 } |
507 | 527 |
508 /* A recv-like function, taking a cnxctx object instead of socket as entry. Only used to filter timeouts error (GNUTLS does not like these...) */ | 528 /* A recv-like function, taking a cnxctx object instead of socket as entry. We use it to quickly react to timeouts without traversing GNUTLS wrapper each time */ |
509 ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length) | 529 ssize_t fd_cnx_s_recv(struct cnxctx * conn, void *buffer, size_t length) |
510 { | 530 { |
511 ssize_t ret = 0; | 531 ssize_t ret = 0; |
512 int timedout = 0; | 532 int timedout = 0; |
513 again: | 533 again: |
514 ret = recv(conn->cc_socket, buffer, length, 0); | 534 ret = recv(conn->cc_socket, buffer, length, 0); |
515 /* Handle special case of timeout */ | 535 /* Handle special case of timeout */ |
516 if ((ret < 0) && (errno == EAGAIN)) { | 536 if ((ret < 0) && (errno == EAGAIN)) { |
517 if (!conn->cc_closing) | 537 if (! (conn->cc_status & CC_STATUS_CLOSING)) |
518 goto again; /* don't care, just ignore */ | 538 goto again; /* don't care, just ignore */ |
519 if (!timedout) { | 539 if (!timedout) { |
520 timedout ++; /* allow for one timeout while closing */ | 540 timedout ++; /* allow for one timeout while closing */ |
521 goto again; | 541 goto again; |
522 } | 542 } |
523 CHECK_SYS_DO(ret, /* continue */); | 543 } |
524 } | 544 |
545 CHECK_SYS_DO(ret, /* continue */); | |
525 | 546 |
526 /* Mark the error */ | 547 /* Mark the error */ |
527 if (ret <= 0) | 548 if (ret <= 0) |
528 conn->cc_goterror=1; | 549 fd_cnx_markerror(conn); |
529 | 550 |
530 return ret; | 551 return ret; |
531 } | 552 } |
532 | 553 |
533 /* Send */ | 554 /* Send */ |
537 int timedout = 0; | 558 int timedout = 0; |
538 again: | 559 again: |
539 ret = send(conn->cc_socket, buffer, length, 0); | 560 ret = send(conn->cc_socket, buffer, length, 0); |
540 /* Handle special case of timeout */ | 561 /* Handle special case of timeout */ |
541 if ((ret < 0) && (errno == EAGAIN)) { | 562 if ((ret < 0) && (errno == EAGAIN)) { |
542 if (!conn->cc_closing) | 563 if (! (conn->cc_status & CC_STATUS_CLOSING)) |
543 goto again; /* don't care, just ignore */ | 564 goto again; /* don't care, just ignore */ |
544 if (!timedout) { | 565 if (!timedout) { |
545 timedout ++; /* allow for one timeout while closing */ | 566 timedout ++; /* allow for one timeout while closing */ |
546 goto again; | 567 goto again; |
547 } | 568 } |
548 CHECK_SYS_DO(ret, /* continue */); | 569 CHECK_SYS_DO(ret, /* continue */); |
549 } | 570 } |
550 | 571 |
551 /* Mark the error */ | 572 /* Mark the error */ |
552 if (ret <= 0) | 573 if (ret <= 0) |
553 conn->cc_goterror=1; | 574 fd_cnx_markerror(conn); |
554 | 575 |
555 return ret; | 576 return ret; |
556 } | 577 } |
557 | 578 |
558 /* Receiver thread (TCP & noTLS) : incoming message is directly saved into the target queue */ | 579 /* Receiver thread (TCP & noTLS) : incoming message is directly saved into the target queue */ |
569 snprintf(buf, sizeof(buf), "Receiver (%d) TCP/noTLS)", conn->cc_socket); | 590 snprintf(buf, sizeof(buf), "Receiver (%d) TCP/noTLS)", conn->cc_socket); |
570 fd_log_threadname ( buf ); | 591 fd_log_threadname ( buf ); |
571 } | 592 } |
572 | 593 |
573 ASSERT( conn->cc_proto == IPPROTO_TCP ); | 594 ASSERT( conn->cc_proto == IPPROTO_TCP ); |
574 ASSERT( conn->cc_tls == 0 ); | 595 ASSERT( ! (conn->cc_status & CC_STATUS_TLS) ); |
575 ASSERT( Target_Queue(conn) ); | 596 ASSERT( Target_Queue(conn) ); |
576 | 597 |
577 /* Receive from a TCP connection: we have to rebuild the message boundaries */ | 598 /* Receive from a TCP connection: we have to rebuild the message boundaries */ |
578 do { | 599 do { |
579 uint8_t header[4]; | 600 uint8_t header[4]; |
583 size_t received = 0; | 604 size_t received = 0; |
584 | 605 |
585 do { | 606 do { |
586 ret = fd_cnx_s_recv(conn, &header[received], sizeof(header) - received); | 607 ret = fd_cnx_s_recv(conn, &header[received], sizeof(header) - received); |
587 if (ret <= 0) { | 608 if (ret <= 0) { |
588 CHECK_SYS_DO(ret, /* continue */); | 609 goto out; /* Stop the thread, the event was already sent */ |
589 goto error; /* Stop the thread, the recipient of the event will cleanup */ | |
590 } | 610 } |
591 | 611 |
592 received += ret; | 612 received += ret; |
593 } while (received < sizeof(header)); | 613 } while (received < sizeof(header)); |
594 | 614 |
597 /* Check the received word is a valid begining of a Diameter message */ | 617 /* Check the received word is a valid begining of a Diameter message */ |
598 if ((header[0] != DIAMETER_VERSION) /* defined in <libfreeDiameter.h> */ | 618 if ((header[0] != DIAMETER_VERSION) /* defined in <libfreeDiameter.h> */ |
599 || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */ | 619 || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */ |
600 /* The message is suspect */ | 620 /* The message is suspect */ |
601 TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length); | 621 TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length); |
602 goto error; /* Stop the thread, the recipient of the event will cleanup */ | 622 fd_cnx_markerror(conn); |
623 goto out; /* Stop the thread, the recipient of the event will cleanup */ | |
603 } | 624 } |
604 | 625 |
605 /* Ok, now we can really receive the data */ | 626 /* Ok, now we can really receive the data */ |
606 CHECK_MALLOC_DO( newmsg = malloc( length ), goto error ); | 627 CHECK_MALLOC_DO( newmsg = malloc( length ), goto fatal ); |
607 memcpy(newmsg, header, sizeof(header)); | 628 memcpy(newmsg, header, sizeof(header)); |
608 | 629 |
609 while (received < length) { | 630 while (received < length) { |
610 pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */ | 631 pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */ |
611 ret = fd_cnx_s_recv(conn, newmsg + received, length - received); | 632 ret = fd_cnx_s_recv(conn, newmsg + received, length - received); |
612 pthread_cleanup_pop(0); | 633 pthread_cleanup_pop(0); |
613 | 634 |
614 if (ret <= 0) { | 635 if (ret <= 0) { |
615 CHECK_SYS_DO(ret, /* continue */); | |
616 free(newmsg); | 636 free(newmsg); |
617 goto error; /* Stop the thread, the recipient of the event will cleanup */ | 637 goto out; |
618 } | 638 } |
619 received += ret; | 639 received += ret; |
620 } | 640 } |
621 | 641 |
622 /* We have received a complete message, send it */ | 642 /* We have received a complete message, pass it to the daemon */ |
623 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), /* continue or destroy everything? */); | 643 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), /* continue or destroy everything? */); |
624 | 644 |
625 } while (conn->cc_loop); | 645 } while (conn->cc_loop); |
626 | 646 |
627 out: | 647 out: |
628 TRACE_DEBUG(FULL, "Thread terminated"); | 648 TRACE_DEBUG(FULL, "Thread terminated"); |
629 return NULL; | 649 return NULL; |
630 error: | 650 |
631 if (!conn->cc_closing) { | 651 fatal: |
632 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_ERROR, 0, NULL), /* continue or destroy everything? */); | 652 /* An unrecoverable error occurred, stop the daemon */ |
633 } | 653 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); |
634 goto out; | 654 goto out; |
635 } | 655 } |
636 | 656 |
637 #ifndef DISABLE_SCTP | 657 #ifndef DISABLE_SCTP |
638 /* Receiver thread (SCTP & noTLS) : incoming message is directly saved into cc_incoming, no need to care for the stream ID */ | 658 /* Receiver thread (SCTP & noTLS) : incoming message is directly saved into cc_incoming, no need to care for the stream ID */ |
642 uint8_t * buf; | 662 uint8_t * buf; |
643 size_t bufsz; | 663 size_t bufsz; |
644 int event; | 664 int event; |
645 | 665 |
646 TRACE_ENTRY("%p", arg); | 666 TRACE_ENTRY("%p", arg); |
647 CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out); | 667 CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto fatal); |
648 | 668 |
649 /* Set the thread name */ | 669 /* Set the thread name */ |
650 { | 670 { |
651 char buf[48]; | 671 char buf[48]; |
652 snprintf(buf, sizeof(buf), "Receiver (%d) SCTP/noTLS)", conn->cc_socket); | 672 snprintf(buf, sizeof(buf), "Receiver (%d) SCTP/noTLS)", conn->cc_socket); |
653 fd_log_threadname ( buf ); | 673 fd_log_threadname ( buf ); |
654 } | 674 } |
655 | 675 |
656 ASSERT( conn->cc_proto == IPPROTO_SCTP ); | 676 ASSERT( conn->cc_proto == IPPROTO_SCTP ); |
657 ASSERT( conn->cc_tls == 0 ); | 677 ASSERT( ! (conn->cc_status & CC_STATUS_TLS) ); |
658 ASSERT( Target_Queue(conn) ); | 678 ASSERT( Target_Queue(conn) ); |
659 | 679 |
660 do { | 680 do { |
661 CHECK_FCT_DO( fd_sctp_recvmeta(conn->cc_socket, NULL, &buf, &bufsz, &event, &conn->cc_closing), goto error ); | 681 CHECK_FCT_DO( fd_sctp_recvmeta(conn->cc_socket, NULL, &buf, &bufsz, &event, &conn->cc_status), goto fatal ); |
662 if (event == FDEVP_CNX_ERROR) { | 682 if (event == FDEVP_CNX_ERROR) { |
663 conn->cc_goterror = 1; | 683 fd_cnx_markerror(conn); |
664 goto error; | 684 goto out; |
665 } | 685 } |
666 | 686 |
667 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), event, bufsz, buf), goto error ); | 687 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), event, bufsz, buf), goto fatal ); |
668 | 688 |
669 } while (conn->cc_loop); | 689 } while (conn->cc_loop); |
670 | 690 |
671 out: | 691 out: |
672 TRACE_DEBUG(FULL, "Thread terminated"); | 692 TRACE_DEBUG(FULL, "Thread terminated"); |
673 return NULL; | 693 return NULL; |
674 error: | 694 |
675 if (!conn->cc_closing) { | 695 fatal: |
676 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_ERROR, 0, NULL), /* continue or destroy everything? */); | 696 /* An unrecoverable error occurred, stop the daemon */ |
677 } | 697 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); |
678 goto out; | 698 goto out; |
679 } | 699 } |
680 #endif /* DISABLE_SCTP */ | 700 #endif /* DISABLE_SCTP */ |
681 | |
682 /* Returns 0 on error, received data size otherwise (always >= 0) */ | |
683 static ssize_t fd_tls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz) | |
684 { | |
685 ssize_t ret; | |
686 again: | |
687 CHECK_GNUTLS_DO( ret = gnutls_record_recv(session, data, sz), | |
688 { | |
689 switch (ret) { | |
690 case GNUTLS_E_REHANDSHAKE: | |
691 if (!conn->cc_closing) | |
692 CHECK_GNUTLS_DO( ret = gnutls_handshake(session), | |
693 { | |
694 if (TRACE_BOOL(INFO)) { | |
695 fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret)); | |
696 } | |
697 ret = 0; | |
698 goto end; | |
699 } ); | |
700 | |
701 case GNUTLS_E_AGAIN: | |
702 case GNUTLS_E_INTERRUPTED: | |
703 if (!conn->cc_closing) | |
704 goto again; | |
705 TRACE_DEBUG(INFO, "Connection is closing, so abord gnutls_record_recv now."); | |
706 ret = 0; | |
707 break; | |
708 | |
709 default: | |
710 TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error"); | |
711 ret = 0; | |
712 } | |
713 } ); | |
714 end: | |
715 if (ret <= 0) | |
716 conn->cc_goterror = 1; | |
717 return ret; | |
718 } | |
719 | |
720 /* Wrapper around gnutls_record_send to handle some error codes */ | |
721 static ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz) | |
722 { | |
723 ssize_t ret; | |
724 again: | |
725 CHECK_GNUTLS_DO( ret = gnutls_record_send(session, data, sz), | |
726 { | |
727 switch (ret) { | |
728 case GNUTLS_E_REHANDSHAKE: | |
729 if (!conn->cc_closing) | |
730 CHECK_GNUTLS_DO( ret = gnutls_handshake(session), | |
731 { | |
732 if (TRACE_BOOL(INFO)) { | |
733 fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret)); | |
734 } | |
735 goto end; | |
736 } ); | |
737 | |
738 case GNUTLS_E_AGAIN: | |
739 case GNUTLS_E_INTERRUPTED: | |
740 if (!conn->cc_closing) | |
741 goto again; | |
742 TRACE_DEBUG(INFO, "Connection is closing, so abord gnutls_record_send now."); | |
743 break; | |
744 | |
745 default: | |
746 TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error"); | |
747 } | |
748 } ); | |
749 end: | |
750 if (ret <= 0) | |
751 conn->cc_goterror = 1; | |
752 return ret; | |
753 } | |
754 | |
755 | |
756 /* The function that receives TLS data and re-builds a Diameter message -- it exits only on error or cancelation */ | |
757 int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session) | |
758 { | |
759 /* No guarantee that GnuTLS preserves the message boundaries, so we re-build it as in TCP */ | |
760 do { | |
761 uint8_t header[4]; | |
762 uint8_t * newmsg; | |
763 size_t length; | |
764 ssize_t ret = 0; | |
765 size_t received = 0; | |
766 | |
767 do { | |
768 ret = fd_tls_recv_handle_error(conn, session, &header[received], sizeof(header) - received); | |
769 if (ret == 0) { | |
770 /* The connection is closed */ | |
771 goto out; | |
772 } | |
773 received += ret; | |
774 } while (received < sizeof(header)); | |
775 | |
776 length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3]; | |
777 | |
778 /* Check the received word is a valid beginning of a Diameter message */ | |
779 if ((header[0] != DIAMETER_VERSION) /* defined in <libfreeDiameter.h> */ | |
780 || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */ | |
781 /* The message is suspect */ | |
782 TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length); | |
783 goto out; | |
784 } | |
785 | |
786 /* Ok, now we can really receive the data */ | |
787 CHECK_MALLOC( newmsg = malloc( length ) ); | |
788 memcpy(newmsg, header, sizeof(header)); | |
789 | |
790 while (received < length) { | |
791 pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */ | |
792 ret = fd_tls_recv_handle_error(conn, session, newmsg + received, length - received); | |
793 pthread_cleanup_pop(0); | |
794 | |
795 if (ret == 0) { | |
796 free(newmsg); | |
797 goto out; /* Stop the thread, the recipient of the event will cleanup */ | |
798 } | |
799 received += ret; | |
800 } | |
801 | |
802 /* We have received a complete message, send it */ | |
803 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), /* continue or destroy everything? */); | |
804 | |
805 } while (1); | |
806 out: | |
807 return ENOTCONN; | |
808 } | |
809 | |
810 /* Receiver thread (TLS & 1 stream SCTP or TCP) : gnutls directly handles the socket, save records into the target queue */ | |
811 static void * rcvthr_tls_single(void * arg) | |
812 { | |
813 struct cnxctx * conn = arg; | |
814 | |
815 TRACE_ENTRY("%p", arg); | |
816 CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto error); | |
817 | |
818 /* Set the thread name */ | |
819 { | |
820 char buf[48]; | |
821 snprintf(buf, sizeof(buf), "Receiver (%d) TLS/single stream", conn->cc_socket); | |
822 fd_log_threadname ( buf ); | |
823 } | |
824 | |
825 ASSERT( conn->cc_tls == 1 ); | |
826 ASSERT( Target_Queue(conn) ); | |
827 | |
828 CHECK_FCT_DO(fd_tls_rcvthr_core(conn, conn->cc_tls_para.session), /* continue */); | |
829 error: | |
830 if (!conn->cc_closing) { | |
831 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_ERROR, 0, NULL), /* continue or destroy everything? */); | |
832 } | |
833 TRACE_DEBUG(FULL, "Thread terminated"); | |
834 return NULL; | |
835 } | |
836 | 701 |
837 /* Start receving messages in clear (no TLS) on the connection */ | 702 /* Start receving messages in clear (no TLS) on the connection */ |
838 int fd_cnx_start_clear(struct cnxctx * conn, int loop) | 703 int fd_cnx_start_clear(struct cnxctx * conn, int loop) |
839 { | 704 { |
840 TRACE_ENTRY("%p %i", conn, loop); | 705 TRACE_ENTRY("%p %i", conn, loop); |
841 | 706 |
842 CHECK_PARAMS( conn && Target_Queue(conn) && (!conn->cc_tls) && (!conn->cc_loop)); | 707 CHECK_PARAMS( conn && Target_Queue(conn) && (!(conn->cc_status & CC_STATUS_TLS)) && (!conn->cc_loop)); |
843 | 708 |
844 /* Release resources in case of a previous call was already made */ | 709 /* Release resources in case of a previous call was already made */ |
845 CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */); | 710 CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */); |
846 | 711 |
847 /* Save the loop request */ | 712 /* Save the loop request */ |
858 CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_notls_sctp, conn ) ); | 723 CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_notls_sctp, conn ) ); |
859 break; | 724 break; |
860 #endif /* DISABLE_SCTP */ | 725 #endif /* DISABLE_SCTP */ |
861 default: | 726 default: |
862 TRACE_DEBUG(INFO, "Unknown protocol: %d", conn->cc_proto); | 727 TRACE_DEBUG(INFO, "Unknown protocol: %d", conn->cc_proto); |
728 ASSERT(0); | |
863 return ENOTSUP; | 729 return ENOTSUP; |
864 } | 730 } |
865 | 731 |
866 return 0; | 732 return 0; |
867 } | 733 } |
868 | 734 |
735 | |
736 | |
737 | |
738 /* Returns 0 on error, received data size otherwise (always >= 0) */ | |
739 static ssize_t fd_tls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz) | |
740 { | |
741 ssize_t ret; | |
742 again: | |
743 CHECK_GNUTLS_DO( ret = gnutls_record_recv(session, data, sz), | |
744 { | |
745 switch (ret) { | |
746 case GNUTLS_E_REHANDSHAKE: | |
747 if (!(conn->cc_status & CC_STATUS_CLOSING)) | |
748 CHECK_GNUTLS_DO( ret = gnutls_handshake(session), | |
749 { | |
750 if (TRACE_BOOL(INFO)) { | |
751 fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret)); | |
752 } | |
753 goto end; | |
754 } ); | |
755 | |
756 case GNUTLS_E_AGAIN: | |
757 case GNUTLS_E_INTERRUPTED: | |
758 if (!(conn->cc_status & CC_STATUS_CLOSING)) | |
759 goto again; | |
760 TRACE_DEBUG(INFO, "Connection is closing, so abord gnutls_record_recv now."); | |
761 break; | |
762 | |
763 default: | |
764 TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error"); | |
765 } | |
766 } ); | |
767 end: | |
768 if (ret <= 0) | |
769 fd_cnx_markerror(conn); | |
770 return ret; | |
771 } | |
772 | |
773 /* Wrapper around gnutls_record_send to handle some error codes */ | |
774 static ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz) | |
775 { | |
776 ssize_t ret; | |
777 again: | |
778 CHECK_GNUTLS_DO( ret = gnutls_record_send(session, data, sz), | |
779 { | |
780 switch (ret) { | |
781 case GNUTLS_E_REHANDSHAKE: | |
782 if (!(conn->cc_status & CC_STATUS_CLOSING)) | |
783 CHECK_GNUTLS_DO( ret = gnutls_handshake(session), | |
784 { | |
785 if (TRACE_BOOL(INFO)) { | |
786 fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret)); | |
787 } | |
788 goto end; | |
789 } ); | |
790 | |
791 case GNUTLS_E_AGAIN: | |
792 case GNUTLS_E_INTERRUPTED: | |
793 if (!(conn->cc_status & CC_STATUS_CLOSING)) | |
794 goto again; | |
795 TRACE_DEBUG(INFO, "Connection is closing, so abord gnutls_record_send now."); | |
796 break; | |
797 | |
798 default: | |
799 TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error"); | |
800 } | |
801 } ); | |
802 end: | |
803 if (ret <= 0) | |
804 fd_cnx_markerror(conn); | |
805 | |
806 return ret; | |
807 } | |
808 | |
809 | |
810 /* The function that receives TLS data and re-builds a Diameter message -- it exits only on error or cancelation */ | |
811 int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session) | |
812 { | |
813 /* No guarantee that GnuTLS preserves the message boundaries, so we re-build it as in TCP */ | |
814 do { | |
815 uint8_t header[4]; | |
816 uint8_t * newmsg; | |
817 size_t length; | |
818 ssize_t ret = 0; | |
819 size_t received = 0; | |
820 | |
821 do { | |
822 ret = fd_tls_recv_handle_error(conn, session, &header[received], sizeof(header) - received); | |
823 if (ret <= 0) { | |
824 /* The connection is closed */ | |
825 goto out; | |
826 } | |
827 received += ret; | |
828 } while (received < sizeof(header)); | |
829 | |
830 length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3]; | |
831 | |
832 /* Check the received word is a valid beginning of a Diameter message */ | |
833 if ((header[0] != DIAMETER_VERSION) /* defined in <libfreeDiameter.h> */ | |
834 || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */ | |
835 /* The message is suspect */ | |
836 TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length); | |
837 fd_cnx_markerror(conn); | |
838 goto out; | |
839 } | |
840 | |
841 /* Ok, now we can really receive the data */ | |
842 CHECK_MALLOC( newmsg = malloc( length ) ); | |
843 memcpy(newmsg, header, sizeof(header)); | |
844 | |
845 while (received < length) { | |
846 pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */ | |
847 ret = fd_tls_recv_handle_error(conn, session, newmsg + received, length - received); | |
848 pthread_cleanup_pop(0); | |
849 | |
850 if (ret <= 0) { | |
851 free(newmsg); | |
852 goto out; | |
853 } | |
854 received += ret; | |
855 } | |
856 | |
857 /* We have received a complete message, pass it to the daemon */ | |
858 CHECK_FCT_DO( ret = fd_event_send( Target_Queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), | |
859 { | |
860 free(newmsg); | |
861 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); | |
862 return ret; | |
863 } ); | |
864 | |
865 } while (1); | |
866 | |
867 out: | |
868 return ENOTCONN; | |
869 } | |
870 | |
871 /* Receiver thread (TLS & 1 stream SCTP or TCP) */ | |
872 static void * rcvthr_tls_single(void * arg) | |
873 { | |
874 struct cnxctx * conn = arg; | |
875 | |
876 TRACE_ENTRY("%p", arg); | |
877 CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), return NULL ); | |
878 | |
879 /* Set the thread name */ | |
880 { | |
881 char buf[48]; | |
882 snprintf(buf, sizeof(buf), "Receiver (%d) TLS/single stream", conn->cc_socket); | |
883 fd_log_threadname ( buf ); | |
884 } | |
885 | |
886 ASSERT( conn->cc_status & CC_STATUS_TLS ); | |
887 ASSERT( Target_Queue(conn) ); | |
888 | |
889 /* The next function only returns when there is an error on the socket */ | |
890 CHECK_FCT_DO(fd_tls_rcvthr_core(conn, conn->cc_tls_para.session), /* continue */); | |
891 | |
892 TRACE_DEBUG(FULL, "Thread terminated"); | |
893 return NULL; | |
894 } | |
895 | |
869 /* Prepare a gnutls session object for handshake */ | 896 /* Prepare a gnutls session object for handshake */ |
870 int fd_tls_prepare(gnutls_session_t * session, int mode, char * priority, void * alt_creds) | 897 int fd_tls_prepare(gnutls_session_t * session, int mode, char * priority, void * alt_creds) |
871 { | 898 { |
872 /* Create the master session context */ | 899 /* Create the session context */ |
873 CHECK_GNUTLS_DO( gnutls_init (session, mode), return ENOMEM ); | 900 CHECK_GNUTLS_DO( gnutls_init (session, mode), return ENOMEM ); |
874 | 901 |
875 /* Set the algorithm suite */ | 902 /* Set the algorithm suite */ |
876 if (priority) { | 903 if (priority) { |
877 const char * errorpos; | 904 const char * errorpos; |
898 int ret, i; | 925 int ret, i; |
899 const gnutls_datum_t *cert_list; | 926 const gnutls_datum_t *cert_list; |
900 unsigned int cert_list_size; | 927 unsigned int cert_list_size; |
901 gnutls_x509_crt_t cert; | 928 gnutls_x509_crt_t cert; |
902 time_t now; | 929 time_t now; |
930 | |
931 TRACE_ENTRY("%p %d", conn, verbose); | |
932 CHECK_PARAMS(conn); | |
903 | 933 |
904 /* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */ | 934 /* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */ |
905 if (verbose && TRACE_BOOL(FULL)) { | 935 if (verbose && TRACE_BOOL(FULL)) { |
906 const char *tmp; | 936 const char *tmp; |
907 gnutls_kx_algorithm_t kx; | 937 gnutls_kx_algorithm_t kx; |
1104 } | 1134 } |
1105 | 1135 |
1106 /* TLS handshake a connection; no need to have called start_clear before. Reception is active if handhsake is successful */ | 1136 /* TLS handshake a connection; no need to have called start_clear before. Reception is active if handhsake is successful */ |
1107 int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds) | 1137 int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds) |
1108 { | 1138 { |
1109 TRACE_ENTRY( "%p %d", conn, mode); | 1139 TRACE_ENTRY( "%p %d %p %p", conn, mode, priority, alt_creds); |
1110 CHECK_PARAMS( conn && (!conn->cc_tls) && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) && (!conn->cc_loop) ); | 1140 CHECK_PARAMS( conn && (!(conn->cc_status & CC_STATUS_TLS)) && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) && (!conn->cc_loop) ); |
1111 | 1141 |
1112 /* Save the mode */ | 1142 /* Save the mode */ |
1113 conn->cc_tls_para.mode = mode; | 1143 conn->cc_tls_para.mode = mode; |
1114 | 1144 |
1115 /* Cancel receiving thread if any -- it should already be terminated anyway, we just release the resources */ | 1145 /* Cancel receiving thread if any -- it should already be terminated anyway, we just release the resources */ |
1137 /* Set the push and pull callbacks */ | 1167 /* Set the push and pull callbacks */ |
1138 gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv); | 1168 gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv); |
1139 gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send); | 1169 gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send); |
1140 } | 1170 } |
1141 | 1171 |
1172 /* Mark the connection as protected from here, so that the gnutls credentials will be freed */ | |
1173 conn->cc_status |= CC_STATUS_TLS; | |
1174 | |
1142 /* Handshake master session */ | 1175 /* Handshake master session */ |
1143 { | 1176 { |
1144 int ret; | 1177 int ret; |
1145 CHECK_GNUTLS_DO( ret = gnutls_handshake(conn->cc_tls_para.session), | 1178 CHECK_GNUTLS_DO( ret = gnutls_handshake(conn->cc_tls_para.session), |
1146 { | 1179 { |
1147 if (TRACE_BOOL(INFO)) { | 1180 if (TRACE_BOOL(INFO)) { |
1148 fd_log_debug("TLS Handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret)); | 1181 fd_log_debug("TLS Handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret)); |
1149 } | 1182 } |
1150 conn->cc_goterror = 1; | 1183 fd_cnx_markerror(conn); |
1151 return EINVAL; | 1184 return EINVAL; |
1152 } ); | 1185 } ); |
1153 | 1186 |
1154 /* Now verify the remote credentials are valid -- only simple tests here */ | 1187 /* Now verify the remote credentials are valid -- only simple tests here */ |
1155 CHECK_FCT_DO( fd_tls_verify_credentials(conn->cc_tls_para.session, conn, 1), | 1188 CHECK_FCT_DO( fd_tls_verify_credentials(conn->cc_tls_para.session, conn, 1), |
1156 { | 1189 { |
1157 CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_RDWR), /* Continue */ ); | 1190 CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_RDWR), ); |
1158 gnutls_deinit(conn->cc_tls_para.session); | 1191 fd_cnx_markerror(conn); |
1159 return EINVAL; | 1192 return EINVAL; |
1160 }); | 1193 }); |
1161 } | 1194 } |
1162 | |
1163 /* Mark the connection as protected from here */ | |
1164 conn->cc_tls = 1; | |
1165 | 1195 |
1166 /* Multi-stream TLS: handshake other streams as well */ | 1196 /* Multi-stream TLS: handshake other streams as well */ |
1167 if (conn->cc_sctp_para.pairs > 1) { | 1197 if (conn->cc_sctp_para.pairs > 1) { |
1168 #ifndef DISABLE_SCTP | 1198 #ifndef DISABLE_SCTP |
1169 /* Resume all additional sessions from the master one. */ | 1199 /* Resume all additional sessions from the master one. */ |
1182 | 1212 |
1183 /* Retrieve TLS credentials of the remote peer, after handshake */ | 1213 /* Retrieve TLS credentials of the remote peer, after handshake */ |
1184 int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size) | 1214 int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size) |
1185 { | 1215 { |
1186 TRACE_ENTRY("%p %p %p", conn, cert_list, cert_list_size); | 1216 TRACE_ENTRY("%p %p %p", conn, cert_list, cert_list_size); |
1187 CHECK_PARAMS( conn && (conn->cc_tls) && cert_list && cert_list_size ); | 1217 CHECK_PARAMS( conn && (conn->cc_status & CC_STATUS_TLS) && cert_list && cert_list_size ); |
1188 | 1218 |
1189 /* This function only works for X.509 certificates. */ | 1219 /* This function only works for X.509 certificates. */ |
1190 CHECK_PARAMS( gnutls_certificate_type_get (conn->cc_tls_para.session) == GNUTLS_CRT_X509 ); | 1220 CHECK_PARAMS( gnutls_certificate_type_get (conn->cc_tls_para.session) == GNUTLS_CRT_X509 ); |
1191 | 1221 |
1192 *cert_list = gnutls_certificate_get_peers (conn->cc_tls_para.session, cert_list_size); | 1222 *cert_list = gnutls_certificate_get_peers (conn->cc_tls_para.session, cert_list_size); |
1199 | 1229 |
1200 return 0; | 1230 return 0; |
1201 } | 1231 } |
1202 | 1232 |
1203 /* Receive next message. if timeout is not NULL, wait only until timeout. This function only pulls from a queue, mgr thread is filling that queue aynchrounously. */ | 1233 /* Receive next message. if timeout is not NULL, wait only until timeout. This function only pulls from a queue, mgr thread is filling that queue aynchrounously. */ |
1234 /* if the altfifo has been set on this conn object, this function must not be called */ | |
1204 int fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len) | 1235 int fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len) |
1205 { | 1236 { |
1206 int ev; | 1237 int ev; |
1207 size_t ev_sz; | 1238 size_t ev_sz; |
1208 void * ev_data; | 1239 void * ev_data; |
1261 { | 1292 { |
1262 ssize_t ret; | 1293 ssize_t ret; |
1263 size_t sent = 0; | 1294 size_t sent = 0; |
1264 TRACE_ENTRY("%p %p %zd", conn, buf, len); | 1295 TRACE_ENTRY("%p %p %zd", conn, buf, len); |
1265 do { | 1296 do { |
1266 if (conn->cc_tls) { | 1297 if (conn->cc_status & CC_STATUS_TLS) { |
1267 CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_tls_para.session, buf + sent, len - sent), return ENOTCONN ); | 1298 CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_tls_para.session, buf + sent, len - sent), ); |
1268 } else { | 1299 } else { |
1269 CHECK_SYS( ret = fd_cnx_s_send(conn, buf + sent, len - sent) ); /* better to replace with sendmsg for atomic sending? */ | 1300 /* Maybe better to replace this call with sendmsg for atomic sending? */ |
1270 } | 1301 CHECK_SYS_DO( ret = fd_cnx_s_send(conn, buf + sent, len - sent), ); |
1302 } | |
1303 if (ret <= 0) | |
1304 return ENOTCONN; | |
1305 | |
1271 sent += ret; | 1306 sent += ret; |
1272 } while ( sent < len ); | 1307 } while ( sent < len ); |
1273 return 0; | 1308 return 0; |
1274 } | 1309 } |
1275 | 1310 |
1276 /* Send a message -- this is synchronous -- and we assume it's never called by several threads at the same time, so we don't protect. */ | 1311 /* Send a message -- this is synchronous -- and we assume it's never called by several threads at the same time, so we don't protect. */ |
1277 int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len, int ordered) | 1312 int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len, int ordered) |
1278 { | 1313 { |
1279 TRACE_ENTRY("%p %p %zd %i", conn, buf, len, ordered); | 1314 TRACE_ENTRY("%p %p %zd %i", conn, buf, len, ordered); |
1280 | 1315 |
1281 CHECK_PARAMS(conn && (conn->cc_socket > 0) && (! conn->cc_goterror) && buf && len); | 1316 CHECK_PARAMS(conn && (conn->cc_socket > 0) && (! (conn->cc_status & CC_STATUS_ERROR)) && buf && len); |
1282 | 1317 |
1283 TRACE_DEBUG(FULL, "Sending %zdb %sdata on connection %s", len, conn->cc_tls ? "TLS-protected ":"", conn->cc_id); | 1318 TRACE_DEBUG(FULL, "Sending %zdb %sdata on connection %s", len, (conn->cc_status & CC_STATUS_TLS) ? "TLS-protected ":"", conn->cc_id); |
1284 | 1319 |
1285 switch (conn->cc_proto) { | 1320 switch (conn->cc_proto) { |
1286 case IPPROTO_TCP: | 1321 case IPPROTO_TCP: |
1287 CHECK_FCT( send_simple(conn, buf, len) ); | 1322 CHECK_FCT( send_simple(conn, buf, len) ); |
1288 break; | 1323 break; |
1289 | 1324 |
1290 #ifndef DISABLE_SCTP | 1325 #ifndef DISABLE_SCTP |
1291 case IPPROTO_SCTP: { | 1326 case IPPROTO_SCTP: { |
1292 int multistr = 0; | 1327 int multistr = 0; |
1293 | 1328 |
1294 if ((!ordered) && (conn->cc_sctp_para.str_out > 1) && ((! conn->cc_tls) || (conn->cc_sctp_para.pairs > 1))) { | 1329 if ((!ordered) && (conn->cc_sctp_para.str_out > 1) && ((! (conn->cc_status & CC_STATUS_TLS)) || (conn->cc_sctp_para.pairs > 1))) { |
1295 /* Update the id of the stream we will send this message on */ | 1330 /* Update the id of the stream we will send this message on */ |
1296 conn->cc_sctp_para.next += 1; | 1331 conn->cc_sctp_para.next += 1; |
1297 conn->cc_sctp_para.next %= (conn->cc_tls ? conn->cc_sctp_para.pairs : conn->cc_sctp_para.str_out); | 1332 conn->cc_sctp_para.next %= ((conn->cc_status & CC_STATUS_TLS) ? conn->cc_sctp_para.pairs : conn->cc_sctp_para.str_out); |
1298 multistr = 1; | 1333 multistr = 1; |
1299 } | 1334 } |
1300 | 1335 |
1301 if ((!multistr) || (conn->cc_sctp_para.next == 0)) { | 1336 if ((!multistr) || (conn->cc_sctp_para.next == 0)) { |
1302 CHECK_FCT( send_simple(conn, buf, len) ); | 1337 CHECK_FCT( send_simple(conn, buf, len) ); |
1303 } else { | 1338 } else { |
1304 if (!conn->cc_tls) { | 1339 if (!(conn->cc_status & CC_STATUS_TLS)) { |
1305 CHECK_FCT_DO( fd_sctp_sendstr(conn->cc_socket, conn->cc_sctp_para.next, buf, len, &conn->cc_closing), { conn->cc_goterror = 1; return ENOTCONN; } ); | 1340 CHECK_FCT_DO( fd_sctp_sendstr(conn->cc_socket, conn->cc_sctp_para.next, buf, len, &conn->cc_status), { fd_cnx_markerror(conn); return ENOTCONN; } ); |
1306 } else { | 1341 } else { |
1307 /* push the record to the appropriate session */ | 1342 /* push the record to the appropriate session */ |
1308 ssize_t ret; | 1343 ssize_t ret; |
1309 size_t sent = 0; | 1344 size_t sent = 0; |
1310 ASSERT(conn->cc_sctps_data.array != NULL); | 1345 ASSERT(conn->cc_sctps_data.array != NULL); |
1311 do { | 1346 do { |
1312 CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctps_data.array[conn->cc_sctp_para.next].session, buf + sent, len - sent), return ENOTCONN ); | 1347 CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctps_data.array[conn->cc_sctp_para.next].session, buf + sent, len - sent), ); |
1348 if (ret <= 0) | |
1349 return ENOTCONN; | |
1350 | |
1313 sent += ret; | 1351 sent += ret; |
1314 } while ( sent < len ); | 1352 } while ( sent < len ); |
1315 } | 1353 } |
1316 } | 1354 } |
1317 } | 1355 } |
1318 break; | 1356 break; |
1319 #endif /* DISABLE_SCTP */ | 1357 #endif /* DISABLE_SCTP */ |
1320 | 1358 |
1321 default: | 1359 default: |
1322 TRACE_DEBUG(INFO, "Unknwon protocol: %d", conn->cc_proto); | 1360 TRACE_DEBUG(INFO, "Unknwon protocol: %d", conn->cc_proto); |
1361 ASSERT(0); | |
1323 return ENOTSUP; /* or EINVAL... */ | 1362 return ENOTSUP; /* or EINVAL... */ |
1324 } | 1363 } |
1325 | 1364 |
1326 return 0; | 1365 return 0; |
1327 } | 1366 } |
1336 { | 1375 { |
1337 TRACE_ENTRY("%p", conn); | 1376 TRACE_ENTRY("%p", conn); |
1338 | 1377 |
1339 CHECK_PARAMS_DO(conn, return); | 1378 CHECK_PARAMS_DO(conn, return); |
1340 | 1379 |
1341 conn->cc_closing = 1; | 1380 conn->cc_status |= CC_STATUS_CLOSING; |
1342 | 1381 |
1343 /* Initiate shutdown of the TLS session(s): call gnutls_bye(WR), then read until error */ | 1382 /* Initiate shutdown of the TLS session(s): call gnutls_bye(WR), then read until error */ |
1344 if (conn->cc_tls) { | 1383 if (conn->cc_status & CC_STATUS_TLS) { |
1345 #ifndef DISABLE_SCTP | 1384 #ifndef DISABLE_SCTP |
1346 if (conn->cc_sctp_para.pairs > 1) { | 1385 if (conn->cc_sctp_para.pairs > 1) { |
1347 if (! conn->cc_goterror ) { | 1386 if (! (conn->cc_status & CC_STATUS_ERROR )) { |
1348 /* Bye on master session */ | 1387 /* Bye on master session */ |
1349 CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), /* Continue */ ); | 1388 CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) ); |
1350 | 1389 } |
1390 | |
1391 if (! (conn->cc_status & CC_STATUS_ERROR ) ) { | |
1351 /* and other stream pairs */ | 1392 /* and other stream pairs */ |
1352 fd_sctps_bye(conn); | 1393 fd_sctps_bye(conn); |
1353 | 1394 } |
1395 | |
1396 if (! (conn->cc_status & CC_STATUS_ERROR ) ) { | |
1354 /* Now wait for all decipher threads to terminate */ | 1397 /* Now wait for all decipher threads to terminate */ |
1355 fd_sctps_waitthreadsterm(conn); | 1398 fd_sctps_waitthreadsterm(conn); |
1356 } else { | 1399 } else { |
1357 /* Abord the threads, the connection is dead already */ | 1400 /* Abord the threads, the connection is dead already */ |
1358 fd_sctps_stopthreads(conn); | 1401 fd_sctps_stopthreads(conn); |
1359 } | 1402 } |
1360 | 1403 |
1361 /* Deinit gnutls resources */ | 1404 /* Deinit gnutls resources */ |
1362 fd_sctps_gnutls_deinit_others(conn); | 1405 fd_sctps_gnutls_deinit_others(conn); |
1363 gnutls_deinit(conn->cc_tls_para.session); | 1406 if (conn->cc_tls_para.session) { |
1407 gnutls_deinit(conn->cc_tls_para.session); | |
1408 conn->cc_tls_para.session = NULL; | |
1409 } | |
1364 | 1410 |
1365 /* Destroy the wrapper (also stops the demux thread) */ | 1411 /* Destroy the wrapper (also stops the demux thread) */ |
1366 fd_sctps_destroy(conn); | 1412 fd_sctps_destroy(conn); |
1367 | 1413 |
1368 } else { | 1414 } else { |
1369 #endif /* DISABLE_SCTP */ | 1415 #endif /* DISABLE_SCTP */ |
1370 /* We are not using the sctps wrapper layer */ | 1416 /* We are not using the sctps wrapper layer */ |
1371 if (! conn->cc_goterror ) { | 1417 if (! (conn->cc_status & CC_STATUS_ERROR ) ) { |
1372 /* Master session */ | 1418 /* Master session */ |
1373 CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), /* Continue */ ); | 1419 CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) ); |
1374 | 1420 } |
1421 | |
1422 if (! (conn->cc_status & CC_STATUS_ERROR ) ) { | |
1375 /* In this case, just wait for thread rcvthr_tls_single to terminate */ | 1423 /* In this case, just wait for thread rcvthr_tls_single to terminate */ |
1376 if (conn->cc_rcvthr != (pthread_t)NULL) { | 1424 if (conn->cc_rcvthr != (pthread_t)NULL) { |
1377 CHECK_POSIX_DO( pthread_join(conn->cc_rcvthr, NULL), /* continue */ ); | 1425 CHECK_POSIX_DO( pthread_join(conn->cc_rcvthr, NULL), /* continue */ ); |
1378 conn->cc_rcvthr = (pthread_t)NULL; | 1426 conn->cc_rcvthr = (pthread_t)NULL; |
1379 } | 1427 } |
1381 /* Cancel the receiver thread in case it did not already terminate */ | 1429 /* Cancel the receiver thread in case it did not already terminate */ |
1382 CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ ); | 1430 CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ ); |
1383 } | 1431 } |
1384 | 1432 |
1385 /* Free the resources of the TLS session */ | 1433 /* Free the resources of the TLS session */ |
1386 gnutls_deinit(conn->cc_tls_para.session); | 1434 if (conn->cc_tls_para.session) { |
1435 gnutls_deinit(conn->cc_tls_para.session); | |
1436 conn->cc_tls_para.session = NULL; | |
1437 } | |
1387 | 1438 |
1388 #ifndef DISABLE_SCTP | 1439 #ifndef DISABLE_SCTP |
1389 } | 1440 } |
1390 #endif /* DISABLE_SCTP */ | 1441 #endif /* DISABLE_SCTP */ |
1391 } | 1442 } |