/* $Id$ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include #include #include "config.h" #include "upnpdescstrings.h" #include "minidlnapath.h" #include "upnphttp.h" #include "upnpglobalvars.h" #include "minissdp.h" #include "log.h" /* SSDP ip/port */ #define SSDP_PORT (1900) #define SSDP_MCAST_ADDR ("239.255.255.250") static int AddMulticastMembership(int s, in_addr_t ifaddr/*const char * ifaddr*/) { struct ip_mreq imr; /* Ip multicast membership */ /* setting up imr structure */ imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR); /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/ imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0) { DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp, IP_ADD_MEMBERSHIP): %s\n", strerror(errno)); return -1; } return 0; } int OpenAndConfSSDPReceiveSocket() { int s; int i = 1; struct sockaddr_in sockname; if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { DPRINTF(E_ERROR, L_SSDP, "socket(udp): %s\n", strerror(errno)); return -1; } if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) { DPRINTF(E_WARN, L_SSDP, "setsockopt(udp, SO_REUSEADDR): %s\n", strerror(errno)); } memset(&sockname, 0, sizeof(struct sockaddr_in)); sockname.sin_family = AF_INET; sockname.sin_port = htons(SSDP_PORT); /* NOTE : it seems it doesnt work when binding on the specific address */ /*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/ sockname.sin_addr.s_addr = htonl(INADDR_ANY); /*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/ if(bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0) { DPRINTF(E_ERROR, L_SSDP, "bind(udp): %s\n", strerror(errno)); close(s); return -1; } i = n_lan_addr; while(i>0) { i--; if(AddMulticastMembership(s, lan_addr[i].addr.s_addr) < 0) { DPRINTF(E_WARN, L_SSDP, "Failed to add multicast membership for address %s\n", lan_addr[i].str ); } } return s; } /* open the UDP socket used to send SSDP notifications to * the multicast group reserved for them */ static int OpenAndConfSSDPNotifySocket(in_addr_t addr) { int s; unsigned char loopchar = 0; int bcast = 1; struct in_addr mc_if; struct sockaddr_in sockname; if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { DPRINTF(E_ERROR, L_SSDP, "socket(udp_notify): %s\n", strerror(errno)); return -1; } mc_if.s_addr = addr; /*inet_addr(addr);*/ if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0) { DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %s\n", strerror(errno)); close(s); return -1; } if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0) { DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_IF): %s\n", strerror(errno)); close(s); return -1; } if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0) { DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, SO_BROADCAST): %s\n", strerror(errno)); close(s); return -1; } memset(&sockname, 0, sizeof(struct sockaddr_in)); sockname.sin_family = AF_INET; sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/ if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0) { DPRINTF(E_ERROR, L_SSDP, "bind(udp_notify): %s\n", strerror(errno)); close(s); return -1; } return s; } int OpenAndConfSSDPNotifySockets(int * sockets) { int i, j; for(i=0; i1?"1":""), uuidvalue, (st_no>0?"::":""), (st_no>0?known_service_types[st_no]:""), (st_no>1?"1":""), host, (unsigned int)port); //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending M-SEARCH response:\n%s", buf); n = sendto(s, buf, l, 0, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) ); if(n < 0) { DPRINTF(E_ERROR, L_SSDP, "sendto(udp): %s\n", strerror(errno)); } } static void SendSSDPNotifies(int s, const char * host, unsigned short port, unsigned int lifetime) { struct sockaddr_in sockname; int l, n, dup, i=0; char bufr[512]; memset(&sockname, 0, sizeof(struct sockaddr_in)); sockname.sin_family = AF_INET; sockname.sin_port = htons(SSDP_PORT); sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR); for( dup=0; dup<2; dup++ ) { if( dup ) usleep(200000); i=0; while(known_service_types[i]) { l = snprintf(bufr, sizeof(bufr), "NOTIFY * HTTP/1.1\r\n" "HOST:%s:%d\r\n" "CACHE-CONTROL:max-age=%u\r\n" "LOCATION:http://%s:%d" ROOTDESC_PATH"\r\n" "SERVER: " MINIDLNA_SERVER_STRING "\r\n" "NT:%s%s\r\n" "USN:%s%s%s%s\r\n" "NTS:ssdp:alive\r\n" "\r\n", SSDP_MCAST_ADDR, SSDP_PORT, lifetime, host, port, known_service_types[i], (i>1?"1":""), uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") ); if(l>=sizeof(bufr)) { DPRINTF(E_WARN, L_SSDP, "SendSSDPNotifies(): truncated output\n"); l = sizeof(bufr); } //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr); n = sendto(s, bufr, l, 0, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) ); if(n < 0) { DPRINTF(E_ERROR, L_SSDP, "sendto(udp_notify=%d, %s): %s\n", s, host, strerror(errno)); } i++; } } } void SendSSDPNotifies2(int * sockets, unsigned short port, unsigned int lifetime) /*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr, unsigned short port, unsigned int lifetime)*/ { int i; DPRINTF(E_DEBUG, L_SSDP, "Sending SSDP notifies\n"); for(i=0; i 0) ) { DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n", inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port), st_len, st, mx_len, mx, man_len, man); /* find in which sub network the client is */ for(i = 0; i>20); SendSSDPAnnounce2(s, sendername, i, lan_addr[lan_addr_index].str, port); break; } } /* Responds to request with ST: ssdp:all */ /* strlen("ssdp:all") == 8 */ if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8))) { for(i=0; known_service_types[i]; i++) { l = (int)strlen(known_service_types[i]); SendSSDPAnnounce2(s, sendername, i, lan_addr[lan_addr_index].str, port); } } } else { DPRINTF(E_INFO, L_SSDP, "Invalid SSDP M-SEARCH from %s:%d\n", inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port)); } } else { DPRINTF(E_WARN, L_SSDP, "Unknown udp packet received from %s:%d\n", inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port)); } } /* This will broadcast ssdp:byebye notifications to inform * the network that UPnP is going down. */ int SendSSDPGoodbye(int * sockets, int n_sockets) { struct sockaddr_in sockname; int n, l; int i, j; char bufr[512]; memset(&sockname, 0, sizeof(struct sockaddr_in)); sockname.sin_family = AF_INET; sockname.sin_port = htons(SSDP_PORT); sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR); for(j=0; j1?"1":""), uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") ); //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr); n = sendto(sockets[j], bufr, l, 0, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) ); if(n < 0) { DPRINTF(E_ERROR, L_SSDP, "sendto(udp_shutdown=%d): %s\n", sockets[j], strerror(errno)); return -1; } } } return 0; }