1/*
2    Copyright 2004, Broadcom Corporation
3    All Rights Reserved.
4
5    THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
6    KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
7    SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
8    FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
9*/
10
11#include "upnp_dbg.h"
12#include "upnp_osl.h"
13#include "upnp.h"
14
15// #include <bits/socket.h>
16#include <sys/ioctl.h>
17#include <net/if.h>
18#include <netinet/in.h>
19
20extern struct iface *global_lans;
21extern struct net_connection *net_connections;
22extern PDevice root_devices;
23
24extern char *strip_chars(char *str, char *reject);
25extern PDevice find_dev_by_udn(char *udn);
26
27static void advertise_device(PDevice pdev, ssdp_t sstype, struct iface *pif, struct sockaddr *addr, int addrlen);
28static void process_msearch(char *, struct iface *, struct sockaddr *, int);
29static void ssdp_packet( UFILE *, ssdp_t, char * ntst, char *Usn, const char * location, int Duration);
30
31const char* rfc1123_fmt = "%a, %d %b %Y %H:%M:%S GMT";
32
33void send_advertisements(ssdp_t sstype);
34void ssdp_receive(caction_t flag, struct net_connection *nc, struct iface *pif);
35
36/* create a UDP socket bound to the SSDP multicast address. */
37struct net_connection *make_ssdp_socket(struct iface *pif)
38{
39    struct net_connection *c = NULL;
40    int                fd;
41    u_char             ttl;
42
43    fd = socket( AF_INET, SOCK_DGRAM, 0 );
44    UPNP_SOCKET(("%s: socket returns %d\n", __FUNCTION__, fd));
45    if (fd < 0) {
46	goto error;
47    }
48
49    if (!osl_join_multicast(pif, fd, inet_addr(SSDP_IP), SSDP_PORT))
50	goto error;
51
52    // set the multicast TTL
53    ttl = 4;
54    if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) {
55	goto error;
56    }
57
58    c = (struct net_connection *) malloc(sizeof(struct net_connection));
59    if (c == NULL)
60	goto error;
61
62    memset(c, 0, sizeof(struct net_connection));
63
64    c->fd = fd;
65    c->expires = 0;  // never expires.
66    c->func = (CONNECTION_HANDLER) ssdp_receive;
67    c->arg = pif;
68
69    c->next = net_connections;
70    net_connections = (struct net_connection *) c;
71
72    /* a successful return ... */
73    return c;
74
75 error:
76    UPNP_ERROR(("make_ssdp_socket failed\n"));
77    UPNP_SOCKET(("%s: close %d\n", __FUNCTION__, fd));
78    close(fd);
79    fd = -1;
80    return NULL;
81
82}
83
84void ssdp_receive(caction_t flag, struct net_connection *nc, struct iface *pif)
85{
86    char buf[UPNP_BUFSIZE];
87    struct sockaddr_in srcaddr;
88    int nbytes, addrlen;
89
90    UPNP_TRACE((__FUNCTION__ "\n"));
91    if (flag == CONNECTION_RECV) {
92	addrlen = sizeof(srcaddr);
93	memset(&srcaddr, 0, addrlen);
94
95	// memset(buf, 0, sizeof(buf));
96	nbytes = recvfrom(nc->fd, buf, sizeof(buf), 0, (struct sockaddr*)&srcaddr, &addrlen);
97	buf[nbytes] = '\0';
98	if (MATCH_PREFIX(buf, "M-SEARCH * HTTP/1.1")) {
99	    process_msearch(buf, pif, (struct sockaddr *)&srcaddr, addrlen);
100	}
101	else if (MATCH_PREFIX(buf, "NOTIFY "))
102	    ;
103	else {
104	    UPNP_ERROR(("unknown multicast message:\n%s\r\n", buf));
105	}
106    } else if (flag == CONNECTION_DELETE) {
107	close(nc->fd);
108	free(nc);
109    }
110}
111
112static void process_msearch_all(char *st, struct iface *pif, struct sockaddr *srcaddr, int addrlen)
113{
114    PDevice pdev;
115
116    UPNP_TRACE((__FUNCTION__ "\n"));
117    forall_devices(pdev) {
118	advertise_device(pdev, SSDP_REPLY, pif, srcaddr,  addrlen);
119    }
120}
121
122static const char *location(PDevice pdev, struct iface *pif)
123{
124    static char location[100];
125    const unsigned char *bytep = (const unsigned char *) &(pif->inaddr.s_addr);
126
127    snprintf(location, sizeof(location), "http://%d.%d.%d.%d:%d/dyndev/%s",
128	     bytep[0], bytep[1], bytep[2], bytep[3], HTTP_PORT, rootdev(pdev)->udn);
129
130    return location;
131}
132
133static void process_msearch_rootdevice(char *st, struct iface *pif, struct sockaddr *srcaddr, int addrlen)
134{
135    UFILE *upkt;
136    char tmpbuf[100];
137    PDevice pdev;
138
139    UPNP_TRACE((__FUNCTION__ "\n"));
140    if ((upkt = usopen(NULL, 0))) {
141
142	for (pdev = root_devices; pdev; pdev = pdev->next) {
143	    snprintf(tmpbuf, sizeof(tmpbuf), "%s::upnp:rootdevice", pdev->udn);
144
145	    // build the SSDP packet
146	    ssdp_packet( upkt, SSDP_REPLY, st, tmpbuf, location(pdev, pif), SSDP_REFRESH);
147
148	    if (sendto(pif->ssdp_connection->fd, ubuffer(upkt), utell(upkt), 0, srcaddr, addrlen) < 0)
149		UPNP_ERROR(("sendto failed at %s:%d - err %d\n", __FILE__, __LINE__, errno));
150	    uflush(upkt); 	// reset the packet buffer before we build another.
151	}
152	uclose(upkt);
153    }
154}
155
156
157static void process_msearch_uuid(char *st, struct iface *pif, struct sockaddr *srcaddr, int addrlen)
158{
159    UFILE *upkt;
160    PDevice pdev;
161
162    UPNP_TRACE((__FUNCTION__ "\n"));
163    if ((upkt = usopen(NULL, 0))) {
164	if ((pdev = find_dev_by_udn(st)) != NULL) {
165
166	    // build the SSDP packet
167	    ssdp_packet( upkt, SSDP_REPLY, pdev->udn, pdev->udn,
168			 location(pdev, pif), SSDP_REFRESH);
169
170	    if (sendto(pif->ssdp_connection->fd, ubuffer(upkt), utell(upkt), 0, srcaddr, addrlen) < 0)
171		UPNP_ERROR(("sendto failed at %s:%d - err %d\n", __FILE__, __LINE__, errno));
172	}
173
174	uclose(upkt);
175    }
176}
177
178
179static void process_msearch_devicetype(char *st, struct iface *pif, struct sockaddr *addr, int addrlen)
180{
181    UFILE *upkt;
182    PDevice pdev;
183    char tmpbuf[100];
184
185    UPNP_TRACE((__FUNCTION__ "\n"));
186    if ((upkt = usopen(NULL, 0))) {
187	forall_devices(pdev) {
188	    if (strcmp(pdev->template->type, st) == 0) {
189		snprintf(tmpbuf, sizeof(tmpbuf), "%s::%s", pdev->udn, pdev->template->type);
190
191		// build the SSDP packet
192		ssdp_packet( upkt, SSDP_REPLY, pdev->template->type, tmpbuf,
193			     location(pdev, pif), SSDP_REFRESH);
194		if (sendto(pif->ssdp_connection->fd, ubuffer(upkt), utell(upkt), 0, addr, addrlen) < 0)
195		    UPNP_ERROR(("sendto failed at %s:%d - err %d\n", __FILE__, __LINE__, errno));
196		uflush(upkt); 	// reset the packet buffer before we build another.
197	    }
198	}
199
200	uclose(upkt);
201    }
202}
203
204
205static void process_msearch_service(char *st, struct iface *pif, struct sockaddr *addr, int addrlen)
206{
207    UFILE *upkt;
208    char *name = NULL, *p;
209    int namelen = 0;
210    PDevice pdev;
211    PService psvc;
212    char tmpbuf[100];
213
214    UPNP_TRACE((__FUNCTION__ "\n"));
215    p = strstr(st, ":service:");
216    if (p) {
217	name = p+strlen(":service:");
218	namelen = strlen(name);
219    }
220
221    if (namelen) {
222	UPNP_TRACE(("%s: looking for service \"%s\"\n", __FUNCTION__, name));
223	if ((upkt = usopen(NULL, 0))) {
224	    forall_devices(pdev) {
225		const char *loc = location(pdev, pif);
226		forall_services(pdev, psvc) {
227		    UPNP_TRACE(("\tcomparing to \"%s\"\n", psvc->template->name));
228		    if (strlen(psvc->template->name) == namelen && strncmp(psvc->template->name, name, namelen) == 0) {
229			UPNP_TRACE(("\tmatched!\n"));
230			snprintf(tmpbuf, sizeof(tmpbuf), "%s::%s", pdev->udn, st);
231
232			// build the SSDP packet
233			ssdp_packet(upkt, SSDP_REPLY, st, tmpbuf,
234				    loc, SSDP_REFRESH);
235
236			if (sendto(pif->ssdp_connection->fd, ubuffer(upkt), utell(upkt), 0, addr, addrlen) < 0)
237			    UPNP_ERROR(("sendto failed at %s:%d - err %d\n", __FILE__, __LINE__, errno));
238			uflush(upkt); 	// reset the packet buffer before we build another.
239		    } else {
240			UPNP_TRACE(("\tdoes not match\n"));
241		    }
242		}
243	    }
244
245	    uclose(upkt);
246	}
247    } else {
248	UPNP_TRACE(("%s: namelen == 0\n", __FUNCTION__));
249    }
250}
251
252/* process an SSDP M-SEARCH request. */
253static void process_msearch(char *msg, struct iface *pif, struct sockaddr *srcaddr, int addrlen)
254{
255    char *line, *body, *p, *mxend;
256    char *st = NULL;   // the ST: header
257    char *mx = NULL;   // the MX: header
258    char *man = NULL;  // the MAN: header
259    long int mxval;
260
261    UPNP_TRACE((__FUNCTION__ "\n"));
262    if ( (body = strstr(msg, "\r\n\r\n" )) != NULL )
263	body += 4;
264    else if ( (body = strstr(msg, "\r\n" )) != NULL )
265	body += 2;
266    else {
267	UPNP_ERROR(("M-SEARCH is missing blank line after header!\n%s", msg));
268	return;
269    }
270
271    p = msg;
272    while (p != NULL && p < body) {
273	line = strsep(&p, "\r\n");
274	if (IMATCH_PREFIX(line, "ST:")) {
275	    st = strip_chars(&line[3], " \t");
276	} else if (IMATCH_PREFIX(line, "MX:")) {
277	    mx = strip_chars(&line[3], " \t");
278	} else if (IMATCH_PREFIX(line, "MAN:")) {
279	    man = strip_chars(&line[4], " \t");
280	}
281    }
282
283    if (!st || !mx || !man)
284	UPNP_ERROR(("M-SEARCH is missing required ST:, MX:, or MAN: header!\r\n"));
285    else if (!man || (strcmp(man, "\"ssdp:discover\"") != 0))
286	UPNP_ERROR(("M-SEARCH has invalid MAN: header - \"%s\"\r\n", man));
287    else {
288	mxval = strtol(mx, &mxend, 10);
289	if (mxend == mx || *mxend != '\0' || mxval < 0) {
290	    UPNP_ERROR(("M-SEARCH has invalid MX: header - \"%s\"\r\n", mx));
291	    return;
292	}
293	if (strcmp(st, "ssdp:all") == 0) {
294	    // Is client searching for any and all devices and services?
295	    //
296	    process_msearch_all(st, pif, srcaddr, addrlen);
297	}
298	else if (strcmp(st, "upnp:rootdevice") == 0) {
299	    // Is client searching for root devices?
300	    //
301	    process_msearch_rootdevice(st, pif, srcaddr, addrlen);
302	}
303	else if (MATCH_PREFIX(st, "uuid:")) {
304	    // Is client searching for a particular device ID?
305	    //
306	    process_msearch_uuid(st, pif, srcaddr, addrlen);
307	}
308	else if (MATCH_PREFIX(st, "urn:") && strstr(st, ":device:")) {
309	    // Is client searching for a particular device type?
310	    //
311	    process_msearch_devicetype(st, pif, srcaddr, addrlen);
312	}
313	else if (MATCH_PREFIX(st, "urn:") && strstr(st, ":service:")) {
314	    // Is client searching for a particular service type?
315	    //
316	    process_msearch_service(st, pif, srcaddr, addrlen);
317	} else {
318	    UPNP_ERROR(("unrecognized ST: header - \"%s\"\r\n", st));
319	}
320    }
321}
322
323
324/* Should really call this twice to give the UDP packets a better chance of reaching their destination.
325*/
326static void advertise_device(PDevice pdev, ssdp_t sstype, struct iface *pif, struct sockaddr *addr, int addrlen)
327{
328    UFILE *upkt;
329    PService psvc;
330    char tmpbuf[100], svctype[100];
331    const char *loc = location(pdev, pif);
332
333    // create a growable string UFILE.
334    if ((upkt = usopen(NULL, 0))) {
335
336	if (ISROOT(pdev)) {
337	    snprintf(tmpbuf, sizeof(tmpbuf), "%s::upnp:rootdevice", pdev->udn);
338	    ssdp_packet(
339		upkt, sstype, "upnp:rootdevice", tmpbuf,
340		loc, SSDP_REFRESH);
341	    if (sendto(pif->ssdp_connection->fd, ubuffer(upkt), utell(upkt), 0, addr, addrlen) < 0)
342		UPNP_ERROR(("sendto failed at %s:%d - err %d\n", __FILE__, __LINE__, errno));
343	    uflush(upkt); 	// reset the packet buffer before we build another.
344	}
345
346	// advertise device by UDN
347	ssdp_packet( upkt, sstype, pdev->udn, pdev->udn,
348		     loc, SSDP_REFRESH);
349	if (sendto(pif->ssdp_connection->fd, ubuffer(upkt), utell(upkt), 0, addr, addrlen) < 0)
350	    UPNP_ERROR(("sendto failed at %s:%d - err %d\n", __FILE__, __LINE__, errno));
351	uflush(upkt); 	// reset the packet buffer before we build another.
352
353	// advertise device by combination of UDN and type
354	snprintf(tmpbuf, sizeof(tmpbuf), "%s::%s", pdev->udn, pdev->template->type);
355	ssdp_packet( upkt, sstype, pdev->template->type, tmpbuf,
356		     loc, SSDP_REFRESH);
357	if (sendto(pif->ssdp_connection->fd, ubuffer(upkt), utell(upkt), 0, addr, addrlen) < 0)
358	    UPNP_ERROR(("sendto failed at %s:%d - err %d\n", __FILE__, __LINE__, errno));
359	uflush(upkt); 	// reset the packet buffer before we build another.
360
361	// advertise the services...
362	for (psvc = pdev->services; psvc; psvc = psvc->next) {
363	    snprintf(svctype, sizeof(svctype), "urn:%s:service:%s", psvc->template->schema, psvc->template->name);
364	    snprintf(tmpbuf, sizeof(tmpbuf), "%s::%s", pdev->udn, svctype);
365
366	    ssdp_packet( upkt, sstype, svctype, tmpbuf,
367			 loc, SSDP_REFRESH);
368
369	    if (sendto(pif->ssdp_connection->fd, ubuffer(upkt), utell(upkt), 0, addr, addrlen) == -1)
370		UPNP_ERROR(("sendto failed at %s:%d - err %d\n", __FILE__, __LINE__, errno));
371	    uflush(upkt); 	// reset the packet buffer before we build another.
372	}
373
374	// release the growable string UFILE.
375	uclose(upkt);
376    }
377
378}
379
380void periodic_advertiser(timer_t t, ssdp_t sstype)
381{
382    send_advertisements(sstype);
383}
384
385/* Construct the multicast address and send out the appropriate device advertisement packets. */
386void send_advertisements(ssdp_t sstype)
387{
388    struct sockaddr_in addr;
389    struct iface *pif;
390    PDevice pdev;
391
392    addr.sin_family = AF_INET;
393    addr.sin_addr.s_addr = inet_addr(SSDP_IP);
394    addr.sin_port = htons(SSDP_PORT);
395
396    for (pif = global_lans; pif; pif = pif->next) {
397	forall_devices(pdev) {
398	    advertise_device(pdev, sstype, pif, (struct sockaddr *)&addr,  sizeof(addr));
399	}
400    }
401}
402
403
404static void ssdp_packet(
405    UFILE *up, ssdp_t ptype,
406    char * ntst, char *Usn,
407    const char * location, int Duration)
408{
409    extern const char* rfc1123_fmt;
410    time_t now;
411    char date[100];
412
413    UPNP_TRACE((__FUNCTION__ "\n"));
414    switch (ptype) {
415    case SSDP_REPLY:
416	uprintf(up, "HTTP/1.1 200 OK\r\n");
417	uprintf(up, "ST:%s\r\n", ntst);
418	uprintf(up, "USN:%s\r\n",Usn);
419	uprintf(up, "Location: %s\r\n", location);
420	uprintf(up, "Server: Custom/1.0 UPnP/1.0 Proc/Ver\r\n");
421	uprintf(up, "EXT:\r\n");
422	uprintf(up, "Cache-Control:max-age=%d\r\n", Duration);
423	now = time( (time_t*) 0 );
424	(void) strftime( date, sizeof(date), rfc1123_fmt, gmtime( &now ) );
425	uprintf(up, "DATE: %s\r\n", date);
426	break;
427    case SSDP_ALIVE:
428	uprintf(up, "NOTIFY * HTTP/1.1 \r\n");
429	uprintf(up, "HOST: %s:%d\r\n", SSDP_IP, SSDP_PORT);
430	uprintf(up, "CACHE-CONTROL: max-age=%d\r\n", Duration);
431	uprintf(up, "Location: %s\r\n", location);
432	uprintf(up, "NT: %s\r\n", ntst);
433	uprintf(up, "NTS: ssdp:alive\r\n");
434	uprintf(up, "SERVER:%s\r\n", SERVER);
435    uprintf(up, "USN: %s\r\n",Usn);
436	break;
437    case SSDP_BYEBYE:
438	uprintf(up, "NOTIFY * HTTP/1.1 \r\n");
439	uprintf(up, "HOST: %s:%d\r\n", SSDP_IP, SSDP_PORT);
440
441	// Following two header is added to interop with Windows Millenium but this is not
442	// a part of UPNP spec 1.0
443	uprintf(up, "CACHE-CONTROL: max-age=%d\r\n",Duration);
444	uprintf(up, "Location: %s\r\n", location);
445	uprintf(up, "NT: %s\r\n", ntst);
446	uprintf(up, "NTS: ssdp:byebye\r\n");
447    uprintf(up, "USN: %s\r\n",Usn);
448	break;
449    default:
450	UPNP_ERROR(("Bad SSDP packet type supplied - %d\r\n", ptype));
451    }
452
453    /*     uprintf(up, "USN: %s\r\n",Usn); */
454    uprintf(up, "\r\n");
455}
456
457
458