/src/pupnp/upnp/src/ssdp/ssdp_ctrlpt.c
Line | Count | Source |
1 | | /************************************************************************** |
2 | | * |
3 | | * Copyright (c) 2000-2003 Intel Corporation |
4 | | * All rights reserved. |
5 | | * |
6 | | * Redistribution and use in source and binary forms, with or without |
7 | | * modification, are permitted provided that the following conditions are met: |
8 | | * |
9 | | * - Redistributions of source code must retain the above copyright notice, |
10 | | * this list of conditions and the following disclaimer. |
11 | | * - Redistributions in binary form must reproduce the above copyright notice, |
12 | | * this list of conditions and the following disclaimer in the documentation |
13 | | * and/or other materials provided with the distribution. |
14 | | * - Neither name of Intel Corporation nor the names of its contributors |
15 | | * may be used to endorse or promote products derived from this software |
16 | | * without specific prior written permission. |
17 | | * |
18 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR |
22 | | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
23 | | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
24 | | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
25 | | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
26 | | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
27 | | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
28 | | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | | * |
30 | | **************************************************************************/ |
31 | | |
32 | | /*! |
33 | | * \addtogroup SSDPlib |
34 | | * |
35 | | * @{ |
36 | | * |
37 | | * \file |
38 | | */ |
39 | | |
40 | | #include "Callback.h" |
41 | | #include "LinkedList.h" |
42 | | #include "TimerThread.h" |
43 | | #include "UpnpDiscovery.h" |
44 | | #include "UpnpString.h" |
45 | | #include "config.h" |
46 | | |
47 | | #include "ithread.h" |
48 | | #include "membuffer.h" |
49 | | #include "upnp.h" |
50 | | #include "upnpdebug.h" |
51 | | #include "upnpdebug_internal.h" |
52 | | #include "upnputil.h" |
53 | | #include <errno.h> |
54 | | #include <stdlib.h> |
55 | | #include <string.h> |
56 | | |
57 | | #ifndef _WIN32 |
58 | | #include <poll.h> |
59 | 0 | #define _poll(fds, nfds, timeout) poll(fds, nfds, timeout) |
60 | | #endif |
61 | | |
62 | | #ifdef INCLUDE_CLIENT_APIS |
63 | | #if EXCLUDE_SSDP == 0 |
64 | | |
65 | | #include "SSDPResultData.h" |
66 | | #include "SSDPResultDataCallback.h" |
67 | | #include "ThreadPool.h" |
68 | | #include "UpnpInet.h" |
69 | | #include "httpparser.h" |
70 | | #include "ssdplib.h" |
71 | | #include "statcodes.h" |
72 | | #include "upnpapi.h" |
73 | | |
74 | | #include <stdio.h> |
75 | | |
76 | | #include "posix_overwrites.h" // IWYU pragma: keep |
77 | | |
78 | | /*! |
79 | | * \brief Sends a callback to the control point application with a SEARCH |
80 | | * result. |
81 | | */ |
82 | | static void send_search_result( |
83 | | /* [in] Search reply from the device. */ |
84 | | void *data) |
85 | 0 | { |
86 | 0 | SSDPResultData *temp = (SSDPResultData *)data; |
87 | |
|
88 | 0 | SSDPResultData_Callback(temp); |
89 | 0 | SSDPResultData_delete(temp); |
90 | 0 | } |
91 | | |
92 | | void ssdp_handle_ctrlpt_msg( |
93 | | http_message_t *hmsg, struct sockaddr_storage *dest_addr, int timeout) |
94 | 0 | { |
95 | 0 | int handle; |
96 | 0 | int handle_start; |
97 | 0 | struct Handle_Info *ctrlpt_info = NULL; |
98 | 0 | memptr hdr_value; |
99 | | /* byebye or alive */ |
100 | 0 | int is_byebye; |
101 | 0 | UpnpDiscovery *param = UpnpDiscovery_new(); |
102 | 0 | int expires; |
103 | 0 | int ret; |
104 | 0 | SsdpEvent event; |
105 | 0 | int nt_found; |
106 | 0 | int usn_found; |
107 | 0 | int st_found; |
108 | 0 | char save_char; |
109 | 0 | Upnp_EventType event_type; |
110 | 0 | Upnp_FunPtr ctrlpt_callback; |
111 | 0 | void *ctrlpt_cookie; |
112 | 0 | ListNode *node = NULL; |
113 | 0 | SsdpSearchArg *searchArg = NULL; |
114 | 0 | int matched = 0; |
115 | 0 | SSDPResultData *threadData = NULL; |
116 | 0 | ThreadPoolJob job; |
117 | | |
118 | | /* we are assuming that there can be only one client supported at a time |
119 | | */ |
120 | 0 | HandleReadLock(__FILE__, __LINE__); |
121 | |
|
122 | 0 | if (GetClientHandleInfo(&handle_start, &ctrlpt_info) != HND_CLIENT) { |
123 | 0 | HandleUnlock(__FILE__, __LINE__); |
124 | 0 | goto end_ssdp_handle_ctrlpt_msg; |
125 | 0 | } |
126 | 0 | HandleUnlock(__FILE__, __LINE__); |
127 | | /* search timeout */ |
128 | 0 | if (timeout) { |
129 | 0 | for (handle = handle_start; handle < NUM_HANDLE; handle++) { |
130 | 0 | HandleLock(__FILE__, __LINE__); |
131 | | |
132 | | /* get client info */ |
133 | 0 | if (GetHandleInfo(handle, &ctrlpt_info) != HND_CLIENT) { |
134 | 0 | HandleUnlock(__FILE__, __LINE__); |
135 | 0 | continue; |
136 | 0 | } |
137 | | /* copy */ |
138 | 0 | ctrlpt_callback = ctrlpt_info->Callback; |
139 | 0 | ctrlpt_cookie = ctrlpt_info->Cookie; |
140 | 0 | HandleUnlock(__FILE__, __LINE__); |
141 | |
|
142 | 0 | ctrlpt_callback(UPNP_DISCOVERY_SEARCH_TIMEOUT, |
143 | 0 | NULL, |
144 | 0 | ctrlpt_cookie); |
145 | 0 | } |
146 | 0 | goto end_ssdp_handle_ctrlpt_msg; |
147 | 0 | } |
148 | | |
149 | 0 | UpnpDiscovery_set_ErrCode(param, UPNP_E_SUCCESS); |
150 | | /* MAX-AGE, assume error */ |
151 | 0 | expires = -1; |
152 | 0 | UpnpDiscovery_set_Expires(param, expires); |
153 | 0 | if (httpmsg_find_hdr(hmsg, HDR_CACHE_CONTROL, &hdr_value) != NULL) { |
154 | 0 | ret = matchstr(hdr_value.buf, |
155 | 0 | hdr_value.length, |
156 | 0 | "%imax-age = %d%0", |
157 | 0 | &expires); |
158 | 0 | UpnpDiscovery_set_Expires(param, expires); |
159 | 0 | if (ret != PARSE_OK) |
160 | 0 | goto end_ssdp_handle_ctrlpt_msg; |
161 | 0 | } |
162 | | /* DATE */ |
163 | 0 | if (httpmsg_find_hdr(hmsg, HDR_DATE, &hdr_value) != NULL) { |
164 | 0 | UpnpDiscovery_strcpy_Date(param, hdr_value.buf); |
165 | 0 | } |
166 | | /* dest addr */ |
167 | 0 | UpnpDiscovery_set_DestAddr(param, dest_addr); |
168 | | /* EXT */ |
169 | 0 | if (httpmsg_find_hdr(hmsg, HDR_EXT, &hdr_value) != NULL) { |
170 | 0 | UpnpDiscovery_strncpy_Ext( |
171 | 0 | param, hdr_value.buf, hdr_value.length); |
172 | 0 | } |
173 | | /* LOCATION */ |
174 | 0 | if (httpmsg_find_hdr(hmsg, HDR_LOCATION, &hdr_value) != NULL) { |
175 | 0 | UpnpDiscovery_strncpy_Location( |
176 | 0 | param, hdr_value.buf, hdr_value.length); |
177 | 0 | } |
178 | | /* SERVER / USER-AGENT */ |
179 | 0 | if (httpmsg_find_hdr(hmsg, HDR_SERVER, &hdr_value) != NULL || |
180 | 0 | httpmsg_find_hdr(hmsg, HDR_USER_AGENT, &hdr_value) != NULL) { |
181 | 0 | UpnpDiscovery_strncpy_Os( |
182 | 0 | param, hdr_value.buf, hdr_value.length); |
183 | 0 | } |
184 | | /* clear everything */ |
185 | 0 | event.UDN[0] = '\0'; |
186 | 0 | event.DeviceType[0] = '\0'; |
187 | 0 | event.ServiceType[0] = '\0'; |
188 | 0 | nt_found = 0; |
189 | 0 | if (httpmsg_find_hdr(hmsg, HDR_NT, &hdr_value) != NULL) { |
190 | 0 | save_char = hdr_value.buf[hdr_value.length]; |
191 | 0 | hdr_value.buf[hdr_value.length] = '\0'; |
192 | 0 | nt_found = (ssdp_request_type(hdr_value.buf, &event) == 0); |
193 | 0 | hdr_value.buf[hdr_value.length] = save_char; |
194 | 0 | } |
195 | 0 | usn_found = 0; |
196 | 0 | if (httpmsg_find_hdr(hmsg, HDR_USN, &hdr_value) != NULL) { |
197 | 0 | save_char = hdr_value.buf[hdr_value.length]; |
198 | 0 | hdr_value.buf[hdr_value.length] = '\0'; |
199 | 0 | usn_found = (unique_service_name(hdr_value.buf, &event) == 0); |
200 | 0 | hdr_value.buf[hdr_value.length] = save_char; |
201 | 0 | } |
202 | 0 | if (nt_found || usn_found) { |
203 | 0 | UpnpDiscovery_strcpy_DeviceID(param, event.UDN); |
204 | 0 | UpnpDiscovery_strcpy_DeviceType(param, event.DeviceType); |
205 | 0 | UpnpDiscovery_strcpy_ServiceType(param, event.ServiceType); |
206 | 0 | } |
207 | | /* ADVERT. OR BYEBYE */ |
208 | 0 | if (hmsg->is_request) { |
209 | | /* use NTS hdr to determine advert., or byebye */ |
210 | 0 | if (httpmsg_find_hdr(hmsg, HDR_NTS, &hdr_value) == NULL) { |
211 | | /* error; NTS header not found */ |
212 | 0 | goto end_ssdp_handle_ctrlpt_msg; |
213 | 0 | } |
214 | 0 | if (memptr_cmp(&hdr_value, "ssdp:alive") == 0) { |
215 | 0 | is_byebye = 0; |
216 | 0 | } else if (memptr_cmp(&hdr_value, "ssdp:byebye") == 0) { |
217 | 0 | is_byebye = 1; |
218 | 0 | } else { |
219 | | /* bad value */ |
220 | 0 | goto end_ssdp_handle_ctrlpt_msg; |
221 | 0 | } |
222 | 0 | if (is_byebye) { |
223 | | /* check device byebye */ |
224 | 0 | if (!nt_found || !usn_found) { |
225 | | /* bad byebye */ |
226 | 0 | goto end_ssdp_handle_ctrlpt_msg; |
227 | 0 | } |
228 | 0 | event_type = UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE; |
229 | 0 | } else { |
230 | | /* check advertisement. |
231 | | * Expires is valid if positive. This is for testing |
232 | | * only. Expires should be greater than 1800 (30 mins) |
233 | | */ |
234 | 0 | if (!nt_found || !usn_found || |
235 | 0 | UpnpString_get_Length( |
236 | 0 | UpnpDiscovery_get_Location(param)) == |
237 | 0 | 0 || |
238 | 0 | UpnpDiscovery_get_Expires(param) <= 0) { |
239 | | /* bad advertisement */ |
240 | 0 | goto end_ssdp_handle_ctrlpt_msg; |
241 | 0 | } |
242 | 0 | event_type = UPNP_DISCOVERY_ADVERTISEMENT_ALIVE; |
243 | 0 | } |
244 | | /* call callback */ |
245 | 0 | for (handle = handle_start; handle < NUM_HANDLE; handle++) { |
246 | 0 | HandleLock(__FILE__, __LINE__); |
247 | | |
248 | | /* get client info */ |
249 | 0 | if (GetHandleInfo(handle, &ctrlpt_info) != HND_CLIENT) { |
250 | 0 | HandleUnlock(__FILE__, __LINE__); |
251 | 0 | continue; |
252 | 0 | } |
253 | | /* copy */ |
254 | 0 | ctrlpt_callback = ctrlpt_info->Callback; |
255 | 0 | ctrlpt_cookie = ctrlpt_info->Cookie; |
256 | 0 | HandleUnlock(__FILE__, __LINE__); |
257 | |
|
258 | 0 | ctrlpt_callback(event_type, param, ctrlpt_cookie); |
259 | 0 | } |
260 | 0 | } else { |
261 | | /* reply (to a SEARCH) */ |
262 | | /* only checking to see if there is a valid ST header */ |
263 | 0 | st_found = 0; |
264 | 0 | if (httpmsg_find_hdr(hmsg, HDR_ST, &hdr_value) != NULL) { |
265 | 0 | save_char = hdr_value.buf[hdr_value.length]; |
266 | 0 | hdr_value.buf[hdr_value.length] = '\0'; |
267 | 0 | st_found = |
268 | 0 | ssdp_request_type(hdr_value.buf, &event) == 0; |
269 | 0 | hdr_value.buf[hdr_value.length] = save_char; |
270 | 0 | } |
271 | 0 | if (hmsg->status_code != HTTP_OK || |
272 | 0 | UpnpDiscovery_get_Expires(param) <= 0 || |
273 | 0 | UpnpString_get_Length( |
274 | 0 | UpnpDiscovery_get_Location(param)) == 0 || |
275 | 0 | !usn_found || !st_found) { |
276 | | /* bad reply */ |
277 | 0 | goto end_ssdp_handle_ctrlpt_msg; |
278 | 0 | } |
279 | | /* check each current search */ |
280 | 0 | for (handle = handle_start; handle < NUM_HANDLE; handle++) { |
281 | 0 | HandleLock(__FILE__, __LINE__); |
282 | | |
283 | | /* get client info */ |
284 | 0 | if (GetHandleInfo(handle, &ctrlpt_info) != HND_CLIENT) { |
285 | 0 | HandleUnlock(__FILE__, __LINE__); |
286 | 0 | continue; |
287 | 0 | } |
288 | | /* copy */ |
289 | 0 | ctrlpt_callback = ctrlpt_info->Callback; |
290 | 0 | ctrlpt_cookie = ctrlpt_info->Cookie; |
291 | |
|
292 | 0 | node = ListHead(&ctrlpt_info->SsdpSearchList); |
293 | | /* temporary add null termination */ |
294 | | /*save_char = hdr_value.buf[ hdr_value.length ]; */ |
295 | | /*hdr_value.buf[ hdr_value.length ] = '\0'; */ |
296 | 0 | while (node != NULL) { |
297 | 0 | searchArg = node->item; |
298 | | /* check for match of ST header and search |
299 | | * target */ |
300 | 0 | switch (searchArg->requestType) { |
301 | 0 | case SSDP_ALL: |
302 | 0 | matched = 1; |
303 | 0 | break; |
304 | 0 | case SSDP_ROOTDEVICE: |
305 | 0 | matched = (event.RequestType == |
306 | 0 | SSDP_ROOTDEVICE); |
307 | 0 | break; |
308 | 0 | case SSDP_DEVICEUDN: |
309 | 0 | matched = !strncmp( |
310 | 0 | searchArg->searchTarget, |
311 | 0 | hdr_value.buf, |
312 | 0 | hdr_value.length); |
313 | 0 | break; |
314 | 0 | case SSDP_DEVICETYPE: { |
315 | 0 | size_t m = min(hdr_value.length, |
316 | 0 | strlen(searchArg->searchTarget)); |
317 | 0 | matched = !strncmp( |
318 | 0 | searchArg->searchTarget, |
319 | 0 | hdr_value.buf, |
320 | 0 | m); |
321 | 0 | break; |
322 | 0 | } |
323 | 0 | case SSDP_SERVICE: { |
324 | 0 | size_t m = min(hdr_value.length, |
325 | 0 | strlen(searchArg->searchTarget)); |
326 | 0 | matched = !strncmp( |
327 | 0 | searchArg->searchTarget, |
328 | 0 | hdr_value.buf, |
329 | 0 | m); |
330 | 0 | break; |
331 | 0 | } |
332 | 0 | default: |
333 | 0 | matched = 0; |
334 | 0 | break; |
335 | 0 | } |
336 | 0 | if (matched) { |
337 | | /* schedule call back */ |
338 | 0 | threadData = SSDPResultData_new(); |
339 | 0 | if (threadData != NULL) { |
340 | 0 | SSDPResultData_set_Param( |
341 | 0 | threadData, param); |
342 | 0 | SSDPResultData_set_Cookie( |
343 | 0 | threadData, |
344 | 0 | searchArg->cookie); |
345 | 0 | SSDPResultData_set_CtrlptCallback( |
346 | 0 | threadData, |
347 | 0 | ctrlpt_callback); |
348 | 0 | memset(&job, 0, sizeof(job)); |
349 | |
|
350 | 0 | TPJobInit(&job, |
351 | 0 | (start_routine) |
352 | 0 | send_search_result, |
353 | 0 | threadData); |
354 | 0 | TPJobSetPriority( |
355 | 0 | &job, MED_PRIORITY); |
356 | 0 | TPJobSetFreeFunction(&job, |
357 | 0 | (free_routine)free); |
358 | 0 | if (ThreadPoolAdd( |
359 | 0 | &gRecvThreadPool, |
360 | 0 | &job, |
361 | 0 | NULL) != 0) { |
362 | 0 | SSDPResultData_delete( |
363 | 0 | threadData); |
364 | 0 | } |
365 | 0 | } |
366 | 0 | } |
367 | 0 | node = ListNext( |
368 | 0 | &ctrlpt_info->SsdpSearchList, node); |
369 | 0 | } |
370 | | |
371 | 0 | HandleUnlock(__FILE__, __LINE__); |
372 | | /*ctrlpt_callback( UPNP_DISCOVERY_SEARCH_RESULT, param, |
373 | | * ctrlpt_cookie ); */ |
374 | 0 | } |
375 | 0 | } |
376 | | |
377 | 0 | end_ssdp_handle_ctrlpt_msg: |
378 | 0 | UpnpDiscovery_delete(param); |
379 | 0 | } |
380 | | |
381 | | /*! |
382 | | * \brief Creates a HTTP search request packet depending on the input |
383 | | * parameter. |
384 | | */ |
385 | | static int CreateClientRequestPacket( |
386 | | /*! [in,out] Output string in HTTP format. */ |
387 | | char *RqstBuf, |
388 | | /*! [in] RqstBuf size. */ |
389 | | size_t RqstBufSize, |
390 | | /*! [in] Search Target. */ |
391 | | int Mx, |
392 | | /*! [in] Number of seconds to wait to collect all the responses. */ |
393 | | char *SearchTarget, |
394 | | /*! [in] search address family. */ |
395 | | int AddressFamily) |
396 | 0 | { |
397 | 0 | int rc; |
398 | 0 | size_t offset = 0; |
399 | |
|
400 | 0 | switch (AddressFamily) { |
401 | 0 | case AF_INET: |
402 | 0 | rc = snprintf(RqstBuf, |
403 | 0 | RqstBufSize, |
404 | 0 | "M-SEARCH * HTTP/1.1\r\n" |
405 | 0 | "HOST: %s:%d\r\n" |
406 | 0 | "MAN: \"ssdp:discover\"\r\n", |
407 | 0 | SSDP_IP, |
408 | 0 | SSDP_PORT); |
409 | 0 | break; |
410 | 0 | case AF_INET6: |
411 | 0 | rc = snprintf(RqstBuf, |
412 | 0 | RqstBufSize, |
413 | 0 | "M-SEARCH * HTTP/1.1\r\n" |
414 | 0 | "HOST: [%s]:%d\r\n" |
415 | 0 | "MAN: \"ssdp:discover\"\r\n", |
416 | 0 | SSDP_IPV6_LINKLOCAL, |
417 | 0 | SSDP_PORT); |
418 | 0 | break; |
419 | 0 | default: |
420 | 0 | return UPNP_E_INVALID_ARGUMENT; |
421 | 0 | } |
422 | 0 | if (rc < 0 || (size_t)rc >= RqstBufSize) |
423 | 0 | return UPNP_E_INTERNAL_ERROR; |
424 | 0 | offset = (size_t)rc; |
425 | |
|
426 | 0 | if (Mx > 0) { |
427 | 0 | rc = snprintf(RqstBuf + offset, |
428 | 0 | RqstBufSize - offset, |
429 | 0 | "MX: %d\r\n", |
430 | 0 | Mx); |
431 | 0 | if (rc < 0 || (size_t)rc >= RqstBufSize - offset) |
432 | 0 | return UPNP_E_BUFFER_TOO_SMALL; |
433 | 0 | offset += (size_t)rc; |
434 | 0 | } |
435 | | |
436 | 0 | if (SearchTarget) { |
437 | 0 | rc = snprintf(RqstBuf + offset, |
438 | 0 | RqstBufSize - offset, |
439 | 0 | "ST: %s\r\n", |
440 | 0 | SearchTarget); |
441 | 0 | if (rc < 0 || (size_t)rc >= RqstBufSize - offset) |
442 | 0 | return UPNP_E_BUFFER_TOO_SMALL; |
443 | 0 | offset += (size_t)rc; |
444 | 0 | } |
445 | | |
446 | 0 | rc = snprintf(RqstBuf + offset, RqstBufSize - offset, "\r\n"); |
447 | 0 | if (rc < 0 || (size_t)rc >= RqstBufSize - offset) |
448 | 0 | return UPNP_E_BUFFER_TOO_SMALL; |
449 | | |
450 | 0 | return UPNP_E_SUCCESS; |
451 | 0 | } |
452 | | |
453 | | /*! |
454 | | * \brief |
455 | | */ |
456 | | #ifdef UPNP_ENABLE_IPV6 |
457 | | static int CreateClientRequestPacketUlaGua( |
458 | | /*! [in,out] . */ |
459 | | char *RqstBuf, |
460 | | /*! [in] . */ |
461 | | size_t RqstBufSize, |
462 | | /*! [in] . */ |
463 | | int Mx, |
464 | | /*! [in] . */ |
465 | | char *SearchTarget, |
466 | | /*! [in] . */ |
467 | | int AddressFamily) |
468 | 0 | { |
469 | 0 | int rc; |
470 | 0 | size_t offset = 0; |
471 | |
|
472 | 0 | switch (AddressFamily) { |
473 | 0 | case AF_INET: |
474 | 0 | rc = snprintf(RqstBuf, |
475 | 0 | RqstBufSize, |
476 | 0 | "M-SEARCH * HTTP/1.1\r\n" |
477 | 0 | "HOST: %s:%d\r\n" |
478 | 0 | "MAN: \"ssdp:discover\"\r\n", |
479 | 0 | SSDP_IP, |
480 | 0 | SSDP_PORT); |
481 | 0 | break; |
482 | 0 | case AF_INET6: |
483 | 0 | rc = snprintf(RqstBuf, |
484 | 0 | RqstBufSize, |
485 | 0 | "M-SEARCH * HTTP/1.1\r\n" |
486 | 0 | "HOST: [%s]:%d\r\n" |
487 | 0 | "MAN: \"ssdp:discover\"\r\n", |
488 | 0 | SSDP_IPV6_SITELOCAL, |
489 | 0 | SSDP_PORT); |
490 | 0 | break; |
491 | 0 | default: |
492 | 0 | return UPNP_E_INVALID_ARGUMENT; |
493 | 0 | } |
494 | 0 | if (rc < 0 || (size_t)rc >= RqstBufSize) |
495 | 0 | return UPNP_E_INTERNAL_ERROR; |
496 | 0 | offset = (size_t)rc; |
497 | |
|
498 | 0 | if (Mx > 0) { |
499 | 0 | rc = snprintf(RqstBuf + offset, |
500 | 0 | RqstBufSize - offset, |
501 | 0 | "MX: %d\r\n", |
502 | 0 | Mx); |
503 | 0 | if (rc < 0 || (size_t)rc >= RqstBufSize - offset) |
504 | 0 | return UPNP_E_BUFFER_TOO_SMALL; |
505 | 0 | offset += (size_t)rc; |
506 | 0 | } |
507 | | |
508 | 0 | if (SearchTarget) { |
509 | 0 | rc = snprintf(RqstBuf + offset, |
510 | 0 | RqstBufSize - offset, |
511 | 0 | "ST: %s\r\n", |
512 | 0 | SearchTarget); |
513 | 0 | if (rc < 0 || (size_t)rc >= RqstBufSize - offset) |
514 | 0 | return UPNP_E_BUFFER_TOO_SMALL; |
515 | 0 | offset += (size_t)rc; |
516 | 0 | } |
517 | | |
518 | 0 | rc = snprintf(RqstBuf + offset, RqstBufSize - offset, "\r\n"); |
519 | 0 | if (rc < 0 || (size_t)rc >= RqstBufSize - offset) |
520 | 0 | return UPNP_E_BUFFER_TOO_SMALL; |
521 | | |
522 | 0 | return UPNP_E_SUCCESS; |
523 | 0 | } |
524 | | #endif /* UPNP_ENABLE_IPV6 */ |
525 | | |
526 | | /*! |
527 | | * \brief |
528 | | */ |
529 | | static void searchExpired( |
530 | | /* [in] . */ |
531 | | void *arg) |
532 | 0 | { |
533 | 0 | int id = ((SsdpSearchExpArg *)arg)->timeoutEventId; |
534 | 0 | int handle = ((SsdpSearchExpArg *)arg)->handle; |
535 | 0 | struct Handle_Info *ctrlpt_info = NULL; |
536 | | |
537 | | /* remove search Target from list and call client back */ |
538 | 0 | ListNode *node = NULL; |
539 | 0 | SsdpSearchArg *item; |
540 | 0 | Upnp_FunPtr ctrlpt_callback; |
541 | 0 | void *cookie = NULL; |
542 | 0 | int found = 0; |
543 | |
|
544 | 0 | HandleLock(__FILE__, __LINE__); |
545 | | |
546 | | /* remove search target from search list */ |
547 | 0 | if (GetHandleInfo(handle, &ctrlpt_info) != HND_CLIENT) { |
548 | 0 | free(arg); |
549 | 0 | HandleUnlock(__FILE__, __LINE__); |
550 | 0 | return; |
551 | 0 | } |
552 | 0 | ctrlpt_callback = ctrlpt_info->Callback; |
553 | 0 | node = ListHead(&ctrlpt_info->SsdpSearchList); |
554 | 0 | while (node != NULL) { |
555 | 0 | item = (SsdpSearchArg *)node->item; |
556 | 0 | if (item->timeoutEventId == id) { |
557 | 0 | free(item->searchTarget); |
558 | 0 | cookie = item->cookie; |
559 | 0 | found = 1; |
560 | 0 | item->searchTarget = NULL; |
561 | 0 | free(item); |
562 | 0 | ListDelNode(&ctrlpt_info->SsdpSearchList, node, 0); |
563 | 0 | break; |
564 | 0 | } |
565 | 0 | node = ListNext(&ctrlpt_info->SsdpSearchList, node); |
566 | 0 | } |
567 | 0 | HandleUnlock(__FILE__, __LINE__); |
568 | |
|
569 | 0 | if (found) |
570 | 0 | ctrlpt_callback(UPNP_DISCOVERY_SEARCH_TIMEOUT, NULL, cookie); |
571 | |
|
572 | 0 | free(arg); |
573 | 0 | } |
574 | | |
575 | | int SearchByTarget(int Hnd, int Mx, char *St, void *Cookie) |
576 | 0 | { |
577 | 0 | char errorBuffer[ERROR_BUFFER_LEN]; |
578 | 0 | int *id = NULL; |
579 | 0 | int ret = 0; |
580 | 0 | char ReqBufv4[BUFSIZE]; |
581 | 0 | #ifdef UPNP_ENABLE_IPV6 |
582 | 0 | char ReqBufv6[BUFSIZE]; |
583 | 0 | char ReqBufv6UlaGua[BUFSIZE]; |
584 | 0 | #endif |
585 | 0 | struct sockaddr_storage __ss_v4; |
586 | 0 | #ifdef UPNP_ENABLE_IPV6 |
587 | 0 | struct sockaddr_storage __ss_v6; |
588 | 0 | #endif |
589 | 0 | struct sockaddr_in *destAddr4 = (struct sockaddr_in *)&__ss_v4; |
590 | 0 | #ifdef UPNP_ENABLE_IPV6 |
591 | 0 | struct sockaddr_in6 *destAddr6 = (struct sockaddr_in6 *)&__ss_v6; |
592 | 0 | #endif |
593 | 0 | SsdpSearchArg *newArg = NULL; |
594 | 0 | SsdpSearchExpArg *expArg = NULL; |
595 | 0 | int timeTillRead = 0; |
596 | 0 | struct Handle_Info *ctrlpt_info = NULL; |
597 | 0 | enum SsdpSearchType requestType; |
598 | 0 | struct in_addr addrv4; |
599 | 0 | int retVal; |
600 | | #ifdef _WIN32 |
601 | | SOCKET max_fd = 0; |
602 | | fd_set wrSet; |
603 | | #else |
604 | 0 | int numCopies; |
605 | 0 | #endif |
606 | | |
607 | | /*ThreadData *ThData; */ |
608 | 0 | ThreadPoolJob job; |
609 | |
|
610 | 0 | if (strlen(gIF_IPV4) > (size_t)0 && |
611 | 0 | !inet_pton(AF_INET, gIF_IPV4, &addrv4)) { |
612 | 0 | return UPNP_E_INVALID_PARAM; |
613 | 0 | } |
614 | | |
615 | 0 | memset(&job, 0, sizeof(job)); |
616 | |
|
617 | 0 | requestType = ssdp_request_type1(St); |
618 | 0 | if (requestType == SSDP_SERROR) |
619 | 0 | return UPNP_E_INVALID_PARAM; |
620 | 0 | UpnpPrintf( |
621 | 0 | UPNP_INFO, SSDP, __FILE__, __LINE__, "Inside SearchByTarget\n"); |
622 | 0 | timeTillRead = Mx; |
623 | 0 | if (timeTillRead < MIN_SEARCH_TIME) |
624 | 0 | timeTillRead = MIN_SEARCH_TIME; |
625 | 0 | else if (timeTillRead > MAX_SEARCH_TIME) |
626 | 0 | timeTillRead = MAX_SEARCH_TIME; |
627 | 0 | retVal = CreateClientRequestPacket( |
628 | 0 | ReqBufv4, sizeof(ReqBufv4), timeTillRead, St, AF_INET); |
629 | 0 | if (retVal != UPNP_E_SUCCESS) |
630 | 0 | return retVal; |
631 | 0 | #ifdef UPNP_ENABLE_IPV6 |
632 | 0 | retVal = CreateClientRequestPacket( |
633 | 0 | ReqBufv6, sizeof(ReqBufv6), timeTillRead, St, AF_INET6); |
634 | 0 | if (retVal != UPNP_E_SUCCESS) |
635 | 0 | return retVal; |
636 | 0 | retVal = CreateClientRequestPacketUlaGua(ReqBufv6UlaGua, |
637 | 0 | sizeof(ReqBufv6UlaGua), |
638 | 0 | timeTillRead, |
639 | 0 | St, |
640 | 0 | AF_INET6); |
641 | 0 | if (retVal != UPNP_E_SUCCESS) |
642 | 0 | return retVal; |
643 | 0 | #endif |
644 | | |
645 | 0 | memset(&__ss_v4, 0, sizeof(__ss_v4)); |
646 | 0 | destAddr4->sin_family = (sa_family_t)AF_INET; |
647 | 0 | inet_pton(AF_INET, SSDP_IP, &destAddr4->sin_addr); |
648 | 0 | destAddr4->sin_port = htons(SSDP_PORT); |
649 | |
|
650 | 0 | #ifdef UPNP_ENABLE_IPV6 |
651 | 0 | memset(&__ss_v6, 0, sizeof(__ss_v6)); |
652 | 0 | destAddr6->sin6_family = (sa_family_t)AF_INET6; |
653 | 0 | inet_pton(AF_INET6, SSDP_IPV6_SITELOCAL, &destAddr6->sin6_addr); |
654 | 0 | destAddr6->sin6_port = htons(SSDP_PORT); |
655 | 0 | destAddr6->sin6_scope_id = gIF_INDEX; |
656 | 0 | #endif |
657 | | |
658 | | /* add search criteria to list */ |
659 | 0 | HandleLock(__FILE__, __LINE__); |
660 | 0 | if (GetHandleInfo(Hnd, &ctrlpt_info) != HND_CLIENT) { |
661 | 0 | HandleUnlock(__FILE__, __LINE__); |
662 | 0 | return UPNP_E_INTERNAL_ERROR; |
663 | 0 | } |
664 | 0 | newArg = (SsdpSearchArg *)malloc(sizeof(SsdpSearchArg)); |
665 | 0 | newArg->searchTarget = strdup(St); |
666 | 0 | newArg->cookie = Cookie; |
667 | 0 | newArg->requestType = requestType; |
668 | |
|
669 | 0 | expArg = (SsdpSearchExpArg *)malloc(sizeof(SsdpSearchExpArg)); |
670 | 0 | expArg->handle = Hnd; |
671 | 0 | id = (int *)&(expArg->timeoutEventId); |
672 | 0 | TPJobInit(&job, (start_routine)searchExpired, expArg); |
673 | 0 | TPJobSetPriority(&job, MED_PRIORITY); |
674 | 0 | TPJobSetFreeFunction(&job, (free_routine)free); |
675 | | /* Schedule a timeout event to remove search Arg */ |
676 | 0 | TimerThreadSchedule( |
677 | 0 | &gTimerThread, timeTillRead, REL_SEC, &job, SHORT_TERM, id); |
678 | 0 | newArg->timeoutEventId = *id; |
679 | 0 | ListAddTail(&ctrlpt_info->SsdpSearchList, newArg); |
680 | 0 | HandleUnlock(__FILE__, __LINE__); |
681 | | /* End of lock */ |
682 | |
|
683 | | #ifdef _WIN32 |
684 | | FD_ZERO(&wrSet); |
685 | | if (gSsdpReqSocket4 != INVALID_SOCKET) { |
686 | | setsockopt(gSsdpReqSocket4, |
687 | | IPPROTO_IP, |
688 | | IP_MULTICAST_IF, |
689 | | (char *)&addrv4, |
690 | | sizeof(addrv4)); |
691 | | FD_SET(gSsdpReqSocket4, &wrSet); |
692 | | max_fd = max(max_fd, gSsdpReqSocket4); |
693 | | } |
694 | | #ifdef UPNP_ENABLE_IPV6 |
695 | | if (gSsdpReqSocket6 != INVALID_SOCKET) { |
696 | | setsockopt(gSsdpReqSocket6, |
697 | | IPPROTO_IPV6, |
698 | | IPV6_MULTICAST_IF, |
699 | | (char *)&gIF_INDEX, |
700 | | sizeof(gIF_INDEX)); |
701 | | FD_SET(gSsdpReqSocket6, &wrSet); |
702 | | max_fd = max(max_fd, gSsdpReqSocket6); |
703 | | } |
704 | | #endif |
705 | | ret = select((int)max_fd + 1, NULL, &wrSet, NULL, NULL); |
706 | | if (ret == -1) { |
707 | | strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN); |
708 | | UpnpPrintf(UPNP_INFO, |
709 | | SSDP, |
710 | | __FILE__, |
711 | | __LINE__, |
712 | | "SSDP_LIB: Error in select(): %s\n", |
713 | | errorBuffer); |
714 | | UpnpCloseSocket(gSsdpReqSocket4); |
715 | | #ifdef UPNP_ENABLE_IPV6 |
716 | | UpnpCloseSocket(gSsdpReqSocket6); |
717 | | #endif |
718 | | return UPNP_E_INTERNAL_ERROR; |
719 | | } |
720 | | #ifdef UPNP_ENABLE_IPV6 |
721 | | if (gSsdpReqSocket6 != INVALID_SOCKET && |
722 | | FD_ISSET(gSsdpReqSocket6, &wrSet)) { |
723 | | int NumCopy = 0; |
724 | | while (NumCopy < NUM_SSDP_COPY) { |
725 | | UpnpPrintf(UPNP_INFO, |
726 | | SSDP, |
727 | | __FILE__, |
728 | | __LINE__, |
729 | | ">>> SSDP SEND M-SEARCH >>>\n%s\n", |
730 | | ReqBufv6UlaGua); |
731 | | sendto(gSsdpReqSocket6, |
732 | | ReqBufv6UlaGua, |
733 | | (int)strlen(ReqBufv6UlaGua), |
734 | | 0, |
735 | | (struct sockaddr *)&__ss_v6, |
736 | | sizeof(struct sockaddr_in6)); |
737 | | imillisleep(SSDP_PAUSE); |
738 | | NumCopy++; |
739 | | } |
740 | | NumCopy = 0; |
741 | | inet_pton(AF_INET6, SSDP_IPV6_LINKLOCAL, &destAddr6->sin6_addr); |
742 | | while (NumCopy < NUM_SSDP_COPY) { |
743 | | UpnpPrintf(UPNP_INFO, |
744 | | SSDP, |
745 | | __FILE__, |
746 | | __LINE__, |
747 | | ">>> SSDP SEND M-SEARCH >>>\n%s\n", |
748 | | ReqBufv6); |
749 | | sendto(gSsdpReqSocket6, |
750 | | ReqBufv6, |
751 | | (int)strlen(ReqBufv6), |
752 | | 0, |
753 | | (struct sockaddr *)&__ss_v6, |
754 | | sizeof(struct sockaddr_in6)); |
755 | | imillisleep(SSDP_PAUSE); |
756 | | NumCopy++; |
757 | | } |
758 | | } |
759 | | #endif /* IPv6 */ |
760 | | if (gSsdpReqSocket4 != INVALID_SOCKET && |
761 | | FD_ISSET(gSsdpReqSocket4, &wrSet)) { |
762 | | int NumCopy = 0; |
763 | | while (NumCopy < NUM_SSDP_COPY) { |
764 | | UpnpPrintf(UPNP_INFO, |
765 | | SSDP, |
766 | | __FILE__, |
767 | | __LINE__, |
768 | | ">>> SSDP SEND M-SEARCH >>>\n%s\n", |
769 | | ReqBufv4); |
770 | | sendto(gSsdpReqSocket4, |
771 | | ReqBufv4, |
772 | | (int)strlen(ReqBufv4), |
773 | | 0, |
774 | | (struct sockaddr *)&__ss_v4, |
775 | | sizeof(struct sockaddr_in)); |
776 | | imillisleep(SSDP_PAUSE); |
777 | | NumCopy++; |
778 | | } |
779 | | } |
780 | | #else /* _WIN32 */ |
781 | 0 | { |
782 | 0 | struct pollfd fds[2]; |
783 | 0 | nfds_t nfds = 0; |
784 | |
|
785 | 0 | if (gSsdpReqSocket4 != INVALID_SOCKET) { |
786 | 0 | setsockopt(gSsdpReqSocket4, |
787 | 0 | IPPROTO_IP, |
788 | 0 | IP_MULTICAST_IF, |
789 | 0 | (char *)&addrv4, |
790 | 0 | sizeof(addrv4)); |
791 | 0 | fds[nfds].fd = gSsdpReqSocket4; |
792 | 0 | fds[nfds].events = POLLOUT; |
793 | 0 | nfds++; |
794 | 0 | } |
795 | 0 | #ifdef UPNP_ENABLE_IPV6 |
796 | 0 | if (gSsdpReqSocket6 != INVALID_SOCKET) { |
797 | 0 | setsockopt(gSsdpReqSocket6, |
798 | 0 | IPPROTO_IPV6, |
799 | 0 | IPV6_MULTICAST_IF, |
800 | 0 | (char *)&gIF_INDEX, |
801 | 0 | sizeof(gIF_INDEX)); |
802 | 0 | fds[nfds].fd = gSsdpReqSocket6; |
803 | 0 | fds[nfds].events = POLLOUT; |
804 | 0 | nfds++; |
805 | 0 | } |
806 | 0 | #endif |
807 | 0 | ret = _poll(fds, nfds, -1); |
808 | 0 | if (ret == -1) { |
809 | 0 | strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN); |
810 | 0 | UpnpPrintf(UPNP_INFO, |
811 | 0 | SSDP, |
812 | 0 | __FILE__, |
813 | 0 | __LINE__, |
814 | 0 | "SSDP_LIB: Error in _poll(): %s\n", |
815 | 0 | errorBuffer); |
816 | 0 | UpnpCloseSocket(gSsdpReqSocket4); |
817 | 0 | #ifdef UPNP_ENABLE_IPV6 |
818 | 0 | UpnpCloseSocket(gSsdpReqSocket6); |
819 | 0 | #endif |
820 | 0 | return UPNP_E_INTERNAL_ERROR; |
821 | 0 | } |
822 | 0 | #ifdef UPNP_ENABLE_IPV6 |
823 | 0 | if (gSsdpReqSocket6 != INVALID_SOCKET && |
824 | 0 | (fds[nfds - 1].revents & POLLOUT)) { |
825 | 0 | for (numCopies = 0; numCopies < NUM_SSDP_COPY; |
826 | 0 | numCopies++) { |
827 | 0 | UpnpPrintf(UPNP_INFO, |
828 | 0 | SSDP, |
829 | 0 | __FILE__, |
830 | 0 | __LINE__, |
831 | 0 | ">>> SSDP SEND M-SEARCH >>>\n%s\n", |
832 | 0 | ReqBufv6UlaGua); |
833 | 0 | sendto(gSsdpReqSocket6, |
834 | 0 | ReqBufv6UlaGua, |
835 | 0 | strlen(ReqBufv6UlaGua), |
836 | 0 | 0, |
837 | 0 | (struct sockaddr *)&__ss_v6, |
838 | 0 | sizeof(struct sockaddr_in6)); |
839 | 0 | imillisleep(SSDP_PAUSE); |
840 | 0 | } |
841 | 0 | inet_pton(AF_INET6, |
842 | 0 | SSDP_IPV6_LINKLOCAL, |
843 | 0 | &destAddr6->sin6_addr); |
844 | 0 | for (numCopies = 0; numCopies < NUM_SSDP_COPY; |
845 | 0 | numCopies++) { |
846 | 0 | UpnpPrintf(UPNP_INFO, |
847 | 0 | SSDP, |
848 | 0 | __FILE__, |
849 | 0 | __LINE__, |
850 | 0 | ">>> SSDP SEND M-SEARCH >>>\n%s\n", |
851 | 0 | ReqBufv6); |
852 | 0 | sendto(gSsdpReqSocket6, |
853 | 0 | ReqBufv6, |
854 | 0 | strlen(ReqBufv6), |
855 | 0 | 0, |
856 | 0 | (struct sockaddr *)&__ss_v6, |
857 | 0 | sizeof(struct sockaddr_in6)); |
858 | 0 | imillisleep(SSDP_PAUSE); |
859 | 0 | } |
860 | 0 | } |
861 | 0 | #endif /* IPv6 */ |
862 | 0 | if (gSsdpReqSocket4 != INVALID_SOCKET && |
863 | 0 | (fds[0].revents & POLLOUT)) { |
864 | 0 | for (numCopies = 0; numCopies < NUM_SSDP_COPY; |
865 | 0 | numCopies++) { |
866 | 0 | UpnpPrintf(UPNP_INFO, |
867 | 0 | SSDP, |
868 | 0 | __FILE__, |
869 | 0 | __LINE__, |
870 | 0 | ">>> SSDP SEND M-SEARCH >>>\n%s\n", |
871 | 0 | ReqBufv4); |
872 | 0 | sendto(gSsdpReqSocket4, |
873 | 0 | ReqBufv4, |
874 | 0 | strlen(ReqBufv4), |
875 | 0 | 0, |
876 | 0 | (struct sockaddr *)&__ss_v4, |
877 | 0 | sizeof(struct sockaddr_in)); |
878 | 0 | imillisleep(SSDP_PAUSE); |
879 | 0 | } |
880 | 0 | } |
881 | 0 | } |
882 | 0 | #endif /* _WIN32 */ |
883 | 0 | return 1; |
884 | 0 | } |
885 | | #endif /* EXCLUDE_SSDP */ |
886 | | #endif /* INCLUDE_CLIENT_APIS */ |
887 | | |
888 | | /* @} SSDPlib */ |