Hi all, I'm currently enhancing HTTP(S) clients based on OpenSSL in several flavors, in particular a CMP client, which in turn uses simple HTTP clients for contacting CRL distribution points or OCSP responders.
Getting the client connect right appears surprisingly messy when one needs to cope with all kinds of network error situations including domain name resolution issues and temporarily unreachable servers. Both indefinitely blocking and non-blocking behavior (i.e., connection attempts with and without a timeout) should be supported. Since these are pretty general problems I wonder why there there is rather limited support via generic higher-level OpenSSL or C library functions, or at least I was unable to find it. Instead, the OpenSSL apps contain code that calls BIO_do_connect directly (or the equivalent BIO_do_handshake), in particular query_responder() in apps/ocsp.c. (The situation is similar for the subsequent exchange of data via the BIO, optionally with a timeout). So I constructed my own abstraction, called bio_connect, which took quite some effort testing network error situations. Please see below its code including comments on some strange behavior I experienced and my workarounds for that. Does this code make sense, or do I miss anything? How about adding such a function for instance to crypto/bio/bio_lib.c? BTW, my code uses a handy generic helper function, socket_wait, for waiting for read/write form/to a socket, with a given timeout. Since several instances of that pretty common code pattern using select() are spread over the OpenSSL apps (and crypto lib), I suggest adding this function to the library. Where would be a good place to put it? Thanks, David > /* returns -1 on error, 0 on timeout, 1 on success */ > int bio_connect(BIO *bio, int timeout) { > int blocking; > time_t max_time; > int rv; > > blocking = timeout == 0; > max_time = timeout != 0 ? time(NULL) + timeout : 0; > > if (!blocking) > BIO_set_nbio(bio, 1); > retry: > rv = BIO_do_connect(bio); > if (rv <= 0 && (errno == ETIMEDOUT /* in blocking case, > despite blocking BIO, BIO_do_connect() timed out */ || > ERR_GET_REASON(ERR_peek_error()) == ETIMEDOUT/* when non-blocking, > BIO_do_connect() timed out early with rv == -1 and errno == 0 */)) { > ERR_clear_error(); > (void)BIO_reset(bio); /* otherwise, blocking next connect() may crash > and non-blocking next BIO_do_connect() will fail > */ > goto retry; > } > if (!blocking && rv <= 0 && BIO_should_retry(bio)) { > int fd; > if (BIO_get_fd(bio, &fd) <= 0) > return -1; > rv = socket_wait(fd, 1, max_time - time(NULL)); > if (rv > 0) > /* for some reason, select() may wrongly have returned success */ > goto retry; > } > return rv; > } > /* returns < 0 on error, 0 on timeout, >0 on success */ > int socket_wait(int fd, int for_read, int timeout) > { > fd_set confds; > struct timeval tv; > > if (timeout <= 0) > return 0; > > FD_ZERO(&confds); > openssl_fdset(fd, &confds); > tv.tv_usec = 0; > tv.tv_sec = timeout; > return select(fd + 1, for_read ? &confds : NULL, > for_read ? NULL : &confds, NULL, &tv); > } -- openssl-dev mailing list To unsubscribe: https://mta.openssl.org/mailman/listinfo/openssl-dev