Annotation of embedaddon/miniupnpd/minissdp.c, revision 1.1
1.1 ! misho 1: /* $Id: minissdp.c,v 1.19 2010/09/21 15:31:02 nanard Exp $ */
! 2: /* MiniUPnP project
! 3: * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
! 4: * (c) 2006-2010 Thomas Bernard
! 5: * This software is subject to the conditions detailed
! 6: * in the LICENCE file provided within the distribution */
! 7:
! 8: #include <stdio.h>
! 9: #include <string.h>
! 10: #include <unistd.h>
! 11: #include <sys/socket.h>
! 12: #include <sys/un.h>
! 13: #include <netinet/in.h>
! 14: #include <arpa/inet.h>
! 15: #include <syslog.h>
! 16: #include "config.h"
! 17: #include "upnpdescstrings.h"
! 18: #include "miniupnpdpath.h"
! 19: #include "upnphttp.h"
! 20: #include "upnpglobalvars.h"
! 21: #include "minissdp.h"
! 22: #include "codelength.h"
! 23:
! 24: /* SSDP ip/port */
! 25: #define SSDP_PORT (1900)
! 26: /* Prototypes */
! 27: void ProcessSSDPData(int s, char *bufr, struct sockaddr_in sendername, int n, unsigned short port) ;
! 28:
! 29:
! 30: #define SSDP_MCAST_ADDR ("239.255.255.250")
! 31:
! 32: static int
! 33: AddMulticastMembership(int s, in_addr_t ifaddr)
! 34: {
! 35: struct ip_mreq imr; /* Ip multicast membership */
! 36:
! 37: /* setting up imr structure */
! 38: imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
! 39: /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
! 40: imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/
! 41:
! 42: if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
! 43: {
! 44: syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m");
! 45: return -1;
! 46: }
! 47:
! 48: return 0;
! 49: }
! 50:
! 51: /* Open and configure the socket listening for
! 52: * SSDP udp packets sent on 239.255.255.250 port 1900 */
! 53: int
! 54: OpenAndConfSSDPReceiveSocket()
! 55: {
! 56: int s;
! 57: int i;
! 58: int j = 1;
! 59: struct sockaddr_in sockname;
! 60:
! 61: if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
! 62: {
! 63: syslog(LOG_ERR, "socket(udp): %m");
! 64: return -1;
! 65: }
! 66:
! 67: memset(&sockname, 0, sizeof(struct sockaddr_in));
! 68: sockname.sin_family = AF_INET;
! 69: sockname.sin_port = htons(SSDP_PORT);
! 70: /* NOTE : it seems it doesnt work when binding on the specific address */
! 71: /*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
! 72: sockname.sin_addr.s_addr = htonl(INADDR_ANY);
! 73: /*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/
! 74:
! 75: if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j)) < 0)
! 76: {
! 77: syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m");
! 78: }
! 79:
! 80:
! 81: if(bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
! 82: {
! 83: syslog(LOG_ERR, "bind(udp): %m");
! 84: close(s);
! 85: return -1;
! 86: }
! 87:
! 88: i = n_lan_addr;
! 89: while(i>0)
! 90: {
! 91: i--;
! 92: if(AddMulticastMembership(s, lan_addr[i].addr.s_addr) < 0)
! 93: {
! 94: syslog(LOG_WARNING,
! 95: "Failed to add multicast membership for address %s",
! 96: lan_addr[i].str );
! 97: }
! 98: }
! 99:
! 100: return s;
! 101: }
! 102:
! 103: /* open the UDP socket used to send SSDP notifications to
! 104: * the multicast group reserved for them */
! 105: static int
! 106: OpenAndConfSSDPNotifySocket(in_addr_t addr)
! 107: {
! 108: int s;
! 109: unsigned char loopchar = 0;
! 110: int bcast = 1;
! 111: struct in_addr mc_if;
! 112: struct sockaddr_in sockname;
! 113:
! 114: if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
! 115: {
! 116: syslog(LOG_ERR, "socket(udp_notify): %m");
! 117: return -1;
! 118: }
! 119:
! 120: mc_if.s_addr = addr; /*inet_addr(addr);*/
! 121:
! 122: if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
! 123: {
! 124: syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m");
! 125: close(s);
! 126: return -1;
! 127: }
! 128:
! 129: if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
! 130: {
! 131: syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_IF): %m");
! 132: close(s);
! 133: return -1;
! 134: }
! 135:
! 136: if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
! 137: {
! 138: syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m");
! 139: close(s);
! 140: return -1;
! 141: }
! 142:
! 143: memset(&sockname, 0, sizeof(struct sockaddr_in));
! 144: sockname.sin_family = AF_INET;
! 145: sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
! 146:
! 147: if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
! 148: {
! 149: syslog(LOG_ERR, "bind(udp_notify): %m");
! 150: close(s);
! 151: return -1;
! 152: }
! 153:
! 154: return s;
! 155: }
! 156:
! 157: int
! 158: OpenAndConfSSDPNotifySockets(int * sockets)
! 159: /*OpenAndConfSSDPNotifySockets(int * sockets,
! 160: struct lan_addr_s * lan_addr, int n_lan_addr)*/
! 161: {
! 162: int i, j;
! 163: for(i=0; i<n_lan_addr; i++)
! 164: {
! 165: sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr[i].addr.s_addr);
! 166: if(sockets[i] < 0)
! 167: {
! 168: for(j=0; j<i; j++)
! 169: {
! 170: close(sockets[j]);
! 171: sockets[j] = -1;
! 172: }
! 173: return -1;
! 174: }
! 175: }
! 176: return 0;
! 177: }
! 178:
! 179: /*
! 180: * response from a LiveBox (Wanadoo)
! 181: HTTP/1.1 200 OK
! 182: CACHE-CONTROL: max-age=1800
! 183: DATE: Thu, 01 Jan 1970 04:03:23 GMT
! 184: EXT:
! 185: LOCATION: http://192.168.0.1:49152/gatedesc.xml
! 186: SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
! 187: ST: upnp:rootdevice
! 188: USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
! 189:
! 190: * response from a Linksys 802.11b :
! 191: HTTP/1.1 200 OK
! 192: Cache-Control:max-age=120
! 193: Location:http://192.168.5.1:5678/rootDesc.xml
! 194: Server:NT/5.0 UPnP/1.0
! 195: ST:upnp:rootdevice
! 196: USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
! 197: EXT:
! 198: */
! 199:
! 200: /* not really an SSDP "announce" as it is the response
! 201: * to a SSDP "M-SEARCH" */
! 202: static void
! 203: SendSSDPAnnounce2(int s, struct sockaddr_in sockname,
! 204: const char * st, int st_len, const char * suffix,
! 205: const char * host, unsigned short port)
! 206: {
! 207: int l, n;
! 208: char buf[512];
! 209: /*
! 210: * follow guideline from document "UPnP Device Architecture 1.0"
! 211: * uppercase is recommended.
! 212: * DATE: is recommended
! 213: * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
! 214: * - check what to put in the 'Cache-Control' header
! 215: * */
! 216: l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
! 217: "CACHE-CONTROL: max-age=120\r\n"
! 218: /*"DATE: ...\r\n"*/
! 219: "ST: %.*s%s\r\n"
! 220: "USN: %s::%.*s%s\r\n"
! 221: "EXT:\r\n"
! 222: "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
! 223: "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
! 224: "\r\n",
! 225: st_len, st, suffix,
! 226: uuidvalue, st_len, st, suffix,
! 227: host, (unsigned int)port);
! 228: n = sendto(s, buf, l, 0,
! 229: (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
! 230: syslog(LOG_INFO, "SSDP Announce %d bytes to %s:%d ST: %.*s",n,
! 231: inet_ntoa(sockname.sin_addr),
! 232: ntohs(sockname.sin_port),
! 233: l, buf);
! 234: if(n < 0)
! 235: {
! 236: syslog(LOG_ERR, "sendto(udp): %m");
! 237: }
! 238: }
! 239:
! 240: static const char * const known_service_types[] =
! 241: {
! 242: "upnp:rootdevice",
! 243: "urn:schemas-upnp-org:device:InternetGatewayDevice:",
! 244: "urn:schemas-upnp-org:device:WANConnectionDevice:",
! 245: "urn:schemas-upnp-org:device:WANDevice:",
! 246: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:",
! 247: "urn:schemas-upnp-org:service:WANIPConnection:",
! 248: "urn:schemas-upnp-org:service:WANPPPConnection:",
! 249: "urn:schemas-upnp-org:service:Layer3Forwarding:",
! 250: 0
! 251: };
! 252:
! 253: static void
! 254: SendSSDPNotifies(int s, const char * host, unsigned short port,
! 255: unsigned int lifetime)
! 256: {
! 257: struct sockaddr_in sockname;
! 258: int l, n, i=0;
! 259: char bufr[512];
! 260:
! 261: memset(&sockname, 0, sizeof(struct sockaddr_in));
! 262: sockname.sin_family = AF_INET;
! 263: sockname.sin_port = htons(SSDP_PORT);
! 264: sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
! 265:
! 266: while(known_service_types[i])
! 267: {
! 268: l = snprintf(bufr, sizeof(bufr),
! 269: "NOTIFY * HTTP/1.1\r\n"
! 270: "HOST:%s:%d\r\n"
! 271: "Cache-Control:max-age=%u\r\n"
! 272: "Location:http://%s:%d" ROOTDESC_PATH"\r\n"
! 273: /*"Server:miniupnpd/1.0 UPnP/1.0\r\n"*/
! 274: "Server: " MINIUPNPD_SERVER_STRING "\r\n"
! 275: "NT:%s%s\r\n"
! 276: "USN:%s::%s%s\r\n"
! 277: "NTS:ssdp:alive\r\n"
! 278: "\r\n",
! 279: SSDP_MCAST_ADDR, SSDP_PORT,
! 280: lifetime,
! 281: host, port,
! 282: known_service_types[i], (i==0?"":"1"),
! 283: uuidvalue, known_service_types[i], (i==0?"":"1") );
! 284: if(l>=sizeof(bufr))
! 285: {
! 286: syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output");
! 287: l = sizeof(bufr);
! 288: }
! 289: n = sendto(s, bufr, l, 0,
! 290: (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
! 291: if(n < 0)
! 292: {
! 293: syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s, host);
! 294: }
! 295: i++;
! 296: }
! 297: }
! 298:
! 299: void
! 300: SendSSDPNotifies2(int * sockets,
! 301: unsigned short port,
! 302: unsigned int lifetime)
! 303: /*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
! 304: unsigned short port,
! 305: unsigned int lifetime)*/
! 306: {
! 307: int i;
! 308: for(i=0; i<n_lan_addr; i++)
! 309: {
! 310: SendSSDPNotifies(sockets[i], lan_addr[i].str, port, lifetime);
! 311: }
! 312: }
! 313:
! 314: /* ProcessSSDPRequest()
! 315: * process SSDP M-SEARCH requests and responds to them */
! 316: void
! 317: ProcessSSDPRequest(int s, unsigned short port)
! 318: {
! 319: int n;
! 320: char bufr[1500];
! 321: socklen_t len_r;
! 322: struct sockaddr_in sendername;
! 323: len_r = sizeof(struct sockaddr_in);
! 324:
! 325: n = recvfrom(s, bufr, sizeof(bufr), 0,
! 326: (struct sockaddr *)&sendername, &len_r);
! 327: if(n < 0)
! 328: {
! 329: syslog(LOG_ERR, "recvfrom(udp): %m");
! 330: return;
! 331: }
! 332: ProcessSSDPData(s, bufr, sendername, n, port);
! 333:
! 334: }
! 335:
! 336: void ProcessSSDPData(int s, char *bufr, struct sockaddr_in sendername, int n, unsigned short port) {
! 337: int i, l;
! 338: int lan_addr_index = 0;
! 339: char * st = 0;
! 340: int st_len = 0;
! 341:
! 342:
! 343: if(memcmp(bufr, "NOTIFY", 6) == 0)
! 344: {
! 345: /* ignore NOTIFY packets. We could log the sender and device type */
! 346: return;
! 347: }
! 348: else if(memcmp(bufr, "M-SEARCH", 8) == 0)
! 349: {
! 350: i = 0;
! 351: while(i < n)
! 352: {
! 353: while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
! 354: i++;
! 355: i += 2;
! 356: if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0))
! 357: {
! 358: st = bufr+i+3;
! 359: st_len = 0;
! 360: while((*st == ' ' || *st == '\t') && (st < bufr + n))
! 361: st++;
! 362: while(st[st_len]!='\r' && st[st_len]!='\n'
! 363: && (st + st_len < bufr + n))
! 364: st_len++;
! 365: /*syslog(LOG_INFO, "ST: %.*s", st_len, st);*/
! 366: /*j = 0;*/
! 367: /*while(bufr[i+j]!='\r') j++;*/
! 368: /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
! 369: }
! 370: }
! 371: /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s:%d",
! 372: inet_ntoa(sendername.sin_addr),
! 373: ntohs(sendername.sin_port) );*/
! 374: if(st && (st_len > 0))
! 375: {
! 376: /* TODO : doesnt answer at once but wait for a random time */
! 377: syslog(LOG_INFO, "SSDP M-SEARCH from %s:%d ST: %.*s",
! 378: inet_ntoa(sendername.sin_addr),
! 379: ntohs(sendername.sin_port),
! 380: st_len, st);
! 381: /* find in which sub network the client is */
! 382: for(i = 0; i<n_lan_addr; i++)
! 383: {
! 384: if( (sendername.sin_addr.s_addr & lan_addr[i].mask.s_addr)
! 385: == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
! 386: {
! 387: lan_addr_index = i;
! 388: break;
! 389: }
! 390: }
! 391: /* Responds to request with a device as ST header */
! 392: for(i = 0; known_service_types[i]; i++)
! 393: {
! 394: l = (int)strlen(known_service_types[i]);
! 395: if(l<=st_len && (0 == memcmp(st, known_service_types[i], l)))
! 396: {
! 397: syslog(LOG_INFO, "Single search found");
! 398: SendSSDPAnnounce2(s, sendername,
! 399: st, st_len, "",
! 400: lan_addr[lan_addr_index].str, port);
! 401: break;
! 402: }
! 403: }
! 404: /* Responds to request with ST: ssdp:all */
! 405: /* strlen("ssdp:all") == 8 */
! 406: if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
! 407: {
! 408: syslog(LOG_INFO, "ssdp:all found");
! 409: for(i=0; known_service_types[i]; i++)
! 410: {
! 411: l = (int)strlen(known_service_types[i]);
! 412: SendSSDPAnnounce2(s, sendername,
! 413: known_service_types[i], l, i==0?"":"1",
! 414: lan_addr[lan_addr_index].str, port);
! 415: }
! 416: }
! 417: /* responds to request by UUID value */
! 418: l = (int)strlen(uuidvalue);
! 419: if(l==st_len && (0 == memcmp(st, uuidvalue, l)))
! 420: {
! 421: syslog(LOG_INFO, "ssdp:uuid found");
! 422: SendSSDPAnnounce2(s, sendername, st, st_len, "",
! 423: lan_addr[lan_addr_index].str, port);
! 424: }
! 425: }
! 426: else
! 427: {
! 428: syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s:%d",
! 429: inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
! 430: }
! 431: }
! 432: else
! 433: {
! 434: syslog(LOG_NOTICE, "Unknown udp packet received from %s:%d",
! 435: inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
! 436: }
! 437: }
! 438:
! 439: /* This will broadcast ssdp:byebye notifications to inform
! 440: * the network that UPnP is going down. */
! 441: int
! 442: SendSSDPGoodbye(int * sockets, int n_sockets)
! 443: {
! 444: struct sockaddr_in sockname;
! 445: int n, l;
! 446: int i, j;
! 447: char bufr[512];
! 448:
! 449: memset(&sockname, 0, sizeof(struct sockaddr_in));
! 450: sockname.sin_family = AF_INET;
! 451: sockname.sin_port = htons(SSDP_PORT);
! 452: sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
! 453:
! 454: for(j=0; j<n_sockets; j++)
! 455: {
! 456: for(i=0; known_service_types[i]; i++)
! 457: {
! 458: l = snprintf(bufr, sizeof(bufr),
! 459: "NOTIFY * HTTP/1.1\r\n"
! 460: "HOST:%s:%d\r\n"
! 461: "NT:%s%s\r\n"
! 462: "USN:%s::%s%s\r\n"
! 463: "NTS:ssdp:byebye\r\n"
! 464: "\r\n",
! 465: SSDP_MCAST_ADDR, SSDP_PORT,
! 466: known_service_types[i], (i==0?"":"1"),
! 467: uuidvalue, known_service_types[i], (i==0?"":"1"));
! 468: n = sendto(sockets[j], bufr, l, 0,
! 469: (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
! 470: if(n < 0)
! 471: {
! 472: syslog(LOG_ERR, "SendSSDPGoodbye: sendto(udp_shutdown=%d): %m",
! 473: sockets[j]);
! 474: return -1;
! 475: }
! 476: }
! 477: }
! 478: return 0;
! 479: }
! 480:
! 481: /* SubmitServicesToMiniSSDPD() :
! 482: * register services offered by MiniUPnPd to a running instance of
! 483: * MiniSSDPd */
! 484: int
! 485: SubmitServicesToMiniSSDPD(const char * host, unsigned short port) {
! 486: struct sockaddr_un addr;
! 487: int s;
! 488: unsigned char buffer[2048];
! 489: char strbuf[256];
! 490: unsigned char * p;
! 491: int i, l;
! 492:
! 493: s = socket(AF_UNIX, SOCK_STREAM, 0);
! 494: if(s < 0) {
! 495: syslog(LOG_ERR, "socket(unix): %m");
! 496: return -1;
! 497: }
! 498: addr.sun_family = AF_UNIX;
! 499: strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
! 500: if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
! 501: syslog(LOG_ERR, "connect(\"%s\"): %m", minissdpdsocketpath);
! 502: return -1;
! 503: }
! 504: for(i = 0; known_service_types[i]; i++) {
! 505: buffer[0] = 4;
! 506: p = buffer + 1;
! 507: l = (int)strlen(known_service_types[i]);
! 508: if(i > 0)
! 509: l++;
! 510: CODELENGTH(l, p);
! 511: memcpy(p, known_service_types[i], l);
! 512: if(i > 0)
! 513: p[l-1] = '1';
! 514: p += l;
! 515: l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
! 516: uuidvalue, known_service_types[i], (i==0)?"":"1");
! 517: CODELENGTH(l, p);
! 518: memcpy(p, strbuf, l);
! 519: p += l;
! 520: l = (int)strlen(MINIUPNPD_SERVER_STRING);
! 521: CODELENGTH(l, p);
! 522: memcpy(p, MINIUPNPD_SERVER_STRING, l);
! 523: p += l;
! 524: l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
! 525: host, (unsigned int)port);
! 526: CODELENGTH(l, p);
! 527: memcpy(p, strbuf, l);
! 528: p += l;
! 529: if(write(s, buffer, p - buffer) < 0) {
! 530: syslog(LOG_ERR, "write(): %m");
! 531: return -1;
! 532: }
! 533: }
! 534: close(s);
! 535: return 0;
! 536: }
! 537:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>