Subject: Connected UDP sockets in SIP

From: Shail Bhatnagar <[email-masked]>
Date: Mon, 09 Aug 1999 15:21:03 -0400

According to the SIP spec, clients should use connected UDP sockets
to send requests. Does this apply only to INVITE or any request like
CANCEL/BYE. I thought the idea is to save retransmissions and reduce
the network traffic - hopefully an error code would be returned
by the socket API on sending the second or third retransmission of
the request. BYE/CANCEL retransmissions are even more than INVITE.

Does this also mean that the SIP entity which receives an INVITE with
a Contact header, should open a connected UDP socket to that address
and send future requests like BYE to that address ? 

-- 
Best regards,
Shail

---

From: [email-masked]
Date: Tue, 10 Aug 1999 00:33:21 +0300

Hi,

> the request. BYE/CANCEL retransmissions are even more than INVITE.

The UDP socket has to be connected in order to catch asynchronous errors... 
That is, in order to receive the ICMP messages (such as port unreachable).
This way, if there is nobody on the other side listening to the UDP port we are 
sending our SIP messages to, we know that the SIP on the other side server is 
down.

> and send future requests like BYE to that address ? 

Everytime you send requests to a certain SIP server, if you do it through a UDP 
connected socket, you will notice when/if it goes down... it does not matter 
the state of the connection...

Regards,

Gonzalo

---

From: Jonathan Rosenberg <[email-masked]>
Date: Mon, 09 Aug 1999 21:09:07 -0400

Gonzalo has already made some good points on this; I just want to add a
few more things:

Shail Bhatnagar wrote:
> CANCEL/BYE.

Any request. 

> and send future requests like BYE to that address ?

By "SIP entity", I assume you mean called UA. As specified in RFC2543,
the called UA should send future requests to the Contact address from
the INVITE. If this address indicates UDP, its probably a good idea to
connect the socket to receive ICMP error messages. Note, however, that
due to an omission, the Record-Route headers are not currently specified
to be used by the called UA. One possible procedure for the called UA
is:

1. take the Record Route list from the INVITE
2. append the Contact header to the end of the list
3. if the list resulting from (1) and (2) is empty (no Contact or
Record-Route), use the From field for the request URI
4. otherwise, "pop" the top entry off the list, and place that in the
request-URI. The rest of it, if present, goes in a Route header.

This is the procedure described in:
http://www.cs.columbia.edu/~hgs/sip/notes.html

at the bottom. I'm not convinced this is the right approach; its fairly
"brittle", in the sense that requests from the called UA are only
correctly routed if (1) a Contact was inserted in the INVITE, (2) every
proxy obeys the Route headers. If either is not true, the request will
actually be looped back to the called UA, which is very bad. 

The problem boils down to the fact that Record-Route specifies two
things. One of them is the request URI to use (i.e., the result of the
location service), and the second is the address of the server to send
it to. The original, way back when idea for Record-Route was that the
first implied the second as a result of a DNS query. However, it has
been pointed out that the DNS query may not always yield consistent
results. Thus, the maddr tag was allowed to contain a unicast address to
specify the IP address of the server directly. Now, with routing from
called UA to calling UA, we want to use the IP address component of the
Record-Routes, but NOT the URI's. Thats because the URIs are those for
the called party; only the Contact header from the INVITE actually names
the calling party. Here's an example. Lets say A calls B, and this
invite goes through two proxies with IP addresses P1 and P2,
respectively, which translate B to B1 and B2, respectively. The invite
received by B is:

INVITE B2 SIP/2.0
Record-Route: B1;maddr=P2, B;maddr=P1
Contact: A

The procedure above results in a request from user B, destined for A,
sent to IP adddress P2, that looks like:

BYE B SIP/2.0
Route: B;maddr=P1, A

Notice only the last Route header (A) actually names the intended target
of the request. This is why it is brittle.

My proposal to fix this is:

1. Every proxy MUST use the maddr tag, but otherwise insertion of
Record-Route is unchanged.
2. The UA constructs a Route list as follows:

    a. For each URI in the record route, add the URI in the Contact
field (if present), otherwise From field, but with the maddr from the
URI in the record route.
    b. append the Contact header to the end

3. pop the top entry (if present, otherwise use From field), use this
for the request URI, and use the rest in the Route header.

The proposal basically makes sure that the URI in the Route headers is
always that of the calling UA. So, the request from user B back to A
would look like:

BYE A SIP/2.0
Route: A;maddr=P1, A

and would be sent to P2.

Comments?

Thanks,
Jonathan R.

-- 
Jonathan D. Rosenberg                       Lucent Technologies
Member of Technical Staff                   101 Crawfords Corner Rd.
High Speed Networks Research                Holmdel, NJ 07733
FAX: (732) 834-5379                         Rm. 4C-526
EMAIL: [email-masked]
URL: http://www.cs.columbia.edu/~jdrosen

---

From: Anders Kristensen <[email-masked]>
Date: Tue, 10 Aug 1999 12:05:04 +0100

This subject reminds me of a request for enhancement (bordering on being
a bug report) I registered with Suns Java Developer Connection some time
ago.

The problem is that currently in Java there is no way of receiving ICMP
Port Unreachable messages. This potentially has some highly adverse
effects on SIP connection setup times so this is kind of a big deal and
should be a concern for everyone implementing SIP in Java.

The bug report is at 

  http://developer.java.sun.com/developer/bugParade/bugs/4204320.html

Sun says bugs and enhancements are fixed according to the number of
votes they get, so please go and vote for this one!

I've included the reply I got back from Sun below.

Cheers,
Anders

-- 
Anders Kristensen <[email-masked]>,
http://www-uk.hpl.hp.com/people/ak/
Hewlett-Packard Labs, Bristol, UK

Subject: Re: A connected DatagramSocket doesn't throw exception when
address invalid
Date: Wed, 20 Jan 1999 08:09:28 -0800
From: Jeff Nisewanger <[email-masked]>
To: [email-masked]
CC: [email-masked]

Your bug has been submitted into our internal Bug Tracking
system. It has been assigned Bug Id: 4204320.

You make an interesting point. Actually, the connect() method
was primarily added to reduce the overhead of Java security
checking which otherwise has to check the destination address
of each datagram sent. I have forwarded your web bug report
into our internal bug database as a Request For Enhancement
rather than as a "bug" report. I will look into whether this 4.3BSD
connected datagram socket behavior is widely enough supported on
other likely Java platform network stack implementations that
we can add this in some way to the semantics of connected
DatagramSockets.
Currently, the connect() method is implemented purely in Java and does
not
actually perform a connect() method to any underlying Unix datagram
socket
that might exist.

The state of the bug can be monitored via the Java Developer
Connection, which is now free:
         http://java.sun.com/jdc.

You can search for this bug by going to Technical Support,
and then Technical Support Search.

It may take a day or two before your bug shows up in this
external database.
-------------- Original Bug Report-------------------

id : 52238
category : java
subcategory : classes_net
type : bug
synopsis : A connected DatagramSocket doesn't throw exception when
address invalid
description : JDK1.2 includes a connect(InetAddress addr, int port) call
in class
java.net.DatagramSocket.  This is only really useful for one thing,
which is to throw an Exception when the VM receives an ICMP error
message saying that the address was invalid, for example because
no process was listening on the specified port.

Quoting from "Unix Network Programming" [Stevens 1990, p271]:

"...the Internet protocols specify that a host should generate an
ICMP port unreachable message if it receives a UDP datagram specifying
a UDP port for which no process is waiting to read from. The host that
sent the UDP datagram and receives this ICMP message can try to
identify the process that sent the datagram, and notify the
process. 4.3BSD, for example, notifies the process with a "connection
refused" error (ECONNREFUSED) on the next system call for this socket,
only if the process had connect()'ed the socket to the destination
address."

Having this behaviour is essential when implementing certain
protocols on top of UDP, because relying only on timeouts takes
too long. The Session Initiation Protocol (SIP) is an example of
such a protocol.

The following code generates 5 UDP messages and SHOULD fail with
an exception on the second send().  I've tested it on NT4.0 with no
exception being thrown.  This is when the remote address is
either an NT4.0 or an HP-UX host. It may be the case that NT4
doesn't generate the ICMP error but I'm assuming that HP-UX does.

Maybe the problem is that NT4 doesn't pass on those ICMP error
to the sending process, in which case running the test program
on a Solaris box should work (ie result in an exception).

import java.net.*;

public class UdpConnect {
    public static void main(String[] args) throws Exception {
        int port = 7000;
        String host = "127.0.0.1";
        int i;

        for (i = 0; i < args.length; i++) {
            if ("-p".equals(args[i])) {
                port = Integer.parseInt(args[++i]);
            } else if ("-h".equals(args[i])) {
                host = args[++i];
            }
        }
        InetAddress addr = InetAddress.getByName(host);
        DatagramSocket sock = new DatagramSocket();
        byte[] buf      = "Hello, server!".getBytes();
        DatagramPacket p = new DatagramPacket(buf, buf.length, addr,
port);
        int nsend       = 0;

        sock.connect(addr, port);

        for (i = 0; i < 5; i++) {
            System.out.print(".");
            sock.send(p);
            try {
                Thread.currentThread().sleep(1000);
            } catch (Exception ex) {
                // if no server at other end we SHOULD get an
ECONNREFUSED
                // exception here on second send()
                ex.printStackTrace(System.err);
            }
        }
    }
}
comments : (company - Hewlett-Packard , email - [email-masked])
workaround : Rely on retransmissions + timeouts.
cust_name : Anders Kristensen
cust_email : [email-masked]
company : Hewlett-Packard
release : 1.2fcs
hardware : x86
OSversion : win_nt_4.0
status : Posted
delReason : New Bug
priority : 4
sev_impact : 2
sev_function : 2
cust_type : R
bugtraqID : 0
dateCreated : 1999-01-07 06:27:02.0
dateEvaluated : 1999-1-20 8:3:2

---

From: Igor Slepchin <[email-masked]>
Date: Tue, 10 Aug 1999 12:09:06 -0400

Surprisingly, I found that Blackdown's Linux port of JDK actually throws
an IOException in DatagramSocket.send() if an ICMP error message was
received. It even throws the exception on unconnected sockets! However,
this is the only Java port I've seen that supports this.

---
Igor Slepchin