reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
TcpBaseTransport.cxx
Go to the documentation of this file.
1#if defined(HAVE_CONFIG_H)
2#include "config.h"
3#endif
4
5#include <memory>
6#include "rutil/compat.hxx"
7#include "rutil/Socket.hxx"
8#include "rutil/Data.hxx"
9#include "rutil/DnsUtil.hxx"
10#include "rutil/Logger.hxx"
11#include "rutil/NetNs.hxx"
13
14#define RESIPROCATE_SUBSYSTEM Subsystem::TRANSPORT
15
16using namespace std;
17using namespace resip;
18
19
20const size_t TcpBaseTransport::MaxWriteSize = 4096;
21const size_t TcpBaseTransport::MaxReadSize = 4096;
22
24 int portNum, IpVersion version,
25 const Data& pinterface,
27 Compression &compression,
28 unsigned transportFlags,
29 const Data& netNs)
30 : InternalTransport(fifo, portNum, version, pinterface, socketFunc, compression, transportFlags, netNs)
31{
33 {
34#ifdef USE_NETNS
35 DebugLog(<< "TcpBaseTransport: " << this << " netns: " << netNs);
36 // setns here
37 NetNs::setNs(netNs);
38#endif
40 }
41}
42
43
45{
46 //DebugLog (<< "Shutting down TCP Transport " << this << " " << mFd << " " << mInterface << ":" << port());
47
48 // !jf! this is not right. should drain the sends before
49 while (mTxFifoOutBuffer.messageAvailable())
50 {
51 SendData* data = mTxFifoOutBuffer.getNext();
52 InfoLog (<< "Throwing away queued data for " << data->destination);
53
55 delete data;
56 }
57 DebugLog (<< "Shutting down " << mTuple);
58 //mSendRoundRobin.clear(); // clear before we delete the connections
60 {
63 }
64}
65
66// called from constructor of TcpTransport
67void
69{
71 {
72 return;
73 }
74
75 //DebugLog (<< "Opening TCP " << mFd << " : " << this);
76
77 int on = 1;
78#if !defined(WIN32)
80 {
81 if (::setsockopt(mFd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)))
82 {
83 int e = getErrno();
84 InfoLog(<< "Couldn't set sockoptions SO_REUSEPORT: " << strError(e));
85 error(e);
86 throw Exception("Failed setsockopt", __FILE__, __LINE__);
87 }
88 }
89 if ( ::setsockopt ( mFd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) )
90#else
91 if ( ::setsockopt ( mFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) )
92#endif
93 {
94 int e = getErrno();
95 InfoLog (<< "Couldn't set sockoptions SO_REUSEADDR: " << strError(e));
96 error(e);
97 throw Exception("Failed setsockopt", __FILE__,__LINE__);
98 }
99
100 bind();
102
103 // do the listen, seting the maximum queue size for compeletly established
104 // sockets -- on linux, tcp_max_syn_backlog should be used for the incomplete
105 // queue size(see man listen)
106 int e = listen(mFd,64 );
107
108 if (e != 0 )
109 {
110 int e = getErrno();
111 InfoLog (<< "Failed listen " << strError(e));
112 error(e);
113 // !cj! deal with errors
114 throw Transport::Exception("Address already in use", __FILE__,__LINE__);
115 }
116}
117
118// ?kw?: when should this be called relative to init() above? merge?
119void
121{
123 {
126 }
127
128 if ( mFd!=INVALID_SOCKET && grp)
129 {
131 // above released by InternalTransport destructor
132 // ?bwc? Is this really a good idea? If the InternalTransport d'tor is
133 // freeing this, shouldn't InternalTransport::setPollGrp() handle
134 // creating it?
135 }
136 mConnectionManager.setPollGrp(grp);
137
139}
140
141void
143{
144 resip_assert( mPollGrp==NULL );
145 mConnectionManager.buildFdSet(fdset);
146 if ( mFd!=INVALID_SOCKET )
147 {
148 fdset.setRead(mFd); // for the transport itself (accept)
149 }
151 {
153 }
154}
155
160int
162{
163 if (1)
164 {
165 Tuple tuple(mTuple);
166 struct sockaddr& peer = tuple.getMutableSockaddr();
167 socklen_t peerLen = tuple.length();
168 Socket sock = accept( mFd, &peer, &peerLen);
169 if ( sock == SOCKET_ERROR )
170 {
171 int e = getErrno();
172 switch (e)
173 {
174 case EAGAIN:
175#if EAGAIN != EWOULDBLOCK
176 case EWOULDBLOCK: // Treat EGAIN and EWOULDBLOCK as the same: http://stackoverflow.com/questions/7003234/which-systems-define-eagain-and-ewouldblock-as-different-values
177#endif
178 // !jf! this can not be ready in some cases
179 // !kw! this will happen every epoll cycle
180 return 0;
181 default:
183 }
184 return -1;
185 }
186 if(!configureConnectedSocket(sock))
187 {
188 closeSocket(sock);
189 throw Exception("Failed to configure connected socket", __FILE__,__LINE__);
190 }
192
193 DebugLog (<< this << " Received TCP connection from: " << tuple << " mTuple: " << mTuple << " as fd=" << sock);
194
195 applySocketOptions(sock);
196
197 Connection* c = mConnectionManager.findConnection(tuple);
198 if(!c)
199 {
200 createConnection(tuple, sock, true);
201 }
202 else if(false == c->isServer())
203 {
204 InfoLog( << "Have client connection for " << tuple << ", but got server one, recreate connection" );
205 delete c;
206 createConnection(tuple, sock, true);
207 }
208 else
209 {
210 InfoLog(<<"Someone probably sent a reciprocal SYN at us.");
211 // ?bwc? Can we call this right after calling accept()?
212 closeSocket(sock);
213 }
214 }
215 return 1;
216}
217
218bool
219TcpBaseTransport::bindClientSocket(Socket sock,
220 TransportFailure::FailureReason &failReason, int &failSubCode)
221{
222 char _sa[RESIP_MAX_SOCKADDR_SIZE];
223 sockaddr *sa = reinterpret_cast<sockaddr*>(_sa);
225
226 // Check if SYMMETRIC_CONNECTIONS flag is set
228 {
229 // Symmetric connections mode: bind to the same port as the listening transport
230 // This ensures TCP source port matches the advertised SIP port in Via/Contact headers
231
232 // Enable SO_REUSEADDR to allow binding to the same port as the listening socket
233 int on = 1;
234#if !defined(WIN32)
235 if (::setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)))
236 {
237 int err = getErrno();
238 WarningLog(<< "Failed to set SO_REUSEPORT on client socket: " << strError(err));
239 }
240 if (::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
241#else
242 if (::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)))
243#endif
244 {
245 int err = getErrno();
246 WarningLog(<< "Failed to set SO_REUSEADDR on client socket: " << strError(err));
247 // Continue anyway - this is not fatal
248 }
249
250 // Bind to the same port as the listening transport (not ephemeral port)
251 memcpy(sa, &mTuple.getSockaddr(), mTuple.length());
252
253 DebugLog(<< "Symmetric connections mode: binding client socket to " << mTuple);
254 }
255 else
256 {
257 // Default behavior: use ephemeral port (port = 0)
259
260 DebugLog(<< "Default mode: binding client socket with ephemeral port: " << mTuple);
261 }
262
263#ifdef USE_NETNS
264 NetNs::setNs(netNs());
265#endif
266 if(::bind(sock, sa, mTuple.length()) != 0)
267 {
268 int err = getErrno();
269 WarningLog( << "Error in binding to source interface address: " << strError(err));
270 failReason = TransportFailure::Failure;
271 failSubCode = err;
272 return false;
273 }
274
275 return true;
276}
277
280 TransportFailure::FailureReason &failReason, int &failSubCode)
281{
282 // attempt to open
283#ifdef USE_NETNS
284 NetNs::setNs(netNs());
285#endif
287 // fdset.clear(sock); !kw! removed as part of epoll impl
288
289 if ( sock == INVALID_SOCKET ) // no socket found - try to free one up and try again
290 {
291 int err = getErrno();
292 InfoLog (<< "Failed to create a socket " << strError(err));
293 error(err);
294 if(mConnectionManager.gc(ConnectionManager::MinimumGcAge, 1) == 0)
295 {
296 mConnectionManager.gcWithTarget(1); // free one up
297 }
298
299#ifdef USE_NETNS
300 NetNs::setNs(netNs());
301#endif
303 if ( sock == INVALID_SOCKET )
304 {
305 err = getErrno();
306 WarningLog( << "Error in finding free filedescriptor to use. " << strError(err));
307 error(err);
309 failSubCode = err;
310 return NULL;
311 }
312 }
313
315
316 DebugLog (<<"Opening new connection to " << dest);
317
318 // Bind client socket: symmetric port (if RESIP_TRANSPORT_FLAG_SYMMETRIC_CONNECTIONS set)
319 // or ephemeral port (default behavior)
320 if (!bindClientSocket(sock, failReason, failSubCode))
321 {
322 closeSocket(sock);
323 return NULL;
324 }
325 if(!configureConnectedSocket(sock))
326 {
327 closeSocket(sock);
328 throw Exception("Failed to configure connected socket", __FILE__,__LINE__);
329 }
331 applySocketOptions(sock);
332 const sockaddr& servaddr = dest.getSockaddr();
333 int ret = connect( sock, &servaddr, dest.length() );
334
335 // See Chapter 15.3 of Stevens, Unix Network Programming Vol. 1 2nd Edition
336 if (ret == SOCKET_ERROR)
337 {
338 int err = getErrno();
339
340 switch (err)
341 {
342 case EINPROGRESS:
343 case EAGAIN:
344#if EAGAIN != EWOULDBLOCK
345 case EWOULDBLOCK: // Treat EGAIN and EWOULDBLOCK as the same: http://stackoverflow.com/questions/7003234/which-systems-define-eagain-and-ewouldblock-as-different-values
346#endif
347 break;
348 default:
349 {
350 // !jf! this has failed
351 InfoLog( << "Error on TCP connect to " << dest << ", err=" << err << ": " << strError(err));
352 error(err);
353 //fdset.clear(sock);
354 closeSocket(sock);
356 failSubCode = err;
357 return NULL;
358 }
359 }
360 }
361
362 // This will add the connection to the manager
363 Connection *conn = createConnection(dest, sock, false);
364 resip_assert(conn);
366
367 return conn;
368}
369
370void
372{
373 while (mTxFifoOutBuffer.messageAvailable())
374 {
375 SendData* data = mTxFifoOutBuffer.getNext();
376 DebugLog (<< "Processing write for " << data->destination);
377
378 // this will check by connectionId first, then by address
379 Connection* conn = mConnectionManager.findConnection(data->destination);
380
381 //DebugLog (<< "TcpBaseTransport::processAllWriteRequests() using " << conn);
382
383#ifdef WIN32
385 {
386 // Workaround for bug in WSAPoll implementation: see
387 // http://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/
388 // http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/18769abd-fca0-4d3c-9884-1a38ce27ae90/wsapoll-and-nonblocking-connects-to-nonexistent-ports?forum=wsk
389 // Note: This is not an ideal solution - since we won't cleanup the connection until
390 // after the connect has timedout and someone else tries to write to the same
391 // destination. However the only impact to users is that requests will take the
392 // full 32 seconds transaction timeout to get an error vs the 21s connect timeout
393 // observered when using the select implemention (vs Poll). This does save us from
394 // having to use some form of timer to periodically check the connect state though.
395 if(conn->checkConnectionTimedout())
396 {
397 // If checkConnectionTimedout returns true, then connection is no longer available.
398 // Clear conn so that we create a new connection below.
399 conn = 0;
400 }
401 }
402#endif
403
404 // There is no connection yet, so make a client connection
405 if (conn == 0 &&
407 data->command == 0) // SendData commands (ie. close connection and enable flow timers) shouldn't cause new connections to form
408 {
410 int subCode = 0;
411 if((conn = makeOutgoingConnection(data->destination, failCode, subCode)) == 0)
412 {
413 DebugLog (<< "Failed to create connection: " << data->destination);
414 fail(data->transactionId, failCode, subCode);
415 delete data;
416 // NOTE: We fail this one but don't give up on others in queue
417 return;
418 }
420 data->destination.mFlowKey = (FlowKey)conn->getSocket(); // Note: https://stackoverflow.com/questions/1953639/is-it-safe-to-cast-socket-to-int-under-win64
421 }
422
423 if (conn == 0)
424 {
425 DebugLog (<< "Failed to find connection: " << data->destination);
427 delete data;
428 // NOTE: We fail this one but don't give up on others in queue
429 }
430 else // have a connection
431 {
432 // Check if we have written anything or not on the connection. If not, then this is either the first or
433 // a subsequent transaction trying to use this connection attempt - set TcpConnectState for this
434 // transaction to ConnectStarted
435 if (conn->mFirstWriteAfterConnectedPending == true)
436 {
437 // Notify the transaction state that we have started a TCP connect, so that it can run a TCP connect timer
439 }
440 conn->requestWrite(data);
441 }
442 }
443}
444
445void
447{
449
450 // called within SipStack's thread. There is some risk of
451 // recursion here if connection starts doing anything fancy.
452 // For backward-compat when not-epoll, don't handle transmit synchronously
453 // now, but rather wait for the process() call
454 if (mPollGrp)
455 {
457 }
458 mStateMachineFifo.flush();
459}
460
461void
463{
465
466 resip_assert( mPollGrp==NULL );
467
469
470 // process the connections in ConnectionManager
471 mConnectionManager.process(fdSet);
472
473 // process our own listen/accept socket for incoming connections
474 if (mFd!=INVALID_SOCKET && fdSet.readyToRead(mFd))
475 {
477 }
478
479 mStateMachineFifo.flush();
480}
481
482void
484{
485 if (mask & FPEM_Read)
486 {
487 while(processListen() > 0);
488 }
489}
490
491void
493{
494 resip_assert(0); // not implemented yet
495 // need to store away the length and use when setting up new connections
496}
497
498void
500{
501 // Call for base socket
503 // Call for each connection
504 mConnectionManager.invokeAfterSocketCreationFunc();
505}
506
507
508/* ====================================================================
509 * The Vovida Software License, Version 1.0
510 *
511 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
512 *
513 * Redistribution and use in source and binary forms, with or without
514 * modification, are permitted provided that the following conditions
515 * are met:
516 *
517 * 1. Redistributions of source code must retain the above copyright
518 * notice, this list of conditions and the following disclaimer.
519 *
520 * 2. Redistributions in binary form must reproduce the above copyright
521 * notice, this list of conditions and the following disclaimer in
522 * the documentation and/or other materials provided with the
523 * distribution.
524 *
525 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
526 * and "Vovida Open Communication Application Library (VOCAL)" must
527 * not be used to endorse or promote products derived from this
528 * software without prior written permission. For written
529 * permission, please contact vocal@vovida.org.
530 *
531 * 4. Products derived from this software may not be called "VOCAL", nor
532 * may "VOCAL" appear in their name, without prior written
533 * permission of Vovida Networks, Inc.
534 *
535 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
536 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
537 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
538 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
539 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
540 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
541 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
542 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
543 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
544 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
545 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
546 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
547 * DAMAGE.
548 *
549 * ====================================================================
550 *
551 * This software consists of voluntary contributions made by Vovida
552 * Networks, Inc. and many individuals on behalf of Vovida Networks,
553 * Inc. For more information on Vovida Networks, Inc., please see
554 * <http://www.vovida.org/>.
555 *
556 * vi: set shiftwidth=3 expandtab:
557 */
#define FPEM_Edge
Definition FdPoll.hxx:32
#define FPEM_Read
Definition FdPoll.hxx:29
#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.
#define INVALID_SOCKET
Definition Socket.hxx:95
#define SOCKET_ERROR
Definition Socket.hxx:96
#define RESIP_TRANSPORT_FLAG_SYMMETRIC_CONNECTIONS
Definition Transport.hxx:70
#define RESIP_TRANSPORT_FLAG_NOBIND
TransportFlags is bit-mask that can be set when creating a transport.
Definition Transport.hxx:65
Connection * findConnection(const Tuple &tuple)
may return 0
void setPollGrp(FdPollGrp *grp)
populate the fdset againt the read and write lists
static uint64_t MinimumGcAge
connection must have no inbound traffic for greater than this time (in ms) before it is garbage colle...
virtual void invokeAfterSocketCreationFunc() const
Connection implements, via sockets, ConnectionBase for managed connections.
Socket getSocket() const
virtual bool checkConnectionTimedout()
bool mFirstWriteAfterConnectedPending
bool isServer() const
void requestWrite(SendData *sendData)
queue data to write and add this to writable list
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
virtual FdPollItemHandle addPollItem(Socket sock, FdPollEventMask newMask, FdPollItemIf *item)=0
virtual ImplType getImplType() const =0
virtual void delPollItem(FdPollItemHandle handle)=0
Object-oriented wrapper for your platform's file-descriptor set.
Definition Socket.hxx:162
bool readyToRead(Socket fd)
Definition Socket.hxx:189
void setRead(Socket fd)
Definition Socket.hxx:204
A templated, threadsafe message-queue class.
Definition Fifo.hxx:16
virtual bool shareStackProcessAndSelect() const
Returns true if this Transport should be included in the FdSet processing loop, false if the Transpor...
static Socket socket(TransportType type, IpVersion ipVer)
SelectInterruptor mSelectInterruptor
ConsumerFifoBuffer< SendData > mTxFifoOutBuffer
virtual void invokeAfterSocketCreationFunc() const
virtual void setPollGrp(FdPollGrp *grp)
void buildFdSet(FdSet &fdset)
Used to add the 'artificial' fd to the fdset that will be responsible for interrupting a subsequent s...
SendDataCommand command
Definition SendData.hxx:80
TcpBaseTransport(Fifo< TransactionMessage > &fifo, int portNum, IpVersion version, const Data &interfaceName, AfterSocketCreationFuncPtr socketFunc, Compression &compression, unsigned transportFlags=0, const Data &netNs=Data::Empty)
virtual void processPollEvent(FdPollEventMask mask)
Called by PollGrp when activity is possible.
Connection * makeOutgoingConnection(const Tuple &dest, TransportFailure::FailureReason &failCode, int &subCode)
static const size_t MaxReadSize
virtual void setRcvBufLen(int buflen)
virtual void buildFdSet(FdSet &fdset)
Adds the Transport's socket FD to the appropriate read or write sets as applicable.
virtual void process()
Version of process to be invoked periodically when using callback-based IO (via FdPollGrp).
int processListen()
This doesn't exist anywhere that I can find? !kw! void sendFromRoundRobin(FdSet& fdset);.
virtual void setPollGrp(FdPollGrp *grp)
static const size_t MaxWriteSize
void processAllWriteRequests()
Forms a connection if one doesn't exist, moves requests to the appropriate connection's fifo.
virtual void init()
Performs constructor activities that depend on virtual functions specified by derived classes.
virtual Connection * createConnection(const Tuple &who, Socket fd, bool server=false)=0
Makes new Connection using provided socket.
virtual void invokeAfterSocketCreationFunc() const
General exception class for Transport.
void drainDscpRefresh()
Serves a pending refresh and a pending report.
IpVersion ipVersion() const
unsigned mTransportFlags
const Data & netNs() const
void fail(const Data &tid, TransportFailure::FailureReason reason=TransportFailure::Failure, int subCode=0)
Posts a TransportFailure to the TransactionMessage Fifo.
void applySocketOptions(Socket sock) const
The one place a socket of this transport is configured.
static void error(int e)
Generates a generic log for the platform specific socket error number.
ProducerFifoBuffer< TransactionMessage > mStateMachineFifo
void setTcpConnectState(const Data &tid, TcpConnectState::State state)
Posts a TcpConnectState to the TransactionMessage Fifo.
Represents a network address.
Definition Tuple.hxx:59
sockaddr & getMutableSockaddr()
Retrieve the binary representation of the socket address for this tuple.
Definition Tuple.hxx:113
bool onlyUseExistingConnection
Definition Tuple.hxx:192
FlowKey mFlowKey
This is a (largely) opaque key that subclasses of Transport will use to help record/find flows.
Definition Tuple.hxx:189
void copySockaddrAnyPort(sockaddr *sa)
Get a copy of the socket address including the interface and not the port number.
Definition Tuple.cxx:218
socklen_t length() const
Definition Tuple.cxx:655
const sockaddr & getSockaddr() const
Retrieve a const binary representation of the socket address for this tuple.
Definition Tuple.hxx:109
This file is used to handle compatibility fixes/tweaks so reSIProcate can function on multiple platfo...
#define RESIP_MAX_SOCKADDR_SIZE
Definition compat.hxx:107
dcm! – add UnusedChecking(_enum) below;
unsigned long FlowKey
Definition Tuple.hxx:37
void(* AfterSocketCreationFuncPtr)(Socket s, int transportType, const char *file, int line)
Definition Socket.hxx:107
unsigned short FdPollEventMask
Definition FdPoll.hxx:28
bool configureConnectedSocket(Socket fd)
Definition Socket.cxx:66
int Socket
Definition Socket.hxx:94
int closeSocket(Socket fd)
Definition Socket.cxx:134
int getErrno()
Definition Socket.hxx:97
std::string strError(int err)
Definition compat.hxx:274
IpVersion
An enumeration of IP versions.
bool makeSocketNonBlocking(Socket fd)
Definition Socket.cxx:21