diff options
| -rw-r--r-- | src/idevice.c | 106 | ||||
| -rw-r--r-- | src/idevice.h | 2 |
2 files changed, 66 insertions, 42 deletions
diff --git a/src/idevice.c b/src/idevice.c index ecc0418..a2a4d0b 100644 --- a/src/idevice.c +++ b/src/idevice.c | |||
| @@ -432,6 +432,8 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connect(idevice_t device, uint16_t | |||
| 432 | new_connection->data = (void*)(long)sfd; | 432 | new_connection->data = (void*)(long)sfd; |
| 433 | new_connection->ssl_data = NULL; | 433 | new_connection->ssl_data = NULL; |
| 434 | new_connection->device = device; | 434 | new_connection->device = device; |
| 435 | new_connection->ssl_recv_timeout = (unsigned int)-1; | ||
| 436 | new_connection->status = IDEVICE_E_SUCCESS; | ||
| 435 | *connection = new_connection; | 437 | *connection = new_connection; |
| 436 | return IDEVICE_E_SUCCESS; | 438 | return IDEVICE_E_SUCCESS; |
| 437 | } else if (device->conn_type == CONNECTION_NETWORK) { | 439 | } else if (device->conn_type == CONNECTION_NETWORK) { |
| @@ -478,6 +480,7 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connect(idevice_t device, uint16_t | |||
| 478 | new_connection->data = (void*)(long)sfd; | 480 | new_connection->data = (void*)(long)sfd; |
| 479 | new_connection->ssl_data = NULL; | 481 | new_connection->ssl_data = NULL; |
| 480 | new_connection->device = device; | 482 | new_connection->device = device; |
| 483 | new_connection->ssl_recv_timeout = (unsigned int)-1; | ||
| 481 | 484 | ||
| 482 | *connection = new_connection; | 485 | *connection = new_connection; |
| 483 | 486 | ||
| @@ -558,15 +561,10 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_send(idevice_connection_ | |||
| 558 | } | 561 | } |
| 559 | 562 | ||
| 560 | if (connection->ssl_data) { | 563 | if (connection->ssl_data) { |
| 564 | connection->status = IDEVICE_E_SUCCESS; | ||
| 561 | uint32_t sent = 0; | 565 | uint32_t sent = 0; |
| 562 | while (sent < len) { | 566 | while (sent < len) { |
| 563 | #ifdef HAVE_OPENSSL | 567 | #ifdef HAVE_OPENSSL |
| 564 | int c = socket_check_fd((int)(long)connection->data, FDM_WRITE, 100); | ||
| 565 | if (c == 0 || c == -ETIMEDOUT || c == -EAGAIN) { | ||
| 566 | continue; | ||
| 567 | } else if (c < 0) { | ||
| 568 | break; | ||
| 569 | } | ||
| 570 | int s = SSL_write(connection->ssl_data->session, (const void*)(data+sent), (int)(len-sent)); | 568 | int s = SSL_write(connection->ssl_data->session, (const void*)(data+sent), (int)(len-sent)); |
| 571 | if (s <= 0) { | 569 | if (s <= 0) { |
| 572 | int sslerr = SSL_get_error(connection->ssl_data->session, s); | 570 | int sslerr = SSL_get_error(connection->ssl_data->session, s); |
| @@ -586,7 +584,7 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_send(idevice_connection_ | |||
| 586 | debug_info("SSL_write %d, sent %d", len, sent); | 584 | debug_info("SSL_write %d, sent %d", len, sent); |
| 587 | if (sent < len) { | 585 | if (sent < len) { |
| 588 | *sent_bytes = 0; | 586 | *sent_bytes = 0; |
| 589 | return IDEVICE_E_SSL_ERROR; | 587 | return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status; |
| 590 | } | 588 | } |
| 591 | *sent_bytes = sent; | 589 | *sent_bytes = sent; |
| 592 | return IDEVICE_E_SUCCESS; | 590 | return IDEVICE_E_SUCCESS; |
| @@ -670,30 +668,17 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_receive_timeout(idevice_ | |||
| 670 | 668 | ||
| 671 | if (connection->ssl_data) { | 669 | if (connection->ssl_data) { |
| 672 | uint32_t received = 0; | 670 | uint32_t received = 0; |
| 673 | int do_select = 1; | ||
| 674 | idevice_error_t error = IDEVICE_E_SSL_ERROR; | ||
| 675 | 671 | ||
| 672 | if (connection->ssl_recv_timeout != (unsigned int)-1) { | ||
| 673 | debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout"); | ||
| 674 | } | ||
| 675 | |||
| 676 | // this should be reset after the SSL_read call on all codepaths, as | ||
| 677 | // the supplied timeout should only apply to the current read. | ||
| 678 | connection->ssl_recv_timeout = timeout; | ||
| 679 | connection->status = IDEVICE_E_SUCCESS; | ||
| 676 | while (received < len) { | 680 | while (received < len) { |
| 677 | #ifdef HAVE_OPENSSL | 681 | #ifdef HAVE_OPENSSL |
| 678 | do_select = (SSL_pending(connection->ssl_data->session) == 0); | ||
| 679 | #endif | ||
| 680 | if (do_select) { | ||
| 681 | int conn_error = socket_check_fd((int)(long)connection->data, FDM_READ, timeout); | ||
| 682 | error = socket_recv_to_idevice_error(conn_error, len, received); | ||
| 683 | switch (error) { | ||
| 684 | case IDEVICE_E_SUCCESS: | ||
| 685 | case IDEVICE_E_TIMEOUT: | ||
| 686 | break; | ||
| 687 | case IDEVICE_E_UNKNOWN_ERROR: | ||
| 688 | default: | ||
| 689 | debug_info("ERROR: socket_check_fd returned %d (%s)", conn_error, strerror(-conn_error)); | ||
| 690 | return error; | ||
| 691 | } | ||
| 692 | } | ||
| 693 | if (error == IDEVICE_E_TIMEOUT) { | ||
| 694 | break; | ||
| 695 | } | ||
| 696 | #ifdef HAVE_OPENSSL | ||
| 697 | int r = SSL_read(connection->ssl_data->session, (void*)((char*)(data+received)), (int)len-received); | 682 | int r = SSL_read(connection->ssl_data->session, (void*)((char*)(data+received)), (int)len-received); |
| 698 | if (r > 0) { | 683 | if (r > 0) { |
| 699 | received += r; | 684 | received += r; |
| @@ -713,11 +698,12 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_receive_timeout(idevice_ | |||
| 713 | } | 698 | } |
| 714 | #endif | 699 | #endif |
| 715 | } | 700 | } |
| 701 | connection->ssl_recv_timeout = (unsigned int)-1; | ||
| 716 | 702 | ||
| 717 | debug_info("SSL_read %d, received %d", len, received); | 703 | debug_info("SSL_read %d, received %d", len, received); |
| 718 | if (received < len) { | 704 | if (received < len) { |
| 719 | *recv_bytes = received; | 705 | *recv_bytes = received; |
| 720 | return error; | 706 | return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status; |
| 721 | } | 707 | } |
| 722 | 708 | ||
| 723 | *recv_bytes = received; | 709 | *recv_bytes = received; |
| @@ -763,6 +749,10 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_receive(idevice_connecti | |||
| 763 | } | 749 | } |
| 764 | 750 | ||
| 765 | if (connection->ssl_data) { | 751 | if (connection->ssl_data) { |
| 752 | if (connection->ssl_recv_timeout != (unsigned int)-1) { | ||
| 753 | debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout"); | ||
| 754 | connection->ssl_recv_timeout = (unsigned int)-1; | ||
| 755 | } | ||
| 766 | #ifdef HAVE_OPENSSL | 756 | #ifdef HAVE_OPENSSL |
| 767 | int received = SSL_read(connection->ssl_data->session, (void*)data, (int)len); | 757 | int received = SSL_read(connection->ssl_data->session, (void*)data, (int)len); |
| 768 | debug_info("SSL_read %d, received %d", len, received); | 758 | debug_info("SSL_read %d, received %d", len, received); |
| @@ -816,28 +806,36 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_get_udid(idevice_t device, char **u | |||
| 816 | return IDEVICE_E_SUCCESS; | 806 | return IDEVICE_E_SUCCESS; |
| 817 | } | 807 | } |
| 818 | 808 | ||
| 819 | #ifndef HAVE_OPENSSL | ||
| 820 | /** | 809 | /** |
| 821 | * Internally used gnutls callback function for receiving encrypted data. | 810 | * Internally used SSL callback function for receiving encrypted data. |
| 822 | */ | 811 | */ |
| 823 | static ssize_t internal_ssl_read(gnutls_transport_ptr_t transport, char *buffer, size_t length) | 812 | static ssize_t internal_ssl_read(idevice_connection_t connection, char *buffer, size_t length) |
| 824 | { | 813 | { |
| 825 | int bytes = 0, pos_start_fill = 0; | 814 | int bytes = 0, pos_start_fill = 0; |
| 826 | size_t tbytes = 0; | 815 | size_t tbytes = 0; |
| 827 | int this_len = length; | 816 | int this_len = length; |
| 828 | idevice_error_t res; | 817 | idevice_error_t res; |
| 829 | idevice_connection_t connection = (idevice_connection_t)transport; | ||
| 830 | char *recv_buffer; | 818 | char *recv_buffer; |
| 831 | 819 | ||
| 832 | debug_info("pre-read client wants %zi bytes", length); | 820 | debug_info("pre-read client wants %zi bytes", length); |
| 833 | 821 | ||
| 834 | recv_buffer = (char *)malloc(sizeof(char) * this_len); | 822 | recv_buffer = (char *)malloc(sizeof(char) * this_len); |
| 835 | 823 | ||
| 824 | unsigned int timeout = connection->ssl_recv_timeout; | ||
| 825 | |||
| 836 | /* repeat until we have the full data or an error occurs */ | 826 | /* repeat until we have the full data or an error occurs */ |
| 837 | do { | 827 | do { |
| 838 | if ((res = internal_connection_receive(connection, recv_buffer, this_len, (uint32_t*)&bytes)) != IDEVICE_E_SUCCESS) { | 828 | if (timeout == (unsigned int)-1) { |
| 839 | debug_info("ERROR: idevice_connection_receive returned %d", res); | 829 | res = internal_connection_receive(connection, recv_buffer, this_len, (uint32_t*)&bytes); |
| 840 | return res; | 830 | } else { |
| 831 | res = internal_connection_receive_timeout(connection, recv_buffer, this_len, (uint32_t*)&bytes, (unsigned int)timeout); | ||
| 832 | } | ||
| 833 | if (res != IDEVICE_E_SUCCESS) { | ||
| 834 | if (res != IDEVICE_E_TIMEOUT) { | ||
| 835 | debug_info("ERROR: %s returned %d", (timeout == (unsigned int)-1) ? "internal_connection_receive" : "internal_connection_receive_timeout", res); | ||
| 836 | } | ||
| 837 | connection->status = res; | ||
| 838 | return -1; | ||
| 841 | } | 839 | } |
| 842 | debug_info("post-read we got %i bytes", bytes); | 840 | debug_info("post-read we got %i bytes", bytes); |
| 843 | 841 | ||
| @@ -863,22 +861,21 @@ static ssize_t internal_ssl_read(gnutls_transport_ptr_t transport, char *buffer, | |||
| 863 | } | 861 | } |
| 864 | 862 | ||
| 865 | /** | 863 | /** |
| 866 | * Internally used gnutls callback function for sending encrypted data. | 864 | * Internally used SSL callback function for sending encrypted data. |
| 867 | */ | 865 | */ |
| 868 | static ssize_t internal_ssl_write(gnutls_transport_ptr_t transport, char *buffer, size_t length) | 866 | static ssize_t internal_ssl_write(idevice_connection_t connection, const char *buffer, size_t length) |
| 869 | { | 867 | { |
| 870 | uint32_t bytes = 0; | 868 | uint32_t bytes = 0; |
| 871 | idevice_error_t res; | 869 | idevice_error_t res; |
| 872 | idevice_connection_t connection = (idevice_connection_t)transport; | ||
| 873 | debug_info("pre-send length = %zi", length); | 870 | debug_info("pre-send length = %zi", length); |
| 874 | if ((res = internal_connection_send(connection, buffer, length, &bytes)) != IDEVICE_E_SUCCESS) { | 871 | if ((res = internal_connection_send(connection, buffer, length, &bytes)) != IDEVICE_E_SUCCESS) { |
| 875 | debug_info("ERROR: internal_connection_send returned %d", res); | 872 | debug_info("ERROR: internal_connection_send returned %d", res); |
| 873 | connection->status = res; | ||
| 876 | return -1; | 874 | return -1; |
| 877 | } | 875 | } |
| 878 | debug_info("post-send sent %i bytes", bytes); | 876 | debug_info("post-send sent %i bytes", bytes); |
| 879 | return bytes; | 877 | return bytes; |
| 880 | } | 878 | } |
| 881 | #endif | ||
| 882 | 879 | ||
| 883 | /** | 880 | /** |
| 884 | * Internally used function for cleaning up SSL stuff. | 881 | * Internally used function for cleaning up SSL stuff. |
| @@ -918,6 +915,32 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data) | |||
| 918 | } | 915 | } |
| 919 | 916 | ||
| 920 | #ifdef HAVE_OPENSSL | 917 | #ifdef HAVE_OPENSSL |
| 918 | static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, int argi, long argl, long retvalue) | ||
| 919 | { | ||
| 920 | idevice_connection_t conn = (idevice_connection_t)BIO_get_callback_arg(b); | ||
| 921 | size_t len = (size_t)argi; | ||
| 922 | switch (oper) { | ||
| 923 | case (BIO_CB_READ|BIO_CB_RETURN): | ||
| 924 | return argp ? (long)internal_ssl_read(conn, (char *)argp, len) : 0; | ||
| 925 | case (BIO_CB_PUTS|BIO_CB_RETURN): | ||
| 926 | len = strlen(argp); | ||
| 927 | // fallthrough | ||
| 928 | case (BIO_CB_WRITE|BIO_CB_RETURN): | ||
| 929 | return (long)internal_ssl_write(conn, argp, len); | ||
| 930 | default: | ||
| 931 | return retvalue; | ||
| 932 | } | ||
| 933 | } | ||
| 934 | |||
| 935 | static BIO *ssl_idevice_bio_new(idevice_connection_t conn) | ||
| 936 | { | ||
| 937 | BIO *b = BIO_new(BIO_s_null()); | ||
| 938 | if (!b) return NULL; | ||
| 939 | BIO_set_callback_arg(b, (char *)conn); | ||
| 940 | BIO_set_callback(b, ssl_idevice_bio_callback); | ||
| 941 | return b; | ||
| 942 | } | ||
| 943 | |||
| 921 | static int ssl_verify_callback(int ok, X509_STORE_CTX *ctx) | 944 | static int ssl_verify_callback(int ok, X509_STORE_CTX *ctx) |
| 922 | { | 945 | { |
| 923 | return 1; | 946 | return 1; |
| @@ -1009,12 +1032,11 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne | |||
| 1009 | if (pair_record) | 1032 | if (pair_record) |
| 1010 | plist_free(pair_record); | 1033 | plist_free(pair_record); |
| 1011 | 1034 | ||
| 1012 | BIO *ssl_bio = BIO_new(BIO_s_socket()); | 1035 | BIO *ssl_bio = ssl_idevice_bio_new(connection); |
| 1013 | if (!ssl_bio) { | 1036 | if (!ssl_bio) { |
| 1014 | debug_info("ERROR: Could not create SSL bio."); | 1037 | debug_info("ERROR: Could not create SSL bio."); |
| 1015 | return ret; | 1038 | return ret; |
| 1016 | } | 1039 | } |
| 1017 | BIO_set_fd(ssl_bio, (int)(long)connection->data, BIO_NOCLOSE); | ||
| 1018 | 1040 | ||
| 1019 | SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); | 1041 | SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); |
| 1020 | if (ssl_ctx == NULL) { | 1042 | if (ssl_ctx == NULL) { |
diff --git a/src/idevice.h b/src/idevice.h index 8709c9a..4e53a7f 100644 --- a/src/idevice.h +++ b/src/idevice.h | |||
| @@ -68,6 +68,8 @@ struct idevice_connection_private { | |||
| 68 | enum idevice_connection_type type; | 68 | enum idevice_connection_type type; |
| 69 | void *data; | 69 | void *data; |
| 70 | ssl_data_t ssl_data; | 70 | ssl_data_t ssl_data; |
| 71 | unsigned int ssl_recv_timeout; | ||
| 72 | idevice_error_t status; | ||
| 71 | }; | 73 | }; |
| 72 | 74 | ||
| 73 | struct idevice_private { | 75 | struct idevice_private { |
