Professional Documents
Culture Documents
Contact headers contain a URI which should be used by other nodes and
devices communicating with it (within the same dialogue) when they want to
send it a SIP request. A Contact header is normally found in all SIP requests
and is often found in SIP responses too. Its role is best illustrated by looking
at examples.
Beginning with the simplest possible case: Two endpoints; one initiates a
dialogue by sending a SIP request (e.g. INVITE) to the other. The Contact
header is inserted into the SIP request by the device initiating the dialogue
(the UAC). It is received by the downstream endpoint (the UAS), which must
store the Contact address URI in case it needs to use it later in the dialogue.
Remember that the routing of SIP responses – such as “180 Ringing” or “200
OK” – is determined by the Via headers. The Contact header is used by
downstream endpoints when they need to send SIP requests back
upstream. However, it is equally valid for an upstream endpoint to use the
URI from the downstream device’s Contact header in sequential requests,
once it has received it.
When might we expect to see an endpoint send a request upstream as
shown above? An obvious example is a BYE request. If the called party hangs
up first, a BYE request would be sent upstream to the caller’s device to tell it
that call has ended. The Contact header supplied during the call setup phase
tells the called device how to address the calling device in such a situation.
It is important to note that the Contact header defines a URI, not just an IP
address and port. It is usual for the calling device to expect the Request-URI
(R-URI) in an upstream request to use the same <username> element that it
wrote in the original Contact header URI. However, many devices will be
tolerant and accept a request where the domain of the URI has changed. This
is important because it allows intermediate proxies to fix the Contact
address when the UAC device is behind NAT (discussed in detail here). The
Contact header may also include parameters and you would expect these to
be copied to the R-URI on an upstream request. Here is an example of a
Contact header from a device behind NAT which also includes two
parameters:
Contact: "John Quick" <sip:1005@192.168.0.81:2048;line=n01ly175;transport=tcp>
The standard solution used by SIP Proxy servers involves adding another
header, a Record-Route header, to identify itself as an intermediate node.
Each Proxy in the chain adds a new Record-Route header containing its own
address. It always adds it above any existing RR headers – the order is
important. In the example above, when the SIP request arrives at the UAS, it
contains three RR headers. The whole set of RR headers describes the path
through all intermediate nodes. Combined with the Contact header, this
provides a complete description of the upstream path that leads back to the
calling device.
The UAS has a complete description of the path back to the UAC, but at this
point the UAC doesn’t have any knowledge of the path, the intermediate
nodes or even the address of the UAS. It needs to know the path too, for
example when it wants to send more requests to the UAS within the current
dialogue. So how can this information be exchanged? The answer is fairly
simple – the UAS includes a complete copy of all the Record-Route headers in
its response. It also includes its own Contact header in the response. The
path that the response follows is defined by the Via headers – the RR headers
are present in the response but do not influence its transmission path.
So now, both the UAC and the UAS have a copy of the full set of Record-
Route headers. This is essential for loose routing to work and it happens at
the start of the dialogue. Here is an edited example of a real-world SIP INVITE
request containing two Record-Route headers:
Note how the Record-Route headers all include the parameter “lr”. This is
very important – it shows that loose routing is to be used. One of the RR
headers also has a custom parameter “MPXON=Y” which was used to show
that a media proxy was active. You would expect to see exactly the same two
Record-Route headers in the “200 OK” response when the call is answered.
Once the “Route Set” has been established, it is remembered by both end
points. Once the dialogue is established, the proxies should not insert any
more Record-Route headers after that initial transaction. Instead, all the
sequential SIP requests should contain Route headers.
Record-Route:
sip:192.168.10.11:5060;transport=tcp;lr;r2=on;did2=09c.7ab64763>
Record-Route: sip:86.4.139.77:5060;transport=tcp;lr;r2=on>
RFC 5658 is the official specification document for using double RR headers
in these special situations.
Topology Hiding
When the connection to a SIP proxy is made using TCP or TLS, it is possible
that the port shown in a Route header will be ignored because the proxy
prefers to re-use a pre-existing connection. This behaviour is somewhat
tricky to explain in detail and I would refer you to RFC 5923 if you want to
understand it. Strictly it should only happen if the “alias” parameter was
present in the Via received in an earlier request, but OpenSIPS also has a
core function, force_tcp_alias(), that will trigger the same behaviour even if
the “alias” parameter was not present.
Implementation in OpenSIPS
To handle loose routing in OpenSIPS, you must have the TM and RR modules
loaded. The RR module provides functions for adding Record-Route headers
to an initial request and another function to deal with Route headers in a
received sequential request.
OpenSIPS defines a core property for SIP requests called “the destination”. In
xlog statements you can print its value using $du. It is distinct from the
Request-URI (printed using $ru) and, if present, it takes precedence in
determining where a request is relayed when the t_relay() function is called.
If no destination is set, the request will be routed according to the R-URI.
The loose-route() function detects if Route headers are present, strips off the
topmost header (or the topmost two if “r2=on” is detected), then looks to see
if there are any more Route headers. If it finds at least one, the network
address defined in the new topmost header is assigned to “the destination”
($du). Your script must still use t_relay() to send the request onwards. The
following flow chart (which is highly simplified) shows how a typical OpenSIPS
script might process requests using the functions mentioned above:
Further reading and feedback
This topic is quite well covered on the Internet by various authors from
around the world, so if you want to supplement what you learnt here with
additional information, a Google search should bring you plenty of results. If
you want to study the detailed specifications, then look for the following
RFC’s: