首页 文章

SSDP协议 - 实施

提问于
浏览
5

我正在尝试实现SSDP协议,但我不确定它是如何工作的 . SSDP通过udp发送数据,这很清楚 . 如果控制器连接到网络,它可以搜索具有MSEARCH消息的设备,该消息可以发送到多播地址239.255.255.250:1900 . 每个设备都必须收听此地址并做出响应 . 但我不知道他们是如何回应的 . 我在wireshark中看到他们用单播响应,但我不知道如何确定接收响应的端口 .

编辑 - - - - - - - -

我正在尝试使用spike fuzzing框架编写ssdp fuzzer . 正如我所说,我能够发送正确的数据,但无法收到回复 . 我将尝试粘贴一些尖峰代码并简要说明 . 有Spike结构,它代表要发送的数据(它存储实际数据,大小,协议信息......) . 我删除了一些变量以使其更清晰 .

struct spike {

  /*total size of all data*/
  unsigned long datasize;
  unsigned char *databuf;
  unsigned char *endbuf;
  int fd; /*for holding socket or file information*/
  int proto; /*1 for tcp, 2 for udp*/
  struct sockaddr_in *destsockaddr;
};

现在我通过udp发送数据,并希望通过以下功能接收一些响应

spike_connect_udp(target,port);
spike_send();
s_read_packet();

功能实现:

int 
spike_connect_udp(char * host, int port)
{
  int fd;
  /*ahh, having udpstuff.c makes this stuff easy*/
  fd=udpconnect(host,port);
  if (fd==-1)
    {
      fprintf(stderr,"Couldn't udp connect to target\n");
      return (0);
    }
  current_spike->fd=fd;
  current_spike->proto=2; /*UDP*/
  return 1;
}

int
udpconnect(const char * host,  const unsigned short port )
{
  int sfd = -1;
  struct sockaddr_in addr;
  /* Translate hostname from DNS or IP-address form */

  memset(&addr, 0, sizeof(addr));
  if (!getHostAddress(host, &addr))
    {
      hdebug("can't resolve host or address.\n");
      return -1;
    }
  addr.sin_family = AF_INET;
  addr.sin_port = ntohs(port);

  if ((sfd = socket(AF_INET,  SOCK_DGRAM, 0)) < 0)
    {
      hdebug("Could not create socket!\n");
      return -1;
    }

  /* Now connect! */

  if (connect(sfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
      close(sfd);
      return -1;
    }

  return sfd;

}

int
spike_send()
{
  int retval;

  switch (current_spike->proto) 
    {
    case 1: /*TCP*/
    //deleted, doesnt matter, i am sending via udp
    case 2: /*UDP*/
      //udp_write_data is function from framework
      retval=udp_write_data(current_spike->fd, current_spike->destsockaddr, s_get_size(), s_get_databuf());
      break;

    }

  fflush(0);

  return retval;
}

这很好,并通过udp发送数据 . 现在我想通过open socket current_spike-> fd收到一些回复 . 函数s_read_packet void

s_read_packet()
{
  unsigned char buffer[5000];
  int i;
  int size;
  s_fd_wait();
  printf("Reading packet\n");
  memset(buffer,0x00,sizeof(buffer));
  /what alarm and fcntl does?
  alarm(1);
  fcntl(current_spike->fd, F_SETFL, O_NONBLOCK);
  //this read return error -1 and sets errno to 11 service temporarily unavailable
  size=read(current_spike->fd,buffer,1500);
  fcntl(current_spike->fd, F_SETFL, 0);
  alarm(0);

  for (i=0; i<size; i++)
    {
      if (isprint(buffer[i]))
    printf("%c",buffer[i]);
      else
    printf("[%2.2x]",buffer[i]);
    }

  printf("\nDone with read\n");
}

int
s_fd_wait()
{
  /*this function does a select to wait for
    input on the fd, and if there
    is, returns 1, else 0 */
  int fd;
  fd_set rfds;
  struct timeval tv;
  int retval;

  fd=current_spike->fd;

  /* Watch server_fd (fd 0) to see when it has input. */
  FD_ZERO(&rfds);
  FD_SET(fd, &rfds);
  /* Wait up to zero seconds  .  will this wait forever? not on linux.*/

  /* from man page: timeout is an upper bound on the amount of time
       elapsed before select returns. It may be zero, causing select
       to return immediately.  If timeout is NULL (no timeout), select
       can block indefinitely. */

  /*wait 2 seconds only*/
  tv.tv_sec = TIMEINSECONDS;
  tv.tv_usec = TIMEINUSECONDS;
  //printf("Before select %d:%d\n",TIMEINSECONDS,TIMEINUSECONDS);
  retval = select(fd+1, &rfds, NULL, NULL, &tv);
  /* Don't rely on the value of tv now! */
  //printf("After select retval=%d.\n",retval);
  switch (retval)
   {
     case 0:
       /*Timeout - no packet or keypress*/
        return(0);
        break;
     case -1:
      /* ignore interrupted system calls */
       if (errno != EINTR)
         {
           /*some kind of weird select error. Die. */
           exit(-1);
         }
      /*otherwise we got interrupted, so just return false*/
       return (0);
       break;
     default:
         {
           if (FD_ISSET(fd,&rfds))
             return (1);
           else
            return (0);
         }
   }
}

但是函数s_read_packet没有产生数据......

2 回答

  • 5

    问题是关于TCP / UDP通信的一般原则,而不是关于SSDP细节 . 如果作为UDP网络客户端的控制器打开到特定远程地址的套接字(无论是多播还是单播),则本地地址是适用的本地网络适配器地址和由OS分配的 some 端口号 . 它似乎是随机的,但操作系统会仔细分配它以管理使用相同网络适配器的所有应用程序的唯一性 . 在Wireshark中,你会看到类似的东西:

    IP, Src: 192.168.1.40 Dst: 239.255.255.250
    UDP, Src Port: 42578 Dst Port: 1900
    

    其中 192.168.1.40 是控制器的(传出)网络地址 . 设备必须响应 192.168.1.40:42578 . UDP / IP堆栈实现为您提供了元组 .

    我建议阅读UPnP Device Architecture document . 第1章是关于SSDP的,而且正是您要问的部分:发现,广告和搜索 .

    Edit after code added:

    我没有看到任何"server"代码,任何 bind() . 您正尝试使用相同的描述符进行发送和接收 . 此外,您正在使用 read() ,它是连接资源的通用POSIX函数(当描述符是持久的时) . 一般来说,它看起来像你已经采取TCP客户端的例子,只是改变了协议 . 它赢得了't work with UDP. To receive UDP packets, you must set up a server on your side. UDP doesn' t知道是否有一些数据包"is a response"到其他一些数据包,就像TCP一样 . UDP是无连接的,但你应该已经知道了 .

    我建议阅读泛型UDP原则,尝试在UDP(random good looking start point)中实现非常简单的echo服务器,然后才能在其上堆积SSDP多播 .

  • 5

    为了实现SSDP,您的应用程序需要能够将 NOTIFYM-SEARCH 标头发送到指定的多播地址,并且还应该能够接收这些消息 . 为此,您需要创建一个专用的UDP套接字 .

    以下是如何初始化此类套接字的示例:

    // Structs needed
    struct in_addr localInterface;
    struct sockaddr_in groupSock;
    struct sockaddr_in localSock;
    struct ip_mreq group;   
    
    // Create the Socket
    int udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
    
    // Enable SO_REUSEADDR to allow multiple instances of this application to receive copies of the multicast datagrams.
    int reuse = 1;
    
    setsockopt(udpSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));
    
    // Initialize the group sockaddr structure with a group address of 239.255.255.250 and port 1900.
    memset((char *) &groupSock, 0, sizeof(groupSock));
    
    groupSock.sin_family = AF_INET;
    groupSock.sin_addr.s_addr = inet_addr("239.255.255.250");
    groupSock.sin_port = htons(1900);
    
    // Disable loopback so you do not receive your own datagrams.
    char loopch = 0;
    
    setsockopt(udpSocket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopch, sizeof(loopch));
    
    // Set local interface for outbound multicast datagrams. The IP address specified must be associated with a local, multicast capable interface.
    localInterface.s_addr = inet_addr("192.168.0.1");
    
    setsockopt(udpSocket, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface));
    
    // Bind to the proper port number with the IP address specified as INADDR_ANY.
    memset((char *) &localSock, 0, sizeof(localSock));
    localSock.sin_family = AF_INET;
    localSock.sin_port = htons(1900);
    localSock.sin_addr.s_addr = INADDR_ANY;
    
    bind(udpSocket, (struct sockaddr*)&localSock, sizeof(localSock));
    
    // Join the multicast group on the local interface. Note that this IP_ADD_MEMBERSHIP option must be called for each local interface over which the multicast datagrams are to be received.
    group.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
    group.imr_interface.s_addr = inet_addr("192.168.0.1");
    
    setsockopt(udpSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group));
    

    现在,您可以使用此套接字将数据发送到多播组:

    sendto(udpSocket, message, message_length, 0, (struct sockaddr*)&groupSock, sizeof(groupSock));
    

    接收消息:

    struct sockaddr_in si_other;
    socklen_t slen = sizeof(si_other);
    char buffer[1024];
    
    recvfrom(udpSocket, buffer, 1024, 0, (struct sockaddr *) &si_other, &slen);
    

    要回应特定请求(如上所述),请执行以下操作:

    sendto(udpSocket, message, message_length, 0, (struct sockaddr*)&si_other, sizeof(si_other));
    

    您现在要做的就是创建所需的消息以发送和处理接收的数据 . 假设您向组播组发送 M-SEARCH 请求(如上所述),那么您将从每个设备获得这样的响应:

    HTTP/1.1 200 OK
    SERVER: Linux/2.6.15.2 UPnP/1.0 Mediaserver/1.0
    CACHE-CONTROL: max-age=1200
    LOCATION: http://192.168.0.223:5001/description.xml 
    ST: urn:schemas-upnp-org:device:MediaServer:4
    USN: uuid:550e8400-e29b-11d4-a716-446655440000::urn:schemas-upnp-org:device:MediaServer:4
    Content-Length: 0
    EXT:
    

相关问题