OSDN Git Service

Initial release of mdns interface.
authorRobert Greenwalt <rgreenwalt@google.com>
Thu, 29 Mar 2012 21:45:54 +0000 (14:45 -0700)
committerRobert Greenwalt <rgreenwalt@google.com>
Thu, 12 Apr 2012 22:45:27 +0000 (15:45 -0700)
Uses extern/mdnsresponder and communicate with the framework via nativedaemonconnector.

Change-Id: I5c090528197afa090836d7cb5bf75dfba33ff11c

Android.mk
MDnsSdListener.cpp [new file with mode: 0644]
MDnsSdListener.h [new file with mode: 0644]
ResponseCode.h
main.cpp
ndc.c

index 2c17979..76f395d 100644 (file)
@@ -6,6 +6,7 @@ LOCAL_SRC_FILES:=                                      \
                   BandwidthController.cpp              \
                   CommandListener.cpp                  \
                   DnsProxyListener.cpp                 \
+                  MDnsSdListener.cpp                   \
                   NatController.cpp                    \
                   NetdCommand.cpp                      \
                   NetdConstants.cpp                    \
@@ -28,6 +29,7 @@ LOCAL_MODULE:= netd
 LOCAL_C_INCLUDES := $(KERNEL_HEADERS) \
                     $(LOCAL_PATH)/../bluetooth/bluedroid/include \
                     $(LOCAL_PATH)/../bluetooth/bluez-clean-headers \
+                    external/mdnsresponder/mDNSShared \
                     external/openssl/include \
                     external/stlport/stlport \
                     bionic \
@@ -37,7 +39,7 @@ LOCAL_C_INCLUDES := $(KERNEL_HEADERS) \
 LOCAL_CFLAGS := -Werror=format
 
 LOCAL_SHARED_LIBRARIES := libstlport libsysutils libcutils libnetutils \
-                          libcrypto libhardware_legacy
+                          libcrypto libhardware_legacy libmdnssd
 
 ifneq ($(BOARD_HOSTAPD_DRIVER),)
   LOCAL_CFLAGS += -DHAVE_HOSTAPD
diff --git a/MDnsSdListener.cpp b/MDnsSdListener.cpp
new file mode 100644 (file)
index 0000000..ff401e0
--- /dev/null
@@ -0,0 +1,720 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 <arpa/inet.h>
+#include <dirent.h>
+#include <errno.h>
+#include <linux/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <string.h>
+
+#define LOG_TAG "MDnsDS"
+#define DBG 1
+#define VDBG 1
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <sysutils/SocketClient.h>
+
+#include "MDnsSdListener.h"
+#include "ResponseCode.h"
+
+#define MDNS_SERVICE_NAME "mdnsd"
+#define MDNS_SERVICE_STATUS "init.svc.mdnsd"
+
+MDnsSdListener::MDnsSdListener() :
+                 FrameworkListener("mdns", true) {
+    Monitor *m = new Monitor();
+    registerCmd(new Handler(m, this));
+}
+
+MDnsSdListener::Handler::Handler(Monitor *m, MDnsSdListener *listener) :
+   NetdCommand("mdnssd") {
+   if (DBG) ALOGD("MDnsSdListener::Hander starting up");
+   mMonitor = m;
+   mListener = listener;
+}
+
+MDnsSdListener::Handler::~Handler() {}
+
+void MDnsSdListener::Handler::discover(SocketClient *cli,
+        const char *iface,
+        const char *regType,
+        const char *domain,
+        const int requestId,
+        const int requestFlags) {
+    if (VDBG) {
+        ALOGD("discover(%s, %s, %s, %d, %d)", iface, regType, domain, requestId,
+                requestFlags);
+    }
+    Context *context = new Context(requestId, mListener);
+    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
+    if (ref == NULL) {
+        ALOGE("requestId %d already in use during discover call", requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "RequestId already in use during discover call", false);
+        return;
+    }
+    if (VDBG) ALOGD("using ref %p", ref);
+    DNSServiceFlags nativeFlags = iToFlags(requestFlags);
+    int interfaceInt = ifaceNameToI(iface);
+
+    DNSServiceErrorType result = DNSServiceBrowse(ref, nativeFlags, interfaceInt, regType,
+            domain, &MDnsSdListenerDiscoverCallback, context);
+    if (result != kDNSServiceErr_NoError) {
+        ALOGE("Discover request %d got an error from DNSServiceBrowse %d", requestId, result);
+        mMonitor->freeServiceRef(requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "Discover request got an error from DNSServiceBrowse", false);
+        return;
+    }
+    mMonitor->startMonitoring(requestId);
+    if (VDBG) ALOGD("discover successful");
+    cli->sendMsg(ResponseCode::CommandOkay, "Discover operation started", false);
+    return;
+}
+
+void MDnsSdListenerDiscoverCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
+        uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName,
+        const char *regType, const char *replyDomain, void *inContext) {
+    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
+    char *msg;
+    int refNumber = context->mRefNumber;
+
+    if (errorCode != kDNSServiceErr_NoError) {
+        asprintf(&msg, "%d %d", refNumber, errorCode);
+        context->mListener->sendBroadcast(ResponseCode::ServiceDiscoveryFailed, msg, false);
+        if (DBG) ALOGE("discover failure for %d, error= %d", refNumber, errorCode);
+    } else {
+        int respCode;
+        if (flags & kDNSServiceFlagsAdd) {
+            if (VDBG) {
+                ALOGD("Discover found new serviceName %s, regType %s and domain %s for %d",
+                        serviceName, regType, replyDomain, refNumber);
+            }
+            respCode = ResponseCode::ServiceDiscoveryServiceAdded;
+        } else {
+            if (VDBG) {
+                ALOGD("Discover lost serviceName %s, regType %s and domain %s for %d",
+                        serviceName, regType, replyDomain, refNumber);
+            }
+            respCode = ResponseCode::ServiceDiscoveryServiceRemoved;
+        }
+        asprintf(&msg, "%d %s %s %s", refNumber, serviceName, regType, replyDomain);
+        context->mListener->sendBroadcast(respCode, msg, false);
+    }
+    free(msg);
+}
+
+void MDnsSdListener::Handler::stop(SocketClient *cli, int argc, char **argv, const char *str) {
+    if (argc != 3) {
+        char *msg;
+        asprintf(&msg, "Invalid number of arguments to %s", str);
+        cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+        free(msg);
+        return;
+    }
+    int requestId = atoi(argv[2]);
+    DNSServiceRef *ref = mMonitor->lookupServiceRef(requestId);
+    if (ref == NULL) {
+        if (DBG) ALOGE("%s stop used unknown requestId %d", str, requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError, "Unknown requestId", false);
+        return;
+    }
+    if (VDBG) ALOGD("Stopping %s with ref %p", str, ref);
+    DNSServiceRefDeallocate(*ref);
+    mMonitor->freeServiceRef(requestId);
+    char *msg;
+    asprintf(&msg, "%s stopped", str);
+    cli->sendMsg(ResponseCode::CommandOkay, msg, false);
+    free(msg);
+}
+
+void MDnsSdListener::Handler::serviceRegister(SocketClient *cli, int requestId,
+        const char *interfaceName, const char *serviceName, const char *serviceType,
+        const char *domain, const char *host, int port, int txtLen, void *txtRecord) {
+    if (VDBG) {
+        ALOGD("serviceRegister(%d, %s, %s, %s, %s, %s, %d, %d, <binary>)", requestId,
+                interfaceName, serviceName, serviceType, domain, host, port, txtLen);
+    }
+    Context *context = new Context(requestId, mListener);
+    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
+    port = htons(port);
+    if (ref == NULL) {
+        ALOGE("requestId %d already in use during register call", requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "RequestId already in use during register call", false);
+        return;
+    }
+    DNSServiceFlags nativeFlags = 0;
+    int interfaceInt = ifaceNameToI(interfaceName);
+    DNSServiceErrorType result = DNSServiceRegister(ref, interfaceInt, nativeFlags, serviceName,
+            serviceType, domain, host, port, txtLen, txtRecord, &MDnsSdListenerRegisterCallback,
+            context);
+    if (result != kDNSServiceErr_NoError) {
+        ALOGE("service register request %d got an error from DNSServiceRegister %d", requestId,
+                result);
+        mMonitor->freeServiceRef(requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "serviceRegister request got an error from DNSServiceRegister", false);
+        return;
+    }
+    mMonitor->startMonitoring(requestId);
+    if (VDBG) ALOGD("serviceRegister successful");
+    cli->sendMsg(ResponseCode::CommandOkay, "serviceRegister started", false);
+    return;
+}
+
+void MDnsSdListenerRegisterCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
+        DNSServiceErrorType errorCode, const char *serviceName, const char *regType,
+        const char *domain, void *inContext) {
+    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
+    char *msg;
+    int refNumber = context->mRefNumber;
+    if (errorCode != kDNSServiceErr_NoError) {
+        asprintf(&msg, "%d %d", refNumber, errorCode);
+        context->mListener->sendBroadcast(ResponseCode::ServiceRegistrationFailed, msg, false);
+        if (DBG) ALOGE("register failure for %d, error= %d", refNumber, errorCode);
+    } else {
+        asprintf(&msg, "%d %s", refNumber, serviceName);
+        context->mListener->sendBroadcast(ResponseCode::ServiceRegistrationSucceeded, msg, false);
+        if (VDBG) ALOGD("register succeeded for %d as %s", refNumber, serviceName);
+    }
+    free(msg);
+}
+
+
+void MDnsSdListener::Handler::resolveService(SocketClient *cli, int requestId,
+        const char *interfaceName, const char *serviceName, const char *regType,
+        const char *domain) {
+    if (VDBG) {
+        ALOGD("resolveService(%d, %s, %s, %s, %s)", requestId, interfaceName,
+                serviceName, regType, domain);
+    }
+    Context *context = new Context(requestId, mListener);
+    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
+    if (ref == NULL) {
+        ALOGE("request Id %d already in use during resolve call", requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "RequestId already in use during resolve call", false);
+        return;
+    }
+    DNSServiceFlags nativeFlags = 0;
+    int interfaceInt = ifaceNameToI(interfaceName);
+    DNSServiceErrorType result = DNSServiceResolve(ref, nativeFlags, interfaceInt, serviceName,
+            regType, domain, &MDnsSdListenerResolveCallback, context);
+    if (result != kDNSServiceErr_NoError) {
+        ALOGE("service resolve request %d got an error from DNSServiceResolve %d", requestId,
+                result);
+        mMonitor->freeServiceRef(requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "resolveService got an error from DNSServiceResolve", false);
+        return;
+    }
+    mMonitor->startMonitoring(requestId);
+    if (VDBG) ALOGD("resolveService successful");
+    cli->sendMsg(ResponseCode::CommandOkay, "resolveService started", false);
+    return;
+}
+
+void MDnsSdListenerResolveCallback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interface,
+        DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port,
+        uint16_t txtLen, const unsigned char *txtRecord, void *inContext) {
+    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
+    char *msg;
+    int refNumber = context->mRefNumber;
+    port = ntohs(port);
+    if (errorCode != kDNSServiceErr_NoError) {
+        asprintf(&msg, "%d %d", refNumber, errorCode);
+        context->mListener->sendBroadcast(ResponseCode::ServiceResolveFailed, msg, false);
+        if (DBG) ALOGE("resolve failure for %d, error= %d", refNumber, errorCode);
+    } else {
+        asprintf(&msg, "%d %s %s %d %d", refNumber, fullname, hosttarget, port, txtLen);
+        context->mListener->sendBroadcast(ResponseCode::ServiceResolveSuccess, msg, false);
+        if (VDBG) {
+            ALOGD("resolve succeeded for %d finding %s at %s:%d with txtLen %d",
+                    refNumber, fullname, hosttarget, port, txtLen);
+        }
+    }
+    free(msg);
+}
+
+void MDnsSdListener::Handler::getAddrInfo(SocketClient *cli, int requestId,
+        const char *interfaceName, uint32_t protocol, const char *hostname) {
+    if (VDBG) ALOGD("getAddrInfo(%d, %s %d, %s)", requestId, interfaceName, protocol, hostname);
+    Context *context = new Context(requestId, mListener);
+    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
+    if (ref == NULL) {
+        ALOGE("request ID %d already in use during getAddrInfo call", requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "RequestId already in use during getAddrInfo call", false);
+        return;
+    }
+    DNSServiceFlags nativeFlags = 0;
+    int interfaceInt = ifaceNameToI(interfaceName);
+    DNSServiceErrorType result = DNSServiceGetAddrInfo(ref, nativeFlags, interfaceInt, protocol,
+            hostname, &MDnsSdListenerGetAddrInfoCallback, context);
+    if (result != kDNSServiceErr_NoError) {
+        ALOGE("getAddrInfo request %d got an error from DNSServiceGetAddrInfo %d", requestId,
+                result);
+        mMonitor->freeServiceRef(requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "getAddrInfo request got an error from DNSServiceGetAddrInfo", false);
+        return;
+    }
+    mMonitor->startMonitoring(requestId);
+    if (VDBG) ALOGD("getAddrInfo successful");
+    cli->sendMsg(ResponseCode::CommandOkay, "getAddrInfo started", false);
+    return;
+}
+
+void MDnsSdListenerGetAddrInfoCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
+        uint32_t interface, DNSServiceErrorType errorCode, const char *hostname,
+        const struct sockaddr *const sa, uint32_t ttl, void *inContext) {
+    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
+    int refNumber = context->mRefNumber;
+
+    if (errorCode != kDNSServiceErr_NoError) {
+        char *msg;
+        asprintf(&msg, "%d %d", refNumber, errorCode);
+        context->mListener->sendBroadcast(ResponseCode::ServiceGetAddrInfoFailed, msg, false);
+        if (DBG) ALOGE("getAddrInfo failure for %d, error= %d", refNumber, errorCode);
+        free(msg);
+    } else {
+        char addr[INET6_ADDRSTRLEN];
+        char *msg;
+        if (sa->sa_family == AF_INET) {
+            inet_ntop(sa->sa_family, &(((struct sockaddr_in *)sa)->sin_addr), addr, sizeof(addr));
+        } else {
+            inet_ntop(sa->sa_family, &(((struct sockaddr_in6 *)sa)->sin6_addr), addr, sizeof(addr));
+        }
+        asprintf(&msg, "%d %s %d %s", refNumber, hostname, ttl, addr);
+        context->mListener->sendBroadcast(ResponseCode::ServiceGetAddrInfoSuccess, msg, false);
+        if (VDBG) {
+            ALOGD("getAddrInfo succeeded for %d: %s", refNumber, msg);
+        }
+        free(msg);
+    }
+}
+
+void MDnsSdListener::Handler::setHostname(SocketClient *cli, int requestId,
+        const char *hostname) {
+    if (VDBG) ALOGD("setHostname(%d, %s)", requestId, hostname);
+    Context *context = new Context(requestId, mListener);
+    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
+    if (ref == NULL) {
+        ALOGE("request Id %d already in use during setHostname call", requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "RequestId already in use during setHostname call", false);
+        return;
+    }
+    DNSServiceFlags nativeFlags = 0;
+    DNSServiceErrorType result = DNSSetHostname(ref, nativeFlags, hostname,
+            &MDnsSdListenerSetHostnameCallback, context);
+    if (result != kDNSServiceErr_NoError) {
+        ALOGE("setHostname request %d got an error from DNSSetHostname %d", requestId, result);
+        mMonitor->freeServiceRef(requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "setHostname got an error from DNSSetHostname", false);
+        return;
+    }
+    mMonitor->startMonitoring(requestId);
+    if (VDBG) ALOGD("setHostname successful");
+    cli->sendMsg(ResponseCode::CommandOkay, "setHostname started", false);
+    return;
+}
+
+void MDnsSdListenerSetHostnameCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
+        DNSServiceErrorType errorCode, const char *hostname, void *inContext) {
+    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
+    char *msg;
+    int refNumber = context->mRefNumber;
+    if (errorCode != kDNSServiceErr_NoError) {
+        asprintf(&msg, "%d %d", refNumber, errorCode);
+        context->mListener->sendBroadcast(ResponseCode::ServiceSetHostnameFailed, msg, false);
+        if (DBG) ALOGE("setHostname failure for %d, error= %d", refNumber, errorCode);
+    } else {
+        asprintf(&msg, "%d %s", refNumber, hostname);
+        context->mListener->sendBroadcast(ResponseCode::ServiceSetHostnameSuccess, msg, false);
+        if (VDBG) ALOGD("setHostname succeeded for %d.  Set to %s", refNumber, hostname);
+    }
+    free(msg);
+}
+
+
+int MDnsSdListener::Handler::ifaceNameToI(const char *iface) {
+    return 0;
+}
+
+const char *MDnsSdListener::Handler::iToIfaceName(int i) {
+    return NULL;
+}
+
+DNSServiceFlags MDnsSdListener::Handler::iToFlags(int i) {
+    return 0;
+}
+
+int MDnsSdListener::Handler::flagsToI(DNSServiceFlags flags) {
+    return 0;
+}
+
+int MDnsSdListener::Handler::runCommand(SocketClient *cli,
+                                        int argc, char **argv) {
+    if (argc < 2) {
+        char* msg = NULL;
+        asprintf( &msg, "Invalid number of arguments to mdnssd: %i", argc);
+        ALOGW("%s", msg);
+        cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+        free(msg);
+        return -1;
+    }
+
+    char* cmd = argv[1];
+
+    if (strcmp(cmd, "discover") == 0) {
+        if (argc != 4) {
+            cli->sendMsg(ResponseCode::CommandParameterError,
+                    "Invalid number of arguments to mdnssd discover", false);
+            return 0;
+        }
+        int requestId = atoi(argv[2]);
+        char *serviceType = argv[3];
+
+        discover(cli, NULL, serviceType, NULL, requestId, 0);
+    } else if (strcmp(cmd, "stop-discover") == 0) {
+        stop(cli, argc, argv, "discover");
+    } else if (strcmp(cmd, "register") == 0) {
+        if (argc != 6) {
+            cli->sendMsg(ResponseCode::CommandParameterError,
+                    "Invalid number of arguments to mdnssd register", false);
+            return 0;
+        }
+        int requestId = atoi(argv[2]);
+        char *serviceName = argv[3];
+        char *serviceType = argv[4];
+        int port = atoi(argv[5]);
+        char *interfaceName = NULL; // will use all
+        char *domain = NULL;        // will use default
+        char *host = NULL;          // will use default hostname
+        int textLen = 0;
+        void *textRecord = NULL;
+
+        serviceRegister(cli, requestId, interfaceName, serviceName,
+                serviceType, domain, host, port, textLen, textRecord);
+    } else if (strcmp(cmd, "stop-register") == 0) {
+        stop(cli, argc, argv, "register");
+    } else if (strcmp(cmd, "resolve") == 0) {
+        if (argc != 6) {
+            cli->sendMsg(ResponseCode::CommandParameterError,
+                    "Invalid number of arguments to mdnssd resolve", false);
+            return 0;
+        }
+        int requestId = atoi(argv[2]);
+        char *interfaceName = NULL;  // will use all
+        char *serviceName = argv[3];
+        char *regType = argv[4];
+        char *domain = argv[5];
+        resolveService(cli, requestId, interfaceName, serviceName, regType, domain);
+    } else if (strcmp(cmd, "stop-resolve") == 0) {
+        stop(cli, argc, argv, "resolve");
+    } else if (strcmp(cmd, "start-service") == 0) {
+        if (mMonitor->startService()) {
+            cli->sendMsg(ResponseCode::CommandOkay, "Service Started", false);
+        } else {
+            cli->sendMsg(ResponseCode::ServiceStartFailed, "Service already running", false);
+        }
+    } else if (strcmp(cmd, "stop-service") == 0) {
+        if (mMonitor->stopService()) {
+            cli->sendMsg(ResponseCode::CommandOkay, "Service Stopped", false);
+        } else {
+            cli->sendMsg(ResponseCode::ServiceStopFailed, "Service still in use", false);
+        }
+    } else if (strcmp(cmd, "sethostname") == 0) {
+        if (argc != 4) {
+            cli->sendMsg(ResponseCode::CommandParameterError,
+                    "Invalid number of arguments to mdnssd sethostname", false);
+            return 0;
+        }
+        int requestId = atoi(argv[2]);
+        char *hostname = argv[3];
+        setHostname(cli, requestId, hostname);
+    } else if (strcmp(cmd, "stop-sethostname") == 0) {
+        stop(cli, argc, argv, "sethostname");
+    } else if (strcmp(cmd, "getaddrinfo") == 0) {
+        if (argc != 4) {
+            cli->sendMsg(ResponseCode::CommandParameterError,
+                    "Invalid number of arguments to mdnssd getaddrinfo", false);
+            return 0;
+        }
+        int requestId = atoi(argv[2]);
+        char *hostname = argv[3];
+        char *interfaceName = NULL;  // default
+        int protocol = 0;            // intelligient heuristic (both v4 + v6)
+        getAddrInfo(cli, requestId, interfaceName, protocol, hostname);
+    } else if (strcmp(cmd, "stop-getaddrinfo") == 0) {
+        stop(cli, argc, argv, "getaddrinfo");
+    } else {
+        if (VDBG) ALOGE("Unknown cmd %s", cmd);
+        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown mdnssd cmd", false);
+        return 0;
+    }
+    return 0;
+}
+
+MDnsSdListener::Monitor::Monitor() {
+    mHead = NULL;
+    pthread_mutex_init(&mHeadMutex, NULL);
+    socketpair(AF_LOCAL, SOCK_STREAM, 0, mCtrlSocketPair);
+    pthread_create(&mThread, NULL, MDnsSdListener::Monitor::threadStart, this);
+}
+
+void *MDnsSdListener::Monitor::threadStart(void *obj) {
+    Monitor *monitor = reinterpret_cast<Monitor *>(obj);
+
+    monitor->run();
+    delete monitor;
+    pthread_exit(NULL);
+    return NULL;
+}
+
+int MDnsSdListener::Monitor::startService() {
+    int result = 0;
+    char property_value[PROPERTY_VALUE_MAX];
+    pthread_mutex_lock(&mHeadMutex);
+    property_get(MDNS_SERVICE_STATUS, property_value, "");
+    if (strcmp("running", property_value) != 0) {
+        ALOGD("Starting MDNSD");
+        property_set("ctl.start", MDNS_SERVICE_NAME);
+        wait_for_property(MDNS_SERVICE_STATUS, "running", 5);
+        result = -1;
+    } else {
+        result = 0;
+    }
+    pthread_mutex_unlock(&mHeadMutex);
+    return result;
+}
+
+int MDnsSdListener::Monitor::stopService() {
+    int result = 0;
+    pthread_mutex_lock(&mHeadMutex);
+    if (mHead == NULL) {
+        ALOGD("Stopping MDNSD");
+        property_set("ctl.stop", MDNS_SERVICE_NAME);
+        wait_for_property(MDNS_SERVICE_STATUS, "stopped", 5);
+        result = -1;
+    } else {
+        result = 0;
+    }
+    pthread_mutex_unlock(&mHeadMutex);
+    return result;
+}
+
+void MDnsSdListener::Monitor::run() {
+    int pollCount = 1;
+    mPollSize = 10;
+
+    mPollFds = (struct pollfd *)calloc(sizeof(struct pollfd), mPollSize);
+    mPollRefs = (DNSServiceRef **)calloc(sizeof(DNSServiceRef *), mPollSize);
+
+    mPollFds[0].fd = mCtrlSocketPair[0];
+    mPollFds[0].events = POLLIN;
+
+    if (VDBG) ALOGD("MDnsSdListener starting to monitor");
+    while (1) {
+        if (VDBG) ALOGD("Going to poll with pollCount %d", pollCount);
+        int pollResults = poll(mPollFds, pollCount, 10000000);
+        if (pollResults < 0) {
+            ALOGE("Error in poll - got %d", errno);
+        } else if (pollResults > 0) {
+            if (VDBG) ALOGD("Monitor poll got data pollCount = %d, %d", pollCount, pollResults);
+            for(int i = 1; i < pollCount; i++) {
+                if (mPollFds[i].revents != 0) {
+                    if (VDBG) {
+                        ALOGD("Monitor found [%d].revents = %d - calling ProcessResults",
+                                i, mPollFds[i].revents);
+                    }
+                    DNSServiceProcessResult(*(mPollRefs[i]));
+                    mPollFds[i].revents = 0;
+                }
+            }
+            if (VDBG) ALOGD("controlSocket shows revent= %d", mPollFds[0].revents);
+            switch (mPollFds[0].revents) {
+                case POLLIN: {
+                    char readBuf[2];
+                    read(mCtrlSocketPair[0], &readBuf, 1);
+                    if (DBG) ALOGD("MDnsSdListener::Monitor got %c", readBuf[0]);
+                    if (memcmp(RESCAN, readBuf, 1) == 0) {
+                        pollCount = rescan();
+                    }
+                }
+            }
+            mPollFds[0].revents = 0;
+        } else {
+            if (VDBG) ALOGD("MDnsSdListener::Monitor poll timed out");
+        }
+    }
+    free(mPollFds);
+    free(mPollRefs);
+}
+
+#define DBG_RESCAN 0
+
+int MDnsSdListener::Monitor::rescan() {
+// rescan the list from mHead and make new pollfds and serviceRefs
+    if (VDBG) {
+        ALOGD("MDnsSdListener::Monitor poll rescanning - size=%d, live=%d", mPollSize, mLiveCount);
+    }
+    int count = 0;
+    pthread_mutex_lock(&mHeadMutex);
+    Element **prevPtr = &mHead;
+    int i = 1;
+    if (mPollSize <= mLiveCount) {
+        mPollSize = mLiveCount + 5;
+        free(mPollFds);
+        free(mPollRefs);
+        mPollFds = (struct pollfd *)calloc(sizeof(struct pollfd), mPollSize);
+        mPollRefs = (DNSServiceRef **)calloc(sizeof(DNSServiceRef *), mPollSize);
+    } else {
+        memset(mPollFds, sizeof(struct pollfd) * mPollSize, 0);
+        memset(mPollRefs, sizeof(DNSServiceRef *) * mPollSize, 0);
+    }
+    mPollFds[0].fd = mCtrlSocketPair[0];
+    mPollFds[0].events = POLLIN;
+    if (DBG_RESCAN) ALOGD("mHead = %p", mHead);
+    while (*prevPtr != NULL) {
+        if (DBG_RESCAN) ALOGD("checking %p, mReady = %d", *prevPtr, (*prevPtr)->mReady);
+        if ((*prevPtr)->mReady == 1) {
+            int fd = DNSServiceRefSockFD((*prevPtr)->mRef);
+            if (fd != -1) {
+                if (DBG_RESCAN) ALOGD("  adding FD %d", fd);
+                mPollFds[i].fd = fd;
+                mPollFds[i].events = POLLIN;
+                mPollRefs[i] = &((*prevPtr)->mRef);
+                i++;
+            } else {
+                ALOGE("Error retreving socket FD for live ServiceRef");
+            }
+            prevPtr = &((*prevPtr)->mNext); // advance to the next element
+        } else if ((*prevPtr)->mReady == -1) {
+            if (DBG_RESCAN) ALOGD("  removing %p from  play", *prevPtr);
+            Element *cur = *prevPtr;
+            *prevPtr = (cur)->mNext; // change our notion of this element and don't advance
+            delete cur;
+        }
+    }
+    pthread_mutex_unlock(&mHeadMutex);
+    return i;
+}
+
+DNSServiceRef *MDnsSdListener::Monitor::allocateServiceRef(int id, Context *context) {
+    if (lookupServiceRef(id) != NULL) {
+        delete(context);
+        return NULL;
+    }
+    Element *e = new Element(id, context);
+    pthread_mutex_lock(&mHeadMutex);
+    e->mNext = mHead;
+    mHead = e;
+    pthread_mutex_unlock(&mHeadMutex);
+    return &(e->mRef);
+}
+
+DNSServiceRef *MDnsSdListener::Monitor::lookupServiceRef(int id) {
+    pthread_mutex_lock(&mHeadMutex);
+    Element *cur = mHead;
+    while (cur != NULL) {
+        if (cur->mId == id) {
+            DNSServiceRef *result = &(cur->mRef);
+            pthread_mutex_unlock(&mHeadMutex);
+            return result;
+        }
+        cur = cur->mNext;
+    }
+    pthread_mutex_unlock(&mHeadMutex);
+    return NULL;
+}
+
+void MDnsSdListener::Monitor::startMonitoring(int id) {
+    if (VDBG) ALOGD("startMonitoring %d", id);
+    pthread_mutex_lock(&mHeadMutex);
+    Element *cur = mHead;
+    while (cur != NULL) {
+        if (cur->mId == id) {
+            if (DBG_RESCAN) ALOGD("marking %p as ready to be added", cur);
+            mLiveCount++;
+            cur->mReady = 1;
+            pthread_mutex_unlock(&mHeadMutex);
+            write(mCtrlSocketPair[1], RESCAN, 1);  // trigger a rescan for a fresh poll
+            if (VDBG) ALOGD("triggering rescan");
+            return;
+        }
+        cur = cur->mNext;
+    }
+    pthread_mutex_unlock(&mHeadMutex);
+}
+
+#define NAP_TIME 200  // 200 ms between polls
+static int wait_for_property(const char *name, const char *desired_value, int maxwait)
+{
+    char value[PROPERTY_VALUE_MAX] = {'\0'};
+    int maxnaps = (maxwait * 1000) / NAP_TIME;
+
+    if (maxnaps < 1) {
+        maxnaps = 1;
+    }
+
+    while (maxnaps-- > 0) {
+        usleep(NAP_TIME * 1000);
+        if (property_get(name, value, NULL)) {
+            if (desired_value == NULL || strcmp(value, desired_value) == 0) {
+                return 0;
+            }
+        }
+    }
+    return -1; /* failure */
+}
+
+void MDnsSdListener::Monitor::freeServiceRef(int id) {
+    if (VDBG) ALOGD("freeServiceRef %d", id);
+    pthread_mutex_lock(&mHeadMutex);
+    Element **prevPtr = &mHead;
+    Element *cur;
+    while (*prevPtr != NULL) {
+        cur = *prevPtr;
+        if (cur->mId == id) {
+            if (DBG_RESCAN) ALOGD("marking %p as ready to be removed", cur);
+            mLiveCount--;
+            if (cur->mReady == 1) {
+                cur->mReady = -1; // tell poll thread to delete
+                write(mCtrlSocketPair[1], RESCAN, 1); // trigger a rescan for a fresh poll
+                if (VDBG) ALOGD("triggering rescan");
+            } else {
+                *prevPtr = cur->mNext;
+                delete cur;
+            }
+            pthread_mutex_unlock(&mHeadMutex);
+            return;
+        }
+        prevPtr = &(cur->mNext);
+    }
+    pthread_mutex_unlock(&mHeadMutex);
+}
diff --git a/MDnsSdListener.h b/MDnsSdListener.h
new file mode 100644 (file)
index 0000000..a3b14ad
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef _MDNSSDLISTENER_H__
+#define _MDNSSDLISTENER_H__
+
+#include <pthread.h>
+#include <sysutils/FrameworkListener.h>
+#include <dns_sd.h>
+
+#include "NetdCommand.h"
+
+// callbacks
+void MDnsSdListenerDiscoverCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
+        uint32_t interfaceIndex, DNSServiceErrorType errorCode,
+        const char *serviceName, const char *regType, const char *replyDomain,
+        void *inContext);
+
+void MDnsSdListenerRegisterCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
+        DNSServiceErrorType errorCode, const char *serviceName, const char *regType,
+        const char *domain, void *inContext);
+
+void MDnsSdListenerResolveCallback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interface,
+        DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port,
+        uint16_t txtLen, const unsigned char *txtRecord, void *inContext);
+
+void MDnsSdListenerSetHostnameCallback(DNSServiceRef, DNSServiceFlags flags,
+        DNSServiceErrorType errorCode, const char *hostname, void *inContext);
+
+void MDnsSdListenerGetAddrInfoCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
+        uint32_t interface, DNSServiceErrorType errorCode, const char *hostname,
+        const struct sockaddr *const sa, uint32_t ttl, void *inContext);
+
+#define RESCAN "1"
+
+class MDnsSdListener : public FrameworkListener {
+public:
+    MDnsSdListener();
+    virtual ~MDnsSdListener() {}
+
+    class Context {
+    public:
+        MDnsSdListener *mListener;
+        int mRefNumber;
+
+        Context(int refNumber, MDnsSdListener *m) {
+            mRefNumber = refNumber;
+            mListener = m;
+        }
+
+        ~Context() {
+        }
+    };
+
+    class Monitor {
+    public:
+        Monitor();
+        virtual ~Monitor() {}
+        DNSServiceRef *allocateServiceRef(int id, Context *c);
+        void startMonitoring(int id);
+        DNSServiceRef *lookupServiceRef(int id);
+        void freeServiceRef(int id);
+        static void *threadStart(void *handler);
+        int startService();
+        int stopService();
+    private:
+        void run();
+        int rescan(); // returns the number of elements in the poll
+        class Element {
+        public:
+            int mId;
+            Element *mNext;
+            DNSServiceRef mRef;
+            Context *mContext;
+            int mReady;
+            Element(int id, Context *context)
+                    : mId(id), mNext(NULL), mContext(context), mReady(0) {}
+            virtual ~Element() { delete(mContext); }
+        };
+        Element *mHead;
+        int mLiveCount;
+        struct pollfd *mPollFds;
+        DNSServiceRef **mPollRefs;
+        int mPollSize;
+        pthread_t mThread;
+        int mCtrlSocketPair[2];
+        pthread_mutex_t mHeadMutex;
+    };
+
+    class Handler : public NetdCommand {
+    public:
+        Handler(Monitor *m, MDnsSdListener *listener);
+        virtual ~Handler();
+        int runCommand(SocketClient *c, int argc, char** argv);
+
+        MDnsSdListener *mListener; // needed for broadcast purposes
+    private:
+        void stop(SocketClient *cli, int argc, char **argv, const char *str);
+
+        void discover(SocketClient *cli, const char *iface, const char *regType,
+                const char *domain, const int requestNumber,
+                const int requestFlags);
+
+        void serviceRegister(SocketClient *cli, int requestId, const char *interfaceName,
+                const char *serviceName, const char *serviceType, const char *domain,
+                const char *host, int port, int textLen, void *txtRecord);
+
+        void resolveService(SocketClient *cli, int requestId,
+                const char *interfaceName, const char *serviceName, const char *regType,
+                const char *domain);
+
+        void setHostname(SocketClient *cli, int requestId, const char *hostname);
+
+        void getAddrInfo(SocketClient *cli, int requestId, const char *interfaceName,
+                uint32_t protocol, const char *hostname);
+
+        int ifaceNameToI(const char *iface);
+        const char *iToIfaceName(int i);
+        DNSServiceFlags iToFlags(int i);
+        int flagsToI(DNSServiceFlags flags);
+        Monitor *mMonitor;
+    };
+};
+
+static int wait_for_property(const char *name, const char *desired_value, int maxwait);
+
+#endif
index 07c2be3..5bebb0f 100644 (file)
@@ -50,6 +50,8 @@ public:
     // did not take place.
     static const int OperationFailed           = 400;
     static const int DnsProxyOperationFailed   = 401;
+    static const int ServiceStartFailed        = 402;
+    static const int ServiceStopFailed         = 403;
 
     // 500 series - The command was not accepted and the requested
     // action did not take place.
@@ -57,9 +59,20 @@ public:
     static const int CommandParameterError = 501;
 
     // 600 series - Unsolicited broadcasts
-    static const int InterfaceChange        = 600;
-    static const int BandwidthControl       = 601;
-    static const int InterfaceActive        = 602;
-    static const int InterfaceIdle          = 603;
+    static const int InterfaceChange                = 600;
+    static const int BandwidthControl               = 601;
+    static const int ServiceDiscoveryFailed         = 602;
+    static const int ServiceDiscoveryServiceAdded   = 603;
+    static const int ServiceDiscoveryServiceRemoved = 604;
+    static const int ServiceRegistrationFailed      = 605;
+    static const int ServiceRegistrationSucceeded   = 606;
+    static const int ServiceResolveFailed           = 607;
+    static const int ServiceResolveSuccess          = 608;
+    static const int ServiceSetHostnameFailed       = 609;
+    static const int ServiceSetHostnameSuccess      = 610;
+    static const int ServiceGetAddrInfoFailed       = 611;
+    static const int ServiceGetAddrInfoSuccess      = 612;
+    static const int InterfaceActive                = 613;
+    static const int InterfaceIdle                  = 614;
 };
 #endif
index 14eb729..b466e42 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -33,6 +33,7 @@
 #include "CommandListener.h"
 #include "NetlinkManager.h"
 #include "DnsProxyListener.h"
+#include "MDnsSdListener.h"
 
 static void coldboot(const char *path);
 static void sigchld_handler(int sig);
@@ -43,6 +44,7 @@ int main() {
     CommandListener *cl;
     NetlinkManager *nm;
     DnsProxyListener *dpl;
+    MDnsSdListener *mdnsl;
 
     ALOGI("Netd 1.0 starting");
 
@@ -72,6 +74,11 @@ int main() {
         exit(1);
     }
 
+    mdnsl = new MDnsSdListener();
+    if (mdnsl->startListener()) {
+        ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno));
+        exit(1);
+    }
     /*
      * Now that we're up, we can respond to commands
      */
diff --git a/ndc.c b/ndc.c
index 3e59246..17750bd 100644 (file)
--- a/ndc.c
+++ b/ndc.c
@@ -37,20 +37,31 @@ static int do_cmd(int sock, int argc, char **argv);
 
 int main(int argc, char **argv) {
     int sock;
+    int cmdOffset = 0;
 
     if (argc < 2)
         usage(argv[0]);
 
-    if ((sock = socket_local_client("netd",
+    // try interpreting the first arg as the socket name - if it fails go back to netd
+
+    if ((sock = socket_local_client(argv[1],
                                      ANDROID_SOCKET_NAMESPACE_RESERVED,
                                      SOCK_STREAM)) < 0) {
-        fprintf(stderr, "Error connecting (%s)\n", strerror(errno));
-        exit(4);
+        if ((sock = socket_local_client("netd",
+                                         ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                         SOCK_STREAM)) < 0) {
+            fprintf(stderr, "Error connecting (%s)\n", strerror(errno));
+            exit(4);
+        }
+    } else {
+        if (argc < 3) usage(argv[0]);
+        printf("Using alt socket %s\n", argv[1]);
+        cmdOffset = 1;
     }
 
-    if (!strcmp(argv[1], "monitor"))
+    if (!strcmp(argv[1+cmdOffset], "monitor"))
         exit(do_monitor(sock, 0));
-    exit(do_cmd(sock, argc, argv));
+    exit(do_cmd(sock, argc-cmdOffset, &(argv[cmdOffset])));
 }
 
 static int do_cmd(int sock, int argc, char **argv) {
@@ -142,7 +153,6 @@ static int do_monitor(int sock, int stop_after_cmd) {
 }
 
 static void usage(char *progname) {
-    fprintf(stderr, "Usage: %s <monitor>|<cmd> [arg1] [arg2...]\n", progname);
+    fprintf(stderr, "Usage: %s [sockname] <monitor>|<cmd> [arg1] [arg2...]\n", progname);
     exit(1);
 }
-