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