forked from embeddedmz/socket-cpp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTCPSSLClient.cpp
More file actions
307 lines (261 loc) · 10.4 KB
/
TCPSSLClient.cpp
File metadata and controls
307 lines (261 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/**
* @file TCPSSLClient.cpp
* @brief implementation of the TCP SSL client class
* @author Mohamed Amine Mzoughi <[email protected]>
*/
#ifdef OPENSSL
#include "TCPSSLClient.h"
CTCPSSLClient::CTCPSSLClient(const LogFnCallback& oLogger,
const OpenSSLProtocol& eSSLVersion,
const SettingsFlag eSettings /*= ALL_FLAGS*/)
: ASecureSocket(oLogger, eSSLVersion, eSettings)
, m_TCPClient(oLogger, eSettings)
{
}
CTCPSSLClient::~CTCPSSLClient()
{
Disconnect();
}
bool CTCPSSLClient::SetRcvTimeout(unsigned int msec_timeout)
{
return m_TCPClient.SetRcvTimeout(msec_timeout);
}
#ifndef _WIN32
bool CTCPSSLClient::SetRcvTimeout(struct timeval timeout)
{
return m_TCPClient.SetRcvTimeout(timeout);
}
#endif
bool CTCPSSLClient::SetSndTimeout(unsigned int msec_timeout)
{
return m_TCPClient.SetSndTimeout(msec_timeout);
}
#ifndef _WIN32
bool CTCPSSLClient::SetSndTimeout(struct timeval timeout)
{
return m_TCPClient.SetSndTimeout(timeout);
}
#endif
// Connexion au serveur
bool CTCPSSLClient::Connect(const std::string& strServer, const std::string& strPort)
{
if (!m_TCPClient.Connect(strServer, strPort))
{
SocketLog("[ERROR]TCPSSLClient, m_TCPClient Connect failed[:Unable to establish a TCP connection with the server.][%s:%s]", strServer.c_str(), strPort.c_str());
return false;
}
do
{
m_SSLConnectSocket.m_SockFd = m_TCPClient.m_ConnectSocket;
if (!SetUpCtxClient(m_SSLConnectSocket))
{
SocketLog("[ERROR]TCPSSLClient, SSL_CTX_new failed[%s:%s][%d]", strServer.c_str(), strPort.c_str(), m_SSLConnectSocket.m_SockFd);
//ERR_print_errors_fp(stdout);
break;
}
/* process SSL certificates */
/* Load a client certificate into the SSL_CTX structure. */
if (!m_strSSLCertFile.empty())
{
if (SSL_CTX_use_certificate_file(m_SSLConnectSocket.m_pCTXSSL, m_strSSLCertFile.c_str(), SSL_FILETYPE_PEM) != 1)
{
SocketLog("[ERROR]TCPSSLClient, SSL_CTX_use_certificate_file failed[Loading cert file failed.][%lu:%s][%d][%s]", ERR_get_error(), ERR_error_string(ERR_get_error(), nullptr), m_SSLConnectSocket.m_SockFd, m_strSSLCertFile.c_str());
break;
}
}
/* Load trusted CA. Mandatory to verify server's certificate */
if (!m_strCAFile.empty())
{
if (SSL_CTX_load_verify_locations(m_SSLConnectSocket.m_pCTXSSL, m_strCAFile.c_str(), nullptr) != 1)
{
SocketLog("[ERROR]TCPSSLClient, SSL_CTX_load_verify_locations failed[Loading CA file failed.][%lu:%s][%d][%s]", ERR_get_error(), ERR_error_string(ERR_get_error(), nullptr), m_SSLConnectSocket.m_SockFd, m_strCAFile.c_str());
break;
}
SSL_CTX_set_verify_depth(m_SSLConnectSocket.m_pCTXSSL, 1);
}
/**
* Load a private-key into the SSL_CTX structure.
* set key file that corresponds to the server or client certificate.
* In the SSL handshake, a certificate (which contains the public key) is transmitted to allow
* the peer to use it for encryption. The encrypted message sent from the peer can be decrypted
* only using the private key.
*/
if (!m_strSSLKeyFile.empty())
{
if (SSL_CTX_use_PrivateKey_file(m_SSLConnectSocket.m_pCTXSSL, m_strSSLKeyFile.c_str(), SSL_FILETYPE_PEM) != 1)
{
SocketLog("[ERROR]TCPSSLClient, SSL_CTX_use_PrivateKey_file failed[Loading key file failed.][%lu:%s][%d][%s]", ERR_get_error(), ERR_error_string(ERR_get_error(), nullptr), m_SSLConnectSocket.m_SockFd, m_strSSLKeyFile.c_str());
//ERR_print_errors_fp(stdout);
break;
}
#if 0
/* verify private key */
if (SSL_CTX_check_private_key(m_SSLConnectSocket.m_pCTXSSL) != 1)
{
SocketLog("[ERROR]TCPSSLClient, SSL_CTX_check_private_key failed[Private key does not match the public certificate.][%lu:%s][%d]", ERR_get_error(), ERR_error_string(ERR_get_error(), nullptr), m_SSLConnectSocket.m_SockFd);
break;
}
#endif
}
//SSL_CTX_set_cert_verify_callback(m_SSLConnectSocket.m_pCTXSSL, AlwaysTrueCallback, nullptr);
/* create new SSL connection state */
m_SSLConnectSocket.m_pSSL = SSL_new(m_SSLConnectSocket.m_pCTXSSL);
if (m_SSLConnectSocket.m_pSSL == nullptr)
{
SocketLog("[ERROR]TCPSSLClient, SSL_new failed[%lu:%s][%d]", ERR_get_error(), ERR_error_string(ERR_get_error(), nullptr), m_SSLConnectSocket.m_SockFd);
break;
}
if (SSL_set_fd(m_SSLConnectSocket.m_pSSL, (int)m_SSLConnectSocket.m_SockFd) != 1)
{
SocketLog("[ERROR]TCPSSLClient, SSL_set_fd failed[%lu:%s][%d]", ERR_get_error(), ERR_error_string(ERR_get_error(), nullptr), m_SSLConnectSocket.m_SockFd);
break;
}
bool connectOK = false;
do
{
/* initiate the TLS/SSL handshake with an TLS/SSL server */
int iResult = SSL_connect(m_SSLConnectSocket.m_pSSL);
if (iResult <= 0)
{
int iErrCode = SSL_get_error(m_SSLConnectSocket.m_pSSL, iResult);
if (iErrCode == SSL_ERROR_WANT_CONNECT || iErrCode == SSL_ERROR_WANT_WRITE || iErrCode == SSL_ERROR_WANT_READ)
{
continue;
}
// under Windows it creates problems
SocketLog("[ERROR]TCPSSLClient, SSL_connect failed[%d:%s][%d]", iErrCode, GetSSLErrorString(iErrCode), m_SSLConnectSocket.m_SockFd);
#ifndef _WIN32
//ERR_print_errors_fp(stdout);
#endif
break;
}
connectOK = true;
break;
} while (1);
if (!connectOK)
{
break;
}
/* The data can now be transmitted securely over this connection. */
SocketLog("[INFO ]TCPSSLClient, SSL_connect with '%s' encryption.", SSL_get_cipher(m_SSLConnectSocket.m_pSSL));
#if 0
if (SSL_get_peer_certificate(m_SSLConnectSocket.m_pSSL) == nullptr)
{
SocketLog("[WARN ]TCPSSLClient, SSL_get_peer_certificate failed[the peer certificate was not presented.][%d:%s][%d]", iErrCode, GetSSLErrorString(iErrCode), m_SSLConnectSocket.m_SockFd);
}
else
{
if (SSL_get_verify_result(m_SSLConnectSocket.m_pSSL) != X509_V_OK)
{
SocketLog("[ERROR]TCPSSLClient, client verification with SSL_get_verify_result failed.[%d]", m_SSLConnectSocket.m_SockFd);
break;
}
SocketLog("[WARN ]TCPSSLClient, client verification with SSL_get_verify_result succeeded.[%d]", m_SSLConnectSocket.m_SockFd);
}
#endif
return true;
} while (0);
Disconnect();
return false;
}
int CTCPSSLClient::Receive(char* pData, size_t uSize, bool bReadFully /*= true*/) const
{
if (m_TCPClient.m_eStatus != CTCPClient::CONNECTED || m_TCPClient.m_ConnectSocket == INVALID_SOCKET)
{
SocketLog("[ERROR]TCPSSLClient, SSL_read failed[not connected to a SSL server.]");
return -1;
}
if (pData == nullptr && uSize != 0)
{
SocketLog("[ERROR]TCPSSLClient, SSL_read failed[%d][%p:%zu]", m_SSLConnectSocket.m_SockFd, pData, uSize);
return -2;
}
int total = 0;
do
{
int nRecvd = SSL_read(m_SSLConnectSocket.m_pSSL, pData + total, (int)uSize - total);
if (nRecvd <= 0)
{
int iErrCode = SSL_get_error(m_SSLConnectSocket.m_pSSL, nRecvd);
if (iErrCode == SSL_ERROR_WANT_READ || iErrCode == SSL_ERROR_WANT_WRITE)
{
continue;
}
if (total == 0)
{
m_iSSLErrCode = iErrCode;
if (iErrCode == SSL_ERROR_ZERO_RETURN)
{
SocketLog("[INFO ]TCPSSLClient, SSL_read failed(peer shut down)[%d:%s][%d]", iErrCode, GetSSLErrorString(iErrCode), m_SSLConnectSocket.m_SockFd);
return 0;
}
SocketLog("[ERROR]TCPSSLClient, SSL_read failed[%d:%s][%d]", iErrCode, GetSSLErrorString(iErrCode), m_SSLConnectSocket.m_SockFd);
return -1;
}
break;
}
total += nRecvd;
} while (bReadFully && (total < (int)uSize));
return total;
}
int CTCPSSLClient::Send(const char* pData, size_t uSize) const
{
if (m_TCPClient.m_eStatus != CTCPClient::CONNECTED || m_TCPClient.m_ConnectSocket == INVALID_SOCKET)
{
SocketLog("[ERROR]TCPSSLClient, SSL_write failed[not connected to a SSL server.]");
return -1;
}
//OpenSSL 1.1.1
if (pData == nullptr && uSize != 0)
{
SocketLog("[ERROR]TCPSSLClient, send failed[%d][%p:%zu]", m_SSLConnectSocket.m_SockFd, pData, uSize);
return -2;
}
int total = 0;
do
{
/* encrypt & send message */
int nSent = SSL_write(m_SSLConnectSocket.m_pSSL, pData + total, (int)uSize - total);
if (nSent <= 0)
{
int iErrCode = SSL_get_error(m_SSLConnectSocket.m_pSSL, nSent);
if (iErrCode == SSL_ERROR_WANT_WRITE || iErrCode == SSL_ERROR_WANT_READ)
{
continue;
}
if (iErrCode == SSL_ERROR_ZERO_RETURN)
{
//SocketLog("[WARN ]TCPSSLClient, SSL_write SSL_shutdowned[%d:%s][%d]", iErrCode, GetSSLErrorString(iErrCode), m_SSLConnectSocket.m_SockFd);
break;
}
SocketLog("[ERROR]TCPSSLClient, SSL_write failed[%d:%s][%d]", iErrCode, GetSSLErrorString(iErrCode), m_SSLConnectSocket.m_SockFd);
return -1;
}
total += nSent;
} while (total < (int)uSize);
return total;
}
int CTCPSSLClient::Send(const std::string& strData) const
{
return Send(strData.c_str(), strData.length());
}
int CTCPSSLClient::Send(const std::vector<char>& Data) const
{
return Send(Data.data(), Data.size());
}
bool CTCPSSLClient::HasPending()
{
return m_SSLConnectSocket.HasPending();
}
int CTCPSSLClient::PendingBytes()
{
return m_SSLConnectSocket.PendingBytes();
}
void CTCPSSLClient::Disconnect()
{
// send close_notify message to notify peer of the SSL closure.
m_SSLConnectSocket.Disconnect();
//ShutdownSSL(m_SSLConnectSocket);
//m_TCPClient.Disconnect();
}
#endif