0


基于C语言实现DNS服务器程序【100010438】

DNS服务器程序

目的:设计一个DNS服务器程序,读入“域名-IP地址”对照表(一个文件),当客户端查询域名对应的IP地址时,用域名检索该对照表,得到三种检索结果。

基本内容:设计一个DNS服务器程序,读入“域名-IP地址”对照表(一个文件),当客户端查询域名对应的IP地址时,用域名检索该对照表,得到三种检索结果。

实验方法:C语言编程。

系统的功能设计

读入“IP地址-域名”对照表,当客户端查询域名对应的IP地址时,用域名检索该对照表。

不良网站拦截功能

检索结果为ip地址0.0.0.0,则向客户端返回“域名不存在”的报错消息

服务器功能

检索结果为普通IP地址,则向客户返回这个地址

中继功能

表中未检到该域名,则向因特网DNS服务器发出查询,并将结果返给客户端

需要进行消息ID的转换,以满足多个计算机上的客户端会同时查询。

主要的数据结构:

//DNS报文首部 12字节
typedef struct DNSHeader
    {
    unsigned short ID; //标志
    unsigned short Flags; //标识
    unsigned short QuestionNum;  //问题数
    unsigned short AnswerNum; //资源记录数
    unsigned short AuthorNum; //授权资源记录数
    unsigned short AdditionNum; //额外资源记录数
    } DNSHDR, * pDNSHDR;
//DNS域名解析表结构
typedef struct translate
    {
    char * IP;                        //IP地址
    char * domain;                    //域名
    } Translate;
//ID转换表结构
typedef struct IDChange
    {
    unsigned short oldID;            //原有ID
    BOOL done;                        //标记是否完成解析
    SOCKADDR_IN client;                //请求者套接字地址
    } IDTransform;

模块划分(函数划分)

//加载本地txt文件
int InitialDNSTable(char* path)
    
//获取DNS请求中的域名
void GetUrl(char* recvbuf, int recvnum)
    
//判断能不能在本中找到DNS请求中的域名,找到返回下标
int IsFind(char* url, int num)
    
//将请求ID转换为新的ID,并将信息写入ID转换表中
unsigned short ReplaceNewID(unsigned short OldID, SOCKADDR_IN temp, BOOL ifdone)
    
//打印 时间 newID 功能 域名 IP
void PrintInfo(unsigned short newID, int find)
    
//socket通信模块,内置于主函数
int main()

软件流程图

测试用例以及运行结果

首先将本地连接中的DNS服务器地址改为本地的127.0.0.1

此时无法正常使用网页。

开启设计好的DNS服务器

即可正常浏览网页。

而且能正常PING通网址:

此时服务器端显示信息:

此外还能正常使用nslookup命令查ip地址:

本地解析表存在的记录且并不屏蔽

服务器信息:

本地解析表存在记录但返回IP为0.0.0.0,即屏蔽:

服务器信息:

本地没有记录,转发出去:

服务端信息:

调试中遇到并解决的问题

在代码编写过程中,踩了很多坑。

  • 初次采用了两个socket共用一个端口,天真地将其简单化为收——发的简单循环过程中。在后续的调试过程中,认识到两个socket、共用端口造成的阻塞和丢失消息,具体表现为:当客户端socket从客户端拿到消息、发给服务端并等待服务端回复时,此时处于阻塞状态;而服务端socket也处于阻塞状态,所以服务端没有办法快速地处理客户端信息并给予应答,在这段没有办法应答的时间内,很有可能客户端判断超时,效率低下;且客户端socket在漫长地等待服务端socket回信的同时,客户端这边源源不断地发起请求都会被客户端socket错失掉。发现这个问题后,我们先是尝试了加入其它socket,具体尝试为客户端一个收socket、一个发socket;服务端一个收socket、一个发socket,但这样的方法在实现中出现了大量的bug,迫于时间压力只能放弃。我们又尝试了利用多线程。但过于频繁的请求发起使用多线程,又会造成大量问题。在验收前期,我们通过讨论才意识到:我们其实可以使用一个socket,在收发过程中判断报文的类型是请求还是响应,同时记录报文的ID和IP还有PORT,然后对其进行精准转发,这样其实就可以更好地解决上述问题。但是,由于时间压力和自身编程能力水平的限制,我们最终只能采取非阻塞对这种情况进行稍许的改善。
  • 编程优秀习惯的养成。本次课程设计要求全C语言,熊宇同学编写完程序后交给马锐文同学修订,马锐文同学发现了熊宇同学代码中使用了部分C++内容,例如string库、iomanip库。迅速对其修改并检查对程序的影响,充分发挥了团队协作的优势。

源代码(613行)

“definition.h”

#ifndef DEFINITION_H_INCLUDED
#define DEFINITION_H_INCLUDED

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <iomanip>
#include <WinSock2.h>
#include <windows.h>
#include <time.h>

#define BUFSIZE 1024 //最大报文缓存大小
#define PORT 53   //53端口号
#define DEF_DNS_ADDRESS "223.5.5.5"    //ipconfig/all 得知外部服务器dns地址
#define LOCAL_DNS_ADDRESS "127.0.0.1" //本地DNS服务器地址
#define AMOUNT 1500//最大ID转换表大小
#define NOTFOUND 32767 //没有找到
#define LENGTHOFURL 64 //0~63

//DNS报文首部 12字节
typedef struct DNSHeader
{
    unsigned short ID; //标志
    unsigned short Flags; //标识
    unsigned short QuestionNum;  //问题数
    unsigned short AnswerNum; //资源记录数
    unsigned short AuthorNum; //授权资源记录数
    unsigned short AdditionNum; //额外资源记录数
} DNSHDR, * pDNSHDR;

//DNS域名解析表结构
typedef struct translate
{
    char * IP;                        //IP地址
    char * domain;                    //域名
} Translate;

//ID转换表结构
typedef struct IDChange
{
    unsigned short oldID;            //原有ID
    BOOL done;                        //标记是否完成解析
    SOCKADDR_IN client;                //请求者套接字地址
} IDTransform;

#endif // DEFINITION_H_INCLUDED

“functions.h”

#ifndef FUNCTIONS_H_INCLUDED
#define FUNCTIONS_H_INCLUDED
#pragma once

int InitialDNSTable(char* path); //加载本地txt文件
void GetUrl(char* recvbuf, int recvnum); //获取DNS请求中的域名
int IsFind(char* url, int num);//判断能不能在本中找到DNS请求中的域名,找到返回下标
unsigned short ReplaceNewID(unsigned short OldID, SOCKADDR_IN temp, BOOL ifdone); //将请求ID转换为新的ID,并将信息写入ID转换表中
void PrintInfo(unsigned short newID, int find); //打印 时间 newID 功能 域名 IP

#endif // FUNCTIONS_H_INCLUDED

<functions.c>

#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <windows.h>
#include <time.h>
#include <process.h>
#include "definition.h"

#pragma  comment(lib, "Ws2_32.lib") //加载 ws2_32.dll

extern Translate DNSTable[AMOUNT];        //DNS域名解析表
extern IDTransform IDTransTable[AMOUNT];    //ID转换表
extern int IDcount;                    //转换表中的条目个数
extern char Url[LENGTHOFURL];                    //域名
extern SYSTEMTIME TimeOfSys;                     //系统时间
extern int Day, Hour, Minute, Second, Milliseconds;//保存系统时间的变量

//加载本地txt文件
int InitialDNSTable(char* path)
{
    int i = 0, j = 0;
    int num = 0;
    char* Temp[AMOUNT];//char型指针1500数组
    FILE* fp = fopen(path, "ab+");
    if (!fp)
    {
        printf("Open file failed.\n");
        exit(-1);
    }
    char* reac;
    while (i < AMOUNT - 1)//实现把每一行分开的操作
    {
        Temp[i] = (char*)malloc(sizeof(char)*200);
        //Temp[200];
        //fscanf(fp, "%*c%*[^\n]", IPTemp[i]);
        if (fgets(Temp[i],1000,fp)== NULL)//如果错误或者读到结束符,就返回NULL;
            break;
        else
        {
            //reac = strchr(Temp[i], '\n');          //查找换行符
            //if (reac)                            //如果find不为空指针
            //    *reac = '\0';
            ;//printf("%s", Temp[i]);
        }
        i++;
    }
    if (i == AMOUNT - 1)
        printf("The DNS record memory is full.\n");

    for (j; j < i; j++)//用来把刚分好的TEMP【i】再次切割成IP和domain
    {
        char* ex1 = strtok(Temp[j], " ");
        char* ex2 = strtok(NULL, " ");
        if (ex2 == NULL)
        {
            printf("The record is not in a correct format.\n");
        }
        else
        {

            DNSTable[j].IP = ex1;
            DNSTable[j].domain = ex2;
            //DNSTable[j].IP[strlen(DNSTable[j].IP) - 1] = 0;

            //DNSTable[j].domain[strlen(DNSTable[j].domain) - 1] = '\0';
            //printf("%d\n%s\n%s\n",j, DNSTable[j].IP, DNSTable[j].domain);
            //printf("%d %s %s\n", j,  Temp[j], DNSTable[j].domain);
            num++;
        }
    }

    //printf("%d\n", num);
    //
    fclose(fp);
    printf("Load records success.\n");
    return num;
}

//获取DNS请求中的域名
void GetUrl(char* recvbuf, int recvnum)
{
    char urlname[LENGTHOFURL];
    int i = 0, j, k = 0;

    memset(Url, 0, LENGTHOFURL); //全用0初始化
    memcpy(urlname, &(recvbuf[sizeof(DNSHDR)]), recvnum - 12);    //获取请求报文中的域名表示,要去掉DNS报文首部的12字节

    int len = strlen(urlname);

    //域名转换
    while (i < len)
    {
        if (urlname[i] > 0 && urlname[i] <= 63)
            for (j = urlname[i], i++; j > 0; j--, i++, k++)
                Url[k] = urlname[i];

        if (urlname[i] != 0)
        {
            Url[k] = '.';
            k++;
        }
    }

    Url[k] = '\0';
}

//判断能不能在本中找到DNS请求中的域名,找到返回下标
int IsFind(char* url, int num)
{
    int find = NOTFOUND;
    char* domain;

    char* NUrl;
    NUrl = (char*)malloc(sizeof(char)*210);
    strcpy(NUrl, url);
    strcat(NUrl, "\r\n");

    //printf("URL: %sabab\n", url);
    //printf("%sabab\n", DNSTable[20].domain);

    for (int i = 0; i < num+2; i++)
    {
        if (DNSTable[i].domain)
        {
            domain = DNSTable[i].domain;
            /*printf("%s", domain);
            printf("%s", NUrl);
            printf("%d\n", strcmp(domain, NUrl));
            printf("wwwwwwwwwwwwwwwwwwwwwwwwwwww\n");*/

            if (strcmp(domain, NUrl) == 0)
            {
                //printf("!~!FOUND!~!\n");
                find = i;
                break;
            }
        }
    }

    //printf("find:%d\n", find);

    return find;
}

//将请求ID转换为新的ID,并将信息写入ID转换表中
unsigned short ReplaceNewID(unsigned short OldID, SOCKADDR_IN temp, BOOL ifdone)
{
    srand(time(NULL)); //随机数种子
    IDTransTable[IDcount].oldID = OldID;
    IDTransTable[IDcount].client = temp;
    IDTransTable[IDcount].done = ifdone;
    IDcount++; //ID转换表数目要更新~

    return (unsigned short)(IDcount - 1);    //以表中下标作为新的ID
}

//打印 时间 newID 功能 域名 IP
void PrintInfo(unsigned short newID, int find)
{
    //打印时间
    GetLocalTime(&TimeOfSys);
    //输出指定长度的字符串, 超长时不截断, 不足时左对齐:
    //printf("%-ns", str);            --n 为指定长度的10进制数值
    int Btime;
    int Ltime;
    Btime = ((((TimeOfSys.wDay - Day) * 24 + TimeOfSys.wHour - Hour) * 60 + TimeOfSys.wMinute - Minute) * 60) + TimeOfSys.wSecond - Second;
    Ltime = abs(TimeOfSys.wMilliseconds - Milliseconds);
    printf("%d.%d   %d", Btime, Ltime, newID);
    printf("    ");

    //在表中没有找到DNS请求中的域名
    if (find == NOTFOUND)
    {
        //中继功能
        printf("中继");
        printf("    ");
        //打印域名
        printf("%s",Url);
        printf("    ");
        //打印IP
        printf("\n");
    }

    //在表中找到DNS请求中的域名
    else
    {
        if (strcmp(DNSTable[find].IP, "0.0.0.0") == 0)  //不良网站拦截
        {
            //屏蔽功能
            printf("屏蔽");
            printf("    ");
            //打印域名(加*)
            //打印域名
            printf("***%s", Url);
            printf("    ");
            //打印IP
            printf("%s\n", DNSTable[find].IP);
        }

        //检索结果为普通IP地址,则向客户返回这个地址
        else
        {
            //服务器功能
            printf("Local服务器");
            printf("    ");
            //打印域名
            printf("***%s", Url);
            printf("    ");
            //打印IP
            printf("%s\n", DNSTable[find].IP);
        }
    }
}

<main.c>

//#define _CRT_SECURE_NO_WARNINGS
//#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <windows.h>
#include <time.h>
#include <process.h>
#include "definition.h"
#include "functions.h"
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib") //加载 ws2_32.dll

Translate DNSTable[AMOUNT];        //DNS域名解析表
IDTransform IDTransTable[AMOUNT];    //ID转换表
int IDcount = 0;                    //转换表中的条目个数
char Url[LENGTHOFURL];                    //域名
SYSTEMTIME TimeOfSys;                     //系统时间
int Day, Hour, Minute, Second, Milliseconds;//保存系统时间的变量

int main()
{

    //参考:https://wenku.baidu.com/view/ed7d64c852d380eb62946df4.html

    //初始化 DLL
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);

    //创建套接字
    SOCKET servSock = socket(AF_INET, SOCK_DGRAM, 0);
    SOCKET localSock = socket(AF_INET, SOCK_DGRAM, 0);

    //将套接口都设置为非阻塞
    int unBlock = 1;
    ioctlsocket(servSock, FIONBIO, (u_long FAR*) &unBlock);//将外部套街口设置为非阻塞
    ioctlsocket(localSock, FIONBIO, (u_long FAR*) &unBlock);//将本地套街口设置为非阻塞

    //绑定套接字
    SOCKADDR_IN serverName, clientName, localName;    //本地DNS、外部DNS和请求端三个网络套接字地址
    localName.sin_family = AF_INET;
    localName.sin_port = htons(PORT);
    localName.sin_addr.s_addr = inet_addr(LOCAL_DNS_ADDRESS);
    serverName.sin_family = AF_INET;
    serverName.sin_port = htons(PORT);
    serverName.sin_addr.s_addr = inet_addr(DEF_DNS_ADDRESS);

    //绑定本地服务器地址
    if (bind(localSock, (SOCKADDR*)&localName, sizeof(localName)))
    {
        printf("Bind 53 port failed.\n");
        exit(-1);
    }
    else
        printf("Bind 53 port success.\n");

    char sendBuf[BUFSIZE]; //发送缓存
    char recvBuf[BUFSIZE]; //接收缓存
    char* Path;
    Path=(char*)malloc(sizeof(char)*100);
    int recordNum; //txt文件有效行数
    int iLen_cli, iSend, iRecv;

    strcpy(Path, "C:\\Users\\mrw29\\Desktop\\dnsrelay.txt");
    recordNum = InitialDNSTable(Path);
    //保存系统时间
    GetLocalTime(&TimeOfSys);
    Day = TimeOfSys.wDay;
    Hour = TimeOfSys.wHour;
    Minute = TimeOfSys.wMinute;
    Milliseconds = TimeOfSys.wMilliseconds;

    int find;
    unsigned short NewID;
    unsigned short* pID;

    //下面是服务器的具体操作
    while (1)
    {
        iLen_cli = sizeof(clientName);
        memset(recvBuf, 0, BUFSIZE); //将接收缓存先置为全0

        //接收DNS请求
        //函数:int recvfrom(int s, void* buf, int len, unsigned int flags, struct sockaddr* from, int* fromlen);
        //函数说明:recv()用来接收远程主机经指定的socket 传来的数据, 并把数据存到由参数buf 指向的内存空间, 参数len 为可接收数据的最大长度.
        //参数flags 一般设0, 其他数值定义请参考recv().参数from 用来指定欲传送的网络地址, 结构sockaddr 请参考bind().参数fromlen 为sockaddr 的结构长度.
        iRecv = recvfrom(localSock, recvBuf, sizeof(recvBuf), 0, (SOCKADDR*)&clientName, &iLen_cli);
        //错误反馈
        if (iRecv == SOCKET_ERROR)
        {
            //printf("Recvfrom Failed: %s\n", strerror(WSAGetLastError()));
            continue; //强制开始下一次循环
        }
        else if (iRecv == 0)
        {
            break; //没东西,跳出循环0
        }
        else
        {
            GetUrl(recvBuf, iRecv);                //获取域名
            find = IsFind(Url, recordNum);        //在域名解析表中查找

            //printf("We have get the url: %s\n", Url);

            //printf("%d\n", find);

            //开始分情况讨论
            //在域名解析表中没有找到
            if (find == NOTFOUND)
            {
                //printf("We dont find this url, will get a new ID and forward to SERVER.\n");
                //ID转换
                //pID = new (unsigned short);
                pID = (unsigned short*)malloc(sizeof(unsigned short*));
                memcpy(pID, recvBuf, sizeof(unsigned short)); //报文前两字节为ID
                NewID = htons(ReplaceNewID(ntohs(*pID), clientName, FALSE));
                memcpy(recvBuf, &NewID, sizeof(unsigned short));

                //打印 时间 newID 功能 域名 IP
                PrintInfo(ntohs(NewID), find);

                //把recvbuf转发至指定的外部DNS服务器
                iSend = sendto(servSock, recvBuf, iRecv, 0, (SOCKADDR*)&serverName, sizeof(serverName));
                if (iSend == SOCKET_ERROR)
                {
                    //printf("sendto Failed: %s\n", strerror(WSAGetLastError()));
                    continue;
                }
                else if (iSend == 0)
                    break;

                //delete pID; //释放动态分配的内存
                free(pID);
                clock_t start, stop; //定时
                double duration = 0;

                //接收来自外部DNS服务器的响应报文
                start = clock();
                iRecv = recvfrom(servSock, recvBuf, sizeof(recvBuf), 0, (SOCKADDR*)&clientName, &iLen_cli);
                while ((iRecv == 0) || (iRecv == SOCKET_ERROR))
                {
                    iRecv = recvfrom(servSock, recvBuf, sizeof(recvBuf), 0, (SOCKADDR*)&clientName, &iLen_cli);
                    stop = clock();
                    duration = (double)(stop - start) / CLK_TCK;
                    if (duration > 5)
                    {
                        printf("Long Time No Response From Server.\n");
                        goto ps;
                    }
                }
                //ID转换
                pID = (unsigned short*)malloc(sizeof(unsigned short*));
                memcpy(pID, recvBuf, sizeof(unsigned short)); //报文前两字节为ID
                int GetId = ntohs(*pID); //ntohs的功能:将网络字节序转换为主机字节序
                unsigned short oID = htons(IDTransTable[GetId].oldID);
                memcpy(recvBuf, &oID, sizeof(unsigned short));
                IDTransTable[GetId].done = TRUE;

                //char* urlname;
                //memcpy(urlname, &(recvBuf[sizeof(DNSHDR)]), iRecv - 12);    //获取请求报文中的域名表示,要去掉DNS报文首部的12字节
                //char* NewIP;

                //打印 时间 newID 功能 域名 IP
                PrintInfo(ntohs(NewID), find);

                //从ID转换表中获取发出DNS请求者的信息
                clientName = IDTransTable[GetId].client;

                //printf("We get a answer from SERVER, now we give it back to client.\n");

                //把recvbuf转发至请求者处
                iSend = sendto(localSock, recvBuf, iRecv, 0, (SOCKADDR*)&clientName, sizeof(clientName));
                if (iSend == SOCKET_ERROR)
                {
                    //printf("sendto Failed: %s\n\n", strerror(WSAGetLastError()));
                    continue;
                }
                else if (iSend == 0)
                    break;

                free(pID); //释放动态分配的内存
            }

            //在域名解析表中找到
            else
            {
                //printf("We have find this url.\n");
                //获取请求报文的ID
                pID = (unsigned short*)malloc(sizeof(unsigned short*));
                memcpy(pID, recvBuf, sizeof(unsigned short));

                //转换ID
                unsigned short nID = ReplaceNewID(ntohs(*pID), clientName, FALSE);

                //printf("We have get a new ID, now we will create an answer.\n");

                //打印 时间 newID 功能 域名 IP
                PrintInfo(nID, find);
                //参考:https://blog.csdn.net/weixin_34192993/article/details/87949701
                //构造响应报文头
                memcpy(sendBuf, recvBuf, iRecv); //拷贝请求报文
                unsigned short AFlag = htons(0x8180); //htons的功能:将主机字节序转换为网络字节序,即大端模式(big-endian) 0x8180为DNS响应报文的标志Flags字段
                memcpy(&sendBuf[2], &AFlag, sizeof(unsigned short)); //修改标志域,绕开ID的两字节

                //修改回答数域
                if (strcmp(DNSTable[find].IP, "0.0.0.0") == 0)
                    AFlag = htons(0x0000);    //屏蔽功能:回答数为0
                else
                    AFlag = htons(0x0001);    //服务器功能:回答数为1
                memcpy(&sendBuf[6], &AFlag, sizeof(unsigned short)); //修改回答记录数,绕开ID两字节、Flags两字节、问题记录数两字节

                int curLen = 0; //不断更新的长度

                //构造DNS响应部分
                //参考:http://c.biancheng.net/view/6457.html
                char answer[16];
                unsigned short Name = htons(0xc00c); //域名指针(偏移量)
                memcpy(answer, &Name, sizeof(unsigned short));
                curLen += sizeof(unsigned short);

                unsigned short TypeA = htons(0x0001); //类型
                memcpy(answer + curLen, &TypeA, sizeof(unsigned short));
                curLen += sizeof(unsigned short);

                unsigned short ClassA = htons(0x0001); //查询类
                memcpy(answer + curLen, &ClassA, sizeof(unsigned short));
                curLen += sizeof(unsigned short);

                //TTL四字节
                unsigned long timeLive = htonl(0x7b); //生存时间
                memcpy(answer + curLen, &timeLive, sizeof(unsigned long));
                curLen += sizeof(unsigned long);

                unsigned short RDLength = htons(0x0004); //资源数据长度
                memcpy(answer + curLen, &RDLength, sizeof(unsigned short));
                curLen += sizeof(unsigned short);

                unsigned long IP = (unsigned long)inet_addr(DNSTable[find].IP); //inet_addr为IP地址转化函数
                memcpy(answer + curLen, &IP, sizeof(unsigned long));
                curLen += sizeof(unsigned long);
                curLen += iRecv;

                //请求报文和响应部分共同组成DNS响应报文存入sendbuf
                memcpy(sendBuf + iRecv, answer, curLen);

                //printf("Create Over, give it to client.\n");

                clock_t Nstart, Nstop; //clock_t为clock()函数返回的变量类型
                double Nduration;

                //发送DNS响应报文
                Nstart = clock();
                iSend = sendto(localSock, sendBuf, curLen, 0, (SOCKADDR*)&clientName, sizeof(clientName));
                //if (iSend == SOCKET_ERROR)
                //{
                //    //printf("sendto Failed: %s\n", strerror(WSAGetLastError()));
                //    Nstop = clock();
                //    Nduration = (double)(Nstop - Nstart) / CLK_TCK;
                //    if (Nduration > 1)
                //        goto ps;
                //    else
                //        continue;
                //}
                //else if (iSend == 0)
                //    break;

                free(pID); //释放动态分配的内存

                //printf("\nThis loop is over, thanks.\n\n");
            }
        }
    ps:;
    }

    closesocket(servSock);
    closesocket(localSock);
    WSACleanup();                //释放ws2_32.dll动态链接库初始化时分配的资源

    system("pause");
    return 0;
}

WireShark调试笔记

Nslookup www.baidu.com

从3890->3895

请求报文1

响应报文1

请求报文2

响应报文2

请求报文3

响应报文3

Nslookup 555.265.com

从2797->2829

请求报文1

响应报文1

请求报文2

响应报文2

Nslookup content.googleapis.com

从3190->3191

请求报文

响应报文

Nslookup

从86->87

请求报文

响应报文

♻️ 资源

在这里插入图片描述

大小: 6.16MB
➡️ 资源下载:https://download.csdn.net/download/s1t16/87388351


本文转载自: https://blog.csdn.net/s1t16/article/details/129295019
版权归原作者 神仙别闹 所有, 如有侵权,请联系我们删除。

“基于C语言实现DNS服务器程序【100010438】”的评论:

还没有评论