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 * Public utilities and convenience calls (from the API spec):
31 * SLPFindScopes (queries for all known scopes)
32 * SLPEscape / Unescape
33 * SLPFree
34 * SLPSet/GetProperty
35 */
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <syslog.h>
41 #include <netdb.h>
42 #include <unistd.h>
43 #include <libintl.h>
44 #include <slp-internal.h>
45
46 struct scopes_tree {
47 void *scopes;
48 int len;
49 };
50
51 typedef SLPBoolean SLPScopeCallback(SLPHandle, const char *, SLPError, void *);
52
53 static SLPSrvURLCallback collate_scopes;
54 static void collect_scopes(void *, VISIT, int, void *);
55 static SLPBoolean unpackSAAdvert_scope(slp_handle_impl_t *, char *,
56 SLPScopeCallback, void *,
57 void **, int *);
58 static SLPError SAAdvert_for_scopes(SLPHandle, void **);
59 static SLPError slp_unescape(const char *, char **, SLPBoolean, const char);
60
61 /*
62 * Finds scopes according the the user administrative model.
63 */
SLPFindScopes(SLPHandle hSLP,char ** ppcScopes)64 SLPError SLPFindScopes(SLPHandle hSLP, char **ppcScopes) {
65 SLPError err;
66 char *reply, *unesc_reply;
67 void *stree = NULL;
68 void *collator = NULL;
69
70 if (!hSLP || !ppcScopes) {
71 return (SLP_PARAMETER_BAD);
72 }
73
74 /* first try administratively configured scopes */
75 if ((err = slp_administrative_scopes(ppcScopes, SLP_FALSE))
76 != SLP_OK) {
77 return (err);
78 }
79
80 if (*ppcScopes) {
81 /* got scopes */
82 return (SLP_OK);
83 }
84
85 /* DAs from active and passive discovery */
86 if ((err = slp_find_das("", &reply)) != SLP_OK &&
87 err != SLP_NETWORK_ERROR)
88 return (err);
89
90 /* Unpack the reply */
91 if (reply) {
92 int numResults = 0; /* placeholder; not actually used */
93
94 /* tag call as internal */
95 ((slp_handle_impl_t *)hSLP)->internal_call = SLP_TRUE;
96
97 (void) slp_unpackSrvReply(
98 hSLP, reply, collate_scopes,
99 &stree, &collator, &numResults);
100 /* invoke last call */
101 (void) slp_unpackSrvReply(
102 hSLP, NULL, collate_scopes,
103 &stree, &collator, &numResults);
104 free(reply);
105
106 /* revert internal call tag */
107 ((slp_handle_impl_t *)hSLP)->internal_call = SLP_FALSE;
108 }
109
110 /* Finally, if no results yet, try SA discovery */
111 if (!stree) {
112 (void) SAAdvert_for_scopes(hSLP, &stree);
113 }
114
115 if (!stree) {
116 /* found none, so just return "default" */
117 if (!(*ppcScopes = strdup("default"))) {
118 slp_err(LOG_CRIT, 0, "SLPFindScopes", "out of memory");
119 return (SLP_MEMORY_ALLOC_FAILED);
120 }
121 return (SLP_OK);
122 }
123
124 /* we now have a btree, each leaf of which is a unique scope */
125 slp_twalk(stree, collect_scopes, 0, (void *) ppcScopes);
126
127 /* unescape scopes list */
128 if ((err = slp_unescape(*ppcScopes, &unesc_reply, SLP_FALSE, '%'))
129 == SLP_OK) {
130 free(*ppcScopes);
131 *ppcScopes = unesc_reply;
132 } else {
133 free(unesc_reply);
134 }
135
136 return (err);
137 }
138
139 /*
140 * Finds scopes according to the adminstrative scoping model. A
141 * comma-seperated list of scopes is returned in *ppcScopes; the
142 * caller must free *ppcScopes.
143 * If the return_default parameter is true, and no scopes are found,
144 * *ppcScopes will be set to 'default', otherwise, *ppcScopes will
145 * be NULL. This helps simplify internal memory management.
146 */
slp_administrative_scopes(char ** ppcScopes,SLPBoolean return_default)147 SLPError slp_administrative_scopes(char **ppcScopes,
148 SLPBoolean return_default) {
149 const char *useScopes;
150
151 *ppcScopes = NULL;
152
153 /* @@@ first try DHCP */
154 /* next try the useScopes property */
155 useScopes = SLPGetProperty(SLP_CONFIG_USESCOPES);
156
157 if (useScopes && *useScopes) {
158 if (!(*ppcScopes = strdup(useScopes))) {
159 slp_err(LOG_CRIT, 0, "SLPFindScopes", "out of memory");
160 return (SLP_MEMORY_ALLOC_FAILED);
161 }
162 return (SLP_OK);
163 }
164
165 /* found none, so just return "default" */
166 if (return_default && !(*ppcScopes = strdup("default"))) {
167 slp_err(LOG_CRIT, 0, "SLPFindScopes", "out of memory");
168 return (SLP_MEMORY_ALLOC_FAILED);
169 }
170 return (SLP_OK);
171 }
172
173 /*
174 * This function operates on the same btree as the collate_scopes().
175 * The difference is that this one is called for each
176 * SAAdvert recieved.
177 */
178 /* ARGSUSED */
saadvert_callback(SLPHandle hp,char * scopes,SLPError err,void ** stree)179 static SLPBoolean saadvert_callback(SLPHandle hp, char *scopes,
180 SLPError err, void **stree) {
181 char *s, *tstate;
182
183 if (err != SLP_OK) {
184 return (SLP_TRUE);
185 }
186
187 for (
188 s = strtok_r((char *)scopes, ",", &tstate);
189 s;
190 s = strtok_r(NULL, ",", &tstate)) {
191
192 char *ascope, **srch;
193
194 if (!(ascope = strdup(s))) { /* no memory! */
195 slp_err(LOG_CRIT, 0, "collate_scopes",
196 "out of memory");
197 return (SLP_TRUE);
198 }
199
200 srch = slp_tsearch(
201 (void *) ascope, stree,
202 (int (*)(const void *, const void *)) slp_strcasecmp);
203 if (*srch != ascope)
204 /* scope is already in there, so just free ascope */
205 free(ascope);
206 }
207
208 return (SLP_TRUE);
209 }
210
211 /*
212 * Generates an SAAdvert solicitation, and returns any scopes found
213 * from all recieved SAAdverts in stree. stree must be a btree
214 * structure.
215 */
SAAdvert_for_scopes(SLPHandle hSLP,void ** stree)216 static SLPError SAAdvert_for_scopes(SLPHandle hSLP, void **stree) {
217 SLPError err;
218 SLPBoolean sync_state;
219 slp_handle_impl_t *hp = (slp_handle_impl_t *)hSLP;
220 char *predicate;
221 const char *type_hint;
222
223 /* get type hint, if set */
224 if ((type_hint = SLPGetProperty(SLP_CONFIG_TYPEHINT)) != NULL &&
225 *type_hint != 0) {
226
227 size_t hintlen = strlen(type_hint);
228 size_t predlen = strlen("(service-type=)");
229
230 /* check bounds */
231 if (hintlen > (SLP_MAX_STRINGLEN - predlen)) {
232 return (SLP_PARAMETER_BAD);
233 }
234 if (!(predicate = malloc(hintlen + predlen + 1))) {
235 slp_err(LOG_CRIT, 0, "SAAdvert_for_scopes",
236 "out of memory");
237 return (SLP_MEMORY_ALLOC_FAILED);
238 }
239 (void) strcpy(predicate, "(service-type=");
240 (void) strcat(predicate, type_hint);
241 (void) strcat(predicate, ")");
242 } else {
243 predicate = "";
244 type_hint = NULL;
245 }
246
247 /* No callback for SLPFindScopes, so force synchronous mode only */
248 sync_state = hp->async;
249 hp->async = SLP_FALSE;
250
251 if ((err = slp_start_call(hp)) != SLP_OK)
252 return (err);
253
254 err = slp_packSrvRqst("service:service-agent", predicate, hp);
255
256 if (err == SLP_OK)
257 err = slp_ua_common(hSLP, "",
258 (SLPGenericAppCB *)saadvert_callback,
259 stree,
260 (SLPMsgReplyCB *)unpackSAAdvert_scope);
261
262 if (type_hint) {
263 free(predicate);
264 }
265
266 if (err != SLP_OK)
267 slp_end_call(hp);
268
269 /* restore sync state */
270 hp->async = sync_state;
271
272 return (err);
273 }
274
275 /*
276 * Unpack an SAAdvert and pass each set of scopes into cb.
277 */
278 /* ARGSUSED */
unpackSAAdvert_scope(slp_handle_impl_t * hSLP,char * reply,SLPScopeCallback cb,void * cookie,void ** collator,int * numResults)279 static SLPBoolean unpackSAAdvert_scope(slp_handle_impl_t *hSLP, char *reply,
280 SLPScopeCallback cb, void *cookie,
281 void **collator, int *numResults) {
282 char *surl, *scopes, *attrs;
283 SLPBoolean cont;
284
285 if (!reply) {
286 cb(hSLP, NULL, SLP_LAST_CALL, cookie);
287 return (SLP_FALSE);
288 }
289
290 /* tag call as internal; gets all scopes, regardless of maxResults */
291 hSLP->internal_call = SLP_TRUE;
292
293 if (slp_unpackSAAdvert(reply, &surl, &scopes, &attrs) != SLP_OK) {
294 return (SLP_TRUE);
295 }
296
297 cont = cb(hSLP, scopes, SLP_OK, cookie);
298
299 /* revert internal_call tag */
300 hSLP->internal_call = SLP_FALSE;
301
302 free(surl);
303 free(scopes);
304 free(attrs);
305
306 return (cont);
307 }
308
309 /*
310 * Creates a service request for finding DAs or SAs (based on 'filter'),
311 * and sends it to slpd, returning the reply in 'reply'.
312 */
slp_find_das(const char * filter,char ** reply)313 SLPError slp_find_das(const char *filter, char **reply) {
314 SLPError err;
315 char *msg, hostname[MAXHOSTNAMELEN];
316
317 /* Try the cache first */
318 if (*reply = slp_find_das_cached(filter)) {
319 return (SLP_OK);
320 }
321
322 /*
323 * create a find scopes message:
324 * this is a SrvRqst for the type directory-agent.sun.
325 */
326 /* use the local host's name for the scope */
327 (void) gethostname(hostname, MAXHOSTNAMELEN);
328
329 err = slp_packSrvRqst_single(
330 SLP_SUN_DA_TYPE, hostname, filter, &msg, "en");
331
332 if (err == SLP_OK) {
333 err = slp_send2slpd(msg, reply);
334 free(msg);
335 }
336
337 /* Add the reply to the cache */
338 if (err == SLP_OK) {
339 slp_put_das_cached(filter, *reply, slp_get_length(*reply));
340 }
341
342 return (err);
343 }
344
345 /*
346 * This is called for each URL entry in the DA service reply (sun private).
347 * Contained within the cookie is a btree, to which it adds new
348 * scopes from the URL entry. The scopes are retrieved from the btree
349 * by traversing the tree in SLPFindScopes().
350 * SLPHandle h is NULL, so don't touch it!
351 */
352 /*ARGSUSED*/
collate_scopes(SLPHandle h,const char * u,unsigned short lifetime,SLPError errCode,void * cookie)353 static SLPBoolean collate_scopes(SLPHandle h, const char *u,
354 unsigned short lifetime,
355 SLPError errCode, void *cookie) {
356 SLPSrvURL *surl;
357 char *s, *tstate, *p, *url;
358 void **collator = cookie;
359
360 if (errCode != SLP_OK)
361 return (SLP_TRUE);
362
363 /* dup url so as not to corrupt da cache */
364 if (!(url = strdup(u))) {
365 slp_err(LOG_CRIT, 0, "collate_scopes", "out of memory");
366 return (SLP_FALSE);
367 }
368
369 /* parse url into a SLPSrvURL struct */
370 if (SLPParseSrvURL(url, &surl) != SLP_OK)
371 return (SLP_TRUE); /* bad URL; skip it */
372
373 /* collate the scopes using the btree stree->scopes: */
374 /* skip the 'scopes=' part */
375 if (!(p = strchr(surl->s_pcSrvPart, '='))) {
376 free(surl);
377 return (SLP_TRUE); /* bad URL; skip it */
378 }
379 p++;
380
381 for (
382 s = strtok_r(p, ",", &tstate);
383 s;
384 s = strtok_r(NULL, ",", &tstate)) {
385
386 char *ascope, **srch;
387
388 if (!(ascope = strdup(s))) { /* no memory! */
389 slp_err(LOG_CRIT, 0, "collate_scopes",
390 "out of memory");
391 free(surl);
392 return (SLP_TRUE);
393 }
394
395 srch = slp_tsearch(
396 (void *) ascope, collator,
397 (int (*)(const void *, const void *)) slp_strcasecmp);
398 if (*srch != ascope)
399 /* scope is already in there, so just free ascope */
400 free(ascope);
401 }
402
403 free(url);
404 free(surl);
405
406 return (SLP_TRUE);
407 }
408
409 /*
410 * Each time we visit a node for the last time, copy that scope into
411 * the scope collection and free the scope string and the node.
412 */
413 /*ARGSUSED*/
collect_scopes(void * node,VISIT order,int level,void * cookie)414 static void collect_scopes(void *node, VISIT order, int level, void *cookie) {
415 char **scopes = (char **)cookie;
416
417 if (order == endorder || order == leaf) {
418 char *s = *(char **)node;
419 slp_add2list(s, scopes, SLP_FALSE);
420 free(s);
421 free(node);
422 }
423 }
424
SLPFree(void * pvMem)425 void SLPFree(void *pvMem) {
426 if (pvMem)
427 free(pvMem);
428 }
429
430 /*
431 * Escape / Unescape
432 */
433
434 #define isBadTagChar(c) ((c) == '*' || (c) == '_' || \
435 (c) == '\n' || (c) == '\t' || (c) == '\r')
436
437 #define isReserved(c) ((c) <= 31 || (c) == '(' || (c) == ')' || \
438 (c) == ',' || (c) == '\\' || (c) == '!' || \
439 (c) == '<' || (c) == '=' || (c) == '>' || \
440 (c) == '~')
441
SLPEscape(const char * pcInbuf,char ** ppcOutBuf,SLPBoolean isTag)442 SLPError SLPEscape(const char *pcInbuf, char **ppcOutBuf, SLPBoolean isTag) {
443 char *buf, *pin, *pout;
444
445 if (!pcInbuf || !ppcOutBuf)
446 return (SLP_PARAMETER_BAD);
447
448 if (!(buf = malloc(strlen(pcInbuf) * 3 + 1))) {
449 slp_err(LOG_CRIT, 0, "SLPEscape", "out of memory");
450 return (SLP_MEMORY_ALLOC_FAILED);
451 }
452 *ppcOutBuf = buf;
453
454 for (pin = (char *)pcInbuf, pout = buf; *pin; ) {
455 int len;
456
457 /* If char is start of multibyte char, just copy it in */
458 if ((len = mblen(pin, MB_CUR_MAX)) > 1) {
459 int i;
460 for (i = 0; i < len && *pin; i++)
461 *pout++ = *pin++;
462 continue;
463 }
464
465 /* check for bad tag */
466 if (isTag && isBadTagChar(*pin))
467 return (SLP_PARSE_ERROR);
468
469 if (isReserved(*pin)) {
470 if (isTag)
471 return (SLP_PARSE_ERROR);
472 (void) sprintf(pout, "\\%.2x", *pin);
473 pout += 3;
474 pin++;
475 } else {
476 *pout++ = *pin++;
477 }
478 }
479 *pout = 0;
480
481 return (SLP_OK);
482 }
483
SLPUnescape(const char * pcInbuf,char ** ppcOutBuf,SLPBoolean isTag)484 SLPError SLPUnescape(const char *pcInbuf, char **ppcOutBuf, SLPBoolean isTag) {
485 if (!pcInbuf || !ppcOutBuf)
486 return (SLP_PARAMETER_BAD);
487
488 return (slp_unescape(pcInbuf, ppcOutBuf, isTag, '\\'));
489 }
490
491
492 /*
493 * The actual unescaping routine; allows for different escape chars.
494 */
slp_unescape(const char * pcInbuf,char ** ppcOutBuf,SLPBoolean isTag,const char esc_char)495 static SLPError slp_unescape(const char *pcInbuf, char **ppcOutBuf,
496 SLPBoolean isTag, const char esc_char) {
497 char *buf, *pin, *pout, conv[3];
498
499 if (!(buf = malloc(strlen(pcInbuf) * 3 + 1))) {
500 slp_err(LOG_CRIT, 0, "SLPEscape", "out of memory");
501 return (SLP_MEMORY_ALLOC_FAILED);
502 }
503 *ppcOutBuf = buf;
504
505 conv[2] = 0;
506 for (pin = (char *)pcInbuf, pout = buf; *pin; ) {
507 int len;
508
509 /* If char is start of multibyte char, just copy it in */
510 if ((len = mblen(pin, MB_CUR_MAX)) > 1) {
511 int i;
512 for (i = 0; i < len && *pin; i++)
513 *pout++ = *pin++;
514 continue;
515 }
516
517 if (*pin == esc_char) {
518 if (!pin[1] || !pin[2])
519 return (SLP_PARSE_ERROR);
520 pin++;
521 conv[0] = *pin++;
522 conv[1] = *pin++;
523 *pout++ = (char)strtol(conv, NULL, 16);
524 if (isTag && isBadTagChar(*pout))
525 return (SLP_PARSE_ERROR);
526 } else {
527 *pout++ = *pin++;
528 }
529 }
530 *pout = 0;
531
532 return (SLP_OK);
533 }
534
535 /*
536 * Properties
537 *
538 * All properties are stored in a global tree (prop_table). This
539 * tree is created and accessed by slp_tsearch and slp_tfind.
540 */
541 struct prop_entry {
542 const char *key, *val;
543 };
544 typedef struct prop_entry slp_prop_entry_t;
545
546 /* Global properties table */
547 static void *slp_props = NULL;
548 static mutex_t prop_table_lock = DEFAULTMUTEX;
549
550 static void setDefaults();
551
compare_props(const void * a,const void * b)552 static int compare_props(const void *a, const void *b) {
553 return (strcmp(
554 ((slp_prop_entry_t *)a)->key,
555 ((slp_prop_entry_t *)b)->key));
556 }
557
SLPSetProperty(const char * pcName,const char * pcValue)558 void SLPSetProperty(const char *pcName, const char *pcValue) {
559 slp_prop_entry_t *pe, **pe2;
560
561 if (!slp_props) setDefaults();
562
563 if (!pcName || !pcValue) {
564 return;
565 }
566
567 if (!(pe = malloc(sizeof (*pe)))) {
568 slp_err(LOG_CRIT, 0, "SLPSetProperty", "out of memory");
569 return;
570 }
571
572 /* place the strings under library ownership */
573 if (!(pe->key = strdup(pcName))) {
574 free(pe);
575 slp_err(LOG_CRIT, 0, "SLPSetProperty", "out of memory");
576 return;
577 }
578
579 if (!(pe->val = strdup(pcValue))) {
580 free((void *) pe->key);
581 free(pe);
582 slp_err(LOG_CRIT, 0, "SLPSetProperty", "out of memory");
583 return;
584 }
585
586 /* is pcName already set? */
587 (void) mutex_lock(&prop_table_lock);
588 pe2 = slp_tsearch((void *) pe, &slp_props, compare_props);
589 if (pe != *pe2) {
590 /* this prop is already set; overwrite the old value */
591 free((void *) (*pe2)->val);
592 (*pe2)->val = pe->val;
593 free((void *) pe->key);
594 free(pe);
595 }
596 (void) mutex_unlock(&prop_table_lock);
597 }
598
SLPGetProperty(const char * pcName)599 const char *SLPGetProperty(const char *pcName) {
600 slp_prop_entry_t pe[1], **ans;
601
602 if (!slp_props) setDefaults();
603
604 if (!pcName) {
605 return (NULL);
606 }
607
608 pe->key = pcName;
609
610 (void) mutex_lock(&prop_table_lock);
611 ans = slp_tfind(pe, &slp_props, compare_props);
612 (void) mutex_unlock(&prop_table_lock);
613 if (ans)
614 return ((*ans)->val);
615 return (NULL);
616 }
617
setDefaults()618 static void setDefaults() {
619 slp_prop_entry_t *pe;
620 static mutex_t lock = DEFAULTMUTEX;
621
622 (void) mutex_lock(&lock);
623 if (slp_props) {
624 (void) mutex_unlock(&lock);
625 return;
626 }
627
628 pe = malloc(sizeof (*pe));
629 pe->key = strdup(SLP_CONFIG_ISBROADCASTONLY);
630 pe->val = strdup("false");
631 (void) slp_tsearch((void *) pe, &slp_props, compare_props);
632
633 pe = malloc(sizeof (*pe));
634 pe->key = strdup(SLP_CONFIG_MULTICASTTTL);
635 pe->val = strdup("255");
636 (void) slp_tsearch((void *) pe, &slp_props, compare_props);
637
638 pe = malloc(sizeof (*pe));
639 pe->key = strdup(SLP_CONFIG_MULTICASTMAXWAIT);
640 pe->val = strdup("15000");
641 (void) slp_tsearch((void *) pe, &slp_props, compare_props);
642
643 pe = malloc(sizeof (*pe));
644 pe->key = strdup(SLP_CONFIG_DATAGRAMTIMEOUTS);
645 pe->val = strdup("2000,2000,2000");
646 (void) slp_tsearch((void *) pe, &slp_props, compare_props);
647
648 pe = malloc(sizeof (*pe));
649 pe->key = strdup(SLP_CONFIG_MULTICASTTIMEOUTS);
650 pe->val = strdup("1000,3000,3000,3000,3000");
651 (void) slp_tsearch((void *) pe, &slp_props, compare_props);
652
653 pe = malloc(sizeof (*pe));
654 pe->key = SLP_CONFIG_MTU; pe->val = "1400";
655 (void) slp_tsearch((void *) pe, &slp_props, compare_props);
656
657 pe = malloc(sizeof (*pe));
658 pe->key = strdup(SLP_CONFIG_MAXRESULTS);
659 pe->val = strdup("-1");
660 (void) slp_tsearch((void *) pe, &slp_props, compare_props);
661
662 pe = malloc(sizeof (*pe));
663 pe->key = strdup(SLP_CONFIG_SECURITY_ON);
664 pe->val = strdup("false");
665 (void) slp_tsearch((void *) pe, &slp_props, compare_props);
666
667 pe = malloc(sizeof (*pe));
668 pe->key = strdup(SLP_CONFIG_BYPASS_AUTH);
669 pe->val = strdup("false");
670 (void) slp_tsearch((void *) pe, &slp_props, compare_props);
671
672 slp_readConfig();
673
674 (void) mutex_unlock(&lock);
675 }
676
677 static const char *error_strings[] = {
678 "OK", /* 0 */
679 "Language not supported", /* -1 */
680 "Parse error", /* -2 */
681 "Invalid registration", /* -3 */
682 "Scope not supported", /* -4 */
683 "Invalid error number", /* -5 */
684 "Authentication absent", /* -6 */
685 "Authentication failed", /* -7 */
686 "Invalid error number", /* -8 */
687 "Invalid error number", /* -9 */
688 "Invalid error number", /* -10 */
689 "Invalid error number", /* -11 */
690 "Invalid error number", /* -12 */
691 "Invalid update", /* -13 */
692 "Invalid error number", /* -14 */
693 "Invalid error number", /* -15 */
694 "Invalid error number", /* -16 */
695 "Not implemented", /* -17 */
696 "Buffer overflow", /* -18 */
697 "Network timed out", /* -19 */
698 "Network init failed", /* -20 */
699 "Memory alloc failed", /* -21 */
700 "Parameter bad", /* -22 */
701 "Network error", /* -23 */
702 "Internal system error", /* -24 */
703 "Handle in use", /* -25 */
704 "Type error" /* -26 */
705 };
706
707 #define SLP_MAX_ERR_CNT 26
708
slp_strerror(SLPError err)709 const char *slp_strerror(SLPError err) {
710 int abserr;
711 const char *str;
712
713 if (err == SLP_LAST_CALL) {
714 str = "Last call";
715 } else if (err == SLP_SECURITY_UNAVAILABLE) {
716 str = "Security Implementation Unavailable";
717 } else {
718 abserr = abs(err);
719 if (abserr > SLP_MAX_ERR_CNT) {
720 str = "Invalid error number";
721 } else {
722 str = error_strings[abserr];
723 }
724 }
725
726 return (dgettext("libslp", str));
727 }
728