UDP端口可达性检测(端口扫描)工具开发

UDP端口可达性检测(端口扫描)工具开发

1、应用场景分析

主机X主机Y 部署在AB双网环境下,两个主机间通过UDP协议进行数据交互。应用程序发送数据时,优先使用A网发送数据,如果A网异常则通过B网发送数据。两个主机应用间没有设置心跳帧 ,所以无法检测到对方UDP端口是否可达。

场景诉求

主机间发送UDP数据前,应能检测对方端口是否可达。如果不可达,则进行A/B网切换。达到动态检测对方网络服务端口状态,实时切换网络链路效果。

①、 主机不可达

场景分析:

  • 主机关机。
  • 防火墙开启策略。
  • 网卡故障、网线连接异常。
  • 网络配置错误。

场景应对策略:

通过PING echo 判断主机是否可达。

②、 端口不可达

主机可达,端口不可达。

场景分析:

  • 服务未启动,端口未监听。
  • 防火墙开启策略。

场景应对策略:

结合ICMP和内核网络协议栈,判断UDP端口是否可达。

2、UDP端口扫描程序开发

①、Windows程序

【1】、 理论知识

略。

【2】、 C++源码

界面控件设计。

对话框初始化部分代码。

c 复制代码
BOOL CUdpDetectDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 将"关于..."菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标


	// 初始化Windows套接字
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		CString strMsg;
		strMsg.Format("WSAStartup失败!");
		return 0;
	}

	// CListCtrl
	DWORD dwStyle = m_ListCtrl.GetExtendedStyle();
	m_ListCtrl.SetExtendedStyle(dwStyle | LVS_EX_FULLROWSELECT );

	m_ListCtrl.InsertColumn(0, _T("主机端口"), LVCFMT_LEFT, 130);
	m_ListCtrl.InsertColumn(1, _T("扫描结果"), LVCFMT_LEFT, 100);
	m_ListCtrl.InsertColumn(2, _T("ICMP反馈"), LVCFMT_LEFT, 350);

	// 默认参数;
	m_EditScanHostIP.SetWindowText("192.168.58.1");
	m_EditScanPortStartNo.SetWindowText("50000");
	m_EditScanEndPortNo.SetWindowText("50005");


	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

端口扫描部分代码。

c 复制代码
//扫描端口;
void CUdpDetectDlg::ScanPort()
{
	CString strScanHost;
	m_EditScanHostIP.GetWindowText(strScanHost);
	CString strScanStartPort;
	m_EditScanPortStartNo.GetWindowText(strScanStartPort);
	CString strScanEndPort;
	m_EditScanEndPortNo.GetWindowText(strScanEndPort);

	int iStartPort = _ttoi(strScanStartPort);
	int iEndPort = _ttoi(strScanEndPort);
	if (iEndPort < iStartPort)  return;

	m_ListCtrl.DeleteAllItems();

	for (int ii = iStartPort; ii <= iEndPort;++ii)
	{
		BOOL isPortOpenF = FALSE;

		char szErrText[256] = { 0 };

		sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
		if (sock < 0) return;

		//设置收发缓冲区大小;
		int iTemp = 1024 * 2;
		setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&iTemp, sizeof(iTemp));
		setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&iTemp, sizeof(iTemp));

		//设置接收超时;
		int iRecvTimeout = 1000;
		setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&iRecvTimeout, sizeof(iRecvTimeout));

		struct sockaddr_in txaddr;

		txaddr.sin_family = AF_INET;
		txaddr.sin_addr.s_addr = inet_addr(strScanHost.GetBuffer(0));
		txaddr.sin_port = htons(ii);

		int addrlen = sizeof(txaddr);

		char txbuff[] = "";
		int txlen = sendto(sock, txbuff, sizeof(txbuff), 0, (sockaddr*)&txaddr, addrlen);

		char rxbuff[24] = { 0 };
		int rxlen = recvfrom(sock, rxbuff, 24, 0, (sockaddr*)&txaddr, &addrlen);
		if (rxlen < 0)
		{
			if (WSAGetLastError() == WSAECONNRESET)
			{
				FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
					NULL, WSAECONNRESET, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),szErrText, 256, NULL);

				isPortOpenF = FALSE;
			}
			else
				isPortOpenF = TRUE;
		}

		CString strHostDesc;
		strHostDesc.Format("%s:%d", strScanHost, ii);

		CString strPortState;
		if (isPortOpenF)
			strPortState.Format("端口可达");
		else
			strPortState.Format("端口不可达");

		int iIndex = m_ListCtrl.GetItemCount();
	    m_ListCtrl.InsertItem(iIndex, strHostDesc);
		m_ListCtrl.SetItemText(iIndex, 1, strPortState);
		m_ListCtrl.SetItemText(iIndex, 2, CString(szErrText));

		closesocket(sock);
	}
}
【3】、 编译程序
【4】、 运行程序

先将目标主机的50001端口开启监听。然后用测试程序检测。

开始扫描端口。

②、 Linux程序

【1】、 理论知识

略。

【2】、 C++源码

UdpScan.cxx

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string>
#include<algorithm>

int main()
{
    printf("*********************************************************************\n");
    printf("*                                                                   *\n");
    printf("*                      Linux 端口扫描工具 V0.1                       *\n");
    printf("*                                                                   *\n");
    printf("*********************************************************************\n");
    
    printf("请输入扫描主机IP地址: ");
    char input[256] = {0};
    char* ptr = fgets(input,256,stdin);
    if(ptr == nullptr) { printf("输入参数异常,结束程序!\n"); }
    std::string strHost = std::string(ptr);
    
    printf("请输入扫描UDP端口范围(比如2000-2500): ");
    ptr = fgets(input,256,stdin);
    if(ptr == nullptr) { printf("输入参数异常,结束程序!\n"); }
    std::string strPort = std::string(ptr);
    
    int iStartPort = 0, iEndPort = 0;
    int pos = strPort.find("-");
    if(pos != std::string::npos)
    {
        std::string strStartPort = strPort.substr(0,pos);
        std::string strEndPort = strPort.substr(pos+1);
        
        iStartPort = std::stoi(strStartPort);
        iEndPort = std::stoi(strEndPort);
    }
     
    if(iEndPort < iStartPort) return 1;
    
    for(int ii = iStartPort; ii <= iEndPort; ++ ii)
    {   
        int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
        //将ICMP端口不可达信息报到应用层协议
        int flag = 1; 
        setsockopt(sock, IPPROTO_IP, IP_RECVERR , &flag,sizeof(int)); 
        
        //设置接收超时
        struct timeval timeout = {0,300};
        setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));
        
        struct sockaddr_in addr;
    	addr.sin_family = AF_INET;
    	addr.sin_addr.s_addr = inet_addr(strHost.c_str());
    	addr.sin_port = htons(ii);
        
         char txbuff[] = ""; 
         int ret = sendto(sock,txbuff,sizeof(txbuff),0,(struct sockaddr*)&addr,sizeof(addr)); 
    
         char rxbuff[24] = { 0 };
         int addrlen = sizeof(addr);
         int rxlen = recvfrom(sock, rxbuff, sizeof(rxbuff), 0, (struct sockaddr *)&addr, &addrlen);
         if (rxlen == -1)
         {
             //剔除回车符
             strHost.erase(std::remove(strHost.begin(), strHost.end(), '\n'), strHost.end());
             
	         if (errno == ECONNREFUSED)
	         {
            	  printf("主机【%s:%d】端口不可达!\n",strHost.c_str(),ii);
	         }
             else
                  printf("主机【%s:%d】端口可达!\n",strHost.c_str(),ii);
         } 
        
         close(sock);
    }

    printf("端口扫描结束!\n");
    
    return 0;
}

代码截图:

【3】、 编译程序
shell 复制代码
g++ -o udpscan UdpScan.cxx -fpermissive -std=c++11 -w -W
【4】、 运行程序

先将目标主机的50001端口开启监听。然后用测试程序检测。

开始扫描端口。

相关推荐
ZZZCY20031 小时前
华为ENSP--IP编址及静态路由配置
网络·华为
EasyCVR1 小时前
私有化部署视频平台EasyCVR宇视设备视频平台如何构建视频联网平台及升级视频转码业务?
大数据·网络·音视频·h.265
hgdlip2 小时前
主IP地址与从IP地址:深入解析与应用探讨
网络·网络协议·tcp/ip
珹洺2 小时前
C语言数据结构——详细讲解 双链表
c语言·开发语言·网络·数据结构·c++·算法·leetcode
科技象限2 小时前
电脑禁用U盘的四种简单方法(电脑怎么阻止u盘使用)
大数据·网络·电脑
东方隐侠安全团队-千里2 小时前
网安瞭望台第3期:俄黑客 TAG - 110组织与密码攻击手段分享
网络·chrome·web安全·网络安全
云计算DevOps-韩老师2 小时前
【网络云计算】2024第47周-每日【2024/11/21】周考-实操题-RAID6实操解析2
网络·云计算
lwprain3 小时前
安装支持ssl的harbor 2.1.4 docker 19.03.8 docker-compose 1.24.0
网络协议·ssl·harbor
软件技术员3 小时前
Let‘s Encrypt SSL证书:acmessl.cn申请免费3个月证书
服务器·网络协议·ssl