Reputation: 1237
currently i am learning about DNS servers in my lectures. As we had berkeley sockets before, i thought i may combine my knowledge and try to send a request to a DNS via a socket and process the answer myself. I wrote a little C program on linux which takes 3 arguments: an IP a port (most likely 53 as i found out) and the data which should be send to the given server.
First i tried simply sending the host name as a string. Then i discovered RFC1034 where it becomes clear that a request is more complex and contains several fields. However i somehow can not find out the possible values i can fill in the fields. For example under 6.2.1 in the link it says Header: OPCODE=SQUERY
whereas OPCODE is stated to be a 4 bit integer so i believe these codes are implementation defined and determined by the DNS server? Or did i miss a section of the RFC? It only states type and class are "encoded 16 bit values".
when i try to call my program like this
client 8.8.8.8 53 www.example.com
for the google DNS, it actually connects and sends the data but then waits for an answer which never comes. When using TCP i noticed the DNS server instantly closes the connection after receiving the domain name.
As i understand now i need to assemble a request according to the RFC with a Header containing the opcode for a request and the question containing the domain name and further options like type and class as binary information. But where do i get the possible values for OPCODE, QCLASS, QTYPE
?
I am aware that getaddrinfo
essentially does this for me and that i can get the IP from the sockaddr structure already but this is a matter of experimenting and learning.
I appreciate any help :)
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
int main (int argc, char **argv) {
if (argc < 4) {
printf("Error to few arguments.\nUsage: client <ip/host name> <port> <query>\n");
exit(1);
}
struct addrinfo *server_addr;
struct addrinfo hint;
memset(&hint, 0, sizeof(hint));
hint.ai_family = AF_UNSPEC;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = IPPROTO_UDP;
printf("getting server info\n");
/* Heh...ironic */
int err = getaddrinfo(argv[1], argv[2], &hint, &server_addr);
if (err != 0) {
perror("Error while getting server address info");
exit(1);
}
int client;
/* Iterate over the provided addresses and try to establish a connection */
for (struct addrinfo *cur_info = server_addr; cur_info != NULL; cur_info = cur_info->ai_next) {
/* Create corresponding socket */
client = socket(cur_info->ai_family, cur_info->ai_socktype, cur_info->ai_protocol);
printf("Trying to establish connection\n");
err = connect(client, cur_info->ai_addr, sizeof(*(cur_info->ai_addr)));
/* If connection was established leave loop */
if (err == 0) break;
/* If an error occured notify */
if (err == -1) perror("Error while connecting");
close(client);
/* If the current info was the last one consider connection failed */
if (cur_info->ai_next == NULL) {
printf("Error could not connect to any service\n");
exit(1);
}
}
/* Release information as it is no longer needed */
freeaddrinfo(server_addr);
printf("Sending request\n");
err = send(client, argv[3], strlen(argv[3]), 0);
if (err < 1) {
perror("Error while sending request");
exit(1);
}
printf("%d b sent\n", err);
char buffer[512];
int bytes_received = 0;
printf("receiving answer\n");
while ((bytes_received = recv(client, buffer, sizeof(buffer), 0)) != 0) {
if (bytes_received == -1) {
perror("Error while receiving message from server");
break;
}
fwrite(buffer, bytes_received, 1, stdout);
}
close(client);
}
Upvotes: 2
Views: 4251
Reputation: 215287
The function res_mkquery
, which was originally from BIND/libresolv but appears in almost every version of libc out there (albeit not being defined in any standard), can produce query packets for you, and you can inspect the output without sending it. Playing around with it would be very informative.
As for your specific question about OPCODE
, and DNS query format in general, you should be referring to RFC 1035, not 1034; the latter is higher-level principles not protocol. Page 26 defines the OPCODE
values, and 0 (standard query, "SQUERY") is the only meaningful one.
Note that the ns_*
functions from <arpa/nameser.h>
(also nonstandard but widely available) can be used to parse your query packet or answers received by sending it to a nameserver and getting a response.
Upvotes: 2