1 /*-------------------------------------------------------------------------
2 * Copyright (C) 2000 Caldera Systems, Inc
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of Caldera Systems nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CALDERA
24 * SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *-------------------------------------------------------------------------*/
32
33 /** Tracks Known DA's.
34 *
35 * Tracks known DA's for the user agent.
36 *
37 * @file libslp_knownda.c
38 * @author Matthew Peterson, John Calcote (jcalcote@novell.com)
39 * @attention Please submit patches to http://www.openslp.org
40 * @ingroup LibSLPCode
41 */
42
43 #include "slp.h"
44 #include "libslp.h"
45 #include "slp_dhcp.h"
46 #include "slp_net.h"
47 #include "slp_parse.h"
48 #include "slp_network.h"
49 #include "slp_database.h"
50 #include "slp_compare.h"
51 #include "slp_xmalloc.h"
52 #include "slp_property.h"
53
54 /** The cache DAAdvert messages from known DAs. */
55 static SLPDatabase G_KnownDACache = {0, 0, 0};
56
57 /** The time of the last Multicast for known DAs */
58 static time_t G_KnownDALastCacheRefresh = 0;
59
60 /** Locate a known DA matching the desired scope list and SPI.
61 *
62 * Searches the known DA list in the database for a DA that matches the
63 * specified scope list and security parameter index (SPI) value. Returns
64 * the network address for the first DA to match the specified criteria.
65 *
66 * @param[in] scopelist - The list of scopes whose DA's should be found. All
67 * DA's that support a proper subset of this scope list will be returned.
68 * @param[in] scopelistlen - The length of @p scopelist.
69 * @param[in] spistr - The Security Parameter Index value to use.
70 * @param[in] spistrlen - The length of @p spistr.
71 * @param[out] daaddr - The address of a DA matching the specified search
72 * criteria.
73 * @param[in] daaddrsz - The length in bytes of @p daaddr.
74 *
75 * @return A Bookean value; Zero if DA cannot be found, non-zero on success.
76 *
77 * @internal
78 */
KnownDAListFind(size_t scopelistlen,const char * scopelist,size_t spistrlen,const char * spistr,void * daaddr,size_t daaddrsz)79 static SLPBoolean KnownDAListFind(size_t scopelistlen, const char * scopelist,
80 size_t spistrlen, const char * spistr, void * daaddr, size_t daaddrsz)
81 {
82 int result;
83 SLPDatabaseHandle dh;
84 SLPDatabaseEntry * entry;
85
86 #ifndef ENABLE_SLPv2_SECURITY
87 (void)spistr;
88 (void)spistrlen;
89 #endif
90
91 result = SLP_FALSE;
92 if ((dh = SLPDatabaseOpen(&G_KnownDACache)) != 0)
93 {
94 /* Check to see if there a matching entry, and then check scopes. */
95 while ((entry = SLPDatabaseEnum(dh)) != 0)
96 {
97 if (SLPSubsetStringList(entry->msg->body.daadvert.scopelistlen,
98 entry->msg->body.daadvert.scopelist, scopelistlen,
99 scopelist) != 0)
100 {
101 #if defined(ENABLE_SLPv2_SECURITY)
102 if (SLPCompareString(entry->msg->body.daadvert.spilistlen,
103 entry->msg->body.daadvert.spilist, spistrlen, spistr) == 0)
104 #endif
105 {
106 memcpy(daaddr, &entry->msg->peer, daaddrsz);
107 result = SLP_TRUE;
108 break;
109 }
110 }
111 }
112 SLPDatabaseClose(dh);
113 }
114 return result;
115 }
116
117 /** Find a list of DAs that, between them, handle all the given scopes
118 *
119 * @param[in] scopelistlen - The length of @p scopelist.
120 * @param[in] scopelist - The list of scopes whose DA's should be found. Enough
121 * DA's to cover this scope list will be returned.
122 * @param[in] spistrlen - The length of @p spistr.
123 * @param[in] spistr - The Security Parameter Index value to use.
124 * @param[out] daaddrs - The addresses of the DA's that, together, match the
125 * specified search criteria. NULL if a spanning set of DA's cannot be found.
126 * The last entry in the list (not to be processed) will have an IP address of
127 * 0.0.0.0
128 *
129 * @return Zero if a spanning set of DA's cannot be found, the number of DA's
130 * in the returned list on success.
131 *
132 * @internal
133 */
KnownDASpanningListFind(int scopelistlen,const char * scopelist,size_t spistrlen,const char * spistr,struct sockaddr_in ** daaddrs)134 int KnownDASpanningListFind(int scopelistlen,
135 const char* scopelist,
136 size_t spistrlen,
137 const char* spistr,
138 struct sockaddr_in** daaddrs)
139 {
140 #define NUM_DAS_CHUNK_SIZE 10
141 SLPDatabaseHandle dh;
142 SLPDatabaseEntry * entry;
143 char* scopesleft;
144 int scopesleftlen = scopelistlen;
145 int numdas = 0;
146 int numdasallocated = 0;
147
148 struct sockaddr_in * destaddrs = 0;
149
150 /* Although we are creating a list of IPV4 socket addresses, they may be copied as if they were
151 * generic socket addresses, so pad out the list to make sure there is enough memory following
152 * the last one
153 */
154 #define DESTADDR_PADDING (sizeof (struct sockaddr_storage) - sizeof (struct sockaddr_in))
155
156 scopesleft = malloc(scopelistlen);
157 if (!scopesleft)
158 {
159 /* memory allocation failure */
160 return 0;
161 }
162 memcpy(scopesleft, scopelist, scopelistlen);
163
164 dh = SLPDatabaseOpen(&G_KnownDACache);
165 if (dh)
166 {
167 /*----------------------------------------*/
168 /* Check for matching entries */
169 /*----------------------------------------*/
170 while(scopesleftlen)
171 {
172 entry = SLPDatabaseEnum(dh);
173 if (entry == NULL)
174 break;
175
176 /* Check scopes */
177 if (SLPIntersectStringList(entry->msg->body.daadvert.scopelistlen,
178 entry->msg->body.daadvert.scopelist, scopesleftlen, scopesleft))
179 {
180 /* This DA handles at least one of the remaining scopes */
181 #ifdef ENABLE_SLPv2_SECURITY
182 if (SLPCompareString(entry->msg->body.daadvert.spilistlen,
183 entry->msg->body.daadvert.spilist, (int)spistrlen, spistr) == 0)
184 #else
185 (void) spistr; /*prevent compiler warnings about unused parameters*/
186 (void) spistrlen;
187 #endif
188 {
189 if (entry->msg->peer.ss_family == AF_INET && SLPNetIsIPV4())
190 {
191 /* Remove the DA's scopes from the remaining list of scopes */
192 (void)SLPIntersectRemoveStringList((int)entry->msg->body.daadvert.scopelistlen,
193 entry->msg->body.daadvert.scopelist, &scopesleftlen, scopesleft);
194 if (numdas >= numdasallocated)
195 {
196 struct sockaddr_in * tmp_destaddrs;
197 /* We need a bigger array of addresses */
198 numdasallocated += NUM_DAS_CHUNK_SIZE;
199 if ((tmp_destaddrs = xrealloc(destaddrs, numdasallocated * sizeof (struct sockaddr_in) + DESTADDR_PADDING)) == 0)
200 {
201 SLPDatabaseClose(dh);
202 xfree(destaddrs);
203 xfree(scopesleft);
204 return 0;
205 }
206 destaddrs = tmp_destaddrs;
207 }
208 memcpy(&destaddrs[numdas].sin_addr, &(((struct sockaddr_in *)&entry->msg->peer)->sin_addr),
209 sizeof(struct in_addr));
210 destaddrs[numdas].sin_family = PF_INET;
211 destaddrs[numdas].sin_port = htons((uint16_t)SLPPropertyAsInteger("net.slp.port"));
212 ++numdas;
213 }
214 }
215 }
216 }
217 SLPDatabaseClose(dh);
218 }
219
220 if (numdas && scopesleftlen)
221 {
222 /* some, but not all, of the requested scopes are not handled by any of the cached DAs */
223 xfree(destaddrs);
224 destaddrs = 0;
225 numdas = 0;
226 }
227 else if (numdas)
228 {
229 /* Add a terminating address entry with an IP address of 0.0.0.0 */
230 if (numdas >= numdasallocated)
231 {
232 struct sockaddr_in * tmp_destaddrs;
233 /* We need a bigger array of addresses */
234 numdasallocated += 1;
235 if ((tmp_destaddrs = xrealloc(destaddrs, numdasallocated * sizeof (struct sockaddr_in) + DESTADDR_PADDING)) == 0)
236 {
237 xfree(destaddrs);
238 xfree(scopesleft);
239 return 0;
240 }
241 destaddrs = tmp_destaddrs;
242 }
243 destaddrs[numdas].sin_addr.s_addr = 0;
244 }
245 *daaddrs = destaddrs;
246 xfree(scopesleft);
247 return numdas;
248 }
249
250 /** Add an entry to the KnownDA cache.
251 *
252 * @param[in] msg - The message containing the DA advertisement.
253 * @param[in] buf - The buffer associated with @p msg.
254 *
255 * @return Zero on success, or a non-zero value on failure.
256 *
257 * @internal
258 */
KnownDAAdd(SLPMessage * msg,SLPBuffer buf)259 static int KnownDAAdd(SLPMessage * msg, SLPBuffer buf)
260 {
261 int result = 0;
262 SLPDatabaseHandle dh = SLPDatabaseOpen(&G_KnownDACache);
263
264 if (dh)
265 {
266 SLPDatabaseEntry * entry;
267 SLPDAAdvert * daadvert = &msg->body.daadvert;
268
269 /* Check to see if there is already an identical entry. */
270 while (1)
271 {
272 SLPDAAdvert * entrydaadvert;
273
274 entry = SLPDatabaseEnum(dh);
275 if (!entry)
276 break;
277
278 /* entrydaadvert is the DAAdvert message from the database. */
279 entrydaadvert = &entry->msg->body.daadvert;
280
281 /* Assume DAs are identical if their URLs match. */
282 if (!SLPCompareString(entrydaadvert->urllen, entrydaadvert->url,
283 daadvert->urllen, daadvert->url))
284 {
285 SLPDatabaseRemove(dh, entry);
286 break;
287 }
288 }
289
290 /* Create and link in a new entry. */
291 entry = SLPDatabaseEntryCreate(msg, buf);
292 if (entry)
293 SLPDatabaseAdd(dh, entry);
294 else
295 result = SLP_MEMORY_ALLOC_FAILED;
296 SLPDatabaseClose(dh);
297 }
298 return result;
299 }
300
301 /** Callback for DA discovery algorithm.
302 *
303 * @param[in] errorcode - The error code returned by discovery.
304 * @param[in] peerinfo - The address of the remote peer.
305 * @param[in] replybuf - The reply information from the peer.
306 * @param[in] cookie - Pass through data from the original caller.
307 *
308 * @return A boolean value; True on success, False to stop caller from
309 * calling this routine again.
310 *
311 * @remarks The @p cookie parameter is the address of a count that is
312 * either updated to reflect a new entry added, or not in case of error.
313 *
314 * @internal
315 */
KnownDADiscoveryCallback(SLPError errorcode,void * peerinfo,SLPBuffer replybuf,void * cookie)316 static SLPBoolean KnownDADiscoveryCallback(SLPError errorcode,
317 void * peerinfo, SLPBuffer replybuf, void * cookie)
318 {
319 SLPBuffer dupbuf;
320 SLPMessage * replymsg;
321 SLPBoolean result = SLP_TRUE; /* Default is to continue. */
322
323 if (errorcode) /* Bad response, but do call again. */
324 return SLP_TRUE;
325
326 /* Allocate duplicate buffer and message object. */
327 dupbuf = SLPBufferDup(replybuf);
328 replymsg = SLPMessageAlloc();
329
330 if (dupbuf != 0 && replymsg != 0
331 && SLPMessageParseBuffer(peerinfo, 0, dupbuf, replymsg) == 0
332 && replymsg->header.functionid == SLP_FUNCT_DAADVERT)
333 {
334 if (replymsg->body.daadvert.errorcode == 0)
335 {
336 SLPParsedSrvUrl * srvurl;
337
338 if (SLPParseSrvUrl(replymsg->body.daadvert.urllen,
339 replymsg->body.daadvert.url, &srvurl) == 0)
340 {
341 int retval = -1;
342
343 /* Should call inet_pton with the same address family
344 * as was found in the DA url.
345 */
346 if (replymsg->peer.ss_family == AF_INET && SLPNetIsIPV4())
347 {
348 memset(&((struct sockaddr_in *)&replymsg->peer)->sin_addr, 0,
349 sizeof(struct in_addr));
350 retval = inet_pton(replymsg->peer.ss_family, srvurl->host,
351 &((struct sockaddr_in *)&replymsg->peer)->sin_addr);
352 }
353 else if (replymsg->peer.ss_family == AF_INET6 && SLPNetIsIPV6())
354 {
355 memset(&((struct sockaddr_in6 *)&replymsg->peer)->sin6_addr, 0,
356 sizeof(struct in6_addr));
357 retval = inet_pton(replymsg->peer.ss_family, srvurl->host,
358 &((struct sockaddr_in6 *)&replymsg->peer)->sin6_addr);
359 }
360 if (retval == 0)
361 {
362 struct addrinfo * he;
363 struct addrinfo hints;
364
365 hints.ai_family = replymsg->peer.ss_family;
366 getaddrinfo(srvurl->host, 0, &hints, &he);
367 if (he)
368 {
369 /* Reset the peer to the one in the URL. */
370 if (replymsg->peer.ss_family == AF_INET && SLPNetIsIPV4())
371 memcpy(&((struct sockaddr_in *)&replymsg->peer)->sin_addr,
372 &((struct sockaddr_in *)he->ai_addr)->sin_addr,
373 sizeof(struct in_addr));
374 else if (replymsg->peer.ss_family == AF_INET6 && SLPNetIsIPV6())
375 memcpy(&((struct sockaddr_in6 *)&replymsg->peer)->sin6_addr,
376 &((struct sockaddr_in6 *)he->ai_addr)->sin6_addr,
377 sizeof(struct in6_addr));
378 retval = 1;
379 freeaddrinfo(he);
380 }
381 }
382 xfree(srvurl);
383
384 if (retval > 0)
385 {
386 if (KnownDAAdd(replymsg, dupbuf) == 0)
387 {
388 /* Increment number of entries processed so far. */
389 (*(int *)cookie)++;
390 return SLP_TRUE;
391 /* return (replymsg->header.flags & SLP_FLAG_MCAST)?
392 SLP_FALSE: SLP_TRUE; */
393 }
394 }
395 }
396 }
397 else if (replymsg->body.daadvert.errorcode == SLP_ERROR_INTERNAL_ERROR)
398 result = SLP_FALSE; /* "end of stream" for loopback IPC. */
399 }
400 SLPMessageFree(replymsg);
401 SLPBufferFree(dupbuf);
402 return result;
403 }
404
405 /** Format a Service Request for DA services and send on a socket.
406 *
407 * @param[in] sock - A socket connected to a server that can respond to
408 * A DA SrvRequest.
409 * @param[in] peeraddr - The address connected to on @p sock.
410 * @param[in] scopelistlen - The length of @p scopelist in bytes.
411 * @param[in] scopelist - The DA's returned must support these scopes.
412 * @param[in] handle - The OpenSLP handle on which this request was made.
413 *
414 * @return The number of *new* DAEntries found.
415 *
416 * @internal
417 */
KnownDADiscoveryRqstRply(sockfd_t sock,void * peeraddr,size_t scopelistlen,const char * scopelist,SLPHandleInfo * handle)418 static int KnownDADiscoveryRqstRply(sockfd_t sock,
419 void * peeraddr, size_t scopelistlen,
420 const char * scopelist, SLPHandleInfo * handle)
421 {
422 uint8_t * buf;
423 uint8_t * cur;
424 int result = 0;
425
426 /* 0 1 2 3
427 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
428 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
429 | length of <service-type> | <service-type> String \
430 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
431 | length of <scope-list> | <scope-list> String \
432 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
433 | length of predicate string | Service Request <predicate> \
434 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
435 | length of <SLP SPI> string | <SLP SPI> String \
436 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
437
438 #define SLP_DA_SERVICE_TYPE_LEN (sizeof(SLP_DA_SERVICE_TYPE) - 1)
439
440 /** @todo Make sure that we don't exceed the MTU. */
441 buf = cur = xmalloc(
442 + 2 + SLP_DA_SERVICE_TYPE_LEN
443 + 2 + scopelistlen
444 + 2 + 0
445 + 2 + 0);
446 if (buf == 0)
447 return 0;
448
449 /* <service-type> */
450 PutUINT16(&cur, SLP_DA_SERVICE_TYPE_LEN);
451 memcpy(cur, SLP_DA_SERVICE_TYPE, SLP_DA_SERVICE_TYPE_LEN);
452 cur += SLP_DA_SERVICE_TYPE_LEN;
453
454 /* <scope-list> */
455 PutUINT16(&cur, scopelistlen);
456 memcpy(cur, scopelist, scopelistlen);
457 cur += scopelistlen;
458
459 /* <predicate> */
460 PutUINT16(&cur, 0);
461
462 /* <SLP SPI> */
463 PutUINT16(&cur, 0);
464
465 if (sock == SLP_INVALID_SOCKET)
466 NetworkMcastRqstRply(handle, buf, SLP_FUNCT_DASRVRQST,
467 cur - buf, KnownDADiscoveryCallback, &result, false);
468 else
469 NetworkRqstRply(sock, peeraddr, "en", 0, buf, SLP_FUNCT_DASRVRQST,
470 cur - buf, KnownDADiscoveryCallback, &result, false);
471
472 xfree(buf);
473 return result;
474 }
475
476 /** Locates DAs via multicast convergence.
477 *
478 * @param[in] scopelistlen - The length of @p scopelist.
479 * @param[in] scopelist - A list of scopes that must be supported.
480 * @param[in] handle - The SLP handle associated with this request.
481 *
482 * @return The number of *new* DAs found.
483 *
484 * @internal
485 */
KnownDADiscoverFromMulticast(size_t scopelistlen,const char * scopelist,SLPHandleInfo * handle)486 static int KnownDADiscoverFromMulticast(size_t scopelistlen,
487 const char * scopelist, SLPHandleInfo * handle)
488 {
489 int result = 0;
490
491 if (SLPPropertyAsBoolean("net.slp.activeDADetection")
492 && SLPPropertyAsInteger("net.slp.DADiscoveryMaximumWait"))
493 result = KnownDADiscoveryRqstRply(SLP_INVALID_SOCKET, 0, scopelistlen,
494 scopelist, handle);
495
496 return result;
497 }
498
499 /** Locates DAs via DHCP.
500 *
501 * @param[in] handle - The SLP handle associated with this request.
502 *
503 * @return The number of *new* DAs found via DHCP.
504 *
505 * @internal
506 */
KnownDADiscoverFromDHCP(SLPHandleInfo * handle)507 static int KnownDADiscoverFromDHCP(SLPHandleInfo * handle)
508 {
509 int count = 0;
510 size_t scopelistlen;
511 DHCPContext ctx;
512 uint8_t * alp;
513 struct sockaddr_storage peeraddr;
514 unsigned char dhcpOpts[] = {TAG_SLP_SCOPE, TAG_SLP_DA};
515
516 /* Only do DHCP discovery if IPv4 is enabled. */
517 if (!SLPNetIsIPV4())
518 return 0;
519
520 *ctx.scopelist = 0;
521 ctx.addrlistlen = 0;
522
523 DHCPGetOptionInfo(dhcpOpts, sizeof(dhcpOpts), DHCPParseSLPTags, &ctx);
524
525 if (!*ctx.scopelist)
526 {
527 const char * useScopes = SLPPropertyGet("net.slp.useScopes", 0, 0);
528 if (useScopes)
529 strcpy(ctx.scopelist, useScopes);
530 }
531 scopelistlen = strlen(ctx.scopelist);
532
533 SLPNetSetAddr(&peeraddr, AF_INET, (uint16_t)SLPPropertyAsInteger("net.slp.port"), 0);
534
535 alp = ctx.addrlist;
536
537 while (ctx.addrlistlen >= 4)
538 {
539 memcpy(&((struct sockaddr_in *)&peeraddr)->sin_addr.s_addr, alp, 4);
540 if (((struct sockaddr_in *)&peeraddr)->sin_addr.s_addr)
541 {
542 sockfd_t sockfd;
543 if ((sockfd = SLPNetworkCreateDatagram(peeraddr.ss_family)) != SLP_INVALID_SOCKET)
544 {
545 count = KnownDADiscoveryRqstRply(sockfd, &peeraddr,
546 scopelistlen, ctx.scopelist, handle);
547 closesocket(sockfd);
548 if (scopelistlen && count)
549 break; /* stop after the first set found */
550 }
551 }
552 ctx.addrlistlen -= 4;
553 alp += 4;
554 }
555 return count;
556 }
557
558 /** Locates DAs from the property list of DA hostnames.
559 *
560 * @param[in] scopelistlen - The length of @p scopelist.
561 * @param[in] scopelist - The list of scopes that must be supported.
562 * @param[in] handle - The SLP handle associated with this request.
563 *
564 * @return The number of *new* DAs found.
565 *
566 * @internal
567 */
KnownDADiscoverFromProperties(size_t scopelistlen,const char * scopelist,SLPHandleInfo * handle)568 static int KnownDADiscoverFromProperties(size_t scopelistlen,
569 const char * scopelist, SLPHandleInfo * handle)
570 {
571 char * temp;
572 char * slider1;
573 char * slider2;
574 int result = 0;
575
576 slider1 = slider2 = temp = SLPPropertyXDup("net.slp.DAAddresses");
577 if (temp)
578 {
579 char * tempend = temp + strlen(temp);
580 while (slider1 != tempend)
581 {
582 struct sockaddr_storage peeraddr;
583
584 while (*slider2 && *slider2 != ',')
585 slider2++;
586 *slider2 = 0;
587
588 if (SLPNetResolveHostToAddr(slider1, &peeraddr) == 0)
589 {
590 sockfd_t sockfd;
591
592 SLPNetSetParams(&peeraddr, peeraddr.ss_family, (uint16_t)SLPPropertyAsInteger("net.slp.port"));
593 sockfd = SLPNetworkCreateDatagram(peeraddr.ss_family);
594 if (sockfd != SLP_INVALID_SOCKET)
595 {
596 result = KnownDADiscoveryRqstRply(sockfd, &peeraddr,
597 scopelistlen, scopelist, handle);
598 closesocket(sockfd);
599 if (scopelistlen && result)
600 break; /* return if we found at least one DA */
601 }
602 }
603 slider1 = slider2;
604 slider2++;
605 }
606 xfree(temp);
607 }
608 return result;
609 }
610
611 /** Asks slpd if it knows about a DA.
612 *
613 * @param[in] handle - The SLP handle associated with this request.
614 *
615 * @return The number of *new* DAs found.
616 *
617 * @internal
618 */
KnownDADiscoverFromIPC(SLPHandleInfo * handle)619 static int KnownDADiscoverFromIPC(SLPHandleInfo * handle)
620 {
621 int result = 0;
622 struct sockaddr_storage peeraddr;
623
624 sockfd_t sockfd = NetworkConnectToSlpd(&peeraddr);
625
626 /* First clear the database out so we don't hang on to stale DAs */
627 SLPDatabaseHandle dh = SLPDatabaseOpen(&G_KnownDACache);
628 if (dh)
629 {
630 while (1)
631 {
632 SLPDatabaseEntry * entry = SLPDatabaseEnum(dh);
633 if (!entry)
634 break;
635 SLPDatabaseRemove(dh,entry);
636 }
637 SLPDatabaseClose(dh);
638 }
639
640 if (sockfd != SLP_INVALID_SOCKET)
641 {
642 /* Now we can re-populate the database */
643 result = KnownDADiscoveryRqstRply(sockfd, &peeraddr, 0, "", handle);
644 closesocket(sockfd);
645 }
646 return result;
647 }
648
649 /** Asks slpd about the DA's it knows about.
650 *
651 * @param[in] scopelistlen - The length of @p scopelist.
652 * @param[in] scopelist - The list of scopes that must be supported.
653 * @param[in] handle - The SLP handle associated with this request.
654 *
655 * @return Non-zero on success, zero if DA can not be found.
656 *
657 * @internal
658 */
KnownDARefreshCache(int scopelistlen,const char * scopelist,SLPHandleInfo * handle)659 SLPBoolean KnownDARefreshCache(int scopelistlen,
660 const char* scopelist,
661 SLPHandleInfo * handle)
662 /* Refresh the DA Cache if it's time */
663 /* */
664 /* Returns: SLP_TRUE if a refresh was performed, SLP_FALSE if not */
665 /*-------------------------------------------------------------------------*/
666 {
667 time_t curtime;
668
669 curtime = time(&curtime);
670 if(G_KnownDALastCacheRefresh == 0 ||
671 curtime - G_KnownDALastCacheRefresh > MINIMUM_DISCOVERY_INTERVAL)
672 {
673 G_KnownDALastCacheRefresh = curtime;
674
675 /* discover DAs */
676 if(KnownDADiscoverFromIPC(handle) == 0)
677 if(KnownDADiscoverFromProperties(scopelistlen, scopelist, handle) == 0)
678 if (! SLPPropertyAsBoolean(SLPGetProperty("net.slp.useDHCP"))
679 || KnownDADiscoverFromDHCP(handle) == 0)
680 KnownDADiscoverFromMulticast(scopelistlen, scopelist, handle);
681 return SLP_TRUE;
682 }
683 return SLP_FALSE;
684 }
685
686 /** Asks slpd if it knows about a DA.
687 *
688 * @param[in] scopelistlen - The length of @p scopelist.
689 * @param[in] scopelist - The list of scopes that must be supported.
690 * @param[in] spistrlen - The length of @p spistr.
691 * @param[in] spistr - The Security Provider Index.
692 * @param[in] daaddr - The DA address.
693 * @param[in] handle - The SLP handle associated with this request.
694 *
695 * @return Non-zero on success, zero if DA can not be found.
696 *
697 * @internal
698 */
KnownDAFromCache(size_t scopelistlen,const char * scopelist,size_t spistrlen,const char * spistr,void * daaddr,SLPHandleInfo * handle)699 static SLPBoolean KnownDAFromCache(size_t scopelistlen,
700 const char * scopelist, size_t spistrlen, const char * spistr,
701 void * daaddr, SLPHandleInfo * handle)
702 {
703 if (KnownDAListFind(scopelistlen, scopelist, spistrlen, spistr,
704 daaddr, sizeof(struct sockaddr_storage)) == SLP_FALSE)
705 {
706 if (KnownDARefreshCache((int)scopelistlen,
707 scopelist,
708 handle) == SLP_TRUE)
709 return KnownDAListFind(scopelistlen,
710 scopelist,
711 spistrlen,
712 spistr,
713 daaddr,
714 sizeof (struct sockaddr_storage));
715 /* cache wasn't refreshed, so no point in searching again */
716 return SLP_FALSE;
717 }
718 return SLP_TRUE;
719 }
720
721 /** Find a list of DA's whose combined scopes include all the given scopes
722 *
723 * The memory for the list is allocated by this function, and must be
724 * freed by the caller.
725 *
726 * @param[in] handle - The SLP handle associated with this request.
727 * @param[in] scopelistlen - The length of @p scopelist.
728 * @param[in] scopelist - The scopes the DA must support.
729 * @param[out] daaddrs - The peer to which we connected.
730 *
731 * @return SLP_TRUE if a spanning list can be found (*daaddrs is set to
732 * the address of an array of IP addresses terminated by an entry
733 * with an IP address of 0.0.0.0)
734 * SLP_FALSE if a list cannot be found ie. at least one of the
735 * given scopes is not handled by any known DA, or a memory
736 * allocation failed.
737 *
738 */
KnownDASpanningListFromCache(SLPHandleInfo * handle,int scopelistlen,const char * scopelist,struct sockaddr_in ** daaddrs)739 SLPBoolean KnownDASpanningListFromCache(SLPHandleInfo * handle,
740 int scopelistlen,
741 const char* scopelist,
742 struct sockaddr_in** daaddrs)
743 {
744 SLPBoolean result = SLP_TRUE;
745 size_t spistrlen = 0;
746 char* spistr = 0;
747 #ifdef ENABLE_SLPv2_SECURITY
748 if(SLPPropertyAsBoolean(SLPGetProperty("net.slp.securityEnabled")))
749 {
750 SLPSpiGetDefaultSPI(handle->hspi,
751 SLPSPI_KEY_TYPE_PUBLIC,
752 &spistrlen,
753 &spistr);
754 }
755 #endif
756
757 if(KnownDASpanningListFind(scopelistlen,
758 scopelist,
759 spistrlen,
760 spistr,
761 daaddrs) == 0)
762 {
763 result = SLP_FALSE;
764 /* if cache doesn't get refreshed, there's no point in searching again */
765 if (KnownDARefreshCache(scopelistlen,
766 scopelist,
767 handle) == SLP_TRUE)
768 result = KnownDASpanningListFind(scopelistlen,
769 scopelist,
770 spistrlen,
771 spistr,
772 daaddrs) == 0 ? SLP_FALSE : SLP_TRUE;
773 }
774
775 #ifdef ENABLE_SLPv2_SECURITY
776 if(spistr) xfree(spistr);
777 #endif
778
779 return result;
780 }
781
782 /** Get a connected socket to a DA that supports the specified scope.
783 *
784 * @param[in] handle - The SLP handle associated with this request.
785 * @param[in] scopelistlen - The length of @p scopelist.
786 * @param[in] scopelist - The scopes the DA must support.
787 * @param[out] peeraddr - The peer to which we connected.
788 *
789 * @return A valid socket file descriptor or SLP_INVALID_SOCKET if
790 * no DA supportting the requested scopelist is found.
791 */
KnownDAConnect(SLPHandleInfo * handle,size_t scopelistlen,const char * scopelist,void * peeraddr)792 sockfd_t KnownDAConnect(SLPHandleInfo * handle, size_t scopelistlen,
793 const char * scopelist, void * peeraddr)
794 {
795 sockfd_t sock = SLP_INVALID_SOCKET;
796 size_t spistrlen = 0;
797 char * spistr = 0;
798
799 #ifdef ENABLE_SLPv2_SECURITY
800 if (SLPPropertyAsBoolean("net.slp.securityEnabled"))
801 SLPSpiGetDefaultSPI(handle->hspi, SLPSPI_KEY_TYPE_PUBLIC,
802 &spistrlen, &spistr);
803 #endif
804
805 while (1)
806 {
807 struct sockaddr * addr = peeraddr;
808 memset(peeraddr, 0, sizeof(struct sockaddr_storage));
809
810 if (KnownDAFromCache(scopelistlen, scopelist, spistrlen, spistr,
811 peeraddr, handle) == SLP_FALSE)
812 break;
813
814 if ((addr->sa_family == AF_INET6 && SLPNetIsIPV6())
815 || (addr->sa_family == AF_INET && SLPNetIsIPV4()))
816 {
817 SLPNetSetPort(peeraddr, (uint16_t)SLPPropertyAsInteger("net.slp.port"));
818 sock = SLPNetworkCreateDatagram(addr->sa_family);
819 /* Now test if the DA will actually respond */
820 if (sock != SLP_INVALID_SOCKET)
821 {
822 if (KnownDADiscoveryRqstRply(sock, peeraddr, scopelistlen, scopelist, handle) > 0)
823 break;
824
825 closesocket(sock);
826 }
827 }
828 KnownDABadDA(peeraddr);
829 }
830 xfree(spistr);
831 return sock;
832 }
833
834 /** Mark a KnownDA as a Bad DA.
835 *
836 * @param[in] daaddr - The address of the bad DA.
837 */
KnownDABadDA(void * daaddr)838 void KnownDABadDA(void * daaddr)
839 {
840 SLPDatabaseHandle dh = SLPDatabaseOpen(&G_KnownDACache);
841 if (dh)
842 {
843 /* Check to find the requested entry. */
844 while (1)
845 {
846 SLPDatabaseEntry * entry = SLPDatabaseEnum(dh);
847 if (!entry)
848 break;
849
850 /* Assume DAs are identical if their in_addrs match. */
851 if (SLPNetCompareAddrs(daaddr, &entry->msg->peer) == 0)
852 {
853 SLPDatabaseRemove(dh, entry);
854 break;
855 }
856 }
857 SLPDatabaseClose(dh);
858 }
859 }
860
861 /** Gets a list of scopes from the known DA list.
862 *
863 * @param[out] scopelistlen - The address of storage for the length of the
864 * returned scope list in @p scopelist.
865 * @param[out] scopelist - The address of storage for a scope list ptr.
866 * @param[in] handle - The SLP session handle associated with this request.
867 *
868 * @return Zero on success, non-zero on memory allocation failure.
869 */
KnownDAGetScopes(size_t * scopelistlen,char ** scopelist,SLPHandleInfo * handle)870 int KnownDAGetScopes(size_t * scopelistlen,
871 char ** scopelist, SLPHandleInfo * handle)
872 {
873 #define SCOPE_LIST_CHUNK_SIZE 64
874
875 size_t newlen;
876 SLPDatabaseHandle dh;
877 SLPDatabaseEntry * entry;
878 char const * useScopes;
879
880 /** known scope list length */
881 size_t G_KnownDAScopesLen = 0;
882
883 /** known scope list */
884 char * G_KnownDAScopes = xmalloc(SCOPE_LIST_CHUNK_SIZE);
885
886 /** known scope buffer length */
887 size_t G_KnownDAScopesBufferLen = SCOPE_LIST_CHUNK_SIZE;
888
889 if (G_KnownDAScopes)
890 {
891 /* Discover all DAs. */
892 if (KnownDADiscoverFromIPC(handle) == 0)
893 {
894 if (SLPPropertyAsBoolean(SLPGetProperty("net.slp.useDHCP")))
895 KnownDADiscoverFromDHCP(handle);
896 KnownDADiscoverFromProperties(0,"", handle);
897 KnownDADiscoverFromMulticast(0,"", handle);
898 }
899
900 /* Enumerate all the knownda entries and generate a scopelist. */
901 dh = SLPDatabaseOpen(&G_KnownDACache);
902 if (dh)
903 {
904 /* Check to find the requested entry. */
905 while (1)
906 {
907 entry = SLPDatabaseEnum(dh);
908 if (!entry)
909 break;
910
911 newlen = G_KnownDAScopesBufferLen;
912 while (SLPUnionStringList(G_KnownDAScopesLen, G_KnownDAScopes,
913 entry->msg->body.daadvert.scopelistlen,
914 entry->msg->body.daadvert.scopelist, &newlen,
915 G_KnownDAScopes) < 0)
916 {
917 newlen += SCOPE_LIST_CHUNK_SIZE;
918 G_KnownDAScopesBufferLen = newlen;
919 G_KnownDAScopes = xrealloc(G_KnownDAScopes, G_KnownDAScopesBufferLen);
920 if (!G_KnownDAScopes)
921 {
922 G_KnownDAScopesLen = 0;
923 break;
924 }
925 }
926 G_KnownDAScopesLen = newlen;
927 }
928 SLPDatabaseClose(dh);
929 }
930
931 /* Explicitly add in the useScopes property */
932 useScopes = SLPPropertyGet("net.slp.useScopes", 0, 0);
933 newlen = G_KnownDAScopesBufferLen;
934 while (SLPUnionStringList(G_KnownDAScopesLen, G_KnownDAScopes,
935 strlen(useScopes), useScopes, &newlen, G_KnownDAScopes) < 0)
936 {
937 G_KnownDAScopesBufferLen = newlen;
938 G_KnownDAScopes = xrealloc(G_KnownDAScopes, newlen);
939 if (!G_KnownDAScopes)
940 {
941 G_KnownDAScopesLen = 0;
942 break;
943 }
944 }
945 G_KnownDAScopesLen = newlen;
946 }
947
948 if (G_KnownDAScopesLen)
949 {
950 if (G_KnownDAScopesLen == G_KnownDAScopesBufferLen)
951 {
952 /* Need space for a terminating NUL */
953 G_KnownDAScopes = xrealloc(G_KnownDAScopes, G_KnownDAScopesLen + 1);
954 }
955 *scopelist = G_KnownDAScopes;
956 if (*scopelist == 0)
957 return -1;
958 (*scopelist)[G_KnownDAScopesLen] = 0;
959 *scopelistlen = G_KnownDAScopesLen;
960 }
961 else
962 {
963 xfree(G_KnownDAScopes);
964 *scopelist = xstrdup("");
965 if (*scopelist == 0)
966 return -1;
967 *scopelistlen = 0;
968 }
969 return 0;
970 }
971
972 /** Process a SrvRqst for service:directory-agent.
973 *
974 * @param[in] handle - The SLP session handle associated with this request.
975 */
KnownDAProcessSrvRqst(SLPHandleInfo * handle)976 void KnownDAProcessSrvRqst(SLPHandleInfo * handle)
977 {
978 SLPDatabaseHandle dh;
979
980 /* Discover all DAs. */
981 if (KnownDADiscoverFromIPC(handle) == 0)
982 {
983 if (SLPPropertyAsBoolean(SLPGetProperty("net.slp.useDHCP")))
984 KnownDADiscoverFromDHCP(handle);
985 KnownDADiscoverFromProperties(0,"", handle);
986 KnownDADiscoverFromMulticast(0,"", handle);
987 }
988
989 /* Enumerate through knownDA database. */
990 dh = SLPDatabaseOpen(&G_KnownDACache);
991 if (dh)
992 {
993 /* Check to see if there a matching entry. */
994 while (1)
995 {
996 SLPBoolean cb_result;
997 SLPDatabaseEntry * entry = SLPDatabaseEnum(dh);
998 if (!entry)
999 break;
1000
1001 /* Call the SrvURLCallback. */
1002 cb_result = handle->params.findsrvs.callback(handle,
1003 entry->msg->body.daadvert.url, SLP_LIFETIME_MAXIMUM,
1004 SLP_OK, handle->params.findsrvs.cookie);
1005
1006 /* Does the caller want more? */
1007 if (cb_result == SLP_FALSE)
1008 break;
1009 }
1010 SLPDatabaseClose(dh);
1011 }
1012
1013 /* Make SLP_LAST_CALL. */
1014 handle->params.findsrvs.callback(handle, 0, 0,
1015 SLP_LAST_CALL, handle->params.findsrvs.cookie);
1016 }
1017
1018 /** Frees all (cached) resources associated with known DAs.
1019 */
KnownDAFreeAll(void)1020 void KnownDAFreeAll(void)
1021 {
1022 SLPDatabaseHandle dh = SLPDatabaseOpen(&G_KnownDACache);
1023 if (dh)
1024 {
1025 while (1)
1026 {
1027 SLPDatabaseEntry * entry = SLPDatabaseEnum(dh);
1028 if (!entry)
1029 break;
1030 SLPDatabaseRemove(dh,entry);
1031 }
1032 SLPDatabaseClose(dh);
1033 }
1034 G_KnownDALastCacheRefresh = 0;
1035 }
1036
1037 /*=========================================================================*/
1038