Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that host:port portion of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that those UAs will support symmetric SIP signalling, so that this scheme will work just fine.
In my opinion there are two ways to do it: either add new rewritecontact* family of functions similar to rewritehost ones. or add a new flag for the save() function. This is where I need your help - which implementation looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into the mainline to reduce our local hacks.
Regards,
Maxim
On 10-01 14:32, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that host:port portion of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that those UAs will support symmetric SIP signalling, so that this scheme will work just fine.
In my opinion there are two ways to do it: either add new rewritecontact* family of functions similar to rewritehost ones. or add a new flag for the save() function. This is where I need your help - which implementation looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into the mainline to reduce our local hacks.
This should be implemented as a standalone module for ser. I want to keep registrar clean, it should not be aware of NATs. So, create a new module for ser that will contain all the NAT traversal helper functions, the functions will be then called from the config script.
That includes modifications of contact, adding rport to Via and so on.
regards, Jan.
Sounds reasonably - I'll do it that way.
Thanks!
-Maxim
On Fri, Jan 10, 2003 at 02:15:11PM +0100, Jan Janak wrote:
On 10-01 14:32, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that host:port portion of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that those UAs will support symmetric SIP signalling, so that this scheme will work just fine.
In my opinion there are two ways to do it: either add new rewritecontact* family of functions similar to rewritehost ones. or add a new flag for the save() function. This is where I need your help - which implementation looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into the mainline to reduce our local hacks.
This should be implemented as a standalone module for ser. I want to keep registrar clean, it should not be aware of NATs. So, create a new module for ser that will contain all the NAT traversal helper functions, the functions will be then called from the config script.
That includes modifications of contact, adding rport to Via and so on.
regards, Jan.
On Fri, Jan 10, 2003 at 03:37:46PM +0200, Maxim Sobolev wrote:
Sounds reasonably - I'll do it that way.
I am planning add a new nathelper module, which will export the following functions:
add_rport() - insert a rport= parameter into the first Via field fix_nated_contact() - replaces host:port in Contact field with host:port we received this message from add_direction_passive() - adds direction=passive option to the SDP
Then it would be possible to do the following at the very top of config before any other REGISTER/INVITE processing:
if (method == "INVITE" || method == "REGISTER") { if (search("User-Agent: Cisco ATA.*") { add_rport(); if (method == "INVITE") { add_direction_passive(); }; if (method == "REGISTER") { fix_nated_contact(); }; }; };
Does it sound reasonably for you?
-Maxim
Thanks!
-Maxim
On Fri, Jan 10, 2003 at 02:15:11PM +0100, Jan Janak wrote:
On 10-01 14:32, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that host:port portion of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that those UAs will support symmetric SIP signalling, so that this scheme will work just fine.
In my opinion there are two ways to do it: either add new rewritecontact* family of functions similar to rewritehost ones. or add a new flag for the save() function. This is where I need your help - which implementation looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into the mainline to reduce our local hacks.
This should be implemented as a standalone module for ser. I want to keep registrar clean, it should not be aware of NATs. So, create a new module for ser that will contain all the NAT traversal helper functions, the functions will be then called from the config script.
That includes modifications of contact, adding rport to Via and so on.
regards, Jan.
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Err, s/passive/active/g.
-Maxim
On Fri, Jan 10, 2003 at 07:27:49PM +0200, Maxim Sobolev wrote:
On Fri, Jan 10, 2003 at 03:37:46PM +0200, Maxim Sobolev wrote:
Sounds reasonably - I'll do it that way.
I am planning add a new nathelper module, which will export the following functions:
add_rport() - insert a rport= parameter into the first Via field fix_nated_contact() - replaces host:port in Contact field with host:port we received this message from add_direction_passive() - adds direction=passive option to the SDP
Then it would be possible to do the following at the very top of config before any other REGISTER/INVITE processing:
if (method == "INVITE" || method == "REGISTER") { if (search("User-Agent: Cisco ATA.*") { add_rport(); if (method == "INVITE") { add_direction_passive(); }; if (method == "REGISTER") { fix_nated_contact(); }; }; };
Does it sound reasonably for you?
-Maxim
Thanks!
-Maxim
On Fri, Jan 10, 2003 at 02:15:11PM +0100, Jan Janak wrote:
On 10-01 14:32, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that host:port portion of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that those UAs will support symmetric SIP signalling, so that this scheme will work just fine.
In my opinion there are two ways to do it: either add new rewritecontact* family of functions similar to rewritehost ones. or add a new flag for the save() function. This is where I need your help - which implementation looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into the mainline to reduce our local hacks.
This should be implemented as a standalone module for ser. I want to keep registrar clean, it should not be aware of NATs. So, create a new module for ser that will contain all the NAT traversal helper functions, the functions will be then called from the config script.
That includes modifications of contact, adding rport to Via and so on.
regards, Jan.
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Sounds good, go ahead. If you need any implementation-related advice, post a message to the mailing list, I will try to reply asap, some parts haven't been well documented yet so don't hesitate to ask.
regards, Jan.
On 10-01 19:27, Maxim Sobolev wrote:
On Fri, Jan 10, 2003 at 03:37:46PM +0200, Maxim Sobolev wrote:
Sounds reasonably - I'll do it that way.
I am planning add a new nathelper module, which will export the following functions:
add_rport() - insert a rport= parameter into the first Via field fix_nated_contact() - replaces host:port in Contact field with host:port we received this message from add_direction_passive() - adds direction=passive option to the SDP
Then it would be possible to do the following at the very top of config before any other REGISTER/INVITE processing:
if (method == "INVITE" || method == "REGISTER") { if (search("User-Agent: Cisco ATA.*") { add_rport(); if (method == "INVITE") { add_direction_passive(); }; if (method == "REGISTER") { fix_nated_contact(); }; }; };
Does it sound reasonably for you?
-Maxim
Thanks!
-Maxim
On Fri, Jan 10, 2003 at 02:15:11PM +0100, Jan Janak wrote:
On 10-01 14:32, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that host:port portion of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that those UAs will support symmetric SIP signalling, so that this scheme will work just fine.
In my opinion there are two ways to do it: either add new rewritecontact* family of functions similar to rewritehost ones. or add a new flag for the save() function. This is where I need your help - which implementation looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into the mainline to reduce our local hacks.
This should be implemented as a standalone module for ser. I want to keep registrar clean, it should not be aware of NATs. So, create a new module for ser that will contain all the NAT traversal helper functions, the functions will be then called from the config script.
That includes modifications of contact, adding rport to Via and so on.
regards, Jan.
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
On Fri, Jan 10, 2003 at 08:38:03PM +0100, Jan Janak wrote:
Sounds good, go ahead. If you need any implementation-related advice, post a message to the mailing list, I will try to reply asap, some parts haven't been well documented yet so don't hesitate to ask.
Thank you for your kind proposal!
I have one question:
Am I correct that in order to change some field in header I have to do the following:
1. Check that _m->somefield is NULL and call parse_headers() with the appropriate HDR_FOO flag. 2. Check that _m->somefield is non-NULL and return if false. 3. Modify _m->somefield according to the type of that field. 4. Using del_lump() mark original version of the header for deletion and using insert_new_lump() indicate where modified version should be placed before sending a message out.
-Maxim
regards, Jan.
On 10-01 19:27, Maxim Sobolev wrote:
On Fri, Jan 10, 2003 at 03:37:46PM +0200, Maxim Sobolev wrote:
Sounds reasonably - I'll do it that way.
I am planning add a new nathelper module, which will export the following functions:
add_rport() - insert a rport= parameter into the first Via field fix_nated_contact() - replaces host:port in Contact field with host:port we received this message from add_direction_passive() - adds direction=passive option to the SDP
Then it would be possible to do the following at the very top of config before any other REGISTER/INVITE processing:
if (method == "INVITE" || method == "REGISTER") { if (search("User-Agent: Cisco ATA.*") { add_rport(); if (method == "INVITE") { add_direction_passive(); }; if (method == "REGISTER") { fix_nated_contact(); }; }; };
Does it sound reasonably for you?
-Maxim
Thanks!
-Maxim
On Fri, Jan 10, 2003 at 02:15:11PM +0100, Jan Janak wrote:
On 10-01 14:32, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that host:port portion of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that those UAs will support symmetric SIP signalling, so that this scheme will work just fine.
In my opinion there are two ways to do it: either add new rewritecontact* family of functions similar to rewritehost ones. or add a new flag for the save() function. This is where I need your help - which implementation looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into the mainline to reduce our local hacks.
This should be implemented as a standalone module for ser. I want to keep registrar clean, it should not be aware of NATs. So, create a new module for ser that will contain all the NAT traversal helper functions, the functions will be then called from the config script.
That includes modifications of contact, adding rport to Via and so on.
regards, Jan.
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
that's correct.
- Check that _m->somefield is NULL and call parse_headers() with the
appropriate HDR_FOO flag.
yes. you can skip the first check, parse_headers will do it; don't forget to handle errors if parse_headers returns -1 (most likely mem alloc failure)
- Check that _m->somefield is non-NULL and return if false.
yes, that means, that the header field is not in message.
note too that some header fields may occur multiple times; in which case msg->hdr_foo points to the first occurence; if you wish to process all of them, you need to traverse the list msg->headers
example from record-routing (XXX are my extra comments):
int find_first_route(struct sip_msg* _m) { /* XXX don't look at _m->route ... parse_headers will do it for you */ if (parse_headers(_m, HDR_ROUTE, 0) == -1) { /* XXX -1 is some bad error, most likely lack of memory -- leave! */ LOG(L_ERR, "find_first_route(): Error while parsing headers\n"); return -1; } else {
if (_m->route) { return 0; } else { /* XXX not found .... the header field is not there */ DBG("find_first_route(): No Route headers found\n"); return 1; } } }
- Modify _m->somefield according to the type of that field.
- Using del_lump() mark original version of the header for deletion and
using insert_new_lump() indicate where modified version should be placed before sending a message out.
3 is the same as 4, isn't it. you modify a header field by creating a delete lump refering the old value and an insert lump with the new value. the new value must be created using pkg_malloc. the replace action in textops module is a good example. (The lump lists, sort of "diffs", is processed when later on the message is printed and forwarded.)
-Jiri
Thank you for the explanation! Attached please find first version of `rport' support for SER (we need it in the core engine), the module will follow probably tomorrow. Please note that the code is quite raw (6:20 am here ;) and I did not perform any seriuos tests yet.
The code does the following:
1. Adds support for `rport' Via field parameter into the message parser;
2. Extends parser to allow valueless parameters as requred by the draft RFC. This will probably requre additional code review, as there may be places in the code where it is assumed that parameter value could not be a NULL pointer;
3. Fills in blank rport parameter if present before forwarding a request or replying to it;
4. Changes the code, so that if `rport' is present, then this port is used for connecting to that UA.
Any comments, corrections or suggestions are highly appreciated!
-Maxim
On Sat, Jan 11, 2003 at 12:04:34AM +0100, Jiri Kuthan wrote:
that's correct.
- Check that _m->somefield is NULL and call parse_headers() with the
appropriate HDR_FOO flag.
yes. you can skip the first check, parse_headers will do it; don't forget to handle errors if parse_headers returns -1 (most likely mem alloc failure)
- Check that _m->somefield is non-NULL and return if false.
yes, that means, that the header field is not in message.
note too that some header fields may occur multiple times; in which case msg->hdr_foo points to the first occurence; if you wish to process all of them, you need to traverse the list msg->headers
example from record-routing (XXX are my extra comments):
int find_first_route(struct sip_msg* _m) { /* XXX don't look at _m->route ... parse_headers will do it for you */ if (parse_headers(_m, HDR_ROUTE, 0) == -1) { /* XXX -1 is some bad error, most likely lack of memory -- leave! */ LOG(L_ERR, "find_first_route(): Error while parsing headers\n"); return -1; } else {
if (_m->route) { return 0; } else { /* XXX not found .... the header field is not there */ DBG("find_first_route(): No Route headers found\n"); return 1; } }
}
- Modify _m->somefield according to the type of that field.
- Using del_lump() mark original version of the header for deletion and
using insert_new_lump() indicate where modified version should be placed before sending a message out.
3 is the same as 4, isn't it. you modify a header field by creating a delete lump refering the old value and an insert lump with the new value. the new value must be created using pkg_malloc. the replace action in textops module is a good example. (The lump lists, sort of "diffs", is processed when later on the message is printed and forwarded.)
-Jiri
Hello,
I will review the patch and let you know.
regards, Jan.
On 11-01 06:50, Maxim Sobolev wrote:
Thank you for the explanation! Attached please find first version of `rport' support for SER (we need it in the core engine), the module will follow probably tomorrow. Please note that the code is quite raw (6:20 am here ;) and I did not perform any seriuos tests yet.
The code does the following:
- Adds support for `rport' Via field parameter into the message
parser;
- Extends parser to allow valueless parameters as requred by
the draft RFC. This will probably requre additional code review, as there may be places in the code where it is assumed that parameter value could not be a NULL pointer;
- Fills in blank rport parameter if present before forwarding
a request or replying to it;
- Changes the code, so that if `rport' is present, then this
port is used for connecting to that UA.
Any comments, corrections or suggestions are highly appreciated!
-Maxim
On Sat, Jan 11, 2003 at 12:04:34AM +0100, Jiri Kuthan wrote:
that's correct.
- Check that _m->somefield is NULL and call parse_headers() with the
appropriate HDR_FOO flag.
yes. you can skip the first check, parse_headers will do it; don't forget to handle errors if parse_headers returns -1 (most likely mem alloc failure)
- Check that _m->somefield is non-NULL and return if false.
yes, that means, that the header field is not in message.
note too that some header fields may occur multiple times; in which case msg->hdr_foo points to the first occurence; if you wish to process all of them, you need to traverse the list msg->headers
example from record-routing (XXX are my extra comments):
int find_first_route(struct sip_msg* _m) { /* XXX don't look at _m->route ... parse_headers will do it for you */ if (parse_headers(_m, HDR_ROUTE, 0) == -1) { /* XXX -1 is some bad error, most likely lack of memory -- leave! */ LOG(L_ERR, "find_first_route(): Error while parsing headers\n"); return -1; } else {
if (_m->route) { return 0; } else { /* XXX not found .... the header field is not there */ DBG("find_first_route(): No Route headers found\n"); return 1; } }
}
- Modify _m->somefield according to the type of that field.
- Using del_lump() mark original version of the header for deletion and
using insert_new_lump() indicate where modified version should be placed before sending a message out.
3 is the same as 4, isn't it. you modify a header field by creating a delete lump refering the old value and an insert lump with the new value. the new value must be created using pkg_malloc. the replace action in textops module is a good example. (The lump lists, sort of "diffs", is processed when later on the message is printed and forwarded.)
-Jiri
--- modules/sl/sl_funcs.c.orig Mon Oct 21 23:30:15 2002 +++ modules/sl/sl_funcs.c Sat Jan 11 06:09:54 2003 @@ -121,7 +121,7 @@ to.sin_family = AF_INET; */
if (reply_to_via) {
if (update_sock_struct_from_via( &(to), msg->via1 )==-1)
{ LOG(L_ERR, "ERROR: sl_send_reply: " "cannot lookup reply dst: %s\n",if (update_sock_struct_from_via( &(to), msg->via1, msg)==-1)
--- modules/tm/t_lookup.c.orig Mon Oct 21 22:21:50 2002 +++ modules/tm/t_lookup.c Sat Jan 11 06:09:54 2003 @@ -717,7 +717,7 @@ } else { via=msg->via1; /*init retrans buffer*/
if (update_sock_struct_from_via( &(rb->to),via )==-1) {
if (update_sock_struct_from_via( &(rb->to),via,msg )==-1) { LOG(L_ERR, "ERROR: init_rb: cannot lookup reply dst: %.*s\n", via->host.len, via->host.s ); ser_error=E_BAD_VIA;
--- parser/msg_parser.h.orig Wed Oct 23 18:12:20 2002 +++ parser/msg_parser.h Sat Jan 11 06:15:11 2003 @@ -118,6 +118,7 @@ char* unparsed; /* here we stopped parsing*/
struct ip_addr src_ip;
unsigned short src_port_no; struct ip_addr dst_ip;
char* orig; /* original message copy */
--- parser/parse_via.c.orig Thu Sep 19 15:23:55 2002 +++ parser/parse_via.c Sat Jan 11 06:09:54 2003 @@ -87,8 +87,10 @@ MADDR1, MADDR2, MADDR3, MADDR4, RECEIVED1, RECEIVED2, RECEIVED3, RECEIVED4, RECEIVED5, RECEIVED6, RECEIVED7,
- RPORT1, RPORT2, RPORT3, /* fin states (227-...)*/ FIN_HIDDEN = 230, FIN_TTL, FIN_BRANCH, FIN_MADDR, FIN_RECEIVED,
- FIN_RPORT, /*GEN_PARAM, PARAM_ERROR*/ /* declared in msg_parser.h*/
}; @@ -125,6 +127,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -160,6 +163,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -200,6 +204,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -231,6 +236,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -267,6 +273,7 @@ case FIN_MADDR: case FIN_TTL: case FIN_RECEIVED:
case FIN_RPORT: LOG(L_ERR, "ERROR: parse_via: invalid char <%c> in" " state %d\n", *tmp, state); goto error;
@@ -296,6 +303,7 @@ case FIN_MADDR: case FIN_TTL: case FIN_RECEIVED:
case FIN_RPORT: LOG(L_ERR, "ERROR: parse_via_param: new via found" "(',') when '=' expected (state %d=)\n", state);
@@ -458,6 +466,9 @@ case TTL1: state=TTL2; break;
case RPORT3:
state=FIN_RPORT;
break; case GEN_PARAM: break; case F_CR:
@@ -545,6 +556,9 @@ case BRANCH1: state=BRANCH2; break;
case RPORT2:
state=RPORT3;
break; case GEN_PARAM: break; case F_CR:
@@ -619,7 +633,36 @@ state=GEN_PARAM; } break;
case 'p':
case 'P':
switch(state){
case RECEIVED1:
state=RPORT1;
break;
case F_CR:
case F_LF:
case F_CRLF:
state=END_OF_HEADER;
goto end_via;
default:
state=GEN_PARAM;
}
break;
case 'o':
case 'O':
switch(state){
case RPORT1:
state=RPORT2;
break;
case F_CR:
case F_LF:
case F_CRLF:
state=END_OF_HEADER;
goto end_via;
default:
state=GEN_PARAM;
}
break; default: switch(state){ case F_PARAM:
@@ -750,6 +793,11 @@ param->value.len=tmp-param->value.s; state=F_PARAM; goto endofvalue;
case F_VALUE:
*tmp=0;
param->value.len=0;
state=F_PARAM;
goto endofvalue; case P_STRING: break; /* what to do? */ case F_LF:
@@ -1681,6 +1729,8 @@ vb->branch=param; else if (param->type==PARAM_RECEIVED) vb->received=param;
else if (param->type==PARAM_RPORT)
vb->rport=param; break; case P_PARAM: break;
--- parser/parse_via.h.orig Mon Oct 21 18:46:27 2002 +++ parser/parse_via.h Sat Jan 11 06:09:54 2003 @@ -36,7 +36,7 @@ */ enum { PARAM_HIDDEN=230, PARAM_TTL, PARAM_BRANCH,
- PARAM_MADDR, PARAM_RECEIVED, GEN_PARAM,
- PARAM_MADDR, PARAM_RECEIVED, PARAM_RPORT, GEN_PARAM, PARAM_ERROR
};
@@ -70,6 +70,7 @@ struct via_param* branch; str tid; /* transaction id, part of branch */ struct via_param* received;
struct via_param* rport;
struct via_body* next; /* pointer to next via body string if compact via or null */
--- config.h.orig Mon Oct 21 22:21:50 2002 +++ config.h Sat Jan 11 06:09:54 2003 @@ -82,6 +82,9 @@ #define RECEIVED ";received=" #define RECEIVED_LEN 10
+#define RPORT ";rport=" +#define RPORT_LEN 7
#define SRV_PREFIX "_sip._udp." #define SRV_PREFIX_LEN 10
@@ -112,6 +115,7 @@ /* forwarding -- Via buffer dimensioning */ #define MAX_VIA_LINE_SIZE 240 #define MAX_RECEIVED_SIZE 57 +#define MAX_RPORT_SIZE 13
/* maximum number of branches per transaction */ #define MAX_BRANCHES 4 --- forward.h.orig Wed Oct 23 18:12:20 2002 +++ forward.h Sat Jan 11 06:09:54 2003 @@ -40,7 +40,7 @@ int check_self(str* host, unsigned short port); int forward_request( struct sip_msg* msg, struct proxy_l* p); int update_sock_struct_from_via( union sockaddr_union* to,
struct via_body* via );
- struct via_body* via, struct sip_msg *msg );
int update_sock_struct_from_ip( union sockaddr_union* to, struct sip_msg *msg ); int forward_reply( struct sip_msg* msg); --- ip_addr.h.orig Mon Nov 4 19:05:32 2002 +++ ip_addr.h Sat Jan 11 06:09:54 2003 @@ -201,6 +201,21 @@ } }
+static inline unsigned int su2port_no(union sockaddr_union* su) +{
- switch(su->s.sa_family){
- case AF_INET:
return ntohs(su->sin.sin_port);
+#ifdef USE_IPV6
- case AF_INET6:
return ntohs(su->sin6.sin6_port);
+#endif
- default:
LOG(L_CRIT,"su2port_no: BUG: unknown address family %d\n",
su->s.sa_family);
- }
- return 0;
+}
/* ip_addr2su -> the same as init_su*/ #define ip_addr2su init_su --- msg_translator.c.orig Mon Oct 21 22:21:50 2002 +++ msg_translator.c Sat Jan 11 06:09:54 2003 @@ -145,9 +145,10 @@ else foo=&(msg->first_line.u.request.uri); print_len=snprintf(buf+fix_len, MAX_WARNING_LEN-fix_len,
"pid=%d req_src_ip=%s in_uri=%.*s out_uri=%.*s via_cnt%c=%d\"",
my_pid(), ip_addr2a(&msg->src_ip),"pid=%d req_src_ip=%s req_src_port=%d in_uri=%.*s out_uri=%.*s via_cnt%c=%d\"",
msg->first_line.u.request.uri.len, msg->first_line.u.request.uri.s, foo->len, foo->s, msg->parsed_flag & HDR_EOH ? '=' : '>', /* should be = */msg->src_port_no,
@@ -208,7 +209,26 @@ return buf; }
+char *rport_builder(struct sip_msg *msg, unsigned int *rport_len) +{
char *buf;
buf=pkg_malloc(sizeof(char)*MAX_RPORT_SIZE);
if (buf==0){
ser_error=E_OUT_OF_MEM;
LOG(L_ERR, "ERROR: rport_builder: out of memory\n");
return 0;
}
/*
* Don't need any sanity checks here, because
* msg->src_port_no is unsigned int, which means
* that it couldn't be more than 5 chars long in
* string representation.
*/
*rport_len = snprintf(buf, MAX_RPORT_SIZE, "%s%u", RPORT, msg->src_port_no);
return buf;
+}
/* computes the "unpacked" len of a lump list, code moved from build_req_from_req */ @@ -395,9 +415,10 @@ unsigned int *returned_len, struct socket_info* send_sock) {
- unsigned int len, new_len, received_len, uri_len, via_len;
- unsigned int len, new_len, received_len, rport_len, uri_len, via_len; char* line_buf; char* received_buf;
- char* rport_buf; char* new_buf; char* orig; char* buf;
@@ -411,8 +432,10 @@ buf=msg->buf; len=msg->len; received_len=0;
rport_len=0; new_buf=0; received_buf=0;
rport_buf=0;
line_buf = via_builder( &via_len, send_sock,
@@ -430,6 +453,11 @@ if ((received_buf=received_builder(msg,&received_len))==0) goto error01; /* free also line_buf */ }
/* check if rport needs to be updated */
if (msg->via1->rport && msg->via1->rport->value.s==0) {
if ((rport_buf=rport_builder(msg,&rport_len))==0)
goto error01; /* free also line_buf */
}
/* add via header to the list */ /* try to add it before msg. 1st via */
@@ -459,6 +487,14 @@ if (insert_new_lump_after(anchor, received_buf, received_len, HDR_VIA) ==0 ) goto error02; /* free also line_buf */ }
/* if rport needs to be updated do it */
if (rport_len){
offset = msg->via1->rport->value.s - buf - 1;
anchor=del_lump(&msg->add_rm, offset, msg->via1->rport->size + 1, HDR_VIA);
if (anchor==0) goto error02; /* free also line_buf */
if (insert_new_lump_after(anchor, rport_buf, rport_len, HDR_VIA)
==0 ) goto error02; /* free also line_buf */
}
/* compute new msg len and fix overlapping zones*/ new_len=len+lumps_len(msg->add_rm);
@@ -507,6 +543,7 @@ pkg_free(line_buf); error02: if (received_buf) pkg_free(received_buf);
- if (rport_buf) pkg_free(rport_buf);
error00: *returned_len=0; return 0; @@ -587,14 +624,18 @@ int i; char backup; char *received_buf;
char *rport_buf; unsigned int received_len;
unsigned int rport_len; char *warning; unsigned int warning_len; int r; str to_tag;
received_buf=0;
rport_buf=0; received_len=0;
rport_len=0; buf=0; /* make -Wall happy */ warning=0;
@@ -621,6 +662,15 @@ goto error00; } }
/* check if rport needs to be updated */
if (msg->via1->rport && msg->via1->rport->value.s==0) {
if ((rport_buf=rport_builder(msg,&rport_len))==0) {
LOG(L_ERR, "ERROR: build_res_buf_from_sip_req: "
"alas, rport_builder failed\n");
goto error00;
}
rport_len -= msg->via1->rport->size;
}
/*computes the lenght of the new response buffer*/ len = 0;
@@ -641,7 +691,7 @@ len+=new_tag_len+5/*";tag="*/; } case HDR_VIA:
if (hdr==msg->h_via1) len += received_len;
if (hdr==msg->h_via1) len += received_len + rport_len; case HDR_FROM: case HDR_CALLID: case HDR_CSEQ:
@@ -716,10 +766,23 @@ break; } case HDR_VIA:
append_str_trans( p, hdr->name.s ,
if (hdr==msg->h_via1 && rport_buf) {
append_str_trans( p, hdr->name.s ,
msg->via1->rport->name.s - hdr->name.s - 1, msg);
append_str( p, rport_buf,
rport_len + msg->via1->rport->size, msg);
append_str_trans( p, msg->via1->rport->name.s +
msg->via1->rport->size,
hdr->body.s + hdr->body.len -
msg->via1->rport->name.s -
msg->via1->rport->size, msg);
} else {
append_str_trans( p, hdr->name.s , ((hdr->body.s+hdr->body.len )-hdr->name.s ),msg);
} if (hdr==msg->h_via1 && received_buf) append_str( p, received_buf, received_len, msg);
append_str( p, CRLF,CRLF_LEN,msg); break; case HDR_FROM:
@@ -763,10 +826,12 @@ needs to be deleted here */ if (received_buf) pkg_free(received_buf);
- if (rport_buf) pkg_free(rport_buf); return buf;
error01: if (received_buf) pkg_free(received_buf);
- if (rport_buf) pkg_free(rport_buf);
error00: *returned_len=0; return 0; --- receive.c.orig Thu Oct 3 23:06:10 2002 +++ receive.c Sat Jan 11 06:09:54 2003 @@ -77,6 +77,7 @@ useful as most of the work is done with scrath-pad; -jiri */ buf[len]=0; su2ip_addr(&msg->src_ip, src_su);
- msg->src_port_no=su2port_no(src_su); msg->dst_ip=bind_address->address; /* won't work if listening on 0.0.0.0 */ msg->id=msg_no; /* make a copy of the message */
--- forward.c.orig Thu Oct 24 17:21:08 2002 +++ forward.c Sat Jan 11 06:21:11 2003 @@ -240,14 +240,21 @@ int update_sock_struct_from_ip( union sockaddr_union* to, struct sip_msg *msg ) {
- unsigned short port_no;
- init_su(to, &msg->src_ip,
(msg->via1->port)?htons(msg->via1->port): htons(SIP_PORT) );
- if (msg->src_port_no)
port_no = msg->src_port_no;
- else if (msg->via1->port)
port_no = msg->via1->port;
- else
port_no = SIP_PORT;
- init_su(to, &msg->src_ip, htons(port_no)); return 1;
}
int update_sock_struct_from_via( union sockaddr_union* to,
struct via_body* via )
- struct via_body* via, struct sip_msg *msg )
{ struct hostent* he; char *host_copy; @@ -266,6 +273,11 @@ name=&(via->host); port=via->port; }
- if (via->rport && via->rport->value.s) {
port=atoi(via->rport->value.s);
- } else if (via->rport && via->rport->value.s==0 && !via->received) {
return update_sock_struct_from_ip( to, msg);
- } /* we do now a malloc/memcpy because gethostbyname loves \0-terminated strings; -jiri but only if host is not null terminated
@@ -353,7 +365,7 @@ goto error; }
- if (update_sock_struct_from_via( to, msg->via2 )==-1) goto error;
- if (update_sock_struct_from_via( to, msg->via2, msg)==-1) goto error; send_sock=get_send_socket(to); if (send_sock==0){ LOG(L_ERR, "forward_reply: ERROR: no sending socket found\n");
On Sun, Jan 12, 2003 at 06:02:35PM +0100, Jan Janak wrote:
Hello,
I will review the patch and let you know.
Thanks! In the meantime, I've located and fixed several bugs in it, so that please don't hurry up applying it to the repository yet.
-Maxim
regards, Jan.
On 11-01 06:50, Maxim Sobolev wrote:
Thank you for the explanation! Attached please find first version of `rport' support for SER (we need it in the core engine), the module will follow probably tomorrow. Please note that the code is quite raw (6:20 am here ;) and I did not perform any seriuos tests yet.
The code does the following:
- Adds support for `rport' Via field parameter into the message
parser;
- Extends parser to allow valueless parameters as requred by
the draft RFC. This will probably requre additional code review, as there may be places in the code where it is assumed that parameter value could not be a NULL pointer;
- Fills in blank rport parameter if present before forwarding
a request or replying to it;
- Changes the code, so that if `rport' is present, then this
port is used for connecting to that UA.
Any comments, corrections or suggestions are highly appreciated!
-Maxim
On Sat, Jan 11, 2003 at 12:04:34AM +0100, Jiri Kuthan wrote:
that's correct.
- Check that _m->somefield is NULL and call parse_headers() with the
appropriate HDR_FOO flag.
yes. you can skip the first check, parse_headers will do it; don't forget to handle errors if parse_headers returns -1 (most likely mem alloc failure)
- Check that _m->somefield is non-NULL and return if false.
yes, that means, that the header field is not in message.
note too that some header fields may occur multiple times; in which case msg->hdr_foo points to the first occurence; if you wish to process all of them, you need to traverse the list msg->headers
example from record-routing (XXX are my extra comments):
int find_first_route(struct sip_msg* _m) { /* XXX don't look at _m->route ... parse_headers will do it for you */ if (parse_headers(_m, HDR_ROUTE, 0) == -1) { /* XXX -1 is some bad error, most likely lack of memory -- leave! */ LOG(L_ERR, "find_first_route(): Error while parsing headers\n"); return -1; } else {
if (_m->route) { return 0; } else { /* XXX not found .... the header field is not there */ DBG("find_first_route(): No Route headers found\n"); return 1; } }
}
- Modify _m->somefield according to the type of that field.
- Using del_lump() mark original version of the header for deletion and
using insert_new_lump() indicate where modified version should be placed before sending a message out.
3 is the same as 4, isn't it. you modify a header field by creating a delete lump refering the old value and an insert lump with the new value. the new value must be created using pkg_malloc. the replace action in textops module is a good example. (The lump lists, sort of "diffs", is processed when later on the message is printed and forwarded.)
-Jiri
--- modules/sl/sl_funcs.c.orig Mon Oct 21 23:30:15 2002 +++ modules/sl/sl_funcs.c Sat Jan 11 06:09:54 2003 @@ -121,7 +121,7 @@ to.sin_family = AF_INET; */
if (reply_to_via) {
if (update_sock_struct_from_via( &(to), msg->via1 )==-1)
{ LOG(L_ERR, "ERROR: sl_send_reply: " "cannot lookup reply dst: %s\n",if (update_sock_struct_from_via( &(to), msg->via1, msg)==-1)
--- modules/tm/t_lookup.c.orig Mon Oct 21 22:21:50 2002 +++ modules/tm/t_lookup.c Sat Jan 11 06:09:54 2003 @@ -717,7 +717,7 @@ } else { via=msg->via1; /*init retrans buffer*/
if (update_sock_struct_from_via( &(rb->to),via )==-1) {
if (update_sock_struct_from_via( &(rb->to),via,msg )==-1) { LOG(L_ERR, "ERROR: init_rb: cannot lookup reply dst: %.*s\n", via->host.len, via->host.s ); ser_error=E_BAD_VIA;
--- parser/msg_parser.h.orig Wed Oct 23 18:12:20 2002 +++ parser/msg_parser.h Sat Jan 11 06:15:11 2003 @@ -118,6 +118,7 @@ char* unparsed; /* here we stopped parsing*/
struct ip_addr src_ip;
unsigned short src_port_no; struct ip_addr dst_ip;
char* orig; /* original message copy */
--- parser/parse_via.c.orig Thu Sep 19 15:23:55 2002 +++ parser/parse_via.c Sat Jan 11 06:09:54 2003 @@ -87,8 +87,10 @@ MADDR1, MADDR2, MADDR3, MADDR4, RECEIVED1, RECEIVED2, RECEIVED3, RECEIVED4, RECEIVED5, RECEIVED6, RECEIVED7,
- RPORT1, RPORT2, RPORT3, /* fin states (227-...)*/ FIN_HIDDEN = 230, FIN_TTL, FIN_BRANCH, FIN_MADDR, FIN_RECEIVED,
- FIN_RPORT, /*GEN_PARAM, PARAM_ERROR*/ /* declared in msg_parser.h*/
}; @@ -125,6 +127,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -160,6 +163,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -200,6 +204,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -231,6 +236,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -267,6 +273,7 @@ case FIN_MADDR: case FIN_TTL: case FIN_RECEIVED:
case FIN_RPORT: LOG(L_ERR, "ERROR: parse_via: invalid char <%c> in" " state %d\n", *tmp, state); goto error;
@@ -296,6 +303,7 @@ case FIN_MADDR: case FIN_TTL: case FIN_RECEIVED:
case FIN_RPORT: LOG(L_ERR, "ERROR: parse_via_param: new via found" "(',') when '=' expected (state %d=)\n", state);
@@ -458,6 +466,9 @@ case TTL1: state=TTL2; break;
case RPORT3:
state=FIN_RPORT;
break; case GEN_PARAM: break; case F_CR:
@@ -545,6 +556,9 @@ case BRANCH1: state=BRANCH2; break;
case RPORT2:
state=RPORT3;
break; case GEN_PARAM: break; case F_CR:
@@ -619,7 +633,36 @@ state=GEN_PARAM; } break;
case 'p':
case 'P':
switch(state){
case RECEIVED1:
state=RPORT1;
break;
case F_CR:
case F_LF:
case F_CRLF:
state=END_OF_HEADER;
goto end_via;
default:
state=GEN_PARAM;
}
break;
case 'o':
case 'O':
switch(state){
case RPORT1:
state=RPORT2;
break;
case F_CR:
case F_LF:
case F_CRLF:
state=END_OF_HEADER;
goto end_via;
default:
state=GEN_PARAM;
}
break; default: switch(state){ case F_PARAM:
@@ -750,6 +793,11 @@ param->value.len=tmp-param->value.s; state=F_PARAM; goto endofvalue;
case F_VALUE:
*tmp=0;
param->value.len=0;
state=F_PARAM;
goto endofvalue; case P_STRING: break; /* what to do? */ case F_LF:
@@ -1681,6 +1729,8 @@ vb->branch=param; else if (param->type==PARAM_RECEIVED) vb->received=param;
else if (param->type==PARAM_RPORT)
vb->rport=param; break; case P_PARAM: break;
--- parser/parse_via.h.orig Mon Oct 21 18:46:27 2002 +++ parser/parse_via.h Sat Jan 11 06:09:54 2003 @@ -36,7 +36,7 @@ */ enum { PARAM_HIDDEN=230, PARAM_TTL, PARAM_BRANCH,
- PARAM_MADDR, PARAM_RECEIVED, GEN_PARAM,
- PARAM_MADDR, PARAM_RECEIVED, PARAM_RPORT, GEN_PARAM, PARAM_ERROR
};
@@ -70,6 +70,7 @@ struct via_param* branch; str tid; /* transaction id, part of branch */ struct via_param* received;
struct via_param* rport;
struct via_body* next; /* pointer to next via body string if compact via or null */
--- config.h.orig Mon Oct 21 22:21:50 2002 +++ config.h Sat Jan 11 06:09:54 2003 @@ -82,6 +82,9 @@ #define RECEIVED ";received=" #define RECEIVED_LEN 10
+#define RPORT ";rport=" +#define RPORT_LEN 7
#define SRV_PREFIX "_sip._udp." #define SRV_PREFIX_LEN 10
@@ -112,6 +115,7 @@ /* forwarding -- Via buffer dimensioning */ #define MAX_VIA_LINE_SIZE 240 #define MAX_RECEIVED_SIZE 57 +#define MAX_RPORT_SIZE 13
/* maximum number of branches per transaction */ #define MAX_BRANCHES 4 --- forward.h.orig Wed Oct 23 18:12:20 2002 +++ forward.h Sat Jan 11 06:09:54 2003 @@ -40,7 +40,7 @@ int check_self(str* host, unsigned short port); int forward_request( struct sip_msg* msg, struct proxy_l* p); int update_sock_struct_from_via( union sockaddr_union* to,
struct via_body* via );
- struct via_body* via, struct sip_msg *msg );
int update_sock_struct_from_ip( union sockaddr_union* to, struct sip_msg *msg ); int forward_reply( struct sip_msg* msg); --- ip_addr.h.orig Mon Nov 4 19:05:32 2002 +++ ip_addr.h Sat Jan 11 06:09:54 2003 @@ -201,6 +201,21 @@ } }
+static inline unsigned int su2port_no(union sockaddr_union* su) +{
- switch(su->s.sa_family){
- case AF_INET:
return ntohs(su->sin.sin_port);
+#ifdef USE_IPV6
- case AF_INET6:
return ntohs(su->sin6.sin6_port);
+#endif
- default:
LOG(L_CRIT,"su2port_no: BUG: unknown address family %d\n",
su->s.sa_family);
- }
- return 0;
+}
/* ip_addr2su -> the same as init_su*/ #define ip_addr2su init_su --- msg_translator.c.orig Mon Oct 21 22:21:50 2002 +++ msg_translator.c Sat Jan 11 06:09:54 2003 @@ -145,9 +145,10 @@ else foo=&(msg->first_line.u.request.uri); print_len=snprintf(buf+fix_len, MAX_WARNING_LEN-fix_len,
"pid=%d req_src_ip=%s in_uri=%.*s out_uri=%.*s via_cnt%c=%d\"",
my_pid(), ip_addr2a(&msg->src_ip),"pid=%d req_src_ip=%s req_src_port=%d in_uri=%.*s out_uri=%.*s via_cnt%c=%d\"",
msg->first_line.u.request.uri.len, msg->first_line.u.request.uri.s, foo->len, foo->s, msg->parsed_flag & HDR_EOH ? '=' : '>', /* should be = */msg->src_port_no,
@@ -208,7 +209,26 @@ return buf; }
+char *rport_builder(struct sip_msg *msg, unsigned int *rport_len) +{
char *buf;
buf=pkg_malloc(sizeof(char)*MAX_RPORT_SIZE);
if (buf==0){
ser_error=E_OUT_OF_MEM;
LOG(L_ERR, "ERROR: rport_builder: out of memory\n");
return 0;
}
/*
* Don't need any sanity checks here, because
* msg->src_port_no is unsigned int, which means
* that it couldn't be more than 5 chars long in
* string representation.
*/
*rport_len = snprintf(buf, MAX_RPORT_SIZE, "%s%u", RPORT, msg->src_port_no);
return buf;
+}
/* computes the "unpacked" len of a lump list, code moved from build_req_from_req */ @@ -395,9 +415,10 @@ unsigned int *returned_len, struct socket_info* send_sock) {
- unsigned int len, new_len, received_len, uri_len, via_len;
- unsigned int len, new_len, received_len, rport_len, uri_len, via_len; char* line_buf; char* received_buf;
- char* rport_buf; char* new_buf; char* orig; char* buf;
@@ -411,8 +432,10 @@ buf=msg->buf; len=msg->len; received_len=0;
rport_len=0; new_buf=0; received_buf=0;
rport_buf=0;
line_buf = via_builder( &via_len, send_sock,
@@ -430,6 +453,11 @@ if ((received_buf=received_builder(msg,&received_len))==0) goto error01; /* free also line_buf */ }
/* check if rport needs to be updated */
if (msg->via1->rport && msg->via1->rport->value.s==0) {
if ((rport_buf=rport_builder(msg,&rport_len))==0)
goto error01; /* free also line_buf */
}
/* add via header to the list */ /* try to add it before msg. 1st via */
@@ -459,6 +487,14 @@ if (insert_new_lump_after(anchor, received_buf, received_len, HDR_VIA) ==0 ) goto error02; /* free also line_buf */ }
/* if rport needs to be updated do it */
if (rport_len){
offset = msg->via1->rport->value.s - buf - 1;
anchor=del_lump(&msg->add_rm, offset, msg->via1->rport->size + 1, HDR_VIA);
if (anchor==0) goto error02; /* free also line_buf */
if (insert_new_lump_after(anchor, rport_buf, rport_len, HDR_VIA)
==0 ) goto error02; /* free also line_buf */
}
/* compute new msg len and fix overlapping zones*/ new_len=len+lumps_len(msg->add_rm);
@@ -507,6 +543,7 @@ pkg_free(line_buf); error02: if (received_buf) pkg_free(received_buf);
- if (rport_buf) pkg_free(rport_buf);
error00: *returned_len=0; return 0; @@ -587,14 +624,18 @@ int i; char backup; char *received_buf;
char *rport_buf; unsigned int received_len;
unsigned int rport_len; char *warning; unsigned int warning_len; int r; str to_tag;
received_buf=0;
rport_buf=0; received_len=0;
rport_len=0; buf=0; /* make -Wall happy */ warning=0;
@@ -621,6 +662,15 @@ goto error00; } }
/* check if rport needs to be updated */
if (msg->via1->rport && msg->via1->rport->value.s==0) {
if ((rport_buf=rport_builder(msg,&rport_len))==0) {
LOG(L_ERR, "ERROR: build_res_buf_from_sip_req: "
"alas, rport_builder failed\n");
goto error00;
}
rport_len -= msg->via1->rport->size;
}
/*computes the lenght of the new response buffer*/ len = 0;
@@ -641,7 +691,7 @@ len+=new_tag_len+5/*";tag="*/; } case HDR_VIA:
if (hdr==msg->h_via1) len += received_len;
if (hdr==msg->h_via1) len += received_len + rport_len; case HDR_FROM: case HDR_CALLID: case HDR_CSEQ:
@@ -716,10 +766,23 @@ break; } case HDR_VIA:
append_str_trans( p, hdr->name.s ,
if (hdr==msg->h_via1 && rport_buf) {
append_str_trans( p, hdr->name.s ,
msg->via1->rport->name.s - hdr->name.s - 1, msg);
append_str( p, rport_buf,
rport_len + msg->via1->rport->size, msg);
append_str_trans( p, msg->via1->rport->name.s +
msg->via1->rport->size,
hdr->body.s + hdr->body.len -
msg->via1->rport->name.s -
msg->via1->rport->size, msg);
} else {
append_str_trans( p, hdr->name.s , ((hdr->body.s+hdr->body.len )-hdr->name.s ),msg);
} if (hdr==msg->h_via1 && received_buf) append_str( p, received_buf, received_len, msg);
append_str( p, CRLF,CRLF_LEN,msg); break; case HDR_FROM:
@@ -763,10 +826,12 @@ needs to be deleted here */ if (received_buf) pkg_free(received_buf);
- if (rport_buf) pkg_free(rport_buf); return buf;
error01: if (received_buf) pkg_free(received_buf);
- if (rport_buf) pkg_free(rport_buf);
error00: *returned_len=0; return 0; --- receive.c.orig Thu Oct 3 23:06:10 2002 +++ receive.c Sat Jan 11 06:09:54 2003 @@ -77,6 +77,7 @@ useful as most of the work is done with scrath-pad; -jiri */ buf[len]=0; su2ip_addr(&msg->src_ip, src_su);
- msg->src_port_no=su2port_no(src_su); msg->dst_ip=bind_address->address; /* won't work if listening on 0.0.0.0 */ msg->id=msg_no; /* make a copy of the message */
--- forward.c.orig Thu Oct 24 17:21:08 2002 +++ forward.c Sat Jan 11 06:21:11 2003 @@ -240,14 +240,21 @@ int update_sock_struct_from_ip( union sockaddr_union* to, struct sip_msg *msg ) {
- unsigned short port_no;
- init_su(to, &msg->src_ip,
(msg->via1->port)?htons(msg->via1->port): htons(SIP_PORT) );
- if (msg->src_port_no)
port_no = msg->src_port_no;
- else if (msg->via1->port)
port_no = msg->via1->port;
- else
port_no = SIP_PORT;
- init_su(to, &msg->src_ip, htons(port_no)); return 1;
}
int update_sock_struct_from_via( union sockaddr_union* to,
struct via_body* via )
- struct via_body* via, struct sip_msg *msg )
{ struct hostent* he; char *host_copy; @@ -266,6 +273,11 @@ name=&(via->host); port=via->port; }
- if (via->rport && via->rport->value.s) {
port=atoi(via->rport->value.s);
- } else if (via->rport && via->rport->value.s==0 && !via->received) {
return update_sock_struct_from_ip( to, msg);
- } /* we do now a malloc/memcpy because gethostbyname loves \0-terminated strings; -jiri but only if host is not null terminated
@@ -353,7 +365,7 @@ goto error; }
- if (update_sock_struct_from_via( to, msg->via2 )==-1) goto error;
- if (update_sock_struct_from_via( to, msg->via2, msg)==-1) goto error; send_sock=get_send_socket(to); if (send_sock==0){ LOG(L_ERR, "forward_reply: ERROR: no sending socket found\n");
I've almost done with add_rport() and fix_nated_contact(), it sorta works, but I've bumped to a problem, I have no good solution for. My setup is as follows:
private public [UA1]---[NAT]---[SER]---[UA2]
UA1 is Cisco ATA186 UA2 is eStara SoftPHONE
Since eStara doesn't support symmetric signalling, my config looks like the following:
if (search("User-Agent: Cisco ATA.*") || search("Server: Cisco ATA.*")) { add_rport(); fix_nated_contact(); }; rewriteFromRoute();
[REGISTRATION STUFF STRIPPED]
if (method=="INVITE") { addRecordRoute(); };
# forward to current uri now if (!t_relay()) { sl_reply_error(); };
Everything works just fine when I'm calling from UA1 to UA2, i.e. from behind the NAT to the "outside world", however there are some problems when I'm doing it in reverse direction. The problem is that OK INVITE that the UA1 sends in reply to UA2's INVITE when the user picks up the phone is not passed to my helper functions and hence UA2 makes a wrong conclusion about where to send ACK and BYE to.
Do you have any ideas on how to overcome this problem?
-Maxim
On Sun, Jan 12, 2003 at 08:23:37PM +0200, Maxim Sobolev wrote:
On Sun, Jan 12, 2003 at 06:02:35PM +0100, Jan Janak wrote:
Hello,
I will review the patch and let you know.
Thanks! In the meantime, I've located and fixed several bugs in it, so that please don't hurry up applying it to the repository yet.
-Maxim
regards, Jan.
On 11-01 06:50, Maxim Sobolev wrote:
Thank you for the explanation! Attached please find first version of `rport' support for SER (we need it in the core engine), the module will follow probably tomorrow. Please note that the code is quite raw (6:20 am here ;) and I did not perform any seriuos tests yet.
The code does the following:
- Adds support for `rport' Via field parameter into the message
parser;
- Extends parser to allow valueless parameters as requred by
the draft RFC. This will probably requre additional code review, as there may be places in the code where it is assumed that parameter value could not be a NULL pointer;
- Fills in blank rport parameter if present before forwarding
a request or replying to it;
- Changes the code, so that if `rport' is present, then this
port is used for connecting to that UA.
Any comments, corrections or suggestions are highly appreciated!
-Maxim
On Sat, Jan 11, 2003 at 12:04:34AM +0100, Jiri Kuthan wrote:
that's correct.
- Check that _m->somefield is NULL and call parse_headers() with the
appropriate HDR_FOO flag.
yes. you can skip the first check, parse_headers will do it; don't forget to handle errors if parse_headers returns -1 (most likely mem alloc failure)
- Check that _m->somefield is non-NULL and return if false.
yes, that means, that the header field is not in message.
note too that some header fields may occur multiple times; in which case msg->hdr_foo points to the first occurence; if you wish to process all of them, you need to traverse the list msg->headers
example from record-routing (XXX are my extra comments):
int find_first_route(struct sip_msg* _m) { /* XXX don't look at _m->route ... parse_headers will do it for you */ if (parse_headers(_m, HDR_ROUTE, 0) == -1) { /* XXX -1 is some bad error, most likely lack of memory -- leave! */ LOG(L_ERR, "find_first_route(): Error while parsing headers\n"); return -1; } else {
if (_m->route) { return 0; } else { /* XXX not found .... the header field is not there */ DBG("find_first_route(): No Route headers found\n"); return 1; } }
}
- Modify _m->somefield according to the type of that field.
- Using del_lump() mark original version of the header for deletion and
using insert_new_lump() indicate where modified version should be placed before sending a message out.
3 is the same as 4, isn't it. you modify a header field by creating a delete lump refering the old value and an insert lump with the new value. the new value must be created using pkg_malloc. the replace action in textops module is a good example. (The lump lists, sort of "diffs", is processed when later on the message is printed and forwarded.)
-Jiri
--- modules/sl/sl_funcs.c.orig Mon Oct 21 23:30:15 2002 +++ modules/sl/sl_funcs.c Sat Jan 11 06:09:54 2003 @@ -121,7 +121,7 @@ to.sin_family = AF_INET; */
if (reply_to_via) {
if (update_sock_struct_from_via( &(to), msg->via1 )==-1)
{ LOG(L_ERR, "ERROR: sl_send_reply: " "cannot lookup reply dst: %s\n",if (update_sock_struct_from_via( &(to), msg->via1, msg)==-1)
--- modules/tm/t_lookup.c.orig Mon Oct 21 22:21:50 2002 +++ modules/tm/t_lookup.c Sat Jan 11 06:09:54 2003 @@ -717,7 +717,7 @@ } else { via=msg->via1; /*init retrans buffer*/
if (update_sock_struct_from_via( &(rb->to),via )==-1) {
if (update_sock_struct_from_via( &(rb->to),via,msg )==-1) { LOG(L_ERR, "ERROR: init_rb: cannot lookup reply dst: %.*s\n", via->host.len, via->host.s ); ser_error=E_BAD_VIA;
--- parser/msg_parser.h.orig Wed Oct 23 18:12:20 2002 +++ parser/msg_parser.h Sat Jan 11 06:15:11 2003 @@ -118,6 +118,7 @@ char* unparsed; /* here we stopped parsing*/
struct ip_addr src_ip;
unsigned short src_port_no; struct ip_addr dst_ip;
char* orig; /* original message copy */
--- parser/parse_via.c.orig Thu Sep 19 15:23:55 2002 +++ parser/parse_via.c Sat Jan 11 06:09:54 2003 @@ -87,8 +87,10 @@ MADDR1, MADDR2, MADDR3, MADDR4, RECEIVED1, RECEIVED2, RECEIVED3, RECEIVED4, RECEIVED5, RECEIVED6, RECEIVED7,
- RPORT1, RPORT2, RPORT3, /* fin states (227-...)*/ FIN_HIDDEN = 230, FIN_TTL, FIN_BRANCH, FIN_MADDR, FIN_RECEIVED,
- FIN_RPORT, /*GEN_PARAM, PARAM_ERROR*/ /* declared in msg_parser.h*/
}; @@ -125,6 +127,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -160,6 +163,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -200,6 +204,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -231,6 +236,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -267,6 +273,7 @@ case FIN_MADDR: case FIN_TTL: case FIN_RECEIVED:
case FIN_RPORT: LOG(L_ERR, "ERROR: parse_via: invalid char <%c> in" " state %d\n", *tmp, state); goto error;
@@ -296,6 +303,7 @@ case FIN_MADDR: case FIN_TTL: case FIN_RECEIVED:
case FIN_RPORT: LOG(L_ERR, "ERROR: parse_via_param: new via found" "(',') when '=' expected (state %d=)\n", state);
@@ -458,6 +466,9 @@ case TTL1: state=TTL2; break;
case RPORT3:
state=FIN_RPORT;
break; case GEN_PARAM: break; case F_CR:
@@ -545,6 +556,9 @@ case BRANCH1: state=BRANCH2; break;
case RPORT2:
state=RPORT3;
break; case GEN_PARAM: break; case F_CR:
@@ -619,7 +633,36 @@ state=GEN_PARAM; } break;
case 'p':
case 'P':
switch(state){
case RECEIVED1:
state=RPORT1;
break;
case F_CR:
case F_LF:
case F_CRLF:
state=END_OF_HEADER;
goto end_via;
default:
state=GEN_PARAM;
}
break;
case 'o':
case 'O':
switch(state){
case RPORT1:
state=RPORT2;
break;
case F_CR:
case F_LF:
case F_CRLF:
state=END_OF_HEADER;
goto end_via;
default:
state=GEN_PARAM;
}
break; default: switch(state){ case F_PARAM:
@@ -750,6 +793,11 @@ param->value.len=tmp-param->value.s; state=F_PARAM; goto endofvalue;
case F_VALUE:
*tmp=0;
param->value.len=0;
state=F_PARAM;
goto endofvalue; case P_STRING: break; /* what to do? */ case F_LF:
@@ -1681,6 +1729,8 @@ vb->branch=param; else if (param->type==PARAM_RECEIVED) vb->received=param;
else if (param->type==PARAM_RPORT)
vb->rport=param; break; case P_PARAM: break;
--- parser/parse_via.h.orig Mon Oct 21 18:46:27 2002 +++ parser/parse_via.h Sat Jan 11 06:09:54 2003 @@ -36,7 +36,7 @@ */ enum { PARAM_HIDDEN=230, PARAM_TTL, PARAM_BRANCH,
- PARAM_MADDR, PARAM_RECEIVED, GEN_PARAM,
- PARAM_MADDR, PARAM_RECEIVED, PARAM_RPORT, GEN_PARAM, PARAM_ERROR
};
@@ -70,6 +70,7 @@ struct via_param* branch; str tid; /* transaction id, part of branch */ struct via_param* received;
struct via_param* rport;
struct via_body* next; /* pointer to next via body string if compact via or null */
--- config.h.orig Mon Oct 21 22:21:50 2002 +++ config.h Sat Jan 11 06:09:54 2003 @@ -82,6 +82,9 @@ #define RECEIVED ";received=" #define RECEIVED_LEN 10
+#define RPORT ";rport=" +#define RPORT_LEN 7
#define SRV_PREFIX "_sip._udp." #define SRV_PREFIX_LEN 10
@@ -112,6 +115,7 @@ /* forwarding -- Via buffer dimensioning */ #define MAX_VIA_LINE_SIZE 240 #define MAX_RECEIVED_SIZE 57 +#define MAX_RPORT_SIZE 13
/* maximum number of branches per transaction */ #define MAX_BRANCHES 4 --- forward.h.orig Wed Oct 23 18:12:20 2002 +++ forward.h Sat Jan 11 06:09:54 2003 @@ -40,7 +40,7 @@ int check_self(str* host, unsigned short port); int forward_request( struct sip_msg* msg, struct proxy_l* p); int update_sock_struct_from_via( union sockaddr_union* to,
struct via_body* via );
- struct via_body* via, struct sip_msg *msg );
int update_sock_struct_from_ip( union sockaddr_union* to, struct sip_msg *msg ); int forward_reply( struct sip_msg* msg); --- ip_addr.h.orig Mon Nov 4 19:05:32 2002 +++ ip_addr.h Sat Jan 11 06:09:54 2003 @@ -201,6 +201,21 @@ } }
+static inline unsigned int su2port_no(union sockaddr_union* su) +{
- switch(su->s.sa_family){
- case AF_INET:
return ntohs(su->sin.sin_port);
+#ifdef USE_IPV6
- case AF_INET6:
return ntohs(su->sin6.sin6_port);
+#endif
- default:
LOG(L_CRIT,"su2port_no: BUG: unknown address family %d\n",
su->s.sa_family);
- }
- return 0;
+}
/* ip_addr2su -> the same as init_su*/ #define ip_addr2su init_su --- msg_translator.c.orig Mon Oct 21 22:21:50 2002 +++ msg_translator.c Sat Jan 11 06:09:54 2003 @@ -145,9 +145,10 @@ else foo=&(msg->first_line.u.request.uri); print_len=snprintf(buf+fix_len, MAX_WARNING_LEN-fix_len,
"pid=%d req_src_ip=%s in_uri=%.*s out_uri=%.*s via_cnt%c=%d\"",
my_pid(), ip_addr2a(&msg->src_ip),"pid=%d req_src_ip=%s req_src_port=%d in_uri=%.*s out_uri=%.*s via_cnt%c=%d\"",
msg->first_line.u.request.uri.len, msg->first_line.u.request.uri.s, foo->len, foo->s, msg->parsed_flag & HDR_EOH ? '=' : '>', /* should be = */msg->src_port_no,
@@ -208,7 +209,26 @@ return buf; }
+char *rport_builder(struct sip_msg *msg, unsigned int *rport_len) +{
char *buf;
buf=pkg_malloc(sizeof(char)*MAX_RPORT_SIZE);
if (buf==0){
ser_error=E_OUT_OF_MEM;
LOG(L_ERR, "ERROR: rport_builder: out of memory\n");
return 0;
}
/*
* Don't need any sanity checks here, because
* msg->src_port_no is unsigned int, which means
* that it couldn't be more than 5 chars long in
* string representation.
*/
*rport_len = snprintf(buf, MAX_RPORT_SIZE, "%s%u", RPORT, msg->src_port_no);
return buf;
+}
/* computes the "unpacked" len of a lump list, code moved from build_req_from_req */ @@ -395,9 +415,10 @@ unsigned int *returned_len, struct socket_info* send_sock) {
- unsigned int len, new_len, received_len, uri_len, via_len;
- unsigned int len, new_len, received_len, rport_len, uri_len, via_len; char* line_buf; char* received_buf;
- char* rport_buf; char* new_buf; char* orig; char* buf;
@@ -411,8 +432,10 @@ buf=msg->buf; len=msg->len; received_len=0;
rport_len=0; new_buf=0; received_buf=0;
rport_buf=0;
line_buf = via_builder( &via_len, send_sock,
@@ -430,6 +453,11 @@ if ((received_buf=received_builder(msg,&received_len))==0) goto error01; /* free also line_buf */ }
/* check if rport needs to be updated */
if (msg->via1->rport && msg->via1->rport->value.s==0) {
if ((rport_buf=rport_builder(msg,&rport_len))==0)
goto error01; /* free also line_buf */
}
/* add via header to the list */ /* try to add it before msg. 1st via */
@@ -459,6 +487,14 @@ if (insert_new_lump_after(anchor, received_buf, received_len, HDR_VIA) ==0 ) goto error02; /* free also line_buf */ }
/* if rport needs to be updated do it */
if (rport_len){
offset = msg->via1->rport->value.s - buf - 1;
anchor=del_lump(&msg->add_rm, offset, msg->via1->rport->size + 1, HDR_VIA);
if (anchor==0) goto error02; /* free also line_buf */
if (insert_new_lump_after(anchor, rport_buf, rport_len, HDR_VIA)
==0 ) goto error02; /* free also line_buf */
}
/* compute new msg len and fix overlapping zones*/ new_len=len+lumps_len(msg->add_rm);
@@ -507,6 +543,7 @@ pkg_free(line_buf); error02: if (received_buf) pkg_free(received_buf);
- if (rport_buf) pkg_free(rport_buf);
error00: *returned_len=0; return 0; @@ -587,14 +624,18 @@ int i; char backup; char *received_buf;
char *rport_buf; unsigned int received_len;
unsigned int rport_len; char *warning; unsigned int warning_len; int r; str to_tag;
received_buf=0;
rport_buf=0; received_len=0;
rport_len=0; buf=0; /* make -Wall happy */ warning=0;
@@ -621,6 +662,15 @@ goto error00; } }
/* check if rport needs to be updated */
if (msg->via1->rport && msg->via1->rport->value.s==0) {
if ((rport_buf=rport_builder(msg,&rport_len))==0) {
LOG(L_ERR, "ERROR: build_res_buf_from_sip_req: "
"alas, rport_builder failed\n");
goto error00;
}
rport_len -= msg->via1->rport->size;
}
/*computes the lenght of the new response buffer*/ len = 0;
@@ -641,7 +691,7 @@ len+=new_tag_len+5/*";tag="*/; } case HDR_VIA:
if (hdr==msg->h_via1) len += received_len;
if (hdr==msg->h_via1) len += received_len + rport_len; case HDR_FROM: case HDR_CALLID: case HDR_CSEQ:
@@ -716,10 +766,23 @@ break; } case HDR_VIA:
append_str_trans( p, hdr->name.s ,
if (hdr==msg->h_via1 && rport_buf) {
append_str_trans( p, hdr->name.s ,
msg->via1->rport->name.s - hdr->name.s - 1, msg);
append_str( p, rport_buf,
rport_len + msg->via1->rport->size, msg);
append_str_trans( p, msg->via1->rport->name.s +
msg->via1->rport->size,
hdr->body.s + hdr->body.len -
msg->via1->rport->name.s -
msg->via1->rport->size, msg);
} else {
append_str_trans( p, hdr->name.s , ((hdr->body.s+hdr->body.len )-hdr->name.s ),msg);
} if (hdr==msg->h_via1 && received_buf) append_str( p, received_buf, received_len, msg);
append_str( p, CRLF,CRLF_LEN,msg); break; case HDR_FROM:
@@ -763,10 +826,12 @@ needs to be deleted here */ if (received_buf) pkg_free(received_buf);
- if (rport_buf) pkg_free(rport_buf); return buf;
error01: if (received_buf) pkg_free(received_buf);
- if (rport_buf) pkg_free(rport_buf);
error00: *returned_len=0; return 0; --- receive.c.orig Thu Oct 3 23:06:10 2002 +++ receive.c Sat Jan 11 06:09:54 2003 @@ -77,6 +77,7 @@ useful as most of the work is done with scrath-pad; -jiri */ buf[len]=0; su2ip_addr(&msg->src_ip, src_su);
- msg->src_port_no=su2port_no(src_su); msg->dst_ip=bind_address->address; /* won't work if listening on 0.0.0.0 */ msg->id=msg_no; /* make a copy of the message */
--- forward.c.orig Thu Oct 24 17:21:08 2002 +++ forward.c Sat Jan 11 06:21:11 2003 @@ -240,14 +240,21 @@ int update_sock_struct_from_ip( union sockaddr_union* to, struct sip_msg *msg ) {
- unsigned short port_no;
- init_su(to, &msg->src_ip,
(msg->via1->port)?htons(msg->via1->port): htons(SIP_PORT) );
- if (msg->src_port_no)
port_no = msg->src_port_no;
- else if (msg->via1->port)
port_no = msg->via1->port;
- else
port_no = SIP_PORT;
- init_su(to, &msg->src_ip, htons(port_no)); return 1;
}
int update_sock_struct_from_via( union sockaddr_union* to,
struct via_body* via )
- struct via_body* via, struct sip_msg *msg )
{ struct hostent* he; char *host_copy; @@ -266,6 +273,11 @@ name=&(via->host); port=via->port; }
- if (via->rport && via->rport->value.s) {
port=atoi(via->rport->value.s);
- } else if (via->rport && via->rport->value.s==0 && !via->received) {
return update_sock_struct_from_ip( to, msg);
- } /* we do now a malloc/memcpy because gethostbyname loves \0-terminated strings; -jiri but only if host is not null terminated
@@ -353,7 +365,7 @@ goto error; }
- if (update_sock_struct_from_via( to, msg->via2 )==-1) goto error;
- if (update_sock_struct_from_via( to, msg->via2, msg)==-1) goto error; send_sock=get_send_socket(to); if (send_sock==0){ LOG(L_ERR, "forward_reply: ERROR: no sending socket found\n");
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
On 12-01 23:25, Maxim Sobolev wrote:
Everything works just fine when I'm calling from UA1 to UA2, i.e. from behind the NAT to the "outside world", however there are some problems when I'm doing it in reverse direction. The problem is that OK INVITE that the UA1 sends in reply to UA2's INVITE when the user picks up the phone is not passed to my helper functions and hence UA2 makes a wrong conclusion about where to send ACK and BYE to.
Do you have any ideas on how to overcome this problem?
If I get you right, you need to rewrite Contact in 200 OK response to INVITE comming from the NATed user agent.
You can do this using TM and it's callbacks. TM is transaction module and you can register callbacks with the module. Among others, there is a callback that will be called upon reception of the final response (200 OK in your case). In the callback, you can modify the response and rewrite the contact.
First, you need to include t_hooks.h and tm_load.h from the tm module.
Then, you need to register a callback function. The following code snippet shows how to do it:
static int mod_init( void ) {
load_tm_f load_tm;
/* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT))) { LOG(L_ERR, "radius_acc: mod_init: can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm( &tmb )==-1) return -1;
/* register callback */ if (tmb.rngister_tmcb( TMCB_REPLY, rad_acc_onreply, 0) <= 0) return -1; }
return 0; }
The mod_init function registers rad_acc_onreply function, the function will be called when the tm module receives a response.
The callback follows. Note that radius_acc module (from which the code snippets were taken) registers TMCB_REPLY callback. This type of callback will be called _AFTER_ the response was sent out - that means there is no way how to modify it. Because of that, your module will have to register another type of callback - TMCB_REPLY_IN. Such callback will be called before the reply gets forwarded which means that you can modify it before it will be sent out.
See modules/tm/t_hooks.h file for more details ! There is a description of the callbacks.
/* * Function that gets called on reply * params: struct cell* t The callback structure * struct sip_msg *msg The sip message. * int code The status code * void* param Passed parameter * returns: nothing */ static void rad_acc_onreply( struct cell* t, struct sip_msg *reply, int code, void *param ) {
struct sip_msg *rq;
rq = t->uas.request;
if (t->is_invite && missed_flag && isflagset( rq, missed_flag)==1 && ((code>=400 && code<500) || code>=600)) rad_acc_missed_report( t, reply, code);
/* if acc enabled for flagged transaction, check if flag matches */ if (acc_flag && isflagset( rq, acc_flag )==-1) return; /* early media is reported only if explicitely demanded, other provisional responses are always dropped */ if (code < 200 && ! (early_media && code==183)) return; /* negative transactions reported only if explicitely demanded */ if (!failed_transactions && code >=300) return;
/* anything else, i.e., 2xx, always reported */ radius_log_reply(t, reply);
}
regards, Jan.
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Sunday 12 January 2003 23:44, Jan Janak wrote:
On 12-01 23:25, Maxim Sobolev wrote:
Everything works just fine when I'm calling from UA1 to UA2, i.e. from behind the NAT to the "outside world", however there are some problems when I'm doing it in reverse direction. The problem is that OK INVITE that the UA1 sends in reply to UA2's INVITE when the user picks up the phone is not passed to my helper functions and hence UA2 makes a wrong conclusion about where to send ACK and BYE to.
Do you have any ideas on how to overcome this problem?
If I get you right, you need to rewrite Contact in 200 OK response to INVITE comming from the NATed user agent.
You can do this using TM and it's callbacks. TM is transaction module and you can register callbacks with the module. Among others, there is a callback that will be called upon reception of the final response (200 OK in your case). In the callback, you can modify the response and rewrite the contact.
An alternative without additional programing could be to replace your search for the User-Agent (which is not realistic in any case) with a search for Contact from private adresses.
if ((search("^(Contact|m): .*@(192.168.|10.|172.16)")) && ( src_ip==192.168.0.0/16 || src_ip==10.0.0.0/8 || src_ip==172.16.0.0/12 ))) { fix_nated_contact(); }
If you place this where it also catches 200 you should also be able to fix Contacts in the OK.
Greetings Nils - -- gpg-key: http://www.ohlmeier.org/public_key.asc
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
Jan just told me that responses are not processed by the script. Sorry, my fault.
Nils
On Monday 13 January 2003 00:20, Nils Ohlmeier wrote:
On Sunday 12 January 2003 23:44, Jan Janak wrote:
On 12-01 23:25, Maxim Sobolev wrote:
Everything works just fine when I'm calling from UA1 to UA2, i.e. from behind the NAT to the "outside world", however there are some problems when I'm doing it in reverse direction. The problem is that OK INVITE that the UA1 sends in reply to UA2's INVITE when the user picks up the phone is not passed to my helper functions and hence UA2 makes a wrong conclusion about where to send ACK and BYE to.
Do you have any ideas on how to overcome this problem?
If I get you right, you need to rewrite Contact in 200 OK response to INVITE comming from the NATed user agent.
You can do this using TM and it's callbacks. TM is transaction module and you can register callbacks with the module. Among others, there is a callback that will be called upon reception of the final response (200 OK in your case). In the callback, you can modify the response and rewrite the contact.
An alternative without additional programing could be to replace your search for the User-Agent (which is not realistic in any case) with a search for Contact from private adresses.
if ((search("^(Contact|m): .*@(192.168.|10.|172.16)")) && ( src_ip==192.168.0.0/16 || src_ip==10.0.0.0/8 || src_ip==172.16.0.0/12 ))) { fix_nated_contact(); }
If you place this where it also catches 200 you should also be able to fix Contacts in the OK.
Greetings Nils
- -- gpg-key: http://www.ohlmeier.org/public_key.asc
On Sun, Jan 12, 2003 at 11:44:21PM +0100, Jan Janak wrote:
On 12-01 23:25, Maxim Sobolev wrote:
Everything works just fine when I'm calling from UA1 to UA2, i.e. from behind the NAT to the "outside world", however there are some problems when I'm doing it in reverse direction. The problem is that OK INVITE that the UA1 sends in reply to UA2's INVITE when the user picks up the phone is not passed to my helper functions and hence UA2 makes a wrong conclusion about where to send ACK and BYE to.
Do you have any ideas on how to overcome this problem?
If I get you right, you need to rewrite Contact in 200 OK response to INVITE comming from the NATed user agent.
Correct.
You can do this using TM and it's callbacks. TM is transaction module and you can register callbacks with the module. Among others, there is a callback that will be called upon reception of the final response (200 OK in your case). In the callback, you can modify the response and rewrite the contact.
I see, but IMO this would be not a generic solution, as I'll have to adjust the code for my particular needs. What I am looking for is some way to allow it to be modified by properly constructing config file. Perhaps something like t_on_negative() could be added, i.e. t_on_positive(): the specified routing block will be called when the transaction is completed with positive result but before sending final reply. What do you think?
-Maxim
First, you need to include t_hooks.h and tm_load.h from the tm module.
Then, you need to register a callback function. The following code snippet shows how to do it:
static int mod_init( void ) {
load_tm_f load_tm; /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT))) { LOG(L_ERR, "radius_acc: mod_init: can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm( &tmb )==-1) return -1; /* register callback */ if (tmb.rngister_tmcb( TMCB_REPLY, rad_acc_onreply, 0) <= 0) return -1;
}
return 0;
}
The mod_init function registers rad_acc_onreply function, the function will be called when the tm module receives a response.
The callback follows. Note that radius_acc module (from which the code snippets were taken) registers TMCB_REPLY callback. This type of callback will be called _AFTER_ the response was sent out - that means there is no way how to modify it. Because of that, your module will have to register another type of callback - TMCB_REPLY_IN. Such callback will be called before the reply gets forwarded which means that you can modify it before it will be sent out.
See modules/tm/t_hooks.h file for more details ! There is a description of the callbacks.
/*
- Function that gets called on reply
- params: struct cell* t The callback structure
struct sip_msg *msg The sip message.
int code The status code
void* param Passed parameter
- returns: nothing
*/ static void rad_acc_onreply( struct cell* t, struct sip_msg *reply, int code, void *param ) {
struct sip_msg *rq; rq = t->uas.request; if (t->is_invite && missed_flag && isflagset( rq, missed_flag)==1 && ((code>=400 && code<500) || code>=600)) rad_acc_missed_report( t, reply, code); /* if acc enabled for flagged transaction, check if flag matches */ if (acc_flag && isflagset( rq, acc_flag )==-1) return; /* early media is reported only if explicitely demanded, other provisional responses are always dropped */ if (code < 200 && ! (early_media && code==183)) return; /* negative transactions reported only if explicitely demanded */ if (!failed_transactions && code >=300) return; /* anything else, i.e., 2xx, always reported */ radius_log_reply(t, reply);
}
regards, Jan.
On 13-01 14:46, Maxim Sobolev wrote:
I see, but IMO this would be not a generic solution, as I'll have to adjust the code for my particular needs. What I am looking for is some way to allow it to be modified by properly constructing config file. Perhaps something like t_on_negative() could be added, i.e. t_on_positive(): the specified routing block will be called when the transaction is completed with positive result but before sending final reply. What do you think?
Why is the callback not generic solution ?
regards, Jan.
First, you need to include t_hooks.h and tm_load.h from the tm module.
Then, you need to register a callback function. The following code snippet shows how to do it:
static int mod_init( void ) {
load_tm_f load_tm; /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT))) { LOG(L_ERR, "radius_acc: mod_init: can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm( &tmb )==-1) return -1; /* register callback */ if (tmb.rngister_tmcb( TMCB_REPLY, rad_acc_onreply, 0) <= 0) return -1;
}
return 0;
}
The mod_init function registers rad_acc_onreply function, the function will be called when the tm module receives a response.
The callback follows. Note that radius_acc module (from which the code snippets were taken) registers TMCB_REPLY callback. This type of callback will be called _AFTER_ the response was sent out - that means there is no way how to modify it. Because of that, your module will have to register another type of callback - TMCB_REPLY_IN. Such callback will be called before the reply gets forwarded which means that you can modify it before it will be sent out.
See modules/tm/t_hooks.h file for more details ! There is a description of the callbacks.
/*
- Function that gets called on reply
- params: struct cell* t The callback structure
struct sip_msg *msg The sip message.
int code The status code
void* param Passed parameter
- returns: nothing
*/ static void rad_acc_onreply( struct cell* t, struct sip_msg *reply, int code, void *param ) {
struct sip_msg *rq; rq = t->uas.request; if (t->is_invite && missed_flag && isflagset( rq, missed_flag)==1 && ((code>=400 && code<500) || code>=600)) rad_acc_missed_report( t, reply, code); /* if acc enabled for flagged transaction, check if flag matches */ if (acc_flag && isflagset( rq, acc_flag )==-1) return; /* early media is reported only if explicitely demanded, other provisional responses are always dropped */ if (code < 200 && ! (early_media && code==183)) return; /* negative transactions reported only if explicitely demanded */ if (!failed_transactions && code >=300) return; /* anything else, i.e., 2xx, always reported */ radius_log_reply(t, reply);
}
regards, Jan.
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
On Mon, Jan 13, 2003 at 02:38:37PM +0100, Jan Janak wrote:
On 13-01 14:46, Maxim Sobolev wrote:
I see, but IMO this would be not a generic solution, as I'll have to adjust the code for my particular needs. What I am looking for is some way to allow it to be modified by properly constructing config file. Perhaps something like t_on_negative() could be added, i.e. t_on_positive(): the specified routing block will be called when the transaction is completed with positive result but before sending final reply. What do you think?
Why is the callback not generic solution ?
Because as far as I understand then it will be called on each transaction, or I am missing something? What if I need to apply my function only if the reply match some pre-defined conditions (i.e. "Server: ATA.*")?
-Maxim
regards, Jan.
First, you need to include t_hooks.h and tm_load.h from the tm module.
Then, you need to register a callback function. The following code snippet shows how to do it:
static int mod_init( void ) {
load_tm_f load_tm; /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT))) { LOG(L_ERR, "radius_acc: mod_init: can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm( &tmb )==-1) return -1; /* register callback */ if (tmb.rngister_tmcb( TMCB_REPLY, rad_acc_onreply, 0) <= 0) return -1;
}
return 0;
}
The mod_init function registers rad_acc_onreply function, the function will be called when the tm module receives a response.
The callback follows. Note that radius_acc module (from which the code snippets were taken) registers TMCB_REPLY callback. This type of callback will be called _AFTER_ the response was sent out - that means there is no way how to modify it. Because of that, your module will have to register another type of callback - TMCB_REPLY_IN. Such callback will be called before the reply gets forwarded which means that you can modify it before it will be sent out.
See modules/tm/t_hooks.h file for more details ! There is a description of the callbacks.
/*
- Function that gets called on reply
- params: struct cell* t The callback structure
struct sip_msg *msg The sip message.
int code The status code
void* param Passed parameter
- returns: nothing
*/ static void rad_acc_onreply( struct cell* t, struct sip_msg *reply, int code, void *param ) {
struct sip_msg *rq; rq = t->uas.request; if (t->is_invite && missed_flag && isflagset( rq, missed_flag)==1 && ((code>=400 && code<500) || code>=600)) rad_acc_missed_report( t, reply, code); /* if acc enabled for flagged transaction, check if flag matches */ if (acc_flag && isflagset( rq, acc_flag )==-1) return; /* early media is reported only if explicitely demanded, other provisional responses are always dropped */ if (code < 200 && ! (early_media && code==183)) return; /* negative transactions reported only if explicitely demanded */ if (!failed_transactions && code >=300) return; /* anything else, i.e., 2xx, always reported */ radius_log_reply(t, reply);
}
regards, Jan.
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
On 13-01 16:28, Maxim Sobolev wrote:
Because as far as I understand then it will be called on each transaction, or I am missing something? What if I need to apply my function only if the reply match some pre-defined conditions (i.e. "Server: ATA.*")?
You can write the same test conditions in the function but I admit that this is not very flexible. One could write so called reply_route statements in the config file, look at ser-0.8.10/etc/iptel.cfg for more details. The reply_route statement is similar to route statement but it will be called for replies only. I remember Jiri telling me that there are some restrictions on what you can put in the reply_route statement, but I do not recall details. I CCed Jiri, he his travelling this week but I am sure he will give you a correct answer asap.
regards, Jan.
regards, Jan.
First, you need to include t_hooks.h and tm_load.h from the tm module.
Then, you need to register a callback function. The following code snippet shows how to do it:
static int mod_init( void ) {
load_tm_f load_tm; /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT))) { LOG(L_ERR, "radius_acc: mod_init: can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm( &tmb )==-1) return -1; /* register callback */ if (tmb.rngister_tmcb( TMCB_REPLY, rad_acc_onreply, 0) <= 0) return -1;
}
return 0;
}
The mod_init function registers rad_acc_onreply function, the function will be called when the tm module receives a response.
The callback follows. Note that radius_acc module (from which the code snippets were taken) registers TMCB_REPLY callback. This type of callback will be called _AFTER_ the response was sent out - that means there is no way how to modify it. Because of that, your module will have to register another type of callback - TMCB_REPLY_IN. Such callback will be called before the reply gets forwarded which means that you can modify it before it will be sent out.
See modules/tm/t_hooks.h file for more details ! There is a description of the callbacks.
/*
- Function that gets called on reply
- params: struct cell* t The callback structure
struct sip_msg *msg The sip message.
int code The status code
void* param Passed parameter
- returns: nothing
*/ static void rad_acc_onreply( struct cell* t, struct sip_msg *reply, int code, void *param ) {
struct sip_msg *rq; rq = t->uas.request; if (t->is_invite && missed_flag && isflagset( rq, missed_flag)==1 && ((code>=400 && code<500) || code>=600)) rad_acc_missed_report( t, reply, code); /* if acc enabled for flagged transaction, check if flag matches */ if (acc_flag && isflagset( rq, acc_flag )==-1) return; /* early media is reported only if explicitely demanded, other provisional responses are always dropped */ if (code < 200 && ! (early_media && code==183)) return; /* negative transactions reported only if explicitely demanded */ if (!failed_transactions && code >=300) return; /* anything else, i.e., 2xx, always reported */ radius_log_reply(t, reply);
}
regards, Jan.
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Maxim Sobolev wrote:
On Mon, Jan 13, 2003 at 02:38:37PM +0100, Jan Janak wrote:
On 13-01 14:46, Maxim Sobolev wrote:
I see, but IMO this would be not a generic solution, as I'll have to adjust the code for my particular needs. What I am looking for is some way to allow it to be modified by properly constructing config file. Perhaps something like t_on_negative() could be added, i.e. t_on_positive(): the specified routing block will be called when the transaction is completed with positive result but before sending final reply. What do you think?
Why is the callback not generic solution ?
Because as far as I understand then it will be called on each transaction, or I am missing something? What if I need to apply my function only if the reply match some pre-defined conditions (i.e. "Server: ATA.*")?
Attached please find the patch, which implements t_on_positive() approach. Now my config looks like the following and everything now works as expected:
route { if (search("User-Agent: Cisco ATA.*")) { add_rport(); fix_nated_contact(); }; rewriteFromRoute();
[REGISTRATION STUFF STRIPPED]
if (method=="INVITE") { addRecordRoute(); };
t_on_positive("1") # forward to current uri now if (!t_relay()) { sl_reply_error(); }; }
reply_route[1] { if (search("Server: Cisco ATA.*") { fix_nated_contact(); } }
Please let me know what do you think about my patches.
Thank you for your help!
-Maxim
regards, Jan.
First, you need to include t_hooks.h and tm_load.h from the tm module.
Then, you need to register a callback function. The following code snippet shows how to do it:
static int mod_init( void ) {
load_tm_f load_tm; /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT))) { LOG(L_ERR, "radius_acc: mod_init: can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm( &tmb )==-1) return -1; /* register callback */ if (tmb.rngister_tmcb( TMCB_REPLY, rad_acc_onreply, 0) <= 0) return -1;
}
return 0;
}
The mod_init function registers rad_acc_onreply function, the function will be called when the tm module receives a response.
The callback follows. Note that radius_acc module (from which the code snippets were taken) registers TMCB_REPLY callback. This type of callback will be called _AFTER_ the response was sent out - that means there is no way how to modify it. Because of that, your module will have to register another type of callback - TMCB_REPLY_IN. Such callback will be called before the reply gets forwarded which means that you can modify it before it will be sent out.
See modules/tm/t_hooks.h file for more details ! There is a description of the callbacks.
/*
- Function that gets called on reply
- params: struct cell* t The callback structure
struct sip_msg *msg The sip message.
int code The status code
void* param Passed parameter
- returns: nothing
*/ static void rad_acc_onreply( struct cell* t, struct sip_msg *reply, int code, void *param ) {
struct sip_msg *rq; rq = t->uas.request; if (t->is_invite && missed_flag && isflagset( rq, missed_flag)==1 && ((code>=400 && code<500) || code>=600)) rad_acc_missed_report( t, reply, code); /* if acc enabled for flagged transaction, check if flag matches */ if (acc_flag && isflagset( rq, acc_flag )==-1) return; /* early media is reported only if explicitely demanded, other provisional responses are always dropped */ if (code < 200 && ! (early_media && code==183)) return; /* negative transactions reported only if explicitely demanded */ if (!failed_transactions && code >=300) return; /* anything else, i.e., 2xx, always reported */ radius_log_reply(t, reply);
}
regards, Jan.
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
--- modules/tm/h_table.h 2003/01/13 18:36:18 1.1 +++ modules/tm/h_table.h 2003/01/13 18:36:55 @@ -202,6 +202,8 @@
/* the route to take if no final positive reply arrived */ unsigned int on_negative; + /* the route to take if final positive reply arrived */ + unsigned int on_positive; /* set to one if you want to disallow silent transaction dropping when C timer hits */ --- modules/tm/t_fwd.c 2003/01/13 18:35:29 1.1 +++ modules/tm/t_fwd.c 2003/01/13 18:36:09 @@ -415,6 +415,8 @@
/* if someone set on_negative, store in in T-context */ t->on_negative=get_on_negative(); + /* if someone set on_positive, store in in T-context */ + t->on_positive=get_on_positive();
/* send them out now */ for (i=first_branch; i<t->nr_of_outgoings; i++) { --- modules/tm/t_reply.c.orig Tue Oct 22 00:24:43 2002 +++ modules/tm/t_reply.c Mon Jan 13 22:36:44 2003 @@ -56,6 +56,8 @@
/* where to go if there is no positive reply */ static int goto_on_negative=0; +/* where to go if is a positive reply */ +static int goto_on_positive=0;
/* we store the reply_route # in private memory which is then processed during t_relay; we cannot set this value @@ -73,12 +75,23 @@ return 1; }
+int t_on_positive( unsigned int go_to ) +{ + goto_on_positive=go_to; + return 1; +} +
unsigned int get_on_negative() { return goto_on_negative; }
+unsigned int get_on_positive() +{ + return goto_on_positive; +} + void tm_init_tags() { init_tags(tm_tags, &tm_tag_suffix, @@ -916,4 +929,26 @@ if (faked_msg.new_uri.s) pkg_free(faked_msg.new_uri.s); }
+void on_positive_reply( struct cell* t, struct sip_msg* msg, + int code, void *param ) +{ + int act_ret; + struct lump* tmp;
+ /* nobody cares about a positive transaction -- ok, return */ + if (!t->on_positive) { + DBG("DBG: on_positive_reply: no on_positive\n"); + return; + } + + tmp = msg->add_rm; + msg->add_rm = msg->repl_add_rm; + act_ret=run_actions(reply_rlist[t->on_positive], msg); + msg->repl_add_rm = msg->add_rm; + msg->add_rm = tmp; + if (act_ret<0) { + LOG(L_ERR, "on_positive_reply: Error in do_action\n"); + } + + return; +} --- modules/tm/t_reply.h 2003/01/13 18:40:07 1.1 +++ modules/tm/t_reply.h 2003/01/13 18:41:53 @@ -101,11 +101,16 @@ void on_negative_reply( struct cell* t, struct sip_msg* msg, int code, void *param );
+void on_positive_reply( struct cell* t, struct sip_msg* msg, + int code, void *param ); + /* set which 'reply' structure to take if only negative replies arrive */ int t_on_negative( unsigned int go_to ); unsigned int get_on_negative(); +int t_on_positive( unsigned int go_to ); +unsigned int get_on_positive();
int t_retransmit_reply( struct cell *t );
--- modules/tm/tm.c 2003/01/13 18:15:44 1.1 +++ modules/tm/tm.c 2003/01/13 18:43:19 @@ -95,6 +95,7 @@ inline static int w_t_forward_nonack(struct sip_msg* msg, char* str, char* ); inline static int fixup_hostport2proxy(void** param, int param_no); inline static int w_t_on_negative( struct sip_msg* msg, char *go_to, char *foo ); +inline static int w_t_on_positive( struct sip_msg* msg, char *go_to, char *foo );
static int mod_init(void); @@ -121,6 +122,7 @@ T_RELAY, T_FORWARD_NONACK, "t_on_negative", + "t_on_positive",
/* not applicable from script ... */
@@ -141,6 +143,7 @@ w_t_relay, w_t_forward_nonack, w_t_on_negative, + w_t_on_positive,
(cmd_function) register_tmcb, (cmd_function) t_uac, @@ -159,6 +162,7 @@ 0, /* t_relay */ 2, /* t_forward_nonack */ 1, /* t_on_negative */ + 1, /* t_on_positive */ NO_SCRIPT /* register_tmcb */, NO_SCRIPT /* t_uac */, NO_SCRIPT /* load_tm */, @@ -176,13 +180,14 @@ 0, /* t_relay */ fixup_hostport2proxy, /* t_forward_nonack */ fixup_str2int, /* t_on_negative */ + fixup_str2int, /* t_on_positive */ 0, /* register_tmcb */ 0, /* t_uac */ 0, /* load_tm */ 0 /* t_newdlg */ }, - 15, + 16,
/* ------------ exported variables ---------- */ (char *[]) { /* Module parameter names */ @@ -266,6 +271,7 @@ message's t_on_negative value */ t_on_negative( 0 ); + t_on_positive( 0 );
return 1; } @@ -332,6 +338,8 @@ } register_tmcb( TMCB_ON_NEGATIVE, on_negative_reply, 0 /* empty param */); + register_tmcb( TMCB_REPLY_IN, on_positive_reply, + 0 /* empty param */); /* register post-script clean-up function */ register_script_cb( w_t_unref, POST_SCRIPT_CB, 0 /* empty param */ ); @@ -533,6 +541,11 @@ inline static int w_t_on_negative( struct sip_msg* msg, char *go_to, char *foo ) { return t_on_negative( (unsigned int ) go_to ); +} + +inline static int w_t_on_positive( struct sip_msg* msg, char *go_to, char *foo ) +{ + return t_on_positive( (unsigned int ) go_to ); }
inline static int w_t_relay_to( struct sip_msg *p_msg ,
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
Hello,
Jiri is the author of tm so he will process the patch. Thanks a lot !
regards, Jan.
On 13-01 22:53, Maxim Sobolev wrote:
Maxim Sobolev wrote:
On Mon, Jan 13, 2003 at 02:38:37PM +0100, Jan Janak wrote:
On 13-01 14:46, Maxim Sobolev wrote:
I see, but IMO this would be not a generic solution, as I'll have to adjust the code for my particular needs. What I am looking for is some way to allow it to be modified by properly constructing config file. Perhaps something like t_on_negative() could be added, i.e. t_on_positive(): the specified routing block will be called when the transaction is completed with positive result but before sending final reply. What do you think?
Why is the callback not generic solution ?
Because as far as I understand then it will be called on each transaction, or I am missing something? What if I need to apply my function only if the reply match some pre-defined conditions (i.e. "Server: ATA.*")?
Attached please find the patch, which implements t_on_positive() approach. Now my config looks like the following and everything now works as expected:
route { if (search("User-Agent: Cisco ATA.*")) { add_rport(); fix_nated_contact(); }; rewriteFromRoute();
[REGISTRATION STUFF STRIPPED] if (method=="INVITE") { addRecordRoute(); };
t_on_positive("1") # forward to current uri now if (!t_relay()) { sl_reply_error(); }; }
reply_route[1] { if (search("Server: Cisco ATA.*") { fix_nated_contact(); } }
Please let me know what do you think about my patches.
Thank you for your help!
-Maxim
regards, Jan.
First, you need to include t_hooks.h and tm_load.h from the tm module.
Then, you need to register a callback function. The following code snippet shows how to do it:
static int mod_init( void ) {
load_tm_f load_tm; /* import the TM auto-loading function */ if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT))) { LOG(L_ERR, "radius_acc: mod_init: can't import load_tm\n"); return -1; } /* let the auto-loading function load all TM stuff */ if (load_tm( &tmb )==-1) return -1; /* register callback */ if (tmb.rngister_tmcb( TMCB_REPLY, rad_acc_onreply, 0) <= 0) return -1;
}
return 0;
}
The mod_init function registers rad_acc_onreply function, the function will be called when the tm module receives a response.
The callback follows. Note that radius_acc module (from which the code snippets were taken) registers TMCB_REPLY callback. This type of callback will be called _AFTER_ the response was sent out - that means there is no way how to modify it. Because of that, your module will have to register another type of callback - TMCB_REPLY_IN. Such callback will be called before the reply gets forwarded which means that you can modify it before it will be sent out.
See modules/tm/t_hooks.h file for more details ! There is a description of the callbacks.
/*
- Function that gets called on reply
- params: struct cell* t The callback structure
struct sip_msg *msg The sip message.
int code The status code
void* param Passed parameter
- returns: nothing
*/ static void rad_acc_onreply( struct cell* t, struct sip_msg *reply, int code, void *param ) {
struct sip_msg *rq; rq = t->uas.request; if (t->is_invite && missed_flag && isflagset( rq, missed_flag)==1 && ((code>=400 && code<500) || code>=600)) rad_acc_missed_report( t, reply, code); /* if acc enabled for flagged transaction, check if flag matches */ if (acc_flag && isflagset( rq, acc_flag )==-1) return; /* early media is reported only if explicitely demanded, other provisional responses are always dropped */ if (code < 200 && ! (early_media && code==183)) return; /* negative transactions reported only if explicitely demanded */ if (!failed_transactions && code >=300) return; /* anything else, i.e., 2xx, always reported */ radius_log_reply(t, reply);
}
regards, Jan.
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
--- modules/tm/h_table.h 2003/01/13 18:36:18 1.1 +++ modules/tm/h_table.h 2003/01/13 18:36:55 @@ -202,6 +202,8 @@
/* the route to take if no final positive reply arrived */ unsigned int on_negative;
- /* the route to take if final positive reply arrived */
- unsigned int on_positive; /* set to one if you want to disallow silent transaction dropping when C timer hits */
--- modules/tm/t_fwd.c 2003/01/13 18:35:29 1.1 +++ modules/tm/t_fwd.c 2003/01/13 18:36:09 @@ -415,6 +415,8 @@
/* if someone set on_negative, store in in T-context */ t->on_negative=get_on_negative();
/* if someone set on_positive, store in in T-context */
t->on_positive=get_on_positive();
/* send them out now */ for (i=first_branch; i<t->nr_of_outgoings; i++) {
--- modules/tm/t_reply.c.orig Tue Oct 22 00:24:43 2002 +++ modules/tm/t_reply.c Mon Jan 13 22:36:44 2003 @@ -56,6 +56,8 @@
/* where to go if there is no positive reply */ static int goto_on_negative=0; +/* where to go if is a positive reply */ +static int goto_on_positive=0;
/* we store the reply_route # in private memory which is then processed during t_relay; we cannot set this value @@ -73,12 +75,23 @@ return 1; }
+int t_on_positive( unsigned int go_to ) +{
- goto_on_positive=go_to;
- return 1;
+}
unsigned int get_on_negative() { return goto_on_negative; }
+unsigned int get_on_positive() +{
- return goto_on_positive;
+}
void tm_init_tags() { init_tags(tm_tags, &tm_tag_suffix, @@ -916,4 +929,26 @@ if (faked_msg.new_uri.s) pkg_free(faked_msg.new_uri.s); }
+void on_positive_reply( struct cell* t, struct sip_msg* msg,
- int code, void *param )
+{
int act_ret;
struct lump* tmp;
/* nobody cares about a positive transaction -- ok, return */
if (!t->on_positive) {
DBG("DBG: on_positive_reply: no on_positive\n");
return;
}
tmp = msg->add_rm;
msg->add_rm = msg->repl_add_rm;
act_ret=run_actions(reply_rlist[t->on_positive], msg);
msg->repl_add_rm = msg->add_rm;
msg->add_rm = tmp;
if (act_ret<0) {
LOG(L_ERR, "on_positive_reply: Error in do_action\n");
}
return;
+} --- modules/tm/t_reply.h 2003/01/13 18:40:07 1.1 +++ modules/tm/t_reply.h 2003/01/13 18:41:53 @@ -101,11 +101,16 @@ void on_negative_reply( struct cell* t, struct sip_msg* msg, int code, void *param );
+void on_positive_reply( struct cell* t, struct sip_msg* msg,
- int code, void *param );
/* set which 'reply' structure to take if only negative replies arrive */ int t_on_negative( unsigned int go_to ); unsigned int get_on_negative(); +int t_on_positive( unsigned int go_to ); +unsigned int get_on_positive();
int t_retransmit_reply( struct cell *t );
--- modules/tm/tm.c 2003/01/13 18:15:44 1.1 +++ modules/tm/tm.c 2003/01/13 18:43:19 @@ -95,6 +95,7 @@ inline static int w_t_forward_nonack(struct sip_msg* msg, char* str, char* ); inline static int fixup_hostport2proxy(void** param, int param_no); inline static int w_t_on_negative( struct sip_msg* msg, char *go_to, char *foo ); +inline static int w_t_on_positive( struct sip_msg* msg, char *go_to, char *foo );
static int mod_init(void); @@ -121,6 +122,7 @@ T_RELAY, T_FORWARD_NONACK, "t_on_negative",
"t_on_positive", /* not applicable from script ... */
@@ -141,6 +143,7 @@ w_t_relay, w_t_forward_nonack, w_t_on_negative,
w_t_on_positive, (cmd_function) register_tmcb, (cmd_function) t_uac,
@@ -159,6 +162,7 @@ 0, /* t_relay */ 2, /* t_forward_nonack */ 1, /* t_on_negative */
1, /* t_on_positive */ NO_SCRIPT /* register_tmcb */, NO_SCRIPT /* t_uac */, NO_SCRIPT /* load_tm */,
@@ -176,13 +180,14 @@ 0, /* t_relay */ fixup_hostport2proxy, /* t_forward_nonack */ fixup_str2int, /* t_on_negative */
fixup_str2int, /* t_on_positive */ 0, /* register_tmcb */ 0, /* t_uac */ 0, /* load_tm */ 0 /* t_newdlg */
},
- 15,
16,
/* ------------ exported variables ---------- */ (char *[]) { /* Module parameter names */
@@ -266,6 +271,7 @@ message's t_on_negative value */ t_on_negative( 0 );
t_on_positive( 0 );
return 1;
} @@ -332,6 +338,8 @@ } register_tmcb( TMCB_ON_NEGATIVE, on_negative_reply, 0 /* empty param */);
- register_tmcb( TMCB_REPLY_IN, on_positive_reply,
/* register post-script clean-up function */ register_script_cb( w_t_unref, POST_SCRIPT_CB, 0 /* empty param */ );0 /* empty param */);
@@ -533,6 +541,11 @@ inline static int w_t_on_negative( struct sip_msg* msg, char *go_to, char *foo ) { return t_on_negative( (unsigned int ) go_to ); +}
+inline static int w_t_on_positive( struct sip_msg* msg, char *go_to, char *foo ) +{
- return t_on_positive( (unsigned int ) go_to );
}
inline static int w_t_relay_to( struct sip_msg *p_msg ,
At 01:46 PM 1/13/2003, Maxim Sobolev wrote:
You can do this using TM and it's callbacks. TM is transaction module and you can register callbacks with the module. Among others, there is a callback that will be called upon reception of the final response (200 OK in your case). In the callback, you can modify the response and rewrite the contact.
I see, but IMO this would be not a generic solution, as I'll have to adjust the code for my particular needs. What I am looking for is some way to allow it to be modified by properly constructing config file. Perhaps something like t_on_negative() could be added, i.e. t_on_positive(): the specified routing block will be called when the transaction is completed with positive result but before sending final reply. What do you think?
I'm not sure what you mean by adjusting to your particular needs. If that means that you want to process only "some" replies belonging to some particular transactions, you may want to do the following things: - use TM, as you need to correlate replies with requests - use setflag during request processing to label transactions which shell get a specific handling - register a callback, from which you apply the special handling to replies belonging to requests, that were previously marked
That's how accounting module works, for example. Interesting transactions are labeled, and if a reply arrives, it is processed by the accounting logic only if the label was set.
-Jiri
Jiri Kuthan wrote:
At 01:46 PM 1/13/2003, Maxim Sobolev wrote:
You can do this using TM and it's callbacks. TM is transaction module and you can register callbacks with the module. Among others, there is a callback that will be called upon reception of the final response (200 OK in your case). In the callback, you can modify the response and rewrite the contact.
I see, but IMO this would be not a generic solution, as I'll have to adjust the code for my particular needs. What I am looking for is some way to allow it to be modified by properly constructing config file. Perhaps something like t_on_negative() could be added, i.e. t_on_positive(): the specified routing block will be called when the transaction is completed with positive result but before sending final reply. What do you think?
I'm not sure what you mean by adjusting to your particular needs. If that means that you want to process only "some" replies belonging to some particular transactions, you may want to do the following things:
- use TM, as you need to correlate replies with requests
- use setflag during request processing to label transactions which shell get a specific handling
- register a callback, from which you apply the special handling to replies belonging to requests, that were previously marked
That's how accounting module works, for example. Interesting transactions are labeled, and if a reply arrives, it is processed by the accounting logic only if the label was set.
This is fine, but not sufficient, since I need to do a search() on replies as well to see if the answering side of transaction supports symmetric signalling, even if the originating one doesn't. It is necessary for determining if it is safe to add rport and rewrite Contact (adding rport or rewriting contact for UA that doesn't support symmetric signalling will break things even if that UA is on a public IP). Therefore, one way or another I need another routing block for processing replies conditionally. Labelling will not help much, because transaction originator, for example, may not support symmetric signalling, i.e. somebody calls from softphone with real IP to my nated ata. In this case transaction will not be labelled and reply of my ata will not be adjusted as it have to.
-Maxim
There are two design options how to rewrite replies too. I think the easieast one is to create a reply processing function in your module and rewrite replies from it. (Look how for example TM module registers a reply processing function.)
The other way around would be to hook yourself to transaction management and do the rewrite from a TM callback.
I like the first method better, because it does not require stateful processing.
-Jiri
At 10:25 PM 1/12/2003, Maxim Sobolev wrote:
I've almost done with add_rport() and fix_nated_contact(), it sorta works, but I've bumped to a problem, I have no good solution for. My setup is as follows:
private public [UA1]---[NAT]---[SER]---[UA2]
UA1 is Cisco ATA186 UA2 is eStara SoftPHONE
Since eStara doesn't support symmetric signalling, my config looks like the following:
if (search("User-Agent: Cisco ATA.*") || search("Server: Cisco ATA.*")) { add_rport(); fix_nated_contact(); }; rewriteFromRoute(); [REGISTRATION STUFF STRIPPED] if (method=="INVITE") { addRecordRoute(); }; # forward to current uri now if (!t_relay()) { sl_reply_error(); };
Everything works just fine when I'm calling from UA1 to UA2, i.e. from behind the NAT to the "outside world", however there are some problems when I'm doing it in reverse direction. The problem is that OK INVITE that the UA1 sends in reply to UA2's INVITE when the user picks up the phone is not passed to my helper functions and hence UA2 makes a wrong conclusion about where to send ACK and BYE to.
Do you have any ideas on how to overcome this problem?
-Maxim
On Sun, Jan 12, 2003 at 08:23:37PM +0200, Maxim Sobolev wrote:
On Sun, Jan 12, 2003 at 06:02:35PM +0100, Jan Janak wrote:
Hello,
I will review the patch and let you know.
Thanks! In the meantime, I've located and fixed several bugs in it, so that please don't hurry up applying it to the repository yet.
-Maxim
regards, Jan.
On 11-01 06:50, Maxim Sobolev wrote:
Thank you for the explanation! Attached please find first version of `rport' support for SER (we need it in the core engine), the module will follow probably tomorrow. Please note that the code is quite raw (6:20 am here ;) and I did not perform any seriuos tests yet.
The code does the following:
- Adds support for `rport' Via field parameter into the message
parser;
- Extends parser to allow valueless parameters as requred by
the draft RFC. This will probably requre additional code review, as there may be places in the code where it is assumed that parameter value could not be a NULL pointer;
- Fills in blank rport parameter if present before forwarding
a request or replying to it;
- Changes the code, so that if `rport' is present, then this
port is used for connecting to that UA.
Any comments, corrections or suggestions are highly appreciated!
-Maxim
On Sat, Jan 11, 2003 at 12:04:34AM +0100, Jiri Kuthan wrote:
that's correct.
- Check that _m->somefield is NULL and call parse_headers() with the
appropriate HDR_FOO flag.
yes. you can skip the first check, parse_headers will do it; don't forget to handle errors if parse_headers returns -1 (most likely mem alloc failure)
- Check that _m->somefield is non-NULL and return if false.
yes, that means, that the header field is not in message.
note too that some header fields may occur multiple times; in which case msg->hdr_foo points to the first occurence; if you wish to process all of them, you need to traverse the list msg->headers
example from record-routing (XXX are my extra comments):
int find_first_route(struct sip_msg* _m) { /* XXX don't look at _m->route ... parse_headers will do it for you */ if (parse_headers(_m, HDR_ROUTE, 0) == -1) { /* XXX -1 is some bad error, most likely lack of memory -- leave! */ LOG(L_ERR, "find_first_route(): Error while parsing headers\n"); return -1; } else {
if (_m->route) { return 0; } else { /* XXX not found .... the header field is not there */ DBG("find_first_route(): No Route headers found\n"); return 1; } }
}
- Modify _m->somefield according to the type of that field.
- Using del_lump() mark original version of the header for deletion and
using insert_new_lump() indicate where modified version should be placed before sending a message out.
3 is the same as 4, isn't it. you modify a header field by creating a delete lump refering the old value and an insert lump with the new value. the new value must be created using pkg_malloc. the replace action in textops module is a good example. (The lump lists, sort of "diffs", is processed when later on the message is printed and forwarded.)
-Jiri
--- modules/sl/sl_funcs.c.orig Mon Oct 21 23:30:15 2002 +++ modules/sl/sl_funcs.c Sat Jan 11 06:09:54 2003 @@ -121,7 +121,7 @@ to.sin_family = AF_INET; */
if (reply_to_via) {
if (update_sock_struct_from_via( &(to), msg->via1 )==-1)
if (update_sock_struct_from_via( &(to), msg->via1, msg)==-1) { LOG(L_ERR, "ERROR: sl_send_reply: " "cannot lookup reply dst: %s\n",
--- modules/tm/t_lookup.c.orig Mon Oct 21 22:21:50 2002 +++ modules/tm/t_lookup.c Sat Jan 11 06:09:54 2003 @@ -717,7 +717,7 @@ } else { via=msg->via1; /*init retrans buffer*/
if (update_sock_struct_from_via( &(rb->to),via )==-1) {
if (update_sock_struct_from_via( &(rb->to),via,msg )==-1) { LOG(L_ERR, "ERROR: init_rb: cannot lookup reply dst: %.*s\n", via->host.len, via->host.s ); ser_error=E_BAD_VIA;
--- parser/msg_parser.h.orig Wed Oct 23 18:12:20 2002 +++ parser/msg_parser.h Sat Jan 11 06:15:11 2003 @@ -118,6 +118,7 @@ char* unparsed; /* here we stopped parsing*/
struct ip_addr src_ip;
unsigned short src_port_no; struct ip_addr dst_ip;
char* orig; /* original message copy */
--- parser/parse_via.c.orig Thu Sep 19 15:23:55 2002 +++ parser/parse_via.c Sat Jan 11 06:09:54 2003 @@ -87,8 +87,10 @@ MADDR1, MADDR2, MADDR3, MADDR4, RECEIVED1, RECEIVED2, RECEIVED3, RECEIVED4, RECEIVED5, RECEIVED6, RECEIVED7,
- RPORT1, RPORT2, RPORT3, /* fin states (227-...)*/ FIN_HIDDEN = 230, FIN_TTL, FIN_BRANCH, FIN_MADDR, FIN_RECEIVED,
- FIN_RPORT, /*GEN_PARAM, PARAM_ERROR*/ /* declared in msg_parser.h*/
}; @@ -125,6 +127,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -160,6 +163,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -200,6 +204,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -231,6 +236,7 @@ case FIN_TTL: case FIN_MADDR: case FIN_RECEIVED:
case FIN_RPORT: *tmp=0; param->type=state; param->name.len=tmp-param->name.s;
@@ -267,6 +273,7 @@ case FIN_MADDR: case FIN_TTL: case FIN_RECEIVED:
case FIN_RPORT: LOG(L_ERR, "ERROR: parse_via: invalid char <%c> in" " state %d\n", *tmp, state); goto error;
@@ -296,6 +303,7 @@ case FIN_MADDR: case FIN_TTL: case FIN_RECEIVED:
case FIN_RPORT: LOG(L_ERR, "ERROR: parse_via_param: new via found" "(',') when '=' expected (state %d=)\n", state);
@@ -458,6 +466,9 @@ case TTL1: state=TTL2; break;
case RPORT3:
state=FIN_RPORT;
break; case GEN_PARAM: break; case F_CR:
@@ -545,6 +556,9 @@ case BRANCH1: state=BRANCH2; break;
case RPORT2:
state=RPORT3;
break; case GEN_PARAM: break; case F_CR:
@@ -619,7 +633,36 @@ state=GEN_PARAM; } break;
case 'p':
case 'P':
switch(state){
case RECEIVED1:
state=RPORT1;
break;
case F_CR:
case F_LF:
case F_CRLF:
state=END_OF_HEADER;
goto end_via;
default:
state=GEN_PARAM;
}
break;
case 'o':
case 'O':
switch(state){
case RPORT1:
state=RPORT2;
break;
case F_CR:
case F_LF:
case F_CRLF:
state=END_OF_HEADER;
goto end_via;
default:
state=GEN_PARAM;
}
break; default: switch(state){ case F_PARAM:
@@ -750,6 +793,11 @@ param->value.len=tmp-param->value.s; state=F_PARAM; goto endofvalue;
case F_VALUE:
*tmp=0;
param->value.len=0;
state=F_PARAM;
goto endofvalue; case P_STRING: break; /* what to do? */ case F_LF:
@@ -1681,6 +1729,8 @@ vb->branch=param; else if (param->type==PARAM_RECEIVED) vb->received=param;
else if (param->type==PARAM_RPORT)
vb->rport=param; break; case P_PARAM: break;
--- parser/parse_via.h.orig Mon Oct 21 18:46:27 2002 +++ parser/parse_via.h Sat Jan 11 06:09:54 2003 @@ -36,7 +36,7 @@ */ enum { PARAM_HIDDEN=230, PARAM_TTL, PARAM_BRANCH,
- PARAM_MADDR, PARAM_RECEIVED, GEN_PARAM,
- PARAM_MADDR, PARAM_RECEIVED, PARAM_RPORT, GEN_PARAM, PARAM_ERROR
};
@@ -70,6 +70,7 @@ struct via_param* branch; str tid; /* transaction id, part of branch */ struct via_param* received;
struct via_param* rport;
struct via_body* next; /* pointer to next via body string if compact via or null */
--- config.h.orig Mon Oct 21 22:21:50 2002 +++ config.h Sat Jan 11 06:09:54 2003 @@ -82,6 +82,9 @@ #define RECEIVED ";received=" #define RECEIVED_LEN 10
+#define RPORT ";rport=" +#define RPORT_LEN 7
#define SRV_PREFIX "_sip._udp." #define SRV_PREFIX_LEN 10
@@ -112,6 +115,7 @@ /* forwarding -- Via buffer dimensioning */ #define MAX_VIA_LINE_SIZE 240 #define MAX_RECEIVED_SIZE 57 +#define MAX_RPORT_SIZE 13
/* maximum number of branches per transaction */ #define MAX_BRANCHES 4 --- forward.h.orig Wed Oct 23 18:12:20 2002 +++ forward.h Sat Jan 11 06:09:54 2003 @@ -40,7 +40,7 @@ int check_self(str* host, unsigned short port); int forward_request( struct sip_msg* msg, struct proxy_l* p); int update_sock_struct_from_via( union sockaddr_union* to,
struct via_body* via );
- struct via_body* via, struct sip_msg *msg );
int update_sock_struct_from_ip( union sockaddr_union* to, struct sip_msg *msg ); int forward_reply( struct sip_msg* msg); --- ip_addr.h.orig Mon Nov 4 19:05:32 2002 +++ ip_addr.h Sat Jan 11 06:09:54 2003 @@ -201,6 +201,21 @@ } }
+static inline unsigned int su2port_no(union sockaddr_union* su) +{
- switch(su->s.sa_family){
- case AF_INET:
return ntohs(su->sin.sin_port);
+#ifdef USE_IPV6
- case AF_INET6:
return ntohs(su->sin6.sin6_port);
+#endif
- default:
LOG(L_CRIT,"su2port_no: BUG: unknown address family %d\n",
su->s.sa_family);
- }
- return 0;
+}
/* ip_addr2su -> the same as init_su*/ #define ip_addr2su init_su --- msg_translator.c.orig Mon Oct 21 22:21:50 2002 +++ msg_translator.c Sat Jan 11 06:09:54 2003 @@ -145,9 +145,10 @@ else foo=&(msg->first_line.u.request.uri); print_len=snprintf(buf+fix_len, MAX_WARNING_LEN-fix_len,
"pid=%d req_src_ip=%s in_uri=%.*s out_uri=%.*s via_cnt%c=%d\"",
"pid=%d req_src_ip=%s req_src_port=%d in_uri=%.*s out_uri=%.*s via_cnt%c=%d\"", my_pid(), ip_addr2a(&msg->src_ip),
msg->src_port_no, msg->first_line.u.request.uri.len, msg->first_line.u.request.uri.s, foo->len, foo->s, msg->parsed_flag & HDR_EOH ? '=' : '>', /* should be = */
@@ -208,7 +209,26 @@ return buf; }
+char *rport_builder(struct sip_msg *msg, unsigned int *rport_len) +{
char *buf;
buf=pkg_malloc(sizeof(char)*MAX_RPORT_SIZE);
if (buf==0){
ser_error=E_OUT_OF_MEM;
LOG(L_ERR, "ERROR: rport_builder: out of memory\n");
return 0;
}
/*
- Don't need any sanity checks here, because
- msg->src_port_no is unsigned int, which means
- that it couldn't be more than 5 chars long in
- string representation.
*/
*rport_len = snprintf(buf, MAX_RPORT_SIZE, "%s%u", RPORT, msg->src_port_no);
return buf;
+}
/* computes the "unpacked" len of a lump list, code moved from build_req_from_req */ @@ -395,9 +415,10 @@ unsigned int *returned_len, struct socket_info* send_sock) {
- unsigned int len, new_len, received_len, uri_len, via_len;
- unsigned int len, new_len, received_len, rport_len, uri_len, via_len; char* line_buf; char* received_buf;
- char* rport_buf; char* new_buf; char* orig; char* buf;
@@ -411,8 +432,10 @@ buf=msg->buf; len=msg->len; received_len=0;
rport_len=0; new_buf=0; received_buf=0;
rport_buf=0;
line_buf = via_builder( &via_len, send_sock,
@@ -430,6 +453,11 @@ if ((received_buf=received_builder(msg,&received_len))==0) goto error01; /* free also line_buf */ }
/* check if rport needs to be updated */
if (msg->via1->rport && msg->via1->rport->value.s==0) {
if ((rport_buf=rport_builder(msg,&rport_len))==0)
goto error01; /* free also line_buf */
}
/* add via header to the list */ /* try to add it before msg. 1st via */
@@ -459,6 +487,14 @@ if (insert_new_lump_after(anchor, received_buf, received_len, HDR_VIA) ==0 ) goto error02; /* free also line_buf */ }
/* if rport needs to be updated do it */
if (rport_len){
offset = msg->via1->rport->value.s - buf - 1;
anchor=del_lump(&msg->add_rm, offset, msg->via1->rport->size + 1, HDR_VIA);
if (anchor==0) goto error02; /* free also line_buf */
if (insert_new_lump_after(anchor, rport_buf, rport_len, HDR_VIA)
==0 ) goto error02; /* free also line_buf */
}
/* compute new msg len and fix overlapping zones*/ new_len=len+lumps_len(msg->add_rm);
@@ -507,6 +543,7 @@ pkg_free(line_buf); error02: if (received_buf) pkg_free(received_buf);
- if (rport_buf) pkg_free(rport_buf);
error00: *returned_len=0; return 0; @@ -587,14 +624,18 @@ int i; char backup; char *received_buf;
char *rport_buf; unsigned int received_len;
unsigned int rport_len; char *warning; unsigned int warning_len; int r; str to_tag;
received_buf=0;
rport_buf=0; received_len=0;
rport_len=0; buf=0; /* make -Wall happy */ warning=0;
@@ -621,6 +662,15 @@ goto error00; } }
/* check if rport needs to be updated */
if (msg->via1->rport && msg->via1->rport->value.s==0) {
if ((rport_buf=rport_builder(msg,&rport_len))==0) {
LOG(L_ERR, "ERROR: build_res_buf_from_sip_req: "
"alas, rport_builder failed\n");
goto error00;
}
rport_len -= msg->via1->rport->size;
}
/*computes the lenght of the new response buffer*/ len = 0;
@@ -641,7 +691,7 @@ len+=new_tag_len+5/*";tag="*/; } case HDR_VIA:
if (hdr==msg->h_via1) len += received_len;
if (hdr==msg->h_via1) len += received_len + rport_len; case HDR_FROM: case HDR_CALLID: case HDR_CSEQ:
@@ -716,10 +766,23 @@ break; } case HDR_VIA:
append_str_trans( p, hdr->name.s ,
if (hdr==msg->h_via1 && rport_buf) {
append_str_trans( p, hdr->name.s ,
msg->via1->rport->name.s - hdr->name.s - 1, msg);
append_str( p, rport_buf,
rport_len + msg->via1->rport->size, msg);
append_str_trans( p, msg->via1->rport->name.s +
msg->via1->rport->size,
hdr->body.s + hdr->body.len -
msg->via1->rport->name.s -
msg->via1->rport->size, msg);
} else {
append_str_trans( p, hdr->name.s , ((hdr->body.s+hdr->body.len )-hdr->name.s ),msg);
} if (hdr==msg->h_via1 && received_buf) append_str( p, received_buf, received_len, msg);
append_str( p, CRLF,CRLF_LEN,msg); break; case HDR_FROM:
@@ -763,10 +826,12 @@ needs to be deleted here */ if (received_buf) pkg_free(received_buf);
- if (rport_buf) pkg_free(rport_buf); return buf;
error01: if (received_buf) pkg_free(received_buf);
- if (rport_buf) pkg_free(rport_buf);
error00: *returned_len=0; return 0; --- receive.c.orig Thu Oct 3 23:06:10 2002 +++ receive.c Sat Jan 11 06:09:54 2003 @@ -77,6 +77,7 @@ useful as most of the work is done with scrath-pad; -jiri */ buf[len]=0; su2ip_addr(&msg->src_ip, src_su);
- msg->src_port_no=su2port_no(src_su); msg->dst_ip=bind_address->address; /* won't work if listening on 0.0.0.0 */ msg->id=msg_no; /* make a copy of the message */
--- forward.c.orig Thu Oct 24 17:21:08 2002 +++ forward.c Sat Jan 11 06:21:11 2003 @@ -240,14 +240,21 @@ int update_sock_struct_from_ip( union sockaddr_union* to, struct sip_msg *msg ) {
- unsigned short port_no;
- init_su(to, &msg->src_ip,
(msg->via1->port)?htons(msg->via1->port): htons(SIP_PORT) );
- if (msg->src_port_no)
port_no = msg->src_port_no;
- else if (msg->via1->port)
port_no = msg->via1->port;
- else
port_no = SIP_PORT;
- init_su(to, &msg->src_ip, htons(port_no)); return 1;
}
int update_sock_struct_from_via( union sockaddr_union* to,
struct via_body* via )
- struct via_body* via, struct sip_msg *msg )
{ struct hostent* he; char *host_copy; @@ -266,6 +273,11 @@ name=&(via->host); port=via->port; }
- if (via->rport && via->rport->value.s) {
port=atoi(via->rport->value.s);
- } else if (via->rport && via->rport->value.s==0 && !via->received) {
return update_sock_struct_from_ip( to, msg);
- } /* we do now a malloc/memcpy because gethostbyname loves \0-terminated strings; -jiri but only if host is not null terminated
@@ -353,7 +365,7 @@ goto error; }
- if (update_sock_struct_from_via( to, msg->via2 )==-1) goto error;
- if (update_sock_struct_from_via( to, msg->via2, msg)==-1) goto error; send_sock=get_send_socket(to); if (send_sock==0){ LOG(L_ERR, "forward_reply: ERROR: no sending socket found\n");
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
Hello,
I was looking for a NAT traversal solution for both SIP messages and RTP, but it looks like this modification will only handle SIP messages, am I rigth? If so, is there any solution for UAs registered behind a NAT?
Thanks, Claudio
----- Original Message ----- From: "Maxim Sobolev" sobomax@freebsd.org To: "Jiri Kuthan" jiri@iptel.org Cc: serusers@lists.iptel.org; kapitan@portaone.com Sent: Saturday, January 11, 2003 2:50 AM Subject: Re: [Serusers] Rewriting URI in the Contact field
Thank you for the explanation! Attached please find first version of `rport' support for SER (we need it in the core engine), the module will follow probably tomorrow. Please note that the code is quite raw (6:20 am here ;) and I did not perform any seriuos tests yet.
The code does the following:
- Adds support for `rport' Via field parameter into the message
parser;
- Extends parser to allow valueless parameters as requred by
the draft RFC. This will probably requre additional code review, as there may be places in the code where it is assumed that parameter value could not be a NULL pointer;
- Fills in blank rport parameter if present before forwarding
a request or replying to it;
- Changes the code, so that if `rport' is present, then this
port is used for connecting to that UA.
Any comments, corrections or suggestions are highly appreciated!
-Maxim
On Sat, Jan 11, 2003 at 12:04:34AM +0100, Jiri Kuthan wrote:
that's correct.
- Check that _m->somefield is NULL and call parse_headers() with the
appropriate HDR_FOO flag.
yes. you can skip the first check, parse_headers will do it; don't forget to handle errors if parse_headers returns -1 (most likely mem alloc failure)
- Check that _m->somefield is non-NULL and return if false.
yes, that means, that the header field is not in message.
note too that some header fields may occur multiple times; in which
case
msg->hdr_foo points to the first occurence; if you wish to process all of them, you need to traverse the list msg->headers
example from record-routing (XXX are my extra comments):
int find_first_route(struct sip_msg* _m) { /* XXX don't look at _m->route ... parse_headers will do it for you
*/
if (parse_headers(_m, HDR_ROUTE, 0) == -1) { /* XXX -1 is some bad error, most likely lack of memory --
leave! */
LOG(L_ERR, "find_first_route(): Error while parsing headers\n"); return -1; } else { if (_m->route) { return 0; } else { /* XXX not found .... the header field is not there */ DBG("find_first_route(): No Route headers found\n"); return 1; } }
}
- Modify _m->somefield according to the type of that field.
- Using del_lump() mark original version of the header for deletion
and
using insert_new_lump() indicate where modified version should be
placed
before sending a message out.
3 is the same as 4, isn't it. you modify a header field by creating a delete lump refering the old value and an insert lump with the new value. the new value must be created using pkg_malloc. the replace action in textops module is a good example. (The lump lists, sort of "diffs", is processed when later on the message is printed and
forwarded.)
-Jiri
On Jan 11, 2003 at 06:50, Maxim Sobolev sobomax@FreeBSD.org wrote:
Thank you for the explanation! Attached please find first version of `rport' support for SER (we need it in the core engine), the module will follow probably tomorrow. Please note that the code is quite raw (6:20 am here ;) and I did not perform any seriuos tests yet.
Thanks for the patch. I applied a heavily modified version of it on CVS. Your initial patch did not properly handle all the possible ways of writing rport (rport, rport= with or without spaces, as last parameters a.s.o). It also uncovered a bug in our via parameter parser. I also modified things like rport_builder. You used snprintf which I do not like (it is way too slow). I used instead memcpy and int2str.
Andrei
Few more notes on this construct:
a it takes two servers in series; the first one in chain makes the NAT fixing job, the other one acts as proxy/registrar; the reason is the NAT changes to a request are only used to generate outoging request and do not affect registrar; thus, you first need to send the updated request out; one could still have only one server running (be it easier or not) with a routing policy, which makes the request pass the same server in two rounds -- first one de-natifying, the second real one
b it takes generating symmetric replies and accepting rport in relayed replies -- I think that is a reasonable thing to add to ser core
c the routing policy needs to be set up in such a way, the de-natifying instance of SER is always in the path -- otherwise subsequent inbound requests would be routed based-on Contact directly to NAT, would not use symmetric path opened by initial REGISTERs and fail; the denatifier thus needs to use record-routing
d for the same reason, all initial inbound requests to natted users proxied by the main server need to be statically routed to the de-natifier
-Jiri
the examples bellow demonstrate in ascii charts some more details; A:B is address:port of natted phone, X:Y the natted A:B, C:D that of denatifier
network setting:
fone F: A:B NAT N: X:Y proxy D proxy P ~ /------\ ~ /-------------\ /--------------\ |phone | -------->~----------->|denattifying |--------------->|registrar/ | ------/ ~ |ser/outbound | |inbound proxy | ~ |proxy | --------------/ -------------/\ ^ \ / ----> outbound / domains
1) registers go from F, are rewritten by D and processed at P 2) INVITEs from F visit D, Contact is rewritten, rport is introduced, request is record-routed, so that other party's request visit D too; 3) INVITEs from outside visit first P, contacts rewritten previously by D are put in r-uri, requests are forwarded statically to D, and D forwardes by uri to the fone F
At 06:27 PM 1/10/2003, Maxim Sobolev wrote:
On Fri, Jan 10, 2003 at 03:37:46PM +0200, Maxim Sobolev wrote:
Sounds reasonably - I'll do it that way.
I am planning add a new nathelper module, which will export the following functions:
add_rport() - insert a rport= parameter into the first Via field fix_nated_contact() - replaces host:port in Contact field with host:port we received this message from add_direction_passive() - adds direction=passive option to the SDP
Then it would be possible to do the following at the very top of config before any other REGISTER/INVITE processing:
if (method == "INVITE" || method == "REGISTER") { if (search("User-Agent: Cisco ATA.*") { add_rport(); if (method == "INVITE") { add_direction_passive(); }; if (method == "REGISTER") { fix_nated_contact(); }; }; };
Does it sound reasonably for you?
-Maxim
Thanks!
-Maxim
On Fri, Jan 10, 2003 at 02:15:11PM +0100, Jan Janak wrote:
On 10-01 14:32, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that host:port portion of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that those UAs will support symmetric SIP signalling, so that this scheme will work just fine.
In my opinion there are two ways to do it: either add new rewritecontact* family of functions similar to rewritehost ones. or add a new flag for the save() function. This is where I need your help - which implementation looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into the mainline to reduce our local hacks.
This should be implemented as a standalone module for ser. I want to keep registrar clean, it should not be aware of NATs. So, create a new module for ser that will contain all the NAT traversal helper functions, the functions will be then called from the config script.
That includes modifications of contact, adding rport to Via and so on.
regards, Jan.
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
I can't quite agree that you need two servers for the job and this is proven to be true by my test setup. Following is how it works:
1. Network setup is as follows:
private public UA1 NAT SER UA2 [ ]---------{ }--------< >---[ ]
2. SER configured as follows:
route { if (search("User-Agent: Cisco ATA.*")) { add_rport(); fix_nated_contact(); }; rewriteFromRoute();
[REGISTRATION STUFF STRIPPED]
if (method=="INVITE") { addRecordRoute(); };
t_on_positive("1") # forward to current uri now if (!t_relay()) { sl_reply_error(); }; }
reply_route[1] { if (search("Server: Cisco ATA.*") { fix_nated_contact(); } }
3. Registration
When registration request from UA1 arrives, we add received and rport into the first Via field, which allows us to properly route reply later on and also we rewrite Contact field with real IP:PORT, therefore registration information is correct and we can contact UA1 if necessary. By selecting a small registration expiration interval on UA1 it is possible to keep the nat binding alive.
4. Call from UA1 to UA2
- UA1 send INVITE request to SER. This request is passed through add_rport() and fix_nated_contact() - SER replies with 100 Trying to UA1 to the correct address/port due to received/rport added on the previous step - SER forwards INVITE request to UA1 with RecordRoute and rport/received added and Contact fixed - UA2 replies with 100 Trying. The response contains the second Via field with correct received/rport, but it doesn't matter since reply ignored anyway - UA2 replies with 180 Ringing. The response contains the second Via field with correct received/rport allowing to forward it properly - SER forwards 180 Ringing to UA1 - When the user picks up the phone, UA2 replies with 200 OK, again it contains proper received/rport in the second Via field - SER forwards 200 OK to UA1 - UA1 replies to SER with ACK, upon receiving it SER inserts received/rport into the first Via field - SER forwards ACK to UA2 [call in progress] - When the user at UA2 hangs us, UA2 seds a BYE to SER with Route filled with real IP:PORT of the UA1 from the Contact field it received in the original INVITE - SER forwards BYE to UA1 using IP:PORT from Route field - UA1 replies with 200 OK to SER - SER forwards 200 OK reply to UA2
5. Call from UA2 to UA1
- UA2 send INVITE request to SER - SER replies with 100 Trying to UA2 - SER looks up into registration database and forwards INVITE to UA1 with RecordRoute added - UA1 replies with 100 Trying. Received/rport added into the first Via field, but it doesn't matter since reply ignored anyway - UA1 replies with 180 Ringing. Received/rport added into the first Via field, but it doesn't really matter - SER forwards 180 Ringing to UA1 - When the user picks up the phone, UA1 replies with 200 OK. Contact field is fixed in reply_route[1] - SER forwards 200 OK to UA2 - UA2 replies to SER with ACK, Route field is is filled with IP:PORT from the Contact field in 200 OK - SER forwards ACK to UA1 using IP:PORT from Route field [call in progress] - When the user at UA2 hangs us, UA2 sends a BYE to SER with Route filled with real IP:PORT of the UA1 from the Contact field it received in the original 200 OK - SER forwards BYE to UA1 using IP:PORT from Route field - UA1 replies with 200 OK to SER - SER forwards 200 OK reply to UA2
As you can see, as long as signalling concerned, it is possible to reliably traverse NAT without any additional servers. Of course this requires a reliable way to determine that a client supports symmetric signalling. In our particular case, it is done by searching for "Server: Cisco ATA.*" or "User-Agent: Cisco ATA.*"
-Maxim
Jiri Kuthan wrote:
Few more notes on this construct:
a it takes two servers in series; the first one in chain makes the NAT fixing job, the other one acts as proxy/registrar; the reason is the NAT changes to a request are only used to generate outoging request and do not affect registrar; thus, you first need to send the updated request out; one could still have only one server running (be it easier or not) with a routing policy, which makes the request pass the same server in two rounds -- first one de-natifying, the second real one
b it takes generating symmetric replies and accepting rport in relayed replies -- I think that is a reasonable thing to add to ser core
c the routing policy needs to be set up in such a way, the de-natifying instance of SER is always in the path -- otherwise subsequent inbound requests would be routed based-on Contact directly to NAT, would not use symmetric path opened by initial REGISTERs and fail; the denatifier thus needs to use record-routing
d for the same reason, all initial inbound requests to natted users proxied by the main server need to be statically routed to the de-natifier
-Jiri
the examples bellow demonstrate in ascii charts some more details; A:B is address:port of natted phone, X:Y the natted A:B, C:D that of denatifier
network setting:
fone F: A:B NAT N: X:Y proxy D proxy P ~ /------\ ~ /-------------\ /--------------\ |phone | -------->~----------->|denattifying |--------------->|registrar/ | ------/ ~ |ser/outbound | |inbound proxy | ~ |proxy | --------------/ -------------/\ ^ \ / ----> outbound / domains
- registers go from F, are rewritten by D and processed at P
- INVITEs from F visit D, Contact is rewritten, rport is introduced, request is record-routed, so that other party's request visit D too;
- INVITEs from outside visit first P, contacts rewritten previously by D are put in r-uri, requests are forwarded statically to D, and D forwardes by uri to the fone F
At 06:27 PM 1/10/2003, Maxim Sobolev wrote:
On Fri, Jan 10, 2003 at 03:37:46PM +0200, Maxim Sobolev wrote:
Sounds reasonably - I'll do it that way.
I am planning add a new nathelper module, which will export the following functions:
add_rport() - insert a rport= parameter into the first Via field fix_nated_contact() - replaces host:port in Contact field with host:port we received this message from add_direction_passive() - adds direction=passive option to the SDP
Then it would be possible to do the following at the very top of config before any other REGISTER/INVITE processing:
if (method == "INVITE" || method == "REGISTER") { if (search("User-Agent: Cisco ATA.*") { add_rport(); if (method == "INVITE") { add_direction_passive(); }; if (method == "REGISTER") { fix_nated_contact(); }; }; };
Does it sound reasonably for you?
-Maxim
Thanks!
-Maxim
On Fri, Jan 10, 2003 at 02:15:11PM +0100, Jan Janak wrote:
On 10-01 14:32, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that host:port portion of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that those UAs will support symmetric SIP signalling, so that this scheme will work just fine.
In my opinion there are two ways to do it: either add new rewritecontact* family of functions similar to rewritehost ones. or add a new flag for the save() function. This is where I need your help - which implementation looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into the mainline to reduce our local hacks.
This should be implemented as a standalone module for ser. I want to keep registrar clean, it should not be aware of NATs. So, create a new module for ser that will contain all the NAT traversal helper functions, the functions will be then called from the config script.
That includes modifications of contact, adding rport to Via and so on.
regards, Jan.
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
I'm glad but surprised it works. There are two things I do not understand: registration and reply_route.
ad registration) I understood from your previous email you were wishing to save transport origin as user's contact in usrLoc DB. however, 'fix_nated_contact();save();' doesn't do that imho -- it creates a "diff list" and stores contacts from original message.
ad reply_route) I fear fix_nated_contact does not help here really. From reply_route, you process original requests conserved in share memory now -- you don't affect replies.
Am I missing something?
-Jiri
At 02:00 PM 1/14/2003, Maxim Sobolev wrote:
I can't quite agree that you need two servers for the job and this is proven to be true by my test setup. Following is how it works:
Network setup is as follows:
private public
UA1 NAT SER UA2 [ ]---------{ }--------< >---[ ]
- SER configured as follows:
route { if (search("User-Agent: Cisco ATA.*")) { add_rport(); fix_nated_contact(); }; rewriteFromRoute();
[REGISTRATION STUFF STRIPPED] if (method=="INVITE") { addRecordRoute(); }; t_on_positive("1") # forward to current uri now if (!t_relay()) { sl_reply_error(); };
}
reply_route[1] { if (search("Server: Cisco ATA.*") { fix_nated_contact(); } }
- Registration
When registration request from UA1 arrives, we add received and rport into the first Via field, which allows us to properly route reply later on and also we rewrite Contact field with real IP:PORT, therefore registration information is correct and we can contact UA1 if necessary. By selecting a small registration expiration interval on UA1 it is possible to keep the nat binding alive.
- Call from UA1 to UA2
- UA1 send INVITE request to SER. This request is passed through
add_rport() and fix_nated_contact()
- SER replies with 100 Trying to UA1 to the correct address/port due
to received/rport added on the previous step
- SER forwards INVITE request to UA1 with RecordRoute and
rport/received added and Contact fixed
- UA2 replies with 100 Trying. The response contains the second Via
field with correct received/rport, but it doesn't matter since reply ignored anyway
- UA2 replies with 180 Ringing. The response contains the second Via
field with correct received/rport allowing to forward it properly
- SER forwards 180 Ringing to UA1
- When the user picks up the phone, UA2 replies with 200 OK, again it
contains proper received/rport in the second Via field
- SER forwards 200 OK to UA1
- UA1 replies to SER with ACK, upon receiving it SER inserts
received/rport into the first Via field
- SER forwards ACK to UA2
[call in progress]
- When the user at UA2 hangs us, UA2 seds a BYE to SER with Route
filled with real IP:PORT of the UA1 from the Contact field it received in the original INVITE
- SER forwards BYE to UA1 using IP:PORT from Route field
- UA1 replies with 200 OK to SER
- SER forwards 200 OK reply to UA2
- Call from UA2 to UA1
- UA2 send INVITE request to SER
- SER replies with 100 Trying to UA2
- SER looks up into registration database and forwards INVITE to UA1
with RecordRoute added
- UA1 replies with 100 Trying. Received/rport added into the first Via
field, but it doesn't matter since reply ignored anyway
- UA1 replies with 180 Ringing. Received/rport added into the first
Via field, but it doesn't really matter
- SER forwards 180 Ringing to UA1
- When the user picks up the phone, UA1 replies with 200 OK. Contact
field is fixed in reply_route[1]
- SER forwards 200 OK to UA2
- UA2 replies to SER with ACK, Route field is is filled with IP:PORT
from the Contact field in 200 OK
- SER forwards ACK to UA1 using IP:PORT from Route field
[call in progress]
- When the user at UA2 hangs us, UA2 sends a BYE to SER with Route
filled with real IP:PORT of the UA1 from the Contact field it received in the original 200 OK
- SER forwards BYE to UA1 using IP:PORT from Route field
- UA1 replies with 200 OK to SER
- SER forwards 200 OK reply to UA2
As you can see, as long as signalling concerned, it is possible to reliably traverse NAT without any additional servers. Of course this requires a reliable way to determine that a client supports symmetric signalling. In our particular case, it is done by searching for "Server: Cisco ATA.*" or "User-Agent: Cisco ATA.*"
-Maxim
Jiri Kuthan wrote:
Few more notes on this construct:
a it takes two servers in series; the first one in chain makes the NAT fixing job, the other one acts as proxy/registrar; the reason is the NAT changes to a request are only used to generate outoging request and do not affect registrar; thus, you first need to send the updated request out; one could still have only one server running (be it easier or not) with a routing policy, which makes the request pass the same server in two rounds -- first one de-natifying, the second real one
b it takes generating symmetric replies and accepting rport in relayed replies -- I think that is a reasonable thing to add to ser core
c the routing policy needs to be set up in such a way, the de-natifying instance of SER is always in the path -- otherwise subsequent inbound requests would be routed based-on Contact directly to NAT, would not use symmetric path opened by initial REGISTERs and fail; the denatifier thus needs to use record-routing
d for the same reason, all initial inbound requests to natted users proxied by the main server need to be statically routed to the de-natifier
-Jiri
the examples bellow demonstrate in ascii charts some more details; A:B is address:port of natted phone, X:Y the natted A:B, C:D that of denatifier
network setting:
fone F: A:B NAT N: X:Y proxy D proxy P ~ /------\ ~ /-------------\ /--------------\ |phone | -------->~----------->|denattifying |--------------->|registrar/ | ------/ ~ |ser/outbound | |inbound proxy | ~ |proxy | --------------/ -------------/\ ^ \ / ----> outbound / domains
- registers go from F, are rewritten by D and processed at P
- INVITEs from F visit D, Contact is rewritten, rport is introduced, request is record-routed, so that other party's request visit D too;
- INVITEs from outside visit first P, contacts rewritten previously by D are put in r-uri, requests are forwarded statically to D, and D forwardes by uri to the fone F
At 06:27 PM 1/10/2003, Maxim Sobolev wrote:
On Fri, Jan 10, 2003 at 03:37:46PM +0200, Maxim Sobolev wrote:
Sounds reasonably - I'll do it that way.
I am planning add a new nathelper module, which will export the following functions:
add_rport() - insert a rport= parameter into the first Via field fix_nated_contact() - replaces host:port in Contact field with host:port we received this message from add_direction_passive() - adds direction=passive option to the SDP
Then it would be possible to do the following at the very top of config before any other REGISTER/INVITE processing:
if (method == "INVITE" || method == "REGISTER") { if (search("User-Agent: Cisco ATA.*") { add_rport(); if (method == "INVITE") { add_direction_passive(); }; if (method == "REGISTER") { fix_nated_contact(); }; }; };
Does it sound reasonably for you?
-Maxim
Thanks!
-Maxim
On Fri, Jan 10, 2003 at 02:15:11PM +0100, Jan Janak wrote:
On 10-01 14:32, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that host:port portion of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that those UAs will support symmetric SIP signalling, so that this scheme will work just fine.
In my opinion there are two ways to do it: either add new rewritecontact* family of functions similar to rewritehost ones. or add a new flag for the save() function. This is where I need your help - which implementation looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into the mainline to reduce our local hacks.
This should be implemented as a standalone module for ser. I want to keep registrar clean, it should not be aware of NATs. So, create a new module for ser that will contain all the NAT traversal helper functions, the functions will be then called from the config script.
That includes modifications of contact, adding rport to Via and so on.
regards, Jan.
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
-- Jiri Kuthan http://iptel.org/~jiri/
Jiri Kuthan wrote:
I'm glad but surprised it works. There are two things I do not understand: registration and reply_route.
ad registration) I understood from your previous email you were wishing to save transport origin as user's contact in usrLoc DB. however, 'fix_nated_contact();save();' doesn't do that imho -- it creates a "diff list" and stores contacts from original message.
Yes and no. ;)
Call it dirty trick, or a smart workaround, but after patching the contact in the message using del_lump/insert_new_lump(), I'm replacing msg->contact->parsed->contacts->uri.s with the pointer to the pkg_malloc'ed buffer passed to the insert_new_lump() and also adjusting [...]->uri.len accordingly. Documentation says that buffers passed to insert_new_lump() will be deallocated along with the message itself, therefore memory isn't leaked and reference guranteed to be valid during the lifetime of the message. Later on, registration procedure checks that contact is already parsed (it is) and then takes our replaced [...]->uri.s "as is", and voila, everything works. 8-)
Of course Bad Things[tm] will happen if some module invoked after nathelper will try to calculate offset=[...]->contacts->uri.s - msg->orig, or will try to scratch something in that buffer but no modules do it yet.
ad reply_route) I fear fix_nated_contact does not help here really. From reply_route, you process original requests conserved in share memory now -- you don't affect replies.
Perhaps you are missing something, because my little old buddy gdb disagrees[1] - I am definitely observing "200 OK" reply as msg argument in fix_nated_contact() called from reply_route[].
-Maxim
[1] (gdb) b on_positive_reply Breakpoint 4 at 0x2a18dde2: file t_reply.c, line 939. (gdb) c Continuing. Breakpoint 4, on_positive_reply (t=0x28304e24, msg=0x80b71b0, code=200, param=0x0) at t_reply.c:939 939 if (!t->on_positive) { (gdb) tb fix_nated_contact_f Breakpoint 5 at 0x2a1b4bb3: file nathelper.c, line 145. (gdb) c Continuing. fix_nated_contact_f (msg=0x80b71b0, str1=0x0, str2=0x0) at nathelper.c:145 145 if ((parse_headers(msg, HDR_CONTACT, 0) == -1) || !msg->contact) (gdb) bt #0 fix_nated_contact_f (msg=0x80b71b0, str1=0x0, str2=0x0) at nathelper.c:145 #1 0x0804de0a in do_action (a=0x81b1660, msg=0x80b71b0) at action.c:511 #2 0x0804dff1 in run_actions (a=0x81b1660, msg=0x80b71b0) at action.c:559 #3 0x0804dd97 in do_action (a=0x81b1680, msg=0x80b71b0) at action.c:501 #4 0x0804dff1 in run_actions (a=0x81b1680, msg=0x80b71b0) at action.c:559 #5 0x2a18de75 in on_positive_reply (t=0x28304e24, msg=0x80b71b0, code=200, param=0x0) at t_reply.c:946 #6 0x2a18845e in callback_event (cbt=TMCB_REPLY_IN, trans=0x28304e24, msg=0x80b71b0, code=200) at t_hooks.c:73 #7 0x2a18cee3 in relay_reply (t=0x28304e24, p_msg=0x80b71b0, branch=0, msg_status=200, cancel_bitmap=0xbfbff09c) at t_reply.c:565 #8 0x2a18d995 in t_on_reply (p_msg=0x80b71b0) at t_reply.c:812 #9 0x08052af7 in forward_reply (msg=0x80b71b0) at forward.c:343 #10 0x0805dec5 in receive_msg (buf=0x809a6c0 "SIP/2.0", len=733, src_su=0x81b1220) at receive.c:161 #11 0x08064127 in udp_rcv_loop () at udp_server.c:332 #12 main_loop () at main.c:582 #13 0x080571df in main (argc=3, argv=0xbfbff74c) at main.c:1383 #14 0x0804c679 in _start () (gdb) print *msg $2 = {id = 49, first_line = {type = 2, u = {request = {method = {s = 0x809a6c0 "SIP/2.0", len = 7}, uri = {s = 0x809a6c8 "200", len = 3}, version = {s = 0x809a6cc "OK", len = 2}, method_value = 200}, reply = {version = {s = 0x809a6c0 "SIP/2.0", len = 7}, status = { s = 0x809a6c8 "200", len = 3}, reason = {s = 0x809a6cc "OK", len = 2}, statuscode = 200}}}, via1 = 0x80b7540, via2 = 0x80b8180, headers = 0x80b77a4, last_header = 0x80b78d4, parsed_flag = 575, h_via1 = 0x80b77a4, h_via2 = 0x80b7888, callid = 0x80b7b40, to = 0x80b79f0, cseq = 0x80b78d4, from = 0x80b76c8, contact = 0x0, maxforwards = 0x0, route = 0x0, record_route = 0x80b7714, content_type = 0x0, content_length = 0x0, authorization = 0x0, expires = 0x0, proxy_auth = 0x0, www_auth = 0x0, supported = 0x0, require = 0x0, proxy_require = 0x0, unsupported = 0x0, allow = 0x0, event = 0x0, eoh = 0x0, unparsed = 0x809a82b "Contact: sip:380442466396@192.168.0.9:5060;user=phone;transport=udp\r\nSe rver: Cisco ATA v2.15 ata18x (020927a)\r\nContent-Length: 201\r\nContent-Type: application/sdp\r\n\ r\nv=0\r\no=380442466396 76117 76117"..., src_ip = {af = 2, len = 4, u = {addr32 = {838969536, 0, 0, 0}, addr16 = {43200, 12801, 0, 0, 0, 0, 0, 0}, addr = "(\0012", '\0' <repeats 11 times>}}, src_port_no = 5060, dst_ip = {af = 2, len = 4, u = {addr32 = {1677830336, 0, 0, 0}, addr16 = {43200, 25601, 0, 0, 0, 0, 0, 0}, addr = "(\001d", '\0' <repeats 11 times>}}, orig = 0x80b8a5c "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.1.100;branch=z9hG4bK61d8.02d66d15.0\r \nVia: SIP/2.0/UDP 192.168.1.1:5063\r\nRecord-Route: sip:380442466396@192.168.1.100;branch=0\r\nFr om: Maxim <sip:380442466397@"..., buf = 0x809a6c0 "SIP/2.0", len = 733, new_uri = {s = 0x0, len = 0}, parsed_uri_ok = 0, parsed_uri = {user = {s = 0x0, len = 0}, passwd = {s = 0x0, len = 0}, host = {s = 0x0, len = 0}, port = {s = 0x0, len = 0}, params = {s = 0x0, len = 0}, headers = {s = 0x0, len = 0}, port_no = 0}, add_rm = 0x0, repl_add_rm = 0x0, reply_lump = 0x0, add_to_branch_s = '\0' <repeats 54 times>, add_to_branch_len = 0, hash_index = 0, flags = 0} (gdb) print msg->orig $3 = 0x80b8a5c "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.1.100;branch=z9hG4bK61d8.02d66d15.0\r\nVi a: SIP/2.0/UDP 192.168.1.1:5063\r\nRecord-Route: sip:380442466396@192.168.1.100;branch=0\r\nFrom: Maxim <sip:380442466397@"...
sorry for latency--I'm mostly off-line this week
At 10:17 PM 1/14/2003, Maxim Sobolev wrote:
contact in the message using del_lump/insert_new_lump(), I'm replacing msg->contact->parsed->contacts->uri.s with the pointer to the pkg_malloc'ed buffer passed to the insert_new_lump() and also adjusting [...]->uri.len accordingly.
[...]
Fair enough. I'm not aware of any issues with this smart workaround.
ad reply_route) I fear fix_nated_contact does not help here really. From reply_route, you process original requests conserved in share memory now -- you don't affect replies.
Perhaps you are missing something, because my little old buddy gdb disagrees[1] - I am definitely observing "200 OK" reply as msg argument in fix_nated_contact() called from reply_route[].
My bad -- I did not notice you were using replies in on_positive_reply as opposed to using a copy of shmem-ed request in on_negative_reply. (Actually, I think that the name 'reply_route' is very confusing and I should change it.)
I'm not yet sure whether we should go for script programmability for replies. Currently, "as is" it's imho quite unsafe. The script is now built primarily for request processing. Applying some of the request-processing actions to replies might possibly result in unpredictable behaviour. For examples, attempts to manipulate uri's might manipulate other things (union use), or when manipulating lump list, request lump list (and not reply list) would be mistakenly used. That's just few things which randomly came into my mind -- I'm off-line now and cannot check the code for more details.
So the options are imho: - hard-wire the reply-processing logic in the module. - rewriting ser in a way that permits reply processing too (maybe that could be done along with a change to module interface, which would allow to specify what actions make sense for reply processing, on_reply processing and request processing)
-Jiri
On Wed, Jan 15, 2003 at 10:37:42PM +0100, Jiri Kuthan wrote:
sorry for latency--I'm mostly off-line this week
At 10:17 PM 1/14/2003, Maxim Sobolev wrote:
contact in the message using del_lump/insert_new_lump(), I'm replacing msg->contact->parsed->contacts->uri.s with the pointer to the pkg_malloc'ed buffer passed to the insert_new_lump() and also adjusting [...]->uri.len accordingly.
[...]
Fair enough. I'm not aware of any issues with this smart workaround.
ad reply_route) I fear fix_nated_contact does not help here really. From reply_route, you process original requests conserved in share memory now -- you don't affect replies.
Perhaps you are missing something, because my little old buddy gdb disagrees[1] - I am definitely observing "200 OK" reply as msg argument in fix_nated_contact() called from reply_route[].
My bad -- I did not notice you were using replies in on_positive_reply as opposed to using a copy of shmem-ed request in on_negative_reply. (Actually, I think that the name 'reply_route' is very confusing and I should change it.)
I'm not yet sure whether we should go for script programmability for replies. Currently, "as is" it's imho quite unsafe. The script is now built primarily for request processing. Applying some of the request-processing actions to replies might possibly result in unpredictable behaviour. For examples, attempts to manipulate uri's might manipulate other things (union use), or when manipulating lump list, request lump list (and not reply list) would be mistakenly used.
This is already handled properly by my patches for tm module - before invoking do_acton() it replaces msg->add_rm with msg->repl_add_rm and restores it after return from do_action().
That's just few things which randomly came into my mind -- I'm off-line now and cannot check the code for more details.
So the options are imho:
- hard-wire the reply-processing logic in the module.
It's possible, but I don't see how it will differ from my current approach. In any case, after call-back is invoked the reply will be passed to adjusting function, the only difference is that in one case you can control conditions when it happens (i.e. search() match or something else), while in the other case the logic will be hardcoded into the module itself.
- rewriting ser in a way that permits reply processing too (maybe that could be done along with a change to module interface, which would allow to specify what actions make sense for reply processing, on_reply processing and request processing)
Maybe, but I really don't have a time to do it right now.
-Maxim
/* still catching up on my backlog */
At 05:10 AM 1/16/2003, Maxim Sobolev wrote:
I'm not yet sure whether we should go for script programmability for replies. Currently, "as is" it's imho quite unsafe. The script is now built primarily for request processing. Applying some of the request-processing actions to replies might possibly result in unpredictable behaviour. For examples, attempts to manipulate uri's might manipulate other things (union use), or when manipulating lump list, request lump list (and not reply list) would be mistakenly used.
This is already handled properly by my patches for tm module - before invoking do_acton() it replaces msg->add_rm with msg->repl_add_rm and restores it after return from do_action().
ok, I see. That should work too.
I'm currently refurbishing some of these parts and unless I do not discover some shortcoming, I would like to have three different route blocks. route[] (request processing), reply_route[] (request processing on receipt of reply) and reply[] (reply processing). Along with an updated module interface, it should add more sanity and allow easy syntactical checking of what action can be used from what block type.
Your patches look reasonable and I'll do my best to align them to these undergoing changes. Just bear with my pace -- I have currently too many things to do.
-Jiri
There is actually a plenty of options how to traverse NATs. Sadly, none of them works in all possible scenarios.
a) STUN -- some phones (kphone for linux, snom hardphones) have the ability to "fool" NATs to accomplish traversal using the STUN protocols; particularly good if you cannot manipulate the NAT b) geek tweaks -- you have a configurable NAT and configurable phones (there are some of both of them). you configure static port forwarding in the NAT and phones to advertise the public address in contacts and elsewhere c) ALG -- use a SIP-aware NAT such as PIX or Intertex d) UPnP -- takes UPnP enables phones (snom is) and NATs e) SIP/media relay -- that's a too ugly story
What to choose best depends on your network setting -- can you tweak the NAT, can you afford replacing it with a SIP-enabled one, are the phones you are using configurable or do they use STUN, do you have a server on the public or private NAT side or on each of them, etc.
I remember someone shared with us he was using ser in his network to do the translation of SIP addresses on behalf ot the phones. The ser script was configured to statically rewrite private IP addresses to the public address using replace/textops.
-Jiri
At 01:32 PM 1/10/2003, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that host:port portion of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that those UAs will support symmetric SIP signalling, so that this scheme will work just fine.
In my opinion there are two ways to do it: either add new rewritecontact* family of functions similar to rewritehost ones. or add a new flag for the save() function. This is where I need your help - which implementation looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into the mainline to reduce our local hacks.
Regards,
Maxim
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
Yes, I know - we have studied all those methods in details. Our method of choice is symmetric signalling/symmetric media (aka COMEDIA) due to the following reasons:
1. Things should work without modifying or reconfiguring existing user's infrastructure (NATs) and should be compatible with all widely-used NATs.
2. We are bound to ata186 as UA. It is compatible with this method. Support for other UAs isn't required.
3. The calls will be terminated to Cisco GWs, while COMEDIA support was recently added into Cisco IOS, so that theoretically the only thing we need is to add received/rport support into proxy/registrar and update IOS at termination points.
4. No media relay is allowed, because this will create excessive bandwith load in a single point.
5. COMEDIA support is likely to become part of the standard, so that our investments into development are protected.
-Maxim
On Fri, Jan 10, 2003 at 02:26:07PM +0100, Jiri Kuthan wrote:
There is actually a plenty of options how to traverse NATs. Sadly, none of them works in all possible scenarios.
a) STUN -- some phones (kphone for linux, snom hardphones) have the ability to "fool" NATs to accomplish traversal using the STUN protocols; particularly good if you cannot manipulate the NAT b) geek tweaks -- you have a configurable NAT and configurable phones (there are some of both of them). you configure static port forwarding in the NAT and phones to advertise the public address in contacts and elsewhere c) ALG -- use a SIP-aware NAT such as PIX or Intertex d) UPnP -- takes UPnP enables phones (snom is) and NATs e) SIP/media relay -- that's a too ugly story
What to choose best depends on your network setting -- can you tweak the NAT, can you afford replacing it with a SIP-enabled one, are the phones you are using configurable or do they use STUN, do you have a server on the public or private NAT side or on each of them, etc.
I remember someone shared with us he was using ser in his network to do the translation of SIP addresses on behalf ot the phones. The ser script was configured to statically rewrite private IP addresses to the public address using replace/textops.
-Jiri
At 01:32 PM 1/10/2003, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that host:port portion of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that those UAs will support symmetric SIP signalling, so that this scheme will work just fine.
In my opinion there are two ways to do it: either add new rewritecontact* family of functions similar to rewritehost ones. or add a new flag for the save() function. This is where I need your help - which implementation looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into the mainline to reduce our local hacks.
Regards,
Maxim
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
What is this device? Where can I get one? What does it cost?
Thanks :-)
---greg
-----Original Message----- From: serusers-admin@lists.iptel.org [mailto:serusers-admin@lists.iptel.org] On Behalf Of Maxim Sobolev Sent: Friday, January 10, 2003 8:15 AM To: Jiri Kuthan Cc: serusers@lists.iptel.org; kapitan@portaone.com Subject: Re: [Serusers] Rewriting URI in the Contact field
Yes, I know - we have studied all those methods in details. Our method of choice is symmetric signalling/symmetric media (aka COMEDIA) due to the following reasons:
- Things should work without modifying or reconfiguring existing
user's infrastructure (NATs) and should be compatible with all widely-used NATs.
- We are bound to ata186 as UA. It is compatible with this method.
Support for other UAs isn't required.
- The calls will be terminated to Cisco GWs, while COMEDIA support
was recently added into Cisco IOS, so that theoretically the only thing we need is to add received/rport support into proxy/registrar and update IOS at termination points.
- No media relay is allowed, because this will create excessive
bandwith load in a single point.
- COMEDIA support is likely to become part of the standard, so that
our investments into development are protected.
-Maxim
On Fri, Jan 10, 2003 at 02:26:07PM +0100, Jiri Kuthan wrote:
There is actually a plenty of options how to traverse NATs. Sadly, none of them works in all possible scenarios.
a) STUN -- some phones (kphone for linux, snom hardphones) have the ability to "fool" NATs to accomplish traversal using the STUN protocols; particularly good if you cannot manipulate the NAT b) geek tweaks -- you have a configurable NAT and configurable phones (there are some of both of them). you configure static port forwarding in the NAT and phones to advertise the public address in contacts and elsewhere c) ALG -- use a SIP-aware NAT such as PIX or Intertex d) UPnP -- takes UPnP enables phones (snom is) and NATs e) SIP/media relay -- that's a too ugly story
What to choose best depends on your network setting -- can you tweak the NAT, can you afford replacing it with a SIP-enabled one, are the phones you are using configurable or do they use STUN, do you have a server on the public or private NAT side or on each of them, etc.
I remember someone shared with us he was using ser in his network to do the translation of SIP addresses on behalf ot the phones. The ser script was configured to statically rewrite private IP addresses to the public address using replace/textops.
-Jiri
At 01:32 PM 1/10/2003, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature,
which isn't
currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that
host:port portion
of URI in Contact field should not be used, but host:port
the request
came from should be used instead. By definition we know
that those UAs
will support symmetric SIP signalling, so that this scheme
will work just
fine.
In my opinion there are two ways to do it: either add new
rewritecontact*
family of functions similar to rewritehost ones. or add a
new flag for
the save() function. This is where I need your help -
which implementation
looks better for you (or maybe you have even some better
idea), since
we are really interested in inclusion of our changes into
the mainline to
reduce our local hacks.
Regards,
Maxim
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
On Fri, Jan 10, 2003 at 08:27:46AM -0600, Greg Fausak wrote:
What is this device? Where can I get one? What does it cost?
Cisco ata186 is two-port analog telephone adapter, i.e. it has two standard ports for connecting ordinary phones and one 10M ethernet port. It supports SIP and H323 (G711, G723 and G729 audio codecs) and costs some US$150.
-Maxim
Thanks :-)
---greg
-----Original Message----- From: serusers-admin@lists.iptel.org [mailto:serusers-admin@lists.iptel.org] On Behalf Of Maxim Sobolev Sent: Friday, January 10, 2003 8:15 AM To: Jiri Kuthan Cc: serusers@lists.iptel.org; kapitan@portaone.com Subject: Re: [Serusers] Rewriting URI in the Contact field
Yes, I know - we have studied all those methods in details. Our method of choice is symmetric signalling/symmetric media (aka COMEDIA) due to the following reasons:
- Things should work without modifying or reconfiguring existing
user's infrastructure (NATs) and should be compatible with all widely-used NATs.
- We are bound to ata186 as UA. It is compatible with this method.
Support for other UAs isn't required.
- The calls will be terminated to Cisco GWs, while COMEDIA support
was recently added into Cisco IOS, so that theoretically the only thing we need is to add received/rport support into proxy/registrar and update IOS at termination points.
- No media relay is allowed, because this will create excessive
bandwith load in a single point.
- COMEDIA support is likely to become part of the standard, so that
our investments into development are protected.
-Maxim
On Fri, Jan 10, 2003 at 02:26:07PM +0100, Jiri Kuthan wrote:
There is actually a plenty of options how to traverse NATs. Sadly, none of them works in all possible scenarios.
a) STUN -- some phones (kphone for linux, snom hardphones) have the ability to "fool" NATs to accomplish traversal using the STUN protocols; particularly good if you cannot manipulate the NAT b) geek tweaks -- you have a configurable NAT and configurable phones (there are some of both of them). you configure static port forwarding in the NAT and phones to advertise the public address in contacts and elsewhere c) ALG -- use a SIP-aware NAT such as PIX or Intertex d) UPnP -- takes UPnP enables phones (snom is) and NATs e) SIP/media relay -- that's a too ugly story
What to choose best depends on your network setting -- can you tweak the NAT, can you afford replacing it with a SIP-enabled one, are the phones you are using configurable or do they use STUN, do you have a server on the public or private NAT side or on each of them, etc.
I remember someone shared with us he was using ser in his network to do the translation of SIP addresses on behalf ot the phones. The ser script was configured to statically rewrite private IP addresses to the public address using replace/textops.
-Jiri
At 01:32 PM 1/10/2003, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature,
which isn't
currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that
host:port portion
of URI in Contact field should not be used, but host:port
the request
came from should be used instead. By definition we know
that those UAs
will support symmetric SIP signalling, so that this scheme
will work just
fine.
In my opinion there are two ways to do it: either add new
rewritecontact*
family of functions similar to rewritehost ones. or add a
new flag for
the save() function. This is where I need your help -
which implementation
looks better for you (or maybe you have even some better
idea), since
we are really interested in inclusion of our changes into
the mainline to
reduce our local hacks.
Regards,
Maxim
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Howdy,
I have a ATA186. I may have misunderstood the COMEDIA reference. Does the ATA186 poke itself through a NAT router to a SIP server???
NAT seems to be the biggest hurdle we have.
----greg
-----Original Message----- From: Maxim Sobolev [mailto:sobomax@FreeBSD.org] Sent: Friday, January 10, 2003 8:58 AM To: Greg Fausak Cc: 'Jiri Kuthan'; serusers@lists.iptel.org; kapitan@portaone.com Subject: Re: [Serusers] Rewriting URI in the Contact field
On Fri, Jan 10, 2003 at 08:27:46AM -0600, Greg Fausak wrote:
What is this device? Where can I get one? What does it cost?
Cisco ata186 is two-port analog telephone adapter, i.e. it has two standard ports for connecting ordinary phones and one 10M ethernet port. It supports SIP and H323 (G711, G723 and G729 audio codecs) and costs some US$150.
-Maxim
Thanks :-)
---greg
-----Original Message----- From: serusers-admin@lists.iptel.org [mailto:serusers-admin@lists.iptel.org] On Behalf Of Maxim Sobolev Sent: Friday, January 10, 2003 8:15 AM To: Jiri Kuthan Cc: serusers@lists.iptel.org; kapitan@portaone.com Subject: Re: [Serusers] Rewriting URI in the Contact field
Yes, I know - we have studied all those methods in details. Our method of choice is symmetric signalling/symmetric media
(aka COMEDIA)
due to the following reasons:
- Things should work without modifying or reconfiguring existing
user's infrastructure (NATs) and should be compatible with all widely-used NATs.
- We are bound to ata186 as UA. It is compatible with
this method.
Support for other UAs isn't required.
- The calls will be terminated to Cisco GWs, while
COMEDIA support
was recently added into Cisco IOS, so that theoretically the only thing we need is to add received/rport support into
proxy/registrar
and update IOS at termination points.
- No media relay is allowed, because this will create excessive
bandwith load in a single point.
- COMEDIA support is likely to become part of the
standard, so that
our investments into development are protected.
-Maxim
On Fri, Jan 10, 2003 at 02:26:07PM +0100, Jiri Kuthan wrote:
There is actually a plenty of options how to traverse NATs. Sadly, none of them works in all possible scenarios.
a) STUN -- some phones (kphone for linux, snom hardphones) have the ability to "fool" NATs to accomplish traversal using the STUN protocols; particularly good if you cannot manipulate the NAT b) geek tweaks -- you have a configurable NAT and configurable phones (there are some of both of them). you
configure static
port forwarding in the NAT and phones to advertise the public address in contacts and elsewhere c) ALG -- use a SIP-aware NAT such as PIX or Intertex d) UPnP -- takes UPnP enables phones (snom is) and NATs e) SIP/media relay -- that's a too ugly story
What to choose best depends on your network setting -- can you tweak the NAT, can you afford replacing it with a SIP-enabled one, are the phones you are using configurable or do they use STUN, do you have a server on the public or private NAT side or on each of them, etc.
I remember someone shared with us he was using ser in his network to do the translation of SIP addresses on behalf ot the phones. The ser script was configured to statically rewrite private IP addresses to the public address using replace/textops.
-Jiri
At 01:32 PM 1/10/2003, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature,
which isn't
currently present in SER. We need to allow UAs behind
NAT properly
register with the registrar - by "properly" I mean that
host:port portion
of URI in Contact field should not be used, but host:port
the request
came from should be used instead. By definition we know
that those UAs
will support symmetric SIP signalling, so that this scheme
will work just
fine.
In my opinion there are two ways to do it: either add new
rewritecontact*
family of functions similar to rewritehost ones. or add a
new flag for
the save() function. This is where I need your help -
which implementation
looks better for you (or maybe you have even some better
idea), since
we are really interested in inclusion of our changes into
the mainline to
reduce our local hacks.
Regards,
Maxim
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
On Fri, Jan 10, 2003 at 09:38:11AM -0600, Greg Fausak wrote:
Howdy,
I have a ATA186. I may have misunderstood the COMEDIA reference. Does the ATA186 poke itself through a NAT router to a SIP server???
The problem is that ATA186, while de-facto is partially COMEDIA-compatible as it always sends and receives UDP messages using the same port, i.e. 5060 for signalling and 10000 for RTP, it doesn't yet announce it to the world by inserting a blank rport parameter into the header and couldn't be configured to add direction=passive into SDP. Therefore, SIP server should be properly modified to add missed parts when it receives request from ATA.
Another problem is that those symmetric schemes don't work when both parties are behind NATs, or when the call terminates to UA which doesn't support direction=active.
-Maxim
NAT seems to be the biggest hurdle we have.
----greg
-----Original Message----- From: Maxim Sobolev [mailto:sobomax@FreeBSD.org] Sent: Friday, January 10, 2003 8:58 AM To: Greg Fausak Cc: 'Jiri Kuthan'; serusers@lists.iptel.org; kapitan@portaone.com Subject: Re: [Serusers] Rewriting URI in the Contact field
On Fri, Jan 10, 2003 at 08:27:46AM -0600, Greg Fausak wrote:
What is this device? Where can I get one? What does it cost?
Cisco ata186 is two-port analog telephone adapter, i.e. it has two standard ports for connecting ordinary phones and one 10M ethernet port. It supports SIP and H323 (G711, G723 and G729 audio codecs) and costs some US$150.
-Maxim
Thanks :-)
---greg
-----Original Message----- From: serusers-admin@lists.iptel.org [mailto:serusers-admin@lists.iptel.org] On Behalf Of Maxim Sobolev Sent: Friday, January 10, 2003 8:15 AM To: Jiri Kuthan Cc: serusers@lists.iptel.org; kapitan@portaone.com Subject: Re: [Serusers] Rewriting URI in the Contact field
Yes, I know - we have studied all those methods in details. Our method of choice is symmetric signalling/symmetric media
(aka COMEDIA)
due to the following reasons:
- Things should work without modifying or reconfiguring existing
user's infrastructure (NATs) and should be compatible with all widely-used NATs.
- We are bound to ata186 as UA. It is compatible with
this method.
Support for other UAs isn't required.
- The calls will be terminated to Cisco GWs, while
COMEDIA support
was recently added into Cisco IOS, so that theoretically the only thing we need is to add received/rport support into
proxy/registrar
and update IOS at termination points.
- No media relay is allowed, because this will create excessive
bandwith load in a single point.
- COMEDIA support is likely to become part of the
standard, so that
our investments into development are protected.
-Maxim
On Fri, Jan 10, 2003 at 02:26:07PM +0100, Jiri Kuthan wrote:
There is actually a plenty of options how to traverse NATs. Sadly, none of them works in all possible scenarios.
a) STUN -- some phones (kphone for linux, snom hardphones) have the ability to "fool" NATs to accomplish traversal using the STUN protocols; particularly good if you cannot manipulate the NAT b) geek tweaks -- you have a configurable NAT and configurable phones (there are some of both of them). you
configure static
port forwarding in the NAT and phones to advertise the public address in contacts and elsewhere c) ALG -- use a SIP-aware NAT such as PIX or Intertex d) UPnP -- takes UPnP enables phones (snom is) and NATs e) SIP/media relay -- that's a too ugly story
What to choose best depends on your network setting -- can you tweak the NAT, can you afford replacing it with a SIP-enabled one, are the phones you are using configurable or do they use STUN, do you have a server on the public or private NAT side or on each of them, etc.
I remember someone shared with us he was using ser in his network to do the translation of SIP addresses on behalf ot the phones. The ser script was configured to statically rewrite private IP addresses to the public address using replace/textops.
-Jiri
At 01:32 PM 1/10/2003, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature,
which isn't
currently present in SER. We need to allow UAs behind
NAT properly
register with the registrar - by "properly" I mean that
host:port portion
of URI in Contact field should not be used, but host:port
the request
came from should be used instead. By definition we know
that those UAs
will support symmetric SIP signalling, so that this scheme
will work just
fine.
In my opinion there are two ways to do it: either add new
rewritecontact*
family of functions similar to rewritehost ones. or add a
new flag for
the save() function. This is where I need your help -
which implementation
looks better for you (or maybe you have even some better
idea), since
we are really interested in inclusion of our changes into
the mainline to
reduce our local hacks.
Regards,
Maxim
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Does this scheme work with more than one ATA186 behind the Nat firewall?
---greg
-----Original Message----- From: Maxim Sobolev [mailto:sobomax@FreeBSD.org] Sent: Friday, January 10, 2003 11:02 AM To: Greg Fausak Cc: 'Jiri Kuthan'; serusers@lists.iptel.org; kapitan@portaone.com Subject: Re: [Serusers] Rewriting URI in the Contact field
On Fri, Jan 10, 2003 at 09:38:11AM -0600, Greg Fausak wrote:
Howdy,
I have a ATA186. I may have misunderstood the COMEDIA reference. Does the ATA186 poke itself through a NAT
router to a SIP
server???
The problem is that ATA186, while de-facto is partially COMEDIA-compatible as it always sends and receives UDP messages using the same port, i.e. 5060 for signalling and 10000 for RTP, it doesn't yet announce it to the world by inserting a blank rport parameter into the header and couldn't be configured to add direction=passive into SDP. Therefore, SIP server should be properly modified to add missed parts when it receives request from ATA.
Another problem is that those symmetric schemes don't work when both parties are behind NATs, or when the call terminates to UA which doesn't support direction=active.
-Maxim
NAT seems to be the biggest hurdle we have.
----greg
-----Original Message----- From: Maxim Sobolev [mailto:sobomax@FreeBSD.org] Sent: Friday, January 10, 2003 8:58 AM To: Greg Fausak Cc: 'Jiri Kuthan'; serusers@lists.iptel.org; kapitan@portaone.com Subject: Re: [Serusers] Rewriting URI in the Contact field
On Fri, Jan 10, 2003 at 08:27:46AM -0600, Greg Fausak wrote:
What is this device? Where can I get one? What does it cost?
Cisco ata186 is two-port analog telephone adapter, i.e. it has two standard ports for connecting ordinary phones and one 10M ethernet port. It supports SIP and H323 (G711, G723 and G729 audio codecs) and costs some US$150.
-Maxim
Thanks :-)
---greg
-----Original Message----- From: serusers-admin@lists.iptel.org [mailto:serusers-admin@lists.iptel.org] On Behalf Of Maxim Sobolev Sent: Friday, January 10, 2003 8:15 AM To: Jiri Kuthan Cc: serusers@lists.iptel.org; kapitan@portaone.com Subject: Re: [Serusers] Rewriting URI in the Contact field
Yes, I know - we have studied all those methods in
details. Our
method of choice is symmetric signalling/symmetric media
(aka COMEDIA)
due to the following reasons:
- Things should work without modifying or
reconfiguring existing
user's infrastructure (NATs) and should be compatible with all widely-used NATs.
- We are bound to ata186 as UA. It is compatible with
this method.
Support for other UAs isn't required.
- The calls will be terminated to Cisco GWs, while
COMEDIA support
was recently added into Cisco IOS, so that
theoretically the only
thing we need is to add received/rport support into
proxy/registrar
and update IOS at termination points.
- No media relay is allowed, because this will
create excessive
bandwith load in a single point.
- COMEDIA support is likely to become part of the
standard, so that
our investments into development are protected.
-Maxim
On Fri, Jan 10, 2003 at 02:26:07PM +0100, Jiri Kuthan wrote:
There is actually a plenty of options how to traverse NATs. Sadly, none of them works in all possible scenarios.
a) STUN -- some phones (kphone for linux, snom hardphones) have the ability to "fool" NATs to accomplish traversal using the STUN protocols; particularly good if you cannot manipulate the NAT b) geek tweaks -- you have a configurable NAT and
configurable
phones (there are some of both of them). you
configure static
port forwarding in the NAT and phones to advertise the public address in contacts and elsewhere c) ALG -- use a SIP-aware NAT such as PIX or Intertex d) UPnP -- takes UPnP enables phones (snom is) and NATs e) SIP/media relay -- that's a too ugly story
What to choose best depends on your network setting
-- can you
tweak the NAT, can you afford replacing it with a
SIP-enabled
one, are the phones you are using configurable or
do they use
STUN, do you have a server on the public or private NAT side or on each of them, etc.
I remember someone shared with us he was using ser in his network to do the translation of SIP addresses on behalf ot the phones. The ser script was configured to statically rewrite private IP addresses to the public address using replace/textops.
-Jiri
At 01:32 PM 1/10/2003, Maxim Sobolev wrote: >Folks, > >I need an advise on how to better implement one feature,
which isn't
>currently present in SER. We need to allow UAs behind
NAT properly
>register with the registrar - by "properly" I mean that
host:port portion
>of URI in Contact field should not be used, but host:port
the request
>came from should be used instead. By definition we know
that those UAs
>will support symmetric SIP signalling, so that this scheme
will work just
>fine. > >In my opinion there are two ways to do it: either add new
rewritecontact*
>family of functions similar to rewritehost ones. or add a
new flag for
>the save() function. This is where I need your help -
which implementation
>looks better for you (or maybe you have even some better
idea), since
>we are really interested in inclusion of our changes into
the mainline to
>reduce our local hacks. > >Regards, > >Maxim > >_______________________________________________ >Serusers mailing list >serusers@lists.iptel.org >http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Isn't the ATA able of "double registration" (see draft-ietf-sipping-nat-scenarios-00.txt) ? I thought it was able to register, look at received and rport and re-register then again with the host/port it learned from reply to the first registration.
Or does it do so only for IP address (received) and not for port numbers (rport)?
-Jiri
At 03:15 PM 1/10/2003, Maxim Sobolev wrote:
Yes, I know - we have studied all those methods in details. Our method of choice is symmetric signalling/symmetric media (aka COMEDIA) due to the following reasons:
- Things should work without modifying or reconfiguring existing
user's infrastructure (NATs) and should be compatible with all widely-used NATs.
- We are bound to ata186 as UA. It is compatible with this method.
Support for other UAs isn't required.
- The calls will be terminated to Cisco GWs, while COMEDIA support
was recently added into Cisco IOS, so that theoretically the only thing we need is to add received/rport support into proxy/registrar and update IOS at termination points.
- No media relay is allowed, because this will create excessive
bandwith load in a single point.
- COMEDIA support is likely to become part of the standard, so that
our investments into development are protected.
-Maxim
On Fri, Jan 10, 2003 at 02:26:07PM +0100, Jiri Kuthan wrote:
There is actually a plenty of options how to traverse NATs. Sadly, none of them works in all possible scenarios.
a) STUN -- some phones (kphone for linux, snom hardphones) have the ability to "fool" NATs to accomplish traversal using the STUN protocols; particularly good if you cannot manipulate the NAT b) geek tweaks -- you have a configurable NAT and configurable phones (there are some of both of them). you configure static port forwarding in the NAT and phones to advertise the public address in contacts and elsewhere c) ALG -- use a SIP-aware NAT such as PIX or Intertex d) UPnP -- takes UPnP enables phones (snom is) and NATs e) SIP/media relay -- that's a too ugly story
What to choose best depends on your network setting -- can you tweak the NAT, can you afford replacing it with a SIP-enabled one, are the phones you are using configurable or do they use STUN, do you have a server on the public or private NAT side or on each of them, etc.
I remember someone shared with us he was using ser in his network to do the translation of SIP addresses on behalf ot the phones. The ser script was configured to statically rewrite private IP addresses to the public address using replace/textops.
-Jiri
At 01:32 PM 1/10/2003, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that host:port portion of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that those UAs will support symmetric SIP signalling, so that this scheme will work just fine.
In my opinion there are two ways to do it: either add new rewritecontact* family of functions similar to rewritehost ones. or add a new flag for the save() function. This is where I need your help - which implementation looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into the mainline to reduce our local hacks.
Regards,
Maxim
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
-- Jiri Kuthan http://iptel.org/~jiri/
Jiri Kuthan wrote:
Isn't the ATA able of "double registration" (see draft-ietf-sipping-nat-scenarios-00.txt) ? I thought it was able to register, look at received and rport and re-register then again with the host/port it learned from reply to the first registration.
Or does it do so only for IP address (received) and not for port numbers (rport)?
Unfortunately it doesn't do it yet neither for received nor for rport.
-Maxim
-Jiri
At 03:15 PM 1/10/2003, Maxim Sobolev wrote:
Yes, I know - we have studied all those methods in details. Our method of choice is symmetric signalling/symmetric media (aka COMEDIA) due to the following reasons:
- Things should work without modifying or reconfiguring existing
user's infrastructure (NATs) and should be compatible with all widely-used NATs.
- We are bound to ata186 as UA. It is compatible with this method.
Support for other UAs isn't required.
- The calls will be terminated to Cisco GWs, while COMEDIA support
was recently added into Cisco IOS, so that theoretically the only thing we need is to add received/rport support into proxy/registrar and update IOS at termination points.
- No media relay is allowed, because this will create excessive
bandwith load in a single point.
- COMEDIA support is likely to become part of the standard, so that
our investments into development are protected.
-Maxim
On Fri, Jan 10, 2003 at 02:26:07PM +0100, Jiri Kuthan wrote:
There is actually a plenty of options how to traverse NATs. Sadly, none of them works in all possible scenarios.
a) STUN -- some phones (kphone for linux, snom hardphones) have the ability to "fool" NATs to accomplish traversal using the STUN protocols; particularly good if you cannot manipulate the NAT b) geek tweaks -- you have a configurable NAT and configurable phones (there are some of both of them). you configure static port forwarding in the NAT and phones to advertise the public address in contacts and elsewhere c) ALG -- use a SIP-aware NAT such as PIX or Intertex d) UPnP -- takes UPnP enables phones (snom is) and NATs e) SIP/media relay -- that's a too ugly story
What to choose best depends on your network setting -- can you tweak the NAT, can you afford replacing it with a SIP-enabled one, are the phones you are using configurable or do they use STUN, do you have a server on the public or private NAT side or on each of them, etc.
I remember someone shared with us he was using ser in his network to do the translation of SIP addresses on behalf ot the phones. The ser script was configured to statically rewrite private IP addresses to the public address using replace/textops.
-Jiri
At 01:32 PM 1/10/2003, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that host:port portion of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that those UAs will support symmetric SIP signalling, so that this scheme will work just fine.
In my opinion there are two ways to do it: either add new rewritecontact* family of functions similar to rewritehost ones. or add a new flag for the save() function. This is where I need your help - which implementation looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into the mainline to reduce our local hacks.
Regards,
Maxim
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
-- Jiri Kuthan http://iptel.org/~jiri/
At 11:14 AM 1/14/2003, Maxim Sobolev wrote:
Jiri Kuthan wrote:
Isn't the ATA able of "double registration" (see draft-ietf-sipping-nat-scenarios-00.txt) ? I thought it was able to register, look at received and rport and re-register then again with the host/port it learned from reply to the first registration.
Or does it do so only for IP address (received) and not for port numbers (rport)?
Unfortunately it doesn't do it yet neither for received nor for rport.
I think at least "received" is processed. Though not well documented, Cisco claims to have it and I heard rumours about people using it.
See http://www.cisco.com/univercd/cc/td/doc/product/voice/ata/atarn/186rn214.htm... the paragraph "Receiver-tagged VIA header".
-Jiri
Jiri Kuthan wrote:
At 11:14 AM 1/14/2003, Maxim Sobolev wrote:
Jiri Kuthan wrote:
Isn't the ATA able of "double registration" (see draft-ietf-sipping-nat-scenarios-00.txt) ? I thought it was able to register, look at received and rport and re-register then again with the host/port it learned from reply to the first registration.
Or does it do so only for IP address (received) and not for port numbers (rport)?
Unfortunately it doesn't do it yet neither for received nor for rport.
I think at least "received" is processed. Though not well documented, Cisco claims to have it and I heard rumours about people using it.
See http://www.cisco.com/univercd/cc/td/doc/product/voice/ata/atarn/186rn214.htm... the paragraph "Receiver-tagged VIA header".
Thank you for the pointer, but it seems that 2.15 version doesn't do it, at least it certainly don't try to cancel INVITE or re-register when receiving reply with `received' set. :( Perhaps for some reason the feature was removed.
But it doesn't really matter, because as long as ata (or any other UA that sends/receives from the same UDP port) is concerned, my nathelper module provides the same functionality.
-Maxim
I have been experimenting with the Intertex IX66. It is a NAT device. It is much more expensive than something like a BEFSR41 (Linksys). It does to the job without any configuration. It rewrites SIP packets on the way through the router.
One drawback is you must buy licenses for the number of SIP devices you intend to use.
---greg
-----Original Message----- From: serusers-admin@lists.iptel.org [mailto:serusers-admin@lists.iptel.org] On Behalf Of Jiri Kuthan Sent: Friday, January 10, 2003 7:26 AM To: Maxim Sobolev; serusers@lists.iptel.org Cc: kapitan@portaone.com Subject: Re: [Serusers] Rewriting URI in the Contact field
There is actually a plenty of options how to traverse NATs. Sadly, none of them works in all possible scenarios.
a) STUN -- some phones (kphone for linux, snom hardphones) have the ability to "fool" NATs to accomplish traversal using the STUN protocols; particularly good if you cannot manipulate the NAT b) geek tweaks -- you have a configurable NAT and configurable phones (there are some of both of them). you configure static port forwarding in the NAT and phones to advertise the public address in contacts and elsewhere c) ALG -- use a SIP-aware NAT such as PIX or Intertex d) UPnP -- takes UPnP enables phones (snom is) and NATs e) SIP/media relay -- that's a too ugly story
What to choose best depends on your network setting -- can you tweak the NAT, can you afford replacing it with a SIP-enabled one, are the phones you are using configurable or do they use STUN, do you have a server on the public or private NAT side or on each of them, etc.
I remember someone shared with us he was using ser in his network to do the translation of SIP addresses on behalf ot the phones. The ser script was configured to statically rewrite private IP addresses to the public address using replace/textops.
-Jiri
At 01:32 PM 1/10/2003, Maxim Sobolev wrote:
Folks,
I need an advise on how to better implement one feature, which isn't currently present in SER. We need to allow UAs behind NAT properly register with the registrar - by "properly" I mean that
host:port portion
of URI in Contact field should not be used, but host:port the request came from should be used instead. By definition we know that
those UAs
will support symmetric SIP signalling, so that this scheme
will work just
fine.
In my opinion there are two ways to do it: either add new
rewritecontact*
family of functions similar to rewritehost ones. or add a
new flag for
the save() function. This is where I need your help - which
implementation
looks better for you (or maybe you have even some better idea), since we are really interested in inclusion of our changes into
the mainline to
reduce our local hacks.
Regards,
Maxim
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers