Skip to content

Commit aab9cdc

Browse files
committed
Server::sendMessage: encode the source IP of outgoing UDP packets to match the UDP socket's address family.
Murmur uses the saiTcpLocalAddress field of ServerUser to determine the correct source address of outgoing UDP packets. On some systems, such as multi-homed setups, this is important for correct datagram delivery. The saiTcpLocalAddress field is initialized when the client first connects. Its value is extracted from QHostAddress, and then converted to a sockaddr struct via our own HostAddress class's toSockAddr method. HostAddress::toSockAddr is implemented such that any IPv4 address (including IPv4-mapped IPv6 addresses) will always cause a sockaddr struct belonging to the AF_INET (IPv4) family to be output. Pure IPv6 addresses will be output as an AF_INET6 sockaddr. The code that this change touches assumed that it could use the value of saiTcpLocalAddress directly as the source address in UDP packets. This is not always the case. On most systems, Murmur will listen on [::]:64738 by default, and as such, the address of the server's UDP socket will be in the AF_INET6 family. Attempting to send packets with a source address in the AF_INET family using that socket will cause sendmsg() to return EINVAL on some systems, if not all. The new code for the sendmsg() code path converts the ServerUser's saiTcpLocalAddress back into a HostAddress, which, internally, is fully IPv6. If the input address is IPv4, HostAddress will convert it to an IPv4-mapped IPv6 address. When the client's UDP socket is of the AF_INET6 family, we can now trust that the HostAddress, 'tcpha', is either a real IPv6 address, or an IPv4-mapped IPv6 address. This allows us to use the 'tcpha' address regardless of the address family of the saiTcpLocalAddress. We can simply memcpy it in place. When the UDP socket is of the AF_INET family, we can only set the source address if the saiTcpLocalAddress is also AF_INET, or if it is of the AF_INET6 family and its address is an IPv4-mapped IPv6 address. When a pure IPv6 address is encountered in that path, it is simply dropped.
1 parent c00b44e commit aab9cdc

File tree

1 file changed

+6
-3
lines changed

1 file changed

+6
-3
lines changed

src/murmur/Server.cpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -853,20 +853,23 @@ void Server::sendMessage(ServerUser *u, const char *data, int len, QByteArray &c
853853
msg.msg_controllen = CMSG_SPACE((u->saiUdpAddress.ss_family == AF_INET6) ? sizeof(struct in6_pktinfo) : sizeof(struct in_pktinfo));
854854

855855
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
856-
if (u->saiTcpLocalAddress.ss_family == AF_INET6) {
856+
HostAddress tcpha(u->saiTcpLocalAddress);
857+
if (u->saiUdpAddress.ss_family == AF_INET6) {
857858
cmsg->cmsg_level = IPPROTO_IPV6;
858859
cmsg->cmsg_type = IPV6_PKTINFO;
859860
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
860861
struct in6_pktinfo *pktinfo = reinterpret_cast<struct in6_pktinfo *>(CMSG_DATA(cmsg));
861862
memset(pktinfo, 0, sizeof(*pktinfo));
862-
pktinfo->ipi6_addr = reinterpret_cast<struct sockaddr_in6 *>(& u->saiTcpLocalAddress)->sin6_addr;
863+
memcpy(&pktinfo->ipi6_addr.s6_addr[0], &tcpha.qip6.c[0], sizeof(pktinfo->ipi6_addr.s6_addr));
863864
} else {
864865
cmsg->cmsg_level = IPPROTO_IP;
865866
cmsg->cmsg_type = IP_PKTINFO;
866867
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
867868
struct in_pktinfo *pktinfo = reinterpret_cast<struct in_pktinfo *>(CMSG_DATA(cmsg));
868869
memset(pktinfo, 0, sizeof(*pktinfo));
869-
pktinfo->ipi_spec_dst = reinterpret_cast<struct sockaddr_in *>(& u->saiTcpLocalAddress)->sin_addr;
870+
if (tcpha.isV6())
871+
return;
872+
pktinfo->ipi_spec_dst.s_addr = tcpha.hash[3];
870873
}
871874

872875

0 commit comments

Comments
 (0)