diff options
Diffstat (limited to 'net/udp/udp_socket_libevent.cc')
-rw-r--r-- | net/udp/udp_socket_libevent.cc | 137 |
1 files changed, 132 insertions, 5 deletions
diff --git a/net/udp/udp_socket_libevent.cc b/net/udp/udp_socket_libevent.cc index 063da08..f713786 100644 --- a/net/udp/udp_socket_libevent.cc +++ b/net/udp/udp_socket_libevent.cc @@ -41,7 +41,9 @@ UDPSocketLibevent::UDPSocketLibevent( net::NetLog* net_log, const net::NetLog::Source& source) : socket_(kInvalidSocket), - socket_options_(0), + addr_family_(0), + socket_options_(SOCKET_OPTION_MULTICAST_LOOP), + multicast_time_to_live_(1), bind_type_(bind_type), rand_int_cb_(rand_int_cb), read_watcher_(this), @@ -363,7 +365,8 @@ void UDPSocketLibevent::LogRead(int result, } int UDPSocketLibevent::CreateSocket(const IPEndPoint& address) { - socket_ = socket(address.GetSockAddrFamily(), SOCK_DGRAM, 0); + addr_family_ = address.GetSockAddrFamily(); + socket_ = socket(addr_family_, SOCK_DGRAM, 0); if (socket_ == kInvalidSocket) return MapSystemError(errno); if (SetNonBlocking(socket_)) { @@ -476,11 +479,40 @@ int UDPSocketLibevent::SetSocketOptions() { // port. rv = setsockopt(socket_, SOL_SOCKET, SO_REUSEPORT, &true_value, sizeof(true_value)); - if (rv < 0) - return MapSystemError(errno); -#endif // defined(OS_MACOSX) +#else rv = setsockopt(socket_, SOL_SOCKET, SO_BROADCAST, &true_value, sizeof(true_value)); +#endif // defined(OS_MACOSX) + if (rv < 0) + return MapSystemError(errno); + } + + if (!(socket_options_ & SOCKET_OPTION_MULTICAST_LOOP)) { + int rv; + if (addr_family_ == AF_INET) { + u_char loop = 0; + rv = setsockopt(socket_, IPPROTO_IP, IP_MULTICAST_LOOP, + &loop, sizeof(loop)); + } else { + u_int loop = 0; + rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, + &loop, sizeof(loop)); + } + if (rv < 0) + return MapSystemError(errno); + } + if (multicast_time_to_live_ != IP_DEFAULT_MULTICAST_TTL) { + int rv; + if (addr_family_ == AF_INET) { + u_char ttl = multicast_time_to_live_; + rv = setsockopt(socket_, IPPROTO_IP, IP_MULTICAST_TTL, + &ttl, sizeof(ttl)); + } else { + // Signed interger. -1 to use route default. + int ttl = multicast_time_to_live_; + rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + &ttl, sizeof(ttl)); + } if (rv < 0) return MapSystemError(errno); } @@ -509,4 +541,99 @@ int UDPSocketLibevent::RandomBind(const IPEndPoint& address) { return DoBind(IPEndPoint(ip, 0)); } +int UDPSocketLibevent::JoinGroup(const IPAddressNumber& group_address) const { + DCHECK(CalledOnValidThread()); + if (!is_connected()) + return ERR_SOCKET_NOT_CONNECTED; + + switch (group_address.size()) { + case kIPv4AddressSize: { + if (addr_family_ != AF_INET) + return ERR_ADDRESS_INVALID; + ip_mreq mreq; + mreq.imr_interface.s_addr = INADDR_ANY; + memcpy(&mreq.imr_multiaddr, &group_address[0], kIPv4AddressSize); + int rv = setsockopt(socket_, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)); + if (rv < 0) + return MapSystemError(errno); + return OK; + } + case kIPv6AddressSize: { + if (addr_family_ != AF_INET6) + return ERR_ADDRESS_INVALID; + ipv6_mreq mreq; + mreq.ipv6mr_interface = 0; // 0 indicates default multicast interface. + memcpy(&mreq.ipv6mr_multiaddr, &group_address[0], kIPv6AddressSize); + int rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq, sizeof(mreq)); + if (rv < 0) + return MapSystemError(errno); + return OK; + } + default: + NOTREACHED() << "Invalid address family"; + return ERR_ADDRESS_INVALID; + } +} + +int UDPSocketLibevent::LeaveGroup(const IPAddressNumber& group_address) const { + DCHECK(CalledOnValidThread()); + + if (!is_connected()) + return ERR_SOCKET_NOT_CONNECTED; + + switch (group_address.size()) { + case kIPv4AddressSize: { + if (addr_family_ != AF_INET) + return ERR_ADDRESS_INVALID; + ip_mreq mreq; + mreq.imr_interface.s_addr = INADDR_ANY; + memcpy(&mreq.imr_multiaddr, &group_address[0], kIPv4AddressSize); + int rv = setsockopt(socket_, IPPROTO_IP, IP_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)); + if (rv < 0) + return MapSystemError(errno); + return OK; + } + case kIPv6AddressSize: { + if (addr_family_ != AF_INET6) + return ERR_ADDRESS_INVALID; + ipv6_mreq mreq; + mreq.ipv6mr_interface = 0; // 0 indicates default multicast interface. + memcpy(&mreq.ipv6mr_multiaddr, &group_address[0], kIPv6AddressSize); + int rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_LEAVE_GROUP, + &mreq, sizeof(mreq)); + if (rv < 0) + return MapSystemError(errno); + return OK; + } + default: + NOTREACHED() << "Invalid address family"; + return ERR_ADDRESS_INVALID; + } +} + +int UDPSocketLibevent::SetMulticastTimeToLive(int time_to_live) { + DCHECK(CalledOnValidThread()); + if (is_connected()) + return ERR_UNEXPECTED; + + if (time_to_live < 0 || time_to_live > 255) + return ERR_INVALID_ARGUMENT; + multicast_time_to_live_ = time_to_live; + return OK; +} + +int UDPSocketLibevent::SetMulticastLoopbackMode(bool loopback) { + DCHECK(CalledOnValidThread()); + if (is_connected()) + return ERR_UNEXPECTED; + + if (loopback) + socket_options_ |= SOCKET_OPTION_MULTICAST_LOOP; + else + socket_options_ &= ~SOCKET_OPTION_MULTICAST_LOOP; + return OK; +} } // namespace net |