1 /* $Id: minissdp.c,v 1.17 2009/08/20 09:10:38 nanard Exp $ */
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2009 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
11 #include <sys/socket.h>
13 #include <netinet/in.h>
14 #include <arpa/inet.h>
17 #include "upnpdescstrings.h"
18 #include "miniupnpdpath.h"
20 #include "upnpglobalvars.h"
22 #include "codelength.h"
25 #define SSDP_PORT (1900)
26 #define SSDP_MCAST_ADDR ("239.255.255.250")
29 AddMulticastMembership(int s
, in_addr_t ifaddr
)
31 struct ip_mreq imr
; /* Ip multicast membership */
33 /* setting up imr structure */
34 imr
.imr_multiaddr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
35 /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
36 imr
.imr_interface
.s_addr
= ifaddr
; /*inet_addr(ifaddr);*/
38 if (setsockopt(s
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, (void *)&imr
, sizeof(struct ip_mreq
)) < 0)
40 syslog(LOG_ERR
, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m");
47 /* Open and configure the socket listening for
48 * SSDP udp packets sent on 239.255.255.250 port 1900 */
50 OpenAndConfSSDPReceiveSocket()
55 struct sockaddr_in sockname
;
57 if( (s
= socket(PF_INET
, SOCK_DGRAM
, 0)) < 0)
59 syslog(LOG_ERR
, "socket(udp): %m");
63 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
64 sockname
.sin_family
= AF_INET
;
65 sockname
.sin_port
= htons(SSDP_PORT
);
66 /* NOTE : it seems it doesnt work when binding on the specific address */
67 /*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
68 sockname
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
69 /*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/
71 if(setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &j
, sizeof(j
)) < 0)
73 syslog(LOG_WARNING
, "setsockopt(udp, SO_REUSEADDR): %m");
77 if(bind(s
, (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
)) < 0)
79 syslog(LOG_ERR
, "bind(udp): %m");
88 if(AddMulticastMembership(s
, lan_addr
[i
].addr
.s_addr
) < 0)
91 "Failed to add multicast membership for address %s",
99 /* open the UDP socket used to send SSDP notifications to
100 * the multicast group reserved for them */
102 OpenAndConfSSDPNotifySocket(in_addr_t addr
)
105 unsigned char loopchar
= 0;
107 struct in_addr mc_if
;
108 struct sockaddr_in sockname
;
110 if( (s
= socket(PF_INET
, SOCK_DGRAM
, 0)) < 0)
112 syslog(LOG_ERR
, "socket(udp_notify): %m");
116 mc_if
.s_addr
= addr
; /*inet_addr(addr);*/
118 if(setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *)&loopchar
, sizeof(loopchar
)) < 0)
120 syslog(LOG_ERR
, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m");
125 if(setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_IF
, (char *)&mc_if
, sizeof(mc_if
)) < 0)
127 syslog(LOG_ERR
, "setsockopt(udp_notify, IP_MULTICAST_IF): %m");
132 if(setsockopt(s
, SOL_SOCKET
, SO_BROADCAST
, &bcast
, sizeof(bcast
)) < 0)
134 syslog(LOG_ERR
, "setsockopt(udp_notify, SO_BROADCAST): %m");
139 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
140 sockname
.sin_family
= AF_INET
;
141 sockname
.sin_addr
.s_addr
= addr
; /*inet_addr(addr);*/
143 if (bind(s
, (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
)) < 0)
145 syslog(LOG_ERR
, "bind(udp_notify): %m");
154 OpenAndConfSSDPNotifySockets(int * sockets
)
155 /*OpenAndConfSSDPNotifySockets(int * sockets,
156 struct lan_addr_s * lan_addr, int n_lan_addr)*/
159 for(i
=0; i
<n_lan_addr
; i
++)
161 sockets
[i
] = OpenAndConfSSDPNotifySocket(lan_addr
[i
].addr
.s_addr
);
176 * response from a LiveBox (Wanadoo)
178 CACHE-CONTROL: max-age=1800
179 DATE: Thu, 01 Jan 1970 04:03:23 GMT
181 LOCATION: http://192.168.0.1:49152/gatedesc.xml
182 SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
184 USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
186 * response from a Linksys 802.11b :
188 Cache-Control:max-age=120
189 Location:http://192.168.5.1:5678/rootDesc.xml
190 Server:NT/5.0 UPnP/1.0
192 USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
196 /* not really an SSDP "announce" as it is the response
197 * to a SSDP "M-SEARCH" */
199 SendSSDPAnnounce2(int s
, struct sockaddr_in sockname
,
200 const char * st
, int st_len
, const char * suffix
,
201 const char * host
, unsigned short port
)
206 * follow guideline from document "UPnP Device Architecture 1.0"
207 * uppercase is recommended.
208 * DATE: is recommended
209 * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
210 * - check what to put in the 'Cache-Control' header
212 l
= snprintf(buf
, sizeof(buf
), "HTTP/1.1 200 OK\r\n"
213 "CACHE-CONTROL: max-age=120\r\n"
216 "USN: %s::%.*s%s\r\n"
218 "SERVER: " MINIUPNPD_SERVER_STRING
"\r\n"
219 "LOCATION: http://%s:%u" ROOTDESC_PATH
"\r\n"
222 uuidvalue
, st_len
, st
, suffix
,
223 host
, (unsigned int)port
);
224 n
= sendto(s
, buf
, l
, 0,
225 (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
) );
228 syslog(LOG_ERR
, "sendto(udp): %m");
232 static const char * const known_service_types
[] =
235 "urn:schemas-upnp-org:device:InternetGatewayDevice:",
236 "urn:schemas-upnp-org:device:WANConnectionDevice:",
237 "urn:schemas-upnp-org:device:WANDevice:",
238 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:",
239 "urn:schemas-upnp-org:service:WANIPConnection:",
240 "urn:schemas-upnp-org:service:WANPPPConnection:",
241 "urn:schemas-upnp-org:service:Layer3Forwarding:",
246 SendSSDPNotifies(int s
, const char * host
, unsigned short port
,
247 unsigned int lifetime
)
249 struct sockaddr_in sockname
;
253 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
254 sockname
.sin_family
= AF_INET
;
255 sockname
.sin_port
= htons(SSDP_PORT
);
256 sockname
.sin_addr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
258 while(known_service_types
[i
])
260 l
= snprintf(bufr
, sizeof(bufr
),
261 "NOTIFY * HTTP/1.1\r\n"
263 "Cache-Control:max-age=%u\r\n"
264 "Location:http://%s:%d" ROOTDESC_PATH
"\r\n"
265 /*"Server:miniupnpd/1.0 UPnP/1.0\r\n"*/
266 "Server: " MINIUPNPD_SERVER_STRING
"\r\n"
271 SSDP_MCAST_ADDR
, SSDP_PORT
,
274 known_service_types
[i
], (i
==0?"":"1"),
275 uuidvalue
, known_service_types
[i
], (i
==0?"":"1") );
278 syslog(LOG_WARNING
, "SendSSDPNotifies(): truncated output");
281 n
= sendto(s
, bufr
, l
, 0,
282 (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
) );
285 syslog(LOG_ERR
, "sendto(udp_notify=%d, %s): %m", s
, host
);
292 SendSSDPNotifies2(int * sockets
,
294 unsigned int lifetime
)
295 /*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
297 unsigned int lifetime)*/
300 for(i
=0; i
<n_lan_addr
; i
++)
302 SendSSDPNotifies(sockets
[i
], lan_addr
[i
].str
, port
, lifetime
);
306 /* ProcessSSDPRequest()
307 * process SSDP M-SEARCH requests and responds to them */
309 ProcessSSDPRequest(int s
, unsigned short port
)
310 /*ProcessSSDPRequest(int s, struct lan_addr_s * lan_addr, int n_lan_addr,
311 unsigned short port)*/
316 struct sockaddr_in sendername
;
318 int lan_addr_index
= 0;
321 len_r
= sizeof(struct sockaddr_in
);
323 n
= recvfrom(s
, bufr
, sizeof(bufr
), 0,
324 (struct sockaddr
*)&sendername
, &len_r
);
327 syslog(LOG_ERR
, "recvfrom(udp): %m");
331 if(memcmp(bufr
, "NOTIFY", 6) == 0)
333 /* ignore NOTIFY packets. We could log the sender and device type */
336 else if(memcmp(bufr
, "M-SEARCH", 8) == 0)
341 while((i
< n
- 1) && (bufr
[i
] != '\r' || bufr
[i
+1] != '\n'))
344 if((i
< n
- 3) && (strncasecmp(bufr
+i
, "st:", 3) == 0))
348 while((*st
== ' ' || *st
== '\t') && (st
< bufr
+ n
))
350 while(st
[st_len
]!='\r' && st
[st_len
]!='\n'
351 && (st
+ st_len
< bufr
+ n
))
353 /*syslog(LOG_INFO, "ST: %.*s", st_len, st);*/
355 /*while(bufr[i+j]!='\r') j++;*/
356 /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
359 /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s:%d",
360 inet_ntoa(sendername.sin_addr),
361 ntohs(sendername.sin_port) );*/
362 if(st
&& (st_len
> 0))
364 /* TODO : doesnt answer at once but wait for a random time */
365 syslog(LOG_INFO
, "SSDP M-SEARCH from %s:%d ST: %.*s",
366 inet_ntoa(sendername
.sin_addr
),
367 ntohs(sendername
.sin_port
),
369 /* find in which sub network the client is */
370 for(i
= 0; i
<n_lan_addr
; i
++)
372 if( (sendername
.sin_addr
.s_addr
& lan_addr
[i
].mask
.s_addr
)
373 == (lan_addr
[i
].addr
.s_addr
& lan_addr
[i
].mask
.s_addr
))
379 /* Responds to request with a device as ST header */
380 for(i
= 0; known_service_types
[i
]; i
++)
382 l
= (int)strlen(known_service_types
[i
]);
383 if(l
<=st_len
&& (0 == memcmp(st
, known_service_types
[i
], l
)))
385 SendSSDPAnnounce2(s
, sendername
,
387 lan_addr
[lan_addr_index
].str
, port
);
391 /* Responds to request with ST: ssdp:all */
392 /* strlen("ssdp:all") == 8 */
393 if(st_len
==8 && (0 == memcmp(st
, "ssdp:all", 8)))
395 for(i
=0; known_service_types
[i
]; i
++)
397 l
= (int)strlen(known_service_types
[i
]);
398 SendSSDPAnnounce2(s
, sendername
,
399 known_service_types
[i
], l
, i
==0?"":"1",
400 lan_addr
[lan_addr_index
].str
, port
);
403 /* responds to request by UUID value */
404 l
= (int)strlen(uuidvalue
);
405 if(l
==st_len
&& (0 == memcmp(st
, uuidvalue
, l
)))
407 SendSSDPAnnounce2(s
, sendername
, st
, st_len
, "",
408 lan_addr
[lan_addr_index
].str
, port
);
413 syslog(LOG_INFO
, "Invalid SSDP M-SEARCH from %s:%d",
414 inet_ntoa(sendername
.sin_addr
), ntohs(sendername
.sin_port
));
419 syslog(LOG_NOTICE
, "Unknown udp packet received from %s:%d",
420 inet_ntoa(sendername
.sin_addr
), ntohs(sendername
.sin_port
));
424 /* This will broadcast ssdp:byebye notifications to inform
425 * the network that UPnP is going down. */
427 SendSSDPGoodbye(int * sockets
, int n_sockets
)
429 struct sockaddr_in sockname
;
434 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
435 sockname
.sin_family
= AF_INET
;
436 sockname
.sin_port
= htons(SSDP_PORT
);
437 sockname
.sin_addr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
439 for(j
=0; j
<n_sockets
; j
++)
441 for(i
=0; known_service_types
[i
]; i
++)
443 l
= snprintf(bufr
, sizeof(bufr
),
444 "NOTIFY * HTTP/1.1\r\n"
448 "NTS:ssdp:byebye\r\n"
450 SSDP_MCAST_ADDR
, SSDP_PORT
,
451 known_service_types
[i
], (i
==0?"":"1"),
452 uuidvalue
, known_service_types
[i
], (i
==0?"":"1"));
453 n
= sendto(sockets
[j
], bufr
, l
, 0,
454 (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
) );
457 syslog(LOG_ERR
, "sendto(udp_shutdown=%d): %m", sockets
[j
]);
465 /* SubmitServicesToMiniSSDPD() :
466 * register services offered by MiniUPnPd to a running instance of
469 SubmitServicesToMiniSSDPD(const char * host
, unsigned short port
) {
470 struct sockaddr_un addr
;
472 unsigned char buffer
[2048];
477 s
= socket(AF_UNIX
, SOCK_STREAM
, 0);
479 syslog(LOG_ERR
, "socket(unix): %m");
482 addr
.sun_family
= AF_UNIX
;
483 strncpy(addr
.sun_path
, minissdpdsocketpath
, sizeof(addr
.sun_path
));
484 if(connect(s
, (struct sockaddr
*)&addr
, sizeof(struct sockaddr_un
)) < 0) {
485 syslog(LOG_ERR
, "connect(\"%s\"): %m", minissdpdsocketpath
);
488 for(i
= 0; known_service_types
[i
]; i
++) {
491 l
= (int)strlen(known_service_types
[i
]);
495 memcpy(p
, known_service_types
[i
], l
);
499 l
= snprintf(strbuf
, sizeof(strbuf
), "%s::%s%s",
500 uuidvalue
, known_service_types
[i
], (i
==0)?"":"1");
502 memcpy(p
, strbuf
, l
);
504 l
= (int)strlen(MINIUPNPD_SERVER_STRING
);
506 memcpy(p
, MINIUPNPD_SERVER_STRING
, l
);
508 l
= snprintf(strbuf
, sizeof(strbuf
), "http://%s:%u" ROOTDESC_PATH
,
509 host
, (unsigned int)port
);
511 memcpy(p
, strbuf
, l
);
513 if(write(s
, buffer
, p
- buffer
) < 0) {
514 syslog(LOG_ERR
, "write(): %m");