/* -*- Mode: C; tab-width: 4 -*- * * Copyright (c) 2011-2013 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mDNSEmbeddedAPI.h" #include "mDNSMacOSX.h" #include #include #include #include mDNSexport mDNS mDNSStorage; #define ValidSocket(s) ((s) >= 0) // Global to store the 4 DNS Proxy Listeners (UDPv4/6, TCPv4/6) static int dp_listener[4]; #define NUM_PROXY_TCP_CONNS 100 typedef struct { TCPSocket sock; DNSMessage *reply; mDNSu16 replyLen; mDNSu32 nread; } ProxyTCPInfo_t; // returns -1 for failures including the other end closing the socket // returns 0 if successful in reading data, but still not read the data fully // returns 1 if successful in reading all the data mDNSlocal int ProxyTCPRead(ProxyTCPInfo_t *tcpInfo) { long n; mDNSBool closed; if (tcpInfo->nread < 2) // First read the two-byte length preceeding the DNS message { mDNSu8 *lenptr = (mDNSu8 *)&tcpInfo->replyLen; n = mDNSPlatformReadTCP(&tcpInfo->sock, lenptr + tcpInfo->nread, 2 - tcpInfo->nread, &closed); if (n < 0 || closed) { LogMsg("ProxyTCPRead: attempt to read message length failed"); return -1; } tcpInfo->nread += n; if (tcpInfo->nread < 2) { LogMsg("ProxyTCPRead: nread %d, n %d", tcpInfo->nread, n); return 0; } tcpInfo->replyLen = (mDNSu16)((mDNSu16)lenptr[0] << 8 | lenptr[1]); if (tcpInfo->replyLen < sizeof(DNSMessageHeader)) { LogMsg("ProxyTCPRead: Message length too short (%d bytes)", tcpInfo->replyLen); return -1; } tcpInfo->reply = mallocL("ProxyTCPInfo", tcpInfo->replyLen); if (!tcpInfo->reply) { LogMsg("ProxyTCPRead: Memory failure"); return -1; } } n = mDNSPlatformReadTCP(&tcpInfo->sock, ((char *)tcpInfo->reply) + (tcpInfo->nread - 2), tcpInfo->replyLen - (tcpInfo->nread - 2), &closed); if (n < 0 || closed) { LogMsg("ProxyTCPRead: read failure n %d, closed %d", n, closed); return -1; } tcpInfo->nread += n; if ((tcpInfo->nread - 2) != tcpInfo->replyLen) return 0; else return 1; } mDNSlocal void ProxyTCPSocketCallBack(int s1, short filter, void *context, __unused mDNSBool encounteredEOF) { int ret; struct sockaddr_storage from; struct sockaddr_storage to; mDNSAddr senderAddr, destAddr; mDNSIPPort senderPort; ProxyTCPInfo_t *ti = (ProxyTCPInfo_t *)context; TCPSocket *sock = &ti->sock; KQSocketSet *kq = &sock->ss; struct tcp_info tcp_if; socklen_t size = sizeof(tcp_if); int32_t intf_id = 0; (void) filter; ret = ProxyTCPRead(ti); if (ret == -1) { mDNSPlatformDisposeProxyContext(ti); return; } else if (!ret) { debugf("ProxyTCPReceive: Not yet read completely Actual length %d, Read length %d", ti->replyLen, ti->nread); return; } // We read all the data and hence not interested in read events anymore KQueueSet(s1, EV_DELETE, EVFILT_READ, sock->kqEntry); mDNSPlatformMemZero(&to, sizeof(to)); mDNSPlatformMemZero(&from, sizeof(from)); socklen_t len = sizeof(to); ret = getsockname(s1, (struct sockaddr*) &to, &len); if (ret < 0) { LogMsg("ProxyTCPReceive: getsockname(fd=%d) errno %d", s1, errno); mDNSPlatformDisposeProxyContext(ti); return; } ret = getpeername(s1, (struct sockaddr*) &from, &len); if (ret < 0) { LogMsg("ProxyTCPReceive: getpeername(fd=%d) errno %d", s1, errno); mDNSPlatformDisposeProxyContext(ti); return; } if (getsockopt(s1, IPPROTO_TCP, TCP_INFO, &tcp_if, &size) != 0) { LogMsg("ProxyTCPReceive: getsockopt for TCP_INFO failed (fd=%d) errno %d", s1, errno); return; } intf_id = tcp_if.tcpi_last_outif; if (from.ss_family == AF_INET) { struct sockaddr_in *s = (struct sockaddr_in*)&from; senderAddr.type = mDNSAddrType_IPv4; senderAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr; senderPort.NotAnInteger = s->sin_port; s = (struct sockaddr_in *)&to; destAddr.type = mDNSAddrType_IPv4; destAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr; LogInfo("ProxyTCPReceive received IPv4 packet(len %d) from %#-15a to %#-15a on skt %d %s ifindex %d", ti->replyLen, &senderAddr, &destAddr, s1, NULL, intf_id); } else if (from.ss_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from; senderAddr.type = mDNSAddrType_IPv6; senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; senderPort.NotAnInteger = sin6->sin6_port; sin6 = (struct sockaddr_in6 *)&to; destAddr.type = mDNSAddrType_IPv6; destAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; LogInfo("ProxyTCPReceive received IPv6 packet(len %d) from %#-15a to %#-15a on skt %d %s ifindex %d", ti->replyLen, &senderAddr, &destAddr, s1, NULL, intf_id); } else { LogMsg("ProxyTCPReceive from is unknown address family %d", from.ss_family); mDNSPlatformDisposeProxyContext(ti); return; } // We pass sock for the TCPSocket and the "ti" for context as that's what we want to free at the end. // In the UDP case, there is just a single socket and nothing to free. Hence, the context (last argument) // would be NULL. kq->m->p->TCPProxyCallback(sock, ti->reply, (mDNSu8 *)ti->reply + ti->replyLen, &senderAddr, senderPort, &destAddr, UnicastDNSPort, (mDNSInterfaceID)(uintptr_t)intf_id, ti); } mDNSlocal void ProxyTCPAccept(int s1, short filter, void *context, __unused mDNSBool encounteredEOF) { int newfd; struct sockaddr_storage ss; socklen_t sslen = sizeof(ss); const int on = 1; KQSocketSet *listenSet = (KQSocketSet *)context; (void) filter; while ((newfd = accept(s1, (struct sockaddr *)&ss, &sslen)) != -1) { int err; int *s; KQueueEntry *k; KQSocketSet *kq; // Even though we just need a single KQueueEntry, for simplicity we re-use // the KQSocketSet ProxyTCPInfo_t *ti = mallocL("ProxyTCPContext", sizeof(ProxyTCPInfo_t)); if (!ti) { LogMsg("ProxyTCPAccept: cannot allocate TCPSocket"); close(newfd); return; } mDNSPlatformMemZero(ti, sizeof(ProxyTCPInfo_t)); TCPSocket *sock = &ti->sock; kq = &sock->ss; kq->sktv4 = -1; kq->sktv6 = -1; kq->m = listenSet->m; fcntl(newfd, F_SETFL, fcntl(newfd, F_GETFL, 0) | O_NONBLOCK); // set non-blocking if (ss.ss_family == AF_INET) { s = &kq->sktv4; k = &kq->kqsv4; // Receive interface identifiers err = setsockopt(newfd, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); if (err) { LogMsg("ProxyTCPAccept: IP_RECVIF %d errno %d (%s)", newfd, errno, strerror(errno)); mDNSPlatformDisposeProxyContext(ti); close(newfd); return; } } else { s = &kq->sktv6; k = &kq->kqsv6; // We want to receive destination addresses and receive interface identifiers err = setsockopt(newfd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); if (err) { LogMsg("ProxyTCPAccept: IP_RECVPKTINFO %d errno %d (%s)", newfd, errno, strerror(errno)); mDNSPlatformDisposeProxyContext(ti); close(newfd); return; } } *s = newfd; // mDNSPlatformReadTCP/WriteTCP (unlike the UDP counterpart) does not provide the destination address // from which we can infer the destination address family. Hence we need to remember that here. // Instead of remembering the address family, we remember the right fd. sock->fd = newfd; sock->kqEntry = k; k->KQcallback = ProxyTCPSocketCallBack; k->KQcontext = ti; k->KQtask = "TCP Proxy packet reception"; #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM k->readSource = mDNSNULL; k->writeSource = mDNSNULL; k->fdClosed = mDNSfalse; #endif KQueueSet(*s, EV_ADD, EVFILT_READ, k); } } mDNSlocal mStatus SetupUDPProxySocket(int skt, KQSocketSet *cp, u_short sa_family, mDNSBool useBackgroundTrafficClass) { int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; const int on = 1; mStatus err = mStatus_NoError; cp->m = &mDNSStorage; cp->closeFlag = mDNSNULL; // set default traffic class // setTrafficClass(skt, mDNSfalse); (void) useBackgroundTrafficClass; if (sa_family == AF_INET) { err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); if (err < 0) { LogMsg("SetupUDPProxySocket: IP_RECVDSTADDR %d errno %d (%s)", skt, errno, strerror(errno)); return err; } // We want to receive interface identifiers err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); if (err < 0) { LogMsg("SetupUDPProxySocket: IP_RECVIF %d errno %d (%s)", skt, errno, strerror(errno)); return err; } } else if (sa_family == AF_INET6) { // We want to receive destination addresses and receive interface identifiers err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); if (err < 0) { LogMsg("SetupUDPProxySocket: IPV6_RECVPKTINFO %d errno %d (%s)", skt, errno, strerror(errno)); return err; } // We want to receive packet hop count value so we can check it err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)); if (err < 0) { LogMsg("SetupUDPProxySocket: IPV6_RECVHOPLIMIT %d errno %d (%s)", skt, errno, strerror(errno)); return err; } } else { LogMsg("SetupUDPProxySocket: wrong family %d", sa_family); return -1; } if (fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK) < 0) { LogMsg("SetupUDPProxySocket: fnctl failed %d", errno); return -1; } *s = skt; //k->KQcallback = ProxyUDPSocketCallBack; k->KQcallback = myKQSocketCallBack; k->KQcontext = cp; k->KQtask = "UDP Proxy packet reception"; #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM k->readSource = mDNSNULL; k->writeSource = mDNSNULL; k->fdClosed = mDNSfalse; #endif KQueueSet(*s, EV_ADD, EVFILT_READ, k); return(err); } mDNSlocal mStatus SetupTCPProxySocket(int skt, KQSocketSet *cp, u_short sa_family, mDNSBool useBackgroundTrafficClass) { int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; mStatus err; cp->m = &mDNSStorage; // XXX may not be used by the TCP codepath cp->closeFlag = mDNSNULL; // for TCP sockets, the traffic class is set once and not changed // setTrafficClass(skt, useBackgroundTrafficClass); (void) useBackgroundTrafficClass; // All the socket setup has already been done err = listen(skt, NUM_PROXY_TCP_CONNS); if (err) { LogMsg("SetupTCPProxySocket: listen %d errno %d (%s)", skt, errno, strerror(errno)); return err; } fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking *s = skt; k->KQcallback = ProxyTCPAccept; k->KQcontext = cp; k->KQtask = "TCP Accept"; #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM k->readSource = mDNSNULL; k->writeSource = mDNSNULL; k->fdClosed = mDNSfalse; #endif KQueueSet(*s, EV_ADD, EVFILT_READ, k); return mStatus_NoError; } mDNSlocal void BindDPSocket(int fd, int sa_family) { int err; const int on = 1; if (sa_family == AF_INET) { struct sockaddr_in addr; err = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); if (err < 0) LogMsg("BindDPSocket: setsockopt SO_REUSEPORT failed for IPv4 %d errno %d (%s)", fd, errno, strerror(errno)); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(53); err = bind(fd, (struct sockaddr*) &addr, sizeof(addr)); if (err) { LogMsg("BindDPSocket: bind %d errno %d (%s)", fd, errno, strerror(errno)); return; } } else { struct sockaddr_in6 addr6; // We want to receive only IPv6 packets. Without this option we get IPv4 packets too, // with mapped addresses of the form 0:0:0:0:0:FFFF:xxxx:xxxx, where xxxx:xxxx is the IPv4 address err = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); if (err < 0) { LogMsg("DPFBindSocket: setsockopt IPV6_V6ONLY %d errno %d (%s)", fd, errno, strerror(errno)); return; } err = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); if (err < 0) LogMsg("BindDPSocket: setsockopt SO_REUSEPORT failed for V6 %d errno %d (%s)", fd, errno, strerror(errno)); memset(&addr6, 0, sizeof(addr6)); addr6.sin6_family = AF_INET6; addr6.sin6_port = htons(53); err = bind(fd, (struct sockaddr*) &addr6, sizeof(addr6)); if (err) { LogMsg("BindDPSocket: bind6 %d errno %d (%s)", fd, errno, strerror(errno)); return; } } } // Setup DNS Proxy Skts in main kevent loop and set the skt options mDNSlocal void SetupDNSProxySkts(int fd[4]) { mDNS *const m = &mDNSStorage; int i; mStatus err; KQSocketSet *udpSS; KQSocketSet *tcpSS; udpSS = &m->p->UDPProxy.ss; tcpSS = &m->p->TCPProxy.ss; udpSS->port = UnicastDNSPort; tcpSS->port = UnicastDNSPort; LogMsg("SetupDNSProxySkts: %d, %d, %d, %d", fd[0], fd[1], fd[2], fd[3]); // myKQSocketCallBack checks for proxy and calls the m->p->ProxyCallback instead of mDNSCoreReceive udpSS->proxy = mDNStrue; err = SetupUDPProxySocket(fd[0], udpSS, AF_INET, mDNSfalse); if (err) LogMsg("SetupDNSProxySkts: ERROR!! UDPv4 Socket"); err = SetupUDPProxySocket(fd[1], udpSS, AF_INET6, mDNSfalse); if (err) LogMsg("SetupDNSProxySkts: ERROR!! UDPv6 Socket"); err = SetupTCPProxySocket(fd[2], tcpSS, AF_INET, mDNSfalse); if (err) LogMsg("SetupDNSProxySkts: ERROR!! TCPv4 Socket"); err = SetupTCPProxySocket(fd[3], tcpSS, AF_INET6, mDNSfalse); if (err) LogMsg("SetupDNSProxySkts: ERROR!! TCPv6 Socket"); for (i = 0; i < 4; i++) dp_listener[i] = fd[i]; } // Create and bind the DNS Proxy Skts for use mDNSexport void mDNSPlatformInitDNSProxySkts(ProxyCallback UDPCallback, ProxyCallback TCPCallback) { int dpskt[4]; dpskt[0] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); dpskt[1] = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); dpskt[2] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); dpskt[3] = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); // Close all DNS Proxy skts in case any of them are invalid if (!ValidSocket(dpskt[0]) || !ValidSocket(dpskt[1]) || !ValidSocket(dpskt[2]) || !ValidSocket(dpskt[3])) { if (ValidSocket(dpskt[0])) close(dpskt[0]); if (ValidSocket(dpskt[1])) close(dpskt[1]); if (ValidSocket(dpskt[2])) close(dpskt[2]); if (ValidSocket(dpskt[3])) close(dpskt[3]); } BindDPSocket(dpskt[0], AF_INET); BindDPSocket(dpskt[1], AF_INET6); BindDPSocket(dpskt[2], AF_INET); BindDPSocket(dpskt[3], AF_INET6); LogInfo("mDNSPlatformInitDNSProxySkts: Opened Listener Sockets for DNS Proxy : %d, %d, %d, %d", dpskt[0], dpskt[1], dpskt[2], dpskt[3]); mDNSStorage.p->UDPProxyCallback = UDPCallback; mDNSStorage.p->TCPProxyCallback = TCPCallback; SetupDNSProxySkts(dpskt); } mDNSexport void mDNSPlatformCloseDNSProxySkts(mDNS *const m) { (void) m; int i; for (i = 0; i < 4; i++) close(dp_listener[i]); LogInfo("mDNSPlatformCloseDNSProxySkts: Closing DNS Proxy Listener Sockets"); } mDNSexport void mDNSPlatformDisposeProxyContext(void *context) { ProxyTCPInfo_t *ti; TCPSocket *sock; KQSocketSet *kq; if (!context) return; ti = (ProxyTCPInfo_t *)context; sock = &ti->sock; kq = &sock->ss; if (kq->sktv4 != -1) { shutdown(kq->sktv4, 2); mDNSPlatformCloseFD(&kq->kqsv4, kq->sktv4); } if (kq->sktv6 != -1) { shutdown(kq->sktv6, 2); mDNSPlatformCloseFD(&kq->kqsv6, kq->sktv6); } if (kq->closeFlag) *kq->closeFlag = 1; if (ti->reply) freeL("ProxyTCPInfoLen", ti->reply); freeL("ProxyTCPContext", ti); }