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 /** Find server types.
34  *
35  * Implementation for SLPFindSrvType() call.
36  *
37  * @file       libslp_findsrvtypes.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_net.h"
46 #include "slp_property.h"
47 #include "slp_xmalloc.h"
48 #include "slp_compare.h"
49 #include "slp_message.h"
50 
51 /** Collates response data to user callback for SLPFindSrvType requests.
52  *
53  * @param[in] hSLP - The SLP handle object associated with the request.
54  * @param[in] pcSrvTypes - The service type for this pass.
55  * @param[in] errorcode - The error code received on this pass.
56  *
57  * @return An SLP boolean value; SLP_TRUE indicates we are finished;
58  *    SLP_FALSE indicates we should continue.
59  *
60  * @todo Trace the logic of CollateToSLPSrvTypeCallback to ensure that
61  *    it works.
62  *
63  * @internal
64  */
CollateToSLPSrvTypeCallback(SLPHandle hSLP,const char * pcSrvTypes,SLPError errorcode)65 static SLPBoolean CollateToSLPSrvTypeCallback(SLPHandle hSLP,
66       const char * pcSrvTypes, SLPError errorcode)
67 {
68    int maxResults;
69    char * srvtypes;
70    size_t srvtypeslen;
71    SLPHandleInfo * handle = hSLP;
72 
73    handle->callbackcount++;
74 
75 #ifdef ENABLE_ASYNC_API
76    /* Do not collate for async calls. */
77 	if (handle->isAsync)
78       return handle->params.findsrvtypes.callback(hSLP, pcSrvTypes,
79             errorcode, handle->params.findsrvtypes.cookie);
80 #endif
81 
82    /* Configure behaviour for desired max results. */
83    maxResults = SLPPropertyAsInteger("net.slp.maxResults");
84    if (maxResults == -1)
85       maxResults = INT_MAX;
86 
87    if (errorcode == SLP_LAST_CALL || handle->callbackcount > maxResults)
88    {
89       /* We're done. Send back the collated srvtype string. */
90       if (handle->collatedsrvtypes)
91          if (handle->params.findsrvtypes.callback(handle,
92                handle->collatedsrvtypes, SLP_OK,
93                handle->params.findsrvtypes.cookie) == SLP_TRUE)
94             handle->params.findsrvtypes.callback(handle, 0,
95                   SLP_LAST_CALL, handle->params.findsrvtypes.cookie);
96 
97       /* Free the collatedsrvtype string. */
98       if (handle->collatedsrvtypes)
99       {
100          xfree(handle->collatedsrvtypes);
101          handle->collatedsrvtypes = 0;
102       }
103       handle->callbackcount = 0;
104       return SLP_FALSE;
105    }
106    else if (errorcode != SLP_OK)
107       return SLP_TRUE;
108 
109    /* Add the service types to the colation. */
110    srvtypeslen = strlen(pcSrvTypes) + 1; /* +1 - terminator */
111    if (handle->collatedsrvtypes)
112       srvtypeslen += strlen(handle->collatedsrvtypes) + 1; /* +1 - comma */
113 
114    srvtypes = xmalloc(srvtypeslen);
115    if (srvtypes)
116    {
117       if (handle->collatedsrvtypes)
118       {
119          if (SLPUnionStringList(strlen(handle->collatedsrvtypes),
120                handle->collatedsrvtypes, strlen(pcSrvTypes), pcSrvTypes,
121                &srvtypeslen, srvtypes) != (int)srvtypeslen)
122          {
123             xfree(handle->collatedsrvtypes);
124             handle->collatedsrvtypes = srvtypes;
125          }
126          else
127          {
128 #ifndef COLLATION_CHANGES
129             xfree(handle->collatedsrvtypes);
130             handle->collatedsrvtypes = srvtypes;
131             handle->collatedsrvtypes[srvtypeslen] = 0;
132 #else
133             xfree(srvtypes);
134 #endif
135          }
136       }
137       else
138       {
139          strcpy(srvtypes, pcSrvTypes);
140          handle->collatedsrvtypes = srvtypes;
141       }
142    }
143    return SLP_TRUE;
144 }
145 
146 /** SLPFindSrvTypes callback routine for NetworkRqstRply.
147  *
148  * @param[in] errorcode - The network operation error code.
149  * @param[in] peerinfo - The network address of the responder.
150  * @param[in] replybuf - The response buffer from the network request.
151  * @param[in] cookie - Callback context data from ProcessSrvReg.
152  *
153  * @return SLP_FALSE (to stop any iterative callbacks).
154  *
155  * @internal
156  */
ProcessSrvTypeRplyCallback(SLPError errorcode,void * peerinfo,SLPBuffer replybuf,void * cookie)157 static SLPBoolean ProcessSrvTypeRplyCallback(SLPError errorcode,
158       void * peerinfo, SLPBuffer replybuf, void * cookie)
159 {
160    SLPMessage * replymsg;
161    SLPBoolean result = SLP_TRUE;
162    SLPHandleInfo * handle = (SLPHandleInfo *)cookie;
163 
164    /* Check the errorcode and bail if it is set. */
165    if (errorcode)
166       return CollateToSLPSrvTypeCallback(handle, 0, errorcode);
167 
168    /* Parse the replybuf. */
169    replymsg = SLPMessageAlloc();
170    if (replymsg)
171    {
172       if (!SLPMessageParseBuffer(peerinfo, 0, replybuf, replymsg)
173             && replymsg->header.functionid == SLP_FUNCT_SRVTYPERPLY
174             && !replymsg->body.srvtyperply.errorcode)
175       {
176          SLPSrvTypeRply * srvtyperply = &replymsg->body.srvtyperply;
177          if (srvtyperply->srvtypelistlen)
178             result = CollateToSLPSrvTypeCallback((SLPHandle)handle,
179                   srvtyperply->srvtypelist, srvtyperply->errorcode * -1);
180       }
181       SLPMessageFree(replymsg);
182    }
183    return result;
184 }
185 
186 /** Formats and sends an SLPFindSrvTypes wire buffer request.
187  *
188  * @param handle - The OpenSLP session handle, containing request
189  *    parameters. See docs for SLPFindSrvTypes.
190  *
191  * @return Zero on success, or an SLP API error code.
192  *
193  * @internal
194  */
ProcessSrvTypeRqst(SLPHandleInfo * handle)195 static SLPError ProcessSrvTypeRqst(SLPHandleInfo * handle)
196 {
197    sockfd_t sock;
198    uint8_t * buf;
199    uint8_t * curpos;
200    SLPError serr = SLP_OK;
201    struct sockaddr_storage peeraddr;
202    struct sockaddr_in* destaddrs = 0;
203 
204 /* 0                   1                   2                   3
205    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
206   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
207   |   length of Naming Authority  |   <Naming Authority String>   \
208   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
209   |     length of <scope-list>    |      <scope-list> String      \
210   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
211 
212    /** @todo Ensure that we don't exceed the MTU. */
213 
214    buf = curpos = xmalloc(
215          + 2 + handle->params.findsrvtypes.namingauthlen
216          + 2 + handle->params.findsrvtypes.scopelistlen);
217    if (buf == 0)
218       return SLP_MEMORY_ALLOC_FAILED;
219 
220    /* Naming Authority */
221    if (strcmp(handle->params.findsrvtypes.namingauth, "*") == 0)
222       PutUINT16(&curpos, 0xffff); /* 0xffff is wildcard */
223    else
224       PutL16String(&curpos, handle->params.findsrvtypes.namingauth,
225             handle->params.findsrvtypes.namingauthlen);
226 
227    /* <scope-list> */
228    PutL16String(&curpos, handle->params.findsrvtypes.scopelist,
229          handle->params.findsrvtypes.scopelistlen);
230 
231    /* Send request, receive reply. */
232    do
233    {
234 #ifndef UNICAST_NOT_SUPPORTED
235       if (handle->dounicast == 1)
236       {
237          serr = NetworkUcastRqstRply(handle, buf, SLP_FUNCT_SRVTYPERQST,
238                curpos - buf, ProcessSrvTypeRplyCallback, handle, false);
239          break;
240       }
241       if (SLPNetIsIPV4())
242       {
243          if (KnownDASpanningListFromCache(handle,
244                                           (int)handle->params.findsrvs.scopelistlen,
245                                           handle->params.findsrvs.scopelist,
246                                           &destaddrs) > 0)
247          {
248             serr = NetworkMultiUcastRqstRply(destaddrs,
249                                              handle->langtag,
250                                              (char*)buf,
251                                              SLP_FUNCT_SRVTYPERQST,
252                                              curpos - buf,
253                                              ProcessSrvTypeRplyCallback,
254                                              handle, false);
255             xfree(destaddrs);
256             break;
257          }
258       }
259 #endif
260       sock = NetworkConnectToDA(handle,
261             handle->params.findsrvtypes.scopelist,
262             handle->params.findsrvtypes.scopelistlen,
263             &peeraddr);
264 
265       if (sock == SLP_INVALID_SOCKET)
266       {
267          serr = NetworkMcastRqstRply(handle, buf, SLP_FUNCT_SRVTYPERQST,
268                curpos - buf, ProcessSrvTypeRplyCallback, 0, false);
269          break;
270       }
271       serr = NetworkRqstRply(sock, &peeraddr, handle->langtag, 0, buf,
272             SLP_FUNCT_SRVTYPERQST, curpos - buf, ProcessSrvTypeRplyCallback,
273             handle, false);
274 
275       if (serr)
276          NetworkDisconnectDA(handle);
277 
278    } while (serr == SLP_NETWORK_ERROR);
279 
280    xfree(buf);
281    return serr;
282 }
283 
284 #ifdef ENABLE_ASYNC_API
285 /** Thread start procedure for asynchronous service type request.
286  *
287  * @param[in,out] handle - Contains the request parameters, returns the
288  *    request result.
289  *
290  * @return An SLPError code.
291  *
292  * @internal
293  */
AsyncProcessSrvTypeRqst(SLPHandleInfo * handle)294 static SLPError AsyncProcessSrvTypeRqst(SLPHandleInfo * handle)
295 {
296    SLPError result = ProcessSrvTypeRqst(handle);
297    xfree((void *)handle->params.findsrvtypes.namingauth);
298    xfree((void *)handle->params.findsrvtypes.scopelist);
299    handle->inUse = SLP_FALSE;
300    return result;
301 }
302 #endif
303 
304 /** Return a list of service types available on the network.
305  *
306  * The SLPFindSrvType function issues an SLP service type request for
307  * service types in the scopes indicated by the @p pcScopeList. The
308  * results are returned through the @p callback parameter. The service
309  * types are independent of language locale, but only for services
310  * registered in one of scopes and for the indicated naming authority.
311  *
312  * @par
313  * If the naming authority is "*", then results are returned for all
314  * naming authorities. If the naming authority is the empty string,
315  * i.e. "", then the default naming authority, "IANA", is used. "IANA"
316  * is not a valid naming authority name, and it is a PARAMETER_BAD error
317  * to include it explicitly.
318  *
319  * @par
320  * The service type names are returned with the naming authority intact.
321  * If the naming authority is the default (i.e. empty string) then it
322  * is omitted, as is the separating ".". Service type names from URLs
323  * of the service: scheme are returned with the "service:" prefix
324  * intact. [RFC 2608] See [RFC 2609] for more information on the
325  * syntax of service type names.
326  *
327  * @param[in] hSLP - The SLPHandle on which to search for types.
328  * @param[in] pcNamingAuthority - The naming authority to search. Use "*"
329  *    for all naming authorities and the empty string, "", for the default
330  *    naming authority.
331  * @param[in] pcScopeList - A pointer to a char containing comma separated
332  *    list of scope names to search for service types. May not be the empty
333  *    string, "".
334  * @param[in] callback - A callback function through which the results of
335  *    the operation are reported.
336  * @param[in] pvCookie - Memory passed to the @p callback code from the
337  *    client. May be NULL.
338  *
339  * @return If an error occurs in starting the operation, one of the
340  *    SLPError codes is returned.
341  */
SLPFindSrvTypes(SLPHandle hSLP,const char * pcNamingAuthority,const char * pcScopeList,SLPSrvTypeCallback callback,void * pvCookie)342 SLPError SLPAPI SLPFindSrvTypes(
343       SLPHandle hSLP,
344       const char * pcNamingAuthority,
345       const char * pcScopeList,
346       SLPSrvTypeCallback callback,
347       void * pvCookie)
348 {
349    bool inuse;
350    SLPError serr = 0;
351    SLPHandleInfo * handle = hSLP;
352 
353    /* Check for invalid parameters. */
354    SLP_ASSERT(handle != 0);
355    SLP_ASSERT(handle->sig == SLP_HANDLE_SIG);
356    SLP_ASSERT(pcNamingAuthority != 0);
357    SLP_ASSERT(strcmp(pcNamingAuthority, "IANA") != 0);
358    SLP_ASSERT(callback != 0);
359 
360    if (handle == 0 || handle->sig != SLP_HANDLE_SIG
361          || pcNamingAuthority == 0
362          || strcmp(pcNamingAuthority, "IANA") == 0
363          || callback == 0)
364       return SLP_PARAMETER_BAD;
365 
366    /* Check to see if the handle is in use. */
367    inuse = SLPSpinLockTryAcquire(&handle->inUse);
368    SLP_ASSERT(!inuse);
369    if (inuse)
370       return SLP_HANDLE_IN_USE;
371 
372    /* Get a scope list if none was specified. */
373    if (pcScopeList == 0 || *pcScopeList == 0)
374       pcScopeList = SLPPropertyGet("net.slp.useScopes", 0, 0);
375 
376    /* Set the handle up to reference parameters. */
377    handle->params.findsrvtypes.namingauthlen = strlen(pcNamingAuthority);
378    handle->params.findsrvtypes.namingauth = pcNamingAuthority;
379    handle->params.findsrvtypes.scopelistlen = strlen(pcScopeList);
380    handle->params.findsrvtypes.scopelist = pcScopeList;
381    handle->params.findsrvtypes.callback = callback;
382    handle->params.findsrvtypes.cookie = pvCookie;
383 
384    /* Check to see if we should be async or sync. */
385 #ifdef ENABLE_ASYNC_API
386    if (handle->isAsync)
387    {
388       /* Copy all the referenced parameters. */
389       handle->params.findsrvtypes.namingauth =
390             xstrdup(handle->params.findsrvtypes.namingauth);
391       handle->params.findsrvtypes.scopelist =
392             xstrdup(handle->params.findsrvtypes.scopelist);
393 
394       /* Ensure strdups and thread create succeed. */
395       if (handle->params.findsrvtypes.namingauth == 0
396             || handle->params.findsrvtypes.scopelist == 0
397             || (handle->th = SLPThreadCreate((SLPThreadStartProc)
398                   AsyncProcessSrvTypeRqst, handle)) == 0)
399       {
400          serr = SLP_MEMORY_ALLOC_FAILED;
401          xfree((void *)handle->params.findsrvtypes.namingauth);
402          xfree((void *)handle->params.findsrvtypes.scopelist);
403          SLPSpinLockRelease(&handle->inUse);
404       }
405       return serr;
406    }
407 #endif
408 
409    /* Reference all parameters. */
410    serr = ProcessSrvTypeRqst(handle);
411    SLPSpinLockRelease(&handle->inUse);
412    return serr;
413 }
414 
415 /*=========================================================================*/
416