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