2 ** \file SctpSocket.cpp
4 ** \author grymse@alhem.net
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"
26 #include "ISocketHandler.h"
28 #include "Ipv4Address.h"
29 #include "Ipv6Address.h"
30 #ifdef ENABLE_EXCEPTIONS
31 #include "Exception.h"
34 #ifdef SOCKETS_NAMESPACE
35 namespace SOCKETS_NAMESPACE
40 SctpSocket::SctpSocket(ISocketHandler
& h
,int type
) : StreamSocket(h
)
42 ,m_buf(new char[SCTP_BUFSIZE_READ
])
44 if (type
!= SOCK_STREAM
&& type
!= SOCK_SEQPACKET
)
50 SctpSocket::~SctpSocket()
56 int SctpSocket::Bind(const std::string
& a
,port_t p
)
72 int SctpSocket::Bind(SocketAddress
& ad
)
76 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR
);
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
);
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()));
99 int SctpSocket::AddAddress(const std::string
& a
,port_t p
)
105 Ipv6Address
ad(a
, p
);
106 return AddAddress(ad
);
110 Ipv4Address
ad(a
, p
);
111 return AddAddress(ad
);
115 int SctpSocket::AddAddress(SocketAddress
& ad
)
119 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR
);
122 if (GetSocket() == INVALID_SOCKET
)
124 Handler().LogError(this, "SctpSocket", -1, "AddAddress called with invalid file descriptor", LOG_LEVEL_ERROR
);
127 int n
= sctp_bindx(GetSocket(), ad
, ad
, SCTP_BINDX_ADD_ADDR
);
130 Handler().LogError(this, "SctpSocket", -1, "sctp_bindx() failed", LOG_LEVEL_ERROR
);
136 int SctpSocket::RemoveAddress(const std::string
& a
,port_t p
)
142 Ipv6Address
ad(a
, p
);
143 return RemoveAddress(ad
);
147 Ipv4Address
ad(a
, p
);
148 return RemoveAddress(ad
);
152 int SctpSocket::RemoveAddress(SocketAddress
& ad
)
156 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR
);
159 if (GetSocket() == INVALID_SOCKET
)
161 Handler().LogError(this, "SctpSocket", -1, "RemoveAddress called with invalid file descriptor", LOG_LEVEL_ERROR
);
164 int n
= sctp_bindx(GetSocket(), ad
, ad
, SCTP_BINDX_REM_ADDR
);
167 Handler().LogError(this, "SctpSocket", -1, "sctp_bindx() failed", LOG_LEVEL_ERROR
);
173 int SctpSocket::Open(const std::string
& a
,port_t p
)
179 Ipv6Address
ad(a
, p
);
184 Ipv4Address
ad(a
, p
);
189 int SctpSocket::Open(SocketAddress
& ad
)
193 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR
);
196 if (GetSocket() == INVALID_SOCKET
)
198 Attach(CreateSocket(ad
.GetFamily(), m_type
, "sctp"));
200 if (GetSocket() != INVALID_SOCKET
)
202 if (!SetNonblocking(true))
206 int n
= connect(GetSocket(), ad
, ad
);
209 // check error code that means a connect is in progress
211 if (Errno
== WSAEWOULDBLOCK
)
213 if (Errno
== EINPROGRESS
)
216 Handler().LogError(this, "connect: connection pending", Errno
, StrError(Errno
), LOG_LEVEL_INFO
);
217 SetConnecting( true ); // this flag will control fd_set's
221 Handler().LogError(this, "SctpSocket", -1, "connect() failed", LOG_LEVEL_ERROR
);
231 int SctpSocket::AddConnection(const std::string
& a
,port_t p
)
237 Ipv6Address
ad(a
, p
);
238 return AddConnection(ad
);
242 Ipv4Address
ad(a
, p
);
243 return AddConnection(ad
);
247 int SctpSocket::AddConnection(SocketAddress
& ad
)
251 Handler().LogError(this, "SctpSocket", -1, "invalid address", LOG_LEVEL_ERROR
);
254 if (GetSocket() == INVALID_SOCKET
)
256 Handler().LogError(this, "SctpSocket", -1, "AddConnection called with invalid file descriptor", LOG_LEVEL_ERROR
);
259 int n
= sctp_connectx(GetSocket(), ad
, ad
);
262 Handler().LogError(this, "SctpSocket", -1, "sctp_connectx() failed", LOG_LEVEL_ERROR
);
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
);
279 Handler().LogError(this, "SctpSocket", -1, "sctp_getpaddrs failed", LOG_LEVEL_WARNING
);
282 for (int i
= 0; i
< n
; i
++)
284 vec
.push_back(Utility::Sa2String(&p
[i
]));
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
);
297 Handler().LogError(this, "SctpSocket", -1, "sctp_getladdrs failed", LOG_LEVEL_WARNING
);
300 for (int i
= 0; i
< n
; i
++)
302 vec
.push_back(Utility::Sa2String(&p
[i
]));
309 int SctpSocket::PeelOff(sctp_assoc_t id
)
311 int n
= sctp_peeloff(GetSocket(), id
);
314 Handler().LogError(this, "SctpSocket", -1, "PeelOff failed", LOG_LEVEL_WARNING
);
317 Socket
*p
= Create();
319 p
-> SetDeleteByHandler();
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);
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
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.
345 socklen_t sa_len
= 0;
346 struct sctp_sndrcvinfo sinfo
;
348 int n
= sctp_recvmsg(GetSocket(), m_buf
, SCTP_BUFSIZE_READ
, &sa
, &sa_len
, &sinfo
, &flags
);
351 Handler().LogError(this, "SctpSocket", Errno
, StrError(Errno
), LOG_LEVEL_FATAL
);
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()
372 // don't reset connecting flag on error here, we want the OnConnectFailed timeout later on
373 /// \todo add to read fd_set here
376 Set(!IsDisableRead(), false);
377 SetConnecting(false);
381 Handler().LogError(this, "sctp: connect failed", err
, StrError(err
), LOG_LEVEL_FATAL
);
382 Set(false, false); // no more monitoring because connection failed
388 OnSocks4ConnectFailed();
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...
401 SetConnecting(false);
402 SetCloseAndDelete( true );
403 /// \todo state reason why connect failed
410 void SctpSocket::OnConnectTimeout()
412 Handler().LogError(this, "connect", -1, "connect timeout", LOG_LEVEL_FATAL
);
416 OnSocks4ConnectFailed();
417 // retry direct connection
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();
432 SetCloseAndDelete( true );
433 /// \todo state reason why connect failed
439 SetCloseAndDelete(true);
440 /// \todo state reason why connect failed
444 SetConnecting(false);
449 void SctpSocket::OnException()
455 OnSocks4ConnectFailed();
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...
469 SetConnecting(false); // tnx snibbe
475 // %! exception doesn't always mean something bad happened, this code should be reworked
478 Handler().LogError(this, "exception on select", err
, StrError(err
), LOG_LEVEL_FATAL
);
484 int SctpSocket::Protocol()
490 #ifdef SOCKETS_NAMESPACE
491 } // namespace SOCKETS_NAMESPACE