mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
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.
This commit is contained in:
parent
c00b44eefa
commit
aab9cdcce4
@ -853,20 +853,23 @@ void Server::sendMessage(ServerUser *u, const char *data, int len, QByteArray &c
|
||||
msg.msg_controllen = CMSG_SPACE((u->saiUdpAddress.ss_family == AF_INET6) ? sizeof(struct in6_pktinfo) : sizeof(struct in_pktinfo));
|
||||
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (u->saiTcpLocalAddress.ss_family == AF_INET6) {
|
||||
HostAddress tcpha(u->saiTcpLocalAddress);
|
||||
if (u->saiUdpAddress.ss_family == AF_INET6) {
|
||||
cmsg->cmsg_level = IPPROTO_IPV6;
|
||||
cmsg->cmsg_type = IPV6_PKTINFO;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
|
||||
struct in6_pktinfo *pktinfo = reinterpret_cast<struct in6_pktinfo *>(CMSG_DATA(cmsg));
|
||||
memset(pktinfo, 0, sizeof(*pktinfo));
|
||||
pktinfo->ipi6_addr = reinterpret_cast<struct sockaddr_in6 *>(& u->saiTcpLocalAddress)->sin6_addr;
|
||||
memcpy(&pktinfo->ipi6_addr.s6_addr[0], &tcpha.qip6.c[0], sizeof(pktinfo->ipi6_addr.s6_addr));
|
||||
} else {
|
||||
cmsg->cmsg_level = IPPROTO_IP;
|
||||
cmsg->cmsg_type = IP_PKTINFO;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
|
||||
struct in_pktinfo *pktinfo = reinterpret_cast<struct in_pktinfo *>(CMSG_DATA(cmsg));
|
||||
memset(pktinfo, 0, sizeof(*pktinfo));
|
||||
pktinfo->ipi_spec_dst = reinterpret_cast<struct sockaddr_in *>(& u->saiTcpLocalAddress)->sin_addr;
|
||||
if (tcpha.isV6())
|
||||
return;
|
||||
pktinfo->ipi_spec_dst.s_addr = tcpha.hash[3];
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user