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
slp_new_target_list(slp_handle_impl_t * hp,const char * scopes,slp_target_list_t ** handle)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
slp_get_uc_scopes(slp_target_list_t * h)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
slp_get_mc_scopes(slp_target_list_t * h)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
slp_next_uc_target(slp_target_list_t * h)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
slp_next_failover(slp_target_t * h)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
slp_get_target_sin(slp_target_t * h)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
slp_mark_target_used(slp_target_t * h)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
slp_mark_target_failed(slp_target_t * h)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
slp_fabricate_target(void * s)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
slp_free_target(slp_target_t * target)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
slp_destroy_target_list(slp_target_list_t * h)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
add2scopes_list(struct da_node * te,struct target_list * tl)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
add_da_entry(struct da_node ** tel,struct sockaddr_in * sin,char * scopes,slp_net_prox proximity,int c)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*/
collect_DAs(SLPHandle h,const char * u,unsigned short lifetime,SLPError errCode,void * cookie)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 */
format_query(char * q,const char * scopes)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