An Email Testing Utility

  • Presented by Barry Brevik

Reference Materials

  • Book: "Internet Email Protocols" by Kevin Johnson,
       Addison-Wesley ISBN 0-20143-288-9.
  • Book: "Network Programming with Perl" by Lincoln D. Stein,
       Addison-Wesley ISBN 0-0201-61571-1.
  • Book: "DNS and BIND" by Paul Albitz & Cricket Liu, O'Reilly
       ISBN 1-56592-236-0.
  • Module: "Net::DNS POD". Documentation is a bit thin, IMO.
  • Various Internet Sites: For example,

Typical DNS Zone File

Operating System
Forward Lookup Zone
Reverse Lookup Zone

Each entry in the file below is referred to as a 'Resource Record'. 10800 IN SOA (
                              20110129 ; Serial
                              10800    ; Refresh
                              3600     ; Retry
                              604800   ; Expire
                              3600 )   ; Minimum TTL           10800 IN TXT "v=spf1 a mx -all"           10800 IN A           10800 IN MX 100 INBOUND.OBEDIENCE4LIFE.COM.NETSOLMAIL.NET.           10800 IN NS           10800 IN NS           10800 IN NS       10800 IN A 10800 IN A      10800 IN CNAME MAIL.OBEDIENCE4LIFE.COM.NETSOLMAIL.NET.      10800 IN CNAME SMTP.OBEDIENCE4LIFE.COM.NETSOLMAIL.NET.       10800 IN A

Basics for Using Net::Dns with Perl

use Net::DNS;  # Place this directive at the top of your code (obviously).


# Use the constructor to create a RESOLVER object. Your program can have

# any number of resolvers simultaneously, each keeping it's own state.

my $resolver = Net::DNS::Resolver -> new;


# Resolver queries return DNS PACKET objects. There are 3 query methods:

search - Uses the 'search list' which is a list of domains to be

#          searched and which can be present in /etc/resolv.conf

#          or possibly in the ENVIRONMENT. On Windows machines it looks

#          for a search list in the REGISTRY.

query  - Does not use the search list. If the query name does not have

#          any dots and defnames is true, it appends the default domain.

send   - Neither the searchlist nor the default domain will be used.

my $packet = $resolver -> search($domain);

my $packet = $resolver -> query($domain, 'NS');       # We will use.

my $packet = $resolver -> send($domain, 'SOA', 'IN'); # We will use.


# A PACKET object contains these five data items:

# header     = A list of HEADER objects.

# question   = A list of QUESTION objects.

# answer     = A list of RESOURCE RECORD objects.

# authority  = A list of RESOURCE RECORD objects.

# additional = A list of RESOURCE RECORD objects.

A Stripped Down Working Example

use strict;

use warnings;

use Net::DNS;

my $domain      = '';

my $resolver    = Net::DNS::Resolver -> new;

my $packet      = $resolver -> query($domain, 'NS');

my @nameServers = $packet -> answer;

foreach my $rrNameServer (@nameServers)


  # Even though our query specifically asked for 'NS' records, the

  # documentation states that the resolver may return various record

  # types within the reply, so we have to check each one.

  next unless $rrNameServer -> type eq 'NS';

  print $rrNameServer -> string;


# This is the output from the above program:     9659    IN      NS     9659    IN      NS     9659    IN      NS

Some Additional Net::Dns Methods

# The resolver will use IPv6 transport if the libraries are available and if

# the target nameserver is using an IPv6 transport. Force the resolver to use

# the IPv4 transport.

$resolver -> force_v4(1);

# Set the resolver to use TCP instead of UDP. This can be useful if you are

# experiencing connection issues. Generally, avoid using TCP due to the

# performance penalty for opening and closing a TCP connection.

$resolver -> usevc(1);

# Return a string containing the most recent error.

my $error = $resolver -> errorstring;

# Make calls to search, query and send print debug info to <STDOUT>.

$resolver -> debug(1);

# Get all of the MX resource records for the target domain and using the

# current default name server. This is not necessarily authoritative.

my @mxRecords = mx($resolver, $domain);

# Set or Get the UDP packet size. Default is '0': the transport selects.

$resolver -> udppacketsize(2048);

# There is an entire subset of methods that perform background operations.

# The resolver can be queried to determine if the operation is complete.

A typical (basic) SMTP conversation

use Socket;

my $mailhost = <MX host derived from DNS query>;

my $protocol = (getprotobyname('tcp'))[2];

my $mailport = (getservbyname('smtp', 'tcp'))[2];


# Open a TCP socket and name it 'MAIL'.

socket(MAIL, AF_INET, SOCK_STREAM, $protocol);

select((select(MAIL), $| = 1)[0]);  # Unbuffer the socket.


# Connect the MAIL socket to the target mail server. S n a4 x8 indicates an unsigned

# short (the protocol), followed by a short in network order (the port number), a

# four byte ASCII string (the packed target address), and eight null bytes.

connect(MAIL, pack('Sna4x8', AF_INET, $mailport, $mailhost))


print MAIL "helo $domain\r\n ";# Must use \r\n instead of \n

print MAIL "mail from <$from>\r\n ";# when programming sockets.

print MAIL "rcpt to <$target-user>\r\n";

print MAIL "data\r\n";


# Send the entire message which includes the header and any MIME encoded attachments

# you may have. When you are finished sending data, print a single dot '.' as the

# first character on an otherwise blank line.

print MAIL ".\r\n";

print MAIL "quit\r\n";