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
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.
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]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
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
39 * unicast failovers --->
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
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.
58 * A given DA is called no more than once, and failed DAs are skipped
59 * after they have been marked 'failed'.
61 * All access to a target list is done through the following functions
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.
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
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.
103 #include <arpa/inet.h>
104 #include <slp-internal.h>
105 #include <slp_net_utils.h>
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 */
114 struct sockaddr_in sin
;
116 SLPBoolean used
, failed
;
118 slp_net_prox proximity
;
119 struct da_node
*next
, *prev
;
122 struct scope_targets
{
124 struct scope_targets
*next
;
128 struct scope_targets
**scopes
;
129 struct scope_targets
**state
;
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 *);
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
;
150 void *collator
= NULL
;
152 /* count the number of scopes in the list */
154 for (p
= (char *)scopes
; p
; p
++) {
155 p
= slp_utf_strchr(p
, ',');
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
);
168 if (!(tl
->scopes
= calloc(scope_cnt
+ 1, sizeof (*(tl
->scopes
))))) {
169 slp_err(LOG_CRIT
, 0, "slp_new_target_list", "out of memory");
171 return (SLP_MEMORY_ALLOC_FAILED
);
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
);
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
);
187 if (hp
->force_multicast
) {
188 /* all scopes remain multicast scopes; useful for SAAdverts */
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 +
199 (strlen(SLP_SUN_SCOPES_TAG
) +
200 strlen("(=)")))))) { /* (scopes=) */
201 slp_err(LOG_CRIT
, 0, "slp_new_target_list", "out of memory");
203 free(tl
->all_scopes
);
206 return (SLP_MEMORY_ALLOC_FAILED
);
208 format_query(query
, scopes
);
210 if ((err
= slp_find_das(query
, &reply
)) != SLP_OK
&&
211 err
!= SLP_NETWORK_ERROR
) {
213 free(tl
->all_scopes
);
221 /* Unpack the reply */
223 int numResults
= 0; /* placeholder; not actually used */
224 /* tag call as internal */
225 hp
->internal_call
= SLP_TRUE
;
227 (void) slp_unpackSrvReply(hp
, reply
, collect_DAs
,
228 tl
, &collator
, &numResults
);
230 /* invoke last call */
231 (void) slp_unpackSrvReply(hp
, NULL
, collect_DAs
,
232 tl
, &collator
, &numResults
);
234 /* revert internal call tag */
235 hp
->internal_call
= SLP_FALSE
;
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
243 for (te
= tl
->DAs
; te
; te
= te
->next
)
244 add2scopes_list(te
, tl
);
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
);
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
);
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
;
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
)) {
276 /* else nothing more we can do */
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
) {
286 return (NULL
); /* already did this scope */
287 if (!p
->da
->used
&& !p
->da
->failed
)
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
);
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
;
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
;
308 slp_target_t
*slp_fabricate_target(void *s
) {
310 struct scope_targets
*st
;
311 struct sockaddr_in
*sin
= (struct sockaddr_in
*)s
;
313 if (!(st
= malloc(sizeof (*st
)))) {
314 slp_err(LOG_CRIT
, 0, "slp_fabricate_target", "out of memory");
317 if (!(dn
= malloc(sizeof (*dn
)))) {
319 slp_err(LOG_CRIT
, 0, "slp_fabricate_target", "out of memory");
322 (void) memcpy(&(dn
->sin
), sin
, sizeof (dn
->sin
));
323 dn
->used
= dn
->failed
= SLP_FALSE
;
325 dn
->proximity
= SLP_REMOTE_PROX
;
326 dn
->next
= dn
->prev
= NULL
;
334 void slp_free_target(slp_target_t
*target
) {
335 struct scope_targets
*t
= (struct scope_targets
*)target
;
342 void slp_destroy_target_list(slp_target_list_t
*h
) {
343 struct da_node
*das
, *dap
;
345 struct target_list
*tl
= (struct target_list
*)h
;
347 /* free da node list */
348 for (das
= tl
->DAs
; das
; das
= dap
) {
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
) {
363 /* free scope array */
366 /* free any char * lists in use */
371 free(tl
->all_scopes
);
373 /* free the target list struct */
377 static void add2scopes_list(struct da_node
*te
, struct target_list
*tl
) {
378 struct scope_targets
**scopes
= tl
->scopes
;
383 * for each scope in tl->uc_scopes:
384 * add this DA if it serves the scope.
387 for (s
= tl
->uc_scopes
; s
; s
= p
) {
388 p
= slp_utf_strchr(s
, ',');
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",
401 /* find the end of the target list */
402 for (stp
= scopes
[i
]; stp
&& stp
->next
; ) {
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
;
420 if (!(te
= malloc(sizeof (*te
)))) {
421 slp_err(LOG_CRIT
, 0, "add_da_entry", "out of memory");
426 te
->proximity
= proximity
;
427 (void) memcpy(&(te
->sin
), sin
, sizeof (te
->sin
));
428 te
->used
= SLP_FALSE
;
429 te
->failed
= SLP_FALSE
;
433 /* find its place in the list */
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
; )
454 /* we're at the head */
460 /* didn't find a place in the list, so add it at the end */
461 for (p
= *tel
; p
->next
; )
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
;
478 if (errCode
!= SLP_OK
)
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");
487 /* parse url into a SLPSrvURL struct */
488 if (SLPParseSrvURL(url
, &surl
) != SLP_OK
) {
489 return (SLP_TRUE
); /* bad URL; skip it */
492 /* determine proximity */
493 if (slp_surl2sin(surl
, sin
) != SLP_OK
) {
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
;
501 proximity
= SLP_REMOTE_PROX
;
505 * sort the DAs into the entry list, ranked by the number of
506 * relevant scopes they serve (coverage).
509 if (!(sscopes
= slp_utf_strchr(surl
->s_pcSrvPart
, '='))) {
510 /* URL part should be of the form 'scopes=...' */
515 /* cut off host scope at end */
516 if (sscopes_end
= slp_utf_strchr(sscopes
, '=')) {
517 /* skip the =[hostname] at the end */
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");
528 for (s
= tl
->all_scopes
; s
; s
= p
) {
529 p
= slp_utf_strchr(s
, ',');
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
));
542 add_da_entry(&(tl
->DAs
), sin
, sscopes
, proximity
, coverage
);
546 if (surl
) free(surl
);
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.
557 static void format_query(char *q
, const char *scopes
) {
559 int more_than_one
= slp_utf_strchr(scopes
, ',') ? 1 : 0;
561 *q
++ = '('; *q
++ = '&';
563 *q
++ = '('; *q
++ = '|';
566 for (p
= s
= (char *)scopes
; p
; s
= p
) {
568 (void) strcpy(q
, SLP_SUN_SCOPES_TAG
);
569 q
+= strlen(SLP_SUN_SCOPES_TAG
);
572 p
= slp_utf_strchr(s
, ',');
574 (void) memcpy(q
, s
, p
- s
);
588 (void) strcpy(q
, SLP_SUN_VERSION_TAG
);
589 q
+= strlen(SLP_SUN_VERSION_TAG
);