1 /* $Id: openssdpsocket.c,v 1.17 2015/08/06 14:05:37 nanard Exp $ */
2 /* MiniUPnP project
3  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4  * (c) 2006-2018 Thomas Bernard
5  * This software is subject to the conditions detailed
6  * in the LICENCE file provided within the distribution */
7 
8 #include "config.h"
9 
10 #include <string.h>
11 #include <unistd.h>
12 #include <sys/ioctl.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
16 #include <net/if.h>
17 #include <syslog.h>
18 
19 #include "openssdpsocket.h"
20 #include "upnputils.h"
21 #include "minissdpdtypes.h"
22 
23 extern struct lan_addr_list lan_addrs;
24 
25 /* SSDP ip/port */
26 #define SSDP_PORT (1900)
27 #define SSDP_MCAST_ADDR ("239.255.255.250")
28 /* Link Local and Site Local SSDP IPv6 multicast addresses */
29 #define LL_SSDP_MCAST_ADDR ("FF02::C")
30 #define SL_SSDP_MCAST_ADDR ("FF05::C")
31 
32 /**
33  * Add the multicast membership for SSDP on the interface
34  * @param s	the socket
35  * @param ifaddr	the IPv4 address or interface name
36  * @param ipv6	IPv6 or IPv4
37  * return -1 on error, 0 on success */
38 int
AddDropMulticastMembership(int s,struct lan_addr_s * lan_addr,int ipv6,int drop)39 AddDropMulticastMembership(int s, struct lan_addr_s * lan_addr, int ipv6, int drop)
40 {
41 	int ret = 0;
42 	struct ip_mreq imr;	/* Ip multicast membership */
43 #ifdef ENABLE_IPV6
44 	struct ipv6_mreq mr;
45 #else	/* ENABLE_IPV6 */
46 	(void)ipv6;
47 #endif	/* ENABLE_IPV6 */
48 
49 	if(s <= 0)
50 		return -1;	/* nothing to do */
51 #ifdef ENABLE_IPV6
52 	if(ipv6)
53 	{
54 		memset(&mr, 0, sizeof(mr));
55 		inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
56 		mr.ipv6mr_interface = lan_addr->index;
57 		if(setsockopt(s, IPPROTO_IPV6, drop ? IPV6_LEAVE_GROUP : IPV6_JOIN_GROUP,
58 		   &mr, sizeof(struct ipv6_mreq)) < 0)
59 		{
60 			syslog(LOG_ERR, "setsockopt(s=%d, %s)(%s, %s): %m",
61 			       s, drop ? "IPV6_LEAVE_GROUP" : "IPV6_JOIN_GROUP",
62 			       LL_SSDP_MCAST_ADDR,
63 			       lan_addr->ifname);
64 			ret = -1;
65 		}
66 		inet_pton(AF_INET6, SL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
67 		if(setsockopt(s, IPPROTO_IPV6, drop ? IPV6_LEAVE_GROUP : IPV6_JOIN_GROUP,
68 		   &mr, sizeof(struct ipv6_mreq)) < 0)
69 		{
70 			syslog(LOG_ERR, "setsockopt(s=%d, %s)(%s, %s): %m",
71 			       s, drop ? "IPV6_LEAVE_GROUP" : "IPV6_JOIN_GROUP",
72 			       SL_SSDP_MCAST_ADDR,
73 			       lan_addr->ifname);
74 			ret = -1;
75 		}
76 	}
77 	else
78 	{
79 #endif /* ENABLE_IPV6 */
80 		/* setting up imr structure */
81 		imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
82 		imr.imr_interface.s_addr = lan_addr->addr.s_addr;
83 		if(imr.imr_interface.s_addr == INADDR_NONE)
84 		{
85 			syslog(LOG_ERR, "no IPv4 address for interface %s",
86 			       lan_addr->ifname);
87 			return -1;
88 		}
89 
90 		if (setsockopt(s, IPPROTO_IP, drop ? IP_DROP_MEMBERSHIP : IP_ADD_MEMBERSHIP,
91 		    (void *)&imr, sizeof(struct ip_mreq)) < 0)
92 		{
93 			syslog(LOG_ERR, "setsockopt(s=%d, %s)(%s): %m",
94 			       s, drop ? "IP_DROP_MEMBERSHIP" : "IP_ADD_MEMBERSHIP",
95 			       lan_addr->ifname);
96 			return -1;
97 		}
98 #ifdef ENABLE_IPV6
99 	}
100 #endif /* ENABLE_IPV6 */
101 
102 	return ret;
103 }
104 
105 int
OpenAndConfSSDPReceiveSocket(int ipv6,unsigned char ttl)106 OpenAndConfSSDPReceiveSocket(int ipv6, unsigned char ttl)
107 {
108 	int s;
109 	int opt = 1;
110 	unsigned char loopchar = 0;
111 #ifdef ENABLE_IPV6
112 	struct sockaddr_storage sockname;
113 #else /* ENABLE_IPV6 */
114 	struct sockaddr_in sockname;
115 #endif /* ENABLE_IPV6 */
116 	socklen_t sockname_len;
117 	struct lan_addr_s * lan_addr;
118 
119 #ifndef ENABLE_IPV6
120 	if(ipv6) {
121 		syslog(LOG_ERR, "%s: please compile with ENABLE_IPV6 to allow ipv6=1", __func__);
122 		return -1;
123 	}
124 #endif /* ENABLE_IPV6 */
125 
126 #ifdef ENABLE_IPV6
127 	if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0)
128 #else /* ENABLE_IPV6 */
129 	if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
130 #endif /* ENABLE_IPV6 */
131 	{
132 		syslog(LOG_ERR, "socket(udp): %m");
133 		return -1;
134 	}
135 
136 	if(!set_non_blocking(s)) {
137 		syslog(LOG_WARNING, "Failed to set SSDP socket non blocking : %m");
138 	}
139 
140 #ifdef ENABLE_IPV6
141 	memset(&sockname, 0, sizeof(struct sockaddr_storage));
142 	if(ipv6)
143 	{
144 #ifdef IPV6_V6ONLY
145 		if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
146 		              (char *)&opt, sizeof(opt)) < 0)
147 		{
148 			syslog(LOG_WARNING, "setsockopt(IPV6_V6ONLY): %m");
149 		}
150 #endif /* IPV6_V6ONLY */
151 		struct sockaddr_in6 * sa = (struct sockaddr_in6 *)&sockname;
152 		sa->sin6_family = AF_INET6;
153 		sa->sin6_port = htons(SSDP_PORT);
154 		sa->sin6_addr = in6addr_any;
155 		sockname_len = sizeof(struct sockaddr_in6);
156 	}
157 	else
158 	{
159 		struct sockaddr_in * sa = (struct sockaddr_in *)&sockname;
160 		sa->sin_family = AF_INET;
161 		sa->sin_port = htons(SSDP_PORT);
162 #ifdef SSDP_LISTEN_ON_SPECIFIC_ADDR
163 		if(lan_addrs.lh_first != NULL && lan_addrs.lh_first->list.le_next == NULL)
164 		{
165 			sa->sin_addr.s_addr = lan_addrs.lh_first->addr.s_addr;
166 			if(sa->sin_addr.s_addr == INADDR_NONE)
167 			{
168 				syslog(LOG_ERR, "no IPv4 address for interface %s",
169 				       lan_addrs.lh_first->ifname);
170 				close(s);
171 				return -1;
172 			}
173 		}
174 		else
175 #endif /* SSDP_LISTEN_ON_SPECIFIC_ADDR */
176 			sa->sin_addr.s_addr = htonl(INADDR_ANY);
177 		sockname_len = sizeof(struct sockaddr_in);
178 	}
179 #else /* ENABLE_IPV6 */
180 	memset(&sockname, 0, sizeof(struct sockaddr_in));
181     sockname.sin_family = AF_INET;
182     sockname.sin_port = htons(SSDP_PORT);
183 #ifdef SSDP_LISTEN_ON_SPECIFIC_ADDR
184 	if(lan_addrs.lh_first != NULL && lan_addrs.lh_first->list.le_next == NULL)
185 	{
186 		sockname.sin_addr.s_addr = lan_addrs.lh_first->addr.s_addr;
187 		if(sockname.sin_addr.s_addr == INADDR_NONE)
188 		{
189 			syslog(LOG_ERR, "no IPv4 address for interface %s",
190 			       lan_addrs.lh_first->ifname);
191 			close(s);
192 			return -1;
193 		}
194 	}
195 	else
196 #endif /* SSDP_LISTEN_ON_SPECIFIC_ADDR */
197 	sockname.sin_addr.s_addr = htonl(INADDR_ANY);
198 	sockname_len = sizeof(struct sockaddr_in);
199 #endif /* ENABLE_IPV6 */
200 
201 	if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
202 	{
203 		syslog(LOG_WARNING, "setsockopt(IP_MULTICAST_LOOP): %m");
204 	}
205 
206 	if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
207 	{
208 		syslog(LOG_WARNING, "setsockopt(IP_MULTICAST_TTL): %m");
209 	}
210 
211 	if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
212 	{
213 		syslog(LOG_WARNING, "setsockopt(SO_REUSEADDR): %m");
214 	}
215 
216     if(bind(s, (struct sockaddr *)&sockname, sockname_len) < 0)
217 	{
218 		syslog(LOG_ERR, "bind(udp%s): %m", ipv6 ? "6" : "");
219 		close(s);
220 		return -1;
221     }
222 
223 	for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
224 	{
225 		if(AddDropMulticastMembership(s, lan_addr, ipv6, 0) < 0)
226 		{
227 			syslog(LOG_WARNING, "Failed to add IPv%d multicast membership for interface %s.",
228 			       ipv6 ? 6 : 4,
229 			       lan_addr->ifname);
230 		}
231 	}
232 
233 	return s;
234 }
235