1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
- <section id="ser_intro" xmlns:xi="http://www.w3.org/2001/XInclude">
- <sectioninfo>
- <revhistory>
- <revision>
- <revnumber>$Revision$</revnumber>
- <date>$Date$</date>
- </revision>
- </revhistory>
- </sectioninfo>
- <title>Introduction to SER</title>
- <section id="requestrouting">
- <title>Request Routing and SER Scripts</title>
- <para>
- The most important concept of every SIP server is that of
- request routing. The request routing logic determines the next
- hop of a request. It can be for example used to implement user
- location service or enforce static routing to a gateway. Real-world
- deployments actually ask for quite complex routing logic, which
- needs to reflect static routes to PSTN gateways, dynamic routes
- to registered users, authentication policy, capabilities of
- SIP devices, etc.
- </para>
- <para>
- SER's answer to this need for routing flexibility is a routing
- language, which allows administrators to define the SIP request
- processing logic in a detailed manner. They can for example easily
- split SIP traffic by method or destination, perform user location,
- trigger authentication, verify access permissions, and so on.
- </para>
- <para>
- The primary building block of the routing language are <emphasis>actions</emphasis>.
- There are built-in actions (like <command>forward</command> for stateless forwarding
- or <command>strip</command> for stripping URIs) as
- well as external actions imported from shared library modules. All actions can
- be combined in compound actions by enclosing them in braces,
- e.g. <command>{a1(); a2();}</command>.
- Actions are aggregated in one or more <emphasis>route blocks</emphasis>.
- Initially, only the default routing block denoted by <command>route[0]</command>
- is called. Other routing blocks can be called by the action
- <command>route(blocknumber)</command>, recursion is permitted.
- The language includes <emphasis>conditional statements</emphasis>.
- </para>
- <para>
- The routing script is executed for every received request in sequential order.
- Actions may return positive/negative/zero value.
- Positive values are considered success and evaluated as
- TRUE in conditional expressions. Negative values are considered FALSE.
- Zero value means error and leaves execution of currently processed
- route block. The route block is left too, if <command>break</command> is explicitly
- called from it.
- </para>
- <para>
- The easiest and still very useful way for <application>ser</application>
- users to affect request routing logic is
- to determine next hop statically. An example is
- routing to a PSTN gateway whose static IP address is well known.
- To configure static routing, simply use the action
- <command>forward( IP_address, port_number)</command>.
- This action forwards an incoming request "as is" to the
- destination described in action's parameters.
- </para>
- <example>
- <title>Static Forwarding</title>
- <programlisting>
- # if requests URI is numerical and starts with
- # zero, forward statelessly to a static destination
- if (uri=~"^sip:0[0-9]*@iptel.org") {
- forward( 192.168.99.3, 5080 );
- }
- </programlisting>
- </example>
- <para>
- However, static forwarding is not sufficient in many cases.
- Users desire mobility and change their location frequently.
- Lowering costs for termination of calls in PSTN requires
- locating a least-cost gateway. Which next-hop is taken may
- depend on user's preferences. These and many other scenarios
- need the routing logic to be more dynamic. We describe in
- <xref linkend="conditions"/> how to make request processing
- subject to various conditions and in
- <xref linkend="urirewriting"/> how to determine next SIP hop.
- </para>
- </section>
- <section id="conditions">
- <title>Conditional Statements</title>
- <para>
- A very useful feature is the ability to make routing
- logic depend on a condition. A script condition may for
- example distinguish between request processing for
- served and foreign domains, IP and PSTN routes,
- it may split traffic by method or username, it
- may determine whether a request should be authenticated
- or not, etc. <application>ser</application>
- allows administrators to form conditions based on
- properties of processed request, such as method or uri,
- as well as on virtually any piece of data on the
- Internet.
- </para>
- <example>
- <title>Conditional Statement</title>
- <para>
- This example shows how a conditional statement is
- used to split incoming requests between a PSTN
- gateway and a user location server based on
- request URI.
- </para>
- <programlisting>
- # if request URI is numerical, forward the request to PSTN gateway...
- if (uri=~"^sip:[0-9][email protected]") { # match using a regular expression
- forward( gateway.foo.bar, 5060 );
- } else { # ... forward the request to user location server otherwise
- forward( userloc.foo.bar, 5060 );
- };
- </programlisting>
- </example>
- <para>
- Conditional statements in <application>ser</application> scripts may depend
- on a variety of expressions. The simplest expressions are
- action calls. They return true if they completed successfully or false otherwise.
- An example of an action frequently used in conditional statements is
- <command moreinfo="none">search</command> imported from textops module.
- <command moreinfo="none">search</command> action leverages textual
- nature of SIP and compares SIP requests against a regular expression.
- The action returns true if the expression matched, false otherwise.
- <example>
- <title>Use of <command>search</command> Action in Conditional Expression</title>
- <programlisting>
- # prevent strangers from claiming to belong to our domain;
- # if sender claims to be in our domain in From header field,
- # better authenticate him
- if (search("(f|From): .*@mydomain.com)) {
- if (!(proxy_authorize("mydomain.com" /* realm */,"subscriber" /* table name */ ))) {
- proxy_challenge("mydomain.com /* ream */, "1" /* use qop */ );
- break;
- }
- }
- </programlisting>
- </example>
- </para>
- <para>
- As modules may be created, which export new functions, there is virtually
- no limitation on what functionality <application>ser</application>
- conditions are based on. Implementers may introduce new actions whose
- return status depends on request content or any external data as well. Such actions
- can query SQL, web, local file systems or any other place which can provide
- information wanted for request processing.
- </para>
- <para>
- Furthermore, many request properties may be examined using existing built-in operands
- and operators. Available left-hand-side operands and legal combination with
- operators and right-hand-side operands are described in <xref linkend="logicalexpr"/>.
- Expressions may be grouped together using logical operators:
- negation (<command>!</command>), AND (<command>&&</command>), OR (<command>
- ||</command> and precedence parentheses (<command>()</command>).
- </para>
- <section id="operators">
- <title>Operators and Operands</title>
- <para>
- There is a set of predefined operators and operands
- in ser, which in addition to actions may be evaluated
- in conditional expressions.
- </para>
- <para>
- Left hand-side operands, which <application>ser</application>
- understands are the following:
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>method</emphasis>, which refers to
- request method
- such as REGISTER or INVITE
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>uri</emphasis>, which refers to current request URI,
- such as
- "sip:[email protected]"
- <note>
- <para>
- Note that "uri" always refers to current
- value of URI, which is subject to change
- be uri-rewriting actions.
- </para>
- </note>
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>src_ip</emphasis>, which refers to IP address from
- which a request came.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>dst_ip</emphasis> refers to server's IP address
- at which a request was received
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>src_port</emphasis> port number from which a SIP
- request came
- </para>
- </listitem>
- </itemizedlist>
- </para>
- <para>
- ser understands the following operators:
- <itemizedlist>
- <listitem>
- <para>
- == stands for equity
- </para>
- </listitem>
- <listitem>
- <para>
- =~ stands for regular expression matching
- </para>
- </listitem>
- <listitem>
- <para>
- logical operators: and, or, negation, parentheses
- (C-notation for the operators may be used too)
- </para>
- </listitem>
- </itemizedlist>
- </para>
- <table id="logicalexpr">
- <title>Valid Combinations of Operands and Operators in Expressions</title>
- <tgroup cols="4">
- <thead>
- <row>
- <entry>
- left-hand-side operand
- </entry>
- <entry>
- valid operators
- </entry>
- <entry>
- valid right-hand side operators
- </entry>
- <entry>
- examples/comments
- </entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry>
- method
- </entry>
- <entry>
- == (exact match), =~ (regular expression matching)
- </entry>
- <entry>
- string
- </entry>
- <entry>
- method=="INVITE" || method=="ACK" || method=="CANCEL"
- </entry>
- </row>
- <row>
- <entry>
- uri
- </entry>
- <entry>
- == (exact match), =~ (regular expression matching)
- </entry>
- <entry>
- string
- </entry>
- <entry>
- uri=="sip:[email protected]" matches only if exactly this uri
- is in request URI
- </entry>
- </row>
- <row>
- <entry>
-
- </entry>
- <entry>
- == (exact match)
- </entry>
- <entry>
- myself
- </entry>
- <entry>
-
- the expression uri==myself is true if the host part in
- request URI equals a server name or a server alias (set using
- the alias option in configuration file)
-
- </entry>
- </row>
- <row>
- <entry>
- src_ip
- </entry>
- <entry>
- == (match)
- </entry>
- <entry>
- IP, IP/mask_length, IP/mask, hostname, myself
- </entry>
- <entry>
- src_ip==192.168.0.0/16 matches requests coming from
- a private network
- </entry>
- </row>
- <row>
- <entry>
- dst_ip
- </entry>
- <entry>
- == (match)
- </entry>
- <entry>
- IP, IP/mask_length, IP/mask, hostname, myself
- </entry>
- <entry>
- dst_ip==127.0.0.1 matches if a request was received
- via loopback interface
- </entry>
- </row>
- <row>
- <entry>
- src_port
- </entry>
- <entry>
- == (match)
- </entry>
- <entry>
- port number
- </entry>
- <entry>
- port number from which a request was sent, e.g. src_port==5060
- </entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- <example>
- <title>
- More examples of use of <application>ser</application> operators and operands in conditional
- statements
- </title>
- <programlisting>
- # using an action as condition input; in this
- # case, an actions 'search' looks for Contacts
- # with private IP address in requests; the condition
- # is processed if such a contact header field is
- # found
- if (search("^(Contact|m): .*@(192\.168\.|10\.|172\.16)")) {
- # ....
- # this condition is true if request URI matches
- # the regular expression "@bat\.iptel\.org"
- if (uri=~"@bat\.iptel\.org") {
- # ...
- # and this condition is true if a request came
- # from an IP address (useful for example for
- # authentication by IP address if digest is not
- # supported) AND the request method is INVITE
- # if ( (src_ip==192.68.77.110 and method=="INVITE")
- # ...
- </programlisting>
- </example>
- </section> <!-- operators and operands -->
- <section>
- <title>URI Matching</title>
- <para>URI matching expressions have a broad use in a SIP server
- and deserve more explanation. Typical uses of
- URI matching include implementation of numbering plans,
- domain matching,
- binding external applications to specific URIs,
- etc. This section shows examples of typical applications
- of URI-matching.
- </para>
- <section id="domainmatching">
- <title>Domain Matching</title>
- <para>
- One of most important uses of URI matching is deciding
- whether a request is targeted to a served or outside domain.
- Typically, different request
- processing applies. Requests for outside domains
- are simply forwarded to them, whereas
- more complex logic applies to requests for a served domain.
- The logic may include saving user's contacts
- when REGISTER requests are received, forwarding requests
- to current user's location or a PSTN gateways,
- interaction with external applications, etc.
- </para>
- <para>
- The easiest way to decide whether a request belongs
- a served domain is using the <command>myself</command>
- operand.
- The expression "uri==myself" returns true if domain name
- in request URI matches name of the host at which
- <application>ser</application> is
- running. This may be insufficient in cases when
- server name is not equal to domain name for which the server
- is responsible. For example, the "uri==myself" condition
- does not match if a server "sipserver.foo.bar"
- receives a request for "sip:[email protected]". To
- match other names in URI than server's own,
- set up the <varname>alias</varname> configuration
- option. The option may be used multiple times,
- each its use adds a new item to a list of aliases.
- The myself condition returns then true
- also for any hostname on the list of aliases.
- <example>
- <title>Use of uri==myself Expression</title>
- <programlisting>
- # ser powers a domain "foo.bar" and runs at host sipserver.foo.bar;
- # Names of served domains need to be stated in the aliases
- # option; myself would not match them otherwise and would only
- # match requests with "sipserver.foo.bar" in request-URI
- alias="foo.bar"
- alias="sales.foo.bar"
- route[0] {
- if (uri==myself) {
- # the request either has server name or some of the
- # aliases in its URI
- log(1,"request for served domain")
- # some domain-specific logic follows here ....
- } else {
- # aha -- the server is not responsible for this
- # requests; that happens for example with the following URIs
- # - sip:[email protected]
- # - sip:[email protected]
- log(1,"request for outbound domain");
- # outbound forwarding
- t_relay();
- };
- }
- </programlisting>
- </example>
- </para>
- <para>
- It is possible to recognize whether a request belongs to
- a domain using regular expressions too. Care needs to
- be paid to construction of regular expressions. URI
- syntax is rich and an incorrect expression would result
- in incorrect call processing. The following example shows
- how an expression for domain matching can be formed.
- <example id="redomainmatching">
- <title>Domain Matching Using Regular Expressions</title>
- <para>
- In this example, server named "sip.foo.bar" with
- IP address 192.168.0.10 is responsible for the
- "foo.bar" domain. That means, requests with the
- following hostnames in URI should be matched:
- <itemizedlist>
- <listitem>
- <para>
- foo.bar, which is the name of server domain
- </para>
- </listitem>
- <listitem>
- <para>
- sip.foo.bar, since it is server's name and some
- devices put server's name in request URI
- </para>
- </listitem>
- <listitem>
- <para>
- 192.168.0.10, since it is server's IP address and
- some devices put server's IP address in request URI
- </para>
- </listitem>
- </itemizedlist>
- Note how this regular expression is constructed. In particular:
- <itemizedlist>
- <listitem>
- <para>
- User name is optional (it is for example never included
- in REGISTER requests) and there are no restrictions on
- what characters it contains. That is what
- <emphasis>(.+@)?</emphasis> mandates.
- </para>
- </listitem>
- <listitem>
- <para>
- Hostname must be followed by port number, parameters
- or headers -- that is what the delimiters
- <emphasis>[:;\?]</emphasis> are good for. If none
- it these follows, the URI must be ended
- (<emphasis>$</emphasis>). Otherwise, longer hostnames
- such as 192.168.0.101 or foo.bar.otherdomain.com would
- mistakenly match.
- </para>
- </listitem>
- <listitem>
- <para>
- Matches are case-insensitive. All hostnames "foo.bar", "FOO.BAR"
- and "FoO.bAr" match.
- </para>
- </listitem>
- </itemizedlist>
- </para>
- <programlisting>
- if (uri=~"^sip:(.+@)?(192\.168\.0\.10|(sip\.)?foo\.bar)([:;\?].*)?$")
- log(1, "yes, it is a request for our domain");
- break;
- };
- </programlisting>
- </example>
- </para>
- </section> <!-- domain matching -->
- <section id="numberingplans">
- <title>Numbering Plans</title>
- <para>
- Other use of URI matching is implementation of dialing
- plans. A typical task when designing a dialing plan for SIP networks
- is to distinguish between "pure-IP" and PSTN destinations.
- IP users typically have either alphanumerical or numerical
- usernames. The numerical usernames are convenient for PSTN
- callers who can only
- use numeric keypads. Next-hop destination of IP users is looked up dynamically
- using user location database. On the other hand, PSTN destinations are
- always indicated by numerical usernames. Requests to PSTN are statically
- forwarded to well-known PSTN gateways.
- </para>
- <example>
- <title>A simple Numbering Plan</title>
- <para>
- This example shows a simple dialing plan which reserves
- dialing prefix "8" for IP users, other numbers
- are used for PSTN destinations and all other non-numerical
- usernames are used for IP users.
- </para>
- <programlisting>
- # is it a PSTN destination? (is username numerical and does not begin with 8?)
- if (uri=~"^sip:[0-79][0-9]*@") { # ... forward to gateways then;
- # check first to which PSTN destination the requests goes;
- # if it is US (prefix "1"), use the gateway 192.168.0.1...
- if (uri=~"^sip:1") {
- # strip the leading "1"
- strip(1);
- forward(192.168.0.1, 5060);
- } else {
- # ... use the gateway 10.0.0.1 for all other destinations
- forward(10.0.0.1, 5060);
- }
- break;
- } else {
- # it is an IP destination -- try to lookup it up in user location DB
- if (!lookup("location")) {
- # bad luck ... user off-line
- sl_send_reply("404", "Not Found");
- break;
- }
- # user on-line...forward to his current destination
- forward(uri:host,uri:port);
- }
- </programlisting>
- </example>
- </section> <!-- numbering plans -->
- </section>
- </section> <!-- conditional statements -->
-
- <section id="urirewriting">
- <title>Request URI Rewriting</title>
- <para>
- The ability to give users and services a unique name using URI
- is a powerful tool. It allows users to advertise how to reach
- them, to state to whom they wish to communicate and what services
- they wish to use.
- Thus, the ability to change URIs is very important and is
- used for implementation of many services.
- "Unconditional forwarding" from user "boss" to user
- "secretary" is a typical example of application relying
- on change of URI address.
- </para>
- <para>
- <application>ser</application> has the ability
- to change request URI in many ways.
- A script can use any of the following
- built-in actions to change request URI or a part of it:
- <command>rewriteuri</command>,
- <command>rewritehost</command>,
- <command>rewritehostport</command>,
- <command>rewriteuser</command>,
- <command>rewriteuserpass</command> and
- <command>rewriteport</command>.
- When later in the script
- a forwarding action is encountered, the action forwards
- the request to address in the rewritten URI.
- <example>
- <title>Rewriting URIs</title>
- <programlisting>
- if (uri=~"[email protected]") {
- rewriteuri("sip:[email protected]")
- # forward statelessly to the destination in current URI, i.e.,
- # to sip:[email protected]:5060
- forward( uri:host, uri:port);
- }
- </programlisting>
- </example>
- </para>
- <para>Two more built-in URI-rewriting commands are of special importance
- for implementation of dialing plans and manipulation of dialing
- prefixes. <command>prefix(s)
- </command>, inserts
- a string "s" in front of SIP address and
- <command>strip(n)</command> takes
- away the first "n" characters of a SIP address.
- See <xref linkend="urirewritingexamples"/> for examples of use of
- built-in URI-rewriting actions.
- </para>
- <para>
- Commands exported by external modules can change URI too
- and many do so.
- The most important application is changing URI using the
- user location database. The command
- <command>lookup(table)</command> looks up current
- user's location and rewrites user's address with it.
- If there is no registered contact, the command returns a negative value.
- <example id="rewriteuri">
- <title>Rewriting URIs Using User Location Database</title>
- <programlisting>
- # store user location if a REGISTER appears
- if (method=="REGISTER") {
- save("mydomain1");
- } else {
- # try to use the previously registered contacts to
- # determine next hop
- if(lookup("mydomain1")) {
- # if found, forward there...
- t_relay();
- } else {
- # ... if no contact on-line, tell it upstream
- sl_send_reply("404", "Not Found" );
- };
- };
- </programlisting>
- </example>
- </para>
- <para>
- External applications can be used to rewrite URI too.
- The "exec" module provides script actions, which start external programs
- and read new URI value from their output. <command>exec_dset</command>
- both calls an external program, passes SIP request elements to it, waits until it completes,
- and eventually rewrites current destination set with its output.
- </para>
- <para>
- It is important to realize that <application>ser</application>
- operates over <emphasis>current URI</emphasis> all the time. If an
- original URI is rewritten by a new one, the original will will be
- forgotten and the new one will be used in any further
- processing. In particular, the uri matching operand and the user
- location action <command>lookup</command> always take current URI
- as input, regardless what the original URI was.
- </para>
- <para>
- <xref linkend="urirewritingexamples"/> shows how URI-rewriting actions affect
- an example URI, sip:[email protected]:6060.
- <table id="urirewritingexamples">
- <title>URI-rewriting Using Built-In Actions</title>
- <tgroup cols="2">
- <thead>
- <row>
- <entry>
- Example Action
- </entry>
- <entry>
- Resulting URI
- </entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry>
- <command>rewritehost("192.168.0.10")</command> rewrites
- the hostname in URI, other parts (including port number) remain unaffected.
- </entry>
- <entry>
- sip:[email protected]:6060
- </entry>
- </row>
- <row>
- <entry>
- <command>rewriteuri("sip:[email protected]");</command> rewrites
- the whole URI completely.
- </entry>
- <entry>
- sip:[email protected]
- </entry>
- </row>
- <row>
- <entry>
- <command>rewritehostport("192.168.0.10:3040")</command>rewrites
- both hostname and port number in URI.
- </entry>
- <entry>
- sip:[email protected]:3040
- </entry>
- </row>
- <row>
- <entry>
- <command>rewriteuser("alice")</command> rewrites user part of URI.
- </entry>
- <entry>
- sip:[email protected]:6060
- </entry>
- </row>
- <row>
- <entry>
- <command>rewriteuserpass("alice:pw")</command> replaces the pair
- user:password in URI with a new value. Rewriting password in URI is of historical
- meaning though, since basic password has been replaced with digest authentication.
- </entry>
- <entry>
- sip:alice:[email protected]:6060
- </entry>
- </row>
- <row>
- <entry>
- <command>rewriteport("1234")</command> replaces port number in URI
- </entry>
- <entry>
- sip:[email protected]:1234
- </entry>
- </row>
- <row>
- <entry>
- <command>prefix("9")</command> inserts a string ahead of user part of URI
- </entry>
- <entry>
- sip:[email protected]:6060
- </entry>
- </row>
- <row>
- <entry>
- <command>strip(2)</command> removes leading characters from user part of URI
- </entry>
- <entry>
- sip:[email protected]:6060
- </entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </para>
- <para>
- You can verify whether you understood URI processing by
- looking at the following example. It rewrites URI
- several times. The question is what is the final URI to which
- the script fill forward any incoming request.
- <example>
- <title>URI-rewriting Exercise</title>
- <programlisting>
- exec_dset("echo sip:[email protected]; echo > /dev/null");
- strip(2);
- if (uri=~"^sip:2") {
- prefix("0");
- } else {
- prefix("1");
- };
- forward(uri:host, uri:port);
- </programlisting>
- </example>
- </para>
- <para>
- The correct answer is the resulting URI will be
- "sip:[email protected]". <command>exec_dset</command>
- rewrites original URI to "sip:[email protected]",
- <command>strip(2)</command> takes
- two leading characters from username away resulting
- in "[email protected]", the condition does not match
- because URI does not begin with "2" any more,
- so the prefix "1" is inserted.
- </para>
- </section> <!-- URI rewriting -->
- <section>
- <title>Destination Set</title>
- <para>
- Whereas needs of many scenarios can by accommodated by maintaining
- a single request URI, some scenarios are better served by
- multiple URIs. Consider for example a user with address
- [email protected]. The user wishes to be reachable at his
- home phone, office phone, cell phone, softphone, etc.
- However, he still wishes to maintain a single public address
- on his business card.
- </para>
- <para>
- To enable such scenarios, <application>ser</application>
- allows translation of a single request URI into multiple
- outgoing URIs. The ability to forward a request to multiple
- destinations is known as <emphasis>forking</emphasis>
- in SIP language. All outgoing URIs (in trivial case one of them)
- are called <emphasis>destination set</emphasis>. The destination
- set always includes one default URI, to which additional URIs
- can be appended. Maximum size of a destination set is limited by
- a compile-time constant, MAX_BRANCHES,
- in <filename>config.h</filename>.
- </para>
- <para>
- Some actions are designed for use with a single URI whereas
- other actions work with the whole destination set.
- </para>
- <para>
- Actions which are currently available for creating the destination
- set are <command>lookup</command> from usrloc module and
- <command>exec_dset</command> from exec module.
- <command>lookup</command> fills in the destination
- set with user contact's registered previously with REGISTER
- requests. The <command>exec</command> actions
- fill in the destination set with output of an external program.
- In both cases, current destination set is completely rewritten.
- New URIs can be appended to destination set by a call to the built-in
- action <command>append_branch(uri)</command>.
- </para>
- <para>
- Currently supported features which utilize destination sets
- are <emphasis>forking</emphasis> and <emphasis>redirection</emphasis>.
- Action <command>t_relay</command> (TM module) for stateful
- forwarding supports forking. If called with a non-trivial destination
- set, <command>t_relay</command> forks
- incoming request to all URIs in current destination set.
- See <xref linkend="rewriteuri"/>. If a user
- previously registered from three locations, the destination set is filled with
- all of them by <command>lookup</command> and the <command>t_relay</command>
- command forwards the incoming request to all these destinations.
- Eventually, all user's phone will be ringing in parallel.
- </para>
- <para>
- SIP redirection is another feature which leverages destination sets.
- It is a very light-weighted method to establish communication
- between two parties with minimum burden put on the server. In
- <application>ser</application>, the action <command>sl_send_reply</command>
- (SL module) is used for this purpose. This action
- allows to generate replies to SIP requests without keeping
- any state. If the status code passed to the action is 3xx,
- the current destination set is printed in reply's Contact header
- fields. Such a reply instructs the originating client to
- retry at these addresses. (See <xref linkend="redirectexample"/>).
- </para>
- <para>
- Most other <application>ser</application> actions ignore destination
- sets: they either do not relate to URI processing (<command moreinfo="none">
- log</command>, for example) or they work only with the default URI.
- All URI-rewriting functions such as
- <command moreinfo="none">rewriteuri</command> belong in this
- category. URI-comparison operands only refer to the first URI
- (see <xref linkend="operators"/>). Also, the built-in action
- for stateless forwarding, <command>forward</command> works only
- with the default URI and ignores rest of the destination set. The reason
- is a proxy server willing to fork must guarantee that the burden
- of processing multiple replies is not put unexpectedly on upstream
- client. This is only achievable with stateful processing.
- Forking cannot be used along with stateless <command>forward</command>,
- which thus only processes one URI out of the whole destination set.
- </para>
- </section> <!-- Destination Set -->
- <section>
- <title>User Location</title>
- <para>
- Mobility is a key feature of SIP. Users are able to use one
- one or more SIP devices and be reachable at them. Incoming requests
- for users are forwarded to all user's devices in use. The key
- concept is that of soft-state registration. Users can
- -- if in possession of valid credentials -- link SIP
- devices to their e-mail like address of record. Their SIP devices
- do so using a REGISTER request, as in <xref linkend="register"/>.
- The request creates a binding between the public address of
- record (To header field) and SIP device's current address
- (Contact header field).
- <example id="register">
- <title>REGISTER Request</title>
- <programlisting>
- REGISTER sip:192.168.2.16 SIP/2.0
- Via: SIP/2.0/UDP 192.168.2.16;branch=z9hG4bKd5e5.5a9947e4.0
- Via: SIP/2.0/UDP 192.168.2.33:5060
- From: sip:[email protected]
- To: sip:[email protected]
- Call-ID: [email protected]
- Date: Wed, 29 Jan 2003 18:13:15 GMT
- CSeq: 101 REGISTER
- User-Agent: CSCO/4
- Contact: sip:[email protected]:5060
- Content-Length: 0
- Expires: 600
- </programlisting>
- </example>
- Similar requests can be used to query all user's current contacts or to
- delete them. All Contacts have certain time to live, when the time expires,
- contact is removed and no longer used for processing of incoming requests.
- </para>
- <para>
- <application>ser</application> is built to do both: update
- user location database from received REGISTER requests and look-up these
- contacts when inbound requests for a user arrive. To achieve high performance,
- the user location table is stored in memory. In regular intervals
- (usrloc module's parameter <varname>timer_interval</varname> determines
- their length), all changes to the in-memory table are backed up in
- <application>mysql</application> database to achieve
- persistence across server reboots. Administrators or application writers
- can lookup list of current user's contacts stored in memory using the
- <application>serctl</application> tool (see <xref linkend="serctl"/>).
- <example>
- <title>Use of <application>serctl</application> Tool to Query User Location</title>
- <screen>
- <![CDATA[
- [jiri@fox jiri]$ sc ul show jiri
- <sip:[email protected]>;q=0.00;expires=456
- <sip:[email protected]>;q=0.00;expires=36000
- ]]>
- </screen>
- </example>
- </para>
- <para>
- Building user location in <application>ser</application> scripts is
- quite easy. One first needs to determine whether a request is for served domain,
- as described in <xref linkend="domainmatching"/>. If that is the case, the script
- needs to distinguish between REGISTER requests, that update user location table,
- and all other requests for which next hop is determined from the table. The
- <command>save</command> action is used to update user location
- (i.e., it writes to it). The <command>lookup</command> actions
- reads from the user location table and fills in destination set with current
- user's contacts.
- <example>
- <title>Use of User Location Actions</title>
- <programlisting>
- # is the request for my domain ?
- if (uri==myself) {
- if (method=="REGISTER") { # REGISTERs are used to update
- save("location");
- break; # that's it, we saved the contacts, exit now
- } else {
- if (!lookup("location") { # no registered contact
- sl_send_reply("404", "Not Found");
- break;
- }
- # ok -- there are some contacts for the user; forward
- # the incoming request to all of them
- t_relay();
- };
- };
- </programlisting>
- </example>
- </para>
- <para>
- Note that we used the action for stateful forwarding,
- <command>t_relay</command>. That's because
- stateful forwarding allows to fork an incoming request to
- multiple destinations. If we used stateless forwarding,
- the request would be forwarded only to one uri out of
- all user's contacts.
- </para>
- </section> <!-- User Location -->
-
- <section>
- <title>External Modules</title>
- <para>
- <application>ser</application> provides the ability to link the server with external
- third-party shared libraries. Lot of functionality which is
- included in the <application>ser</application> distribution is actually located in
- modules to keep the server "core" compact and clean.
- Among others, there are modules for checking max_forwards
- value in SIP requests (maxfwd), transactional processing (tm),
- record routing (rr), accounting (acc), authentication (auth),
- SMS gateway (sms), replying requests (sl), user location
- (usrloc, registrar) and more.
- </para>
- <para>
- In order to utilize new actions exported by a module,
- ser must first load it. To load a module, the directive
- <command>loadmodule "filename"</command>
- must be included in beginning of
- a <application>ser</application> script file.
- </para>
- <example>
- <title>Using Modules</title>
- <para>
- This example shows how a script instructs
- <application>ser</application> to
- load a module and use actions exported by it.
- Particularly, the sl module exports an action
- <command>sl_send_reply</command> which makes
- <application>ser</application> act as a stateless
- user agent and reply all incoming requests with 404.
- </para>
- <programlisting>
- # first of all, load the module!
- loadmodule "/usr/lib/ser/modules/sl.so
- route{
- # reply all requests with 404
- sl_send_reply("404", "I am so sorry -- user not found");
- }
- </programlisting>
- </example>
- <note>
- <para>
- Note that unlike with core commands, all actions exported by
- modules must have parameters enclosed in quotation marks in
- current version of <application>ser</application>. In the following example,
- the built-in action <command>forward</command>
- for stateless forwarding takes IP address and port numbers as
- parameters without quotation marks whereas a module action
- <command>t_relay</command> for stateful
- forwarding takes parameters enclosed in quotation marks.
- <example>
- <title>Parameters in built-in and exported
- actions</title>
- <programlisting>
- # built-in action doesn't enclose IP addresses and port numbers
- # in quotation marks
- forward(192.168.99.100, 5060);
- # module-exported functions enclose all parameters in quotation
- # marks
- t_relay_to_udp("192.168.99.100", "5060");
- </programlisting>
- </example>
- </para>
- </note>
- <para>
- Many modules also allow users to change the way how they work using
- predefined parameters. For example, the authentication module needs
- to know location of MySQL database which contains users' security
- credentials. How module parameters are set using the
- <command>modparam</command> directive is shown in <xref
- linkend="moduleparameters"/>. <command>modparam</command> always
- contains identification of module, parameter name and parameter
- value. Description of parameters available in modules is available
- in module documentation.
- </para>
- <para>
- Yet another thing to notice in this example is module
- dependency. Modules may depend on each other. For example, the
- authentication modules leverages the mysql module for accessing
- mysql databases and sl module for generating authentication
- challenges. We recommend that modules are loaded in dependency
- order to avoid ambiguous server behavior. </para>
- <para>
- <example id="moduleparameters">
- <title>Module Parameters</title>
- <programlisting>
- # ------------------ module loading ----------------------------------
- # load first modules on which 'auth' module depends;
- # sl is used for sending challenges, mysql for storage
- # of user credentials
- loadmodule "modules/sl/sl.so"
- loadmodule "modules/mysql/mysql.so"
- loadmodule "modules/auth/auth.so"
- # ------------------ module parameters -------------------------------
- # tell the auth module the access data for SQL database:
- # username, password, hostname and database name
- modparam("auth", "db_url","mysql://ser:secret@dbhost/ser")
- # ------------------------- request routing logic -------------------
- # authenticate all requests prior to forwarding them
- route{
- if (!proxy_authorize("foo.bar" /* realm */,
- "subscriber" /* table name */ )) {
- proxy_challenge("foo.bar", "0");
- break;
- };
- forward(192.168.0.10,5060);
- }
- </programlisting>
- </example>
- </para>
- </section>
- <section id="writing_scripts">
- <title>Writing Scripts</title>
- <para>
- This section demonstrates simple examples
- how to configure server's behavior using the
- <application>ser</application>
- request routing language. All configuration scripts follow the
- <application>ser</application> language
- syntax, which dictates the following section ordering:
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>global configuration parameters</emphasis> --
- these value affect behavior of the server such as port
- number at which it listens, number of spawned children
- processes, and log-level. See <xref
- linkend="coreoptions"/> for a list of available options.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>module loading</emphasis> -- these statements
- link external modules, such as transaction management
- (tm) or stateless UA server (sl) dynamically. See
- <xref linkend="modulereference"/> for a list of modules
- included in <application>ser</application>
- distribution.
- </para>
- <note>
- <para>
- If modules depend on each other, than the depending
- modules must be loaded after modules on which they
- depend. We recommend to load first modules
- <command>tm</command> and <command>sl</command>
- because many other modules (authentication, user
- location, accounting, etc.) depend on these.
- </para>
- </note>
- </listitem>
- <listitem>
- <para>
- <emphasis>module-specific parameters</emphasis> -- determine
- how modules behave; for example, it is possible to configure
- database to be used by authentication module.
- </para>
- </listitem>
- <listitem>
- <para>
- one or more <emphasis>route blocks</emphasis> containing the
- request processing logic, which includes built-in actions
- as well as actions exported by modules. See <xref linkend="builtinref"/>
- for a list of built-in actions.
- </para>
- </listitem>
- <listitem>
- <para>
- optionally, if modules supporting reply
- processing (currently only TM) are loaded,
- one or more <emphasis>failure_route blocks</emphasis> containing
- logic triggered by received replies. Restrictions on use of
- actions within <command>failure_route</command>
- blocks apply -- see <xref linkend="builtinref"/> for more
- information.
- </para>
- </listitem>
- </itemizedlist>
- </para>
- <section id="defaultscript">
- <title>Default Configuration Script</title>
- <para>
- The configuration script, <filename>ser.cfg</filename>,
- is a part of every <application>ser</application>
- distribution and defines default behavior. It allows users
- to register with the server and have requests proxied to each
- other.
- </para>
- <para>
- After performing
- routine checks, the script looks whether incoming request is for
- served domain. If so and the request is "REGISTER", <application>ser</application>
- acts as SIP registrar and updates database of user's contacts.
- Optionally, it verifies user's identity first to avoid
- unauthorized contact manipulation.
- </para>
- <para>
- Non-REGISTER requests for served domains are then processed using
- user location database. If a contact is found for requested URI,
- script execution proceeds to stateful forwarding, a negative 404
- reply is generated otherwise. Requests outside served domain
- are always statefully forwarded.
- </para>
- <para>
- Note that this simple script features several limitations:
- <itemizedlist>
- <listitem>
- <para>
- By default, authentication is turned off to avoid
- dependency on mysql. Unless it it turned on, anyone
- can register using any name and "steal" someone else's
- calls.
- </para>
- </listitem>
- <listitem>
- <para>
- Even it authentication is turned on, there is no relationship
- between authentication username and address of record. That
- means that for example a user authenticating himself correctly
- with "john.doe" id may register contacts for "gw.bush".
- Site policy may wish to mandate authentication id to be equal
- to username claimed in To header field. <action>check_to</action>
- action from auth module can be used to enforce such a policy.
- </para>
- </listitem>
- <listitem>
- <para>
- There is no dialing plan implemented. All users are supposed to
- be reachable via user location database. See <xref linkend="numberingplans"/>
- for more information.
- </para>
- </listitem>
- <listitem>
- <para>
- The script assumes users will be using server's name as a part of
- their address of record. If users wish to use another name (domain
- name for example), this must be set using the <varname>alias</varname>
- options. See <xref linkend="domainmatching"/> for more information.
- </para>
- </listitem>
- <listitem>
- <para>
- If authentication is turned on by uncommenting related configuration
- options, clear-text user passwords will by assumed in back-end database.
- </para>
- </listitem>
- </itemizedlist>
- </para>
- <example>
- <title>Default Configuration Script</title>
- <programlisting>
- <xi:include href="../../etc/ser.cfg" parse="text"/>
- </programlisting>
- </example>
- </section>
- <section id="statefulua">
- <title>Stateful User Agent Server</title>
- <para>
- This examples shows how to make ser act as a stateful user
- agent (UA). Ability to act as as a stateful UA is essential
- to many applications which terminate a SIP path. These
- applications wish to focus on their added value. They
- do not wish to be involved in all SIP gory details, such
- as request and reply retransmission, reply formatting, etc.
- For example, we use the UA functionality to shield
- SMS gateway and instant message store from SIP transactional
- processing.
- The simple example bellow issues a log report on receipt
- of a new transaction.
- If we did not use a stateful UA, every single request retransmission
- would cause the application to be re-executed which would result in
- duplicated SMS messages, instant message in message store or
- log reports.
- </para>
- <para>
- The most important actions are <command> t_newtran</command>
- and <command> t_reply</command>. <command>
-
- t_newtran</command> shields subsequent code from
- retransmissions. It returns success and continues when a new
- request arrived. It exits current route block immediately on
- receipt of a retransmission. It only returns a negative value
- when a serious error, such as lack of memory, occurs.
- </para>
- <para>
- <command>t_reply</command> generates
- a reply for a request. It generates the reply statefully,
- i.e., it is kept for future retransmissions in memory.
- </para>
- <note>
- <para>
- Applications that do not need stateful processing
- may act as stateless UA Server too. They just use
- the <command>sl_send_reply</command> action to
- send replies to requests without keeping any
- state. The benefit is memory cannot run out,
- the drawback is that each retransmission needs to
- be processed as a new request. An example of use
- of a stateless server is shown in
- <xref linkend="redirectserver"/> and
- <xref linkend="executingscript"/>.
- </para>
- </note>
- <example>
- <title>Stateful UA Server</title>
- <programlisting format="linespecific">
- <xi:include href="../../examples/uas.cfg" parse="text"/>
- </programlisting>
- </example>
- </section> <!-- Stateful UAS -->
- <section id="redirectserver">
- <title>Redirect Server</title>
- <para>
- The redirect example shows how to redirect a request
- to multiple destination using 3xx reply. Redirecting
- requests as opposed to proxying them is essential to
- various scalability scenarios. Once a message is
- redirected, <application>ser</application>
- discards all related state and is no more involved
- in subsequent SIP transactions (unless the redirection
- addresses point to the same server again).
- </para>
- <para>
- The key <application>ser</application> actions in this example
- are <command>append_branch</command> and
- <command>sl_send_reply</command> (sl module).
- </para>
- <para>
- <command>append_branch</command> adds
- a new item to the destination set. The destinations set always
- includes the current URI and may be enhanced up to
- <constant>MAX_BRANCHES</constant> items.
- <command>sl_send_reply</command> command,
- if passed SIP reply code 3xx, takes all values in current
- destination set and adds them to Contact header field in
- the reply being sent.
- </para>
- <example id="redirectexample">
- <title>Redirect Server</title>
- <programlisting>
- <xi:include href="../../examples/redirect.cfg" parse="text"/>
- </programlisting>
- </example>
- </section> <!-- redirect server-->
-
- <section id="executingscript">
- <title>Executing External Script</title>
- <para>
- Like in the previous example, we show how to
- make <application>ser</application> act as a redirect server. The difference is
- that we do not use redirection addresses hardwired in
- <application>ser</application> script but
- get them from external shell commands. We also use
- ser's ability to execute shell commands to log
- source IP address of incoming SIP requests.
- </para>
- <para>
- The new commands introduced in this example are
- <command>exec_msg</command> and
- <command>exec_dset</command>.
- <command>exec_msg</command> takes
- current requests, starts an external command, and
- passes the requests to the command's standard input.
- It also passes request's source IP address in
- environment variable named <constant>SRCIP</constant>.
- </para>
- <para>
- <command>exec_dset</command> serves for URI rewriting by
- external applications. The <command>exec_dset</command> action
- passes current URI to the called external program, and rewrites
- current destination set with the program's output. An example
- use would be an implementation of a Least-Cost-Router, software
- which returns URI of the cheapest PSTN provider for a given
- destination based on some pricing tables. <xref
- linkend="execscript"/> is much easier: it prints fixed URIs on
- its output using shell script <command>echo</command> command.
- </para>
- <note>
- <para>
- This script works statelessly -- it uses this action for
- stateless replying, <command>sl_send_reply</command>. No
- transaction is kept in memory and each request
- retransmission is processed as a brand-new request. That
- may be a particular concern if the server logic
- (<command>exec</command> actions in this example) is too
- expensive. See <xref linkend="statefulua"/> for instructions
- on how to make server logic stateful, so that
- retransmissions are absorbed and do not cause re-execution
- of the logic.
- </para>
- </note>
- <example id="execscript">
- <title>Executing External Script</title>
- <programlisting>
- <xi:include href="../../examples/exec.cfg" parse="text"/>
- </programlisting>
- </example>
- </section> <!-- exec example -->
-
- <section id="replyprocessingsection">
- <title>On-Reply Processing (Forward on Unavailable)</title>
- <para>
- Many services depend on status of messages relayed
- downstream: <emphasis>forward on busy</emphasis> and
- <emphasis>forward on no reply</emphasis> to name the
- most well-known ones. To support implementation of
- such services, <application>ser</application>
- allows to return to request processing when request
- forwarding failed. When a request is reprocessed,
- new request branches may be initiated or the transaction
- can be completed at discretion of script writer.
- </para>
- <para>
- The primitives used are <command>t_on_failure(r)</command>
- and <command>failure_route[r]{}.</command> If
- <command>t_on_failure</command> is called before
- a request is statefully forwarded and a forwarding failure occurs,
- <application>ser</application>
- will return to request processing in a <command>failure_route</command>
- block. Failures include receipt of a SIP error
- (status code >= 300 ) from downstream or not receiving
- any final reply within final response period.
- </para>
- <para>
- The length of the timer is governed by parameters of the
- tm module. <varname>fr_timer</varname> is the length of
- timer set for non-INVITE transactions and INVITE transactions
- for which no provisional response is received. If a timer
- hits, it indicates that a downstream server is unresponsive.
- <varname>fr_inv_timer</varname> governs time to wait for
- a final reply for an INVITE. It is typically longer than
- <varname>fr_timer</varname> because final reply may take
- long time until callee (finds a mobile phone in a pocket and)
- answers the call.
- </para>
- <para>
- In <xref linkend="replyprocessing"/>,
- <command>failure_route[1]</command> is set to be entered on
- error using the <command>t_on_failure(1)</command>
- action. Within this reply block,
- <application>ser</application> is instructed to initiate a
- new branch and try to reach called party at another
- destination (sip:[email protected]). To deal with the case
- when neither the alternate destination succeeds,
- <application>t_on_failure</application> is set again. If
- the case really occurs, <command>failure_route[2]</command>
- is entered and a last resort destination
- (sip:[email protected]) is tried.
- </para>
- <example id="replyprocessing">
- <title>On-Reply Processing</title>
- <programlisting>
- <xi:include href="../../examples/onr.cfg" parse="text"/>
- </programlisting>
- </example>
- </section> <!-- reply processing -->
- </section> <!-- examples -->
- </section>
|