1 /**
2 ** \file SctpSocket.cpp
3 ** \date 2006-09-04
4 ** \author grymse@alhem.net
5 **/
6 /*
7 Copyright (C) 2007-2011 Anders Hedstrom
8
9 This library is made available under the terms of the GNU GPL, with
10 the additional exemption that compiling, linking, and/or using OpenSSL
11 is allowed.
12
13 If you would like to use this library in a closed-source application,
14 a separate license agreement is available. For information about
15 the closed-source license agreement for the C++ sockets library,
16 please visit http://www.alhem.net/Sockets/license.html and/or
17 email license@alhem.net.
18
19 This program is free software; you can redistribute it and/or
20 modify it under the terms of the GNU General Public License
21 as published by the Free Software Foundation; either version 2
22 of the License, or (at your option) any later version.
23
24 This program is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
28
29 You should have received a copy of the GNU General Public License
30 along with this program; if not, write to the Free Software
31 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 */
33 #include "SctpSocket.h"
34 #ifdef USE_SCTP
35 #include "Utility.h"
36 #include "ISocketHandler.h"
37 #include <errno.h>
38 #include "Ipv4Address.h"
39 #include "Ipv6Address.h"
40 #ifdef ENABLE_EXCEPTIONS
41 #include "Exception.h"
42 #endif
43
44 #ifdef SOCKETS_NAMESPACE
45 namespace SOCKETS_NAMESPACE
46 {
47 #endif
48
49
SctpSocket(ISocketHandler & h,int type)50 SctpSocket::SctpSocket(ISocketHandler& h,int type) : StreamSocket(h)
51 ,m_type(type)
52 ,m_buf(new char[SCTP_BUFSIZE_READ])
53 {
54 if (type != SOCK_STREAM && type != SOCK_SEQPACKET)
55 {
56 }
57 }
58
59
~SctpSocket()60 SctpSocket::~SctpSocket()
61 {
62 delete[] m_buf;
63 }
64
65
Bind(const std::string & a,port_t p)66 int SctpSocket::Bind(const std::string& a,port_t p)
67 {
68 #ifdef ENABLE_IPV6
69 #ifdef IPPROTO_IPV6
70 if (IsIpv6())
71 {
72 Ipv6Address ad(a, p);
73 return Bind(ad);
74 }
75 #endif
76 #endif
77 Ipv4Address ad(a, p);
78 return Bind(ad);
79 }
80
81
Bind(SocketAddress & ad)82 int SctpSocket::Bind(SocketAddress& ad)
83 {
84 if (!ad.IsValid())
85 {
86 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR);
87 return -1;
88 }
89 if (GetSocket() == INVALID_SOCKET)
90 {
91 Attach(CreateSocket(ad.GetFamily(), m_type, "sctp"));
92 }
93 if (GetSocket() != INVALID_SOCKET)
94 {
95 int n = bind(GetSocket(), ad, ad);
96 if (n == -1)
97 {
98 Handler().LogError(this, "SctpSocket", -1, "bind() failed", LOG_LEVEL_ERROR);
99 #ifdef ENABLE_EXCEPTIONS
100 throw Exception("bind() failed for SctpSocket, port: " + Utility::l2string(ad.GetPort()));
101 #endif
102 }
103 return n;
104 }
105 return -1;
106 }
107
108
AddAddress(const std::string & a,port_t p)109 int SctpSocket::AddAddress(const std::string& a,port_t p)
110 {
111 #ifdef ENABLE_IPV6
112 #ifdef IPPROTO_IPV6
113 if (IsIpv6())
114 {
115 Ipv6Address ad(a, p);
116 return AddAddress(ad);
117 }
118 #endif
119 #endif
120 Ipv4Address ad(a, p);
121 return AddAddress(ad);
122 }
123
124
AddAddress(SocketAddress & ad)125 int SctpSocket::AddAddress(SocketAddress& ad)
126 {
127 if (!ad.IsValid())
128 {
129 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR);
130 return -1;
131 }
132 if (GetSocket() == INVALID_SOCKET)
133 {
134 Handler().LogError(this, "SctpSocket", -1, "AddAddress called with invalid file descriptor", LOG_LEVEL_ERROR);
135 return -1;
136 }
137 int n = sctp_bindx(GetSocket(), ad, ad, SCTP_BINDX_ADD_ADDR);
138 if (n == -1)
139 {
140 Handler().LogError(this, "SctpSocket", -1, "sctp_bindx() failed", LOG_LEVEL_ERROR);
141 }
142 return n;
143 }
144
145
RemoveAddress(const std::string & a,port_t p)146 int SctpSocket::RemoveAddress(const std::string& a,port_t p)
147 {
148 #ifdef ENABLE_IPV6
149 #ifdef IPPROTO_IPV6
150 if (IsIpv6())
151 {
152 Ipv6Address ad(a, p);
153 return RemoveAddress(ad);
154 }
155 #endif
156 #endif
157 Ipv4Address ad(a, p);
158 return RemoveAddress(ad);
159 }
160
161
RemoveAddress(SocketAddress & ad)162 int SctpSocket::RemoveAddress(SocketAddress& ad)
163 {
164 if (!ad.IsValid())
165 {
166 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR);
167 return -1;
168 }
169 if (GetSocket() == INVALID_SOCKET)
170 {
171 Handler().LogError(this, "SctpSocket", -1, "RemoveAddress called with invalid file descriptor", LOG_LEVEL_ERROR);
172 return -1;
173 }
174 int n = sctp_bindx(GetSocket(), ad, ad, SCTP_BINDX_REM_ADDR);
175 if (n == -1)
176 {
177 Handler().LogError(this, "SctpSocket", -1, "sctp_bindx() failed", LOG_LEVEL_ERROR);
178 }
179 return n;
180 }
181
182
Open(const std::string & a,port_t p)183 int SctpSocket::Open(const std::string& a,port_t p)
184 {
185 #ifdef ENABLE_IPV6
186 #ifdef IPPROTO_IPV6
187 if (IsIpv6())
188 {
189 Ipv6Address ad(a, p);
190 return Open(ad);
191 }
192 #endif
193 #endif
194 Ipv4Address ad(a, p);
195 return Open(ad);
196 }
197
198
Open(SocketAddress & ad)199 int SctpSocket::Open(SocketAddress& ad)
200 {
201 if (!ad.IsValid())
202 {
203 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR);
204 return -1;
205 }
206 if (GetSocket() == INVALID_SOCKET)
207 {
208 Attach(CreateSocket(ad.GetFamily(), m_type, "sctp"));
209 }
210 if (GetSocket() != INVALID_SOCKET)
211 {
212 if (!SetNonblocking(true))
213 {
214 return -1;
215 }
216 int n = connect(GetSocket(), ad, ad);
217 if (n == -1)
218 {
219 // check error code that means a connect is in progress
220 #ifdef _WIN32
221 if (Errno == WSAEWOULDBLOCK)
222 #else
223 if (Errno == EINPROGRESS)
224 #endif
225 {
226 Handler().LogError(this, "connect: connection pending", Errno, StrError(Errno), LOG_LEVEL_INFO);
227 SetConnecting( true ); // this flag will control fd_set's
228 }
229 else
230 {
231 Handler().LogError(this, "SctpSocket", -1, "connect() failed", LOG_LEVEL_ERROR);
232 }
233 }
234 return n;
235 }
236 return -1;
237 }
238
239
240 #ifndef SOLARIS
AddConnection(const std::string & a,port_t p)241 int SctpSocket::AddConnection(const std::string& a,port_t p)
242 {
243 #ifdef ENABLE_IPV6
244 #ifdef IPPROTO_IPV6
245 if (IsIpv6())
246 {
247 Ipv6Address ad(a, p);
248 return AddConnection(ad);
249 }
250 #endif
251 #endif
252 Ipv4Address ad(a, p);
253 return AddConnection(ad);
254 }
255
256
AddConnection(SocketAddress & ad)257 int SctpSocket::AddConnection(SocketAddress& ad)
258 {
259 if (!ad.IsValid())
260 {
261 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR);
262 return -1;
263 }
264 if (GetSocket() == INVALID_SOCKET)
265 {
266 Handler().LogError(this, "SctpSocket", -1, "AddConnection called with invalid file descriptor", LOG_LEVEL_ERROR);
267 return -1;
268 }
269 // int n = sctp_connectx(GetSocket(), ad, ad);
270 int n = sctp_connectx(GetSocket(), ad, 1, NULL);
271 if (n == -1)
272 {
273 Handler().LogError(this, "SctpSocket", -1, "sctp_connectx() failed", LOG_LEVEL_ERROR);
274 }
275 else
276 {
277 SetConnecting();
278 }
279 return n;
280 }
281 #endif
282
283
getpaddrs(sctp_assoc_t id,std::list<std::string> & vec)284 int SctpSocket::getpaddrs(sctp_assoc_t id,std::list<std::string>& vec)
285 {
286 struct sockaddr *p = NULL;
287 int n = sctp_getpaddrs(GetSocket(), id, &p);
288 if (!n || n == -1)
289 {
290 Handler().LogError(this, "SctpSocket", -1, "sctp_getpaddrs failed", LOG_LEVEL_WARNING);
291 return n;
292 }
293 for (int i = 0; i < n; i++)
294 {
295 vec.push_back(Utility::Sa2String(&p[i]));
296 }
297 sctp_freepaddrs(p);
298 return n;
299 }
300
301
getladdrs(sctp_assoc_t id,std::list<std::string> & vec)302 int SctpSocket::getladdrs(sctp_assoc_t id,std::list<std::string>& vec)
303 {
304 struct sockaddr *p = NULL;
305 int n = sctp_getladdrs(GetSocket(), id, &p);
306 if (!n || n == -1)
307 {
308 Handler().LogError(this, "SctpSocket", -1, "sctp_getladdrs failed", LOG_LEVEL_WARNING);
309 return n;
310 }
311 for (int i = 0; i < n; i++)
312 {
313 vec.push_back(Utility::Sa2String(&p[i]));
314 }
315 sctp_freeladdrs(p);
316 return n;
317 }
318
319
PeelOff(sctp_assoc_t id)320 int SctpSocket::PeelOff(sctp_assoc_t id)
321 {
322 int n = sctp_peeloff(GetSocket(), id);
323 if (n == -1)
324 {
325 Handler().LogError(this, "SctpSocket", -1, "PeelOff failed", LOG_LEVEL_WARNING);
326 return -1;
327 }
328 Socket *p = Create();
329 p -> Attach(n);
330 p -> SetDeleteByHandler();
331 Handler().Add(p);
332 return n;
333 }
334
335
OnRead()336 void SctpSocket::OnRead()
337 {
338 /*
339 int sctp_recvmsg(int sd, void * msg, size_t * len,
340 struct sockaddr * from, socklen_t * fromlen,
341 struct sctp_sndrcvinfo * sinfo, int * msg_flags);
342
343 DESCRIPTION
344 sctp_recvmsg is a wrapper library function that can be used to receive a message from a socket while using the advanced
345 features of SCTP. sd is the socket descriptor on which the message pointed to by msg of length len is received.
346
347 If from is not NULL, the source address of the message is filled in. The argument fromlen is a value-result parameter.
348 initialized to the size of the buffer associated with from , and modified on return to indicate the actual size of the
349 address stored.
350
351 sinfo is a pointer to a sctp_sndrcvinfo structure to be filled upon receipt of the message. msg_flags is a pointer to a
352 integer that is filled with any message flags like MSG_NOTIFICATION or MSG_EOR.
353
354 */
355 struct sockaddr sa;
356 socklen_t sa_len = 0;
357 struct sctp_sndrcvinfo sinfo;
358 int flags = 0;
359 int n = sctp_recvmsg(GetSocket(), m_buf, SCTP_BUFSIZE_READ, &sa, &sa_len, &sinfo, &flags);
360 if (n == -1)
361 {
362 Handler().LogError(this, "SctpSocket", Errno, StrError(Errno), LOG_LEVEL_FATAL);
363 SetCloseAndDelete();
364 }
365 else
366 {
367 OnReceiveMessage(m_buf, n, &sa, sa_len, &sinfo, flags);
368 }
369 }
370
371
OnReceiveMessage(const char * buf,size_t sz,struct sockaddr * sa,socklen_t sa_len,struct sctp_sndrcvinfo * sinfo,int msg_flags)372 void SctpSocket::OnReceiveMessage(const char *buf,size_t sz,struct sockaddr *sa,socklen_t sa_len,struct sctp_sndrcvinfo *sinfo,int msg_flags)
373 {
374 }
375
376
OnWrite()377 void SctpSocket::OnWrite()
378 {
379 if (Connecting())
380 {
381 int err = SoError();
382
383 // don't reset connecting flag on error here, we want the OnConnectFailed timeout later on
384 /// \todo add to read fd_set here
385 if (!err) // ok
386 {
387 Set(!IsDisableRead(), false);
388 SetConnecting(false);
389 SetCallOnConnect();
390 return;
391 }
392 Handler().LogError(this, "sctp: connect failed", err, StrError(err), LOG_LEVEL_FATAL);
393 Set(false, false); // no more monitoring because connection failed
394
395 // failed
396 #ifdef ENABLE_SOCKS4
397 if (Socks4())
398 {
399 OnSocks4ConnectFailed();
400 return;
401 }
402 #endif
403 if (GetConnectionRetry() == -1 ||
404 (GetConnectionRetry() && GetConnectionRetries() < GetConnectionRetry()) )
405 {
406 // even though the connection failed at once, only retry after
407 // the connection timeout.
408 // should we even try to connect again, when CheckConnect returns
409 // false it's because of a connection error - not a timeout...
410 return;
411 }
412 SetConnecting(false);
413 SetCloseAndDelete( true );
414 /// \todo state reason why connect failed
415 OnConnectFailed();
416 return;
417 }
418 }
419
420
OnConnectTimeout()421 void SctpSocket::OnConnectTimeout()
422 {
423 Handler().LogError(this, "connect", -1, "connect timeout", LOG_LEVEL_FATAL);
424 #ifdef ENABLE_SOCKS4
425 if (Socks4())
426 {
427 OnSocks4ConnectFailed();
428 // retry direct connection
429 }
430 else
431 #endif
432 if (GetConnectionRetry() == -1 ||
433 (GetConnectionRetry() && GetConnectionRetries() < GetConnectionRetry()) )
434 {
435 IncreaseConnectionRetries();
436 // ask socket via OnConnectRetry callback if we should continue trying
437 if (OnConnectRetry())
438 {
439 SetRetryClientConnect();
440 }
441 else
442 {
443 SetCloseAndDelete( true );
444 /// \todo state reason why connect failed
445 OnConnectFailed();
446 }
447 }
448 else
449 {
450 SetCloseAndDelete(true);
451 /// \todo state reason why connect failed
452 OnConnectFailed();
453 }
454 //
455 SetConnecting(false);
456 }
457
458
459 #ifdef _WIN32
OnException()460 void SctpSocket::OnException()
461 {
462 if (Connecting())
463 {
464 #ifdef ENABLE_SOCKS4
465 if (Socks4())
466 OnSocks4ConnectFailed();
467 else
468 #endif
469 if (GetConnectionRetry() == -1 ||
470 (GetConnectionRetry() &&
471 GetConnectionRetries() < GetConnectionRetry() ))
472 {
473 // even though the connection failed at once, only retry after
474 // the connection timeout
475 // should we even try to connect again, when CheckConnect returns
476 // false it's because of a connection error - not a timeout...
477 }
478 else
479 {
480 SetConnecting(false); // tnx snibbe
481 SetCloseAndDelete();
482 OnConnectFailed();
483 }
484 return;
485 }
486 // %! exception doesn't always mean something bad happened, this code should be reworked
487 // errno valid here?
488 int err = SoError();
489 Handler().LogError(this, "exception on select", err, StrError(err), LOG_LEVEL_FATAL);
490 SetCloseAndDelete();
491 }
492 #endif // _WIN32
493
494
Protocol()495 int SctpSocket::Protocol()
496 {
497 return IPPROTO_SCTP;
498 }
499
500
501 #ifdef SOCKETS_NAMESPACE
502 } // namespace SOCKETS_NAMESPACE
503 #endif
504
505
506 #endif // USE_SCTP
507
508