Я изучаю программирование TCP и UDP.Чтобы лучше понять и использовать эти протоколы, я написал небольшую программу, в которой модуль будет отправлять запрос DNS-запроса, который является частью моего кода.
Я определю заголовок IP, заголовок UDP и DNSсам заголовок, затем часть данных нашего DNS-запроса, например:
typedef struct query
{
// 0123456789
// test name github.com
char name[10];
struct question *question;
} Query, *pQuery;
typedef struct question
{
unsigned short int qtype;
unsigned short int qclass;
} Question, *pQuestion;
// DNS header structure
typedef struct dns_header
{
unsigned short int id; // identification number
// flag
// unsigned short int == uint16_t
#if __BYTE_ORDER == __BIG_ENDIAN
unsigned short int qr : 1;
unsigned short int opcode : 4;
unsigned short int aa : 1;
unsigned short int tc : 1;
unsigned short int rd : 1;
unsigned short int ra : 1;
unsigned short int z : 3;
unsigned short int rcode : 4;
#elif __BYTE_ORDER == __LITTLE_ENDIAN
unsigned short int rd : 1;
unsigned short int tc : 1;
unsigned short int aa : 1;
unsigned short int opcode : 4;
unsigned short int qr : 1;
unsigned short int rcode : 4;
unsigned short int z : 3;
unsigned short int ra : 1;
#endif
unsigned short qcount; // question count
unsigned short ancount; // answer record count
unsigned short nscount; // name server count
unsigned short adcount; // additional record count
} DNSHeader, *pDNSHeader;
static int SendDNS(const pDNSStruct ds, const int debug_level)
{
// Perform a DNS query by sending a packet
int socket_fd;
socket_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (socket_fd < 0)
{
return 1;
}
struct sockaddr_in sin;
sin.sin_family = AF_INET;
// dst
sin.sin_port = htons((int)ds->src_port); // set the destination address
sin.sin_addr.s_addr = inet_addr(ds->src_ip); // set the port
char *datagram;
//char *data;
size_t pksize = sizeof(struct ip) + sizeof(struct udphdr) + sizeof(DNSHeader) + sizeof(Query);
datagram = (char *)malloc(pksize);
struct ip *iph;
iph = (struct ip *)datagram;
struct udphdr *udph;
memset(datagram, 0, pksize);
// filed the data
int one = 1;
const int *val = &one;
if (setsockopt(socket_fd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0)
{
//exit(0);
return 1;
}
// entete ip
iph->ip_v = 4;
iph->ip_hl = 5;
iph->ip_tos = 0;
iph->ip_len = pksize;
iph->ip_ttl = 255;
iph->ip_off = 0;
iph->ip_id = sizeof(45);
iph->ip_p = IPPROTO_UDP;
iph->ip_sum = 0; // a remplir aprés
iph->ip_src.s_addr = inet_addr(ds->src_ip);
iph->ip_dst.s_addr = inet_addr(ds->dst_ip);
udph = (struct udphdr *)(datagram + sizeof(struct ip));
// entete udp
udph->uh_sport = htons(ds->src_port);
udph->uh_dport = htons(ds->dst_port);
udph->uh_ulen = htons(sizeof(struct udphdr));
// use the UDP to send the data
pDNSHeader dnsh = (pDNSHeader)(datagram + sizeof(struct ip) + sizeof(struct udphdr));
// set the DNS structure to standard queries
dnsh->id = (unsigned short)htons(getpid());
dnsh->qr = 0; // this is a query
dnsh->opcode = 0; // this is a standard query
dnsh->aa = 0; // not authoritative
dnsh->tc = 0; // this message is not truncated
dnsh->rd = 1; // recursion desired
dnsh->ra = 0; // recursion not available! hey we dont have it (lol)
dnsh->z = 0;
dnsh->rcode = 0;
dnsh->qcount = htons(1); //we have only 1 question
dnsh->ancount = 0;
dnsh->nscount = 0;
dnsh->adcount = 0;
// point to the query portion
// filed the data
pQuery query = (pQuery)(datagram + sizeof(struct ip) + sizeof(struct udphdr) + sizeof(DNSHeader));
// DNS_QUERY_NAME_DEFAULT in here is "github.com"
memcpy(query->name, DNS_QUERY_NAME_DEFAULT, strlen(DNS_QUERY_NAME_DEFAULT));
pQuestion question = (pQuestion)(datagram + sizeof(struct ip) + sizeof(struct udphdr) + sizeof(DNSHeader) + sizeof(Query));
question->qtype = htons(DNS_QUERY_TYPE_DEFAULT); //type of the query , A , MX , CNAME , NS etc
question->qclass = htons(1); //its internet (lol)
if (sendto(socket_fd, datagram, pksize, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
return 1;
}
free(datagram);
close(socket_fd);
return 0;
}
Теперь эта программа работает нормально, но есть проблема, которая не была решена.Когда я использовал Wireshark
для проверки отправленных пакетов, я обнаружил некоторые проблемы.
Это пакет, отправленный программой.
0000 dc fe 18 84 cb e4 3c f8 62 7b 0f d9 08 00 45 00 Üþ..Ëä<øb{.Ù..E.
0010 00 40 04 00 00 00 ff 11 11 1f c0 a8 01 01 72 72 .@....ÿ...À¨..rr
0020 72 72 00 50 00 35 00 08 ff 72 75 6a 01 00 00 01 rr.P.5..ÿruj....
0030 00 00 00 00 00 00 67 69 74 68 75 62 2e 63 00 01 ......github.c..
0040 00 01 00 00 00 00 00 00 00 00 00 00 00 00 ..............
Вы видите, что эти данные не распознаютсяв качестве DNS это был только UDP.
Тогда это пакет, сгенерированный командой nslookup github.com
.
0000 dc fe 18 84 cb e4 3c f8 62 7b 0f d9 08 00 45 00 Üþ..Ëä<øb{.Ù..E.
0010 00 38 3d 03 40 00 40 11 7a 54 c0 a8 01 0c c0 a8 .8=.@.@.zTˬ..ˬ
0020 01 01 bd a0 00 35 00 24 aa de 07 cd 01 00 00 01 ..½ .5.$ªÞ.Í....
0030 00 00 00 00 00 00 06 67 69 74 68 75 62 03 63 6f .......github.co
0040 6d 00 00 01 00 01 m.....
Это выглядит очень похоже, но в Wireshark
оно совершенно другое.Пакеты, отправленные моей программой, могут быть распознаны как UDP только по Wireshark
, и DNS-сервер не может вернуть результат (я думаю, DNS-сервер может не понимать этот запрос), но используемый мной пакет nslookup
может бытьраспознается как DNS, и результат также возвращается.
Итак, вопрос в том, что-то не так с моим кодом?
Спасибо.