diff --git a/ChangeLog b/ChangeLog index 100d55d..72e624c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,8 @@ 0.5.3 ===== - 31-Jan-2004: - Allow 2 of my vias in header to let 2 UA's sitting + 1-Feb-2004: - Added handling of Max-Forwards header + - a detected via loop results in an 482 Loop detected + 31-Jan-2004: - Allow 2 of my vias in header to let 2 UA's sitting behind the same siproxd to have conversation together UA1 -->--\ /-->--\ siproxd Registrar @@ -22,19 +24,20 @@ 0.5.2 ===== - 30-Jan-2004: - If RTP proxy is disabled, don't rewrite incomming + 31-Jan-2004: - Released 0.5.2 + 30-Jan-2004: - If RTP proxy is disabled, don't rewrite incomming SDP bodies (patch from Robert Högberg) - 29-Jan-2004: - new doc/RFC3261_compliance.txt and comments in the + 29-Jan-2004: - new doc/RFC3261_compliance.txt and comments in the code that refer to the RFC. - 28-Jan-2004: - don't die on INVITE requests that include no Contact + 28-Jan-2004: - don't die on INVITE requests that include no Contact header - which is legal. (patch from Robert Högberg) - RTP proxy: don't try to forward empty RTP packets - renamed some variables of rtp_proxytable_t to make better sense (changed meaning in fullduplex RTP proxy) - 27-Jan-2004: - added doc/KNOWN_BUGS + 27-Jan-2004: - added doc/KNOWN_BUGS - better branch parameter calculation (via header), now honors RFC3261 for stateless proxies (section 16.11) - - SIP request: remove a Route-header pointing to myself. + - SIP request: remove a Route header pointing to myself. This was an issue with Linphone 0.12.1. (patch from Robert Högberg). - removed IPCHAINS & IPTABLES (netfilter) proxy support @@ -42,13 +45,13 @@ use one single port (and socket) on each side (inbound/ outbound) to send and receive RTP traffic for every active stream (patch from Christof Meerwald). - 22-Jan-2004: - ./configure option: --enable-static to build + 22-Jan-2004: - ./configure option: --enable-static to build a completely statically linked executable - REGISTER honors the expires parameter of the contact header - Contact header of REGISTER response must be rewritten back to the local (true) URL - 18-Jan-2004: - security_check_raw: + 18-Jan-2004: - security_check_raw: size check: >= 16 bytes - at exit, check registration file to be writable - no WARNING if SIP user-agent header is not supplied. @@ -57,6 +60,7 @@ 0.5.1 ===== + 22-Dec-2003: - Released 0.5.1 21-Dec-2003: - possibility to log call establishment 17-Dec-2003: - full duplex RTP proxy (many thanks to Chris Ross for his work on this). Up to now, only the RTP *Relay* diff --git a/TODO b/TODO index 67e6ba1..38a491d 100644 --- a/TODO +++ b/TODO @@ -22,31 +22,9 @@ TODOs, in random order: - via loop detection: send 482 error code -- support Record-Route header - feature: don't bind to 0.0.0.0 address, but only to inbound/outbound IF's (defined by IFNAME) -- support IPTABLES for RTP proxying -> use libiptc - -- rtpproxy_masq: - RACE CONDITIONS! A slot may be timed out, even if the actual - masquerading tunnel is still active. A following new - INVITE then tries to use the believed free port -> Buh - - Can we poll (/proc/something) to figure out if the tunnel - is still active before deleting? This would require knowledge - of the text layout in /proc/xxx. - - are there some other possibilities (netfilter/libiptc)? - - introduce some kind of connection STATE to the proxy table. - Timeout based discarding only is active for non-established. - An INVITE would set the STATE to CONNECTING, the following - ACK to CONNECTED. A CONNECTED entry can only be deleted by - a BYE or CANCEL. - - NETFILTER: during startup (RTP initialization) siproxd should - clean left over entries that are within the RTP proxy port range -- NETFILTER masquerading: - it looks like it is not possible to create an entry in the - ip_conntrack table from an userspace application. How do we proceed? - We just might insert/delete DNAT entries in the PREROUTE NAT table. - This the means that we must implement a connection state into the - proxy table. +RFC3261 non-compliance: +- Record-Route header handling diff --git a/doc/RFC3261_compliance.txt b/doc/RFC3261_compliance.txt index 0996489..5cc6315 100644 --- a/doc/RFC3261_compliance.txt +++ b/doc/RFC3261_compliance.txt @@ -9,19 +9,19 @@ Request Processing: implemented Section 16.3: Request Validation 1. Reasonable Syntax yes 2. URI scheme no - 3. Max-Forwards no - 4. (Optional) Loop Detection partially + 3. Max-Forwards yes + 4. (Optional) Loop Detection yes 5. Proxy-Require no 6. Proxy-Authorization yes - Section 16.4 Route Information Preprocessing yes + Section 16.4 Route Information Preprocessing partially Section 16.5 Determining Request Targets no Section 16.6 Request Forwarding 1. Make a copy of the received request yes 2. Update the Request-URI yes - 3. Update the Max-Forwards header field no + 3. Update the Max-Forwards header field yes 4. Optionally add a Record-route header field value no 5. Optionally add additional header fields no 6. Postprocess routing information no @@ -37,7 +37,7 @@ Response Processing: implemented 1. Reasonable Syntax yes 2. URI scheme no 3. Max-Forwards no - 4. (Optional) Loop Detection partially + 4. (Optional) Loop Detection yes 5. Proxy-Require no 6. Proxy-Authorization yes diff --git a/src/proxy.c b/src/proxy.c index d676731..4b14091 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -91,47 +91,26 @@ int proxy_request (osip_message_t *request, struct sockaddr_in *from) { DEBUGC(DBCLASS_PROXY,"proxy_request"); - /* - * RFC 3261, Section 16.6 step 1 - * Proxy Behavior - Request Forwarding - Make a copy - */ - /* nothing to do here, copy is ready in 'request'*/ /* - * RFC 3261, Section 16.6 step 3 - * Proxy Behavior - Request Forwarding - Max-Forwards - * (if Max-Forward header exists, decrement by one, if it does not - * exist, add a new one with value SHOULD be 70) - */ - /* NOT IMPLEMENTED */ - - /* - * RFC 3261, Section 16.6 step 4 - * Proxy Behavior - Request Forwarding - Add a Record-route header - */ - /* NOT IMPLEMENTED (optional) */ - - /* - * RFC 3261, Section 16.6 step 5 - * Proxy Behavior - Request Forwarding - Add Additional Header Fields - */ - /* NOT IMPLEMENTED (optional) */ - - /* - * RFC 3261, Section 16.6 step 6 - * Proxy Behavior - Request Forwarding - Postprocess routing information - */ - /* NOT IMPLEMENTED */ - - - /* - * RFC 3261, Section 16.4 + * RFC 3261, Section 16.4 * Proxy Behavior - Route Information Preprocessing - * (process Record-Route header) + * (process Route header) */ +/* + The proxy MUST inspect the Request-URI of the request. If the + Request-URI of the request contains a value this proxy previously + placed into a Record-Route header field (see Section 16.6 item 4), + the proxy MUST replace the Request-URI in the request with the last + value from the Route header field, and remove that value from the + Route header field. The proxy MUST then proceed as if it received + this modified request. + + NOT IMPLEMENTED*/ /* * Check if I am listed at the topmost Route header (if any Route - * header is existing at all). If so, remove it from the list. + * header is existing at all). If so, remove it from the list and + * rewrite the request URI to point to the now topmost Route. */ if (request->routes && !osip_list_eol(request->routes, 0)) { struct in_addr addr1, addr2, addr3; @@ -151,7 +130,8 @@ int proxy_request (osip_message_t *request, struct sockaddr_in *from) { configuration.sip_listen_port == SIP_PORT)) { osip_list_remove(request->routes, 0); osip_route_free(route); - /* possibly request->routes will be freed by osip_message_free() */ + /* request->routes will be freed by osip_message_free() */ + DEBUGC(DBCLASS_PROXY,"removed Route header pointing to myself"); } } @@ -298,6 +278,11 @@ int proxy_request (osip_message_t *request, struct sockaddr_in *from) { } /* log_calls */ + /* + * RFC 3261, Section 16.6 step 1 + * Proxy Behavior - Request Forwarding - Make a copy + */ + /* nothing to do here, copy is ready in 'request'*/ /* get destination address */ url=osip_message_get_uri(request); @@ -321,17 +306,6 @@ int proxy_request (osip_message_t *request, struct sockaddr_in *from) { proxy_rewrite_request_uri(request, i); } - /* - * RFC 3261, Section 16.6 step 8 - * Proxy Behavior - Add a Via header field value - */ - /* add my Via header line (inbound interface)*/ - sts = sip_add_myvia(request, IF_INBOUND); - if (sts == STS_FAILURE) { - ERROR("adding my inbound via failed!"); - return STS_FAILURE; - } - /* if this is CANCEL/BYE request, stop RTP proxying */ if (MSG_IS_BYE(request) || MSG_IS_CANCEL(request)) { /* stop the RTP proxying stream(s) */ @@ -356,7 +330,7 @@ int proxy_request (osip_message_t *request, struct sockaddr_in *from) { /* * from the internal masqueraded host to an external host */ - case REQTYP_OUTGOING: + case REQTYP_OUTGOING: DEBUGC(DBCLASS_PROXY,"outgoing request from %s@%s from inbound", request->from->url->username? request->from->url->username:"*NULL*", request->from->url->host? request->from->url->host: "*NULL*"); @@ -371,6 +345,9 @@ int proxy_request (osip_message_t *request, struct sockaddr_in *from) { /* if it is addressed to myself, then it must be some request * method that I as a proxy do not support. Reject */ #if 0 +/* careful - an internal UA might send an request to another internal UA. + This would be caught here, so don't do this. This situation should be + caught in the default part of the CASE statement below */ if (is_sipuri_local(request) == STS_TRUE) { WARN("unsupported request [%s] directed to proxy from %s@%s -> %s@%s", request->sip_method? request->sip_method:"*NULL*", @@ -393,16 +370,6 @@ int proxy_request (osip_message_t *request, struct sockaddr_in *from) { /* rewrite Contact header to represent the masqued address */ sip_rewrite_contact(request, DIR_OUTGOING); - /* - * RFC 3261, Section 16.8 - * Proxy Behavior - Add a Via header field value - */ - /* add my Via header line (outbound interface)*/ - sts = sip_add_myvia(request, IF_OUTBOUND); - if (sts == STS_FAILURE) { - ERROR("adding my outbound via failed!"); - } - /* if this is CANCEL/BYE request, stop RTP proxying */ if (MSG_IS_BYE(request) || MSG_IS_CANCEL(request)) { /* stop the RTP proxying stream(s) */ @@ -438,10 +405,119 @@ int proxy_request (osip_message_t *request, struct sockaddr_in *from) { return STS_FAILURE; } - /* - * RFC 3261, Section 16.6 step 7 - * Proxy Behavior - Determine Next-Hop Address - */ + + /* + * RFC 3261, Section 16.6 step 3 + * Proxy Behavior - Request Forwarding - Max-Forwards + * (if Max-Forwards header exists, decrement by one, if it does not + * exist, add a new one with value SHOULD be 70) + */ + { + osip_header_t *max_forwards; + int forwards_count = DEFAULT_MAXFWD; + char mfwd[8]; + + osip_message_get_max_forwards(request, 0, &max_forwards); + if (max_forwards == NULL) { + sprintf(mfwd, "%i", forwards_count); + osip_message_set_max_forwards(request, mfwd); + } else { + if (max_forwards->hvalue) { + forwards_count = atoi(max_forwards->hvalue); + forwards_count -=1; + osip_free (max_forwards->hvalue); + } + + sprintf(mfwd, "%i", forwards_count); + max_forwards->hvalue = osip_strdup(mfwd); + } + + DEBUGC(DBCLASS_PROXY,"setting Max-Forwards=%s",mfwd); + } + + /* + * RFC 3261, Section 16.6 step 4 + * Proxy Behavior - Request Forwarding - Add a Record-route header + */ +#if 0 +/* NOT IMPLEMENTED - this requires proper implementation of + the Route headers first. */ + { + struct in_addr addr; + osip_record_route_t *r_route; + osip_uri_t *uri_of_proxy; + + /* + * get the IP address of the interface where I'm going to + * send out this request + */ + switch (type) { + case REQTYP_INCOMING: + sts = get_ip_by_ifname(configuration.inbound_if, &addr); + if (sts == STS_FAILURE) { + ERROR("can't find inbound interface %s - configuration error?", + configuration.outbound_if); + return STS_FAILURE; + } + break; + case REQTYP_OUTGOING: + sts = get_ip_by_ifname(configuration.outbound_if, &addr); + if (sts == STS_FAILURE) { + ERROR("can't find outbound interface %s - configuration error?", + configuration.outbound_if); + return STS_FAILURE; + } + break; + default: + ERROR("Oops, never should end up here (type=%i)", type); + return STS_FAILURE; + } + + sts = osip_record_route_init(&r_route); + if (sts == 0) { + sts = osip_uri_init(&uri_of_proxy); + if (sts == 0) { + char tmp[8]; + + /* host name / IP */ + osip_uri_set_host(uri_of_proxy, osip_strdup(utils_inet_ntoa(addr))); + + /* port number */ + sprintf(tmp, "%i", configuration.sip_listen_port); + osip_uri_set_port(uri_of_proxy, osip_strdup(tmp)); + + /* 'lr' parameter */ + osip_uri_uparam_add(uri_of_proxy, "lr", NULL); + + osip_record_route_set_url(r_route, uri_of_proxy); + + /* insert before all other record-route */ + osip_list_add (request->record_routes, r_route, 0); + } else { + osip_record_route_free (r_route); + osip_free (r_route); + } /* if url_init */ + } /* if record route init */ + + } +#endif + + /* + * RFC 3261, Section 16.6 step 5 + * Proxy Behavior - Request Forwarding - Add Additional Header Fields + */ + /* NOT IMPLEMENTED (optional) */ + + + /* + * RFC 3261, Section 16.6 step 6 + * Proxy Behavior - Request Forwarding - Postprocess routing information + */ + + /* + * RFC 3261, Section 16.6 step 7 + * Proxy Behavior - Determine Next-Hop Address + */ if ((type == REQTYP_OUTGOING) && (configuration.outbound_proxy_host)) { /* I have an outbound proxy configured */ sts = get_ip_by_host(configuration.outbound_proxy_host, &sendto_addr); @@ -472,6 +548,23 @@ int proxy_request (osip_message_t *request, struct sockaddr_in *from) { } } + /* + * RFC 3261, Section 16.6 step 8 + * Proxy Behavior - Add a Via header field value + */ + /* add my Via header line (outbound interface)*/ + if (type == REQTYP_INCOMING) { + sts = sip_add_myvia(request, IF_INBOUND); + if (sts == STS_FAILURE) { + ERROR("adding my inbound via failed!"); + } + } else { + sts = sip_add_myvia(request, IF_OUTBOUND); + if (sts == STS_FAILURE) { + ERROR("adding my outbound via failed!"); + return STS_FAILURE; + } + } /* * RFC 3261, Section 16.6 step 9 * Proxy Behavior - Add a Content-Length header field if necessary diff --git a/src/register.c b/src/register.c index 0190826..33c0aa1 100644 --- a/src/register.c +++ b/src/register.c @@ -215,10 +215,9 @@ int register_client(osip_message_t *my_msg, int force_lcl_masq) { * look for an Contact expires parameter - in case of REGISTER * these two are equal. The Contact expires has higher priority! */ - osip_contact_param_get_byname( (osip_contact_t*) my_msg->contacts->node->element, - "expires", &expires_param); + EXPIRES, &expires_param); if (expires_param && expires_param->gvalue) { /* get expires from contact Header */ diff --git a/src/siproxd.c b/src/siproxd.c index 13115f0..5083429 100644 --- a/src/siproxd.c +++ b/src/siproxd.c @@ -294,9 +294,25 @@ int main (int argc, char *argv[]) /* * RFC 3261, Section 16.3 step 3 * Proxy Behavior - Request Validation - Max-Forwards check - * (check Max-Forward header and refuse with 483 if too many hops) + * (check Max-Forwards header and refuse with 483 if too many hops) */ - /* NOT IMPLEMENTED */ + { + osip_header_t *max_forwards; + int forwards_count = DEFAULT_MAXFWD; + + osip_message_get_max_forwards(my_msg, 0, &max_forwards); + if (max_forwards && max_forwards->hvalue) { + forwards_count = atoi(max_forwards->hvalue); + } + + DEBUGC(DBCLASS_PROXY,"checking Max-Forwards (=%i)",forwards_count); + if (forwards_count <= 0) { + DEBUGC(DBCLASS_SIP, "Forward count reached 0 -> 483 response"); + sip_gen_response(my_msg, 483 /*Too many hops*/); + goto end_loop; /* skip and free resources */ + } + + } /* * RFC 3261, Section 16.3 step 4 @@ -304,8 +320,8 @@ int main (int argc, char *argv[]) * (check for loop and return 482 if a loop is detected) */ if (check_vialoop(my_msg) == STS_TRUE) { - DEBUGC(DBCLASS_PROXY,"via loop detected, ignoring request"); - /* we should return 482, NOT IMPLEMENTED */ + DEBUGC(DBCLASS_SIP,"via loop detected, ignoring request"); + sip_gen_response(my_msg, 482 /*Loop detected*/); goto end_loop; /* skip and free resources */ } diff --git a/src/siproxd.h b/src/siproxd.h index d8f52e9..a7ae8f2 100644 --- a/src/siproxd.h +++ b/src/siproxd.h @@ -156,7 +156,8 @@ struct siproxd_config { /* * some constant definitions */ -#define SIP_PORT 5060 +#define SIP_PORT 5060 /* default port to listen */ +#define DEFAULT_MAXFWD 70 /* default Max-Forward count */ #define URLMAP_SIZE 32 /* number of URL mapping table entries */ #define RTPPROXY_SIZE 64 /* number of rtp proxy entries */