Mercurial > hg > freeDiameter-dtls
comparison libfdcore/cnxctx.c @ 1214:76ac4bb75f0e
Merged with latest proposed version
author | Sebastien Decugis <sdecugis@freediameter.net> |
---|---|
date | Mon, 17 Jun 2013 10:11:57 +0800 |
parents | e1ced4db7f67 98478a8aabb1 |
children | 1e8267ad057c |
comparison
equal
deleted
inserted
replaced
1188:e1ced4db7f67 | 1214:76ac4bb75f0e |
---|---|
290 TRACE_ENTRY("%p %d", sa, addrlen); | 290 TRACE_ENTRY("%p %d", sa, addrlen); |
291 CHECK_PARAMS_DO( sa && addrlen, return NULL ); | 291 CHECK_PARAMS_DO( sa && addrlen, return NULL ); |
292 | 292 |
293 fd_sa_sdump_numeric(sa_buf, sa); | 293 fd_sa_sdump_numeric(sa_buf, sa); |
294 | 294 |
295 LOG_D("Connecting to TCP %s...", sa_buf); | |
296 | |
295 /* Create the socket and connect, which can take some time and/or fail */ | 297 /* Create the socket and connect, which can take some time and/or fail */ |
296 { | 298 { |
297 int ret = fd_tcp_client( &sock, sa, addrlen ); | 299 int ret = fd_tcp_client( &sock, sa, addrlen ); |
298 if (ret != 0) { | 300 if (ret != 0) { |
299 LOG_A("TCP connection to %s failed: %s", sa_buf, strerror(ret)); | 301 LOG_D("TCP connection to %s failed: %s", sa_buf, strerror(ret)); |
300 return NULL; | 302 return NULL; |
301 } | 303 } |
302 } | 304 } |
303 | 305 |
304 /* Once the socket is created successfuly, prepare the remaining of the cnx */ | 306 /* Once the socket is created successfuly, prepare the remaining of the cnx */ |
345 TRACE_ENTRY("%p", list); | 347 TRACE_ENTRY("%p", list); |
346 CHECK_PARAMS_DO( list && !FD_IS_LIST_EMPTY(list), return NULL ); | 348 CHECK_PARAMS_DO( list && !FD_IS_LIST_EMPTY(list), return NULL ); |
347 | 349 |
348 fd_sa_sdump_numeric(sa_buf, &((struct fd_endpoint *)(list->next))->sa); | 350 fd_sa_sdump_numeric(sa_buf, &((struct fd_endpoint *)(list->next))->sa); |
349 | 351 |
352 LOG_D("Connecting to SCTP %s:%hu...", sa_buf, port); | |
353 | |
350 { | 354 { |
351 int ret = fd_sctp_client( &sock, no_ip6, port, list ); | 355 int ret = fd_sctp_client( &sock, no_ip6, port, list ); |
352 if (ret != 0) { | 356 if (ret != 0) { |
353 LOG_A("SCTP connection to [%s,...] failed: %s", sa_buf, strerror(ret)); | 357 LOG_D("SCTP connection to [%s,...] failed: %s", sa_buf, strerror(ret)); |
354 return NULL; | 358 return NULL; |
355 } | 359 } |
356 } | 360 } |
357 | 361 |
358 /* Once the socket is created successfuly, prepare the remaining of the cnx */ | 362 /* Once the socket is created successfuly, prepare the remaining of the cnx */ |
608 | 612 |
609 return; | 613 return; |
610 fatal: | 614 fatal: |
611 /* An unrecoverable error occurred, stop the daemon */ | 615 /* An unrecoverable error occurred, stop the daemon */ |
612 ASSERT(0); | 616 ASSERT(0); |
613 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); | 617 CHECK_FCT_DO(fd_core_shutdown(), ); |
614 } | 618 } |
615 | 619 |
616 /* Set the timeout option on the socket */ | 620 /* Set the timeout option on the socket */ |
617 void fd_cnx_s_setto(int sock) | 621 void fd_cnx_s_setto(int sock) |
618 { | 622 { |
619 struct timeval tv; | 623 struct timeval tv; |
620 | 624 |
621 /* Set a timeout on the socket so that in any case we are not stuck waiting for something */ | 625 /* Set a timeout on the socket so that in any case we are not stuck waiting for something */ |
622 memset(&tv, 0, sizeof(tv)); | 626 memset(&tv, 0, sizeof(tv)); |
623 tv.tv_sec = 3; /* allow 3 seconds timeout for TLS session cleanup */ | 627 tv.tv_usec = 100000L; /* 100ms, to react quickly to head-of-the-line blocking. */ |
624 CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), /* best effort only */ ); | 628 CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), ); |
625 CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), /* Also timeout for sending, to avoid waiting forever */ ); | 629 CHECK_SYS_DO( setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), ); |
626 } | 630 } |
627 | 631 |
628 | 632 |
629 #ifdef GNUTLS_VERSION_300 | 633 #ifdef GNUTLS_VERSION_300 |
630 /* The pull_timeout function for gnutls */ | 634 /* The pull_timeout function for gnutls */ |
668 } | 672 } |
669 | 673 |
670 return ret; | 674 return ret; |
671 } | 675 } |
672 | 676 |
673 /* Send, for older GNUTLS */ | |
674 #ifndef GNUTLS_VERSION_212 | |
675 static ssize_t fd_cnx_s_send(struct cnxctx * conn, const void *buffer, size_t length) | |
676 { | |
677 ssize_t ret = 0; | |
678 int timedout = 0; | |
679 again: | |
680 ret = send(conn->cc_socket, buffer, length, 0); | |
681 /* Handle special case of timeout */ | |
682 if ((ret < 0) && ((errno == EAGAIN) || (errno == EINTR))) { | |
683 pthread_testcancel(); | |
684 if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING )) | |
685 goto again; /* don't care, just ignore */ | |
686 if (!timedout) { | |
687 timedout ++; /* allow for one timeout while closing */ | |
688 goto again; | |
689 } | |
690 CHECK_SYS_DO(ret, /* continue */); | |
691 } | |
692 | |
693 /* Mark the error */ | |
694 if (ret <= 0) | |
695 fd_cnx_markerror(conn); | |
696 | |
697 return ret; | |
698 } | |
699 #endif /* GNUTLS_VERSION_212 */ | |
700 | |
701 /* Send */ | 677 /* Send */ |
702 static ssize_t fd_cnx_s_sendv(struct cnxctx * conn, const struct iovec * iov, int iovcnt) | 678 static ssize_t fd_cnx_s_sendv(struct cnxctx * conn, const struct iovec * iov, int iovcnt) |
703 { | 679 { |
704 ssize_t ret = 0; | 680 ssize_t ret = 0; |
705 int timedout = 0; | 681 struct timespec ts, now; |
682 CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), return -1 ); | |
706 again: | 683 again: |
707 ret = writev(conn->cc_socket, iov, iovcnt); | 684 ret = writev(conn->cc_socket, iov, iovcnt); |
708 /* Handle special case of timeout */ | 685 /* Handle special case of timeout */ |
709 if ((ret < 0) && ((errno == EAGAIN) || (errno == EINTR))) { | 686 if ((ret < 0) && ((errno == EAGAIN) || (errno == EINTR))) { |
687 ret = -errno; | |
710 pthread_testcancel(); | 688 pthread_testcancel(); |
711 if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING )) | 689 |
690 /* Check how much time we were blocked for this sending. */ | |
691 CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), return -1 ); | |
692 if ( ((now.tv_sec - ts.tv_sec) * 1000 + ((now.tv_nsec - ts.tv_nsec) / 1000000L)) > MAX_HOTL_BLOCKING_TIME) { | |
693 LOG_D("Unable to send any data for %dms, closing the connection", MAX_HOTL_BLOCKING_TIME); | |
694 } else if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING )) { | |
712 goto again; /* don't care, just ignore */ | 695 goto again; /* don't care, just ignore */ |
713 if (!timedout) { | 696 } |
714 timedout ++; /* allow for one timeout while closing */ | 697 |
715 goto again; | 698 /* propagate the error */ |
716 } | 699 errno = -ret; |
700 ret = -1; | |
717 CHECK_SYS_DO(ret, /* continue */); | 701 CHECK_SYS_DO(ret, /* continue */); |
718 } | 702 } |
719 | 703 |
720 /* Mark the error */ | 704 /* Mark the error */ |
721 if (ret <= 0) | 705 if (ret <= 0) |
722 fd_cnx_markerror(conn); | 706 fd_cnx_markerror(conn); |
723 | 707 |
724 return ret; | 708 return ret; |
725 } | 709 } |
710 | |
711 /* Send, for older GNUTLS */ | |
712 #ifndef GNUTLS_VERSION_212 | |
713 static ssize_t fd_cnx_s_send(struct cnxctx * conn, const void *buffer, size_t length) | |
714 { | |
715 struct iovec iov; | |
716 iov.iov_base = (void *)buffer; | |
717 iov.iov_len = length; | |
718 return fd_cnx_s_sendv(conn, &iov, 1); | |
719 } | |
720 #endif /* GNUTLS_VERSION_212 */ | |
726 | 721 |
727 #define ALIGNOF(t) ((char *)(&((struct { char c; t _h; } *)0)->_h) - (char *)0) /* Could use __alignof__(t) on some systems but this is more portable probably */ | 722 #define ALIGNOF(t) ((char *)(&((struct { char c; t _h; } *)0)->_h) - (char *)0) /* Could use __alignof__(t) on some systems but this is more portable probably */ |
728 #define PMDL_PADDED(len) ( ((len) + ALIGNOF(struct fd_msg_pmdl) - 1) & ~(ALIGNOF(struct fd_msg_pmdl) - 1) ) | 723 #define PMDL_PADDED(len) ( ((len) + ALIGNOF(struct fd_msg_pmdl) - 1) & ~(ALIGNOF(struct fd_msg_pmdl) - 1) ) |
729 | 724 |
730 size_t fd_msg_pmdl_sizewithoverhead(size_t datalen) | 725 size_t fd_msg_pmdl_sizewithoverhead(size_t datalen) |
805 if (ret <= 0) { | 800 if (ret <= 0) { |
806 goto out; /* Stop the thread, the event was already sent */ | 801 goto out; /* Stop the thread, the event was already sent */ |
807 } | 802 } |
808 | 803 |
809 received += ret; | 804 received += ret; |
805 | |
806 if (header[0] != DIAMETER_VERSION) | |
807 break; /* No need to wait for 4 bytes in this case */ | |
810 } while (received < sizeof(header)); | 808 } while (received < sizeof(header)); |
811 | 809 |
812 rcv_data.length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3]; | 810 rcv_data.length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3]; |
813 | 811 |
814 /* Check the received word is a valid begining of a Diameter message */ | 812 /* Check the received word is a valid begining of a Diameter message */ |
840 | 838 |
841 /* We have received a complete message, pass it to the daemon */ | 839 /* We have received a complete message, pass it to the daemon */ |
842 CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, rcv_data.length, rcv_data.buffer), | 840 CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, rcv_data.length, rcv_data.buffer), |
843 { | 841 { |
844 free_rcvdata(&rcv_data); | 842 free_rcvdata(&rcv_data); |
845 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); | 843 goto fatal; |
846 return NULL; | |
847 } ); | 844 } ); |
848 | 845 |
849 } while (conn->cc_loop); | 846 } while (conn->cc_loop); |
850 | 847 |
851 out: | 848 out: |
852 TRACE_DEBUG(FULL, "Thread terminated"); | 849 TRACE_DEBUG(FULL, "Thread terminated"); |
853 return NULL; | 850 return NULL; |
854 | 851 |
855 fatal: | 852 fatal: |
856 /* An unrecoverable error occurred, stop the daemon */ | 853 /* An unrecoverable error occurred, stop the daemon */ |
857 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); | 854 CHECK_FCT_DO(fd_core_shutdown(), ); |
858 goto out; | 855 goto out; |
859 } | 856 } |
860 | 857 |
861 #ifndef DISABLE_SCTP | 858 #ifndef DISABLE_SCTP |
862 /* Receiver thread (SCTP & noTLS) : incoming message is directly saved into cc_incoming, no need to care for the stream ID */ | 859 /* Receiver thread (SCTP & noTLS) : incoming message is directly saved into cc_incoming, no need to care for the stream ID */ |
905 TRACE_DEBUG(FULL, "Thread terminated"); | 902 TRACE_DEBUG(FULL, "Thread terminated"); |
906 return NULL; | 903 return NULL; |
907 | 904 |
908 fatal: | 905 fatal: |
909 /* An unrecoverable error occurred, stop the daemon */ | 906 /* An unrecoverable error occurred, stop the daemon */ |
910 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); | 907 CHECK_FCT_DO(fd_core_shutdown(), ); |
911 goto out; | 908 goto out; |
912 } | 909 } |
913 #endif /* DISABLE_SCTP */ | 910 #endif /* DISABLE_SCTP */ |
914 | 911 |
915 /* Start receving messages in clear (no TLS) on the connection */ | 912 /* Start receving messages in clear (no TLS) on the connection */ |
999 | 996 |
1000 /* Wrapper around gnutls_record_send to handle some error codes. This is also used for DTLS-protected associations */ | 997 /* Wrapper around gnutls_record_send to handle some error codes. This is also used for DTLS-protected associations */ |
1001 static ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz) | 998 static ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz) |
1002 { | 999 { |
1003 ssize_t ret; | 1000 ssize_t ret; |
1001 struct timespec ts, now; | |
1002 CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), return -1 ); | |
1004 again: | 1003 again: |
1005 CHECK_GNUTLS_DO( ret = gnutls_record_send(session, data, sz), | 1004 CHECK_GNUTLS_DO( ret = gnutls_record_send(session, data, sz), |
1006 { | 1005 { |
1006 pthread_testcancel(); | |
1007 switch (ret) { | 1007 switch (ret) { |
1008 case GNUTLS_E_REHANDSHAKE: | 1008 case GNUTLS_E_REHANDSHAKE: |
1009 if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) { | 1009 if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) { |
1010 CHECK_GNUTLS_DO( ret = gnutls_handshake(session), | 1010 CHECK_GNUTLS_DO( ret = gnutls_handshake(session), |
1011 { | 1011 { |
1016 } ); | 1016 } ); |
1017 } | 1017 } |
1018 | 1018 |
1019 case GNUTLS_E_AGAIN: | 1019 case GNUTLS_E_AGAIN: |
1020 case GNUTLS_E_INTERRUPTED: | 1020 case GNUTLS_E_INTERRUPTED: |
1021 if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) | 1021 CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), return -1 ); |
1022 if ( ((now.tv_sec - ts.tv_sec) * 1000 + ((now.tv_nsec - ts.tv_nsec) / 1000000L)) > MAX_HOTL_BLOCKING_TIME) { | |
1023 LOG_D("Unable to send any data for %dms, closing the connection", MAX_HOTL_BLOCKING_TIME); | |
1024 } else if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING )) { | |
1022 goto again; | 1025 goto again; |
1023 TRACE_DEBUG(INFO, "Connection is closing, so abord gnutls_record_send now."); | 1026 } |
1024 break; | 1027 break; |
1025 | 1028 |
1026 default: | 1029 default: |
1027 if (gnutls_error_is_fatal (ret) == 0) { | 1030 if (gnutls_error_is_fatal (ret) == 0) { |
1028 LOG_N("Ignoring non-fatal GNU TLS error: %s", gnutls_strerror (ret)); | 1031 LOG_N("Ignoring non-fatal GNU TLS error: %s", gnutls_strerror (ret)); |
1096 | 1099 |
1097 /* We have received a complete message, pass it to the daemon */ | 1100 /* We have received a complete message, pass it to the daemon */ |
1098 CHECK_FCT_DO( ret = fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, rcv_data.length, rcv_data.buffer), | 1101 CHECK_FCT_DO( ret = fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, rcv_data.length, rcv_data.buffer), |
1099 { | 1102 { |
1100 free_rcvdata(&rcv_data); | 1103 free_rcvdata(&rcv_data); |
1101 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); | 1104 CHECK_FCT_DO(fd_core_shutdown(), ); |
1102 return ret; | 1105 return ret; |
1103 } ); | 1106 } ); |
1104 | 1107 |
1105 } while (1); | 1108 } while (1); |
1106 | 1109 |
1631 return 1; | 1634 return 1; |
1632 #endif /* DISABLE_SCTP */ | 1635 #endif /* DISABLE_SCTP */ |
1633 return 0; | 1636 return 0; |
1634 } | 1637 } |
1635 | 1638 |
1639 #ifndef DISABLE_SCTP | |
1636 static int fd_cnx_uses_dtls(struct cnxctx * conn) { | 1640 static int fd_cnx_uses_dtls(struct cnxctx * conn) { |
1637 return fd_cnx_may_dtls(conn) && (fd_cnx_teststate(conn, CC_STATUS_TLS)); | 1641 return fd_cnx_may_dtls(conn) && (fd_cnx_teststate(conn, CC_STATUS_TLS)); |
1638 } | 1642 } |
1643 #endif /* DISABLE_SCTP */ | |
1639 | 1644 |
1640 /* TLS handshake a connection; no need to have called start_clear before. Reception is active if handhsake is successful */ | 1645 /* TLS handshake a connection; no need to have called start_clear before. Reception is active if handhsake is successful */ |
1641 int fd_cnx_handshake(struct cnxctx * conn, int mode, int algo, char * priority, void * alt_creds) | 1646 int fd_cnx_handshake(struct cnxctx * conn, int mode, int algo, char * priority, void * alt_creds) |
1642 { | 1647 { |
1643 int dtls = 0; | 1648 int dtls = 0; |