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