svn cleanup
[anytun.git] / Sockets / SctpSocket.cpp
blob2267086675df44dcf1050da64b7728f34cdfa7ef
1 /**
2 ** \file SctpSocket.cpp
3 ** \date 2006-09-04
4 ** \author grymse@alhem.net
5 **/
6 /*
7 Copyright (C) 2007 Anders Hedstrom
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include "SctpSocket.h"
24 #ifdef USE_SCTP
25 #include "Utility.h"
26 #include "ISocketHandler.h"
27 #include <errno.h>
28 #include "Ipv4Address.h"
29 #include "Ipv6Address.h"
30 #ifdef ENABLE_EXCEPTIONS
31 #include "Exception.h"
32 #endif
34 #ifdef SOCKETS_NAMESPACE
35 namespace SOCKETS_NAMESPACE
37 #endif
40 SctpSocket::SctpSocket(ISocketHandler& h,int type) : StreamSocket(h)
41 ,m_type(type)
42 ,m_buf(new char[SCTP_BUFSIZE_READ])
44 if (type != SOCK_STREAM && type != SOCK_SEQPACKET)
50 SctpSocket::~SctpSocket()
52 delete[] m_buf;
56 int SctpSocket::Bind(const std::string& a,port_t p)
58 #ifdef ENABLE_IPV6
59 #ifdef IPPROTO_IPV6
60 if (IsIpv6())
62 Ipv6Address ad(a, p);
63 return Bind(ad);
65 #endif
66 #endif
67 Ipv4Address ad(a, p);
68 return Bind(ad);
72 int SctpSocket::Bind(SocketAddress& ad)
74 if (!ad.IsValid())
76 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR);
77 return -1;
79 if (GetSocket() == INVALID_SOCKET)
81 Attach(CreateSocket(ad.GetFamily(), m_type, "sctp"));
83 if (GetSocket() != INVALID_SOCKET)
85 int n = bind(GetSocket(), ad, ad);
86 if (n == -1)
88 Handler().LogError(this, "SctpSocket", -1, "bind() failed", LOG_LEVEL_ERROR);
89 #ifdef ENABLE_EXCEPTIONS
90 throw Exception("bind() failed for SctpSocket, port: " + Utility::l2string(ad.GetPort()));
91 #endif
93 return n;
95 return -1;
99 int SctpSocket::AddAddress(const std::string& a,port_t p)
101 #ifdef ENABLE_IPV6
102 #ifdef IPPROTO_IPV6
103 if (IsIpv6())
105 Ipv6Address ad(a, p);
106 return AddAddress(ad);
108 #endif
109 #endif
110 Ipv4Address ad(a, p);
111 return AddAddress(ad);
115 int SctpSocket::AddAddress(SocketAddress& ad)
117 if (!ad.IsValid())
119 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR);
120 return -1;
122 if (GetSocket() == INVALID_SOCKET)
124 Handler().LogError(this, "SctpSocket", -1, "AddAddress called with invalid file descriptor", LOG_LEVEL_ERROR);
125 return -1;
127 int n = sctp_bindx(GetSocket(), ad, ad, SCTP_BINDX_ADD_ADDR);
128 if (n == -1)
130 Handler().LogError(this, "SctpSocket", -1, "sctp_bindx() failed", LOG_LEVEL_ERROR);
132 return n;
136 int SctpSocket::RemoveAddress(const std::string& a,port_t p)
138 #ifdef ENABLE_IPV6
139 #ifdef IPPROTO_IPV6
140 if (IsIpv6())
142 Ipv6Address ad(a, p);
143 return RemoveAddress(ad);
145 #endif
146 #endif
147 Ipv4Address ad(a, p);
148 return RemoveAddress(ad);
152 int SctpSocket::RemoveAddress(SocketAddress& ad)
154 if (!ad.IsValid())
156 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR);
157 return -1;
159 if (GetSocket() == INVALID_SOCKET)
161 Handler().LogError(this, "SctpSocket", -1, "RemoveAddress called with invalid file descriptor", LOG_LEVEL_ERROR);
162 return -1;
164 int n = sctp_bindx(GetSocket(), ad, ad, SCTP_BINDX_REM_ADDR);
165 if (n == -1)
167 Handler().LogError(this, "SctpSocket", -1, "sctp_bindx() failed", LOG_LEVEL_ERROR);
169 return n;
173 int SctpSocket::Open(const std::string& a,port_t p)
175 #ifdef ENABLE_IPV6
176 #ifdef IPPROTO_IPV6
177 if (IsIpv6())
179 Ipv6Address ad(a, p);
180 return Open(ad);
182 #endif
183 #endif
184 Ipv4Address ad(a, p);
185 return Open(ad);
189 int SctpSocket::Open(SocketAddress& ad)
191 if (!ad.IsValid())
193 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR);
194 return -1;
196 if (GetSocket() == INVALID_SOCKET)
198 Attach(CreateSocket(ad.GetFamily(), m_type, "sctp"));
200 if (GetSocket() != INVALID_SOCKET)
202 if (!SetNonblocking(true))
204 return -1;
206 int n = connect(GetSocket(), ad, ad);
207 if (n == -1)
209 // check error code that means a connect is in progress
210 #ifdef _WIN32
211 if (Errno == WSAEWOULDBLOCK)
212 #else
213 if (Errno == EINPROGRESS)
214 #endif
216 Handler().LogError(this, "connect: connection pending", Errno, StrError(Errno), LOG_LEVEL_INFO);
217 SetConnecting( true ); // this flag will control fd_set's
219 else
221 Handler().LogError(this, "SctpSocket", -1, "connect() failed", LOG_LEVEL_ERROR);
224 return n;
226 return -1;
230 #ifndef SOLARIS
231 int SctpSocket::AddConnection(const std::string& a,port_t p)
233 #ifdef ENABLE_IPV6
234 #ifdef IPPROTO_IPV6
235 if (IsIpv6())
237 Ipv6Address ad(a, p);
238 return AddConnection(ad);
240 #endif
241 #endif
242 Ipv4Address ad(a, p);
243 return AddConnection(ad);
247 int SctpSocket::AddConnection(SocketAddress& ad)
249 if (!ad.IsValid())
251 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR);
252 return -1;
254 if (GetSocket() == INVALID_SOCKET)
256 Handler().LogError(this, "SctpSocket", -1, "AddConnection called with invalid file descriptor", LOG_LEVEL_ERROR);
257 return -1;
259 int n = sctp_connectx(GetSocket(), ad, ad);
260 if (n == -1)
262 Handler().LogError(this, "SctpSocket", -1, "sctp_connectx() failed", LOG_LEVEL_ERROR);
264 else
266 SetConnecting();
268 return n;
270 #endif
273 int SctpSocket::getpaddrs(sctp_assoc_t id,std::list<std::string>& vec)
275 struct sockaddr *p = NULL;
276 int n = sctp_getpaddrs(GetSocket(), id, &p);
277 if (!n || n == -1)
279 Handler().LogError(this, "SctpSocket", -1, "sctp_getpaddrs failed", LOG_LEVEL_WARNING);
280 return n;
282 for (int i = 0; i < n; i++)
284 vec.push_back(Utility::Sa2String(&p[i]));
286 sctp_freepaddrs(p);
287 return n;
291 int SctpSocket::getladdrs(sctp_assoc_t id,std::list<std::string>& vec)
293 struct sockaddr *p = NULL;
294 int n = sctp_getladdrs(GetSocket(), id, &p);
295 if (!n || n == -1)
297 Handler().LogError(this, "SctpSocket", -1, "sctp_getladdrs failed", LOG_LEVEL_WARNING);
298 return n;
300 for (int i = 0; i < n; i++)
302 vec.push_back(Utility::Sa2String(&p[i]));
304 sctp_freeladdrs(p);
305 return n;
309 int SctpSocket::PeelOff(sctp_assoc_t id)
311 int n = sctp_peeloff(GetSocket(), id);
312 if (n == -1)
314 Handler().LogError(this, "SctpSocket", -1, "PeelOff failed", LOG_LEVEL_WARNING);
315 return -1;
317 Socket *p = Create();
318 p -> Attach(n);
319 p -> SetDeleteByHandler();
320 Handler().Add(p);
321 return n;
325 void SctpSocket::OnRead()
328 int sctp_recvmsg(int sd, void * msg, size_t * len,
329 struct sockaddr * from, socklen_t * fromlen,
330 struct sctp_sndrcvinfo * sinfo, int * msg_flags);
332 DESCRIPTION
333 sctp_recvmsg is a wrapper library function that can be used to receive a message from a socket while using the advanced
334 features of SCTP. sd is the socket descriptor on which the message pointed to by msg of length len is received.
336 If from is not NULL, the source address of the message is filled in. The argument fromlen is a value-result parameter.
337 initialized to the size of the buffer associated with from , and modified on return to indicate the actual size of the
338 address stored.
340 sinfo is a pointer to a sctp_sndrcvinfo structure to be filled upon receipt of the message. msg_flags is a pointer to a
341 integer that is filled with any message flags like MSG_NOTIFICATION or MSG_EOR.
344 struct sockaddr sa;
345 socklen_t sa_len = 0;
346 struct sctp_sndrcvinfo sinfo;
347 int flags = 0;
348 int n = sctp_recvmsg(GetSocket(), m_buf, SCTP_BUFSIZE_READ, &sa, &sa_len, &sinfo, &flags);
349 if (n == -1)
351 Handler().LogError(this, "SctpSocket", Errno, StrError(Errno), LOG_LEVEL_FATAL);
352 SetCloseAndDelete();
354 else
356 OnReceiveMessage(m_buf, n, &sa, sa_len, &sinfo, flags);
361 void SctpSocket::OnReceiveMessage(const char *buf,size_t sz,struct sockaddr *sa,socklen_t sa_len,struct sctp_sndrcvinfo *sinfo,int msg_flags)
366 void SctpSocket::OnWrite()
368 if (Connecting())
370 int err = SoError();
372 // don't reset connecting flag on error here, we want the OnConnectFailed timeout later on
373 /// \todo add to read fd_set here
374 if (!err) // ok
376 Set(!IsDisableRead(), false);
377 SetConnecting(false);
378 SetCallOnConnect();
379 return;
381 Handler().LogError(this, "sctp: connect failed", err, StrError(err), LOG_LEVEL_FATAL);
382 Set(false, false); // no more monitoring because connection failed
384 // failed
385 #ifdef ENABLE_SOCKS4
386 if (Socks4())
388 OnSocks4ConnectFailed();
389 return;
391 #endif
392 if (GetConnectionRetry() == -1 ||
393 (GetConnectionRetry() && GetConnectionRetries() < GetConnectionRetry()) )
395 // even though the connection failed at once, only retry after
396 // the connection timeout.
397 // should we even try to connect again, when CheckConnect returns
398 // false it's because of a connection error - not a timeout...
399 return;
401 SetConnecting(false);
402 SetCloseAndDelete( true );
403 /// \todo state reason why connect failed
404 OnConnectFailed();
405 return;
410 void SctpSocket::OnConnectTimeout()
412 Handler().LogError(this, "connect", -1, "connect timeout", LOG_LEVEL_FATAL);
413 #ifdef ENABLE_SOCKS4
414 if (Socks4())
416 OnSocks4ConnectFailed();
417 // retry direct connection
419 else
420 #endif
421 if (GetConnectionRetry() == -1 ||
422 (GetConnectionRetry() && GetConnectionRetries() < GetConnectionRetry()) )
424 IncreaseConnectionRetries();
425 // ask socket via OnConnectRetry callback if we should continue trying
426 if (OnConnectRetry())
428 SetRetryClientConnect();
430 else
432 SetCloseAndDelete( true );
433 /// \todo state reason why connect failed
434 OnConnectFailed();
437 else
439 SetCloseAndDelete(true);
440 /// \todo state reason why connect failed
441 OnConnectFailed();
444 SetConnecting(false);
448 #ifdef _WIN32
449 void SctpSocket::OnException()
451 if (Connecting())
453 #ifdef ENABLE_SOCKS4
454 if (Socks4())
455 OnSocks4ConnectFailed();
456 else
457 #endif
458 if (GetConnectionRetry() == -1 ||
459 (GetConnectionRetry() &&
460 GetConnectionRetries() < GetConnectionRetry() ))
462 // even though the connection failed at once, only retry after
463 // the connection timeout
464 // should we even try to connect again, when CheckConnect returns
465 // false it's because of a connection error - not a timeout...
467 else
469 SetConnecting(false); // tnx snibbe
470 SetCloseAndDelete();
471 OnConnectFailed();
473 return;
475 // %! exception doesn't always mean something bad happened, this code should be reworked
476 // errno valid here?
477 int err = SoError();
478 Handler().LogError(this, "exception on select", err, StrError(err), LOG_LEVEL_FATAL);
479 SetCloseAndDelete();
481 #endif // _WIN32
484 int SctpSocket::Protocol()
486 return IPPROTO_SCTP;
490 #ifdef SOCKETS_NAMESPACE
491 } // namespace SOCKETS_NAMESPACE
492 #endif
495 #endif // USE_SCTP