sip.edu - ISN Recipes for SER
Subject: SIP in higher education
List archive
- From: Ben Teitelbaum <>
- To: "SIP.edu" <>
- Subject: ISN Recipes for SER
- Date: Thu, 20 Oct 2005 13:55:00 -0400
Take a look at the attached contribution to the ISN cookbook. These
recipes have been tested on the SER that is serving the internet2.edu
domain.
Any questions, comments, or experiential feedback on these is
very welcome.
-- ben
(sip:
ISN:7031*260)
SER Recipes
This section demonstrates how inbound and outbound ISN routing can be
made to work on SIP Express Router (SER).
Routing Inbound ISN Calls
Exactly how inbound ISN calls (those destined for your users) are
routed is highly site-specific. In the implementation shown here, a
user's primary identity is an alphanumeric SIP URI equivalent to his
email address. ISNs are aliases for these primary identities and
phone numbers are fallback routes to use when a user has no registered
SIP user agents (classical SIP.edu). Alternatively, a domain might
decide that the ISN is the primary identity with which a user agent
registers.
In the example used here, all ISNs are stored in SER's MySQL aliases
table with username equal to ISN prepended by "__isn__" and contact
equal to the user's URI. This is to avoid potential conflicts with
actual usernames and their SIP.edu (telephone number aliases). The
aliases table is updated nightly by an external utility that extracts
email address and telephone number for all employees from the
corporate person directory. Each user is assigned an ISN equal to the
last four, five, or six digits of his telephone number (whichever is
shortest and non-ambiguous). For example, the aliases table might
contain the following bindings:
+-------------+------------------------------+
| username | contact |
+-------------+------------------------------+
| bob |
sip:
|
| __isn__7031 |
sip:
|
+-------------+------------------------------+
In the ser.cfg fragment shown below, inbound ISNs are identified and
delegated to the route[4] block. The structure of an inbound ISN will
differ depending on the ISN NAPTR record used by the domain. Option 1
assumes that inbound ISNs will be purely numeric, while Option 2
assumes that a full ISN (including the asterisk and the local ITAD)
will be received.
<pre>
route{
# ...
# Two options (comment out one).
# Option 1: Any string of digits in the user part is regarded as
# an ISN. This is what is expected when using an ISN NAPTR like
# this:
# *.255 IN NAPTR 100 10 "u" "E2U+sip"
#
"!^\\+*([^\\*]*)!sip:\\!"
.
if
(uri=~"sip:[0-9]+@.*sip.bigu\.edu")
{
route(4); # Route inbound ISN calls
};
# Option 2:
# *.255 IN NAPTR 100 10 "u" "E2U+sip"
#
"!^\\+*([^\\*]*)!sip:\\1\\*!"
.
if
(uri=~"sip:[0-9]+\*255@.*sip.bigu\.edu")
{
# substr_uri() requires textops module
subst_uri('/sip:([0-9]+)\*255@(.*)/sip:\1@\2/g');
route(4); # Route inbound ISN calls
};
# ...
}
# Route inbound ISN call
route[4] {
# Assert that URI is in proper format
if (!
(uri=~"sip:[0-9]+@.*sip.bigu\.edu"))
{
xlog("L_CRIT", "%Tf: route[4] called with improper URI\n");
break;
};
prefix("__isn__");
xlog("L_INFO", "%Tf: Routing inbound ISN Call %ru (From [%fu], To
[%tu])\n");
if (!lookup("aliases")) {
xlog("L_ERR", "%Tf: Invalid ISN %ru (From [%fu], To [%tu])\n");
sl_send_reply("404", "Not Found");
break;
};
# Check whether user is reachable via a registered user agent (UA)
if (!lookup("location")) {
# If no registered UA, call desk phone (SIP.edu)
if (!lookup("aliases")) {
xlog("L_ERR", "%Tf: Rejecting Call %ru (From [%fu], To [%tu])\n");
sl_send_reply("404", "Not Found");
break;
};
}; # End of lookup location block
# Relay the message
if (!t_relay()) {
sl_reply_error();
};
}
</pre>
Routing Outbound ISN Calls
To route outbound ISN calls (those originating from within the domain
served by SER), SER must be configured to recognize a request URI
containing a valid ISN and must resolve the ISN before routing the
message further. Domains, especially those with complex legacy
dialing plans, may chose a local prefix to identify dialed ISNs ("012"
is recommended).
The ser.cfg fragment below show logic for recognizing dialed ISNs.
The first option is to be used when a local ISN prefix is in use,
while the second option identifies an ISN purely by the structure of
the request URI (an asterisk embedded between two strings of digits).
All other routing logic is elided in the example.
<pre>
route{
# ...
# Two options (comment out one).
# Option 1: Local ISN prefix
if (uri =~"^sip:012") {
strip(3); # Remove 3-digit local ISN prefix
route(3); # Route outbound ISN calls
};
# Option 2: No local ISN prefix
if (uri =~"^sip:[0-9]+\*[0-9]+@") {
route(3); # Route outbound ISN calls
};
# ...
}
Once it has been recognized that an ISN is being called, it must be
resolved through an ENUM-like NAPTR resolution. Unfortunately, SER's
ENUM module imposes some restrictions that prevent it from being used
for ISN resolution. The route[3] block (shown below) demonstrates two
options of working around these limitations.
The first option is to use Tello's ISN resolver, which will perform
the resolution and issue a "302 Redirect"-style response. Note that
this will only respond with the first (highest preference) URI in any
list of NAPTRs, including "tel" or other URI methods. Also note that
this method relies on an external service with no caching of results
to fall back on should use of that service be disrupted.
The second option is to perform the resolution locally, using SER's
exec_dset method and an external ISN resolver (isn-resolv.pl) that is
run locally. Open source code for isn-resolv.pl follows the ser.cfg
fragment below.
# Route outgoing ISN calls
route[3] {
# Assert that uri is in proper format
if (! (uri =~"^sip:[0-9]+\*[0-9]+@")) {
xlog("L_CRIT", "%Tf: route[3] called with improper ISN\n");
break;
};
# Two options (comment out one).
# Option 1: Tello's ISN resolver
rewritehostport("public.tello.com:5060");
if (!t_relay()) {
sl_reply_error();
};
# Option 2: Local Perl NAPTR resolver (see code below)
if (!exec_dset("/usr/local/bin/isn-resolv.pl")) {
sl_send_reply("404", "Not Found");
break;
} else {
xlog("L_INFO", "%Tf: Routing outbound ISN Call %ru (From [%fu], To
[%tu])\n");
# Relay the message
if (!t_relay()) {
sl_reply_error();
};
};
} # end of ISN route block
</pre>
<pre>
#! /usr/bin/perl -w
# isn-resolv.pl
#
# Take URI containing an ISN as first argument and perform ENUM-like
# ISN resolution, returning resulting URI.
#
# Suitable for use in SER (e.g. exec_dset("isn-resolv.pl"))
#
# Written by Ben Teitelbaum, http://people.internet2.edu/~ben/
# Copyright (c) 2005, Internet2
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the name of the Internet2 nor the names of its contributors
# may be used to endorse or promote products derived from this
# software without explicit prior written permission.
#
# You are under no obligation whatsoever to provide any enhancements to
# Internet2 or its contributors. If you choose to provide your
# enhancements, or if you choose to otherwise publish or distribute your
# enhancements, in source code form without contemporaneously requiring
# end users to enter into a separate written license agreement for such
# enhancements, then you thereby grant Internet2 and its contributors a
# non-exclusive, royalty-free, perpetual license to install, use,
# modify, prepare derivative works, incorporate into the software or
# other computer software, distribute, and sublicense your enhancements
# or derivative works thereof, in binary and source code form.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
# NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY
# QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
# EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS, OR THE UNIVERSITY
# CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# If Net::DNS is missing from your Perl installation, you must install
# it before using this script. Here are a few ways to grab it:
#
# Web: http://www.net-dns.org/download/
# Debian: [sudo] apt-get install libnet-dns-perl
# Red Hat: [sudo] up2date -i perl-Net-DNS
# CPAN: [sudo] cpan -i Net::DNS
#
use Net::DNS;
use strict;
#
# Make sure that $isn_root is set correctly.
#
my $isn_root = 'freenum.org';
my $log = '/tmp/isn.log';
$\ = "\n";
my $res = Net::DNS::Resolver->new;
my $uri;
sub enumize_uri($) {
my $isn = shift;
# Extract ISN from given URI, exiting if URI doesn't contain one
$isn =~
s/^(sip:)?([0-9]*\*[0-9]+)\@?[^\@]*/$2/
|| die("Invalid input: $isn (URI must contain a valid ISN)");
my ($num, $itad) = split(/\*/, $isn);
return join('.', reverse(split(/ */, $num))) . ".$itad.$isn_root";
}
#
# URI to process is first ARG
#
$_ = shift;
chomp;
$uri = $_;
$uri =~ s/(sip:)?([0-9]*\*[0-9]+)/$2/;
my $dname = &enumize_uri($uri);
my $query = $res->query($dname, 'NAPTR') || exit;
# Apply ENUM-style NAPTR algorithm
#
# Strip out non-matching services and record types and any rule with a
# flag other than "U"; then sort by order, preference
my @rr =
sort {$a->order <=> $b->order || $a->preference <=> $b->preference }
grep {$_->type =~ /NAPTR/ && $_->service =~ /^E2U/ && $_->flags =~ /^U$/i}
$query->answer;
# Loop through rules until a match is found
foreach my $rule (@rr) {
my $subst_regexp = $rule->regexp;
# FIXME: NAPTR uses POSIX extended regular expressions, which are
# different from Perl's. I don't understand all the differences,
# but one difference is that @ needs to be escaped.
$subst_regexp =~ s/\@/\\\@/g;
my($nothin, $regexp, $replacement) = split(/\!/, $subst_regexp);
# Backrefs (e.g. \1, \2) won't be available in eval'd assignment
# below, so convert to Perl's $1-$9 match variables. Note that
# per RFC2915, POSIX ERE backrefs only go up to \9.
$replacement =~ s/\\([1-9])/\$$1/g;
if ($uri =~ $regexp) {
eval("\$uri = \"$replacement\"");
print $uri;
exit(0);
}
}
exit(1);
</pre>
--
Ben Teitelbaum http://people.internet2.edu/~ben/
- ISN Recipes for SER, Ben Teitelbaum, 10/20/2005
Archive powered by MHonArc 2.6.16.