1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <arpa/inet.h>
18#include <dirent.h>
19#include <errno.h>
20#include <linux/if.h>
21#include <netdb.h>
22#include <netinet/in.h>
23#include <pthread.h>
24#include <stdlib.h>
25#include <sys/poll.h>
26#include <sys/socket.h>
27#include <sys/types.h>
28#include <string.h>
29#include <resolv.h>
30
31#define LOG_TAG "MDnsDS"
32#define DBG 1
33#define VDBG 1
34
35#include <cutils/log.h>
36#include <cutils/properties.h>
37#include <sysutils/SocketClient.h>
38
39#include "MDnsSdListener.h"
40#include "ResponseCode.h"
41
42#define MDNS_SERVICE_NAME "mdnsd"
43#define MDNS_SERVICE_STATUS "init.svc.mdnsd"
44
45#define CEIL(x, y) (((x) + (y) - 1) / (y))
46
47MDnsSdListener::MDnsSdListener() :
48                 FrameworkListener("mdns", true) {
49    Monitor *m = new Monitor();
50    registerCmd(new Handler(m, this));
51}
52
53MDnsSdListener::Handler::Handler(Monitor *m, MDnsSdListener *listener) :
54   NetdCommand("mdnssd") {
55   if (DBG) ALOGD("MDnsSdListener::Hander starting up");
56   mMonitor = m;
57   mListener = listener;
58}
59
60MDnsSdListener::Handler::~Handler() {}
61
62void MDnsSdListener::Handler::discover(SocketClient *cli,
63        const char *iface,
64        const char *regType,
65        const char *domain,
66        const int requestId,
67        const int requestFlags) {
68    if (VDBG) {
69        ALOGD("discover(%s, %s, %s, %d, %d)", iface, regType, domain, requestId,
70                requestFlags);
71    }
72    Context *context = new Context(requestId, mListener);
73    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
74    if (ref == NULL) {
75        ALOGE("requestId %d already in use during discover call", requestId);
76        cli->sendMsg(ResponseCode::CommandParameterError,
77                "RequestId already in use during discover call", false);
78        return;
79    }
80    if (VDBG) ALOGD("using ref %p", ref);
81    DNSServiceFlags nativeFlags = iToFlags(requestFlags);
82    int interfaceInt = ifaceNameToI(iface);
83
84    DNSServiceErrorType result = DNSServiceBrowse(ref, nativeFlags, interfaceInt, regType,
85            domain, &MDnsSdListenerDiscoverCallback, context);
86    if (result != kDNSServiceErr_NoError) {
87        ALOGE("Discover request %d got an error from DNSServiceBrowse %d", requestId, result);
88        mMonitor->freeServiceRef(requestId);
89        cli->sendMsg(ResponseCode::CommandParameterError,
90                "Discover request got an error from DNSServiceBrowse", false);
91        return;
92    }
93    mMonitor->startMonitoring(requestId);
94    if (VDBG) ALOGD("discover successful");
95    cli->sendMsg(ResponseCode::CommandOkay, "Discover operation started", false);
96    return;
97}
98
99void MDnsSdListenerDiscoverCallback(DNSServiceRef /* sdRef */, DNSServiceFlags flags,
100        uint32_t /* interfaceIndex */, DNSServiceErrorType errorCode, const char *serviceName,
101        const char *regType, const char *replyDomain, void *inContext) {
102    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
103    char *msg;
104    int refNumber = context->mRefNumber;
105
106    if (errorCode != kDNSServiceErr_NoError) {
107        asprintf(&msg, "%d %d", refNumber, errorCode);
108        context->mListener->sendBroadcast(ResponseCode::ServiceDiscoveryFailed, msg, false);
109        if (DBG) ALOGE("discover failure for %d, error= %d", refNumber, errorCode);
110    } else {
111        int respCode;
112        char *quotedServiceName = SocketClient::quoteArg(serviceName);
113        if (flags & kDNSServiceFlagsAdd) {
114            if (VDBG) {
115                ALOGD("Discover found new serviceName %s, regType %s and domain %s for %d",
116                        serviceName, regType, replyDomain, refNumber);
117            }
118            respCode = ResponseCode::ServiceDiscoveryServiceAdded;
119        } else {
120            if (VDBG) {
121                ALOGD("Discover lost serviceName %s, regType %s and domain %s for %d",
122                        serviceName, regType, replyDomain, refNumber);
123            }
124            respCode = ResponseCode::ServiceDiscoveryServiceRemoved;
125        }
126        asprintf(&msg, "%d %s %s %s", refNumber, quotedServiceName, regType, replyDomain);
127        free(quotedServiceName);
128        context->mListener->sendBroadcast(respCode, msg, false);
129    }
130    free(msg);
131}
132
133void MDnsSdListener::Handler::stop(SocketClient *cli, int argc, char **argv, const char *str) {
134    if (argc != 3) {
135        char *msg;
136        asprintf(&msg, "Invalid number of arguments to %s", str);
137        cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
138        free(msg);
139        return;
140    }
141    int requestId = atoi(argv[2]);
142    DNSServiceRef *ref = mMonitor->lookupServiceRef(requestId);
143    if (ref == NULL) {
144        if (DBG) ALOGE("%s stop used unknown requestId %d", str, requestId);
145        cli->sendMsg(ResponseCode::CommandParameterError, "Unknown requestId", false);
146        return;
147    }
148    if (VDBG) ALOGD("Stopping %s with ref %p", str, ref);
149    DNSServiceRefDeallocate(*ref);
150    mMonitor->freeServiceRef(requestId);
151    char *msg;
152    asprintf(&msg, "%s stopped", str);
153    cli->sendMsg(ResponseCode::CommandOkay, msg, false);
154    free(msg);
155}
156
157void MDnsSdListener::Handler::serviceRegister(SocketClient *cli, int requestId,
158        const char *interfaceName, const char *serviceName, const char *serviceType,
159        const char *domain, const char *host, int port, int txtLen, void *txtRecord) {
160    if (VDBG) {
161        ALOGD("serviceRegister(%d, %s, %s, %s, %s, %s, %d, %d, <binary>)", requestId,
162                interfaceName, serviceName, serviceType, domain, host, port, txtLen);
163    }
164    Context *context = new Context(requestId, mListener);
165    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
166    port = htons(port);
167    if (ref == NULL) {
168        ALOGE("requestId %d already in use during register call", requestId);
169        cli->sendMsg(ResponseCode::CommandParameterError,
170                "RequestId already in use during register call", false);
171        return;
172    }
173    DNSServiceFlags nativeFlags = 0;
174    int interfaceInt = ifaceNameToI(interfaceName);
175    DNSServiceErrorType result = DNSServiceRegister(ref, interfaceInt, nativeFlags, serviceName,
176            serviceType, domain, host, port, txtLen, txtRecord, &MDnsSdListenerRegisterCallback,
177            context);
178    if (result != kDNSServiceErr_NoError) {
179        ALOGE("service register request %d got an error from DNSServiceRegister %d", requestId,
180                result);
181        mMonitor->freeServiceRef(requestId);
182        cli->sendMsg(ResponseCode::CommandParameterError,
183                "serviceRegister request got an error from DNSServiceRegister", false);
184        return;
185    }
186    mMonitor->startMonitoring(requestId);
187    if (VDBG) ALOGD("serviceRegister successful");
188    cli->sendMsg(ResponseCode::CommandOkay, "serviceRegister started", false);
189    return;
190}
191
192void MDnsSdListenerRegisterCallback(DNSServiceRef /* sdRef */, DNSServiceFlags /* flags */,
193        DNSServiceErrorType errorCode, const char *serviceName, const char * /* regType */,
194        const char * /* domain */, void *inContext) {
195    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
196    char *msg;
197    int refNumber = context->mRefNumber;
198    if (errorCode != kDNSServiceErr_NoError) {
199        asprintf(&msg, "%d %d", refNumber, errorCode);
200        context->mListener->sendBroadcast(ResponseCode::ServiceRegistrationFailed, msg, false);
201        if (DBG) ALOGE("register failure for %d, error= %d", refNumber, errorCode);
202    } else {
203        char *quotedServiceName = SocketClient::quoteArg(serviceName);
204        asprintf(&msg, "%d %s", refNumber, quotedServiceName);
205        free(quotedServiceName);
206        context->mListener->sendBroadcast(ResponseCode::ServiceRegistrationSucceeded, msg, false);
207        if (VDBG) ALOGD("register succeeded for %d as %s", refNumber, serviceName);
208    }
209    free(msg);
210}
211
212
213void MDnsSdListener::Handler::resolveService(SocketClient *cli, int requestId,
214        const char *interfaceName, const char *serviceName, const char *regType,
215        const char *domain) {
216    if (VDBG) {
217        ALOGD("resolveService(%d, %s, %s, %s, %s)", requestId, interfaceName,
218                serviceName, regType, domain);
219    }
220    Context *context = new Context(requestId, mListener);
221    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
222    if (ref == NULL) {
223        ALOGE("request Id %d already in use during resolve call", requestId);
224        cli->sendMsg(ResponseCode::CommandParameterError,
225                "RequestId already in use during resolve call", false);
226        return;
227    }
228    DNSServiceFlags nativeFlags = 0;
229    int interfaceInt = ifaceNameToI(interfaceName);
230    DNSServiceErrorType result = DNSServiceResolve(ref, nativeFlags, interfaceInt, serviceName,
231            regType, domain, &MDnsSdListenerResolveCallback, context);
232    if (result != kDNSServiceErr_NoError) {
233        ALOGE("service resolve request %d got an error from DNSServiceResolve %d", requestId,
234                result);
235        mMonitor->freeServiceRef(requestId);
236        cli->sendMsg(ResponseCode::CommandParameterError,
237                "resolveService got an error from DNSServiceResolve", false);
238        return;
239    }
240    mMonitor->startMonitoring(requestId);
241    if (VDBG) ALOGD("resolveService successful");
242    cli->sendMsg(ResponseCode::CommandOkay, "resolveService started", false);
243    return;
244}
245
246void MDnsSdListenerResolveCallback(DNSServiceRef /* sdRef */, DNSServiceFlags /* flags */,
247        uint32_t /* interface */, DNSServiceErrorType errorCode, const char *fullname,
248        const char *hosttarget, uint16_t port, uint16_t txtLen,
249        const unsigned char *txtRecord , void *inContext) {
250    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
251    char *msg;
252    int refNumber = context->mRefNumber;
253    port = ntohs(port);
254    if (errorCode != kDNSServiceErr_NoError) {
255        asprintf(&msg, "%d %d", refNumber, errorCode);
256        context->mListener->sendBroadcast(ResponseCode::ServiceResolveFailed, msg, false);
257        if (DBG) ALOGE("resolve failure for %d, error= %d", refNumber, errorCode);
258    } else {
259        char *quotedFullName = SocketClient::quoteArg(fullname);
260        char *quotedHostTarget = SocketClient::quoteArg(hosttarget);
261
262        // Base 64 encodes every 3 bytes into 4 characters, but then adds padding to the next
263        // multiple of 4 and a \0
264        size_t dstLength = CEIL(CEIL(txtLen * 4, 3), 4) * 4 + 1;
265
266        char *dst = (char *)malloc(dstLength);
267        b64_ntop(txtRecord, txtLen, dst, dstLength);
268
269        asprintf(&msg, "%d %s %s %d %d \"%s\"", refNumber, quotedFullName, quotedHostTarget, port,
270                 txtLen, dst);
271        free(quotedFullName);
272        free(quotedHostTarget);
273        free(dst);
274        context->mListener->sendBroadcast(ResponseCode::ServiceResolveSuccess, msg, false);
275        if (VDBG) {
276            ALOGD("resolve succeeded for %d finding %s at %s:%d with txtLen %d",
277                    refNumber, fullname, hosttarget, port, txtLen);
278        }
279    }
280    free(msg);
281}
282
283void MDnsSdListener::Handler::getAddrInfo(SocketClient *cli, int requestId,
284        const char *interfaceName, uint32_t protocol, const char *hostname) {
285    if (VDBG) ALOGD("getAddrInfo(%d, %s %d, %s)", requestId, interfaceName, protocol, hostname);
286    Context *context = new Context(requestId, mListener);
287    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
288    if (ref == NULL) {
289        ALOGE("request ID %d already in use during getAddrInfo call", requestId);
290        cli->sendMsg(ResponseCode::CommandParameterError,
291                "RequestId already in use during getAddrInfo call", false);
292        return;
293    }
294    DNSServiceFlags nativeFlags = 0;
295    int interfaceInt = ifaceNameToI(interfaceName);
296    DNSServiceErrorType result = DNSServiceGetAddrInfo(ref, nativeFlags, interfaceInt, protocol,
297            hostname, &MDnsSdListenerGetAddrInfoCallback, context);
298    if (result != kDNSServiceErr_NoError) {
299        ALOGE("getAddrInfo request %d got an error from DNSServiceGetAddrInfo %d", requestId,
300                result);
301        mMonitor->freeServiceRef(requestId);
302        cli->sendMsg(ResponseCode::CommandParameterError,
303                "getAddrInfo request got an error from DNSServiceGetAddrInfo", false);
304        return;
305    }
306    mMonitor->startMonitoring(requestId);
307    if (VDBG) ALOGD("getAddrInfo successful");
308    cli->sendMsg(ResponseCode::CommandOkay, "getAddrInfo started", false);
309    return;
310}
311
312void MDnsSdListenerGetAddrInfoCallback(DNSServiceRef /* sdRef */, DNSServiceFlags /* flags */,
313        uint32_t /* interface */, DNSServiceErrorType errorCode, const char *hostname,
314        const struct sockaddr *const sa, uint32_t ttl, void *inContext) {
315    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
316    int refNumber = context->mRefNumber;
317
318    if (errorCode != kDNSServiceErr_NoError) {
319        char *msg;
320        asprintf(&msg, "%d %d", refNumber, errorCode);
321        context->mListener->sendBroadcast(ResponseCode::ServiceGetAddrInfoFailed, msg, false);
322        if (DBG) ALOGE("getAddrInfo failure for %d, error= %d", refNumber, errorCode);
323        free(msg);
324    } else {
325        char addr[INET6_ADDRSTRLEN];
326        char *msg;
327        char *quotedHostname = SocketClient::quoteArg(hostname);
328        if (sa->sa_family == AF_INET) {
329            inet_ntop(sa->sa_family, &(((struct sockaddr_in *)sa)->sin_addr), addr, sizeof(addr));
330        } else {
331            inet_ntop(sa->sa_family, &(((struct sockaddr_in6 *)sa)->sin6_addr), addr, sizeof(addr));
332        }
333        asprintf(&msg, "%d %s %d %s", refNumber, quotedHostname, ttl, addr);
334        free(quotedHostname);
335        context->mListener->sendBroadcast(ResponseCode::ServiceGetAddrInfoSuccess, msg, false);
336        if (VDBG) {
337            ALOGD("getAddrInfo succeeded for %d: %s", refNumber, msg);
338        }
339        free(msg);
340    }
341}
342
343void MDnsSdListener::Handler::setHostname(SocketClient *cli, int requestId,
344        const char *hostname) {
345    if (VDBG) ALOGD("setHostname(%d, %s)", requestId, hostname);
346    Context *context = new Context(requestId, mListener);
347    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
348    if (ref == NULL) {
349        ALOGE("request Id %d already in use during setHostname call", requestId);
350        cli->sendMsg(ResponseCode::CommandParameterError,
351                "RequestId already in use during setHostname call", false);
352        return;
353    }
354    DNSServiceFlags nativeFlags = 0;
355    DNSServiceErrorType result = DNSSetHostname(ref, nativeFlags, hostname,
356            &MDnsSdListenerSetHostnameCallback, context);
357    if (result != kDNSServiceErr_NoError) {
358        ALOGE("setHostname request %d got an error from DNSSetHostname %d", requestId, result);
359        mMonitor->freeServiceRef(requestId);
360        cli->sendMsg(ResponseCode::CommandParameterError,
361                "setHostname got an error from DNSSetHostname", false);
362        return;
363    }
364    mMonitor->startMonitoring(requestId);
365    if (VDBG) ALOGD("setHostname successful");
366    cli->sendMsg(ResponseCode::CommandOkay, "setHostname started", false);
367    return;
368}
369
370void MDnsSdListenerSetHostnameCallback(DNSServiceRef /* sdRef */, DNSServiceFlags /* flags */,
371        DNSServiceErrorType errorCode, const char *hostname, void *inContext) {
372    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
373    char *msg;
374    int refNumber = context->mRefNumber;
375    if (errorCode != kDNSServiceErr_NoError) {
376        asprintf(&msg, "%d %d", refNumber, errorCode);
377        context->mListener->sendBroadcast(ResponseCode::ServiceSetHostnameFailed, msg, false);
378        if (DBG) ALOGE("setHostname failure for %d, error= %d", refNumber, errorCode);
379    } else {
380        char *quotedHostname = SocketClient::quoteArg(hostname);
381        asprintf(&msg, "%d %s", refNumber, quotedHostname);
382        free(quotedHostname);
383        context->mListener->sendBroadcast(ResponseCode::ServiceSetHostnameSuccess, msg, false);
384        if (VDBG) ALOGD("setHostname succeeded for %d.  Set to %s", refNumber, hostname);
385    }
386    free(msg);
387}
388
389
390int MDnsSdListener::Handler::ifaceNameToI(const char * /* iface */) {
391    return 0;
392}
393
394const char *MDnsSdListener::Handler::iToIfaceName(int /* i */) {
395    return NULL;
396}
397
398DNSServiceFlags MDnsSdListener::Handler::iToFlags(int /* i */) {
399    return 0;
400}
401
402int MDnsSdListener::Handler::flagsToI(DNSServiceFlags /* flags */) {
403    return 0;
404}
405
406int MDnsSdListener::Handler::runCommand(SocketClient *cli,
407                                        int argc, char **argv) {
408    if (argc < 2) {
409        char* msg = NULL;
410        asprintf( &msg, "Invalid number of arguments to mdnssd: %i", argc);
411        ALOGW("%s", msg);
412        cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
413        free(msg);
414        return -1;
415    }
416
417    char* cmd = argv[1];
418
419    if (strcmp(cmd, "discover") == 0) {
420        if (argc != 4) {
421            cli->sendMsg(ResponseCode::CommandParameterError,
422                    "Invalid number of arguments to mdnssd discover", false);
423            return 0;
424        }
425        int requestId = atoi(argv[2]);
426        char *serviceType = argv[3];
427
428        discover(cli, NULL, serviceType, NULL, requestId, 0);
429    } else if (strcmp(cmd, "stop-discover") == 0) {
430        stop(cli, argc, argv, "discover");
431    } else if (strcmp(cmd, "register") == 0) {
432        if (argc != 7) {
433            cli->sendMsg(ResponseCode::CommandParameterError,
434                    "Invalid number of arguments to mdnssd register", false);
435            return 0;
436        }
437        int requestId = atoi(argv[2]);
438        char *serviceName = argv[3];
439        char *serviceType = argv[4];
440        int port = atoi(argv[5]);
441        char *interfaceName = NULL; // will use all
442        char *domain = NULL;        // will use default
443        char *host = NULL;          // will use default hostname
444
445        // TXT record length is <= 1300, see NsdServiceInfo.setAttribute
446        char dst[1300];
447
448        int length = b64_pton(argv[6], (u_char *)dst, 1300);
449
450        if (length < 0) {
451           cli->sendMsg(ResponseCode::CommandParameterError,
452                    "Could not decode txtRecord", false);
453           return 0;
454        }
455
456        serviceRegister(cli, requestId, interfaceName, serviceName,
457                serviceType, domain, host, port, length, dst);
458    } else if (strcmp(cmd, "stop-register") == 0) {
459        stop(cli, argc, argv, "register");
460    } else if (strcmp(cmd, "resolve") == 0) {
461        if (argc != 6) {
462            cli->sendMsg(ResponseCode::CommandParameterError,
463                    "Invalid number of arguments to mdnssd resolve", false);
464            return 0;
465        }
466        int requestId = atoi(argv[2]);
467        char *interfaceName = NULL;  // will use all
468        char *serviceName = argv[3];
469        char *regType = argv[4];
470        char *domain = argv[5];
471        resolveService(cli, requestId, interfaceName, serviceName, regType, domain);
472    } else if (strcmp(cmd, "stop-resolve") == 0) {
473        stop(cli, argc, argv, "resolve");
474    } else if (strcmp(cmd, "start-service") == 0) {
475        if (mMonitor->startService()) {
476            cli->sendMsg(ResponseCode::CommandOkay, "Service Started", false);
477        } else {
478            cli->sendMsg(ResponseCode::ServiceStartFailed, "Service already running", false);
479        }
480    } else if (strcmp(cmd, "stop-service") == 0) {
481        if (mMonitor->stopService()) {
482            cli->sendMsg(ResponseCode::CommandOkay, "Service Stopped", false);
483        } else {
484            cli->sendMsg(ResponseCode::ServiceStopFailed, "Service still in use", false);
485        }
486    } else if (strcmp(cmd, "sethostname") == 0) {
487        if (argc != 4) {
488            cli->sendMsg(ResponseCode::CommandParameterError,
489                    "Invalid number of arguments to mdnssd sethostname", false);
490            return 0;
491        }
492        int requestId = atoi(argv[2]);
493        char *hostname = argv[3];
494        setHostname(cli, requestId, hostname);
495    } else if (strcmp(cmd, "stop-sethostname") == 0) {
496        stop(cli, argc, argv, "sethostname");
497    } else if (strcmp(cmd, "getaddrinfo") == 0) {
498        if (argc != 4) {
499            cli->sendMsg(ResponseCode::CommandParameterError,
500                    "Invalid number of arguments to mdnssd getaddrinfo", false);
501            return 0;
502        }
503        int requestId = atoi(argv[2]);
504        char *hostname = argv[3];
505        char *interfaceName = NULL;  // default
506        int protocol = 0;            // intelligient heuristic (both v4 + v6)
507        getAddrInfo(cli, requestId, interfaceName, protocol, hostname);
508    } else if (strcmp(cmd, "stop-getaddrinfo") == 0) {
509        stop(cli, argc, argv, "getaddrinfo");
510    } else {
511        if (VDBG) ALOGE("Unknown cmd %s", cmd);
512        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown mdnssd cmd", false);
513        return 0;
514    }
515    return 0;
516}
517
518MDnsSdListener::Monitor::Monitor() {
519    mHead = NULL;
520    mLiveCount = 0;
521    mPollFds = NULL;
522    mPollRefs = NULL;
523    mPollSize = 10;
524    socketpair(AF_LOCAL, SOCK_STREAM, 0, mCtrlSocketPair);
525    pthread_mutex_init(&mHeadMutex, NULL);
526
527    pthread_create(&mThread, NULL, MDnsSdListener::Monitor::threadStart, this);
528    pthread_detach(mThread);
529}
530
531void *MDnsSdListener::Monitor::threadStart(void *obj) {
532    Monitor *monitor = reinterpret_cast<Monitor *>(obj);
533
534    monitor->run();
535    delete monitor;
536    pthread_exit(NULL);
537    return NULL;
538}
539
540#define NAP_TIME 200  // 200 ms between polls
541static int wait_for_property(const char *name, const char *desired_value, int maxwait)
542{
543    char value[PROPERTY_VALUE_MAX] = {'\0'};
544    int maxnaps = (maxwait * 1000) / NAP_TIME;
545
546    if (maxnaps < 1) {
547        maxnaps = 1;
548    }
549
550    while (maxnaps-- > 0) {
551        usleep(NAP_TIME * 1000);
552        if (property_get(name, value, NULL)) {
553            if (desired_value == NULL || strcmp(value, desired_value) == 0) {
554                return 0;
555            }
556        }
557    }
558    return -1; /* failure */
559}
560
561int MDnsSdListener::Monitor::startService() {
562    int result = 0;
563    char property_value[PROPERTY_VALUE_MAX];
564    pthread_mutex_lock(&mHeadMutex);
565    property_get(MDNS_SERVICE_STATUS, property_value, "");
566    if (strcmp("running", property_value) != 0) {
567        ALOGD("Starting MDNSD");
568        property_set("ctl.start", MDNS_SERVICE_NAME);
569        wait_for_property(MDNS_SERVICE_STATUS, "running", 5);
570        result = -1;
571    } else {
572        result = 0;
573    }
574    pthread_mutex_unlock(&mHeadMutex);
575    return result;
576}
577
578int MDnsSdListener::Monitor::stopService() {
579    int result = 0;
580    pthread_mutex_lock(&mHeadMutex);
581    if (mHead == NULL) {
582        ALOGD("Stopping MDNSD");
583        property_set("ctl.stop", MDNS_SERVICE_NAME);
584        wait_for_property(MDNS_SERVICE_STATUS, "stopped", 5);
585        result = -1;
586    } else {
587        result = 0;
588    }
589    pthread_mutex_unlock(&mHeadMutex);
590    return result;
591}
592
593void MDnsSdListener::Monitor::run() {
594    int pollCount = 1;
595
596    mPollFds = (struct pollfd *)calloc(sizeof(struct pollfd), mPollSize);
597    mPollRefs = (DNSServiceRef **)calloc(sizeof(DNSServiceRef *), mPollSize);
598    LOG_ALWAYS_FATAL_IF((mPollFds == NULL), "initial calloc failed on mPollFds with a size of %d",
599            ((int)sizeof(struct pollfd)) * mPollSize);
600    LOG_ALWAYS_FATAL_IF((mPollRefs == NULL), "initial calloc failed on mPollRefs with a size of %d",
601            ((int)sizeof(DNSServiceRef *)) * mPollSize);
602
603    mPollFds[0].fd = mCtrlSocketPair[0];
604    mPollFds[0].events = POLLIN;
605
606    if (VDBG) ALOGD("MDnsSdListener starting to monitor");
607    while (1) {
608        if (VDBG) ALOGD("Going to poll with pollCount %d", pollCount);
609        int pollResults = poll(mPollFds, pollCount, 10000000);
610        if (pollResults < 0) {
611            ALOGE("Error in poll - got %d", errno);
612        } else if (pollResults > 0) {
613            if (VDBG) ALOGD("Monitor poll got data pollCount = %d, %d", pollCount, pollResults);
614            for(int i = 1; i < pollCount; i++) {
615                if (mPollFds[i].revents != 0) {
616                    if (VDBG) {
617                        ALOGD("Monitor found [%d].revents = %d - calling ProcessResults",
618                                i, mPollFds[i].revents);
619                    }
620                    DNSServiceProcessResult(*(mPollRefs[i]));
621                    mPollFds[i].revents = 0;
622                }
623            }
624            if (VDBG) ALOGD("controlSocket shows revent= %d", mPollFds[0].revents);
625            switch (mPollFds[0].revents) {
626                case POLLIN: {
627                    char readBuf[2];
628                    read(mCtrlSocketPair[0], &readBuf, 1);
629                    if (DBG) ALOGD("MDnsSdListener::Monitor got %c", readBuf[0]);
630                    if (memcmp(RESCAN, readBuf, 1) == 0) {
631                        pollCount = rescan();
632                    }
633                }
634            }
635            mPollFds[0].revents = 0;
636        } else {
637            if (VDBG) ALOGD("MDnsSdListener::Monitor poll timed out");
638        }
639    }
640    free(mPollFds);
641    free(mPollRefs);
642}
643
644#define DBG_RESCAN 0
645
646int MDnsSdListener::Monitor::rescan() {
647// rescan the list from mHead and make new pollfds and serviceRefs
648    if (VDBG) {
649        ALOGD("MDnsSdListener::Monitor poll rescanning - size=%d, live=%d", mPollSize, mLiveCount);
650    }
651    pthread_mutex_lock(&mHeadMutex);
652    Element **prevPtr = &mHead;
653    int i = 1;
654    if (mPollSize <= mLiveCount) {
655        mPollSize = mLiveCount + 5;
656        free(mPollFds);
657        free(mPollRefs);
658        mPollFds = (struct pollfd *)calloc(sizeof(struct pollfd), mPollSize);
659        mPollRefs = (DNSServiceRef **)calloc(sizeof(DNSServiceRef *), mPollSize);
660        LOG_ALWAYS_FATAL_IF((mPollFds == NULL), "calloc failed on mPollFds with a size of %d",
661                ((int)sizeof(struct pollfd)) * mPollSize);
662        LOG_ALWAYS_FATAL_IF((mPollRefs == NULL), "calloc failed on mPollRefs with a size of %d",
663                ((int)sizeof(DNSServiceRef *)) * mPollSize);
664    } else {
665        memset(mPollFds, 0, sizeof(struct pollfd) * mPollSize);
666        memset(mPollRefs, 0, sizeof(DNSServiceRef *) * mPollSize);
667    }
668    mPollFds[0].fd = mCtrlSocketPair[0];
669    mPollFds[0].events = POLLIN;
670    if (DBG_RESCAN) ALOGD("mHead = %p", mHead);
671    while (*prevPtr != NULL) {
672        if (DBG_RESCAN) ALOGD("checking %p, mReady = %d", *prevPtr, (*prevPtr)->mReady);
673        if ((*prevPtr)->mReady == 1) {
674            int fd = DNSServiceRefSockFD((*prevPtr)->mRef);
675            if (fd != -1) {
676                if (DBG_RESCAN) ALOGD("  adding FD %d", fd);
677                mPollFds[i].fd = fd;
678                mPollFds[i].events = POLLIN;
679                mPollRefs[i] = &((*prevPtr)->mRef);
680                i++;
681            } else {
682                ALOGE("Error retreving socket FD for live ServiceRef");
683            }
684            prevPtr = &((*prevPtr)->mNext); // advance to the next element
685        } else if ((*prevPtr)->mReady == -1) {
686            if (DBG_RESCAN) ALOGD("  removing %p from  play", *prevPtr);
687            Element *cur = *prevPtr;
688            *prevPtr = (cur)->mNext; // change our notion of this element and don't advance
689            delete cur;
690        } else if ((*prevPtr)->mReady == 0) {
691            // Not ready so just skip this node and continue on
692            if (DBG_RESCAN) ALOGD("%p not ready.  Continuing.", *prevPtr);
693            prevPtr = &((*prevPtr)->mNext);
694        }
695    }
696    pthread_mutex_unlock(&mHeadMutex);
697    return i;
698}
699
700DNSServiceRef *MDnsSdListener::Monitor::allocateServiceRef(int id, Context *context) {
701    if (lookupServiceRef(id) != NULL) {
702        delete(context);
703        return NULL;
704    }
705    Element *e = new Element(id, context);
706    pthread_mutex_lock(&mHeadMutex);
707    e->mNext = mHead;
708    mHead = e;
709    pthread_mutex_unlock(&mHeadMutex);
710    return &(e->mRef);
711}
712
713DNSServiceRef *MDnsSdListener::Monitor::lookupServiceRef(int id) {
714    pthread_mutex_lock(&mHeadMutex);
715    Element *cur = mHead;
716    while (cur != NULL) {
717        if (cur->mId == id) {
718            DNSServiceRef *result = &(cur->mRef);
719            pthread_mutex_unlock(&mHeadMutex);
720            return result;
721        }
722        cur = cur->mNext;
723    }
724    pthread_mutex_unlock(&mHeadMutex);
725    return NULL;
726}
727
728void MDnsSdListener::Monitor::startMonitoring(int id) {
729    if (VDBG) ALOGD("startMonitoring %d", id);
730    pthread_mutex_lock(&mHeadMutex);
731    Element *cur = mHead;
732    while (cur != NULL) {
733        if (cur->mId == id) {
734            if (DBG_RESCAN) ALOGD("marking %p as ready to be added", cur);
735            mLiveCount++;
736            cur->mReady = 1;
737            pthread_mutex_unlock(&mHeadMutex);
738            write(mCtrlSocketPair[1], RESCAN, 1);  // trigger a rescan for a fresh poll
739            if (VDBG) ALOGD("triggering rescan");
740            return;
741        }
742        cur = cur->mNext;
743    }
744    pthread_mutex_unlock(&mHeadMutex);
745}
746
747void MDnsSdListener::Monitor::freeServiceRef(int id) {
748    if (VDBG) ALOGD("freeServiceRef %d", id);
749    pthread_mutex_lock(&mHeadMutex);
750    Element **prevPtr = &mHead;
751    Element *cur;
752    while (*prevPtr != NULL) {
753        cur = *prevPtr;
754        if (cur->mId == id) {
755            if (DBG_RESCAN) ALOGD("marking %p as ready to be removed", cur);
756            mLiveCount--;
757            if (cur->mReady == 1) {
758                cur->mReady = -1; // tell poll thread to delete
759                write(mCtrlSocketPair[1], RESCAN, 1); // trigger a rescan for a fresh poll
760                if (VDBG) ALOGD("triggering rescan");
761            } else {
762                *prevPtr = cur->mNext;
763                delete cur;
764            }
765            pthread_mutex_unlock(&mHeadMutex);
766            return;
767        }
768        prevPtr = &(cur->mNext);
769    }
770    pthread_mutex_unlock(&mHeadMutex);
771}
772