1 |
#if defined(HAVE_CONFIG_H) |
2 |
#include "resiprocate/config.hxx" |
3 |
#endif |
4 |
|
5 |
#include <memory> |
6 |
#include "resiprocate/os/compat.hxx" |
7 |
#include "resiprocate/os/Data.hxx" |
8 |
#include "resiprocate/os/DnsUtil.hxx" |
9 |
#include "resiprocate/os/Socket.hxx" |
10 |
#include "resiprocate/os/Logger.hxx" |
11 |
#include "resiprocate/TcpBaseTransport.hxx" |
12 |
|
13 |
#define RESIPROCATE_SUBSYSTEM Subsystem::TRANSPORT |
14 |
|
15 |
using namespace std; |
16 |
using namespace resip; |
17 |
|
18 |
const size_t TcpBaseTransport::MaxWriteSize = 4096; |
19 |
const size_t TcpBaseTransport::MaxReadSize = 4096; |
20 |
|
21 |
TcpBaseTransport::TcpBaseTransport(Fifo<TransactionMessage>& fifo, int portNum, IpVersion version, |
22 |
const Data& pinterface) |
23 |
: InternalTransport(fifo, portNum, version, pinterface) |
24 |
{ |
25 |
mFd = InternalTransport::socket(TCP, version); |
26 |
//DebugLog (<< "Opening TCP " << mFd << " : " << this); |
27 |
|
28 |
#if !defined(WIN32) |
29 |
int on = 1; |
30 |
if ( ::setsockopt ( mFd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) ) |
31 |
{ |
32 |
int e = getErrno(); |
33 |
InfoLog (<< "Couldn't set sockoptions SO_REUSEPORT | SO_REUSEADDR: " << strerror(e)); |
34 |
error(e); |
35 |
throw Exception("Failed setsockopt", __FILE__,__LINE__); |
36 |
} |
37 |
#endif |
38 |
|
39 |
bind(); |
40 |
makeSocketNonBlocking(mFd); |
41 |
|
42 |
// do the listen, seting the maximum queue size for compeletly established |
43 |
// sockets -- on linux, tcp_max_syn_backlog should be used for the incomplete |
44 |
// queue size(see man listen) |
45 |
int e = listen(mFd,64 ); |
46 |
|
47 |
if (e != 0 ) |
48 |
{ |
49 |
int e = getErrno(); |
50 |
InfoLog (<< "Failed listen " << strerror(e)); |
51 |
error(e); |
52 |
// !cj! deal with errors |
53 |
throw Transport::Exception("Address already in use", __FILE__,__LINE__); |
54 |
} |
55 |
} |
56 |
|
57 |
|
58 |
TcpBaseTransport::~TcpBaseTransport() |
59 |
{ |
60 |
//DebugLog (<< "Shutting down TCP Transport " << this << " " << mFd << " " << mInterface << ":" << port()); |
61 |
|
62 |
// !jf! this is not right. should drain the sends before |
63 |
while (mTxFifo.messageAvailable()) |
64 |
{ |
65 |
SendData* data = mTxFifo.getNext(); |
66 |
InfoLog (<< "Throwing away queued data for " << data->destination); |
67 |
|
68 |
fail(data->transactionId); |
69 |
delete data; |
70 |
} |
71 |
DebugLog (<< "Shutting down " << mTuple); |
72 |
ThreadIf::shutdown(); |
73 |
join(); |
74 |
//mSendRoundRobin.clear(); // clear before we delete the connections |
75 |
} |
76 |
|
77 |
void |
78 |
TcpBaseTransport::buildFdSet( FdSet& fdset) |
79 |
{ |
80 |
mConnectionManager.buildFdSet(fdset); |
81 |
fdset.setRead(mFd); // for the transport itself |
82 |
} |
83 |
|
84 |
void |
85 |
TcpBaseTransport::processListen(FdSet& fdset) |
86 |
{ |
87 |
if (fdset.readyToRead(mFd)) |
88 |
{ |
89 |
Tuple tuple(mTuple); |
90 |
struct sockaddr& peer = tuple.getMutableSockaddr(); |
91 |
socklen_t peerLen = tuple.length(); |
92 |
Socket sock = accept( mFd, &peer, &peerLen); |
93 |
if ( sock == SOCKET_ERROR ) |
94 |
{ |
95 |
int e = getErrno(); |
96 |
switch (e) |
97 |
{ |
98 |
case EWOULDBLOCK: |
99 |
// !jf! this can not be ready in some cases |
100 |
return; |
101 |
default: |
102 |
Transport::error(e); |
103 |
} |
104 |
return; |
105 |
} |
106 |
makeSocketNonBlocking(sock); |
107 |
|
108 |
tuple.transport = this; |
109 |
DebugLog (<< "Received TCP connection from: " << tuple << " as fd=" << sock); |
110 |
createConnection(tuple, sock, true); |
111 |
} |
112 |
} |
113 |
|
114 |
void |
115 |
TcpBaseTransport::processSomeWrites(FdSet& fdset) |
116 |
{ |
117 |
// !jf! may want to do a roundrobin later |
118 |
Connection* curr = mConnectionManager.getNextWrite(); |
119 |
if (curr && fdset.readyToWrite(curr->getSocket())) |
120 |
{ |
121 |
//DebugLog (<< "TcpBaseTransport::processSomeWrites() " << curr->getSocket()); |
122 |
curr->performWrite(); |
123 |
} |
124 |
else if (curr && fdset.hasException(curr->getSocket())) |
125 |
{ |
126 |
int errNum = 0; |
127 |
int errNumSize = sizeof(errNum); |
128 |
getsockopt(curr->getSocket(),SOL_SOCKET,SO_ERROR,(char *)&errNum,(socklen_t *)&errNumSize); |
129 |
InfoLog (<< "Exception writing to socket " << curr->getSocket() << " code: " << errNum << "; closing connection"); |
130 |
delete curr; |
131 |
} |
132 |
} |
133 |
|
134 |
void |
135 |
TcpBaseTransport::processSomeReads(FdSet& fdset) |
136 |
{ |
137 |
Connection* currConnection = mConnectionManager.getNextRead(fdset); |
138 |
if (currConnection) |
139 |
{ |
140 |
if ( fdset.readyToRead(currConnection->getSocket()) || |
141 |
currConnection->hasDataToRead() ) |
142 |
{ |
143 |
DebugLog (<< "TcpBaseTransport::processSomeReads() " << *currConnection); |
144 |
fdset.clear(currConnection->getSocket()); |
145 |
|
146 |
int bytesRead = currConnection->read(mStateMachineFifo); |
147 |
DebugLog (<< "TcpBaseTransport::processSomeReads() " |
148 |
<< *currConnection << " read=" << bytesRead); |
149 |
if (bytesRead < 0) |
150 |
{ |
151 |
DebugLog (<< "Closing connection bytesRead=" << bytesRead); |
152 |
delete currConnection; |
153 |
} |
154 |
} |
155 |
else if (fdset.hasException(currConnection->getSocket())) |
156 |
{ |
157 |
int errNum = 0; |
158 |
int errNumSize = sizeof(errNum); |
159 |
getsockopt(currConnection->getSocket(),SOL_SOCKET,SO_ERROR,(char *)&errNum,(socklen_t *)&errNumSize); |
160 |
InfoLog (<< "Exception reading from socket " << currConnection->getSocket() << " code: " << errNum << "; closing connection"); |
161 |
delete currConnection; |
162 |
} |
163 |
} |
164 |
} |
165 |
|
166 |
|
167 |
void |
168 |
TcpBaseTransport::processAllWriteRequests( FdSet& fdset ) |
169 |
{ |
170 |
while (mTxFifo.messageAvailable()) |
171 |
{ |
172 |
SendData* data = mTxFifo.getNext(); |
173 |
DebugLog (<< "Processing write for " << data->destination); |
174 |
|
175 |
// this will check by connectionId first, then by address |
176 |
Connection* conn = mConnectionManager.findConnection(data->destination); |
177 |
if ( conn ) |
178 |
{ |
179 |
assert( conn->transport() ); |
180 |
} |
181 |
|
182 |
//DebugLog (<< "TcpBaseTransport::processAllWriteRequests() using " << conn); |
183 |
|
184 |
// There is no connection yet, so make a client connection |
185 |
if (conn == 0) |
186 |
{ |
187 |
// attempt to open |
188 |
Socket sock = InternalTransport::socket( TCP, ipVersion()); |
189 |
fdset.clear(sock); |
190 |
|
191 |
if ( sock == INVALID_SOCKET ) // no socket found - try to free one up and try again |
192 |
{ |
193 |
int e = getErrno(); |
194 |
InfoLog (<< "Failed to create a socket " << strerror(e)); |
195 |
error(e); |
196 |
mConnectionManager.gc(ConnectionManager::MinLastUsed); // free one up |
197 |
|
198 |
sock = InternalTransport::socket( TCP, ipVersion()); |
199 |
if ( sock == INVALID_SOCKET ) |
200 |
{ |
201 |
int e = getErrno(); |
202 |
WarningLog( << "Error in finding free filedescriptor to use. " << strerror(e)); |
203 |
error(e); |
204 |
fail(data->transactionId); |
205 |
delete data; |
206 |
return; |
207 |
} |
208 |
} |
209 |
|
210 |
assert(sock != INVALID_SOCKET); |
211 |
const sockaddr& servaddr = data->destination.getSockaddr(); |
212 |
|
213 |
DebugLog (<<"Opening new connection to " << data->destination); |
214 |
makeSocketNonBlocking(sock); |
215 |
int e = connect( sock, &servaddr, data->destination.length() ); |
216 |
|
217 |
// See Chapter 15.3 of Stevens, Unix Network Programming Vol. 1 2nd Edition |
218 |
if (e == INVALID_SOCKET) |
219 |
{ |
220 |
int err = getErrno(); |
221 |
|
222 |
switch (err) |
223 |
{ |
224 |
case EINPROGRESS: |
225 |
case EWOULDBLOCK: |
226 |
break; |
227 |
default: |
228 |
{ |
229 |
// !jf! this has failed |
230 |
InfoLog( << "Error on TCP connect to " << data->destination << ": " << strerror(err)); |
231 |
error(e); |
232 |
fdset.clear(sock); |
233 |
close(sock); |
234 |
fail(data->transactionId); |
235 |
delete data; |
236 |
return; |
237 |
} |
238 |
} |
239 |
} |
240 |
|
241 |
// This will add the connection to the manager |
242 |
conn = createConnection(data->destination, sock, false); |
243 |
assert(conn); |
244 |
assert( conn->transport() ); |
245 |
|
246 |
data->destination.transport = this; |
247 |
data->destination.connectionId = conn->getId(); // !jf! |
248 |
} |
249 |
|
250 |
if (conn == 0) |
251 |
{ |
252 |
DebugLog (<< "Failed to create/get connection: " << data->destination); |
253 |
fail(data->transactionId); |
254 |
delete data; |
255 |
} |
256 |
else // have a connection |
257 |
{ |
258 |
assert( conn->transport() ); |
259 |
|
260 |
conn->requestWrite(data); |
261 |
} |
262 |
} |
263 |
} |
264 |
|
265 |
void |
266 |
TcpBaseTransport::process(FdSet& fdSet) |
267 |
{ |
268 |
processAllWriteRequests(fdSet); |
269 |
if(fdSet.numReady > 0) |
270 |
{ |
271 |
processSomeWrites(fdSet); |
272 |
processSomeReads(fdSet); |
273 |
processListen(fdSet); |
274 |
} |
275 |
} |
276 |
|
277 |
|
278 |
/* ==================================================================== |
279 |
* The Vovida Software License, Version 1.0 |
280 |
* |
281 |
* Copyright (c) 2000 Vovida Networks, Inc. All rights reserved. |
282 |
* |
283 |
* Redistribution and use in source and binary forms, with or without |
284 |
* modification, are permitted provided that the following conditions |
285 |
* are met: |
286 |
* |
287 |
* 1. Redistributions of source code must retain the above copyright |
288 |
* notice, this list of conditions and the following disclaimer. |
289 |
* |
290 |
* 2. Redistributions in binary form must reproduce the above copyright |
291 |
* notice, this list of conditions and the following disclaimer in |
292 |
* the documentation and/or other materials provided with the |
293 |
* distribution. |
294 |
* |
295 |
* 3. The names "VOCAL", "Vovida Open Communication Application Library", |
296 |
* and "Vovida Open Communication Application Library (VOCAL)" must |
297 |
* not be used to endorse or promote products derived from this |
298 |
* software without prior written permission. For written |
299 |
* permission, please contact vocal@vovida.org. |
300 |
* |
301 |
* 4. Products derived from this software may not be called "VOCAL", nor |
302 |
* may "VOCAL" appear in their name, without prior written |
303 |
* permission of Vovida Networks, Inc. |
304 |
* |
305 |
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED |
306 |
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
307 |
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND |
308 |
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA |
309 |
* NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES |
310 |
* IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, |
311 |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
312 |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
313 |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
314 |
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
315 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
316 |
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
317 |
* DAMAGE. |
318 |
* |
319 |
* ==================================================================== |
320 |
* |
321 |
* This software consists of voluntary contributions made by Vovida |
322 |
* Networks, Inc. and many individuals on behalf of Vovida Networks, |
323 |
* Inc. For more information on Vovida Networks, Inc., please see |
324 |
* <http://www.vovida.org/>. |
325 |
* |
326 |
*/ |
327 |
|
328 |
|