Test non-blocking connect in connect BIOs I'm a little surprised this test reliably works on loopback, but it seems to be OK. Let's see what the CQ thinks. Change-Id: I71ede5bd64d8fb82e346b44f27d53475f41677ec Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/94808 Commit-Queue: Adam Langley <agl@google.com> Auto-Submit: David Benjamin <davidben@google.com> Presubmit-BoringSSL-Verified: boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/bio/bio_test.cc b/crypto/bio/bio_test.cc index afe35dd..5c93174 100644 --- a/crypto/bio/bio_test.cc +++ b/crypto/bio/bio_test.cc
@@ -232,26 +232,113 @@ ntohs(addr.ToIPv4().sin_port)); } - // Connect to it with a connect BIO. - UniquePtr<BIO> bio(BIO_new_connect(hostname)); - ASSERT_TRUE(bio); + // Using a connect BIO implicitly connects to it. + { + // Connect to it with a connect BIO. + UniquePtr<BIO> bio(BIO_new_connect(hostname)); + ASSERT_TRUE(bio); - // Write a test message to the BIO. This is assumed to be smaller than the - // transport buffer. - ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)), - BIO_write(bio.get(), kTestMessage, sizeof(kTestMessage))) - << LastSocketError(); + // Write a test message to the BIO. This is assumed to be smaller than the + // transport buffer. + ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)), + BIO_write(bio.get(), kTestMessage, sizeof(kTestMessage))) + << LastSocketError(); - // Accept the socket. - OwnedSocket sock(accept(listening_sock.get(), addr.addr_mut(), &addr.len)); - ASSERT_TRUE(sock.is_valid()) << LastSocketError(); + // Accept the socket. + OwnedSocket sock(accept(listening_sock.get(), addr.addr_mut(), &addr.len)); + ASSERT_TRUE(sock.is_valid()) << LastSocketError(); - // Check the same message is read back out. - char buf[sizeof(kTestMessage)]; - ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)), - recv(sock.get(), buf, sizeof(buf), 0)) - << LastSocketError(); - EXPECT_EQ(Bytes(kTestMessage, sizeof(kTestMessage)), Bytes(buf, sizeof(buf))); + // Check the same message is read back out. + char buf[sizeof(kTestMessage)]; + ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)), + recv(sock.get(), buf, sizeof(buf), 0)) + << LastSocketError(); + EXPECT_EQ(Bytes(kTestMessage, sizeof(kTestMessage)), + Bytes(buf, sizeof(buf))); + } + + // Explicitly connect to the BIO first. + { + UniquePtr<BIO> bio(BIO_new_connect(hostname)); + ASSERT_TRUE(bio); + + ASSERT_EQ(1, BIO_do_connect(bio.get())) << LastSocketError(); + ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)), + BIO_write(bio.get(), kTestMessage, sizeof(kTestMessage))) + << LastSocketError(); + + // Accept and read. + OwnedSocket sock(accept(listening_sock.get(), addr.addr_mut(), &addr.len)); + ASSERT_TRUE(sock.is_valid()) << LastSocketError(); + char buf[sizeof(kTestMessage)]; + ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)), + recv(sock.get(), buf, sizeof(buf), 0)) + << LastSocketError(); + EXPECT_EQ(Bytes(kTestMessage, sizeof(kTestMessage)), + Bytes(buf, sizeof(buf))); + } + + // Connect in non-blocking mode. + { + UniquePtr<BIO> bio(BIO_new_connect(hostname)); + ASSERT_TRUE(bio); + ASSERT_EQ(1, BIO_set_nbio(bio.get(), 1)); + + ASSERT_EQ(-1, BIO_do_connect(bio.get())); + EXPECT_TRUE(BIO_should_retry(bio.get())); + EXPECT_TRUE(BIO_should_io_special(bio.get())); + EXPECT_EQ(BIO_RR_CONNECT, BIO_get_retry_reason(bio.get())); + + // Wait for the underlying socket to become writable and try again. + int fd = BIO_get_fd(bio.get(), nullptr); + ASSERT_GT(fd, -1); + ASSERT_TRUE(WaitForSocket(static_cast<Socket>(fd), WaitType::kWrite)); + ASSERT_EQ(1, BIO_do_connect(bio.get())); + + ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)), + BIO_write(bio.get(), kTestMessage, sizeof(kTestMessage))) + << LastSocketError(); + + // Accept and read. + OwnedSocket sock(accept(listening_sock.get(), addr.addr_mut(), &addr.len)); + ASSERT_TRUE(sock.is_valid()) << LastSocketError(); + char buf[sizeof(kTestMessage)]; + ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)), + recv(sock.get(), buf, sizeof(buf), 0)) + << LastSocketError(); + EXPECT_EQ(Bytes(kTestMessage, sizeof(kTestMessage)), + Bytes(buf, sizeof(buf))); + } + + // Implicitly connect in non-blocking mode. + { + UniquePtr<BIO> bio(BIO_new_connect(hostname)); + ASSERT_TRUE(bio); + ASSERT_EQ(1, BIO_set_nbio(bio.get(), 1)); + + ASSERT_EQ(-1, BIO_write(bio.get(), kTestMessage, sizeof(kTestMessage))); + EXPECT_TRUE(BIO_should_retry(bio.get())); + EXPECT_TRUE(BIO_should_io_special(bio.get())); + EXPECT_EQ(BIO_RR_CONNECT, BIO_get_retry_reason(bio.get())); + + // Wait for the underlying socket to become writable and try again. + int fd = BIO_get_fd(bio.get(), nullptr); + ASSERT_GT(fd, -1); + ASSERT_TRUE(WaitForSocket(static_cast<Socket>(fd), WaitType::kWrite)); + ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)), + BIO_write(bio.get(), kTestMessage, sizeof(kTestMessage))) + << LastSocketError(); + + // Accept and read. + OwnedSocket sock(accept(listening_sock.get(), addr.addr_mut(), &addr.len)); + ASSERT_TRUE(sock.is_valid()) << LastSocketError(); + char buf[sizeof(kTestMessage)]; + ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)), + recv(sock.get(), buf, sizeof(buf), 0)) + << LastSocketError(); + EXPECT_EQ(Bytes(kTestMessage, sizeof(kTestMessage)), + Bytes(buf, sizeof(buf))); + } } TEST(BIOTest, SocketNonBlocking) {