Skip to content

Instantly share code, notes, and snippets.

@ma8ma
Created February 11, 2019 00:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ma8ma/d211e243cecf1e1c6b52b3137d5580b7 to your computer and use it in GitHub Desktop.
Save ma8ma/d211e243cecf1e1c6b52b3137d5580b7 to your computer and use it in GitHub Desktop.
JDim GnuTLSの利用時に発生する受信エラーを修正するパッチ
Fix GnuTLS data receiving for chunked encoding
GnuTLSを使ってデータを受信するとき特定のエラーを受信データ無しに変換する。
GnuTLSの新しいバジョン(3.0.18?)からエラー値
(GNUTLS_E_PREMATURE_TERMINATION)が追加されchunked encodingで送信された
データを受信するときにエラーになってしまう問題があった。
GnuTLSの初期化を再構成し新しいバージョンで導入された機能を使うように
修正する。
データの受信送信をリトライするように修正する。
使い方
https://github.com/JDimproved/JDim のmasterブランチにこのパッチを適用してビルドする
diff --git a/src/jdlib/ssl.cpp b/src/jdlib/ssl.cpp
index 1c31888..ab35303 100644
--- a/src/jdlib/ssl.cpp
+++ b/src/jdlib/ssl.cpp
@@ -65,40 +65,68 @@ bool JDSSL::connect( const int soc, const char *host )
int ret;
+ gnutls_certificate_allocate_credentials( &m_cred );
+#if GNUTLS_VERSION_NUMBER >= 0x030020
+ ret = gnutls_certificate_set_x509_system_trust( m_cred );
+ assert( ret >= 0 );
+#endif
+
ret = gnutls_init( &m_session, GNUTLS_CLIENT );
if( ret != 0 ){
m_errmsg = "gnutls_init failed";
return false;
}
+#if GNUTLS_VERSION_NUMBER >= 0x020104
+ gnutls_set_default_priority( m_session );
#if GNUTLS_VERSION_NUMBER >= 0x020108
- // gnutls >= 2.1.7 (unreleased)
gnutls_priority_set_direct( m_session, "NORMAL:%COMPAT", NULL );
-#else // GNUTLS_VERSION_NUMBER >= 0x020108
+#else
static const int priority_prot[] = { GNUTLS_SSL3, 0 };
- // DEPRECATED (gnutls >= 2.1.4 gnutls =< 2.1.6)
- // UNDEPRECATED (gnutls >= 2.1.7)
- gnutls_set_default_priority( m_session );
// _GNUTLS_GCC_ATTR_DEPRECATE (gnutls >= 2.12.0)
gnutls_protocol_set_priority( m_session, priority_prot );
#endif // GNUTLS_VERSION_NUMBER >= 0x020108
+#endif // GNUTLS_VERSION_NUMBER >= 0x020104
- gnutls_transport_set_ptr( m_session, (gnutls_transport_ptr_t)(long) soc );
- gnutls_certificate_allocate_credentials( &m_cred );
gnutls_credentials_set( m_session, GNUTLS_CRD_CERTIFICATE, m_cred );
gnutls_server_name_set( m_session, GNUTLS_NAME_DNS, host, strlen( host ) );
+#if GNUTLS_VERSION_NUMBER >= 0x030406
+ gnutls_session_set_verify_cert( m_session, host, 0 );
+#endif
+
+#if GNUTLS_VERSION_NUMBER >= 0x030109
+ gnutls_transport_set_int( m_session, soc );
+#else
+ gnutls_transport_set_ptr( m_session, static_cast< gnutls_transport_ptr_t >( soc ) );
+#endif
+#if GNUTLS_VERSION_NUMBER >= 0x030100
+ gnutls_handshake_set_timeout( m_session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT );
+#endif
- while ( ( ret = gnutls_handshake( m_session ) ) != GNUTLS_E_SUCCESS )
- {
- if ( gnutls_error_is_fatal( ret ) != 0 )
- {
- m_errmsg = "gnutls_handshake failed";
- return false;
+ do {
+ ret = gnutls_handshake( m_session );
+ } while( ret < 0 && gnutls_error_is_fatal( ret ) == 0 );
+
+ if( ret < 0 ) {
+#if defined(_DEBUG) && GNUTLS_VERSION_NUMBER >= 0x030406
+ if( ret == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR ) {
+ gnutls_certificate_type_t type = gnutls_certificate_type_get( m_session );
+ const unsigned status = gnutls_session_get_verify_cert_status( m_session );
+ gnutls_datum_t out;
+ gnutls_certificate_verification_status_print( status, type, &out, 0 );
+ std::cout << "JDSSL::connect(gnutls) - cert verify output: " << out.data << std::endl;
+ gnutls_free( out.data );
}
+#endif
+ m_errmsg = "JDSSL::connect(gnutls) *** Handshake failed: ";
+ m_errmsg += gnutls_strerror( ret );
+ return false;
}
#ifdef _DEBUG
- std::cout << "connect ok\n";
+ char* desc = gnutls_session_get_desc( m_session );
+ std::cout << "JDSSL::connect(gnutls) - Session info: " << desc << std::endl;
+ gnutls_free( desc );
#endif
return true;
@@ -108,7 +136,7 @@ bool JDSSL::connect( const int soc, const char *host )
bool JDSSL::close()
{
#ifdef _DEBUG
- std::cout << "JDSSL::close(gnutlsl)\n";
+ std::cout << "JDSSL::close(gnutls)\n";
#endif
if( m_session ){
@@ -127,7 +155,15 @@ bool JDSSL::close()
int JDSSL::write( const char* buf, const size_t bufsize )
{
- int tmpsize = gnutls_record_send( m_session, buf, bufsize );
+ int tmpsize;
+ do {
+ tmpsize = gnutls_record_send( m_session, buf, bufsize );
+ } while( tmpsize == GNUTLS_E_AGAIN || tmpsize == GNUTLS_E_INTERRUPTED );
+
+#ifdef _DEBUG
+ std::cout << "JDSSL::write(gnutls) tmpsize = " << tmpsize << "; bufsize = " << bufsize << std::endl;
+#endif
+
if( tmpsize < 0 ) m_errmsg = "gnutls_record_send failed";
return tmpsize;
@@ -136,7 +172,22 @@ int JDSSL::write( const char* buf, const size_t bufsize )
int JDSSL::read( char* buf, const size_t bufsize )
{
- int tmpsize = gnutls_record_recv( m_session, buf, bufsize );
+ int tmpsize;
+ do {
+ tmpsize = gnutls_record_recv( m_session, buf, bufsize );
+ } while( tmpsize == GNUTLS_E_AGAIN || tmpsize == GNUTLS_E_INTERRUPTED );
+
+#ifdef _DEBUG
+ std::cout << "JDSSL::read(gnutls) tmpsize = " << tmpsize << "; bufsize = " << bufsize << std::endl;
+#endif
+
+#ifdef GNUTLS_E_PREMATURE_TERMINATION
+ if( tmpsize == GNUTLS_E_PREMATURE_TERMINATION || tmpsize == GNUTLS_E_INVALID_SESSION ) {
+ // Transfer-Encoding: chuncked のときはデータの長さが分からない
+ // そのため受信エラーをデータ無しに置き換えて受信終了を判断する
+ return 0;
+ }
+#endif
if( tmpsize < 0 ) m_errmsg = "gnutls_record_recv failed";
return tmpsize;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment