1 /** \file UdpSocket.cpp
3 ** \author grymse@alhem.net
6 Copyright (C) 2004-2007 Anders Hedstrom
8 This library is made available under the terms of the GNU GPL.
10 If you would like to use this library in a closed-source application,
11 a separate license agreement is available. For information about
12 the closed-source license agreement for the C++ sockets library,
13 please visit http://www.alhem.net/Sockets/license.html and/or
14 email license@alhem.net.
16 This program is free software; you can redistribute it and/or
17 modify it under the terms of the GNU General Public License
18 as published by the Free Software Foundation; either version 2
19 of the License, or (at your option) any later version.
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 #pragma warning(disable:4786)
39 #include "ISocketHandler.h"
40 #include "UdpSocket.h"
42 #include "Ipv4Address.h"
43 #include "Ipv6Address.h"
44 #ifdef ENABLE_EXCEPTIONS
45 #include "Exception.h"
47 // include this to see strange sights
48 //#include <linux/in6.h>
51 #ifdef SOCKETS_NAMESPACE
52 namespace SOCKETS_NAMESPACE
{
56 UdpSocket::UdpSocket(ISocketHandler
& h
, int ibufsz
, bool ipv6
, int retries
) : Socket(h
)
57 , m_ibuf(new char[ibufsz
])
61 , m_last_size_written(-1)
73 UdpSocket::~UdpSocket()
80 int UdpSocket::Bind(port_t
&port
, int range
)
87 return Bind(ad
, range
);
92 return Bind(ad
, range
);
96 int UdpSocket::Bind(const std::string
& intf
, port_t
&port
, int range
)
102 Ipv6Address
ad(intf
, port
);
105 return Bind(ad
, range
);
112 Ipv4Address
ad(intf
, port
);
115 return Bind(ad
, range
);
122 int UdpSocket::Bind(ipaddr_t a
, port_t
&port
, int range
)
124 Ipv4Address
ad(a
, port
);
125 return Bind(ad
, range
);
131 int UdpSocket::Bind(in6_addr a
, port_t
&port
, int range
)
133 Ipv6Address
ad(a
, port
);
134 return Bind(ad
, range
);
140 int UdpSocket::Bind(SocketAddress
& ad
, int range
)
142 if (GetSocket() == INVALID_SOCKET
)
144 Attach(CreateSocket(ad
.GetFamily(), SOCK_DGRAM
, "udp"));
146 if (GetSocket() != INVALID_SOCKET
)
148 SetNonblocking(true);
149 int n
= bind(GetSocket(), ad
, ad
);
151 while (n
== -1 && tries
--)
153 ad
.SetPort(ad
.GetPort() + 1);
154 n
= bind(GetSocket(), ad
, ad
);
158 Handler().LogError(this, "bind", Errno
, StrError(Errno
), LOG_LEVEL_FATAL
);
160 #ifdef ENABLE_EXCEPTIONS
161 throw Exception("bind() failed for UdpSocket, port:range: " + Utility::l2string(ad
.GetPort()) + ":" + Utility::l2string(range
));
166 m_port
= ad
.GetPort();
173 /** if you wish to use Send, first Open a connection */
174 bool UdpSocket::Open(ipaddr_t l
, port_t port
)
176 Ipv4Address
ad(l
, port
);
181 bool UdpSocket::Open(const std::string
& host
, port_t port
)
187 Ipv6Address
ad(host
, port
);
196 Ipv4Address
ad(host
, port
);
207 bool UdpSocket::Open(struct in6_addr
& a
, port_t port
)
209 Ipv6Address
ad(a
, port
);
216 bool UdpSocket::Open(SocketAddress
& ad
)
218 if (GetSocket() == INVALID_SOCKET
)
220 Attach(CreateSocket(ad
.GetFamily(), SOCK_DGRAM
, "udp"));
222 if (GetSocket() != INVALID_SOCKET
)
224 SetNonblocking(true);
225 if (connect(GetSocket(), ad
, ad
) == -1)
227 Handler().LogError(this, "connect", Errno
, StrError(Errno
), LOG_LEVEL_FATAL
);
238 void UdpSocket::CreateConnection()
244 if (GetSocket() == INVALID_SOCKET
)
246 SOCKET s
= CreateSocket(AF_INET6
, SOCK_DGRAM
, "udp");
247 if (s
== INVALID_SOCKET
)
251 SetNonblocking(true, s
);
258 if (GetSocket() == INVALID_SOCKET
)
260 SOCKET s
= CreateSocket(AF_INET
, SOCK_DGRAM
, "udp");
261 if (s
== INVALID_SOCKET
)
265 SetNonblocking(true, s
);
271 /** send to specified address */
272 void UdpSocket::SendToBuf(const std::string
& h
, port_t p
, const char *data
, int len
, int flags
)
278 Ipv6Address
ad(h
, p
);
281 SendToBuf(ad
, data
, len
, flags
);
287 Ipv4Address
ad(h
, p
);
290 SendToBuf(ad
, data
, len
, flags
);
295 /** send to specified address */
296 void UdpSocket::SendToBuf(ipaddr_t a
, port_t p
, const char *data
, int len
, int flags
)
298 Ipv4Address
ad(a
, p
);
299 SendToBuf(ad
, data
, len
, flags
);
305 void UdpSocket::SendToBuf(in6_addr a
, port_t p
, const char *data
, int len
, int flags
)
307 Ipv6Address
ad(a
, p
);
308 SendToBuf(ad
, data
, len
, flags
);
314 void UdpSocket::SendToBuf(SocketAddress
& ad
, const char *data
, int len
, int flags
)
316 if (GetSocket() == INVALID_SOCKET
)
318 Attach(CreateSocket(ad
.GetFamily(), SOCK_DGRAM
, "udp"));
320 if (GetSocket() != INVALID_SOCKET
)
322 SetNonblocking(true);
323 if ((m_last_size_written
= sendto(GetSocket(), data
, len
, flags
, ad
, ad
)) == -1)
325 Handler().LogError(this, "sendto", Errno
, StrError(Errno
), LOG_LEVEL_ERROR
);
331 void UdpSocket::SendTo(const std::string
& a
, port_t p
, const std::string
& str
, int flags
)
333 SendToBuf(a
, p
, str
.c_str(), (int)str
.size(), flags
);
337 void UdpSocket::SendTo(ipaddr_t a
, port_t p
, const std::string
& str
, int flags
)
339 SendToBuf(a
, p
, str
.c_str(), (int)str
.size(), flags
);
345 void UdpSocket::SendTo(in6_addr a
, port_t p
, const std::string
& str
, int flags
)
347 SendToBuf(a
, p
, str
.c_str(), (int)str
.size(), flags
);
353 void UdpSocket::SendTo(SocketAddress
& ad
, const std::string
& str
, int flags
)
355 SendToBuf(ad
, str
.c_str(), (int)str
.size(), flags
);
359 /** send to connected address */
360 void UdpSocket::SendBuf(const char *data
, size_t len
, int flags
)
364 Handler().LogError(this, "SendBuf", 0, "not connected", LOG_LEVEL_ERROR
);
367 if ((m_last_size_written
= send(GetSocket(), data
, (int)len
, flags
)) == -1)
369 Handler().LogError(this, "send", Errno
, StrError(Errno
), LOG_LEVEL_ERROR
);
374 void UdpSocket::Send(const std::string
& str
, int flags
)
376 SendBuf(str
.c_str(), (int)str
.size(), flags
);
380 #if defined(LINUX) || defined(MACOSX)
381 int UdpSocket::ReadTS(char *ioBuf
, int inBufSize
, struct sockaddr
*from
, socklen_t fromlen
, struct timeval
*ts
)
388 #ifdef __DARWIN_UNIX03
389 #define ALIGNBYTES __DARWIN_ALIGNBYTES
391 #define myALIGN(p) (((unsigned int)(p) + ALIGNBYTES) &~ ALIGNBYTES)
392 #define myCMSG_SPACE(l) (myALIGN(sizeof(struct cmsghdr)) + myALIGN(l))
393 char data
[ myCMSG_SPACE(sizeof(struct timeval
)) ];
395 char data
[ CMSG_SPACE(sizeof(struct timeval
)) ];
398 struct cmsghdr
*cmsg
;
401 vec
[0].iov_base
= ioBuf
;
402 vec
[0].iov_len
= inBufSize
;
404 memset(&msg
, 0, sizeof(msg
));
405 memset(from
, 0, fromlen
);
406 memset(ioBuf
, 0, inBufSize
);
407 memset(&cmsg_un
, 0, sizeof(cmsg_un
));
409 msg
.msg_name
= (caddr_t
)from
;
410 msg
.msg_namelen
= fromlen
;
413 msg
.msg_control
= cmsg_un
.data
;
414 msg
.msg_controllen
= sizeof(cmsg_un
.data
);
417 // Original version - for reference only
418 //int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len);
420 int n
= recvmsg(GetSocket(), &msg
, MSG_DONTWAIT
);
422 // now ioBuf will contain the data, as if we used recvfrom
425 if(n
!= -1 && msg
.msg_controllen
>= sizeof(struct cmsghdr
) && !(msg
.msg_flags
& MSG_CTRUNC
))
428 for (cmsg
= CMSG_FIRSTHDR(&msg
); cmsg
!= NULL
; cmsg
= CMSG_NXTHDR(&msg
, cmsg
))
430 if (cmsg
->cmsg_level
== SOL_SOCKET
&& cmsg
->cmsg_type
== SCM_TIMESTAMP
)
432 tv
= (struct timeval
*)CMSG_DATA(cmsg
);
437 memcpy(ts
, tv
, sizeof(struct timeval
));
440 // The address is in network order, but that's OK right now
446 void UdpSocket::OnRead()
452 struct sockaddr_in6 sa
;
453 socklen_t sa_len
= sizeof(sa
);
457 Utility::GetTime(&ts
);
458 #if !defined(LINUX) && !defined(MACOSX)
459 int n
= recvfrom(GetSocket(), m_ibuf
, m_ibufsz
, 0, (struct sockaddr
*)&sa
, &sa_len
);
461 int n
= ReadTS(m_ibuf
, m_ibufsz
, (struct sockaddr
*)&sa
, sa_len
, &ts
);
465 this -> OnRawData(m_ibuf
, n
, (struct sockaddr
*)&sa
, sa_len
, &ts
);
471 if (Errno
!= WSAEWOULDBLOCK
)
473 if (Errno
!= EWOULDBLOCK
)
475 Handler().LogError(this, "recvfrom", Errno
, StrError(Errno
), LOG_LEVEL_ERROR
);
479 int n
= recvfrom(GetSocket(), m_ibuf
, m_ibufsz
, 0, (struct sockaddr
*)&sa
, &sa_len
);
480 int q
= m_retries
; // receive max 10 at one cycle
483 if (sa_len
!= sizeof(sa
))
485 Handler().LogError(this, "recvfrom", 0, "unexpected address struct size", LOG_LEVEL_WARNING
);
487 this -> OnRawData(m_ibuf
, n
, (struct sockaddr
*)&sa
, sa_len
);
491 n
= recvfrom(GetSocket(), m_ibuf
, m_ibufsz
, 0, (struct sockaddr
*)&sa
, &sa_len
);
496 if (Errno
!= WSAEWOULDBLOCK
)
498 if (Errno
!= EWOULDBLOCK
)
500 Handler().LogError(this, "recvfrom", Errno
, StrError(Errno
), LOG_LEVEL_ERROR
);
506 struct sockaddr_in sa
;
507 socklen_t sa_len
= sizeof(sa
);
511 Utility::GetTime(&ts
);
512 #if !defined(LINUX) && !defined(MACOSX)
513 int n
= recvfrom(GetSocket(), m_ibuf
, m_ibufsz
, 0, (struct sockaddr
*)&sa
, &sa_len
);
515 int n
= ReadTS(m_ibuf
, m_ibufsz
, (struct sockaddr
*)&sa
, sa_len
, &ts
);
519 this -> OnRawData(m_ibuf
, n
, (struct sockaddr
*)&sa
, sa_len
, &ts
);
525 if (Errno
!= WSAEWOULDBLOCK
)
527 if (Errno
!= EWOULDBLOCK
)
529 Handler().LogError(this, "recvfrom", Errno
, StrError(Errno
), LOG_LEVEL_ERROR
);
533 int n
= recvfrom(GetSocket(), m_ibuf
, m_ibufsz
, 0, (struct sockaddr
*)&sa
, &sa_len
);
537 if (sa_len
!= sizeof(sa
))
539 Handler().LogError(this, "recvfrom", 0, "unexpected address struct size", LOG_LEVEL_WARNING
);
541 this -> OnRawData(m_ibuf
, n
, (struct sockaddr
*)&sa
, sa_len
);
545 n
= recvfrom(GetSocket(), m_ibuf
, m_ibufsz
, 0, (struct sockaddr
*)&sa
, &sa_len
);
550 if (Errno
!= WSAEWOULDBLOCK
)
552 if (Errno
!= EWOULDBLOCK
)
554 Handler().LogError(this, "recvfrom", Errno
, StrError(Errno
), LOG_LEVEL_ERROR
);
559 void UdpSocket::SetBroadcast(bool b
)
564 if (GetSocket() == INVALID_SOCKET
)
570 if (setsockopt(GetSocket(), SOL_SOCKET
, SO_BROADCAST
, (char *) &one
, sizeof(one
)) == -1)
572 Handler().LogError(this, "SetBroadcast", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
577 if (setsockopt(GetSocket(), SOL_SOCKET
, SO_BROADCAST
, (char *) &zero
, sizeof(zero
)) == -1)
579 Handler().LogError(this, "SetBroadcast", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
585 bool UdpSocket::IsBroadcast()
587 int is_broadcast
= 0;
590 if (GetSocket() == INVALID_SOCKET
)
594 if (getsockopt(GetSocket(), SOL_SOCKET
, SO_BROADCAST
, (char *)&is_broadcast
, &size
) == -1)
596 Handler().LogError(this, "IsBroadcast", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
598 return is_broadcast
!= 0;
602 void UdpSocket::SetMulticastTTL(int ttl
)
604 if (GetSocket() == INVALID_SOCKET
)
608 if (setsockopt(GetSocket(), SOL_IP
, IP_MULTICAST_TTL
, (char *)&ttl
, sizeof(int)) == -1)
610 Handler().LogError(this, "SetMulticastTTL", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
615 int UdpSocket::GetMulticastTTL()
618 socklen_t size
= sizeof(int);
620 if (GetSocket() == INVALID_SOCKET
)
624 if (getsockopt(GetSocket(), SOL_IP
, IP_MULTICAST_TTL
, (char *)&ttl
, &size
) == -1)
626 Handler().LogError(this, "GetMulticastTTL", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
632 void UdpSocket::SetMulticastLoop(bool x
)
634 if (GetSocket() == INVALID_SOCKET
)
643 if (setsockopt(GetSocket(), IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *)&val
, sizeof(int)) == -1)
645 Handler().LogError(this, "SetMulticastLoop", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
652 if (setsockopt(GetSocket(), SOL_IP
, IP_MULTICAST_LOOP
, (char *)&val
, sizeof(int)) == -1)
654 Handler().LogError(this, "SetMulticastLoop", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
659 bool UdpSocket::IsMulticastLoop()
661 if (GetSocket() == INVALID_SOCKET
)
670 socklen_t size
= sizeof(int);
671 if (getsockopt(GetSocket(), IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *)&is_loop
, &size
) == -1)
673 Handler().LogError(this, "IsMulticastLoop", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
675 return is_loop
? true : false;
680 socklen_t size
= sizeof(int);
681 if (getsockopt(GetSocket(), SOL_IP
, IP_MULTICAST_LOOP
, (char *)&is_loop
, &size
) == -1)
683 Handler().LogError(this, "IsMulticastLoop", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
685 return is_loop
? true : false;
689 void UdpSocket::AddMulticastMembership(const std::string
& group
, const std::string
& local_if
, int if_index
)
691 if (GetSocket() == INVALID_SOCKET
)
700 struct in6_addr addr
;
701 if (Utility::u2ip( group
, addr
))
703 x
.ipv6mr_multiaddr
= addr
;
704 x
.ipv6mr_interface
= if_index
;
705 if (setsockopt(GetSocket(), IPPROTO_IPV6
, IPV6_ADD_MEMBERSHIP
, (char *)&x
, sizeof(struct ipv6_mreq
)) == -1)
707 Handler().LogError(this, "AddMulticastMembership", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
714 struct ip_mreq x
; // ip_mreqn
716 if (Utility::u2ip( group
, addr
))
718 memcpy(&x
.imr_multiaddr
.s_addr
, &addr
, sizeof(addr
));
719 Utility::u2ip( local_if
, addr
);
720 memcpy(&x
.imr_interface
.s_addr
, &addr
, sizeof(addr
));
721 // x.imr_ifindex = if_index;
722 if (setsockopt(GetSocket(), SOL_IP
, IP_ADD_MEMBERSHIP
, (char *)&x
, sizeof(struct ip_mreq
)) == -1)
724 Handler().LogError(this, "AddMulticastMembership", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
730 void UdpSocket::DropMulticastMembership(const std::string
& group
, const std::string
& local_if
, int if_index
)
732 if (GetSocket() == INVALID_SOCKET
)
741 struct in6_addr addr
;
742 if (Utility::u2ip( group
, addr
))
744 x
.ipv6mr_multiaddr
= addr
;
745 x
.ipv6mr_interface
= if_index
;
746 if (setsockopt(GetSocket(), IPPROTO_IPV6
, IPV6_DROP_MEMBERSHIP
, (char *)&x
, sizeof(struct ipv6_mreq
)) == -1)
748 Handler().LogError(this, "DropMulticastMembership", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
755 struct ip_mreq x
; // ip_mreqn
757 if (Utility::u2ip( group
, addr
))
759 memcpy(&x
.imr_multiaddr
.s_addr
, &addr
, sizeof(addr
));
760 Utility::u2ip( local_if
, addr
);
761 memcpy(&x
.imr_interface
.s_addr
, &addr
, sizeof(addr
));
762 // x.imr_ifindex = if_index;
763 if (setsockopt(GetSocket(), SOL_IP
, IP_DROP_MEMBERSHIP
, (char *)&x
, sizeof(struct ip_mreq
)) == -1)
765 Handler().LogError(this, "DropMulticastMembership", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
773 void UdpSocket::SetMulticastHops(int hops
)
775 if (GetSocket() == INVALID_SOCKET
)
781 Handler().LogError(this, "SetMulticastHops", 0, "Ipv6 only", LOG_LEVEL_ERROR
);
784 if (setsockopt(GetSocket(), IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, (char *)&hops
, sizeof(int)) == -1)
786 Handler().LogError(this, "SetMulticastHops", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
791 int UdpSocket::GetMulticastHops()
793 if (GetSocket() == INVALID_SOCKET
)
799 Handler().LogError(this, "SetMulticastHops", 0, "Ipv6 only", LOG_LEVEL_ERROR
);
803 socklen_t size
= sizeof(int);
804 if (getsockopt(GetSocket(), IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, (char *)&hops
, &size
) == -1)
806 Handler().LogError(this, "GetMulticastHops", Errno
, StrError(Errno
), LOG_LEVEL_WARNING
);
810 #endif // IPPROTO_IPV6
814 bool UdpSocket::IsBound()
820 void UdpSocket::OnRawData(const char *buf
, size_t len
, struct sockaddr
*sa
, socklen_t sa_len
)
825 void UdpSocket::OnRawData(const char *buf
, size_t len
, struct sockaddr
*sa
, socklen_t sa_len
, struct timeval
*ts
)
830 port_t
UdpSocket::GetPort()
836 int UdpSocket::GetLastSizeWritten()
838 return m_last_size_written
;
842 void UdpSocket::SetTimestamp(bool x
)
848 #ifdef SOCKETS_NAMESPACE