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