upnpssdp

How to listen continuosly SSDP response after sending M-SEARCH


I would like to search Sat>IP servers on the network. Sat>IP servers advertise their presence to other Sat>IP servers and clients. I must not continuosly send M-SEARCH messages but that instead it listens to server NOTIFY messages.

After initalizing network settings of my device, I'm sending M-SEARCH message and getting response if there is already any active Sat>IP server. However, I couldn't get any response, If I opens Sat>IP server after sending M-SEARCH message.

Here's my code.

void SatIP::InitDiscoverThread()
{
    if(INVALID_THREAD_CHK == DiscoverThreadChk)
    {
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setstacksize(&attr, PTHREAD_STACK_SIZE);

        printf("InitDiscoverThread pthread_create\n");
        DiscoverThreadChk = PTH_RET_CHK(pthread_create(&DiscoverThreadID, &attr, DiscoverThreadFunc, this));
        if(DiscoverThreadChk != 0)
        {
            ASSERT(0);
        }
    }
}

void SatIP::FinalizeDiscoverThread()
{
    if(INVALID_THREAD_CHK != DiscoverThreadChk)
    {
        printf("FinalizeDiscoverThread pthread_cancel\n");
        pthread_cancel(DiscoverThreadID);
        DiscoverThreadChk = INVALID_THREAD_CHK;
        close(discoverSocket);
    }
}

void *SatIP::DiscoverThreadFunc(void* arg)
{
    SatIP* satip = (SatIP *)arg;
    satip->ListenSSDPResponse();
    pthread_exit(NULL);
}

bool SatIP::SendMSearchMessage()
{
    vSatIPServers.clear();
    FinalizeDiscoverThread();

    const char *searchSatIPDevice = "M-SEARCH * HTTP/1.1\r\n"    \
                                           "HOST: 239.255.255.250:1900\r\n"     \
                                           "MAN: \"ssdp:discover\"\r\n"     \
                                           "MX: 2\r\n"          \
                                           "ST: urn:ses-com:device:SatIPServer:1\r\n\r\n";

    struct sockaddr_in upnpControl, broadcast_addr;
    discoverSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (discoverSocket == INVALID_SOCKET)
    {
        printf("socked failed INVALID_SOCKET\n");
        return false;
    }

    struct timeval tv;
    tv.tv_sec = 1;
    tv.tv_usec = 0;
    if(setsockopt(discoverSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv)) == SOCKET_ERROR)
    {
        printf("setsockopt timeout failed\n");
        close(discoverSocket);
        return false;
    }

    socklen_t ttl = 2;
    if(setsockopt(discoverSocket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) == SOCKET_ERROR)
    {
        printf("setsockopt TTL failed\n");
        close(discoverSocket);
        return false;
    }

    if(setsockopt(discoverSocket, SOL_SOCKET, SO_BROADCAST, searchSatIPDevice, sizeof(searchSatIPDevice)) == SOCKET_ERROR)
    {
        printf("setsockopt broadcast failed\n");
        close(discoverSocket);
        return false;
    }

    upnpControl.sin_family = AF_INET;
    upnpControl.sin_port = htons(0);
    upnpControl.sin_addr.s_addr = INADDR_ANY;
    if (bind(discoverSocket, (sockaddr*)&upnpControl, sizeof(upnpControl)) == SOCKET_ERROR)
    {
        printf("bind failed\n");
        close(discoverSocket);
        return false;
    }

    broadcast_addr.sin_family = AF_INET;
    broadcast_addr.sin_port = htons(1900);
    broadcast_addr.sin_addr.s_addr = inet_addr("239.255.255.250");

    for(int i = 0; i < 3; i++)
    {
        if(sendto(discoverSocket, searchSatIPDevice, strlen(searchSatIPDevice), 0, (sockaddr *)&broadcast_addr, sizeof(broadcast_addr)) == SOCKET_ERROR)
        {
            //printf("sendto failed\n");
            close(discoverSocket);
            return false;
        }
        else
        {
            usleep(10*100);
        }
    }

    InitDiscoverThread();

    return true;
}

void SatIP::ListenSSDPResponse()
{
    while(1)
    {
        char buf[512];
        memset(buf, 0, 512);

        struct sockaddr_in broadcast_addr;
        broadcast_addr.sin_family = AF_INET;
        broadcast_addr.sin_port = htons(1900);
        broadcast_addr.sin_addr.s_addr = inet_addr("239.255.255.250");
        int bcLen = sizeof(broadcast_addr);

        //bool bRet = false;
        while (recvfrom(discoverSocket, buf, 512, 0,  (struct sockaddr*)&broadcast_addr, (socklen_t*)&bcLen) > 0)
        {
            printf("buf:%s\n",buf);
            SATIP_SERVER_DESCRIPTION stServerDesc;
            ostringstream ss;
            if(strstr(buf, "device:SatIPServer"))
            {
                int i = 0;
                char *deviceIp = strstr(buf, "LOCATION:") + 9; // get xml location including server description
                while(deviceIp[i] == ' ') i++; // remove spaces from string
                while(!isspace(deviceIp[i]))
                {
                    ss << deviceIp[i];
                    ++i;
                }
                stServerDesc.location = ss.str().c_str();
                printf("location:%s\n",stServerDesc.location.c_str());

                ss.str(""); // clear ss
                i=0; // clear counter
                deviceIp = strstr(buf, "http://") + 7; // get ip address
                while(deviceIp[i] != ':')
                {
                    ss << deviceIp[i];
                    ++i;
                }
                stServerDesc.ipAddr = ss.str().c_str();
                printf("ipAddr:%s\n", stServerDesc.ipAddr.c_str());

                DownloadDeviceDescription(&stServerDesc);

                stServerDesc.macAddr = GetMACAddressviaIP(stServerDesc.ipAddr);
                printf("macAddr:%s\n", stServerDesc.macAddr.c_str());

                if(IsServerProperToAdd(&stServerDesc))
                    vSatIPServers.push_back(stServerDesc);
                printf("\n");
                //bRet = true;
            }
            memset(buf, 0, 512);
        }
    }
}

How can I fix this issue? Any help would be appreciated.


Solution

  • Listening SSDP notify message is not related to sending M-SEARCH message. Devices like Sat>IP send NOTIFY message to 239.255.255.250 periodically even if you don't send M-SEARCH message. So, you should join a multicast group and receives from the group.

    You can use the listener program in the following link by changing HELLO_PORT as 1900 and HELLO_GROUP as "239.255.255.250".

    http://ntrg.cs.tcd.ie/undergrad/4ba2/multicast/antony/example.html

    /*
    * listener.c -- joins a multicast group and echoes all data it receives from
    *       the group to its stdout...
    *
    * Antony Courtney,  25/11/94
    * Modified by: Frédéric Bastien (25/03/04)
    * to compile without warning and work correctly
    */
    
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <time.h>
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    
    
    #define HELLO_PORT 1900
    #define HELLO_GROUP "239.255.255.250"
    #define MSGBUFSIZE 256
    
    main(int argc, char *argv[])
    {
         struct sockaddr_in addr;
         int fd, nbytes,addrlen;
         struct ip_mreq mreq;
         char msgbuf[MSGBUFSIZE];
    
         u_int yes=1;            /*** MODIFICATION TO ORIGINAL */
    
         /* create what looks like an ordinary UDP socket */
         if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0) {
          perror("socket");
          exit(1);
         }
    
    /**** MODIFICATION TO ORIGINAL */
        /* allow multiple sockets to use the same PORT number */
        if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)) < 0) {
           perror("Reusing ADDR failed");
           exit(1);
           }
    /*** END OF MODIFICATION TO ORIGINAL */
    
         /* set up destination address */
         memset(&addr,0,sizeof(addr));
         addr.sin_family=AF_INET;
         addr.sin_addr.s_addr=htonl(INADDR_ANY); /* N.B.: differs from sender */
         addr.sin_port=htons(HELLO_PORT);
    
         /* bind to receive address */
         if (bind(fd,(struct sockaddr *) &addr,sizeof(addr)) < 0) {
          perror("bind");
          exit(1);
         }
    
         /* use setsockopt() to request that the kernel join a multicast group */
         mreq.imr_multiaddr.s_addr=inet_addr(HELLO_GROUP);
         mreq.imr_interface.s_addr=htonl(INADDR_ANY);
         if (setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) < 0) {
          perror("setsockopt");
          exit(1);
         }
    
         /* now just enter a read-print loop */
         while (1) {
          addrlen=sizeof(addr);
          if ((nbytes=recvfrom(fd,msgbuf,MSGBUFSIZE,0,
                   (struct sockaddr *) &addr,&addrlen)) < 0) {
               perror("recvfrom");
               exit(1);
          }
          puts(msgbuf);
         }
    }