SSL을 사용하여 서버를 만들고 은 소켓을 차단했습니다. 텔넷으로 연결할 때 (핸드 셰이크를하지 않기 때문에) SSL_accept는 무기한 차단하고 새로운 핸드 셰이크/수락 (그리고 정의에 따라 새 연결)을 차단합니다.블로킹 소켓이있는 SSL_accept
이 끔찍한 문제를 어떻게 해결할 수 있습니까?
SSL을 사용하여 서버를 만들고 은 소켓을 차단했습니다. 텔넷으로 연결할 때 (핸드 셰이크를하지 않기 때문에) SSL_accept는 무기한 차단하고 새로운 핸드 셰이크/수락 (그리고 정의에 따라 새 연결)을 차단합니다.블로킹 소켓이있는 SSL_accept
이 끔찍한 문제를 어떻게 해결할 수 있습니까?
은 왜 그냥 SSL_accept()를 호출하기 전에 비 블록 모드의 소켓 스트림을 설정하고 SSL_accept()가 SSL_ERROR_WANT_READ 또는 SSL_ERROR_WANT_WRITE을 반환하는 경우 다음 타임 아웃 select() 같은에 차단? 또는 SSL_accept()를 호출하기 전에 select()를 차단할 수 있습니다. 어느 쪽이든 작동해야합니다. 그렇게하면 최소한 행동/공격과 같은 DoS으로 인해 연결이 차단 된 시간을 제한 할 수 있습니다.
SSL/TLS는 레코드 지향이므로 전체 레코드를 읽을 때까지 반복해야한다는 것을 명심하십시오. 그러한 경우에는 SSL_pending()이 도움이 될 수 있습니다.
텔넷으로 연결하지 마십시오?
일반적으로 TLS 또는 일반 텍스트 연결을 사용할 수있는 서비스는 일반 텍스트로 연결을 설정 한 다음 TLS를 사용하도록 연결을 "업그레이드"하도록 요청하는 명령을 제공하여 작동합니다. 확장 SMTP 및 "STARTTLS"명령은이 예입니다.
이러한 명령이없는 서비스는 일반적으로 TLS 및 비 TLS 트래픽에 대해 고유 한 포트를 사용합니다. 예를 들어 포트 80에서는 HTTP, 포트 443에서는 HTTPS가 사용됩니다. 포트 443에 대한 일반 텍스트 연결은 작동하지 않습니다.
어떤 행동을 보시겠습니까?
소켓을 비 차단 모드로 놓으면 SSL_ERROR_WANT_READ 또는 SSL_ERROR_WANT_WRITE가 SSL_accept에서 가져옵니다. 그런 다음 조금 잠을 자면 SSL_accept를 다시 시도하십시오. 일부 시간 초과 값 후에는 종료하고 ssl 및 소켓 핸들을 닫을 수 있습니다.
모든 SSL 작업에 영향을 미치므로 모든 읽기/쓰기/종료 호출에 대해 유사한 루프를 수행해야합니다. 기본적으로 WANT_READ 또는 WANT_WRITE를 반환 할 수있는 모든 호출.
폴링 아이디어가 마음에 들지 않으면 select를 사용하여 소켓에서 사용할 수있는 데이터가 있는지 알아낼 수 있지만 약간 복잡 할 수 있습니다.
또한 SSL_accept 루프 이후에 소켓을 다시 차단 모드로 전환하여 응용 프로그램을 계속 시도 할 수 있습니다.
아래 코드는 다른 사람들이 문제를 해결하는 데 도움이 될 것으로 생각합니다. 그것을 영감으로 받아 들일만큼 충분히 테스트되지는 않았습니다.
//Nonblocking SSL accept based on ACE/ace/SSL/SSL_SOCK_Acceptor.cpp
SSL_CTX* ctx;
ctx = initServerCTX(); // initialize SSL
loadCertificates(ctx, certificate, privateKey); // load certs
...
SSL* ssl = SSL_new(ctx); /* get new SSL state with context */
SSL_set_fd(ssl, fd); /* set connection socket to SSL state */
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0)
{
printf("fcntl: F_GETFL \n");
return false;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
{
printf("fcntl: F_SETFL \n");
return false;
}
int status = -1;
struct timeval tv, tvRestore;
tv.tv_sec = 2;
tv.tv_usec = 0;
tvRestore = tv;
fd_set writeFdSet;
fd_set readFdSet;
do
{
tv = tvRestore;
FD_ZERO(&writeFdSet);
FD_ZERO(&readFdSet);
status = ::SSL_accept(ssl);
switch (::SSL_get_error(ssl, status))
{
case SSL_ERROR_NONE:
status = 0; // To tell caller about success
break; // Done
case SSL_ERROR_WANT_WRITE:
FD_SET(fd, &writeFdSet);
status = 1; // Wait for more activity
break;
case SSL_ERROR_WANT_READ:
FD_SET(fd, &readFdSet);
status = 1; // Wait for more activity
break;
case SSL_ERROR_ZERO_RETURN:
case SSL_ERROR_SYSCALL:
// The peer has notified us that it is shutting down via
// the SSL "close_notify" message so we need to
// shutdown, too.
printf("Peer closed connection during SSL handshake,status:%d", status);
status = -1;
break;
default:
printf("Unexpected error during SSL handshake,status:%d", status);
status = -1;
break;
}
if (status == 1)
{
// Must have at least one handle to wait for at this point.
status = select(fd + 1, &readFdSet, &writeFdSet, NULL, &tv);
// 0 is timeout, so we're done.
// -1 is error, so we're done.
// Could be both handles set (same handle in both masks) so
// set to 1.
if (status >= 1)
{
status = 1;
}
else // Timeout or failure
{
printf("SSL handshake - peer timeout or failure");
status = -1;
}
}
}
while (status == 1 && !SSL_is_init_finished(ssl));
flags = fcntl(fd, F_GETFL, 0);
if (flags < 0)
{
printf("fcntl: F_GETFL \n");
return false;
}
if (fcntl(fd, F_SETFL, flags & (~O_NONBLOCK)) < 0)
{
printf("fcntl: F_SETFL \n");
return false;
}
return (status >= 0);
그래, 그게 내가하고있는 일이지만 핸드 셰이크를 할 때 클라이언트의 연결을 끊을 때마다 다른 핸드 셰이크를 차단한다고 상상해보십시오. 는 말은 : -는 TCP가 메인 스레드 에서 이루어집니다 동의 - SSL_accept 각 새 스레드 에 의해 이루어집니다 - (별도의 스레드에서) 하나 개의 핸드 셰이크 블록은 블록의 경우 모든 핸드 셰이크 내가하고 싶은 행동 모든 새로운 악수를 차단하는 것이 아니다. – fedj