+// This routine is called when the main loop detects that data is available on a socket.
+mDNSlocal void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt)
+{
+ mDNSAddr senderAddr, destAddr;
+ mDNSIPPort senderPort;
+ ssize_t packetLen;
+ DNSMessage packet;
+ struct my_in_pktinfo packetInfo;
+ struct sockaddr_storage from;
+ socklen_t fromLen;
+ int flags;
+ mDNSu8 ttl;
+ mDNSBool reject;
+ const mDNSInterfaceID InterfaceID = intf ? intf->coreIntf.InterfaceID : NULL;
+
+ assert(m != NULL);
+ assert(skt >= 0);
+
+ fromLen = sizeof(from);
+ flags = 0;
+ packetLen = recvfrom_flags(skt, &packet, sizeof(packet), &flags, (struct sockaddr *) &from, &fromLen, &packetInfo, &ttl);
+
+ if (packetLen >= 0)
+ {
+ SockAddrTomDNSAddr((struct sockaddr*)&from, &senderAddr, &senderPort);
+ SockAddrTomDNSAddr((struct sockaddr*)&packetInfo.ipi_addr, &destAddr, NULL);
+
+ // If we have broken IP_RECVDSTADDR functionality (so far
+ // I've only seen this on OpenBSD) then apply a hack to
+ // convince mDNS Core that this isn't a spoof packet.
+ // Basically what we do is check to see whether the
+ // packet arrived as a multicast and, if so, set its
+ // destAddr to the mDNS address.
+ //
+ // I must admit that I could just be doing something
+ // wrong on OpenBSD and hence triggering this problem
+ // but I'm at a loss as to how.
+ //
+ // If this platform doesn't have IP_PKTINFO or IP_RECVDSTADDR, then we have
+ // no way to tell the destination address or interface this packet arrived on,
+ // so all we can do is just assume it's a multicast
+
+ #if HAVE_BROKEN_RECVDSTADDR || (!defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR))
+ if ((destAddr.NotAnInteger == 0) && (flags & MSG_MCAST))
+ {
+ destAddr.type = senderAddr.type;
+ if (senderAddr.type == mDNSAddrType_IPv4) destAddr.ip.v4 = AllDNSLinkGroup_v4.ip.v4;
+ else if (senderAddr.type == mDNSAddrType_IPv6) destAddr.ip.v6 = AllDNSLinkGroup_v6.ip.v6;
+ }
+ #endif
+
+ // We only accept the packet if the interface on which it came
+ // in matches the interface associated with this socket.
+ // We do this match by name or by index, depending on which
+ // information is available. recvfrom_flags sets the name
+ // to "" if the name isn't available, or the index to -1
+ // if the index is available. This accomodates the various
+ // different capabilities of our target platforms.
+
+ reject = mDNSfalse;
+ if (!intf)
+ {
+ // Ignore multicasts accidentally delivered to our unicast receiving socket
+ if (mDNSAddrIsDNSMulticast(&destAddr)) packetLen = -1;
+ }
+ else
+ {
+ if (packetInfo.ipi_ifname[0] != 0) reject = (strcmp(packetInfo.ipi_ifname, intf->intfName) != 0);
+ else if (packetInfo.ipi_ifindex != -1) reject = (packetInfo.ipi_ifindex != intf->index);
+
+ if (reject)
+ {
+ verbosedebugf("SocketDataReady ignored a packet from %#a to %#a on interface %s/%d expecting %#a/%s/%d/%d",
+ &senderAddr, &destAddr, packetInfo.ipi_ifname, packetInfo.ipi_ifindex,
+ &intf->coreIntf.ip, intf->intfName, intf->index, skt);
+ packetLen = -1;
+ num_pkts_rejected++;
+ if (num_pkts_rejected > (num_pkts_accepted + 1) * (num_registered_interfaces + 1) * 2)
+ {
+ fprintf(stderr,
+ "*** WARNING: Received %d packets; Accepted %d packets; Rejected %d packets because of interface mismatch\n",
+ num_pkts_accepted + num_pkts_rejected, num_pkts_accepted, num_pkts_rejected);
+ num_pkts_accepted = 0;
+ num_pkts_rejected = 0;
+ }
+ }
+ else
+ {
+ verbosedebugf("SocketDataReady got a packet from %#a to %#a on interface %#a/%s/%d/%d",
+ &senderAddr, &destAddr, &intf->coreIntf.ip, intf->intfName, intf->index, skt);
+ num_pkts_accepted++;
+ }
+ }
+ }
+
+ if (packetLen >= 0)
+ mDNSCoreReceive(m, &packet, (mDNSu8 *)&packet + packetLen,
+ &senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID);
+}
+
+mDNSexport TCPSocket *mDNSPlatformTCPSocket(TCPSocketFlags flags, mDNSAddr_Type addrType, mDNSIPPort * port,
+ domainname *hostname, mDNSBool useBackgroundTrafficClass)
+{
+ TCPSocket *sock;
+ int len = sizeof (TCPSocket);
+
+ (void)useBackgroundTrafficClass;
+
+ if (hostname)
+ {
+ len += sizeof (domainname);
+ }
+ sock = malloc(len);
+
+ if (sock == NULL)
+ {
+ LogMsg("mDNSPlatformTCPSocket: no memory for socket");
+ return NULL;
+ }
+ memset(sock, 0, sizeof *sock);
+
+ if (hostname)
+ {
+ sock->hostname = (domainname *)(sock + 1);
+ LogMsg("mDNSPlatformTCPSocket: hostname %##s", hostname->c);
+ AssignDomainName(sock->hostname, hostname);
+ }
+
+ sock->events.fd = -1;
+ if (!mDNSPosixTCPSocketSetup(&sock->events.fd, addrType, port, &sock->port))
+ {
+ if (sock->events.fd != -1) close(sock->events.fd);
+ free(sock);
+ return mDNSNULL;
+ }
+
+ // Set up the other fields in the structure.
+ sock->flags = flags;
+ sock->err = mStatus_NoError;
+ sock->setup = mDNSfalse;
+ sock->connected = mDNSfalse;
+ return sock;
+}
+
+mDNSexport mStatus mDNSPlatformTCPSocketSetCallback(TCPSocket *sock, TCPConnectionCallback callback, void *context)
+{
+ sock->callback = callback;
+ sock->context = context;
+ return mStatus_NoError;
+}
+
+mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int fd)
+{
+ TCPSocket *sock;
+
+ // XXX Add!
+ if (flags & kTCPSocketFlags_UseTLS)
+ {
+ return mDNSNULL; // not supported yet.
+ }
+
+ sock = (TCPSocket *) mDNSPlatformMemAllocateClear(sizeof *sock);
+ if (!sock)
+ {
+ return mDNSNULL;
+ }
+
+ sock->events.fd = fd;
+ sock->flags = flags;
+ sock->connected = mDNStrue;
+ return sock;
+}
+
+
+mDNSlocal void tcpListenCallback(int fd, void *context)
+{
+ TCPListener *listener = context;
+ TCPSocket *sock;
+
+ sock = mDNSPosixDoTCPListenCallback(fd, listener->addressType, listener->socketFlags,
+ listener->callback, listener->context);
+ if (sock != NULL)
+ {
+ requestReadEvents(&sock->events, "mDNSPosix::tcpListenCallback", TCPReadCallback, sock);
+ }
+}
+
+mDNSexport TCPListener *mDNSPlatformTCPListen(mDNSAddr_Type addrType, mDNSIPPort *port, mDNSAddr *addr,
+ TCPSocketFlags socketFlags, mDNSBool reuseAddr, int queueLength,
+ TCPAcceptedCallback callback, void *context)
+{
+ TCPListener *ret;
+ int fd = -1;
+
+ if (!mDNSPosixTCPListen(&fd, addrType, port, addr, reuseAddr, queueLength))
+ {
+ if (fd != -1)
+ {
+ close(fd);
+ }
+ return mDNSNULL;
+ }
+
+ // Allocate a listener structure
+ ret = (TCPListener *) mDNSPlatformMemAllocateClear(sizeof *ret);
+ if (ret == NULL)
+ {
+ LogMsg("mDNSPlatformTCPListen: no memory for TCPListener struct.");
+ close(fd);
+ return mDNSNULL;
+ }
+ ret->events.fd = fd;
+ ret->callback = callback;
+ ret->context = context;
+ ret->addressType = addrType;
+ ret->socketFlags = socketFlags;
+
+ // When we get a connection, mDNSPosixListenCallback will be called, and it will invoke the
+ // callback we were passed.
+ requestReadEvents(&ret->events, "tcpListenCallback", tcpListenCallback, ret);
+ return ret;
+}
+
+mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock)
+{
+ return sock->events.fd;
+}
+
+mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport,
+ mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context)
+{
+ int result;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } addr;
+ socklen_t len;
+
+ sock->callback = callback;
+ sock->context = context;
+ sock->setup = mDNSfalse;
+ sock->connected = mDNSfalse;
+ sock->err = mStatus_NoError;
+
+ result = fcntl(sock->events.fd, F_GETFL, 0);
+ if (result < 0)
+ {
+ LogMsg("mDNSPlatformTCPConnect: F_GETFL failed: %s", strerror(errno));
+ return mStatus_UnknownErr;
+ }
+
+ result = fcntl(sock->events.fd, F_SETFL, result | O_NONBLOCK);
+ if (result < 0)
+ {
+ LogMsg("mDNSPlatformTCPConnect: F_SETFL failed: %s", strerror(errno));
+ return mStatus_UnknownErr;
+ }
+
+ // If we've been asked to bind to a single interface, do it. See comment in mDNSMacOSX.c for more info.
+ if (InterfaceID)
+ {
+ PosixNetworkInterface *iface = (PosixNetworkInterface *)InterfaceID;
+#if defined(SO_BINDTODEVICE)
+ result = setsockopt(sock->events.fd,
+ SOL_SOCKET, SO_BINDTODEVICE, iface->intfName, strlen(iface->intfName));
+ if (result < 0)
+ {
+ LogMsg("mDNSPlatformTCPConnect: SO_BINDTODEVICE failed on %s: %s", iface->intfName, strerror(errno));
+ return mStatus_BadParamErr;
+ }
+#else
+ if (dst->type == mDNSAddrType_IPv4)
+ {
+#if defined(IP_BOUND_IF)
+ result = setsockopt(sock->events.fd, IPPROTO_IP, IP_BOUND_IF, &iface->index, sizeof iface->index);
+ if (result < 0)
+ {
+ LogMsg("mDNSPlatformTCPConnect: IP_BOUND_IF failed on %s (%d): %s",
+ iface->intfName, iface->index, strerror(errno));
+ return mStatus_BadParamErr;
+ }
+#else
+ (void)iface;
+#endif // IP_BOUND_IF
+ }
+ else
+ { // IPv6
+#if defined(IPV6_BOUND_IF)
+ result = setsockopt(sock->events.fd, IPPROTO_IPV6, IPV6_BOUND_IF, &iface->index, sizeof iface->index);
+ if (result < 0)
+ {
+ LogMsg("mDNSPlatformTCPConnect: IP_BOUND_IF failed on %s (%d): %s",
+ iface->intfName, iface->index, strerror(errno));
+ return mStatus_BadParamErr;
+ }
+#else
+ (void)iface;
+#endif // IPV6_BOUND_IF
+ }
+#endif // SO_BINDTODEVICE
+ }
+
+ memset(&addr, 0, sizeof addr);
+ if (dst->type == mDNSAddrType_IPv4)
+ {
+ addr.sa.sa_family = AF_INET;
+ addr.sin.sin_port = dstport.NotAnInteger;
+ len = sizeof (struct sockaddr_in);
+ addr.sin.sin_addr.s_addr = dst->ip.v4.NotAnInteger;
+ }
+ else
+ {
+ addr.sa.sa_family = AF_INET6;
+ len = sizeof (struct sockaddr_in6);
+ addr.sin6.sin6_port = dstport.NotAnInteger;
+ memcpy(&addr.sin6.sin6_addr.s6_addr, &dst->ip.v6, sizeof addr.sin6.sin6_addr.s6_addr);
+ }
+#ifndef NOT_HAVE_SA_LEN
+ addr.sa.sa_len = len;
+#endif