ENUM 遵循 DNS 协议。ENUM是IETF的电话号码映射工作组定义的一个协议——RFC2916,RFC2916的题目为“E.164号码和域名系统”。它定义了将E.164号码转换为域名形式放在DNS服务器数据库中的方法,每个由E.164号码转化而成的域名可以对应一系列的统一资源辨识URI,从而使国际统一的E.164电话号码成为可以在互联网中使用的网络地址资源。
根据ENUM格式的定义,将电话号码变成了DNS中的域名形式。每个E.164号码形成的域名可以对应多条网络资源记录,称为统一资源标志URI,它是采用另一个IETF建议RFC2915定义的格式,称为“名称权威指针”(NAPTR)。
在IMS测试中,需要用到ENUM服务器,一般用bind来替代,需要配置各种号码。下面是一个模拟器,可以很方便的完成一些简单功能,而避开用真正的ENUM
根据ENUM格式的定义,将电话号码变成了DNS中的域名形式。每个E.164号码形成的域名可以对应多条网络资源记录,称为统一资源标志URI,它是采用另一个IETF建议RFC2915定义的格式,称为“名称权威指针”(NAPTR)。
在IMS测试中,需要用到ENUM服务器,一般用bind来替代,需要配置各种号码。下面是一个模拟器,可以很方便的完成一些简单功能,而避开用真正的ENUM
#!/usr/bin/perl
use strict;
use IO::Socket::INET;
use POSIX 'WNOHANG';
use Getopt::Long;
use constant PORT => 53;
use constant SUFFIX => '.e164.arpa';
use constant REALM => 'qd.lucentlab.com';
use constant USAGE => <<USE;
This is a ENUM simulator, only supports 2 response messages now
1. Query success with one record
2. Query failed with "No such Name"
Usage:
$0 --port port --prefix prefix --suffix suffix --realm realm[--debug]
port Default port is 53
prefix Reponse message type 1 for DNs with this prefix
Other DN will response with type 2
suffix The suffix of query message, Default is '.e164.arpa'
realm The SIPURI domain/realm, default is 'qd.lucent.com'
debug Show more debug message
Example:
$0 --prefix 1543 --debug
$0 --prefix 1543 --suffix '.e164.cn'
Contact: Tristan Yu (tyu@)
USE
my ($port, $prefix, $suffix, $realm, $debug, $help);
my $file;
err_exit(USAGE, 255) unless GetOptions(
'port=s' => \$port, 'prefix=s' => \$prefix, 'suffix=s' => \$suffix,
'realm=s' => \$realm, 'debug' => \$debug, 'help' => \$help,
'file=s' => \$file );
if ($help) {
print USAGE;
exit 0;
}
if ( $file ) {
eval(require $file) || die "$file format is wrong, check the program help";
}
$port ||= PORT;
$suffix ||= SUFFIX;
$realm ||= REALM;
err_exit("\tPort should be digits", 1) unless $port =~ /^\d+$/;
err_exit("\tMust have a prefix", 1) if ($prefix eq "");
if ($debug) {
print "Configuration data:\n";
print "Port: $port, Prefix: $prefix, Suffix: $suffix, Realm: $realm, Debug: $debug\n";
}
# MAIN STARTS HERE #
$| = 1;
$/ = '\012\015';
my $ok_prefix = $prefix;
my $count = 0;
$SIG{INT} = sub { print "Totally processed $count ENUM queries\n"; exit 0 };
$SIG{ALRM} = sub { print "X\n";};
$SIG{CHLD} = sub { while ( waitpid(-1, WNOHANG)>0 ) {}};
#Do not change any value below in the while loop
#Variables for ENUM message header
my $corrid = 1; # 16-bit field, used to correlate queries and responses.
my $qr = 1; # 1-bit field, that identifies the message as a response.
my $opcode = 0; # 4bits field to indicate the operation. 0 means QUERY, Standard query
my $aa = 1; # 1-bit field, Authoritative Answer, 1 means "Not authoritative."
my $tc = 0; # 1-bit field, Truncation, 0 means Not truncated.
my $rd = 1; # 1-bit field, Recursion Desired
my $ra = 1; # 1-bit field, Recursion Available
my $ad = 0; # 1-bit field, Authenticated data
my $cd = 0;
my $z = 0; # Must be 0 for NOW (RFC1035)
my $rcode = 0; # 4-bit field, Response Code, is set by the NS to identify the status of the query
# 16-bit field that defines the number of entries in the question section
my $ques = 1;
# 16-bit field that defines the number of resource records in the answer section.
my $answ = 1;
# 16-bit field that defines the number of name server resource records in the authority section
my $auth = 1;
# 16-bit field that defines the number of resource records in the additional records section.
my $addt = 0;
my $byte1 = (($qr?0x80:0)|($opcode << 3)|($aa?0x04:0)|($tc?0x02:0)|($rd?0x01:0));
# Response with resualt
my $byte2 = (($ra?0x80:0)|($ad?0x20:0)|($cd?0x10:0)|($rcode));
# Response with "No Such Name"
my $byte2_f = (($ra?0x80:0)|($ad?0x20:0)|($cd?0x10:0)|(0x3));
my $header_fix_ok = pack("C2 n4", $byte1, $byte2, $ques, $answ, $auth, $addt);
my $header_fix_nf = pack("C2 n4", $byte1, $byte2_f, $ques, 0, $auth, $addt);
#Variables for ENUM message answer
my $dname = 0xC000; #Indicator this is a pointer
my $type = 35; # NAPTR, Naming Authority Pointer.
my $class = 1; # IN, Internet.
my $ttl = 0;
my $order = 100;
my $pref = 100;
# "u" flag means that the next step is not a DNS lookup but that the output
# of the Regexp field is an URI that adheres to the 'absoluteURI' product
my $flag = "u";
my $serv = 'E2U+sip';
#Variables for ENUM message authorize
my $aname = 0xC000; #Indicator this is a pointer
my $atype = 2; #A NS record for the domain name
my $attl = 3600 * 24; # 24 hours
my $ns = dns_normalize($realm);
my $auth_pre_data = pack("n2 L n", $atype, $class, $attl, length($ns)) . $ns;
use constant HEADER_LENGTH => length pack 'n C2 n4', (0)x7;
use constant QUESTION_LENGTH => length pack 'n2', (0)x2;
# Setup Socket
my $enum_server = IO::Socket::INET->new(
#LocalAddr => '135.252.29.135',
LocalPort => $port,
Proto => 'udp',
) or die "Could not open UPD port 53 $!";
binmode $enum_server;
print "UDP ENUM server is running ...\n";
while (1) {
my $data_buf;
next unless my $peer_addr = $enum_server->recv($data_buf, 1024);
defined ( my $enum_s = fork())
or do { print "\nFork an ENUM processor failed\n", next;};
if ($enum_s == 0) {
if ($debug) {
my ($portno, $ipaddr) = sockaddr_in($peer_addr);
print "Host " . inet_ntoa($ipaddr) . ":" . $portno ;
}
alarm(1);
my $corrid = unpack('n', $data_buf);
my ($qname, $offset) = parse_question(\$data_buf, HEADER_LENGTH);
print "\nQuery for $qname [$corrid]\n" if $debug;
#Generate query with query name
my $query_data = dns_normalize($qname) . pack("n2", $type, $class);
my $e164_num = to_e164($qname);
print "E164 is $e164_num\n" if $debug;
my $dname_ptr = HEADER_LENGTH;
my $aname_ptr = $dname_ptr + 2 * length($e164_num);
my $header_data = "";
my $answer_data = "";
if ($e164_num =~ /^$ok_prefix/) {
print "Query successfully response \n" if $debug;
#Generate header with the corrilation identity
$header_data = pack("n", $corrid) . $header_fix_ok;
#Generate the DNS regular expression
my $regex = '!^.*$!sip:+' . $e164_num . '@' . $realm . '!';
my $relen = length($regex); # The length of the regex
my $rdata = pack("n2 C a C a7 C a$relen C",
$order, $pref, length($flag), $flag, length($serv), $serv, $relen, $regex, 0) ;
$answer_data = pack("n", ($dname|$dname_ptr))
. pack("n2 L n", $type, $class, $ttl, length($rdata)) . $rdata;
} else {
print "Query No such Name response \n" if $debug;
#Response with No such Name
$answer_data = "";
$header_data = pack("n", $corrid) . $header_fix_nf;
}
my $auth_data = pack("n", ($aname|$aname_ptr)) . $auth_pre_data;
$enum_server->send($header_data.$query_data.$answer_data.$auth_data);
alarm(0);
print "Done for $qname\n" if $debug;
print "." if not $debug;
exit 0;
}
$count++;
print "\b" x 80 if ( not $debug and ($count % 80 == 0));
}
sub dns_normalize {
my ($i) = @_;
my @ele = split /\./, $i;
my $message = "";
foreach my $e (@ele) {
$message .= pack("C a*", length($e), $e);
}
$message .= pack("C", 0);
return $message;
}
# Function to parse e164 number
sub to_e164 {
my ($var) = @_;
$var =~ s/$suffix//g;
$var =~ s/\.$//o;
my $e164_num = join("", reverse(split /\./, $var));
return $e164_num;
}
# Function to parse the incoming message
sub parse_question {
my ($packet, $offset) = @_;
my $packetlen = length($$packet);
my $name = '';
while ( $offset < $packetlen ) {
unless ( my $length = unpack("\@$offset C", $$packet) ) {
$name =~ s/\.$//o;
return ($name, ++$offset);
} else {
my $element = substr($$packet, ++$offset, $length);
$name .= $element.'.';
$offset += $length;
}
}
return undef;
}
sub err_exit {
my ($err_msg ,$err_no) = @_;
print $err_msg, "\n";
exit $err_no;
}