关于socket组播和ssdp[修改1.2]

Posted qianbo_insist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于socket组播和ssdp[修改1.2]相关的知识,希望对你有一定的参考价值。

有关于ssdp安全的文章
ssdp攻击和防御

组播 单播和广播

    组播方式解决了单播情况下数据的重复拷贝及带宽的重复占用,也解决了广播方式下带宽资源的浪费,我们知道单播在发送者和每一接收者之间实现点对点网络连接。如果一台发送者同时给多个的接收者传输相同的数据,也必须相应的复制多份的相同数据包。如果有大量主机希望获得数据包的同一份拷贝时,将导致发送者负担沉重、延迟长、网络拥塞;为保证一定的服务质量需增加硬件和带宽。

    组播在发送者和每一接收者之间实现点对多点网络连接。如果一台发送者同时给多个接收者传输相同的数据,也只需复制一份相同的数据包。它提高了数据传送效率,减少了骨干网络出现拥塞的可能性。实际上,组播在局域网里,是由交换机和路由器等硬件等完成一次数据的存储,而转发给其他的加入组播组的成员的。

地址

    224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用。实际上,这是属于D类地址。
224.0.1.0~238.255.255.255 为用户可用的组播地址(临时组地址),全网范围内有效。

upnp

    plug and play 即插即用协议里面使用的 ssdp(imple Service Discovery Protocol)就是属于组播协议,简单服务发现协议,使用组播地址 239.255.255.255,端口1900 。使用wireshark等该端口的包,是可以看见很多包的,因为网关也使用这一协议。实际上,里面大量充斥的是类http协议,也有ssdp ddos 攻击的可能。

Mbone

    Mbone是一种跨越网络。它是一个相互连接的子网和路由器的集合,这些子网和路由器支持IP组播业务流的传送。作为因特网上的虚拟网络,Mbone通过隧道(Tunneling)来旁路因特网上无组播能力的路由器。但是,不是所有路由器能够支持这种网络,所以,这个只能是实验性质,所以,组播,一般只能在局域网里实现。

如何实现

以下用用实例来说明, c# 和 c++来实现ssdp协议的组播,看似简短,但是以下程序真的是一个小却能使用的ssdp搜索程序,加上tcp的接收和发送,就可以做控制点,凡是有开始,从这个简单的c#程序着手,会有收获。

using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace ssdp
{
    class Program
    {
        static void Main(string[] args)
        {
            IPEndPoint LocalEndPoint = new IPEndPoint(IPAddress.Any, 23000);
            IPEndPoint MulticastEndPoint = new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900);

            Socket UdpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            UdpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            UdpSocket.Bind(LocalEndPoint);
            UdpSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(MulticastEndPoint.Address, IPAddress.Any));
            UdpSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 2);
            UdpSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastLoopback, true);

            Console.WriteLine("UDP-Socket setup done...\\r\\n");

            string SearchString = "M-SEARCH * HTTP/1.1\\r\\nHOST:239.255.255.250:1900\\r\\nMAN:\\"ssdp:discover\\"\\r\\nST:ssdp:all\\r\\nMX:3\\r\\n\\r\\n";

            UdpSocket.SendTo(Encoding.UTF8.GetBytes(SearchString), SocketFlags.None, MulticastEndPoint);

            Console.WriteLine("M-Search sent...\\r\\n");

            byte[] ReceiveBuffer = new byte[3200];

            int ReceivedBytes = 0;

            while (true)
            {
                if (UdpSocket.Available > 0)
                {
                    ReceivedBytes = UdpSocket.Receive(ReceiveBuffer, SocketFlags.None);

                    if (ReceivedBytes > 0)
                    {
                        Console.WriteLine(Encoding.UTF8.GetString(ReceiveBuffer,0, ReceivedBytes));
                    }
                }
            }
        }
    }
}

windows 下 c++组播

/*
* author :qianbo
* function:组播类
* 注意生成对象发送和接收是两个,生成根据 SOKM_REV 还是SOKM_SND
*/

#ifndef __GROUPSOCK_H1_
#define __GROUPSOCK_H1_

#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif


#include <stdio.h>
namespace CorePhone
{
	enum{
      SOCKM_REV,
      SOCKM_SND
	};
#define BUFSIZE 1500

	class CGroupSock
	{
	private:
		
		//以下为第二版多播通信,

		WSADATA             _wsd;
		struct sockaddr_in  _local,
			_remote,
			_from;
		SOCKET              _sock, _sockM;
		int                 _len ;//= sizeof(struct sockaddr_in),
		int                 _optval;
		int                 _ret;

		int _Receive_Send; //(0,1)
	private:
		BOOL _isConnected;
	protected:


		//BOOL InitWinsock2();


	public:
	
		BOOL Initialize(int RS);

		BOOL JoinGroup(const char* ip = NULL,int port = 0);

		BOOL SendTo(const char *pBuf,int nlen);

		 int ReceiveData(char *pBuf,int bufferlen);

	public:
		CGroupSock(void);

		~CGroupSock(void);
	};

};
#endif
/*
Author:钱波
email: 418511899@qq.com
wei:   18091589062
func  :类 windows 下组播
time:  2018年5月30日
*/
#include "GroupSock.h"

namespace CorePhone
{
	

	CGroupSock::CGroupSock(void)
	{
		_len = sizeof(struct sockaddr_in);
	}

	CGroupSock::~CGroupSock(void)
	{
		closesocket(_sock);
	}


	BOOL CGroupSock::Initialize(int RS)
	{
		_Receive_Send = RS;
		return 0;
	}



	BOOL CGroupSock::JoinGroup(const char* ip, int port)
	{

		//
		//以下为第二版windows多播通信,加入了根通信
		if ((_sock = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0,
			WSA_FLAG_MULTIPOINT_C_LEAF
			| WSA_FLAG_MULTIPOINT_D_LEAF
			| WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
		{
			printf("socket failed with: %d\\n", WSAGetLastError());
			return FALSE;
		}
		// Bind to the local interface. This is done to receive data.
		_local.sin_family = AF_INET;
		//_local.sin_port   = 0;//htons(port);
		_local.sin_port = 0;
		if (_Receive_Send == SOCKM_REV)
			_local.sin_port = htons(port);

		_local.sin_addr.s_addr = INADDR_ANY;

		char optval = FALSE;    // 屏蔽回播
		if (setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(int)) < 0)
		{
			return FALSE;
		}

		if (bind(_sock, (struct sockaddr *)&_local,
			sizeof(_local)) == SOCKET_ERROR)
		{
			printf("bind failed with: %d\\n", WSAGetLastError());
			closesocket(_sock);
			WSACleanup();
			return FALSE;
		}
		// Setup the SOCKADDR_IN structure describing the multicast 
		// group we want to join
		//
		_remote.sin_family = AF_INET;
		_remote.sin_port = htons(port);
		_remote.sin_addr.s_addr = inet_addr(ip);
		//
		// Change the TTL to something more appropriate
		//
		_optval = 32;
		if (setsockopt(_sock, IPPROTO_IP, IP_MULTICAST_TTL,
			(char *)&_optval, sizeof(int)) == SOCKET_ERROR)
		{
			printf("setsockopt(IP_MULTICAST_TTL) failed: %d\\n",
				WSAGetLastError());
			closesocket(_sock);
			WSACleanup();
			return FALSE;
		}
		// Disable loopback if needed
		//
		_optval = 0;
		if (setsockopt(_sock, IPPROTO_IP, IP_MULTICAST_LOOP,
			(char *)&_optval, sizeof(_optval)) == SOCKET_ERROR)
		{
			printf("setsockopt(IP_MULTICAST_LOOP) failed: %d\\n",
				WSAGetLastError());
			closesocket(_sock);
			WSACleanup();
			return FALSE;
		}
		if ((_sockM = WSAJoinLeaf(_sock, (SOCKADDR *)&_remote,
			sizeof(_remote), NULL, NULL, NULL, NULL,
			JL_BOTH)) == INVALID_SOCKET)
		{
			printf("WSAJoinLeaf() failed: %d\\n", WSAGetLastError());
			closesocket(_sock);
			WSACleanup();
			_isConnected = FALSE;
			return FALSE;
		}
		_isConnected = TRUE;

		return TRUE;



	}

	BOOL CGroupSock::SendTo(const char *pBuf, int len)
	{
		if (sendto(_sock, pBuf, len, 0,
			(struct sockaddr *)&_remote,
			sizeof(_remote)) == SOCKET_ERROR)
		{
			printf("sendto failed with: %d\\n", WSAGetLastError());
			closesocket(_sockM);
			closesocket(_sock);
			WSACleanup();
			return FALSE;
		}
		return TRUE;
	}

	int CGroupSock::ReceiveData(char *pBuf, int BufLen)
	{

		int ret = 0;
		if ((ret = recvfrom(_sock, pBuf, BufLen, 0,
			(struct sockaddr *)&_from, &_len)) == SOCKET_ERROR)
		{
			//printf("recvfrom failed with: %d\\n", WSAGetLastError());
			closesocket(_sockM);
			closesocket(_sock);
			WSACleanup();
			return -1;
		}

		return ret;
	}
}

使用boost 库来接收ssdp 组播

这里是chrome

接收组播消息
从上可以看出网关和chrome都在发ssdp信息,而chrome是发的搜索信息,网关噼里啪啦发了一堆回应信息

#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include "boost/bind.hpp"
//ssdp 协议
const short multicast_port = 1900;

class receiver
{
public:
    receiver(boost::asio::io_context& io_service,
        const boost::asio::ip::address& listen_address,
        const boost::asio::ip::address& multicast_address)
        : socket_(io_service)
    {
        // Create the socket so that multiple may be bound to the same address.
        boost::asio::ip::udp::endpoint listen_endpoint(
            listen_address, multicast_port);
        socket_.open(listen_endpoint.protocol());
        socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
        socket_.bind(listen_endpoint);

        // Join the multicast group.
        socket_.set_option(
            boost::asio::ip::multicast::join_group(multicast_address));

        socket_.async_receive_from(
            boost::asio::buffer(data_, max_length), sender_endpoint_,
            boost::bind(&receiver::handle_receive_from, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
    }

    void handle_receive_from(const boost::system::error_code& error,
        size_t bytes_recvd)
    {
        if (!error)
        {
            std::cout.write(data_, bytes_recvd);
            std::cout << std::endl;

            socket_.async_receive_from(
                boost::asio::buffer(data_, max_length), sender_endpoint_,
                boost::bind(&receiver::handle_receive_from, this,
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));
        }
    }

private:
    boost::asio::ip::udp::socket socket_;
    boost::asio::ip::udp::endpoint sender_endpoint_;
    enum { max_length = 1024 };
    char data_[max_length];
};

int main(int argc, char* argv[])
{
    try
    {
        boost::asio::io_context io_service;
        receiver r(io_service,
            boost::asio::ip::address::from_string("192.168.1.144"),
            boost::asio::ip::address::from_string("239.255.255.250"));
        io_service.run();
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\\n";
    }

    return 0;
}

这一次是接收信息,未完待续,下面一节会讲SSDP及DDOS攻击,以及ssdp设备发现的过程。以及如何攻击这种协议。

以上是关于关于socket组播和ssdp[修改1.2]的主要内容,如果未能解决你的问题,请参考以下文章

ssdp安全-攻击和防御

asio 组播包ssdp

图说单播,组播,广播,选播和地域播

Rxjs: 单播和多播

组播和广播的概念,IGMP的用途

vxlan vs GRE(三层组播和二层组播如何对应起来)