ZeroconfConnector.cc

Go to the documentation of this file.
00001 //
00002 // Copyright (C) 2008 Institut fuer Telematik, Universitaet Karlsruhe (TH)
00003 //
00004 // This program is free software; you can redistribute it and/or
00005 // modify it under the terms of the GNU General Public License
00006 // as published by the Free Software Foundation; either version 2
00007 // of the License, or (at your option) any later version.
00008 //
00009 // This program is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License
00015 // along with this program; if not, write to the Free Software
00016 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00017 //
00018 
00024 #include "ZeroconfConnector.h"
00025 
00026 Define_Module(ZeroconfConnector);
00027 
00028 #ifdef HAVE_AVAHI
00029 
00030 #include <stdio.h>
00031 #include <assert.h>
00032 #include <stdlib.h>
00033 #include <time.h>
00034 #include <semaphore.h>
00035 #include <iostream>
00036 #include <arpa/inet.h>
00037 #include <sys/socket.h>
00038 #include <netinet/in.h>
00039 
00040 #include <BaseOverlay.h>
00041 #include <GlobalNodeListAccess.h>
00042 #include <BootstrapList.h>
00043 
00044 using namespace std;
00045 
00046 // TODO cleanup code
00047 void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
00048     AvahiClient *client = avahi_entry_group_get_client(g);
00049     assert(client);
00050     ZeroconfConnector *zConfigurator = (ZeroconfConnector *)userdata;
00051     assert(zConfigurator);
00052 
00053     /* Called whenever the entry group state changes */
00054     switch (state) {
00055         case AVAHI_ENTRY_GROUP_ESTABLISHED :
00056             /* The entry group has been established successfully */
00057             cerr << "Service " << zConfigurator->serviceName << " successfully established." << endl;
00058             break;
00059 
00060         case AVAHI_ENTRY_GROUP_COLLISION : {
00061             char *n;
00062 
00063             /* A service name collision happened. Pick a new name */
00064             n = avahi_alternative_service_name(zConfigurator->serviceName);
00065             avahi_free(zConfigurator->serviceName);
00066             zConfigurator->serviceName = n;
00067 
00068             cerr << "Service name collision, renaming service to " << n  << "." << endl;
00069 
00070             /* Recreate the services with new name*/
00071             create_services(client, zConfigurator);
00072             break;
00073         }
00074 
00075         case AVAHI_ENTRY_GROUP_FAILURE :
00076 
00077             cerr << "Entry group failure: " << avahi_strerror(avahi_client_errno(client)) << endl;
00078 
00079             avahi_threaded_poll_quit(zConfigurator->threadedPoll);
00080             break;
00081 
00082         default:
00083             break;
00084     }
00085 }
00086 
00087 
00088 void create_services(AvahiClient *c, ZeroconfConnector *zConfigurator) {
00089     char *n, r[3][128];
00090     int ret;
00091 
00092     assert(c);
00093     assert(zConfigurator);
00094 
00095     if (!zConfigurator) {
00096         cerr << "ZeroconfConnector not available, can not create service" << endl;
00097         return;
00098     }
00099 
00100     /*
00101      * If this is the first time we're called, let's create a new
00102      * entry group if necessary
00103      */
00104     if (!zConfigurator->group)
00105         if (!(zConfigurator->group = avahi_entry_group_new(c, entry_group_callback, zConfigurator))) {
00106             cerr << "avahi_entry_group_new() failed: " << avahi_strerror(avahi_client_errno(c)) << endl;
00107             goto fail;
00108         }
00109 
00110     /*
00111      * If the group is empty (either because it was just created, or
00112      * because it was reset previously, add our entries
00113      */
00114     if (avahi_entry_group_is_empty(zConfigurator->group)) {
00115         cerr << "Adding service " << zConfigurator->serviceName << endl;
00116 
00117         snprintf(r[0], sizeof(r[0]), "peerid=%s", (zConfigurator->thisNode->key.toString(16)).c_str());
00118         snprintf(r[1], sizeof(r[1]), "overlayid=%s", zConfigurator->overlayName);
00119         snprintf(r[2], sizeof(r[2]), "overlaytype=%s", zConfigurator->overlayType);
00120 
00121 
00122         if ((ret = avahi_entry_group_add_service(zConfigurator->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,(AvahiPublishFlags)0,
00123             zConfigurator->serviceName, zConfigurator->serviceType, NULL, NULL, zConfigurator->thisNode->getPort(), r[0], r[1], r[2], NULL)) < 0) {
00124 
00125             if (ret == AVAHI_ERR_COLLISION)
00126                 goto collision;
00127 
00128             cerr << "Failed to add _p2pbootstrap._udp service: " << avahi_strerror(ret) << endl;
00129             goto fail;
00130         }
00131 
00132         /* Tell the server to register the service */
00133         if ((ret = avahi_entry_group_commit(zConfigurator->group)) < 0) {
00134             cerr << "Failed to commit entry group: " << avahi_strerror(ret) << endl;
00135             goto fail;
00136         }
00137     }
00138 
00139     return;
00140 
00141 collision:
00142 
00143 
00144     /* A service name collision happened. Pick a new name */
00145     n = avahi_alternative_service_name(zConfigurator->serviceName);
00146     avahi_free(zConfigurator->serviceName);
00147     zConfigurator->serviceName = n;
00148 
00149     cerr << "Service name collision, renaming service to " << n << endl;
00150 
00151     avahi_entry_group_reset(zConfigurator->group);
00152 
00153     create_services(c, zConfigurator);
00154     return;
00155 
00156 fail:
00157     avahi_threaded_poll_quit(zConfigurator->threadedPoll);
00158 }
00159 
00160 void resolv_callback(
00161     AvahiServiceResolver *r,
00162     AVAHI_GCC_UNUSED AvahiIfIndex interface,
00163     AVAHI_GCC_UNUSED AvahiProtocol protocol,
00164     AvahiResolverEvent event,
00165     const char *name,
00166     const char *type,
00167     const char *domain,
00168     const char *host_name,
00169     const AvahiAddress *address,
00170     uint16_t port,
00171     AvahiStringList *txt,
00172     AvahiLookupResultFlags flags,
00173     AVAHI_GCC_UNUSED void* userdata) {
00174 
00175     char *peerID = NULL;
00176     char *overlayType = NULL;
00177     AvahiStringList *record;
00178     AvahiClient *client = NULL;
00179     ZeroconfConnector *zConfigurator = NULL;
00180 
00181     assert(r);
00182 
00183     client = avahi_service_resolver_get_client(r);
00184     assert(client);
00185 
00186     zConfigurator = (ZeroconfConnector *)userdata;
00187     assert(zConfigurator);
00188 
00189 
00190     /* Called whenever a service has been resolved successfully or timed out */
00191     switch (event) {
00192         case AVAHI_RESOLVER_FAILURE:
00193             cerr << "(Resolver) Failed to resolve service " << name << " of type " << type << " in domain " <<
00194                 domain << ": " << avahi_strerror(avahi_client_errno(client)) << endl;
00195             break;
00196 
00197         case AVAHI_RESOLVER_FOUND: {
00198             char a[AVAHI_ADDRESS_STR_MAX], *t;
00199 
00200             cout << "Service " << name << " of type " << type << " in domain " << domain << endl;
00201 
00202             avahi_address_snprint(a, sizeof(a), address);
00203             t = avahi_string_list_to_string(txt);
00204             fprintf(stderr,
00205                     "\t%s:%u (%s)\n"
00206                     "\tTXT=%s\n"
00207                     "\tcookie is %u\n"
00208                     "\tis_local: %i\n"
00209                     "\tour_own: %i\n"
00210                     "\twide_area: %i\n"
00211                     "\tmulticast: %i\n"
00212                     "\tcached: %i\n",
00213                     host_name, port, a,
00214                     t,
00215                     avahi_string_list_get_service_cookie(txt),
00216                     !!(flags & AVAHI_LOOKUP_RESULT_LOCAL),
00217                     !!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN),
00218                     !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA),
00219                     !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST),
00220                     !!(flags & AVAHI_LOOKUP_RESULT_CACHED));
00221 
00222             avahi_free(t);
00223 
00224             /* parse the TXT record to search for the overlay algorithm and nodeID of the node */
00225             for (record = txt; record; record = record->next) {
00226                 if (!strncmp((char *)record->text, "overlaytype", 11)) {
00227                     if ((char)record->text[11] == '=') {
00228                         overlayType = avahi_strdup((const char *)(record->text + 12));
00229                     }
00230                 } else {
00231                     if (!strncmp((char *)record->text, "peerid", 6)) {
00232                         if (record->text[6] == '=') {
00233                             peerID = avahi_strdup((const char *)(record->text + 7));
00234                         }
00235                     }
00236                 }
00237             }
00238 
00239             if (!overlayType || !peerID || strncmp(overlayType, zConfigurator->overlayType,
00240                 strlen(overlayType))) {
00241                 cerr << "TXT record of the node is defect, or node does not use the desired p2p algorithm." << endl;
00242 
00243                 if (overlayType)
00244                     avahi_free(overlayType);
00245 
00246                 if (peerID)
00247                     avahi_free(peerID);
00248 
00249                 break;
00250             }
00251 
00252             if (zConfigurator) {
00253                 BootstrapNodeHandle *node = new BootstrapNodeHandle(OverlayKey(peerID, 16),
00254                     IPvXAddress(a), (int)port,
00255                     !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA) ? DNSSD : MDNS);
00256 
00257                 if (node) {
00258                     zConfigurator->insertNode(avahi_strdup(name), node);
00259                 } else {
00260                     cerr << "Failed to allocate memory for NodeHandle" << endl;
00261                 }
00262             }
00263 
00264             avahi_free(overlayType);
00265             avahi_free(peerID);
00266 
00267             break;
00268         }
00269 
00270         default:
00271             break;
00272     }
00273 
00274     avahi_service_resolver_free(r);
00275 
00276     return;
00277 }
00278 
00279 void browse_callback(
00280     AvahiServiceBrowser *b,
00281     AvahiIfIndex interface,
00282     AvahiProtocol protocol,
00283     AvahiBrowserEvent event,
00284     const char *name,
00285     const char *type,
00286     const char *domain,
00287     AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
00288     void* userdata) {
00289 
00290     AvahiClient *c = NULL;
00291     ZeroconfConnector *zConfigurator = NULL;
00292     assert(b);
00293 
00294     c = avahi_service_browser_get_client(b);
00295     assert(c);
00296 
00297     zConfigurator = (ZeroconfConnector *)userdata;
00298     assert(zConfigurator);
00299 
00300     /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
00301     switch (event) {
00302         case AVAHI_BROWSER_FAILURE:
00303             cerr << "(Browser) " << avahi_strerror(avahi_client_errno(c)) << endl;
00304             avahi_threaded_poll_quit(zConfigurator->threadedPoll);
00305             return;
00306 
00307         case AVAHI_BROWSER_NEW:
00308             cerr << "(Browser) NEW: service " << name << " of type " << type << " in domain " << domain << endl;
00309 
00310             if (strcmp(name, zConfigurator->serviceName)) {
00311                 if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_INET, (AvahiLookupFlags)0, resolv_callback, zConfigurator)))
00312                     cerr << "Failed to resolve service " << name << ":" << avahi_strerror(avahi_client_errno(c)) << endl;
00313             }
00314             break;
00315 
00316         case AVAHI_BROWSER_REMOVE:
00317             cerr << "(Browser) REMOVE: service " << name << " of type " << type << " in domain " << domain << endl;
00318             zConfigurator->removeNode((char *)name);
00319             break;
00320 
00321         case AVAHI_BROWSER_ALL_FOR_NOW:
00322         case AVAHI_BROWSER_CACHE_EXHAUSTED:
00323             cerr << "(Browser) " << (event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW") << endl;
00324             break;
00325 
00326         default:
00327             break;
00328     }
00329 }
00330 
00331 
00332 void client_callback(
00333     AvahiClient *c,
00334     AvahiClientState state,
00335     AVAHI_GCC_UNUSED void * userdata) {
00336     ZeroconfConnector *zConfigurator = NULL;
00337 
00338     assert(c);
00339     zConfigurator = (ZeroconfConnector *)userdata;
00340     assert(zConfigurator);
00341 
00342     /* Called whenever the client or server state changes */
00343     switch (state) {
00344         case AVAHI_CLIENT_FAILURE:
00345             cerr <<  "Client failure: " << avahi_strerror(avahi_client_errno(c)) << endl;
00346             avahi_threaded_poll_quit(zConfigurator->threadedPoll);
00347 
00348             break;
00349 
00350         case AVAHI_CLIENT_S_REGISTERING:
00351             if (zConfigurator->group)
00352                 avahi_entry_group_reset(zConfigurator->group);
00353 
00354             break;
00355 
00356         default:
00357            break;
00358     }
00359 }
00360 
00361 
00362 ZeroconfConnector::ZeroconfConnector()
00363 {
00364     initResult = AVAHI_INIT_FAILED;
00365 
00366     client = NULL;
00367     group = NULL;
00368     threadedPoll = NULL;
00369 
00370     sbMDNS = sbUDNS = NULL;
00371 
00372     serviceType = overlayName = overlayType = NULL;
00373     serviceName = NULL;
00374     thisNode = NULL;
00375 
00376     if (sem_init(&nodeSetSem, 0, 1) == -1) {
00377         cerr << "nodeSetSem can't be initialized." << endl;
00378     }
00379 
00380     pollingTimer = NULL;
00381 
00382     return;
00383 }
00384 
00385 
00386 ZeroconfConnector::~ZeroconfConnector()
00387 {
00388     if (!enabled) {
00389         return;
00390     }
00391 
00392     LocalBNodeSet::iterator iter;
00393     cancelAndDelete(pollingTimer);
00394 
00395     avahi_threaded_poll_stop(threadedPoll);
00396     avahi_service_browser_free(sbMDNS);
00397     avahi_service_browser_free(sbUDNS);
00398 
00399     if (group)
00400         avahi_entry_group_free(group);
00401 
00402     avahi_client_free(client);
00403     avahi_threaded_poll_free(threadedPoll);
00404 
00405     sem_destroy(&nodeSetSem);
00406 
00407     for (iter = newSet.begin(); iter != newSet.end(); iter++) {
00408         avahi_free(iter->first);
00409         delete iter->second;
00410     }
00411     newSet.clear();
00412 
00413     if (serviceName)
00414         avahi_free(serviceName);
00415 
00416     if (thisNode)
00417         delete thisNode;
00418 
00419     return;
00420 }
00421 
00422 
00423 void ZeroconfConnector::announceService(const NodeHandle &node)
00424 {
00425     if (node.isUnspecified()) {
00426         cerr << "unspecified node for service announcement" << endl;
00427         return;
00428     }
00429 
00430     if (!(thisNode = new NodeHandle(node))) {
00431         cerr << "no resource to save local node" << endl;
00432         return;
00433     }
00434 
00435     create_services(client, this);
00436 
00437     return;
00438 }
00439 
00440 void ZeroconfConnector::revokeService()
00441 {
00442     /*
00443      * Reset the avahi entry group to revoke
00444      * bootstrap service.
00445      */
00446     if (group)
00447         avahi_entry_group_reset(group);
00448 
00449     return;
00450 }
00451 
00452 
00453 int ZeroconfConnector::insertNode(char *name, BootstrapNodeHandle *node)
00454 {
00455     if ((!node)) {
00456         cerr << "Trying to insert invalid node" << endl;
00457         return -1;
00458     }
00459 
00460     cerr << "insertNode called" << endl;
00461 
00462     sem_wait(&nodeSetSem);
00463     newSet.insert(LocalNodePair(name, node));
00464 
00465     sem_post(&nodeSetSem);
00466 
00467     return 0;
00468 }
00469 
00470 
00471 int ZeroconfConnector::removeNode(char *name)
00472 {
00473     LocalBNodeSet::iterator iter;
00474     if (!name) {
00475         cerr << "node name invalid" << endl;
00476         return -1;
00477     }
00478 
00479     sem_wait(&nodeSetSem);
00480 
00481     iter = newSet.find((char *)name);
00482     if (iter != newSet.end()) {
00483         delete iter->second;
00484         avahi_free(iter->first);
00485         newSet.erase(iter);
00486     }
00487 
00488     sem_post(&nodeSetSem);
00489     return 0;
00490 }
00491 
00492 
00493 inline int ZeroconfConnector::getInitResult()
00494 {
00495     return initResult;
00496 }
00497 
00498 
00499 void ZeroconfConnector::initialize()
00500 {
00501     int error;
00502 
00503     enabled = par("enableZeroconf");
00504     serviceType = par("serviceType");
00505     serviceName = avahi_strdup(par("serviceName"));
00506     overlayType = par("overlayType");
00507     overlayName = par("overlayName");
00508 
00509     if (!enabled) {
00510         return;
00511     }
00512 
00513     if (!(threadedPoll = avahi_threaded_poll_new())) {
00514         cerr << "Failed to create a threaded poll." << endl;
00515         goto fail;
00516     }
00517 
00518 
00519     if (!(client = avahi_client_new(avahi_threaded_poll_get(threadedPoll), (AvahiClientFlags)0, client_callback,
00520         this, &error))) {
00521         cerr << "Failed to create a client." << endl;
00522         goto fail;
00523     }
00524 
00525     /* using AVAHI_PROTO_INET means we only need IPv4 informaion */
00526     if (!(sbMDNS = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, serviceType, NULL,
00527         (AvahiLookupFlags)0, browse_callback, this))) {
00528         cerr << "Failed to create a service browser." << endl;
00529         goto fail;
00530     }
00531 
00532     if (!(sbUDNS = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, serviceType, overlayName,
00533         (AvahiLookupFlags)0, browse_callback, this))) {
00534         cerr << "Failed to create a service browser." << endl;
00535         goto fail;
00536     }
00537 
00538     if (avahi_threaded_poll_start(threadedPoll) < 0) {
00539         cerr << "Failed to start the threaded poll." << endl;
00540         goto fail;
00541     }
00542 
00543     pollingTimer = new cMessage("Zeroconf timer");
00544 
00545     scheduleAt(simTime() + 1, pollingTimer);
00546     initResult = AVAHI_INIT_SUCCEEDED;
00547 
00548     return;
00549 
00550 fail:
00551     if (sbMDNS)
00552         avahi_service_browser_free(sbMDNS);
00553 
00554     if (sbUDNS)
00555         avahi_service_browser_free(sbUDNS);
00556 
00557     if (client)
00558         avahi_client_free(client);
00559 
00560     if (threadedPoll)
00561         avahi_threaded_poll_free(threadedPoll);
00562 
00563     if (pollingTimer)
00564         delete pollingTimer;
00565 
00566     return;
00567 }
00568 
00569 void ZeroconfConnector::handleMessage(cMessage *msg)
00570 {
00571     char *name;
00572     BootstrapNodeHandle *node;
00573     LocalBNodeSet::iterator tempIter;
00574 
00575     BootstrapList *bootstrapList =
00576         check_and_cast<BootstrapList *>(getParentModule()->getSubmodule("singleHost", 0)->getSubmodule("bootstrapList", 0));
00577 
00578     if (msg->isSelfMessage()) {
00579         if (!sem_trywait(&nodeSetSem)) {
00580             if (!newSet.empty()) {
00581                 for (LocalBNodeSet::iterator iter = newSet.begin(); iter != newSet.end();) {
00582                     name = iter->first;
00583                     node = iter->second;
00584                     tempIter = iter;
00585                     iter++;
00586 
00587                     /*
00588                      * Insert bootstrap node into bootstrapList. After
00589                      * insertion, BootstrapList is responsible for deleting
00590                      * these nodes
00591                      */
00592                     bootstrapList->insertBootstrapCandidate(*node);
00593                     newSet.erase(tempIter);
00594                     avahi_free(name);
00595                 }
00596 
00597                 newSet.clear();
00598             }
00599 
00600             sem_post(&nodeSetSem);
00601         }
00602     }
00603 
00604     scheduleAt(simTime() + 5, msg);
00605 
00606     return;
00607 }
00608 
00609 #endif
Generated on Wed May 26 16:21:15 2010 for OverSim by  doxygen 1.6.3