00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
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
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
00054 switch (state) {
00055 case AVAHI_ENTRY_GROUP_ESTABLISHED :
00056
00057 cerr << "Service " << zConfigurator->serviceName << " successfully established." << endl;
00058 break;
00059
00060 case AVAHI_ENTRY_GROUP_COLLISION : {
00061 char *n;
00062
00063
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
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
00102
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
00112
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
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
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
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
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
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
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
00444
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
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
00589
00590
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