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