OpenSSLの統合をほぼ完了したWindows用のC ++で記述されたクライアント/サーバープログラムがあります。
私は自分自身と同様の他のすべての投稿に目を通し、私の問題に対する正しい答えをまだ見つけていないことを述べたいと思います. 他の例のほとんどは答えに非常に近いですが、実際の結果を出すことができないようです。
私のテスト ケースでは、5 秒ごとに OpenSSL セキュア ソケットを使用して、クライアントからサーバーに 7 文字の文字列を渡すだけです。他の情報は送信しません。元の問題は、SSL_read を正しく使用しておらず、明らかに必要な余分なデータを読み取っていなかったため、SSL_read が応答を停止し、クライアントが接続を失ったことでした。
他のすべての例を読んだ後、これを達成するためにさらにいくつかの手順を実行しましたが、望ましい結果を得ることができませんでした。10 種類以上の SSL_read 実装を試みましたが、データの読み取り中にコードを非ブロックにすることはできません。最新の実装を投稿します(信じられないほど最適化されていません):
int received = -1;
int tE = 0;
if( tE != SSL_ERROR_WANT_READ && tE != SSL_ERROR_WANT_WRITE )
{
received = SSL_read(sSecureSocket.ssl, buffer, sizeof(buffer));
if( received == -1 )
{
tE = SSL_get_error( sSecureSocket.ssl, received );
if( tE == SSL_ERROR_WANT_READ )
{
int tReadWaiting = 1;
while( tReadWaiting )
{
tReadWaiting = 0;
fd_set rfd;
FD_ZERO(&rfd);
FD_SET(receiveSocket, &rfd);
timeval tv = { 0 };
select(receiveSocket+1, &rfd, 0, 0, &tv);
if (!FD_ISSET(receiveSocket, &rfd))
{
tReadWaiting = 1;
}
}
received = SSL_read(sSecureSocket.ssl, buffer, sizeof(buffer));
}
}
}
このコードは、送信されるたびにパケットを正常に抽出しますが、ご覧のとおり、SSL_ERROR_WANT_READ/WRITE がトリガーされたら、select() ステートメントを使用する必要があります。このアプローチは、このフォーラムの別のスレッドから取られました。たとえば、SSL_ERROR_WANT_READ/WRITER が呼び出されるまで読む必要があるとします。また、私は SSL_pending() を使用しますが、すべてのケースで常に 0 を返します。ゼロ以外の数値になることはありません。
このプログラムは、Windows メッセージへの WSAASyncSelect 呼び出しを使用するノンブロッキング ソケットで記述されています。それはすべてうまくいきます。
しかし、何が起こるかは、私の予想とは正反対です。コードは、存在しないデータを常に待機しているようです。SSLソケットは、ランダムな時間にハンドシェイクを行うことも意図していることに気づきました。サーバーからデータを受信した後、SSL_read にデータがあるように見える理由、または送信する 5 秒間隔の間にデータを読み込む必要があることを select() が検出した理由を理解しようとしています。 select() ループがずっとブロックされるように、私のパケット。パケットが完全に読み取られた後でも、次のメッセージが読み取り関数にコールバックされ、プログラム自体がこの関数を離れて他のプロセスを実行することはできません。
どんな助けも大歓迎です。私はこれまでプログラミングを最後までやり遂げたことはありませんでした。これは私をそれに非常に近づけています。
更新 1:
これはソケット リッスン コードです。
ctx = sSecureSocket.InitServerCTX(); // initialize SSL
sSecureSocket.LoadCertificates(ctx, VGlobal::CURRWORKDIR + "\\openssl2\\" + "mycert.pem", VGlobal::CURRWORKDIR + "\\openssl2\\" + "mycert.pem"); // load certs
servSecureSocket = sSecureSocket.OpenListener(39245); // create server socket
これは、サーバーでソケットが受け入れられたときに呼び出されます。
//Always connect as a secure socket first
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
SSL *ssl;
receiveSocket = accept(servSecureSocket, (struct sockaddr*)&addr, &len); // accept connection as usual
printf("Connection: %s:%d\n",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
sSecureSocket.ssl = SSL_new(ctx); // get new SSL state with context
SSL_set_fd(sSecureSocket.ssl, receiveSocket); // set connection socket to SSL state
sSecureSocket.Servlet(sSecureSocket.ssl); // service connection
これらは関数です:
//-----------------------------------------------------
int SecureSocket::OpenListener(int port)
{ int sd;
struct sockaddr_in addr;
WSADATA wsadata;
int error = WSAStartup( 0x0202, &wsadata );
if( error )
return false;
sd = socket(PF_INET, SOCK_STREAM, 0);
//bzero(&addr, sizeof(addr));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
perror("can't bind port");
// abort();
}
if ( listen(sd, 10) != 0 )
{
perror("Can't configure listening port");
// abort();
}
return sd;
}
//-----------------------------------------------------
SSL_CTX* SecureSocket::InitServerCTX(void)
{
SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
SSL_load_error_strings(); /* load all error messages */
ctx = SSL_CTX_new(SSLv23_server_method());
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
}
return ctx;
}
//-----------------------------------------------------
void SecureSocket::LoadCertificates(SSL_CTX* ctx, CString CertFile, CString KeyFile)
{
/* set the local certificate from CertFile */
if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
}
/* set the private key from KeyFile (may be the same as CertFile) */
if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
fprintf(stderr, "Private key does not match the public certificate\n");
}
}
//-----------------------------------------------------
void SecureSocket::ShowCerts(SSL* ssl)
{ X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
if ( cert != NULL )
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line);
X509_free(cert);
}
else
printf("No certificates.\n");
}
//-----------------------------------------------------
void SecureSocket::Servlet(SSL* ssl) /* Serve the connection -- threadable */
{
int acceptDone = SSL_accept(ssl);
//If this blocking this will be done immediately, if it is not we must loop until it is complete
while( acceptDone < 1 )
{
acceptDone = SSL_accept(ssl);
}
ShowCerts(ssl); /* get any certificates */
}