SSDP ,SOAP,GENA

本文介绍SSDP协议在UPnP设备发现中的应用,包括SSDP的工作原理、消息格式及编程实现方法,并提供了一个简单的C语言代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SSDP 是UPNP必不可少一个协议

SSDP协议编程 upnp设备查找方法

  1. int ssdp_discovery() 
  2.     struct sockaddr_in   addrin     ; 
  3.     struct timeval       rtime      ; 
  4.     int                  newsock    ; 
  5.     int                  ret        ; 
  6.     char                 buf[1024]  ; 
  7.     int                  i=0        ; 
  8.     int                  yes=1      ; 
  9.  
  10.     rtime.tv_sec  = 2 ; 
  11.     rtime.tv_usec = 0 ; 
  12.  
  13.     bzero(&addrin, sizeof(addrin)); 
  14.     addrin.sin_family = AF_INET; 
  15.     addrin.sin_addr.s_addr = inet_addr("239.255.255.250"); //htonl(INADDR_ANY) 
  16.     addrin.sin_port = htons(1900); 
  17.  
  18.     newsock=socket(AF_INET,SOCK_DGRAM,0); 
  19.     if( newsock < 0) {perror("1"); return -1;} 
  20.  
  21.     setsockopt( newsock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&rtime, sizeof(struct timeval)); 
  22.     setsockopt( newsock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) ; 
  23.  
  24.     ret=bind( newsock, (struct sockaddr *)&addrin, sizeof(addrin)); 
  25.     if( ret < 0 )   {perror("2"); return -1;} 
  26.  
  27.     while(i<8) 
  28.     { 
  29.         i++; 
  30.         yes=sizeof(struct sockaddr_in); 
  31.  
  32.         memset(buf, 0, sizeof(buf)); 
  33.         ret=recvfrom( newsock, &buf,sizeof(buf), 0, (struct sockaddr *)&addrin, &yes); 
  34.         if( ret < 0 ) {perror("3"); continue;} 
  35.         printf("ip:%s/n",inet_ntoa( addrin.sin_addr)); 
  36.        
  37.  
  38.     } 
  39.  
  40.     close(newsock); 
  41.     return 0; 

SSDP是一个“简单服务发现协议” ,即英文“Simple Service Discovery Protocol的缩写” , 该协议定义了如何在网络上发现网络服务的方法。SSDP也规定了存放在XML文件中的信息格式。SSDP信息的传送是依靠HTTPU和HTTPMU进行的。不论是控制指针,或是UPnP设备,工作中都必然用到SSDP,设备接入网络之后,要利用它向网络广播自己的存在(广播的信息中还有设备位置的描述),以便尽快与对应的控制指针建立联系;控制指针则利用SSDP来搜索自己将要控制的设备在哪里?并且可以排除已经存在的设备和控制指针――只为新近的或尚未“联络”上的双方服务。

控制指针利用SSDP的方式是经由HTTPU发出搜索请求,这种请求可以很详细,能具体到需要什么样的设备以及何种服务。例如:请求对特定的VCR机进行设置时钟的服务。

设备利用SSDP的方式是“收听”来自网络端口的消息,从中发现与自己匹配的信息,一旦找到与自己匹配的信息,经由HTTPMU来发送一个响应信息到控制指针。

运行了UPnP服务的系统实施攻击并非难事,只要向该系统的1900端口发送一个UDP包,其中“LOCATION”域的地址指向另一个系统的Chargen端口,就可能使系统进入一个无限的连接循环,这将会导致受影响系统 CPU 和内存占用率达100%,使远程XP系统完全不能使用而拒绝了服务,只有通过重启后系统才能恢复正常。另外,攻击者只要向某个拥有众多XP主机的网络发送一个伪造的UDP报文,也可能会导致目标网络上所有的XP主机通过所选择的URL,执行了一个攻击的选择。而且当UPNP的部分服务被当作UDP来执行的时候,他产生的所有这些攻击都是难以找到的。

包格式举例
NOTIFY * HTTP/1.1
HOST: 239.255.255.250:1900
CACHE-CONTROL: max-age=10
LOCATION:http://IPADDRESS:PORT/.xml
NT: urn:schemas-upnp-org:device:InternetGatewayDevice:1
NTS: ssdp:alive
SERVER: EEYE/2001 UPnP/1.0 product/1.1
USN: uuid:EEYE
---------------------------------------------------------------------------------------------
SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MAN: "ssdp:discover"
MX: 6
ST: urn:schemas-upnp-org:service:WANIPConnection:1

SOAP:

原理大家都知道,直接看代码:
(注明:在其他网站可能会看到同样内容的文章,不是抄袭,都是本人之作。只不过看那个网站不顺眼,把文章移到这上面来了)


对于一个C程序员来说,写webservice是件比较头痛的事。好在MS下有相应的Toolkit,在VC2003开始,编译环境已经集成了相关功能,
 
点"Project->Add Web Reference",显示一个对话框,把一.个wsdl的地址写上,会自动分析生成相关的C++的 class,然后就可以用了。
 
开源软件,有个很不错的产品叫gSoap,使用起来应该更简单,google上能找出很多文章来介绍。这个产品是多平台的,最简单的使用方法就是下 载其二进制文件,一共有两个,wsdl2h 和soapcpp2,看名字就知道了, 前者把wsdl转成.h,后者再根据这个.h生成相关的class 或函数。用的时候,把生成的几个.h,.c放在自己的工程目录下,再到gSoap源码中找出stdsoap2.h和stdsoap2.cpp,都放进工 程,不用引入任何lib或dll,直接使用就可以了。stdsoap2.cpp这里面已经实现了socket的通信功能。
 
MS自己的soap功能已经淘汰一些不太安全的方式,所以有些webservice解析出错,比如最近用到的一个关于rpc的,MS不支持。gSoap兼容性相对好一些, 可以用。
 
而且,gSoap最大的好处,就是可以跟踪调试源码,比如提交时的HTTP字符串内容,接收到的HTTP字符串,直接可以显示出来,省的再用抓包工具了。在调试方面来讲,gSoap远远超过了MS,想想对com进行调试,没几年的经验是弄不好的。

gSoap在Linux下工作也很不错,使用方法和Windwos下完全一样,先把wsdl解析出来,然后直接使用.h和.c。

最近调试一个webservice,检查的非常苛刻,比如HTTP里的Content-Length,一般写为0就可以了,然后会检查\r\n\r \n,表示结束。但这个服务器端非要知道Content-Length的数值,然后再检查结束符,如果这里写0,直接返回错误。调试了一下gSoap,发 现如果不特意指定的话,Content-Length总是置为0。不过这个问题相对也很好解决,只是在调试代码中,很快就了解了soap底层的一些实现过 程,然后自己用socket仿照也实现了一个简单的。
 
代码演示如下:

//*****************
 
//file: main.c
 
//*****************

#include <winsock.h>
#pragma comment(lib, "ws2_32.lib")

int m_hSocket;
int Connect( const char* szHostAddr, int nPort )  
{  
    SOCKADDR_IN sockAddr;  
    memset(&sockAddr,0,sizeof(sockAddr));  
      
    sockAddr.sin_family = AF_INET;  
    sockAddr.sin_addr.s_addr = inet_addr(szHostAddr);  
      
    if (INADDR_NONE == sockAddr.sin_addr.s_addr)  
    {  
        LPHOSTENT lphost;  
        lphost = gethostbyname(szHostAddr);  
        if (lphost != NULL)  
            sockAddr.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr;  
        else  
        {  
            WSASetLastError(WSAEINVAL);  
            return FALSE;  
        }  
    }  
      
    sockAddr.sin_port = htons((u_short)nPort);
 
     
    if (SOCKET_ERROR == connect(m_hSocket, (LPSOCKADDR)&sockAddr, sizeof(sockAddr)))  
    {  
        //int nError = WSAGetLastError();  
        return -1;  
    }  
    return 0;  
}  

//length应该足够大,timeout单位是秒
int TcpRecvTimeout(int fd, char* buffer, int length, int timeout)
{
 struct timeval TimeOut;
 int recv_lenth = 0;
 //int left_length = length;
 int fd_max=fd+1;
 int sel_ret=0, recv_ret=0;
 fd_set fdset ;
 
 TimeOut.tv_sec = timeout;
 TimeOut.tv_usec = 0;
 
 FD_ZERO(&fdset);
 FD_SET(fd, &fdset);
 sel_ret = select(fd_max, &fdset, NULL, NULL, &TimeOut);
 
 if (sel_ret > 0)
 {
  while (1)
  {
   //while (sel_ret==-1 && errno == EINTR);
   recv_ret = recv(fd, buffer, length, 0/*MSG_NOSIGNAL*/);
   if (recv_ret > 0)
   {
    recv_lenth += recv_ret;
    //left_length -= recv_ret;
    buffer += recv_ret;
   }
   else if (recv_ret == 0)//socket close
   {
    return recv_lenth;
   }
   else if (recv_ret < 0)//SOCKET_ERROR
   {
    return recv_lenth;
   }
  }
 }
 else if (sel_ret < 0)
 {
  return -1;
 }
 else//sel_ret == 0
 {
  //超时
  return 0;
 }
 return recv_lenth;
}

int TcpSend(int fd, char* buffer, int length)
{
 int left_length = length;
 int send_ret = 0;
 
 while (left_length>0)
 {
     send_ret = send(fd, buffer, left_length, 0);
    if (send_ret < 0)
  {
   return -1;
  }
 
  if (send_ret < left_length)
  {
   left_length -= send_ret;
   buffer += send_ret;
  }
  else
  {
   left_length = 0;
   break;
  }
 
  if ((left_sec -= 2) <= 0)
  {
   break;
  }
 }
 
 return (left_length<0) ? length : length-left_length;
}

// 发送字符串,最关键的是组HTTP头,如果这里组不好,服务器响应就有问题
 
int send_xml()
{
 
char* pSend;//要发送的内容,应该是一组xml字符串
 
_snprintf(tmp, 4096,
 "POST /monitorService/services/ConfigServiceSOAP HTTP/1.1\r\n"
 "Content-Type: text/xml\r\n"
 "User-Agent: XML Spy\r\n"
 "SOAPAction: \"http://www.sttri.com.cn/test\test\"\r\n"
 "Host: 127.0.0.1:8081\r\n"
 "Content-Length: %d\r\n"
  "\r\n"
 "\r\n"
 
  %s,
 
  strlen(pSend),pSend);

//组包结束,开始发送
 
return TcpSendTimeout(m_hSocket,tmp, leng);
 
 

}

int main()
{
 int nRet = 0;
 char buff[2048];
 WSADATA wsaData;  
 WSAStartup(MAKEWORD(2,2),&wsaData);
 
 if ((m_hSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("创建套接字失败");
        exit(errno);
    }
    else
    {
        printf("创建套接字成功\n");
    }
 
 
 nRet = Connect("127.0.0.1", 8081);
 
 if (nRet != 0)
 
{
 
    exit(errno);
}

 send_xml();//这里发送字符串

 nRet = TcpRecvTimeout(m_hSocket, buff, 2048, 5);
 close(m_hSocket);
 
 if (nRet > 0)
 
 {
 
      //收到webservice值,开始解析,接收的内容先是HTTP信息头,然后跟着是xml格式的字符串

  }
 
 WSACleanup();

 
 return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值