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端口开启监听。然后用测试程序检测。

开始扫描端口。

相关推荐
ZachOn1y10 分钟前
计算机网络:计算机网络概述:网络、互联网与因特网的区别
网络·计算机网络·知识点汇总·考研必备
GOTXX26 分钟前
应用层协议HTTP
linux·网络·网络协议·计算机网络·http·fiddler
小堃学编程8 小时前
计算机网络(十) —— IP协议详解,理解运营商和全球网络
网络·tcp/ip·计算机网络
IPFoxy66610 小时前
探索路由器静态IP的获取方式
网络·智能路由器
menge233311 小时前
VLAN:虚拟局域网
网络·智能路由器
GZ_TOGOGO11 小时前
【2024最新】华为HCIE认证考试流程
大数据·人工智能·网络协议·网络安全·华为
ZachOn1y11 小时前
计算机网络:计算机网络概述 —— 初识计算机网络
网络·计算机网络·知识点汇总·考研必备
三金1213812 小时前
SpringIoC容器的初识
网络·网络协议·rpc
狼头长啸李树身13 小时前
眼儿媚·秋雨绵绵窗暗暗
大数据·网络·服务发现·媒体
SizeTheMoment14 小时前
初识HTTP协议
网络·网络协议·http