Hi, I'm trying to monitor an inbox using IMAP. I'd like to let cURL do the TLS and authentication magic. The program must deal with connection terminated by server or disrupted after some time. So I tried to follow man curl_easy_perform: > You can do any amount of calls to curl_easy_perform(3) while using the same > easy_handle.
The problem is that although everything seems to work (in version 8.9.1), valgrind reports massive memory leaks each time the easy handle is re-used by curl_easy_perform. On the other hand, if interrupted right after first successful connection, memleak report is clear. Therefore I wrote simplest possible program to demonstrate the problem and run it against development version of libcurl. It should login and then logout 10x. However, talking to a locally running test IMAP server (from cURL test suite): $ tests/ftpserver.pl --verbose --port 43143 --proto imap --logdir /tmp/imap ... it connects successfully just once. Second time libcurl aborts with: example: url.c:749: Curl_conn_seems_dead: Assertion `!data->conn' failed. When using a real-world server with TLS (imaps://...), it also aborts when trying to re-use the handle, but somewhere else: example: multi.c:964: Curl_attach_connection: Assertion `!data->conn' failed. The TLS case differs also by extra "* Expire cleared" message. Haven't found additional hints in man curl_easy_send / curl_easy_recv. Changing CURLOPT_MAXCONNECTS doesn't help. Please review attached source code and let me know what's wrong. Thanks, -- Aleksander Mazur
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <time.h> #include <sys/select.h> #include <curl/curl.h> static int unix_wait(curl_socket_t sockfd, unsigned sending, unsigned timeout) { fd_set fds, *rfds = sending ? NULL : &fds, *wfds = sending ? &fds : NULL; struct timeval tv = { timeout, 0 }; int rv; FD_ZERO(&fds); FD_SET(sockfd, &fds); rv = select(sockfd + 1, rfds, wfds, NULL, &tv); return rv > 0 ? 0 /*ready*/ : rv < 0 ? rv /*err*/ : 1 /*timeout*/; } static const char command[] = "A013 LOGOUT\r\n"; static char buf[4096]; int main(void) { CURL *curl; unsigned loop; assert(curl_global_init(CURL_GLOBAL_ALL) == CURLE_OK); fprintf(stderr, "using curl %s\n", curl_version_info(0)->version); assert((curl = curl_easy_init())); assert(curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L) == CURLE_OK); assert(curl_easy_setopt(curl, CURLOPT_MAXCONNECTS, 1L) == CURLE_OK); assert(curl_easy_setopt(curl, CURLOPT_USERNAME, "test") == CURLE_OK); assert(curl_easy_setopt(curl, CURLOPT_PASSWORD, "test") == CURLE_OK); assert(curl_easy_setopt(curl, CURLOPT_URL, "imap://localhost:43143") == CURLE_OK); assert(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L) == CURLE_OK); for (loop = 1; loop <= 10; loop++) { curl_socket_t sockfd; size_t done, n; printf("loop[%u] perform\n", loop); assert(curl_easy_perform(curl) == CURLE_OK); assert(curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd) == CURLE_OK); for (done = 0; done < sizeof(command) - 1; done += n) { CURLcode res = curl_easy_send(curl, command + done, sizeof(command) - 1 - done, &n); if (res == CURLE_OK) { fprintf(stderr, "loop[%u] sent %zu\n", loop, n); if (!n) break; } else if (res == CURLE_AGAIN) { fprintf(stderr, "loop[%u] wait send\n", loop); if (unix_wait(sockfd, 1, 10)) break; } else { abort(); } } if (done == sizeof(command) - 1) { for (done = 0; done < sizeof(buf); done += n) { CURLcode res = curl_easy_recv(curl, buf + done, sizeof(buf) - done, &n); if (res == CURLE_OK) { fprintf(stderr, "loop[%u] recv %zu\n", loop, n); if (!n) break; } else if (res == CURLE_AGAIN) { fprintf(stderr, "loop[%u] wait recv\n", loop); if (unix_wait(sockfd, 0, 3)) break; } else { abort(); } } if (done) printf("loop[%u] response:%.*s--------\n", loop, (int) done, buf); } } return 0; }
using curl 8.12.0-DEV loop[1] perform * !!! WARNING !!! * This is a debug build of libcurl, do not use in production. * STATE: INIT => SETUP handle 0x8421df8; line 2677 * STATE: SETUP => CONNECT handle 0x8421df8; line 2693 * Added connection 0. The cache now contains 1 members * Host localhost:43143 was resolved. * IPv4: 127.0.0.1 * STATE: CONNECT => CONNECTING handle 0x8421df8; line 2608 * Trying 127.0.0.1:43143... * Connected to localhost (127.0.0.1) port 43143 * STATE: CONNECTING => PROTOCONNECT handle 0x8421df8; line 2731 * IMAP 0x841e2c0 state change from STOP to SERVERGREET * STATE: PROTOCONNECT => PROTOCONNECTING handle 0x8421df8; line 2756 < _ _ ____ _ < ___| | | | _ \| | < / __| | | | |_) | | < | (__| |_| | _ {| |___ < \___|\___/|_| \_\_____| < * OK curl IMAP server ready to serve > A001 CAPABILITY * IMAP 0x841e2c0 state change from SERVERGREET to CAPABILITY < A001 BAD Command > A002 LOGIN test test * IMAP 0x841e2c0 state change from CAPABILITY to LOGIN < A002 OK LOGIN completed * IMAP 0x841e2c0 state change from LOGIN to STOP * STATE: PROTOCONNECTING => DO handle 0x8421df8; line 2777 * STATE: DO => DONE handle 0x8421df8; line 2329 * multi_done[DONE]: status: 0 prem: 0 done: 0 * Connection #0 to host localhost left intact loop[1] sent 13 loop[1] wait recv loop[1] recv 36 loop[1] wait recv loop[1] recv 26 loop[1] wait recv loop[1] response:* BYE curl IMAP server signing off A013 OK LOGOUT completed -------- loop[2] perform * STATE: INIT => SETUP handle 0x8421df8; line 2677 * STATE: SETUP => CONNECT handle 0x8421df8; line 2693 example: url.c:749: Curl_conn_seems_dead: Assertion `!data->conn' failed. Program received signal SIGABRT, Aborted.
-- Unsubscribe: https://lists.haxx.se/mailman/listinfo/curl-library Etiquette: https://curl.se/mail/etiquette.html