[pjsip] Parsing expires from REGISTER reply

Alan J. Bond alan.bond at wintology.com
Mon May 5 11:04:27 EDT 2008


Benny,



This fix isn't quite enough.  My assumption that there'll only be one Contact: header is invalid in some circumstances.  As your new WIKI entry says about expiry= appended to Contact: header lines, "This is necessary to support multiple registrations (the same AOR is registered more than once in the server by multiple user agents)...".  Also, of course, in NAT situations SIP servers that do this change the internal address (as supplied by the client in the request) to the client's external address in returned Contact: headers.



I have started using a test server we have running.  This is PortaOne's intended replacement for its aging PortaSIP, which doesn't support buddy registration, etc.  Unfortunately it doesn't put out Client: headers so I can't tell much about its origins.  I'm guessing this is OpenSER.  It has been built with similar bad habits anyway.



One of the things I want to use PJSIP for is to build a *clean* TAPI driver. This will require multiple client registrations behind the same NAT. [BTW I'm well aware of sipTAPI and its big red switch re-entrancy problems on multiprocessor/core/hyper-threading machines].



For starters, if there are multiple Contact: headers my previous fix/hack is clearly going to fail anyway so I made this change to that first to do some testing:



      from:


if (expiration == NOEXP)
  expiration = -1;
else if (expiration == 0 && contact_cnt == 1 && contact[0]->expires > 0)
  expiration = contact[0]->expires;



      to:


if (expiration == NOEXP)
  expiration = -1;
else if (expiration == 0 && contact_cnt > 0 && contact[0]->expires > 0)
  expiration = contact[0]->expires;



The obvious problem with this is that on UNregistration with multiple client registrations present a legitimate value of 0 is not going to be returned to the callback. To test this would actually happen I used two client instances on the same IP address, one using port 5061, the other using 5062.  The test was as follows:



      Register 5061

      Register 5062

      Unregister 5062

      Unregister 5061



As expected, for the 5061 instance the fix works OK since at unregister time it is the only surviving client.  Here is what happened on the 5062 instance: (best viewed in an HTML mail reader).



REGISTER sip:sip2.xxx.xxx SIP/2.0

Via: SIP/2.0/UDP 192.168.128.10:5062;rport;branch=z9hG4bKPjb0aeca4b2b8040b795865e6f4d32d3bd

Max-Forwards: 70

From: <sip:510xxxxxxx at sip2.xxx.xxx>;tag=abf4fc9416704a948c0833db70d6ab4d

To: <sip:510xxxxxxx at sip2.xxx.xxx>

Call-ID: 0020823c2db04b868851a9c632b92cb2

CSeq: 18510 REGISTER

Contact: <sip:510xxxxxxx at 192.168.128.10:5062;transport=UDP>

Expires: 300

Authorization: Digest username="510xxxxxxx", realm="sip2.xxx.xxx", nonce="1209929276:e675cbb3a92c2c28d1e4a47b90992924",

 uri="sip:sip2.xxx.xxx", response="6e2e85e548b0ea427ef40c588fa9a591", algorithm=MD5

Content-Length:  0

________________________________

SIP/2.0 200 OK

Via: SIP/2.0/UDP 192.168.128.10:5062;branch=z9hG4bKPjb0aeca4b2b8040b795865e6f4d32d3bd;received=81.xxx.xxx.xxx;rport=5062

Contact: <sip:510xxxxxxx at 81.xxx.xxx.xxx:5061;transport=UDP>;expires=281

Contact: <sip:510xxxxxxx at 81.xxx.xxx.xxx:5062;transport=UDP>;expires=300

To: <sip:510xxxxxxx at sip2.xxx.xxx>;tag=2d6d6560

From: <sip:510xxxxxxx at sip2.xxx.xxx>;tag=abf4fc9416704a948c0833db70d6ab4d

Call-ID: 0020823c2db04b868851a9c632b92cb2

CSeq: 18510 REGISTER

Date: Sun, 04 May 2008 19:27:56 GMT

Content-Length: 0



20:27:56.912    pjsua_acc.c sip:510xxxxxxx at sip2.xxx.xxx: registration success, status=200 (OK), will re-register in 281 seconds



________________________________

REGISTER sip:sip2.xxx.xxx SIP/2.0

Via: SIP/2.0/UDP 192.168.128.10:5062;rport;branch=z9hG4bKPj48482147229948609ec6b20bad0701ef

Max-Forwards: 70

From: <sip:510xxxxxxx at sip2.xxx.xxx>;tag=7b703bbd3bc547f0b52855ce7027a7cc

To: <sip:510xxxxxxx at sip2.xxx.xxx>

Call-ID: 0020823c2db04b868851a9c632b92cb2

CSeq: 18512 REGISTER

Contact: <sip:510xxxxxxx at 192.168.128.10:5062;transport=UDP>

Expires: 0

Authorization: Digest username="510xxxxxxx", realm="sip2.xxx.xxx", nonce="1209929286:881ded53e0bb2f0555ebaeb672b9f5b7",

 uri="sip:sip2.xxx.xxx", response="f0b6951b81aad73d97b9d0214f7a6ab0", algorithm=MD5

Content-Length:  0

________________________________

SIP/2.0 200 OK

Via: SIP/2.0/UDP 192.168.128.10:5062;branch=z9hG4bKPj48482147229948609ec6b20bad0701ef;received=81.xxx.xxx.xxx;rport=5062

Contact: <sip:510xxxxxxx at 81.xxx.xxx.xxx:5061;transport=UDP>;expires=271

To: <sip:510xxxxxxx at sip2.xxx.xxx>;tag=2bdc4e7d

From: <sip:510xxxxxxx at sip2.xxx.xxx>;tag=7b703bbd3bc547f0b52855ce7027a7cc

Call-ID: 0020823c2db04b868851a9c632b92cb2

CSeq: 18512 REGISTER

Date: Sun, 04 May 2008 19:28:06 GMT

Content-Length: 0



20:28:06.881    pjsua_acc.c sip:510xxxxxxx at sip2.xxx.xxx: registration success, status=200 (OK), will re-register in 271 seconds



Clearly this is hopeless.  After UNregistration we have the client stack in an odd state.  While that may be benign, especially if the client then immediately terminates, obviously after the registration of 5062 it's re-registration timer is wrong since it has got the time remaining until 5061 expires.  In the example above this wouldn't matter much but it's clearly going to cause all sorts of problems in the real world.



So ... attached is a much better solution that handles multiple client registrations and should not break anything. You'll find a comment at the top and all changes are comment bracketed throughout.  This approach relies on timing the transaction latency and deducting it from the client's requested expiry time if and only if all attempts to parse a returned Expires: header and match Contact: headers has failed.



Again, please remember I have only been working with PJSIP code for three days so please scrutinize carefully.  Note in particular that I have extended the opaque pjsip__regc structure and explicitly set the expires member on initialization.  Having chased down all references to this as best I can, as far as I can see this should be fine.



Let me know what you think.  I guess I'd better learn how to get to SVN, any advice for a Wintel coder new to all this appreciated.



Best Regards,

Alan.



-----Original Message-----
From: pjsip-bounces at lists.pjsip.org [mailto:pjsip-bounces at lists.pjsip.org] On Behalf Of Benny Prijono
Sent: 04 May 2008 13:07
To: pjsip list
Subject: Re: [pjsip] Parsing expires from REGISTER reply



Hi Alan,



First of all, I think this issue has come up several times on this

list, so I've just added this entry in the FAQ to explain the

rationale why PJSIP behaves this way, and also few solutions to cope

with it: http://trac.pjsip.org/repos/wiki/FAQ#regc



I do like your solution. And while this is server problem (regardless

of how popular it is; if it's broken, then it needs to be fixed, IMO),

in the SVN trunk we already have couple of settings to deal with this,

so if we want to handle this, why not go all the way. So I've just

added the trick (to get the expiration value from the single Contact,

if Expires header is not present) in the latest SVN version. Thanks

for the suggestion.



Now enjoy the bank holiday. :)



Cheers

 Benny



<snip>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.pjsip.org/pipermail/pjsip_lists.pjsip.org/attachments/20080505/aa1ace07/attachment.html>
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: sip_reg.c
URL: <http://lists.pjsip.org/pipermail/pjsip_lists.pjsip.org/attachments/20080505/aa1ace07/attachment.c>


More information about the pjsip mailing list