I am trying to implement a very simple upnp controller on linux, so that I can control a device which otherwise requires proprietary software.
The docs say that I need to send a UDP multicast request of a specific form (see the "M-SEARCH" string in the code below) to a specific address and port, and that devices will respond by UDP unicast to the address and port I sent from.
I can't make this work. tcpdump shows the UDP multicast request going to the correct address and port, and the format appears correct, but I cannot see a reply.
I am sending from and listening on the loopback interface (the device is on the same machine).
Another upnp controller (i.e. not mine) works properly on the loopback interface.
Can someone suggest what am I doing wrong?
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <unistd.h>
#include <fcntl.h>
#define MAXBUFSIZE 65536
int main(int argc, char ** argv ) {
unsigned char loop;
loop = 0;
unsigned char ttl;
ttl = 4;
int bcast;
bcast = 1;
int sock;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
struct sockaddr_in destadd;
memset(&destadd, 0, sizeof(destadd));
destadd.sin_family = AF_INET;
destadd.sin_port = htons((uint16_t)1900);
if (inet_pton(AF_INET, "239.255.255.250", &destadd.sin_addr) < 1) {
perror("inet_pton dest");
exit(EXIT_FAILURE);
}
struct sockaddr_in interface_addr;
memset(&interface_addr, 0, sizeof(interface_addr));
interface_addr.sin_family = AF_INET;
interface_addr.sin_port = htons(0);
if (inet_pton(AF_INET, "127.0.0.1", &interface_addr.sin_addr) < 1) {
perror("inet_pton interface");
exit(EXIT_FAILURE);
}
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0){
perror("setsockopt loop");
exit(EXIT_FAILURE);
}
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0){
perror("setsockopt ttl");
exit(EXIT_FAILURE);
}
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
(struct in_addr *)&interface_addr.sin_addr,
sizeof(interface_addr.sin_addr)) < 0) {
perror("setsockopt if");
exit(EXIT_FAILURE);
}
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0) {
perror("setsockopt bcast");
exit(EXIT_FAILURE);
}
struct ip_mreqn imr;
memset(&imr, 0, sizeof(imr));
if (inet_pton(AF_INET, "239.255.255.250", &imr.imr_multiaddr.s_addr) < 1) {
perror("inet_pton");
exit(EXIT_FAILURE);
}
inet_pton(AF_INET, "127.0.0.1", (struct in_addr *)&imr.imr_address);
imr.imr_ifindex = 0;
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(void *)&imr, sizeof(imr)) < 0) {
perror("setsockopt addmem");
exit(EXIT_FAILURE);
}
if (bind(sock, (struct sockaddr *)&interface_addr,
sizeof(struct sockaddr_in)) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
char buffer[1024];
strcpy(buffer, "M-SEARCH * HTTP/1.1\r\n"
"Host: 239.255.255.250:1900\r\n"
"Man: \"ssdp:discover\"\r\n"
"ST: upnp:rootdevice\r\n"
"MX: 3\r\n"
"User-Agent: Test/1.0\r\n"
"\r\n");
if (sendto(sock, buffer, strlen(buffer), 0, (struct sockaddr*)&destadd,
sizeof(destadd)) < 0) {
perror("sendto");
exit(EXIT_FAILURE);
}
if (recvfrom(sock, &buffer, sizeof(buffer)-1, 0, NULL, NULL) < 0) {
perror("recvfrom");
exit(EXIT_FAILURE);
}
if (close(sock) < 0) {
perror("close");
exit(EXIT_FAILURE);
}
}