<html>
  <head>
    <meta content="text/html; charset=ISO-8859-1"
      http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    Hello,<br>
    <br>
    On 3/13/12 11:09 AM, Peter Dunkley wrote:
    <blockquote cite="mid:1331633349.2830.10.camel@pd-laptop-linux"
      type="cite">
      <meta http-equiv="Content-Type" content="text/html;
        charset=ISO-8859-1">
      <meta name="GENERATOR" content="GtkHTML/4.2.3">
      Hi Anca,<br>
      <br>
      I have huge levels of back-end subscribes going on here so I don't
      want to be wasting time handling retransmissions for this very
      frequent condition.<br>
      <br>
      When I first saw this race condition I thought moving to TCP on
      the back-end would help.&nbsp; It did help a bit, but the hazard still
      occurred (though less frequently) and in a slightly different way.<br>
      <br>
      This temporary dialog stuff was a pain to do, but in the end it
      was the only way to get things working without retransmissions.<br>
    </blockquote>
    <br>
    also have in mind there is no retransmission for sip over tcp, tls
    or sctp -- a connection directly to the presence server may ensure
    the messages are received in order they were sent, but if in between
    is a proxy that does transport gatewaying from client to presence
    server (e.g., udp from client to proxy, then tcp to PS), the order
    cannot be ensured. The SIP path can be a mixture of segments, some
    with retransmissions, some without.<br>
    <br>
    Cheers,<br>
    Daniel<br>
    <blockquote cite="mid:1331633349.2830.10.camel@pd-laptop-linux"
      type="cite">
      <br>
      Peter <br>
      <br>
      On Tue, 2012-03-13 at 12:00 +0200, Anca Vamanu wrote:<br>
      <blockquote type="CITE"> Hi Peter,<br>
        <br>
        Thanks for explaining, I understand now.<br>
        Just one more curiosity if you allow me :) . Suppose the case
        when Notify comes and doesn't find any dialog. What if no reply
        is sent in this case? It will be retransmitted and even the
        first retransmission has big chances of coming after the 200OK
        and finding the dialog in database. Have you thought at this
        solution?<br>
        <br>
        Regards,<br>
        Anca<br>
        <br>
        <br>
        On 03/13/2012 11:31 AM, Peter Dunkley wrote: <br>
        <blockquote type="CITE"> Hi,<br>
          <br>
          This is what the PUA module used to do.&nbsp; Unfortunately, there
          is a race hazard (which happens very frequently in certain
          scenarios) where the NOTIFY that follows a SUBSCRIBE can be
          received and processed faster than the 200 OK to the
          SUBSCRIBE.&nbsp; The temporary dialog stuff is in there so that
          this NOTIFY can be matched and processed.<br>
          <br>
          So, while putting the PUA behaviour back to this will fix the
          latest race hazard I found it will re-introduce a much more
          likely one.<br>
          <br>
          Regards,<br>
          <br>
          Peter<br>
          <br>
          On Tue, 2012-03-13 at 10:55 +0200, Anca Vamanu wrote:<br>
          <br>
          <blockquote type="CITE">
            <pre>Hi Peter,


Wouldn't it had been easier if you waited until 200OK to do the insert?
Kamailio is does not store transactions persistently, so there is no 
point to store the subscribe dialog information until it is not 
established. The reason is that if kamailio is restarted in the mean 
time before receiving the 200OK, it won't match the 200OK to the 
transaction and won't complete the dialog anyways.
This is actually a solution I adopted in the b2bua modules which had the 
same race problems.

Regards,
Anca



On 03/13/2012 12:01 AM, Peter Dunkley wrote:
&gt; Module: sip-router
&gt; Branch: master
&gt; Commit: 287ee15ffa985cb6d07f192f1d1cbfadb31c0fd8
&gt; URL:    <a moz-do-not-send="true" href="http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=287ee15ffa985cb6d07f192f1d1cbfadb31c0fd8">http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=287ee15ffa985cb6d07f192f1d1cbfadb31c0fd8</a>
&gt;
&gt; Author: Peter Dunkley&lt;<a moz-do-not-send="true" href="mailto:peter.dunkley@crocodile-rcs.com">peter.dunkley@crocodile-rcs.com</a>&gt;
&gt; Committer: Peter Dunkley&lt;<a moz-do-not-send="true" href="mailto:peter.dunkley@crocodile-rcs.com">peter.dunkley@crocodile-rcs.com</a>&gt;
&gt; Date:   Mon Mar 12 21:52:39 2012 +0000
&gt;
&gt; modules_k/pua: Fixed race hazard on pua table
&gt;
&gt; - During testing a race hazard where the 2XX to a back-end SUBSCRIBE can be
&gt;    received and processed (and the DB UPDATE to convert a temporary dialog to a
&gt;    full dialog) before the DB INSERT to create a temporary dialog is run.  There
&gt;    is an incredibly small window for this, but it was happening consistently on
&gt;    one system.
&gt; - The easiest way to fix this is to use the replace() DB API to convert the
&gt;    dialog and live with the initial INSERT failing (this does not actually
&gt;    return an error from the SRDB1 interface so the rest of the code continues
&gt;    OK).  Unfortunately, the replace() API is not available for some databases
&gt;    (for example, PostgreSQL).
&gt; - I have updated the code to use replace() when it is available and to do an
&gt;    update() then check affected_rows() (and if 0 then do an insert()) when
&gt;    replace() is not available.
&gt; - The update() and then insert() process makes the window for the race much
&gt;    smaller, but doesn't get rid of it completely.  However, with PostgreSQL a
&gt;    DB rule can be used to fix it completely:
&gt; - PostgreSQL DB rule:
&gt; CREATE RULE "pua_insert_race1" AS ON INSERT TO "pua"
&gt;   WHERE EXISTS(
&gt;    SELECT 1 FROM pua WHERE call_id=NEW.call_id AND from_tag=NEW.from_tag
&gt;          AND pres_id=NEW.pres_id AND to_tag=''
&gt;   ) DO INSTEAD (
&gt;    UPDATE pua
&gt;      SET expires=NEW.expires,
&gt;          desired_expires=NEW.desired_expires,
&gt;          flag=NEW.flag,
&gt;          etag=NEW.etag,
&gt;          tuple_id=NEW.tuple_id,
&gt;          to_tag=NEW.to_tag,
&gt;          cseq=NEW.cseq,
&gt;          record_route=NEW.record_route,
&gt;          contact=NEW.contact,
&gt;          remote_contact=NEW.remote_contact,
&gt;          version=NEW.version,
&gt;          extra_headers=NEW.extra_headers
&gt;      WHERE call_id=NEW.call_id AND from_tag=NEW.from_tag
&gt;           AND pres_id=NEW.pres_id
&gt; );
&gt; - You can also add another PostgreSQL rule to make the failing INSERT
&gt;    (described above) do so quietly.  This does not affect the function of the
&gt;    code, but it will make the logs quieter (which is nice):
&gt; CREATE RULE "pua_insert_race2" AS ON INSERT TO "pua"
&gt;   WHERE EXISTS(
&gt;    SELECT 1 FROM pua WHERE call_id=NEW.call_id AND from_tag=NEW.from_tag
&gt;          AND to_tag&lt;&gt;''
&gt;   ) DO INSTEAD NOTHING;
&gt;
&gt; ---
&gt;
&gt;   modules_k/pua/pua_db.c         |  187 +++++++++++++++++++++++++---------------
&gt;   modules_k/pua/send_subscribe.c |   54 ------------
&gt;   2 files changed, 118 insertions(+), 123 deletions(-)
&gt;
&gt; diff --git a/modules_k/pua/pua_db.c b/modules_k/pua/pua_db.c
&gt; index b83e3ed..b652cee 100644
&gt; --- a/modules_k/pua/pua_db.c
&gt; +++ b/modules_k/pua/pua_db.c
&gt; @@ -883,9 +883,9 @@ int get_record_id_puadb(ua_pres_t *pres, str **rec_id )
&gt;   /******************************************************************************/
&gt;   int convert_temporary_dialog_puadb(ua_pres_t *pres)
&gt;   {
&gt; -       db_key_t query_cols[4], data_cols[10];
&gt; -       db_val_t query_vals[4], data_vals[10];
&gt; -       int n_query_cols = 0, n_data_cols = 0;
&gt; +       db_key_t query_cols[18];
&gt; +       db_val_t query_vals[18];
&gt; +       int n_query_cols = 0;
&gt;
&gt;          if (pres==NULL)
&gt;          {
&gt; @@ -920,82 +920,131 @@ int convert_temporary_dialog_puadb(ua_pres_t *pres)
&gt;          n_query_cols++;
&gt;
&gt;          /* The columns I need to fill in to convert a temporary dialog to a dialog */
&gt; -       data_cols[n_data_cols] =&amp;str_expires_col;
&gt; -       data_vals[n_data_cols].type = DB1_INT;
&gt; -       data_vals[n_data_cols].nul = 0;
&gt; -       data_vals[n_data_cols].val.int_val = pres-&gt;expires;
&gt; -       n_data_cols++;
&gt; -
&gt; -       data_cols[n_data_cols] =&amp;str_desired_expires_col;
&gt; -       data_vals[n_data_cols].type = DB1_INT;
&gt; -       data_vals[n_data_cols].nul = 0;
&gt; -       data_vals[n_data_cols].val.int_val = pres-&gt;desired_expires;
&gt; -       n_data_cols++;
&gt; -
&gt; -       data_cols[n_data_cols] =&amp;str_flag_col;
&gt; -       data_vals[n_data_cols].type = DB1_INT;
&gt; -       data_vals[n_data_cols].nul = 0;
&gt; -       data_vals[n_data_cols].val.int_val = pres-&gt;flag;
&gt; -       n_data_cols++;
&gt; -
&gt; -       data_cols[n_data_cols] =&amp;str_to_tag_col;
&gt; -       data_vals[n_data_cols].type = DB1_STR;
&gt; -       data_vals[n_data_cols].nul = 0;
&gt; -       data_vals[n_data_cols].val.str_val = pres-&gt;to_tag;
&gt; -       n_data_cols++;
&gt; -
&gt; -       data_cols[n_data_cols] =&amp;str_cseq_col;
&gt; -       data_vals[n_data_cols].type = DB1_INT;
&gt; -       data_vals[n_data_cols].nul = 0;
&gt; -       data_vals[n_data_cols].val.int_val = pres-&gt;cseq;
&gt; -       n_data_cols++;
&gt; -
&gt; -       data_cols[n_data_cols] =&amp;str_record_route_col;
&gt; -       data_vals[n_data_cols].type = DB1_STR;
&gt; -       data_vals[n_data_cols].nul = 0;
&gt; -       data_vals[n_data_cols].val.str_val = pres-&gt;record_route;
&gt; -       n_data_cols++;
&gt; -
&gt; -       data_cols[n_data_cols] =&amp;str_contact_col;
&gt; -       data_vals[n_data_cols].type = DB1_STR;
&gt; -       data_vals[n_data_cols].nul = 0;
&gt; -       data_vals[n_data_cols].val.str_val = pres-&gt;contact;
&gt; -       n_data_cols++;
&gt; -
&gt; -       data_cols[n_data_cols] =&amp;str_remote_contact_col;
&gt; -       data_vals[n_data_cols].type = DB1_STR;
&gt; -       data_vals[n_data_cols].nul = 0;
&gt; -       data_vals[n_data_cols].val.str_val = pres-&gt;remote_contact;
&gt; -       n_data_cols++;
&gt; -
&gt; -       data_cols[n_data_cols] =&amp;str_version_col;
&gt; -       data_vals[n_data_cols].type = DB1_INT;
&gt; -       data_vals[n_data_cols].nul = 0;
&gt; -       data_vals[n_data_cols].val.int_val = pres-&gt;version;
&gt; -       n_data_cols++;
&gt; -
&gt; -       data_cols[n_data_cols] =&amp;str_extra_headers_col;
&gt; -       data_vals[n_data_cols].type = DB1_STR;
&gt; -       data_vals[n_data_cols].nul = 0;
&gt; +       query_cols[n_query_cols] =&amp;str_expires_col;
&gt; +       query_vals[n_query_cols].type = DB1_INT;
&gt; +       query_vals[n_query_cols].nul = 0;
&gt; +       query_vals[n_query_cols].val.int_val = pres-&gt;expires;
&gt; +       n_query_cols++;
&gt; +
&gt; +       query_cols[n_query_cols] =&amp;str_desired_expires_col;
&gt; +       query_vals[n_query_cols].type = DB1_INT;
&gt; +       query_vals[n_query_cols].nul = 0;
&gt; +       query_vals[n_query_cols].val.int_val = pres-&gt;desired_expires;
&gt; +       n_query_cols++;
&gt; +
&gt; +       query_cols[n_query_cols] =&amp;str_flag_col;
&gt; +       query_vals[n_query_cols].type = DB1_INT;
&gt; +       query_vals[n_query_cols].nul = 0;
&gt; +       query_vals[n_query_cols].val.int_val = pres-&gt;flag;
&gt; +       n_query_cols++;
&gt; +
&gt; +       query_cols[n_query_cols] =&amp;str_to_tag_col;
&gt; +       query_vals[n_query_cols].type = DB1_STR;
&gt; +       query_vals[n_query_cols].nul = 0;
&gt; +       query_vals[n_query_cols].val.str_val = pres-&gt;to_tag;
&gt; +       n_query_cols++;
&gt; +
&gt; +       query_cols[n_query_cols] =&amp;str_cseq_col;
&gt; +       query_vals[n_query_cols].type = DB1_INT;
&gt; +       query_vals[n_query_cols].nul = 0;
&gt; +       query_vals[n_query_cols].val.int_val = pres-&gt;cseq;
&gt; +       n_query_cols++;
&gt; +
&gt; +       query_cols[n_query_cols] =&amp;str_record_route_col;
&gt; +       query_vals[n_query_cols].type = DB1_STR;
&gt; +       query_vals[n_query_cols].nul = 0;
&gt; +       query_vals[n_query_cols].val.str_val = pres-&gt;record_route;
&gt; +       n_query_cols++;
&gt; +
&gt; +       query_cols[n_query_cols] =&amp;str_contact_col;
&gt; +       query_vals[n_query_cols].type = DB1_STR;
&gt; +       query_vals[n_query_cols].nul = 0;
&gt; +       query_vals[n_query_cols].val.str_val = pres-&gt;contact;
&gt; +       n_query_cols++;
&gt; +
&gt; +       query_cols[n_query_cols] =&amp;str_remote_contact_col;
&gt; +       query_vals[n_query_cols].type = DB1_STR;
&gt; +       query_vals[n_query_cols].nul = 0;
&gt; +       query_vals[n_query_cols].val.str_val = pres-&gt;remote_contact;
&gt; +       n_query_cols++;
&gt; +
&gt; +       query_cols[n_query_cols] =&amp;str_version_col;
&gt; +       query_vals[n_query_cols].type = DB1_INT;
&gt; +       query_vals[n_query_cols].nul = 0;
&gt; +       query_vals[n_query_cols].val.int_val = pres-&gt;version;
&gt; +       n_query_cols++;
&gt; +
&gt; +       query_cols[n_query_cols] =&amp;str_extra_headers_col;
&gt; +       query_vals[n_query_cols].type = DB1_STR;
&gt; +       query_vals[n_query_cols].nul = 0;
&gt;          if (pres-&gt;extra_headers)
&gt;          {
&gt; -               data_vals[n_data_cols].val.str_val.s = pres-&gt;extra_headers-&gt;s;
&gt; -               data_vals[n_data_cols].val.str_val.len = pres-&gt;extra_headers-&gt;len;
&gt; +               query_vals[n_query_cols].val.str_val.s = pres-&gt;extra_headers-&gt;s;
&gt; +               query_vals[n_query_cols].val.str_val.len = pres-&gt;extra_headers-&gt;len;
&gt;          }
&gt;          else
&gt;          {
&gt; -               data_vals[n_data_cols].val.str_val.s = "";
&gt; -               data_vals[n_data_cols].val.str_val.len = 0;
&gt; +               query_vals[n_query_cols].val.str_val.s = "";
&gt; +               query_vals[n_query_cols].val.str_val.len = 0;
&gt;          }
&gt; -       n_data_cols++;
&gt; +       n_query_cols++;
&gt;
&gt; -       if (pua_dbf.update(pua_db, query_cols, 0, query_vals,
&gt; -                       data_cols, data_vals, n_query_cols, n_data_cols)&lt;  0)
&gt; +       query_cols[n_query_cols] =&amp;str_event_col;
&gt; +       query_vals[n_query_cols].type = DB1_INT;
&gt; +       query_vals[n_query_cols].nul = 0;
&gt; +       query_vals[n_query_cols].val.int_val = pres-&gt;event;
&gt; +       n_query_cols++;
&gt; +
&gt; +       query_cols[n_query_cols] =&amp;str_watcher_uri_col;
&gt; +       query_vals[n_query_cols].type = DB1_STR;
&gt; +       query_vals[n_query_cols].nul = 0;
&gt; +       query_vals[n_query_cols].val.str_val.s = pres-&gt;watcher_uri-&gt;s;
&gt; +       query_vals[n_query_cols].val.str_val.len = pres-&gt;watcher_uri-&gt;len;
&gt; +       n_query_cols++;
&gt; +
&gt; +       query_cols[n_query_cols] =&amp;str_etag_col;
&gt; +       query_vals[n_query_cols].type = DB1_STR;
&gt; +       query_vals[n_query_cols].nul = 0;
&gt; +       query_vals[n_query_cols].val.str_val.s = 0;
&gt; +       query_vals[n_query_cols].val.str_val.len = 0;
&gt; +       n_query_cols++;
&gt; +
&gt; +       query_cols[n_query_cols] =&amp;str_tuple_id_col;
&gt; +       query_vals[n_query_cols].type = DB1_STR;
&gt; +       query_vals[n_query_cols].nul = 0;
&gt; +       query_vals[n_query_cols].val.str_val.s = 0;
&gt; +       query_vals[n_query_cols].val.str_val.len = 0;
&gt; +       n_query_cols++;
&gt; +
&gt; +       if (pua_dbf.replace != NULL)
&gt;          {
&gt; -               LM_ERR("Failed update db\n");
&gt; -               return -1;
&gt; +               if (pua_dbf.replace(pua_db, query_cols, query_vals, n_query_cols)&lt;  0)
&gt; +               {
&gt; +                       LM_ERR("Failed replace db\n");
&gt; +                       return -1;
&gt; +               }
&gt; +       }
&gt; +       else
&gt; +       {
&gt; +               if (pua_dbf.update(pua_db, query_cols, 0, query_vals,
&gt; +                               query_cols + 4, query_vals + 4, 4, n_query_cols - 4)&lt;  0)
&gt; +               {
&gt; +                       LM_ERR("Failed update db\n");
&gt; +                       return -1;
&gt; +               }
&gt; +
&gt; +               LM_DBG("affected_rows: %d\n", pua_dbf.affected_rows(pua_db));
&gt; +               if (pua_dbf.affected_rows(pua_db) == 0)
&gt; +               {
&gt; +                       if (pua_dbf.insert(pua_db, query_cols, query_vals, n_query_cols)&lt;  0)
&gt; +                       {
&gt; +                               LM_ERR("Failed insert db\n");
&gt; +                               return -1;
&gt; +                       }
&gt; +               }
&gt;          }
&gt;
&gt; +
&gt;          shm_free(pres-&gt;remote_contact.s);
&gt;          shm_free(pres);
&gt;
&gt; diff --git a/modules_k/pua/send_subscribe.c b/modules_k/pua/send_subscribe.c
&gt; index 15a1a02..b135ac3 100644
&gt; --- a/modules_k/pua/send_subscribe.c
&gt; +++ b/modules_k/pua/send_subscribe.c
&gt; @@ -1050,18 +1050,9 @@ insert:
&gt;
&gt;                  if(subs-&gt;flag&amp;  UPDATE_TYPE)
&gt;                  {
&gt; -                       /*
&gt; -                       LM_ERR("request for a subscription"
&gt; -                                       " with update type and no record found\n");
&gt; -                       ret= -1;
&gt; -                       goto done;
&gt; -                       commented this because of the strange type parameter in usrloc callback functions
&gt; -                       */
&gt; -
&gt;                          LM_DBG("request for a subscription with update type"
&gt;                                          " and no record found\n");
&gt;                          subs-&gt;flag= INSERT_TYPE;
&gt; -
&gt;                  }
&gt;                  hentity= subscribe_cbparam(subs, REQ_OTHER);
&gt;                  if(hentity== NULL)
&gt; @@ -1168,50 +1159,6 @@ insert:
&gt;          }
&gt;          else
&gt;          {
&gt; -        /*
&gt; -               if(presentity-&gt;desired_expires== 0)
&gt; -               {
&gt; -
&gt; -                       if(subs-&gt;expires&lt;  0)
&gt; -                       {
&gt; -                           LM_DBG("Found previous request for unlimited subscribe-"
&gt; -                                               " do not send subscribe\n");
&gt; -
&gt; -                               if (subs-&gt;event&amp;  PWINFO_EVENT)
&gt; -                               {
&gt; -                                       presentity-&gt;watcher_count++;
&gt; -                               }
&gt; -                               lock_release(&amp;HashT-&gt;p_records[hash_code].lock);
&gt; -                           goto done;
&gt; -
&gt; -                       }
&gt; -
&gt; -
&gt; -                       if(subs-&gt;event&amp;  PWINFO_EVENT)
&gt; -                       {
&gt; -                               if(subs-&gt;expires== 0)
&gt; -                               {
&gt; -                                       presentity-&gt;watcher_count--;
&gt; -                                       if(     presentity-&gt;watcher_count&gt;  0)
&gt; -                                       {
&gt; -                                               lock_release(&amp;HashT-&gt;p_records[hash_code].lock);
&gt; -                                               goto done;
&gt; -                                       }
&gt; -                               }
&gt; -                               else
&gt; -                               {
&gt; -                                       presentity-&gt;watcher_count++;
&gt; -                                       if(presentity-&gt;watcher_count&gt;  1)
&gt; -                                       {
&gt; -                                               lock_release(&amp;HashT-&gt;p_records[hash_code].lock);
&gt; -                                               goto done;
&gt; -                                       }
&gt; -                               }
&gt; -                       }
&gt; -
&gt; -               }
&gt; -        */
&gt; -
&gt;                  if (subs-&gt;internal_update_flag == INTERNAL_UPDATE_TRUE)
&gt;                  {
&gt;                          LM_INFO("attempting to re-SUBSCRIBE on internal (rls_update_subs()) update - skipping\n");
&gt; @@ -1265,7 +1212,6 @@ insert:
&gt;                  if (dbmode!=PUA_DB_ONLY)
&gt;                          lock_release(&amp;HashT-&gt;p_records[hash_code].lock);
&gt;
&gt; -       //      hentity-&gt;flag= flag;
&gt;                  LM_DBG("event parameter: %d\n", hentity-&gt;event);
&gt;
&gt;                  set_uac_req(&amp;uac_r,&amp;met, str_hdr, 0, td, TMCB_LOCAL_COMPLETED,
&gt;
&gt;
&gt; _______________________________________________
&gt; sr-dev mailing list
&gt; <a moz-do-not-send="true" href="mailto:sr-dev@lists.sip-router.org">sr-dev@lists.sip-router.org</a>
&gt; <a moz-do-not-send="true" href="http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-dev">http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-dev</a>
&gt;

</pre>
          </blockquote>
          <br>
          <table cellpadding="0" cellspacing="0" width="100%">
            <tbody>
              <tr>
                <td>
                  <pre>-- 
Peter Dunkley
Technical Director
Crocodile RCS Ltd
</pre>
                </td>
              </tr>
            </tbody>
          </table>
        </blockquote>
        <br>
      </blockquote>
      <br>
      <table cellpadding="0" cellspacing="0" width="100%">
        <tbody>
          <tr>
            <td>
              <pre>-- 
Peter Dunkley
Technical Director
Crocodile RCS Ltd
</pre>
            </td>
          </tr>
        </tbody>
      </table>
      <br>
      <fieldset class="mimeAttachmentHeader"></fieldset>
      <br>
      <pre wrap="">_______________________________________________
sr-dev mailing list
<a class="moz-txt-link-abbreviated" href="mailto:sr-dev@lists.sip-router.org">sr-dev@lists.sip-router.org</a>
<a class="moz-txt-link-freetext" href="http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-dev">http://lists.sip-router.org/cgi-bin/mailman/listinfo/sr-dev</a>
</pre>
    </blockquote>
    <br>
    <pre class="moz-signature" cols="72">-- 
Daniel-Constantin Mierla
Kamailio Advanced Training, April 23-26, 2012, Berlin, Germany
<a class="moz-txt-link-freetext" href="http://www.asipto.com/index.php/kamailio-advanced-training/">http://www.asipto.com/index.php/kamailio-advanced-training/</a></pre>
  </body>
</html>