1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Target Lists 31 * ============ 32 * All UA functions use target lists to select and manage their 33 * network targets. There are two types of network targets: unicast (uc) 34 * and multicast (mc) -- multicast will also work for broadcast. This 35 * module organizes unicast targets into an efficient ordering. The 36 * targeting structure can be though of as a 2-dimensional matrix, with 37 * the following axes: 38 * 39 * unicast failovers ---> 40 * targets 41 * | 42 * | 43 * \ / 44 * 45 * Callers walk down the unicast targets, unicasting to each. If any 46 * unicast target fails, callers then walk to the right, through failover 47 * targets until they either find one that works, or there are no more 48 * failover targets. 49 * 50 * The targeting heuristic orders the unicast targets so that those 51 * DAs which support the greatest number of requested scopes are called 52 * first, thus minimizing the number of unicasts which need to be done. 53 * Within groups of DAs supporting the same scope coverage, the DAs are 54 * sorted according to network proximity relative to the local host: 55 * DAs on the local host come first, then those on a same subnet, then 56 * all other (remote) DAs. 57 * 58 * A given DA is called no more than once, and failed DAs are skipped 59 * after they have been marked 'failed'. 60 * 61 * All access to a target list is done through the following functions 62 * and types: 63 * There are two opaque types: 64 * slp_target_list_t: A handle to a target list 65 * slp_target_t: A handle to an individual target. slp_get_target_sin 66 * will extract an inet address for this target. 67 * 68 * There are the following accessor functions: 69 * slp_new_target_list: creates a new target list for the given scopes, 70 * and populates with all known DAs for these scopes. 71 * slp_get_uc_scopes: returns a list of all scopes for which there are 72 * DAs (and which can thus be used for unicasts) 73 * slp_get_mc_scopes: returns a list of all scopes for which there are 74 * no DAs (and which must thus be used for multicasts). 75 * slp_next_uc_target: Returns a slp_target_t handle for the next unicast 76 * target, or NULL for none. 77 * slp_next_failover: Returns the next failover DA for a given target, or 78 * NULL for none. 79 * slp_get_target_sin: extracts a sockaddr_in for a given slp_target_t; 80 * slp_mark_target_used: callers should mark a slp_target_t used after 81 * successfully communicating with that target. 82 * slp_mark_target_failed: callers should mark a slp_target_t failed after 83 * trying and failing to communicate with a target. 84 * slp_destroy_target_list: destroys and frees a target list and all its 85 * associated resources. 86 * slp_fabricate_target: Creates a slp_target_t from a given sockaddr_in. 87 * This is useful for situations such as when a 88 * multicast routine needs to hand off to a TCP 89 * routine (due to overflow), and there is no target 90 * list available. Fabricated targets should be free'd 91 * with slp_free_target; the input sin will duplicated 92 * in the target, so the caller can free it after 93 * calling slp_fabricate_target. 94 * slp_free_target: Frees an slp_target_t created by slp_fabricate_target. 95 * This should not be used to free any other target. 96 * 97 */ 98 99 #include <stdio.h> 100 #include <stdlib.h> 101 #include <string.h> 102 #include <syslog.h> 103 #include <arpa/inet.h> 104 #include <slp-internal.h> 105 #include <slp_net_utils.h> 106 107 typedef enum { 108 SLP_REMOTE_PROX = 0, /* remote to local host */ 109 SLP_SUBNET_PROX = 1, /* on same subnet as local host */ 110 SLP_LOCAL_PROX = 2 /* on local host */ 111 } slp_net_prox; 112 113 struct da_node { 114 struct sockaddr_in sin; 115 char *scopes; 116 SLPBoolean used, failed; 117 int coverage; 118 slp_net_prox proximity; 119 struct da_node *next, *prev; 120 }; 121 122 struct scope_targets { 123 struct da_node *da; 124 struct scope_targets *next; 125 }; 126 127 struct target_list { 128 struct scope_targets **scopes; 129 struct scope_targets **state; 130 char *uc_scopes; 131 char *mc_scopes; 132 char *all_scopes; 133 struct da_node *DAs; 134 }; 135 136 static void add2scopes_list(struct da_node *, struct target_list *); 137 static void add_da_entry(struct da_node **, struct sockaddr_in *, 138 char *, slp_net_prox, int); 139 static SLPSrvURLCallback collect_DAs; 140 static void format_query(char *, const char *); 141 142 SLPError slp_new_target_list(slp_handle_impl_t *hp, const char *scopes, 143 slp_target_list_t **handle) { 144 struct target_list *tl; 145 int scope_cnt; 146 char *p; 147 struct da_node *te; 148 char *query, *reply; 149 SLPError err; 150 void *collator = NULL; 151 152 /* count the number of scopes in the list */ 153 scope_cnt = 0; 154 for (p = (char *)scopes; p; p++) { 155 p = slp_utf_strchr(p, ','); 156 scope_cnt++; 157 if (!p) 158 break; 159 } 160 161 /* create a new target list */ 162 if (!(tl = calloc(1, sizeof (*tl)))) { 163 slp_err(LOG_CRIT, 0, "slp_new_target_list", "out of memory"); 164 return (SLP_MEMORY_ALLOC_FAILED); 165 } 166 tl->DAs = NULL; 167 168 if (!(tl->scopes = calloc(scope_cnt + 1, sizeof (*(tl->scopes))))) { 169 slp_err(LOG_CRIT, 0, "slp_new_target_list", "out of memory"); 170 free(tl); 171 return (SLP_MEMORY_ALLOC_FAILED); 172 } 173 tl->uc_scopes = NULL; 174 tl->state = tl->scopes; 175 if (!(tl->all_scopes = strdup(scopes))) { 176 slp_err(LOG_CRIT, 0, "slp_new_target_list", "out of memory"); 177 free(tl->scopes); free(tl); 178 return (SLP_MEMORY_ALLOC_FAILED); 179 } 180 /* As scopes are added to uc list, they are removed from the mc list */ 181 if (!(tl->mc_scopes = strdup(scopes))) { 182 slp_err(LOG_CRIT, 0, "slp_new_target_list", "out of memory"); 183 free(tl->scopes); free(tl->all_scopes); free(tl); 184 return (SLP_MEMORY_ALLOC_FAILED); 185 } 186 187 if (hp->force_multicast) { 188 /* all scopes remain multicast scopes; useful for SAAdverts */ 189 *handle = tl; 190 return (SLP_OK); 191 } 192 193 /* DAs from active and passive discovery */ 194 if (!(query = malloc(strlen(scopes) - 195 (scope_cnt - 1) + /* exclude commas */ 196 strlen(SLP_SUN_VERSION_TAG) + 197 strlen("(&(=2)(|))") + 1 + 198 (scope_cnt * 199 (strlen(SLP_SUN_SCOPES_TAG) + 200 strlen("(=)")))))) { /* (scopes=) */ 201 slp_err(LOG_CRIT, 0, "slp_new_target_list", "out of memory"); 202 free(tl->scopes); 203 free(tl->all_scopes); 204 free(tl->mc_scopes); 205 free(tl); 206 return (SLP_MEMORY_ALLOC_FAILED); 207 } 208 format_query(query, scopes); 209 210 if ((err = slp_find_das(query, &reply)) != SLP_OK && 211 err != SLP_NETWORK_ERROR) { 212 free(tl->scopes); 213 free(tl->all_scopes); 214 free(tl->mc_scopes); 215 free(tl); 216 free(query); 217 return (err); 218 } 219 free(query); 220 221 /* Unpack the reply */ 222 if (reply) { 223 int numResults = 0; /* placeholder; not actually used */ 224 /* tag call as internal */ 225 hp->internal_call = SLP_TRUE; 226 227 (void) slp_unpackSrvReply(hp, reply, collect_DAs, 228 tl, &collator, &numResults); 229 free(reply); 230 /* invoke last call */ 231 (void) slp_unpackSrvReply(hp, NULL, collect_DAs, 232 tl, &collator, &numResults); 233 234 /* revert internal call tag */ 235 hp->internal_call = SLP_FALSE; 236 } 237 238 /* 239 * tl->DAs now points to a list of DAs sorted by the number of 240 * relevant scopes they serve. Using this ordering, populate the 241 * scope array lists. 242 */ 243 for (te = tl->DAs; te; te = te->next) 244 add2scopes_list(te, tl); 245 246 *handle = tl; 247 return (SLP_OK); 248 } 249 250 const char *slp_get_uc_scopes(slp_target_list_t *h) { 251 struct target_list *tl = (struct target_list *)h; 252 return (tl->uc_scopes); 253 } 254 255 const char *slp_get_mc_scopes(slp_target_list_t *h) { 256 struct target_list *tl = (struct target_list *)h; 257 return (tl->mc_scopes); 258 } 259 260 slp_target_t *slp_next_uc_target(slp_target_list_t *h) { 261 struct scope_targets *p; 262 struct target_list *tl = (struct target_list *)h; 263 264 if (!(*tl->state)) 265 return (NULL); 266 /* find the next unused target */ 267 for (; *tl->state; tl->state++) { 268 if (!(*tl->state)->da->used && !(*tl->state)->da->failed) 269 return (*tl->state++); 270 if ((*tl->state)->da->failed) { 271 /* get next failover */ 272 if (p = slp_next_failover(*tl->state)) { 273 tl->state++; 274 return (p); 275 } 276 /* else nothing more we can do */ 277 } 278 } 279 return (NULL); 280 } 281 282 slp_target_t *slp_next_failover(slp_target_t *h) { 283 struct scope_targets *p = (struct scope_targets *)h; 284 for (p = p->next; p; p = p->next) { 285 if (p->da->used) 286 return (NULL); /* already did this scope */ 287 if (!p->da->used && !p->da->failed) 288 return (p); 289 } 290 return (NULL); 291 } 292 293 void *slp_get_target_sin(slp_target_t *h) { 294 struct scope_targets *p = (struct scope_targets *)h; 295 return (void *)(p ? &(p->da->sin) : NULL); 296 } 297 298 void slp_mark_target_used(slp_target_t *h) { 299 struct scope_targets *p = (struct scope_targets *)h; 300 p->da->used = SLP_TRUE; 301 } 302 303 void slp_mark_target_failed(slp_target_t *h) { 304 struct scope_targets *p = (struct scope_targets *)h; 305 p->da->failed = SLP_TRUE; 306 } 307 308 slp_target_t *slp_fabricate_target(void *s) { 309 struct da_node *dn; 310 struct scope_targets *st; 311 struct sockaddr_in *sin = (struct sockaddr_in *)s; 312 313 if (!(st = malloc(sizeof (*st)))) { 314 slp_err(LOG_CRIT, 0, "slp_fabricate_target", "out of memory"); 315 return (NULL); 316 } 317 if (!(dn = malloc(sizeof (*dn)))) { 318 free(st); 319 slp_err(LOG_CRIT, 0, "slp_fabricate_target", "out of memory"); 320 return (NULL); 321 } 322 (void) memcpy(&(dn->sin), sin, sizeof (dn->sin)); 323 dn->used = dn->failed = SLP_FALSE; 324 dn->coverage = 0; 325 dn->proximity = SLP_REMOTE_PROX; 326 dn->next = dn->prev = NULL; 327 328 st->da = dn; 329 st->next = NULL; 330 331 return (st); 332 } 333 334 void slp_free_target(slp_target_t *target) { 335 struct scope_targets *t = (struct scope_targets *)target; 336 if (!t) 337 return; 338 free(t->da); 339 free(t); 340 } 341 342 void slp_destroy_target_list(slp_target_list_t *h) { 343 struct da_node *das, *dap; 344 int i; 345 struct target_list *tl = (struct target_list *)h; 346 347 /* free da node list */ 348 for (das = tl->DAs; das; das = dap) { 349 dap = das->next; 350 free(das->scopes); 351 free(das); 352 } 353 354 /* free scope target linked lists */ 355 for (i = 0; tl->scopes[i]; i++) { 356 struct scope_targets *sts, *stp; 357 for (sts = tl->scopes[i]; sts; sts = stp) { 358 stp = sts->next; 359 free(sts); 360 } 361 } 362 363 /* free scope array */ 364 free(tl->scopes); 365 366 /* free any char * lists in use */ 367 if (tl->uc_scopes) 368 free(tl->uc_scopes); 369 if (tl->mc_scopes) 370 free(tl->mc_scopes); 371 free(tl->all_scopes); 372 373 /* free the target list struct */ 374 free(tl); 375 } 376 377 static void add2scopes_list(struct da_node *te, struct target_list *tl) { 378 struct scope_targets **scopes = tl->scopes; 379 char *p, *s; 380 int i; 381 382 /* 383 * for each scope in tl->uc_scopes: 384 * add this DA if it serves the scope. 385 */ 386 i = 0; 387 for (s = tl->uc_scopes; s; s = p) { 388 p = slp_utf_strchr(s, ','); 389 if (p) 390 *p = 0; 391 if (slp_onlist(s, te->scopes)) { 392 struct scope_targets *st, *stp; 393 /* add this DA node to this scope's target list */ 394 if (!(st = malloc(sizeof (*st)))) { 395 slp_err(LOG_CRIT, 0, "add2scopes_list", 396 "out of memory"); 397 return; 398 } 399 st->da = te; 400 st->next = NULL; 401 /* find the end of the target list */ 402 for (stp = scopes[i]; stp && stp->next; ) { 403 stp = stp->next; 404 } 405 if (stp) 406 stp->next = st; 407 else 408 scopes[i] = st; 409 } 410 if (p) 411 *p++ = ','; 412 i++; 413 } 414 } 415 416 static void add_da_entry(struct da_node **tel, struct sockaddr_in *sin, 417 char *scopes, slp_net_prox proximity, int c) { 418 struct da_node *te, *p; 419 420 if (!(te = malloc(sizeof (*te)))) { 421 slp_err(LOG_CRIT, 0, "add_da_entry", "out of memory"); 422 return; 423 } 424 te->scopes = scopes; 425 te->coverage = c; 426 te->proximity = proximity; 427 (void) memcpy(&(te->sin), sin, sizeof (te->sin)); 428 te->used = SLP_FALSE; 429 te->failed = SLP_FALSE; 430 te->prev = NULL; 431 te->next = NULL; 432 433 /* find its place in the list */ 434 if (!(*tel)) { 435 *tel = te; 436 return; 437 } 438 for (p = *tel; p; p = p->next) 439 if (c >= p->coverage) { 440 /* found a coverage grouping; now sort by proximity */ 441 for (; p && proximity < p->proximity; ) 442 p = p->next; 443 444 if (!p) { 445 break; 446 } 447 448 /* add it here */ 449 te->next = p; 450 te->prev = p->prev; 451 if (p->prev) 452 p->prev->next = te; 453 else 454 /* we're at the head */ 455 (*tel) = te; 456 p->prev = te; 457 return; 458 } 459 460 /* didn't find a place in the list, so add it at the end */ 461 for (p = *tel; p->next; ) 462 p = p->next; 463 464 p->next = te; 465 te->prev = p; 466 } 467 468 /*ARGSUSED*/ 469 static SLPBoolean collect_DAs(SLPHandle h, const char *u, 470 unsigned short lifetime, 471 SLPError errCode, void *cookie) { 472 SLPSrvURL *surl = NULL; 473 char *s, *p, *sscopes, *sscopes_end, *url; 474 int coverage, proximity; 475 struct sockaddr_in sin[1]; 476 struct target_list *tl = (struct target_list *)cookie; 477 478 if (errCode != SLP_OK) 479 return (SLP_TRUE); 480 481 /* dup url so as not to corrupt da cache */ 482 if (!(url = strdup(u))) { 483 slp_err(LOG_CRIT, 0, "collect_DAs", "out of memory"); 484 return (SLP_FALSE); 485 } 486 487 /* parse url into a SLPSrvURL struct */ 488 if (SLPParseSrvURL(url, &surl) != SLP_OK) { 489 return (SLP_TRUE); /* bad URL; skip it */ 490 } 491 492 /* determine proximity */ 493 if (slp_surl2sin(surl, sin) != SLP_OK) { 494 goto cleanup; 495 } 496 if (slp_on_localhost(h, sin->sin_addr)) { 497 proximity = SLP_LOCAL_PROX; 498 } else if (slp_on_subnet(h, sin->sin_addr)) { 499 proximity = SLP_SUBNET_PROX; 500 } else { 501 proximity = SLP_REMOTE_PROX; 502 } 503 504 /* 505 * sort the DAs into the entry list, ranked by the number of 506 * relevant scopes they serve (coverage). 507 */ 508 coverage = 0; 509 if (!(sscopes = slp_utf_strchr(surl->s_pcSrvPart, '='))) { 510 /* URL part should be of the form 'scopes=...' */ 511 goto cleanup; 512 } 513 sscopes++; 514 515 /* cut off host scope at end */ 516 if (sscopes_end = slp_utf_strchr(sscopes, '=')) { 517 /* skip the =[hostname] at the end */ 518 *sscopes_end = 0; 519 } 520 521 /* copy out the scopes part, since url will be freed after this call */ 522 if (!(sscopes = strdup(sscopes))) { 523 slp_err(LOG_CRIT, 0, "collect_DAs", "out of memory"); 524 free(surl); 525 return (SLP_FALSE); 526 } 527 528 for (s = tl->all_scopes; s; s = p) { 529 p = slp_utf_strchr(s, ','); 530 if (p) 531 *p = 0; 532 if (slp_onlist(s, sscopes)) { 533 /* add to uc list; remove from mc list */ 534 slp_add2list(s, &(tl->uc_scopes), SLP_TRUE); 535 slp_list_subtract(s, &(tl->mc_scopes)); 536 coverage++; 537 } 538 if (p) 539 *p++ = ','; 540 } 541 if (coverage) 542 add_da_entry(&(tl->DAs), sin, sscopes, proximity, coverage); 543 544 cleanup: 545 free(url); 546 if (surl) free(surl); 547 548 return (SLP_TRUE); 549 } 550 551 /* 552 * Takes a scopes list of the form 's1,s2,s3,...' and formats it into 553 * an LDAP search filter of the form '(|(SCOPETAG=s1)(SCOPETAG=s2)...)'. 554 * 'scopes' contains the scopes list; 'q' is a buffer allocated 555 * by the caller into which the result will be placed. 556 */ 557 static void format_query(char *q, const char *scopes) { 558 char *p, *s; 559 int more_than_one = slp_utf_strchr(scopes, ',') ? 1 : 0; 560 561 *q++ = '('; *q++ = '&'; 562 if (more_than_one) { 563 *q++ = '('; *q++ = '|'; 564 } 565 566 for (p = s = (char *)scopes; p; s = p) { 567 *q++ = '('; 568 (void) strcpy(q, SLP_SUN_SCOPES_TAG); 569 q += strlen(SLP_SUN_SCOPES_TAG); 570 *q++ = '='; 571 572 p = slp_utf_strchr(s, ','); 573 if (p) { 574 (void) memcpy(q, s, p - s); 575 q += (p - s); 576 p++; 577 } else { 578 (void) strcpy(q, s); 579 q += strlen(s); 580 } 581 *q++ = ')'; 582 } 583 584 if (more_than_one) { 585 *q++ = ')'; 586 } 587 *q++ = '('; 588 (void) strcpy(q, SLP_SUN_VERSION_TAG); 589 q += strlen(SLP_SUN_VERSION_TAG); 590 *q++ = '='; 591 *q++ = '2'; 592 *q++ = ')'; 593 *q++ = ')'; 594 *q = 0; 595 } 596