reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
DtlsTransport.cxx
Go to the documentation of this file.
1
2#if defined(HAVE_CONFIG_H)
3 #include "config.h"
4#endif
5
6#ifdef USE_SSL
7#ifdef USE_DTLS
8
9#include <memory>
10
11#ifndef RESIP_COMPAT_HXX
12#include "rutil/compat.hxx"
13#endif
14
15#ifndef RESIP_DATA_HXX
16#include "rutil/Data.hxx"
17#endif
18
19#ifndef RESIP_DNSUTIL_HXX
20#include "rutil/DnsUtil.hxx"
21#endif
22
23#ifndef RESIP_SOCKET_HXX
24#include "rutil/Socket.hxx"
25#endif
26
27#ifndef RESIP_LOGGER_HXX
28#include "rutil/Logger.hxx"
29#endif
30
31#ifndef RESIP_SIPMESSAGE_HXX
33#endif
34
35#ifndef RESIP_HELPER_HXX
37#endif
38
39#ifndef RESIP_SECURITY_HXX
41#endif
42
43#ifndef RESIP_DTLSMESSAGE_HXX
45#endif
46
47#ifndef RESIP_DTLSTRANSPORT_HXX
49#endif
50
52
53#include <openssl/opensslv.h>
54#if !defined(LIBRESSL_VERSION_NUMBER)
55#include <openssl/e_os2.h>
56#endif
57#include <openssl/evp.h>
58#include <openssl/crypto.h>
59#include <openssl/err.h>
60#include <openssl/pem.h>
61#include <openssl/pkcs7.h>
62#include <openssl/x509v3.h>
63#include <openssl/ssl.h>
64
65#ifdef USE_SIGCOMP
66#include <osc/Stack.h>
67#include <osc/StateChanges.h>
68#include <osc/SigcompMessage.h>
69#endif
70
71#define RESIPROCATE_SUBSYSTEM Subsystem::TRANSPORT
72
73using namespace std;
74using namespace resip;
75
76// .slg. Note that DTLS handshakes were really broken in older versions of OpenSSL 0.9.8
77// You should use at least version 0.9.8g for both client and server.
78
80 int portNum,
81 IpVersion version,
82 const Data& interfaceObj,
83 Security& security,
84 const Data& sipDomain,
86 Compression& compression,
87 const Data& certificateFilename,
88 const Data& privateKeyFilename,
89 const Data& privateKeyPassPhrase)
90 : UdpTransport(fifo, portNum, version, StunDisabled, interfaceObj, socketFunc, compression),
91 mTimer(mHandshakePending),
92 mSecurity(&security),
93 mDomain(sipDomain)
94{
95 // Note on AfterSocketCreateFuncPtr: because this class uses UdpTransport the bind operation
96 // is called in the UdpTransport constructor and the transport type passed to AfterSocketCreationFuncPtr
97 // will end up being UDP and not DTLS. TODO - this should be fixed. Creating a UdpBaseTransport is one
98 // solution that would align with the TCP flavour of transports.
99
100 setTlsDomain(sipDomain);
101 InfoLog(<< "Creating DTLS transport host=" << interfaceObj
102 << " port=" << mTuple.getPort()
103 << " ipv4=" << version);
104
105 mTxFifo.setDescription("DtlsTransport::mTxFifo");
106
108
109 mClientCtx = mSecurity->createDomainCtx(DTLS_client_method(), Data::Empty, certificateFilename, privateKeyFilename, privateKeyPassPhrase);
110 mServerCtx = mSecurity->createDomainCtx(DTLS_server_method(), sipDomain, certificateFilename, privateKeyFilename, privateKeyPassPhrase);
111 resip_assert(mClientCtx);
112 resip_assert(mServerCtx);
113
114 mDummyBio = BIO_new(BIO_s_mem());
115 resip_assert(mDummyBio);
116
117 mSendData = nullptr;
118
119 /* DTLS: partial reads end up discarding unread UDP bytes :-(
120 * Setting read ahead solves this problem.
121 * Source of this comment is: apps/s_client.c from OpenSSL source
122 */
123 SSL_CTX_set_read_ahead(mClientCtx, 1);
124 SSL_CTX_set_read_ahead(mServerCtx, 1);
125
126 /* trying to read from this BIO always returns retry */
127 BIO_set_mem_eof_return(mDummyBio, -1);
128}
129
131{
132 DebugLog(<< "Shutting down " << mTuple);
133
134 while (mDtlsConnections.begin() != mDtlsConnections.end())
135 {
136 _cleanupConnectionState(mDtlsConnections.begin()->second, mDtlsConnections.begin()->first);
137 }
138 SSL_CTX_free(mClientCtx); mClientCtx = nullptr;
139 SSL_CTX_free(mServerCtx); mServerCtx = nullptr;
140
141 BIO_free(mDummyBio);
142}
143
144void
145DtlsTransport::_read()
146{
147 Tuple sender(mTuple);
148 auto len = processRxRecv(sender);
149 if (len <= 0)
150 {
151 return;
152 }
153
154 SSL* ssl;
155 BIO* rbio;
156 BIO* wbio;
157
158 /* begin SSL stuff */
159 struct sockaddr peer = sender.getMutableSockaddr();
160
161 ssl = mDtlsConnections[*((struct sockaddr_in*)&peer)];
162
163 /*
164 * If we don't have a binding for this peer,
165 * then we're a server.
166 */
167 if (ssl == nullptr)
168 {
169 ssl = SSL_new(mServerCtx);
170 resip_assert(ssl);
171
172 // clear SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE set in SSL_CTX if we are a server
173 SSL_set_verify(ssl, 0, nullptr);
174
175 InfoLog(<< "DTLS handshake starting (Server mode)");
176
177 SSL_set_accept_state(ssl);
178
179 wbio = BIO_new_dgram((int)mFd, BIO_NOCLOSE);
180 resip_assert(wbio);
181
182 BIO_dgram_set_peer(wbio, &peer);
183
184 SSL_set_bio(ssl, nullptr, wbio);
185
186 /* remember this connection */
187 mDtlsConnections[*((struct sockaddr_in*)&peer)] = ssl;
188 }
189
190 rbio = BIO_new_mem_buf(mRxBuffer.data(), len);
191 BIO_set_mem_eof_return(rbio, -1);
192
193 SSL_set0_rbio(ssl, rbio);
194
195 // SSL_get_error() checks this thread's error queue first, so it must be empty before the
196 // call: an error something else on this thread left there would otherwise turn a
197 // retryable SSL_ERROR_WANT_READ into SSL_ERROR_SSL
198 ERR_clear_error();
199 len = SSL_read(ssl, mRxSslBuffer.data(), static_cast<int>(mRxSslBuffer.size()));
200 int err = SSL_get_error(ssl, len);
201
202 /* done with the rbio */
203 SSL_set0_rbio(ssl, mDummyBio);
204
205 if (len <= 0)
206 {
207 char errorString[1024];
208
209 switch (err)
210 {
211 case SSL_ERROR_NONE:
212 break;
213 case SSL_ERROR_SSL:
214 {
215 ERR_error_string_n(ERR_get_error(), errorString, sizeof(errorString));
216 DebugLog(<< "Got DTLS read condition SSL_ERROR_SSL on"
217 << " addr = " << DnsUtil::inet_ntop(((struct sockaddr_in*)&peer)->sin_addr)
218 << " port = " << ntohs(((struct sockaddr_in*)&peer)->sin_port)
219 << " error = " << errorString);
220 }
221 break;
222 case SSL_ERROR_WANT_READ:
223 break;
224 case SSL_ERROR_WANT_WRITE:
225 break;
226 case SSL_ERROR_SYSCALL:
227 {
228 ERR_error_string_n(ERR_get_error(), errorString, sizeof(errorString));
229 DebugLog(<< "Got DTLS read condition SSL_ERROR_SYSCALL on"
230 << " addr = " << DnsUtil::inet_ntop(((struct sockaddr_in*)&peer)->sin_addr)
231 << " port = " << ntohs(((struct sockaddr_in*)&peer)->sin_port)
232 << " error = " << errorString);
233 }
234 break;
235 /* connection closed */
236 case SSL_ERROR_ZERO_RETURN:
237 {
238 ERR_error_string_n(ERR_get_error(), errorString, sizeof(errorString));
239 DebugLog(<< "Got DTLS read condition SSL_ERROR_ZERO_RETURN on"
240 << " addr = " << DnsUtil::inet_ntop(((struct sockaddr_in*)&peer)->sin_addr)
241 << " port = " << ntohs(((struct sockaddr_in*)&peer)->sin_port)
242 << " error = " << errorString);
243
244 _cleanupConnectionState(ssl, *((struct sockaddr_in*)&peer));
245 }
246 break;
247 case SSL_ERROR_WANT_CONNECT:
248 break;
249 case SSL_ERROR_WANT_ACCEPT:
250 break;
251 default:
252 break;
253 }
254 }
255
256 if (len <= 0)
257 {
258 return;
259 }
260
261 if (SSL_in_init(ssl))
262 {
263 mTimer.add(ssl, DtlsReceiveTimeout);
264 }
265
266 processRxParseSip(mRxSslBuffer.data(), len, sender);
267}
268
269void DtlsTransport::_write(FdSet& fdset)
270{
271 SSL* ssl;
272 BIO* wBio;
273 int retry = 0;
274
275 SendData* sendData;
276 if (mSendData != nullptr)
277 sendData = mSendData;
278 else
279 sendData = mTxFifo.getNext();
280
281 //DebugLog (<< "Sent: " << sendData->data);
282 //DebugLog (<< "Sending message on udp.");
283
284 resip_assert(&(*sendData));
285 resip_assert(sendData->destination.getPort() != 0);
286
287 sockaddr peer = sendData->destination.getSockaddr();
288
289 ssl = mDtlsConnections[*((struct sockaddr_in*)&peer)];
290
291 /* If we don't have a binding, then we're a client */
292 if (ssl == nullptr)
293 {
294 ssl = SSL_new(mClientCtx);
295 resip_assert(ssl);
296
297 InfoLog(<< "DTLS handshake starting (client mode)");
298
299 SSL_set_connect_state(ssl);
300
301 wBio = BIO_new_dgram((int)mFd, BIO_NOCLOSE);
302 resip_assert(wBio);
303
304 BIO_dgram_set_peer(wBio, &peer);
305
306 /* the real rbio will be set by _read */
307 SSL_set_bio(ssl, mDummyBio, wBio);
308
309 /* we should be ready to take this out if the
310 * connection fails later */
311 mDtlsConnections[*((struct sockaddr_in*)&peer)] = ssl;
312 }
313
314 int expected;
315 int count;
316
317 ERR_clear_error(); // SSL_get_error() needs an empty error queue - see _read()
318
319#ifdef USE_SIGCOMP
320 // If message needs to be compressed, compress it here.
321 if (mSigcompStack &&
322 sendData->sigcompId.size() > 0 &&
323 !sendData->isAlreadyEncoded)
324 {
325 osc::SigcompMessage* sm = mSigcompStack->compressMessage
326 (sendData->data.data(), sendData->data.size(),
327 sendData->sigcompId.data(), sendData->sigcompId.size(),
328 isReliable());
329
330 DebugLog(<< "Compressed message from "
331 << sendData->data.size() << " bytes to "
332 << sm->getDatagramLength() << " bytes");
333
334 expected = sm->getDatagramLength();
335
336 count = SSL_write(ssl,
337 sm->getDatagramMessage(),
338 sm->getDatagramLength());
339 delete sm;
340 }
341 else
342#endif
343 {
344 expected = (int)sendData->data.size();
345
346 count = SSL_write(ssl, sendData->data.data(),
347 (int)sendData->data.size());
348
349 if (count < expected)
350 {
351 WarningLog(<< "expected = " << expected << " but SSL_write only sent " << count);
352 }
353 }
354
355 /*
356 * all reads go through _read, so the most likely result during a handshake
357 * will be SSL_ERROR_WANT_READ
358 */
359
360 if (count <= 0)
361 {
362 /* cache unqueued data */
363 mSendData = sendData;
364
365 int err = SSL_get_error(ssl, count);
366
367 char errorString[1024];
368
369 switch (err)
370 {
371 case SSL_ERROR_NONE:
372 break;
373 case SSL_ERROR_SSL:
374 {
375 ERR_error_string_n(ERR_get_error(), errorString, sizeof(errorString));
376 DebugLog(<< "Got DTLS write condition SSL_ERROR_SSL on "
377 << sendData->destination
378 << " error = " << errorString);
379 }
380 break;
381 case SSL_ERROR_WANT_READ:
382 retry = 1;
383 break;
384 case SSL_ERROR_WANT_WRITE:
385 retry = 1;
386 fdset.setWrite(mFd);
387 break;
388 case SSL_ERROR_SYSCALL:
389 {
390 int e = getErrno();
391 error(e);
392
393 ERR_error_string_n(ERR_get_error(), errorString, sizeof(errorString));
394 DebugLog(<< "Got DTLS write condition SSL_ERROR_SYSCALL "
395 << "Failed (" << e << ") sending to "
396 << sendData->destination
397 << " error = " << errorString);
398
399 fail(sendData->transactionId);
400 }
401 break;
402 case SSL_ERROR_ZERO_RETURN:
403 {
404 ERR_error_string_n(ERR_get_error(), errorString, sizeof(errorString));
405 DebugLog(<< "Got DTLS write condition SSL_ERROR_ZERO_RETURN on "
406 << sendData->destination
407 << " error = " << errorString);
408
409 _cleanupConnectionState(ssl, *((struct sockaddr_in*)&peer));
410 }
411 break;
412 case SSL_ERROR_WANT_CONNECT:
413 break;
414 case SSL_ERROR_WANT_ACCEPT:
415 break;
416 default:
417 break;
418 }
419 }
420 else
421 {
422 mSendData = nullptr;
423 }
424
425 /*
426 * ngm: is sendData deleted by a higher layer? Seems to be the case after
427 * checking with UdpTransport
428 */
429
430 if (!retry && count != int(sendData->data.size()))
431 {
432 ErrLog(<< "UDPTransport - send buffer full");
433 fail(sendData->transactionId);
434 }
435}
436
437void
438DtlsTransport::_doHandshake(void)
439{
440 DtlsMessage* msg = mHandshakePending.getNext();
441 SSL* ssl = msg->getSsl();
442
443 delete msg;
444
445 ERR_clear_error();
446
447 int ret = SSL_do_handshake(ssl);
448
449 if (ret <= 0) {
450 int err = SSL_get_error(ssl, ret);
451
452 char errorString[1024];
453
454 switch (err)
455 {
456 case SSL_ERROR_NONE:
457 break;
458 case SSL_ERROR_SSL:
459 {
460 ERR_error_string_n(ERR_get_error(), errorString, sizeof(errorString));
461 DebugLog(<< "Got DTLS handshake code SSL_ERROR_SSL"
462 << " error = " << errorString);
463 }
464 break;
465 case SSL_ERROR_WANT_READ:
466 break;
467 case SSL_ERROR_WANT_WRITE:
468 break;
469 case SSL_ERROR_SYSCALL:
470 {
471 ERR_error_string_n(ERR_get_error(), errorString, sizeof(errorString));
472 DebugLog(<< "Got DTLS handshake code SSL_ERROR_SYSCALL"
473 << " error = " << errorString);
474 }
475 break;
476 case SSL_ERROR_ZERO_RETURN:
477 {
478 ERR_error_string_n(ERR_get_error(), errorString, sizeof(errorString));
479 DebugLog(<< "Got DTLS handshake code SSL_ERROR_ZERO_RETURN"
480 << " error = " << errorString);
481 }
482 break;
483 case SSL_ERROR_WANT_CONNECT:
484 break;
485 case SSL_ERROR_WANT_ACCEPT:
486 break;
487 default:
488 break;
489 }
490 }
491}
492
493void
495{
497
498 // pull buffers to send out of TxFifo
499 // receive datagrams from fd
500 // preparse and stuff into RxFifo
501
502 mTimer.process();
503
504 while (mHandshakePending.messageAvailable())
505 _doHandshake();
506
507 if ((mSendData != nullptr || mTxFifo.messageAvailable())
508 && fdset.readyToWrite(mFd))
509 _write(fdset);
510
511 // !jf! this may have to change - when we read a message that is too big
512 if (fdset.readyToRead(mFd))
513 {
514 _read();
515 }
516}
517
518void
520{
521 fdset.setRead(mFd);
522
523 if (mSendData != nullptr || mTxFifo.messageAvailable())
524 {
525 fdset.setWrite(mFd);
526 }
527}
528
529void
530DtlsTransport::_cleanupConnectionState(SSL* ssl, struct sockaddr_in peer)
531{
532 /*
533 * SSL_free decrements the ref-count for mDummyBio by 1, so
534 * add 1 to the ref-count to make sure it does not get free'd
535 */
536 BIO_up_ref(mDummyBio);
537 SSL_shutdown(ssl);
538 SSL_free(ssl);
539 mDtlsConnections.erase(peer);
540}
541
542void
543DtlsTransport::_mapDebug(const char* where, const char* action, SSL* ssl)
544{
545 fprintf(stderr, "%s: %s\t%p\n", where, action, ssl);
546 fprintf(stderr, "map sizet = %d\n", (unsigned int)mDtlsConnections.size());
547}
548
549void
550DtlsTransport::_printSock(const struct sockaddr_in* sock)
551{
552 fprintf(stderr, "addr = %s\t port = %d\n", DnsUtil::inet_ntop(sock->sin_addr).c_str(),
553 ntohs(sock->sin_port));
554}
555
556#endif /* USE_DTLS */
557#endif /* USE_SSL */
558
559/* ====================================================================
560 * The Vovida Software License, Version 1.0
561 *
562 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
563 *
564 * Redistribution and use in source and binary forms, with or without
565 * modification, are permitted provided that the following conditions
566 * are met:
567 *
568 * 1. Redistributions of source code must retain the above copyright
569 * notice, this list of conditions and the following disclaimer.
570 *
571 * 2. Redistributions in binary form must reproduce the above copyright
572 * notice, this list of conditions and the following disclaimer in
573 * the documentation and/or other materials provided with the
574 * distribution.
575 *
576 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
577 * and "Vovida Open Communication Application Library (VOCAL)" must
578 * not be used to endorse or promote products derived from this
579 * software without prior written permission. For written
580 * permission, please contact vocal@vovida.org.
581 *
582 * 4. Products derived from this software may not be called "VOCAL", nor
583 * may "VOCAL" appear in their name, without prior written
584 * permission of Vovida Networks, Inc.
585 *
586 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
587 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
588 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
589 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
590 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
591 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
592 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
593 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
594 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
595 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
596 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
597 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
598 * DAMAGE.
599 *
600 * ====================================================================
601 *
602 * This software consists of voluntary contributions made by Vovida
603 * Networks, Inc. and many individuals on behalf of Vovida Networks,
604 * Inc. For more information on Vovida Networks, Inc., please see
605 * <http://www.vovida.org/>.
606 *
607 */
struct ssl_st SSL
#define DebugLog(args_)
Definition Logger.hxx:128
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
Handles cross-platform sockets compatibility.
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
static const Data Empty
Constant that represents a zero-length data.
Definition Data.hxx:882
const char * c_str() const
Returns a null-terminated string representing.
Definition Data.cxx:1097
size_type size() const noexcept
Returns the number of bytes in this Data.
Definition Data.hxx:599
const char * data() const noexcept
Returns a pointer to the contents of this Data.
Definition Data.hxx:608
static Data inet_ntop(const struct in_addr &addr)
Converts from the network format to presentation format.
Definition DnsUtil.cxx:240
uint64_t add(SSL *, unsigned long msOffset)
static const unsigned long DtlsReceiveTimeout
virtual void buildFdSet(FdSet &fdset)
Adds the Transport's socket FD to the appropriate read or write sets as applicable.
DtlsTransport(Fifo< TransactionMessage > &fifo, int portNum, IpVersion version, const Data &interfaceObj, Security &security, const Data &sipDomain, AfterSocketCreationFuncPtr socketFunc=0, Compression &compression=Compression::Disabled, const Data &certificateFilename="", const Data &privateKeyFilename="", const Data &privateKeyPassPhrase="")
Object-oriented wrapper for your platform's file-descriptor set.
Definition Socket.hxx:162
bool readyToWrite(Socket fd)
Definition Socket.hxx:194
bool readyToRead(Socket fd)
Definition Socket.hxx:189
void setRead(Socket fd)
Definition Socket.hxx:204
void setWrite(Socket fd)
Definition Socket.hxx:216
A templated, threadsafe message-queue class.
Definition Fifo.hxx:16
virtual SSL_CTX * createDomainCtx(const SSL_METHOD *method, const Data &domain, const Data &certificateFilename, const Data &privateKeyFilename, const Data &privateKeyPassPhrase)
Throws BaseSecurity::Exception if the certificate or private key can't be loaded; nothing is left all...
Definition Security.cxx:407
bool isAlreadyEncoded
Definition SendData.hxx:77
virtual uint64_t process()
gets the set of timers that have fired, and inserts TimerMsg into the state machine fifo and applicat...
void drainDscpRefresh()
Serves a pending refresh and a pending report.
void setTlsDomain(const Data &domain)
void fail(const Data &tid, TransportFailure::FailureReason reason=TransportFailure::Failure, int subCode=0)
Posts a TransportFailure to the TransactionMessage Fifo.
virtual void process()=0
Version of process to be invoked periodically when using callback-based IO (via FdPollGrp).
static void error(int e)
Generates a generic log for the platform specific socket error number.
Represents a network address.
Definition Tuple.hxx:59
void setType(TransportType type)
Definition Tuple.hxx:123
int getPort() const
Definition Tuple.cxx:466
const sockaddr & getSockaddr() const
Retrieve a const binary representation of the socket address for this tuple.
Definition Tuple.hxx:109
A Transport based on UDP.
osc::Stack * mSigcompStack
int processRxRecv(Tuple &sender)
std::array< char, MaxMessageSize > mRxBuffer
void processRxParseSip(char *buffer, int len, const Tuple &sender)
This file is used to handle compatibility fixes/tweaks so reSIProcate can function on multiple platfo...
dcm! – add UnusedChecking(_enum) below;
void(* AfterSocketCreationFuncPtr)(Socket s, int transportType, const char *file, int line)
Definition Socket.hxx:107
int getErrno()
Definition Socket.hxx:97
IpVersion
An enumeration of IP versions.