DNS and the resolver fu
Programming the resolver library
by
Lord Soth
All rights
reserved to the Immortal Descendants
Visit us on
the web at : http://www.ImmortalDescendants.org/
Contents:
1.1 System/Reader
requirements.
1.2 Introduction.
2.1 The normal DNS
fu
3.1 The resolver fu
3.2 A DNS message format (basic)
3.3 Example code: doing MX queries.
1.1 -
System/Reader Requirements:
Any UNIX type operating system (OS).
Or,
if you really must, WinSock32, but
it's
not that great of an implementation
of TCPIP
:p
The
reader requirements is to have a good
understanding
of UNIX (nix) network programming.
I'ts
not required to know Berkley sockets or any other interface like X/Open,
but
it will be useful in understanding the entire scheme.
Introduction
OK.
So you've been wondering what this essay is really about.
This
text is meant for network programmers that wish to understand a little more
about
the workings of the DNS interface in
the nix environment, and most probably
in
WinSock as well (but with different headers, etc..)
The
problem lies in the fact that there aren't that many fu
DNS queries of any kind. There
are the two basic fu
a PTR query. One asks the DNS to convert an IP address to a host
name,
which
is a human readable string (with dots of course heh), and the other is a
request
to the DNS server to convert an
address (host name) to an IP address,
which
is basically a 4 byte value.
There
are a few more fu
give
the programmer all the fu
One
such example is given here and is taken from my own personal experie
Performing
MX queries to a DNS server.
The
MX resource record (RR) contained in the DNS
configuration files let the DNS
server
know which host (hostname+IP) is handling mail exchange for the domain.
This
is a very important RR because any one wishing to send Email to a domain,
has
to know to which host to issue the commands and send the data.
After
we'll review the resolver fu
an
example of how to perform MX queries.
The
idea behind this text was that not many people know how to operate the
resolver
fu
documentation
about DNS. A programmer can only
find the funtion's prototypes
in
the manual page, but it still doesn't help understand what possible values
can
pass to the fu
After
we've stated this, let's start.
2.1 - The normal DNS fu
Let's
begin at the beginning. We'll now explore the wonderful and exciting world
of
performing A and PTR queries (ok maybe it's not THAT exciting, so sue me hehe)
The
two fu
are:
struct hostent *gethostbyname( const char *hostname);
struct hostent *gethostbyaddr( const char *addr, size_t len, int family);
Both
of these fu
the
response, or NULL if there was an error of some kind.
Notice
one VERY IMPORTANT thing about the gethostbyaddr fu
the
fu
pointer
to a string (char *), but is actually a pointer to an in_addr structure.
So
the prototype should be:
struct hostent *gethostbyaddr( struct in_addr *addr, size_t len, int family);
One
more thing to notice is that the required header file for these fu
<netdb.h>
You
can basically figure out which header is needed by using the manual page for
the
fu
OK,
now let's describe these fu
hostname
(A query) to an IP adderss. The gethostbyaddr fu
and
returns the hostname for a specified IP address.
The
specifics of how it's done is not important, although in the next few
sections
we'll talk a little about the fu
queries.
For
now, we only need to know the reply type for those fu
The
reply is a pointer to a static hostent structure that is declared inside the
resolver
library. This of course pops a problem: What if we call any of the
fu
reply
memory area!
Before
we go into that, let's talk a little about the reply itself, the hostent
structure.
The
structure is defined as follows:
struct hostent {
char *h_name; // official (canonial) name of the host
char **h_aliases // pointer to an array of pointers to alias
names (if any)
int h_addrtype; // host address type: AF_INET (IPv4) or AF_INET6
(IPv6)
int n_length; // length of address (4 for IPv4, 16 for IPv6)
char **h_addr_list; // pointer to an array of
pointers with IPv4/6 addresses
};
#define h_addr h_addr_list[0]
So
the important thing for us is when we call gethostbyname for example, we expect
to
receive the IP address of the host.
To
access the member variable that contains the IP address we'll use the following
syntax,
assuming pHost is a pointer to a hostent struct (declared somewhere in our
prog):
pHost->h_addr_list[0]
This
points to a small segment of memory that contains the first IP address for the
host,
and he
and
should be converted to the presentation form for printing, for example:
printf("\nThe returned IP address is:
%s\n", inet_ntoa( pHost->h_addr_list[0])
);
The
above line will convert the network byte ordered IP address to the presentation
form.
There is also the reverse fu
In
any case, getting back to our discussion, we now can see which part of the
hostent
structure contains the IP address. Now let's say we want to call
gethostbyaddr
and print the returned canonial name, which is accessed as follows:
pHost->h_name
Simple,
isn't it ? Well, YES! IT IS!
So
the printing would be as follows:
printf("\nThe returned canonial name
is : %s\n",
pHost->h_name);
And
that is basically all we need to know about the most basic DNS fu
If
we're not coding a multithreaded application, we can safely use these fu
Those
two fu
which
is shared between fu
overwrite
the other's data.
But
for multiprocessed applications, which contain independent processes, this is
not
a problem.
For
those cases that do pose a problem to us, there are versions of these fu
that
are reentrant, and there is also the very good all-in-wonder fu
getaddrinfo.
We'll
not dwelve into it, but it's basically based on the same guidelines of thses
two
fu
multiple
IPs and both TCP and UDP.
By
presenting these two basic fu
The
A resource record is used to map between host names and IP addresses.
The
PTR resource record is used to map between IP addresses and host name.
But
we have other types of records, such as MX, NS, etc.. What will we do then ?
The
answer, although we don't like to hear it, is to write our own fu
to
do the job, or alternatively, use someone else's library. There are not many
and
you'll have to put your trust in someone else's hands.
The
resolver, which is the portion that does all the DNS resolving for the
program,
is actually a part of the application, and it's linked at link time
by
the compiler. The resolver fu
inside
the gethostbyname and gethostbyaddr fu
fu
The
next section talks about the resolver fu
3.1 - The resolver fu
The
lowest level fu
resolver
fu
a
name. Those however are not the only resolver fu
The
usable fu
the
resolver (man resolver).
I'll
now describe some of the most important of them:
int res_query( char *host, int class, int type, u_char *answer, size_t
anslen);
int res_search( char *host, int class, int type, u_char *answer, size_t
anslen);
int dn_expand( u_char *answer, u_char
*endofanswer, u_char *ptr, char
*buffer, int buflen);
int dn_skipname( u_char *ptr, u_char *eom);
res_query
- This fu
for
the domain (host) and puts the response in the answer buffer of size anslen
bytes.
This
fu
In
the next section we'll see how the DNS message looks like and it's important
to
know that this fu
DNS
server of the specified host.
res_search - Does the same as res_query but considers some of the
flags that can
be
set in the _res.options member of the _res structure (which is defined in
the <arpa/nameser.h> header file). Typical flags
are a flag to operate using
default
domain names (any domain that doesn't end with a period will have the
default
domain name appended to it), and another useful flag is to tell the
server
to recursively seek the answer, going to other servers as well.
IMO the best idea is to use the res_search fu
succeeding,
and it requires less care on our part.
dn_skipname - This fu
pointer
inside the answer buffer in which the value that is stored is a name
pointer
(we'll get to that part soon).
It
needs the eom pointer to know it's not out of the memory location of the
answer
buffer (VERY IMPORTANT!!).
It
then measures the name pointer's length and returns it. This is useful
for,
well, you guessed it, skipping names :)
dn_expand - This fu
of
the answer buffer, a pointer inside the buffer that should point to
a
name pointer, a buffer for storing the decompressed name, and the length
of
that last buffer.
This
fu
pointer
at the address in which ptr is pointing to.
The
decompressed name will be put in (buffer) and will not exceed buflen
in
size.
Now
the questions are popping. What are all those buffers, answers, classes,
types
and the other things ??
You're
probably asking yourself these questions for a very good reason. here
lies
the resolver fu
programming
manual. You can find bits of information in different places.
For
example, I found that the class and type parameters can be any one of
the
values that are defined in the <arpa/nameser.h> file.
This
header file contains definitions for the resolver library.
The
usual class (actually, this class is used almost exclusively) is the
C_IN
class. This class tells the resolver that the medium we're operating
on
is the ARPA Internet. This is not trivial as the resolver was designed
by
the guys at Berkley to operate on other networks as well, not just
IP
driven networks, theoretically.
The
type parameter will be the type of query needed. For an MX query for
example,
we'll specify a type of T_MX.
Again,
all these are written in the header file.
How
did I find this header file ?
Well,
I've searched the header files using the following command to see
what
comes up:
fgrep
'T_MX' $(find /usr/i
This
command line uses the find command to search for any files with an
extension
of h (header files) and it feeds those as arguments to the
fgrep
command which looks inside each for the pattern (T_MX in this case).
Notice
that this is different from piping because pipes link the stdout
of a
program to the stdin of another program. What we've done here is
to
use the output of one program as command arguments for another program.
If
we were to pipe the find stdout to the fgrep stdin, fgrep will look
inside
the data it receives for T_MX, but the data it receives is a list
of
files to look into them!
So
after I've explained this little shell programming trivial trick, let's
move
on.
Other
information is still required by us to get a picture of what is happening
with
those resolver fu
The
only thing left for us to figure out is how the answer looks like. This
should
also explain what is all this compressing/decompressing we see in there.
Let
me save you the trouble of looking into other people's sources to figure
this
one out. The answer buffer is filled with a DNS message, which is no more
than
a DNS packet (i
So
the resolver fu
kernel
stripped the IP and UDP/TCP headers of the packet (not looking at any
additional
info put in by the interface. An Ethernet interface would make
all
the headers+data into a thing called a Frame).
The
last thing for us to do is to translate the returned packet into what we
need
to use in our application.
For
this end we'll need to know about the DNS message format. My biggest
source
for information on this is the book TCPIP Illustrated Vol. 1, by
W.
Richard Stevens.
It's
a comprehensive overview of networking and protocols, i
There
are of course a few loose ends in this book, as it can't cover
everything,
but it covers a LOT.
The
rest you can find in the various RFCs. An example to this will be given
although
we won't actually look into any RFCs.
And
so, the next section describes the DNS message format briefly, and we'll
focus
on one type of response when we get to the MX query section.
Maybe
one other thing worth mentioning about the resolver library is that
you
can't just i
work.
It wasn't designed to be one of the libraries that gets linked by the
compiler
by default. He
compilation
command (in your Makefile or not), so that the compiler will link
the
necessary libraries for you to use in your code.
3.2 - A DNS message format
In
the last section we came to see that we now only need to know the message
format
in order to perform a query. In fact, our situation is even more
simplified
by the fact that we don't actually have to create any kind of DNS
message.
This is a bit vague, si
to
know the format of the message so we can get the information we need from
it.
The
point is that DNS messages can contain questions and answers at the same
time,
and because the res_xxx fu
the
creation and sending of a DNS question to a DNS server, we only need to deal
with
the answer buffer.
The
information dealing with what fields exist in the question buffer is
therefore
not our co
that
would just be copying it from TCPIP Illustrated, and therefore I decided
not
to i
What
I will have to i
about
the question portion, and it looks as follows.
Notice
this is a bits/bytes diagram. The lower you go, the bigger the offset
is
in the buffer. Each block represents an element and the bits at the top
let
you figure out the size of each element.
0 15
31 32
------------------------------------------------------------------
identification | flags
------------------------------------------------------------------
number of questions |
number of answer RRs
------------------------------------------------------------------
num.
of authority RRs | num. of additional RRs
------------------------------------------------------------------
questions
------------------------------------------------------------------
answers
------------------------------------------------------------------
authority
------------------------------------------------------------------
additional information
------------------------------------------------------------------
Of
all those fields, the important are the number of questions, number of
answers
and the answers section itself.
The
number of questions will be used to skip the question portion of the
DNS
message. The DNS server replies with the original questions themselves
and
so we'll need to know how many of them to skip.
The
number of answers is trivial. We need to know how many answers the server
has
given to our query.
The
last part is the most important one. In the answer section lies the real
information
we need from the server. The format of that information differs
between
types of queries and it's up to us to know which type of answer it
is
and act accordingly.
For
that end, I've i
message.
0 15
31 32
------------------------------------------------------------------
| domain
name |
------------------------------------------------------------------
| type | class |
------------------------------------------------------------------
| Time To Live (TTL) |
------------------------------------------------------------------
|
resource data length | |
------------------------------- |
| resource data |
------------------------------------------------------------------
Let's
now describe each field:
Domain
Name - The compressed default domain name of the domain we queried.
Believe
it or not, I discovered this the hard way, while studying sendmail's
source
code. At first I didn't look at this diagram that I had in TCPIP
Illustrated
and I figured this is the answer itself, only it didn't make sense
because
I knew the REAL answer to the MX query I was trying to make. This
should
not be surprising as I'm the one who configured my LAN's DNS server.
The
domain name field is a 32 bit pointer field. DNS uses a compression scheme
on
which it stores names in the data block of the message, and it stores
pointers
to these names in the various fields (such as this one). Any name that
repeats
itself will be replaced by the pointer itself, which will save room
in
the target header. The fu
backwards
are the dn_comp and dn_expand fu
We'll
stick with the dn_expand
fu
the
response message.
Type
- This is a 16 bit field describing the type of query answer. The various
types
are located inside the <arpa/nameser.h> header file, as I've stated
before.
This field designates what kind of answer it is. This is a very
important
point to consider. If several questions were issued in the same
message,
to the DNS server, it'll reply with several types of answers. Therefore
we
have to check the answer type (this field) in order to know what to do with
it.
Class
- The class of medium we use for the message. This is usually C_IN, which
states
the ARPA Internet.
TTL
- This is the Time To Live value of the packet. As you may or may not know,
the
TTL is the "timer" that keeps a packet alive on the net. The
designers of
IP
made this so packets won't float around forever on the net. If for example,
a
TCP connection tries to resend it's packet (say a timeout value for and
acknowledgement
has elapsed), there would be more than one insta
packet
on the net. Basically the TTL is a hop counter, not a real timer.
Whenever
a packet hops from one router/machine to another, that machine's
kernel
decreases 1 from the TTL. A router/machine that sees a packet with a TTL
of 1
will discard the packet and send an ICMP message packet back to the
original
sender of the packet, so the later will know to resend it.
This
and other really neat tricks are what makes TCP a great protocol, however
DNS
mainly uses UDP, unless the size of the info is too large.
For
those interested, the TCPIP Illustrated Vol. 1 book describes the UDP and
TCP
protocols in a remarkable way for just one book, I suggest reading it.
Resource
data length - Naturally, the length of the resource data itself, in
bytes.
Notice that the data buffer of an answer starts right after this field,
which
is a 16 bit field.
We've
looked at a fairly basic way into the DNS message format, yet there are
other
issues with it. One of which is the data format of the answer itself.
We've
seen that the answer starts right after the resource data length field,
yet
we don't have any information about the format of the actual data.
For
these ends, even TCPIP Illustrated will not help. For that we'll need to
go
and look for the DNS RFCs and search for the data format.
Luckily
in the MXRR example I've i
is
fairly simple. If I understood it from source files, so can anyone.
With
that in mind, we have to write code that will traverse the answer buffers,
look
into each, extract the necessary information and so forth.
I'll
give a brief description of an MX query in the next section, along with
the
source code for a fu
3.3 - Example code: performing MX queries to a DNS
server
Alright,
this is the last part of this essay, and arguably the most important
one,
si
Basically,
one can learn everything he needs to know from the source itself,
without
looking at any books. However, it took me several hours to figure
everything
out, for exampkle.
To
perform an MX query using the res_search fu
a
few things, then call it in the following manner:
res_search( host, C_IN, T_MX, (u_char
*) &answer, sizeof( answer));
The
various parameters are:
host
- a string or a pointer to a string (the same thing really) that contains
the
domain name to query. Notice that with the res_search fu
specify
just the leftmost part of the domain, if you query your domain, and
the
full domain name if you query other domains.
C_IN
- The class value, ARPA Internet.
T_MX
- The type of query, in this case MX query.
&answer
- A pointer to an unsigned char buffer that will contain the answer
which
is the entire response message itself.
The
answer buffer is usually defined and then declared as follows:
typedef union {
HEADER hdr; // defines a DNS answer header
u_char buffer[MAXSIZE]; // define a buffer of MAXSIZE
bytes
}
qbuf;
This
will declare a union type containing a header and a buffer. The header
we
define is a header defined in the <arpa/nameser.h> header file. This header
contains
various flags and information, some of which are important to us,
such
as the number of answers and questions.
To
declare such a union type:
qbuf
answer;
Will
of course declare a qbuf type of union named answer.
I
would suggest the MAXSIZE value be compatible with the BIND max packet size.
Si
the
job quite admireably if I may say so), it is the best way to insure
compatibility
in the response size. BIND defines the max packet size as 8192
bytes.
sizeof( answer) - This should
really be trivial to any C programmer :)
Assuming
the fu
bytes
written to the answer buffer (-1 if an error occured), we can start
the
data extraction.
First
we'll need to skip the 12 byte DNS header, then skip all the questions.
O
part
we are usually interested in.
In
an MX answer we have first the domain name to extract to a temporary location
and
then to skip it, the type field to check (have to be T_MX for us to use it)
and
skipping the class field altogether.
Then
we can also skip the size of the resource if we so desire and then we reach
the
real answer itself. Note that each answer has all the following fields of
course,
because each answer can be of a different type and class and length.
When
we reach the MX answer itself, the first 16 bit are the priority of the
reply.
If you know a little DNS you'll know you can specify more than one MX
host
to handle mail in your domain. Each has a priority. The priority is a 16
bit
value and the lower it is, the more prefered the host is.
So
this field is naturally quite important for us. We'll extract it and after
it
comes the pointer to the reply itself. For the pointer we'll again use
dn_expand
and store the
answer in our buffer or some other field.
I've
found that the sendmail's implementation of the MX query fu
best
I've seen so I've decided to write my fu
And
without further delay, I present to you 2 parts of codes. The first are
things
to put in your header file in preparation of the fu
the
second is the fu
Stuff to put in header file/s
-------------------------------------------------------------------------------------------------------------------
#ifndef
MAXPACKET // make sure a maximum packet
size is declared by BIND
#define
MAXPACKET 8192 // BIND maximum packet size
#endif
#define
MAXMXHOSTS 5 // max number of MX records returned
#define
MXBUFFERSIZE
(128 * MAXMXHOSTS)
#ifndef
HFIXEDSZ // make sure header size is
declared
#define
HFIXEDSZ 12
#endif
//
definitions of return codes for your fu
//
define custom data types
typedef
union
{
HEADER hdr; // define a header structure
u_char qbuf[MAXPACKET]; // define a query buffer
}
mxquery;
The fu
---------------------------------------------------------------------------------------------------------------------------
extern
int GetMXRecord( char *dname, char **mxhosts, u_short *mxprefs, char *mxbuf)
{
// a fu
// host - string containing host name
// mxhosts - a pointer to an array of
pointers each pointing to an MX record (in the same buffer, for efficie
// mxprefs - a pointer to an array of
unsigned shorts that specify the prefera
// mxbuf - a pointer to an allocated buffer
that will contain all the host names (each name's pointer is in the mxhosts
array)
u_char *end, *puChar; // pointers to end of message, generic u_char
pointer.
int
i, j, n, nmx;
char
*pByte; // generic char pointer
HEADER *hp; // points to a header struct
mxquery answer; // declare an mxquey buffer
int
answers, questions, buflen;
u_short pref, type, *pPrefs;
struct
hostent *h; // for the A record, if needed
// check pointers
if (
mxprefs == NULL)
return
( MX_NOBUFFERMEM);
if (
mxhosts == NULL)
return
( MX_NOBUFFERMEM);
if (
mxbuf == NULL)
return
( MX_NOBUFFERMEM);
// make query
errno=0;
n = res_query( dname, C_IN, T_MX, (u_char *) &answer, sizeof(
answer));
if (
n < 0)
{
// handle error conditions
switch(
h_errno)
{
case NO_DATA:
case NO_RECOVERY:
// no MX
RRs, try the A record..
h = gethostbyname( dname);
if ( h != NULL)
{
// returned a resolved
result, store
if (
h->h_name != NULL)
{
if ( strlen(
h->h_name) != 0)
snprintf( mxbuf, MXBUFFERSIZE-1, h->h_name);
}
else
snprintf( mxbuf, MXBUFFERSIZE-1,
dname);
// set the arrays
nmx=0;
mxprefs[nmx]=0;
mxhosts[nmx]=mxbuf;
nmx++;
return(
nmx);
}
return( MX_NODATA);
break;
case TRY_AGAIN:
case -1:
// couldn't connect or temp failure
return(
MX_TEMPFAIL);
break;
default:
return( MX_UNKNOWNERROR);
break;
}
// who knows what happened
return(
MX_UNKNOWNERROR);
}
// make sure we don't exceed buffer length
if (
n > sizeof( answer))
n = sizeof(
answer);
// skip the question portion of the DNS
packet
hp = (HEADER *) &answer;
puChar = (u_char *) &answer + HFIXEDSZ; // point after the header
end = (u_char *) &answer + n; // point right after the
entire answer
pPrefs = mxprefs; // initialize the pointer to
the array of prefere
for(
questions = ntohs((u_short)hp->qdcount) ; questions
> 0 ; questions--, puChar += n+QFIXEDSZ)
{
// loop on question count (taken from
header), and skip them one by one
if (
(n = dn_skipname(puChar, end)) < 0)
{
// couldn't account for a question portion in the packet.
return ( MX_QUERROR);
}
}
// initialize and start decompressing the
answer
nmx = 0;
buflen = MXBUFFERSIZE-1;
pByte = mxbuf; // point to start of mx hosts string buffer
ZeroMemory( mxbuf, MXBUFFERSIZE);
ZeroMemory( mxhosts, MAXMXHOSTS * 4);
ZeroMemory( pPrefs, MAXMXHOSTS * sizeof(u_short));
answers = ntohs((u_short)hp->a
while(
(--answers >= 0) && (puChar < end) && (nmx <
MAXMXHOSTS-1) )
{
// puChar constantly changes (moves
forward in the answer buffer)
//
pByte points to the mx buffer, moves forward after we stored a host name
// decompress the domain's default host
name into the buffer so we can skip it and check if it's an
// MXhost
if (
(n = dn_expand( (u_char *) &answer, end, puChar,
(char *)pByte, buflen)) < 0)
break; // error in answer
puChar += n; // skip the name, go to its
attributes
GETSHORT( type, puChar); // get the type and move
forward
puChar += INT16SZ + INT32SZ; // skip the class and TTL
portion of the answer RR
GETSHORT( n, puChar); // get the resource size and move on
if (
type != T_MX)
{
// type of record is somehow NOT an MX record, move on
puChar += n;
continue;
}
GETSHORT( pref, puChar); // get the prefere
if (
(n = dn_expand( (u_char *) &answer, end, puChar,
(char *)pByte, buflen)) <
0) // expand the MXRR
break; // error in decompression
puChar += n;
// store it's attributes
pPrefs[nmx] = pref;
mxhosts[nmx] = pByte;
nmx++;
n = strlen(
pByte);
pByte += n+1; // make sure it's null terminated, notice the
buffer was set to 0 initially
buflen -= n+1;
}
// in case the records aren't sorted, bubble
sort them
for(
i=0 ; i < nmx ; i++)
for(
j=i+1 ; j < nmx ; j++)
if (
pPrefs[i] > pPrefs[j])
{
int temp;
char *temp2;
temp = pPrefs[i];
pPrefs[i] = pPrefs[j];
pPrefs[j] = temp;
temp2 = mxhosts[i];
mxhosts[i] = mxhosts[j];
mxhosts[j] = temp2;
}
// remove duplicates
for(
i=0 ; i < nmx-1 ; i++)
{
if ( strcasecmp( mxhosts[i], mxhosts[i+1])
!= 0)
continue;
else
{
// found a duplicate
for( j=i+1 ; j < nmx ; j++)
{
mxhosts[j] = mxhosts[j+1];
pPrefs[j] = pPrefs[j+1];
}
nmx--; // 1 less MX record
}
}
// all done, bug out
return
nmx;
}
-------------------------------------------------------------------------------------------------------
The ZeroMemory fu
-------------------------------------------------------------------------------------------------------
extern
int ZeroMemory( void *ptr, int size)
{
// zeroes out a memory buffer
if ( memset( ptr, 0, size) < 0)
return
-1;
else return 1;
}
-------------------------------------------------------------------------------------------------------
Comments
Take
a look at the code. At first let's talk some about the parameters to this fu
The
host param is understood I hope. The mxhosts param is a pointer to an array
of
char pointers. So in the caller code you'll have to declare something along
the
following lines:
char *mxhosts[MAXMXHOSTS];
You
should then pass this to the fu
first
pointer of course, which is &mxhosts[0].
These
pointers will at the end point to a big buffer that will contain all the
answers.
You should also declared such a buffer in the following manner:
char mxbuf[MXBUFFERSIZE];
The
buffer size is 128 times the maximum number of MX hosts wanted.
The
pointers to specific parts in this buffers will be set in the GetMXRecord
fu
You
might be asking yourself why this weird looking scheme. Well, while I was
going
over sendmail's source for this fu
in
this model. If all the answers are located in the mxbuf, and you naturally
may
want to sort the answers so that the lowest priority answer is the first,
you
can declare pointers to each answer, and it would be much easier to sort
an
array of pointers against a uniform buffer with several answers each
different
in size.
Look
at the bubble sort part of the fu
it
appears like this in the sendmail source file as well.
The
mxprefs array is an array of unsigned shorts you need to pass so that
the
fu
You
will usually declare it in the called routine and pass it like this:
u_short
prefs[MAXMXHOSTS];
Then
you should pass a pointer to the beginning of this array: &prefs[0].
Another
thing to notice in this fu
gets
the short value pointer to by a pointer that points to some memory.
The
short type can be different in size accross platform therefore it's
very
important to use such a macro that is defined in the standard library
of
the OS. The most important thing about this macro is that it also adva
the
pointer to after the short value, so you can continue from there.
The
last two parts are the bubble sorter that will sort the pointers according
to
each's prefere
the
list (you may never know).
It's
important to note that a DNS server might contain MX resource records that
have
the same priority. In that case, you can either leave it like it is, or
you
can try to somehow differentiate between them so you'll know which one to
put
first. Sendmail has a fu
so
that it'll know which one to put in first. I consider this to be redundant
because
with both the same priority, it doesn't really matter which one is
first.
After all, that is the point of the priority field!
IMO
this should be left untouched.
Also
notice the error handling of the res_search call. You can define your own
error
codes to return. You don't have to rely on mine. Note that if you really
want
to dig into this, the sendmail's source contains quite an extensive
error
controlling, for what end, who knows..
Last
but not least, the fu
this
value can be used to iterate through the array of pointers (mxhosts)
by
the caller routine.
Summary
I
hope I showed you something you were interested in knowing with this essay.
The
negotiation with DNS servers is one such subject that doesn't contain
much
documentation, and IMO those that do deal with DNS interaction don't
contain
nearly enough information about the subject.
That
is it for today, let's hope some one will benefit from this little
tutorial
of mine.
For
any comments/questions/suggestions/bug reports, I can be contacted at:
lordsoth8@hotmail.com
lordsoth@immortaldescendants.org
or
via ICQ # : 5178515
Lord
Soth
17/11/2000
Greetings:
Greetings
go to ALL the Immortal Descendants members, all the +HCU members, the +Sandman
(I owe him special thanks , si
Of
course, Ghirrizibo (not sure how to type it) for his wonderful IceDump util,
and many other I probably forgot, if I did, I apologize!!