reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
Connection.cxx
Go to the documentation of this file.
1#if defined(HAVE_CONFIG_H)
2#include "config.h"
3#endif
4
5#include "rutil/ResipAssert.h"
6#include "rutil/Socket.hxx"
7#include "rutil/Logger.hxx"
14
15#ifdef USE_SSL
17#endif
18
19#ifdef WIN32
20#include <Mswsock.h>
21#endif
22
23#ifdef USE_SIGCOMP
24#include <osc/Stack.h>
25#include <osc/SigcompMessage.h>
26#endif
27
28using namespace resip;
29
31
32#define RESIPROCATE_SUBSYSTEM Subsystem::TRANSPORT
33
34Connection::Connection(Transport* transport,const Tuple& who, Socket socket,
35 Compression &compression,
36 bool isServer)
37 : ConnectionBase(transport,who,compression),
38 mFirstWriteAfterConnectedPending(false),
39 mInWritable(false),
40 mFlowTimerEnabled(false),
41 mPollItemHandle(0),
42 mIsServer(isServer)
43{
44 mWho.mFlowKey=(FlowKey)socket;
45 InfoLog (<< "Connection::Connection: new connection created to who: " << mWho << ", is server = " << mIsServer);
46
48 {
51 }
52
54 {
55 getConnectionManager().addConnection(this);
56 }
57}
58
60{
62 {
63 getConnectionManager().removeConnection(this);
64 // remove first then close, since conn manager may need socket
66 }
67}
68
69void
71{
72 mOutstandingSends.push_back(sendData);
73 if (isWritable())
74 {
76 }
77}
78
79void
80Connection::removeFrontOutstandingSend()
81{
82 delete mOutstandingSends.front();
83 mOutstandingSends.pop_front();
84
85 if (mOutstandingSends.empty())
86 {
87 resip_assert(mInWritable);
88 getConnectionManager().removeFromWritable(this);
89 mInWritable = false;
90 }
91}
92
93int
95{
96 if(transportWrite())
97 {
98 // If we get here it means:
99 // a. on a previous invocation, SSL_do_handshake wanted to write
100 // (SSL_ERROR_WANT_WRITE)
101 // b. now the handshake is complete or it wants to read
102 if(mInWritable)
103 {
104 getConnectionManager().removeFromWritable(this);
105 mInWritable = false;
106 }
107 else
108 {
109 WarningLog(<<"performWrite invoked while not in write set");
110 }
111 return 0; // Q. What does this transportWrite() mean?
112 // A. It makes the TLS handshake move along after it
113 // was waiting in the write set.
114 }
115
116 // If the TLS handshake returned SSL_ERROR_WANT_WRITE again
117 // then we could get here without really having something to write
118 // so just return, remaining in the write set.
119 if(mOutstandingSends.empty())
120 {
121 // FIXME: this needs to be more elaborate with respect
122 // to TLS handshaking but it doesn't appear we can do that
123 // without ABI breakage.
124 return 0;
125 }
126
127 switch(mOutstandingSends.front()->command)
128 {
130 // .bwc. Close this connection. A WebSocket connection sends a Close frame
131 // first: it is framed below, and the connection is closed once it is written.
133 {
134 return -1;
135 }
136 break;
139 removeFrontOutstandingSend();
140 return 0;
141 break;
144 {
145 WarningLog(<<"WebSocket Pong queued on a connection that is not a WebSocket, dropping it");
146 removeFrontOutstandingSend();
147 return 0;
148 }
149 break;
150 default:
151 // do nothing
152 break;
153 }
154
155 const Data& sigcompId = mOutstandingSends.front()->sigcompId;
156
158 {
159 if (sigcompId.size() > 0 && mCompression.isEnabled())
160 {
162 }
163 else
164 {
166 }
167 }
169 {
171 // this is the handshake response, which is HTTP and is sent as it is: marking it
172 // stops a retry after a partial write from framing the rest of it
173 mOutstandingSends.front()->isAlreadyEncoded = true;
174 }
176 !mOutstandingSends.front()->isAlreadyEncoded)
177 {
178 // Frame the data, once: it is replaced in place, so the SendData keeps its command,
179 // and marked as encoded, so a write retried after a partial write (or
180 // SSL_ERROR_WANT_WRITE) sends the same bytes instead of framing them again
181 SendData* sd = mOutstandingSends.front();
184 {
186 if(sd->data.empty())
187 {
188 // a Close we start ourselves: status 1000, normal closure
189 static const char normalClosure[2] = { 0x03, (char)0xE8 };
190 sd->data = Data(normalClosure, 2);
191 }
192 }
193 else if(sd->command == SendData::SendWsPong)
194 {
196 }
197
198 const Data& dataRaw = sd->data;
199 uint64_t dataSize = 1 + 1 + dataRaw.size();
200 uint64_t lSize = (uint64_t)dataRaw.size();
201 uint8_t* uBuffer;
202
203 if(lSize > 0x7D && lSize <= 0xFFFF)
204 {
205 dataSize += 2;
206 }
207 else if(lSize > 0xFFFF)
208 {
209 dataSize += 8;
210 }
211
212 Data framed(Data::Take, new char[(int)dataSize], (Data::size_type)dataSize);
213 resip_assert(framed.data());
214 uBuffer = (uint8_t*)framed.data();
215
216 // FIN plus the opcode
217 uBuffer[0] = (uint8_t)(0x80 | opcode);
218 if(lSize <= 0x7D)
219 {
220 uBuffer[1] = (uint8_t)lSize;
221 uBuffer = &uBuffer[2];
222 }
223 else if(lSize <= 0xFFFF)
224 {
225 uBuffer[1] = 0x7E;
226 uBuffer[2] = (uint8_t)((lSize >> 8) & 0xFF);
227 uBuffer[3] = (uint8_t)(lSize & 0xFF);
228 uBuffer = &uBuffer[4];
229 }
230 else
231 {
232 uBuffer[1] = 0x7F;
233 uBuffer[2] = (uint8_t)((lSize >> 56) & 0xFF);
234 uBuffer[3] = (uint8_t)((lSize >> 48) & 0xFF);
235 uBuffer[4] = (uint8_t)((lSize >> 40) & 0xFF);
236 uBuffer[5] = (uint8_t)((lSize >> 32) & 0xFF);
237 uBuffer[6] = (uint8_t)((lSize >> 24) & 0xFF);
238 uBuffer[7] = (uint8_t)((lSize >> 16) & 0xFF);
239 uBuffer[8] = (uint8_t)((lSize >> 8) & 0xFF);
240 uBuffer[9] = (uint8_t)(lSize & 0xFF);
241 uBuffer = &uBuffer[10];
242 }
243
244 memcpy(uBuffer, dataRaw.data(), dataRaw.size());
245 sd->data.takeBuf(framed);
246 sd->isAlreadyEncoded = true;
247 }
248
249#ifdef USE_SIGCOMP
250 // Perform compression here, if appropriate
252 && !(mOutstandingSends.front()->isAlreadyEncoded))
253 {
254 const Data& uncompressed = mOutstandingSends.front()->data;
255 osc::SigcompMessage *sm =
256 mSigcompStack->compressMessage(uncompressed.data(), uncompressed.size(),
257 sigcompId.data(), sigcompId.size(),
258 true);
259 DebugLog (<< "Compressed message from "
260 << uncompressed.size() << " bytes to "
261 << sm->getStreamLength() << " bytes");
262
263 SendData *oldSd = mOutstandingSends.front();
264 SendData *newSd = new SendData(oldSd->destination,
265 Data(sm->getStreamMessage(),
266 sm->getStreamLength()),
267 oldSd->transactionId,
268 oldSd->sigcompId,
269 true);
270 mOutstandingSends.front() = newSd;
271 delete oldSd;
272 delete sm;
273 }
274#endif
275
276 // Note: The first time the socket is available for write, is when the TCP connect call is completed
278 {
279 mFirstWriteAfterConnectedPending = false; // reset
280
281 // Notify all outstanding sends that we are now connected - stops the TCP Connection timer for all transactions
282 for (std::list<SendData*>::iterator it = mOutstandingSends.begin(); it != mOutstandingSends.end(); it++)
283 {
285 }
287 {
289 }
290 }
291
292 const Data& data = mOutstandingSends.front()->data;
293 int nBytes = write(data.data() + mSendPos,int(data.size() - mSendPos));
294
295 //DebugLog (<< "Tried to send " << data.size() - mSendPos << " bytes, sent " << nBytes << " bytes");
296
297 if (nBytes < 0)
298 {
299 //fail(data.transactionId);
300 InfoLog(<< "Write failed on socket: " << this->getSocket() << ", closing connection");
301 return -1;
302 }
303 else if (nBytes == 0)
304 {
305 // Nothing was written - likely socket buffers are backed up and EWOULDBLOCK was returned
306 // no need to do calculations in else statement
307 return 0;
308 }
309 else
310 {
311 // Safe because of the conditional above ( < 0 ).
312 Data::size_type bytesWritten = static_cast<Data::size_type>(nBytes);
313 mSendPos += bytesWritten;
314 if (mSendPos == data.size())
315 {
316 mSendPos = 0;
317 // only a WebSocket Close gets this far as a CloseConnection
318 const bool closeNow = mOutstandingSends.front()->command == SendData::CloseConnection;
319 removeFrontOutstandingSend();
320 if (closeNow)
321 {
322 DebugLog(<< "WebSocket Close sent, closing connection: " << mWho);
323 return -1;
324 }
325 }
326 return (int)bytesWritten;
327 }
328}
329
330
331bool
333{
334 int res;
335 // if max==0, we will overflow into UINT_MAX. This is intentional.
336 while((res=performWrite())>0 && !mOutstandingSends.empty() && --max!=0)
337 {;}
338
339 if(res<0)
340 {
341 // error writing, caller should ensure this connection is deleted
342 return false;
343 }
344 return true;
345}
346
347void
349{
350 if(!mInWritable)
351 {
352 //assert(!mOutstandingSends.empty()); // empty during TLS handshake
353 // therefore must be careful to check mOutstandingSends later
354 getConnectionManager().addToWritable(this);
355 mInWritable = true;
356 }
357}
358
360Connection::getConnectionManager() const
361{
363
364 return transport->getConnectionManager();
365}
366
368resip::operator<<(EncodeStream& strm, const resip::Connection& c)
369{
370 strm << "CONN: " << &c << " " << int(c.getSocket()) << " " << c.mWho;
371 return strm;
372}
373
374int
376{
377 std::pair<char*, size_t> writePair = getWriteBuffer();
378 size_t bytesToRead = resipMin(writePair.second,
379 static_cast<size_t>(Connection::ChunkSize));
380
381 resip_assert(bytesToRead > 0);
382
383 int bytesRead = read(writePair.first, (int)bytesToRead);
384 if (bytesRead <= 0)
385 {
386 return bytesRead;
387 }
388
389 getConnectionManager().touch(this);
390
391#ifdef USE_SIGCOMP
392 // If this is the first data we read, determine whether the
393 // connection is compressed.
395 {
396 // mBuffer might have been reallocated inside read()
397 writePair = getCurrentWriteBuffer();
398
399 if (((writePair.first[0] & 0xf8) == 0xf8) && mCompression.isEnabled())
400 {
402 }
403 else
404 {
406 }
407 }
408
409 // SigComp compressed messages are handed very differently
410 // than non-compressed messages: they are guaranteed to
411 // be framed within SigComp, and each frame contains
412 // *exactly* one SIP message. Processing looks a lot like
413 // it does for Datagram-oriented transports.
414
416 {
417 decompressNewBytes(bytesRead);
418 }
419 else
420#endif
421 {
423 {
424 bool dropConnection = false;
425 if(wsProcessHandshake(bytesRead, dropConnection))
426 {
428 if(performWrites())
429 {
431 }
432 else
433 {
434 // The write failed, returning -1 to the read will cause this connection to get deleted in performReads
435 bytesRead = -1;
436 }
437 }
438 else if(dropConnection)
439 {
440 bytesRead=-1;
441 }
442 }
443 else
444 {
446 {
447 if(!wsProcessData(bytesRead))
448 {
449 bytesRead=-1;
450 }
451 }
452 else if(!preparseNewBytes(bytesRead))
453 {
454 // Iffy; only way we have right now to indicate that this connection has
455 // gone away.
456 bytesRead=-1;
457 }
458 }
459 }
460 return bytesRead;
461}
462
463bool
465{
466 int bytesRead;
467
468 // if max==0, we will overflow into UINT_MAX. This is intentional.
469 while((bytesRead = read())>0 && --max!=0)
470 {
471 DebugLog(<< "Connection::performReads() " << " read=" << bytesRead);
472 }
473
474 if ( bytesRead < 0 )
475 {
476 DebugLog(<< "Closing connection bytesRead=" << bytesRead);
477 delete this;
478 return false;
479 }
480 return true;
481}
482
483void
485{
486 if(!mFlowTimerEnabled)
487 {
488 mFlowTimerEnabled = true;
489
490 // ensure connection is in a FlowTimer LRU list on the connection manager
491 getConnectionManager().moveToFlowTimerLru(this);
492 }
493}
494
495void
497{
498 // !bwc! TODO might need to make this more efficient.
499 // !bwc! Need to make this sigcomp-friendly
501 {
502 DebugLog(<<"Sending response CRLF (aka pong).");
504 }
505}
506
507void
509{
510 DebugLog(<<"Received response CRLF (aka pong).");
512}
513
514void
516{
517 // RFC 6455 section 5.5.1: answer with a Close of our own, echoing the peer's
518 // status code, then close the TCP connection (performWrite() does both). The
519 // write is queued, so anything already waiting to be sent goes out first.
520 SendData* close = new SendData(mWho,
521 payload.size() >= 2 ? Data(payload.data(), 2) : Data::Empty,
524 requestWrite(close);
525}
526
527void
529{
530 // RFC 6455 section 5.5.3: a Pong carries the Ping's payload back
531 SendData* pong = new SendData(mWho, payload, Data::Empty, Data::Empty);
533 requestWrite(pong);
534}
535
536bool
538{
539 return true;
540}
541
542bool
544{
545 return true;
546}
547
548bool
550{
551 int errNum = 0;
552 int errNumSize = sizeof(errNum);
553 if(getsockopt(mWho.mFlowKey, SOL_SOCKET, SO_ERROR, (char *)&errNum, (socklen_t *)&errNumSize) == 0)
554 {
555 if (errNum == ETIMEDOUT || errNum == EHOSTUNREACH ||
556 errNum == ECONNREFUSED || errNum == ECONNABORTED)
557 {
558 Data failureString;
559 {
560 DataStream ds(failureString);
561 ds << "Exception on socket " << mWho.mFlowKey << ", errNum=" << errNum << ", err=" << Transport::errorToString(errNum);
562 }
563 InfoLog(<< failureString << "; closing connection");
565 delete this;
566 return true;
567 }
568 else if (errNum != 0)
569 {
570 WarningLog(<< "checkConnectionTimedout " << mWho.mFlowKey << " code: " << errNum << "; ignoring - should we error out?");
571 }
572 }
573 return false;
574}
575
576bool
578{
579 return true;
580}
581
585void
587{
588 /* The original code in ConnectionManager.cxx didn't check
589 * for error events unless no writable event. (e.g., writable
590 * masked error. Why?)
591 */
592 if ( mask & FPEM_Error )
593 {
594 Socket fd = getSocket();
595 int errNum = getSocketError(fd);
596 Data failureString;
597 {
598 DataStream ds(failureString);
599 ds << "Exception on socket " << fd << ", errNum=" << errNum << ", err=" << Transport::errorToString(errNum);
600 }
601
602 InfoLog(<< failureString << "; closing connection");
604 delete this;
605 return;
606 }
607 if ( mask & FPEM_Write )
608 {
609 if(!performWrites())
610 {
611 delete this;
612 return;
613 }
614 }
615 if ( mask & FPEM_Read )
616 {
617 if (!performReads())
618 {
619 // Just deleted self
620 return;
621 }
622 }
624}
625
626bool
628{
629 return mIsServer;
630}
631
632void
637
638/* ====================================================================
639 * The Vovida Software License, Version 1.0
640 *
641 * Copyright (c) 2026 SIP Spectrum, Inc. https://www.sipspectrum.com
642 * Copyright (c) 2000
643 *
644 * Redistribution and use in source and binary forms, with or without
645 * modification, are permitted provided that the following conditions
646 * are met:
647 *
648 * 1. Redistributions of source code must retain the above copyright
649 * notice, this list of conditions and the following disclaimer.
650 *
651 * 2. Redistributions in binary form must reproduce the above copyright
652 * notice, this list of conditions and the following disclaimer in
653 * the documentation and/or other materials provided with the
654 * distribution.
655 *
656 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
657 * and "Vovida Open Communication Application Library (VOCAL)" must
658 * not be used to endorse or promote products derived from this
659 * software without prior written permission. For written
660 * permission, please contact vocal@vovida.org.
661 *
662 * 4. Products derived from this software may not be called "VOCAL", nor
663 * may "VOCAL" appear in their name, without prior written
664 * permission of Vovida Networks, Inc.
665 *
666 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
667 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
668 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
669 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
670 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
671 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
672 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
673 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
674 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
675 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
676 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
677 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
678 * DAMAGE.
679 *
680 * ====================================================================
681 *
682 * This software consists of voluntary contributions made by Vovida
683 * Networks, Inc. and many individuals on behalf of Vovida Networks,
684 * Inc. For more information on Vovida Networks, Inc., please see
685 * <http://www.vovida.org/>.
686 *
687 * vi: set shiftwidth=3 expandtab:
688 */
#define FPEM_Write
Definition FdPoll.hxx:30
#define FPEM_Read
Definition FdPoll.hxx:29
#define FPEM_Error
Definition FdPoll.hxx:31
#define max(x, y)
#define DebugLog(args_)
Definition Logger.hxx:128
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define resip_assert(x)
This file needs to work for both C and C++ code.
Handles cross-platform sockets compatibility.
virtual Transport * transport() const
void setFailureReason(TransportFailure::FailureReason failReason, int subCode, const Data &failureString)
TransmissionFormat mSendingTransmissionFormat
bool wsProcessData(int bytesRead)
std::pair< char *, size_t > getWriteBuffer()
bool wsProcessHandshake(int bytesRead, bool &dropConnection)
void decompressNewBytes(int bytesRead)
TransmissionFormat mReceivingTransmissionFormat
std::pair< char *, size_t > getCurrentWriteBuffer()
bool preparseNewBytes(int bytesRead)
std::list< SendData * > mOutstandingSends
Data::size_type mSendPos
Collection of Connection per Transport.
Connection implements, via sockets, ConnectionBase for managed connections.
Socket getSocket() const
int performWrite()
send some or all of a queued data; remove from writable if completely written
bool performWrites(unsigned int max=0)
Call performWrite() repeatedly, until either the send queue is exhausted, the write() call fails (pro...
virtual void onWsClose(const Data &payload)
virtual bool checkConnectionTimedout()
virtual ~Connection()
virtual bool hasDataToRead()
always true – always add to fdset as read ready
virtual void onWsPing(const Data &payload)
bool mFirstWriteAfterConnectedPending
virtual bool isGood()
has valid connection
virtual bool isWritable()
virtual void onDoubleCRLF()
void enableFlowTimer()
Ensures this connection is in the FlowTimer LRU list in the connection manager.
virtual void invokeAfterSocketCreationFunc() const
bool isServer() const
void requestWrite(SendData *sendData)
queue data to write and add this to writable list
virtual void onSingleCRLF()
static volatile bool mEnablePostConnectSocketFuncCall
virtual void processPollEvent(FdPollEventMask mask)
Virtual function of FdPollItemIf, called to process io events.
bool performReads(unsigned int max=0)
Call read() repeatedly, until an error occurs (because the fd is not ready to read,...
virtual int write(const char *, const int)
pure virtual, but need concrete Connection for book-ends of lists
void ensureWritable()
ensure that we are on the writeable list if required
virtual bool transportWrite()
int read()
move data from the connection to the buffer; move this to front of least recently used list.
An iostream that operates on an existing Data.
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
@ Take
The Data instance takes complete ownership of the buffer.
Definition Data.hxx:272
Data & takeBuf(Data &other) noexcept
Take the data from {other}.
Definition Data.cxx:641
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
size_t size_type
Definition Data.hxx:94
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 int getOutboundVersion()
bool isAlreadyEncoded
Definition SendData.hxx:77
SendDataCommand command
Definition SendData.hxx:80
static const char * CRLF
Definition Symbols.hxx:21
The base class for Transport classes.
Definition Transport.hxx:83
const TransportType transport() const
void flushStateMacFifo()
static Data errorToString(int e)
void applySocketOptions(Socket sock) const
The one place a socket of this transport is configured.
void keepAlivePong(const Tuple &flow)
void setTcpConnectState(const Data &tid, TcpConnectState::State state)
Posts a TcpConnectState to the TransactionMessage Fifo.
Represents a network address.
Definition Tuple.hxx:59
FlowKey mFlowKey
This is a (largely) opaque key that subclasses of Transport will use to help record/find flows.
Definition Tuple.hxx:189
static const uint8_t OpClose
static const uint8_t OpBinary
static const uint8_t OpPong
dcm! – add UnusedChecking(_enum) below;
const _Tp & resipMin(const _Tp &__a, const _Tp &__b)
Definition compat.hxx:182
unsigned long FlowKey
Definition Tuple.hxx:37
unsigned short FdPollEventMask
Definition FdPoll.hxx:28
int getSocketError(Socket fd)
Definition Socket.cxx:148
bool isWebSocket(TransportType type)
Returns true if passed in transport type is a WebSocket transport protocol.
int Socket
Definition Socket.hxx:94
unsigned long long uint64_t
Definition Stun.hxx:39
int closeSocket(Socket fd)
Definition Socket.cxx:134
EncodeStream & operator<<(EncodeStream &strm, const BaseException &e)
unsigned char uint8_t
Definition Stun.hxx:29
#define EncodeStream