1 /* -*- Mode: C; c-basic-offset: 3 -*-
2 Copyright (C) 2002-2005 Thomas Ries <tries@gmx.net>
4 This file is part of Siproxd.
6 Siproxd is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 Siproxd is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Siproxd; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <sys/types.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
32 #include <osipparser2/osip_parser.h>
33 #include <osipparser2/sdp_message.h>
38 static char const ident[]="$Id: proxy.c,v 1.80 2005/01/24 19:12:40 hb9xar Exp $";
40 /* configuration storage */
41 extern struct siproxd_config configuration; /* defined in siproxd.c */
43 extern struct urlmap_s urlmap[]; /* URL mapping table */
44 extern struct lcl_if_s local_addresses;
51 * STS_SUCCESS on success
52 * STS_FAILURE on error
55 * Section 16.3: Proxy Behavior - Request Validation
56 * 1. Reasonable Syntax
59 * 4. (Optional) Loop Detection
61 * 6. Proxy-Authorization
63 * Section 16.6: Proxy Behavior - Request Forwarding
64 * 1. Make a copy of the received request
65 * 2. Update the Request-URI
66 * 3. Update the Max-Forwards header field
67 * 4. Optionally add a Record-route header field value
68 * 5. Optionally add additional header fields
69 * 6. Postprocess routing information
70 * 7. Determine the next-hop address, port, and transport
71 * 8. Add a Via header field value
72 * 9. Add a Content-Length header field if necessary
73 * 10. Forward the new request
76 int proxy_request (sip_ticket_t *ticket) {
80 struct in_addr sendto_addr;
84 osip_message_t *request;
85 struct sockaddr_in *from;
86 struct in_addr local_ip;
88 DEBUGC(DBCLASS_PROXY,"proxy_request");
91 ERROR("proxy_request: called with NULL ticket");
95 request=ticket->sipmsg;
99 * RFC 3261, Section 16.4
100 * Proxy Behavior - Route Information Preprocessing
101 * (process Route header)
103 route_preprocess(ticket);
106 * figure out whether this is an incoming or outgoing request
107 * by doing a lookup in the registration table.
109 #define _OLD_DIRECTION_EVALUATION 0
110 #if _OLD_DIRECTION_EVALUATION
112 for (i=0; i<URLMAP_SIZE; i++) {
113 if (urlmap[i].active == 0) continue;
115 /* incoming request ('to' == 'masq') || (('to' == 'reg') && !REGISTER)*/
116 if ((compare_url(request->to->url, urlmap[i].masq_url)==STS_SUCCESS) ||
117 (!MSG_IS_REGISTER(request) &&
118 (compare_url(request->to->url, urlmap[i].reg_url)==STS_SUCCESS))) {
119 type=REQTYP_INCOMING;
120 DEBUGC(DBCLASS_PROXY,"incoming request from %s@%s from outbound",
121 request->from->url->username? request->from->url->username:"*NULL*",
122 request->from->url->host? request->from->url->host: "*NULL*");
126 /* outgoing request ('from' == 'reg') */
127 if (compare_url(request->from->url, urlmap[i].reg_url)==STS_SUCCESS) {
128 type=REQTYP_OUTGOING;
129 DEBUGC(DBCLASS_PROXY,"outgoing request from %s@%s from inbound",
130 request->from->url->username? request->from->url->username:"*NULL*",
131 request->from->url->host? request->from->url->host: "*NULL*");
138 * did I receive the telegram from a REGISTERED host?
139 * -> it must be an OUTGOING request
141 for (i=0; i<URLMAP_SIZE; i++) {
142 struct in_addr tmp_addr;
144 if (urlmap[i].active == 0) continue;
145 if (get_ip_by_host(urlmap[i].true_url->host, &tmp_addr) == STS_FAILURE) {
146 DEBUGC(DBCLASS_PROXY, "proxy_request: cannot resolve host [%s]",
149 DEBUGC(DBCLASS_PROXY, "proxy_request: reghost:%s ip:%s",
150 urlmap[i].true_url->host, utils_inet_ntoa(from->sin_addr));
151 if (memcmp(&tmp_addr, &from->sin_addr, sizeof(tmp_addr)) == 0) {
152 type=REQTYP_OUTGOING;
159 * is the telegram directed to an internally registered host?
160 * -> it must be an INCOMING request
163 for (i=0; i<URLMAP_SIZE; i++) {
164 if (urlmap[i].active == 0) continue;
166 * 'To' contains a display name (Bob) and a SIP or SIPS URI
167 * (sip:bob@biloxi.com) towards which the request was originally
168 * directed. Display names are described in RFC 2822 [3].
171 /* So this means, that we must check the SIP URI supplied with the
172 * INVITE method, as this points to the real wanted target.
173 * Q: does there exist a situation where the SIP URI itself does
174 * point to "somewhere" but the To: points to the correct UA?
175 * So for now, we just look at both of them (SIP URI and To: header)
178 /* incoming request (SIP URI == 'masq') || ((SIP URI == 'reg') && !REGISTER)*/
179 if ((compare_url(request->req_uri, urlmap[i].masq_url)==STS_SUCCESS) ||
180 (!MSG_IS_REGISTER(request) &&
181 (compare_url(request->req_uri, urlmap[i].reg_url)==STS_SUCCESS))) {
182 type=REQTYP_INCOMING;
185 /* incoming request ('to' == 'masq') || (('to' == 'reg') && !REGISTER)*/
186 if ((compare_url(request->to->url, urlmap[i].masq_url)==STS_SUCCESS) ||
187 (!MSG_IS_REGISTER(request) &&
188 (compare_url(request->to->url, urlmap[i].reg_url)==STS_SUCCESS))) {
189 type=REQTYP_INCOMING;
195 ticket->direction=type;
198 * logging of passing calls
200 if (configuration.log_calls) {
201 osip_uri_t *cont_url = NULL;
202 if (!osip_list_eol(request->contacts, 0))
203 cont_url = ((osip_contact_t*)(request->contacts->node->element))->url;
206 if (MSG_IS_INVITE(request)) {
208 INFO("%s Call from: %s@%s",
209 (type==REQTYP_INCOMING) ? "Incoming":"Outgoing",
210 cont_url->username ? cont_url->username:"*NULL*",
211 cont_url->host ? cont_url->host : "*NULL*");
213 INFO("%s Call (w/o contact header) from: %s@%s",
214 (type==REQTYP_INCOMING) ? "Incoming":"Outgoing",
215 request->from->url->username ?
216 request->from->url->username:"*NULL*",
217 request->from->url->host ?
218 request->from->url->host : "*NULL*");
221 } else if (MSG_IS_BYE(request) || MSG_IS_CANCEL(request)) {
223 INFO("Ending Call from: %s@%s",
224 cont_url->username ? cont_url->username:"*NULL*",
225 cont_url->host ? cont_url->host : "*NULL*");
227 INFO("Ending Call (w/o contact header) from: %s@%s",
228 request->from->url->username ?
229 request->from->url->username:"*NULL*",
230 request->from->url->host ?
231 request->from->url->host : "*NULL*");
238 * RFC 3261, Section 16.6 step 1
239 * Proxy Behavior - Request Forwarding - Make a copy
241 /* nothing to do here, copy is ready in 'request'*/
243 /* get destination address */
244 url=osip_message_get_uri(request);
246 /* Determine local address to use based on the To: address */
247 get_local_ip(request->to->url->host, &local_ip);
248 DEBUGC(DBCLASS_PROXY, "local IP: %s", inet_ntoa(local_ip));
252 * from an external host to the internal masqueraded host
254 case REQTYP_INCOMING:
255 DEBUGC(DBCLASS_PROXY,"incoming request from %s@%s from outbound",
256 request->from->url->username? request->from->url->username:"*NULL*",
257 request->from->url->host? request->from->url->host: "*NULL*");
260 * RFC 3261, Section 16.6 step 2
261 * Proxy Behavior - Request Forwarding - Request-URI
262 * (rewrite request URI to point to the real host)
264 /* 'i' still holds the valid index into the URLMAP table */
265 if (check_rewrite_rq_uri(request) == STS_TRUE) {
266 proxy_rewrite_request_uri(request, i);
269 /* if this is CANCEL/BYE request, stop RTP proxying */
270 if (MSG_IS_BYE(request) || MSG_IS_CANCEL(request)) {
271 /* stop the RTP proxying stream(s) */
272 rtp_stop_fwd(osip_message_get_call_id(request), DIR_INCOMING);
273 rtp_stop_fwd(osip_message_get_call_id(request), DIR_OUTGOING);
275 /* check for incoming request */
276 } else if (MSG_IS_INVITE(request)) {
277 /* rewrite the body */
278 if (configuration.rtp_proxy_enable == 1) {
279 sts = proxy_rewrite_invitation_body(request, DIR_INCOMING, NULL);
281 } else if (MSG_IS_ACK(request)) {
282 /* rewrite the body */
283 sts = proxy_rewrite_invitation_body(request, DIR_INCOMING, NULL);
288 * from the internal masqueraded host to an external host
290 case REQTYP_OUTGOING:
291 DEBUGC(DBCLASS_PROXY,"outgoing request from %s@%s from inbound",
292 request->from->url->username? request->from->url->username:"*NULL*",
293 request->from->url->host? request->from->url->host: "*NULL*");
296 * RFC 3261, Section 16.6 step 2
297 * Proxy Behavior - Request Forwarding - Request-URI
299 /* nothing to do for an outgoing request */
302 /* if it is addressed to myself, then it must be some request
303 * method that I as a proxy do not support. Reject */
305 /* careful - an internal UA might send an request to another internal UA.
306 This would be caught here, so don't do this. This situation should be
307 caught in the default part of the CASE statement below */
308 if (is_sipuri_local(ticket) == STS_TRUE) {
309 WARN("unsupported request [%s] directed to proxy from %s@%s -> %s@%s",
310 request->sip_method? request->sip_method:"*NULL*",
311 request->from->url->username? request->from->url->username:"*NULL*",
312 request->from->url->host? request->from->url->host : "*NULL*",
313 url->username? url->username : "*NULL*",
314 url->host? url->host : "*NULL*");
316 sip_gen_response(ticket, 403 /*forbidden*/);
322 /* rewrite Contact header to represent the masqued address */
323 sip_rewrite_contact(ticket, DIR_OUTGOING, &local_ip);
325 /* if an INVITE, rewrite body */
326 if (MSG_IS_INVITE(request)) {
327 sts = proxy_rewrite_invitation_body(request, DIR_OUTGOING, &local_ip);
328 } else if (MSG_IS_ACK(request)) {
329 sts = proxy_rewrite_invitation_body(request, DIR_OUTGOING, &local_ip);
332 /* if this is CANCEL/BYE request, stop RTP proxying */
333 if (MSG_IS_BYE(request) || MSG_IS_CANCEL(request)) {
334 /* stop the RTP proxying stream(s) */
335 rtp_stop_fwd(osip_message_get_call_id(request), DIR_INCOMING);
336 rtp_stop_fwd(osip_message_get_call_id(request), DIR_OUTGOING);
342 DEBUGC(DBCLASS_PROXY, "request [%s] from/to unregistered UA "
343 "(RQ: %s@%s -> %s@%s)",
344 request->sip_method? request->sip_method:"*NULL*",
345 request->from->url->username? request->from->url->username:"*NULL*",
346 request->from->url->host? request->from->url->host : "*NULL*",
347 url->username? url->username : "*NULL*",
348 url->host? url->host : "*NULL*");
351 * we may end up here for two reasons:
352 * 1) An incomming request (from outbound) that is directed to
353 * an unknown (not registered) local UA
354 * 2) an outgoing request from a local UA that is not registered.
356 * Case 1) we should probably answer with "404 Not Found",
357 * case 2) more likely a "403 Forbidden"
359 * How about "408 Request Timeout" ?
362 sip_gen_response(ticket, 408 /* Request Timeout */);
369 * RFC 3261, Section 16.6 step 3
370 * Proxy Behavior - Request Forwarding - Max-Forwards
371 * (if Max-Forwards header exists, decrement by one, if it does not
372 * exist, add a new one with value SHOULD be 70)
375 osip_header_t *max_forwards;
376 int forwards_count = DEFAULT_MAXFWD;
379 osip_message_get_max_forwards(request, 0, &max_forwards);
380 if (max_forwards == NULL) {
381 sprintf(mfwd, "%i", forwards_count);
382 osip_message_set_max_forwards(request, mfwd);
384 if (max_forwards->hvalue) {
385 forwards_count = atoi(max_forwards->hvalue);
387 osip_free (max_forwards->hvalue);
390 sprintf(mfwd, "%i", forwards_count);
391 max_forwards->hvalue = osip_strdup(mfwd);
394 DEBUGC(DBCLASS_PROXY,"setting Max-Forwards=%s",mfwd);
398 * RFC 3261, Section 16.6 step 4
399 * Proxy Behavior - Request Forwarding - Add a Record-route header
403 * for ALL incoming requests, include my Record-Route header.
404 * The local UA will probably send its answer to the topmost
405 * Route Header (8.1.2 of RFC3261)
407 if (type == REQTYP_INCOMING) {
408 DEBUGC(DBCLASS_PROXY,"Adding my Record-Route");
409 route_add_recordroute(ticket);
412 * outgoing packets must not have my record route header, as
413 * this likely will contain a private IP address (my inbound).
415 DEBUGC(DBCLASS_PROXY,"Purging Record-Routes (outgoing packet)");
416 route_purge_recordroute(ticket);
420 * RFC 3261, Section 16.6 step 5
421 * Proxy Behavior - Request Forwarding - Add Additional Header Fields
423 /* NOT IMPLEMENTED (optional) */
427 * RFC 3261, Section 16.6 step 6
428 * Proxy Behavior - Request Forwarding - Postprocess routing information
430 * If the copy contains a Route header field, the proxy MUST
431 * inspect the URI in its first value. If that URI does not
432 * contain an lr parameter, the proxy MUST modify the copy as
435 * - The proxy MUST place the Request-URI into the Route header
436 * field as the last value.
438 * - The proxy MUST then place the first Route header field value
439 * into the Request-URI and remove that value from the Route
443 route_postprocess(ticket);
447 * RFC 3261, Section 16.6 step 7
448 * Proxy Behavior - Determine Next-Hop Address
450 /*&&&& priority probably should be:
452 * 2) fixed outbound proxy
456 * fixed or domain outbound proxy defined ?
458 if ((type == REQTYP_OUTGOING) &&
459 (sip_find_outbound_proxy(ticket, &sendto_addr, &port) == STS_SUCCESS)) {
460 DEBUGC(DBCLASS_PROXY, "proxy_request: have outbound proxy %s:%i",
461 utils_inet_ntoa(sendto_addr), port);
464 * If so, fetch address from topmost Route: header and remove it.
466 } else if ((type == REQTYP_OUTGOING) &&
467 (request->routes && !osip_list_eol(request->routes, 0))) {
468 sts=route_determine_nexthop(ticket, &sendto_addr, &port);
469 if (sts == STS_FAILURE) {
470 DEBUGC(DBCLASS_PROXY, "proxy_request: route_determine_nexthop failed");
473 DEBUGC(DBCLASS_PROXY, "proxy_request: have Route header to %s:%i",
474 utils_inet_ntoa(sendto_addr), port);
476 * destination from SIP URI
479 /* get the destination from the SIP URI */
480 sts = get_ip_by_host(url->host, &sendto_addr);
481 if (sts == STS_FAILURE) {
482 DEBUGC(DBCLASS_PROXY, "proxy_request: cannot resolve URI [%s]",
488 port=atoi(url->port);
492 DEBUGC(DBCLASS_PROXY, "proxy_request: have SIP URI to %s:%i",
497 * RFC 3261, Section 16.6 step 8
498 * Proxy Behavior - Add a Via header field value
500 /* add my Via header line (outbound interface)*/
501 if (type == REQTYP_INCOMING) {
502 sts = sip_add_myvia(ticket, IF_INBOUND, NULL);
503 if (sts == STS_FAILURE) {
504 ERROR("adding my inbound via failed!");
507 sts = sip_add_myvia(ticket, IF_OUTBOUND, &local_ip);
508 if (sts == STS_FAILURE) {
509 ERROR("adding my outbound via failed!");
514 * RFC 3261, Section 16.6 step 9
515 * Proxy Behavior - Add a Content-Length header field if necessary
517 /* not necessary, already in message and we do not support TCP */
520 * RFC 3261, Section 16.6 step 10
521 * Proxy Behavior - Forward the new request
523 sts = osip_message_to_str(request, &buffer);
525 ERROR("proxy_request: osip_message_to_str failed");
529 sipsock_send(sendto_addr, port, ticket->protocol,
530 buffer, strlen(buffer));
534 * RFC 3261, Section 16.6 step 11
535 * Proxy Behavior - Set timer C
537 /* NOT IMPLEMENTED - does this really apply for stateless proxies? */
547 * STS_SUCCESS on success
548 * STS_FAILURE on error
550 * Section 16.7: Proxy Behavior - Response Processing
551 * 1. Find the appropriate response context
552 * 2. Update timer C for provisional responses
553 * 3. Remove the topmost Via
554 * 4. Add the response to the response context
555 * 5. Check to see if this response should be forwarded immediately
556 * 6. When necessary, choose the best final response from the
558 * 7. Aggregate authorization header field values if necessary
559 * 8. Optionally rewrite Record-Route header field values
560 * 9. Forward the response
561 * 10. Generate any necessary CANCEL requests
564 int proxy_response (sip_ticket_t *ticket) {
568 struct in_addr sendto_addr, local_ip;
572 osip_message_t *response;
573 struct sockaddr_in *from;
575 DEBUGC(DBCLASS_PROXY,"proxy_response");
578 ERROR("proxy_response: called with NULL ticket");
582 response=ticket->sipmsg;
585 /* Determine local address to use based on the To: address */
586 get_local_ip(response->to->url->host, &local_ip);
587 DEBUGC(DBCLASS_PROXY, "local IP: %s", inet_ntoa(local_ip));
590 * RFC 3261, Section 16.7 step 3
591 * Proxy Behavior - Response Processing - Remove my Via header field value
593 /* remove my Via header line */
594 sts = sip_del_myvia(ticket, &local_ip);
595 if (sts == STS_FAILURE) {
596 DEBUGC(DBCLASS_PROXY,"not addressed to my VIA, ignoring response");
601 * figure out if this is an request coming from the outside
602 * world to one of our registered clients
605 /* Ahhrghh...... a response seems to have NO contact information...
606 * so let's take FROM instead...
607 * the TO and FROM headers are EQUAL to the request - that means
608 * they are swapped in their meaning for a response...
611 #if _OLD_DIRECTION_EVALUATION
613 for (i=0; i<URLMAP_SIZE; i++) {
614 if (urlmap[i].active == 0) continue;
616 /* incoming response ('from' == 'masq') || ('from' == 'reg') */
617 if ((compare_url(response->from->url, urlmap[i].reg_url)==STS_SUCCESS) ||
618 (compare_url(response->from->url, urlmap[i].masq_url)==STS_SUCCESS)) {
619 type=RESTYP_INCOMING;
620 DEBUGC(DBCLASS_PROXY,"incoming response for %s@%s from outbound",
621 response->from->url->username? response->from->url->username:"*NULL*",
622 response->from->url->host? response->from->url->host : "*NULL*");
626 /* outgoing response ('to' == 'reg') || ('to' == 'masq' ) */
627 if ((compare_url(response->to->url, urlmap[i].masq_url)==STS_SUCCESS) ||
628 (compare_url(response->to->url, urlmap[i].reg_url)==STS_SUCCESS)){
629 type=RESTYP_OUTGOING;
630 DEBUGC(DBCLASS_PROXY,"outgoing response for %s@%s from inbound",
631 response->from->url->username ?
632 response->from->url->username : "*NULL*",
633 response->from->url->host ?
634 response->from->url->host : "*NULL*");
641 * did I receive the telegram from a REGISTERED host?
642 * -> it must be an OUTGOING response
644 for (i=0; i<URLMAP_SIZE; i++) {
645 struct in_addr tmp_addr;
646 if (urlmap[i].active == 0) continue;
648 if (get_ip_by_host(urlmap[i].true_url->host, &tmp_addr) == STS_FAILURE) {
649 DEBUGC(DBCLASS_PROXY, "proxy_response: cannot resolve host [%s]",
652 DEBUGC(DBCLASS_PROXY, "proxy_response: reghost:%s ip:%s",
653 urlmap[i].true_url->host, utils_inet_ntoa(from->sin_addr));
654 if (memcmp(&tmp_addr, &from->sin_addr, sizeof(tmp_addr)) == 0) {
655 type=RESTYP_OUTGOING;
661 * is the telegram directed to an internal registered host?
662 * -> it must be an INCOMING response
665 for (i=0; i<URLMAP_SIZE; i++) {
666 if (urlmap[i].active == 0) continue;
667 /* incoming response ('from' == 'masq') || ('from' == 'reg') */
668 if ((compare_url(response->from->url, urlmap[i].reg_url)==STS_SUCCESS) ||
669 (compare_url(response->from->url, urlmap[i].masq_url)==STS_SUCCESS)) {
670 type=RESTYP_INCOMING;
675 /* &&&& Open Issue &&&&
676 it has been seen with cross-provider calls that the FROM may be 'garbled'
677 (e.g 1393xxx@proxy01.sipphone.com for calls made sipphone -> FWD)
678 How can we deal with this? Should I take into consideration the 'Via'
679 headers? This is the only clue I have, pointing to the *real* UA.
680 Maybe I should put in a 'siproxd' ftag value to recognize it a header
683 if ((type == 0) && (!osip_list_eol(response->vias, 0))) {
685 struct in_addr addr_via, addr_myself;
686 int port_via, port_ua;
688 /* get the via address */
689 via = (osip_via_t *) osip_list_get (response->vias, 0);
690 DEBUGC(DBCLASS_PROXY, "proxy_response: check via [%s] for "
691 "registered UA",via->host);
692 sts=get_ip_by_host(via->host, &addr_via);
693 if (sts == STS_FAILURE) {
694 DEBUGC(DBCLASS_DNS, "proxy_response: cannot resolve VIA [%s]",
698 for (i=0; i<URLMAP_SIZE; i++) {
699 if (urlmap[i].active == 0) continue;
700 /* incoming response (1st via in list points to a registered UA) */
701 sts=get_ip_by_host(urlmap[i].true_url->host, &addr_myself);
702 if (sts == STS_FAILURE) {
703 DEBUGC(DBCLASS_DNS, "proxy_response: cannot resolve "
704 "true_url [%s]", via->host);
709 if (via->port) port_via=atoi(via->port);
710 if (port_via <= 0) port_via=SIP_PORT;
713 if (urlmap[i].true_url->port)
714 port_ua=atoi(urlmap[i].true_url->port);
715 if (port_ua <= 0) port_ua=SIP_PORT;
717 DEBUGC(DBCLASS_BABBLE, "proxy_response: checking for registered "
718 "host [%s:%i] <-> [%s:%i]",
719 urlmap[i].true_url->host, port_ua,
720 via->host, port_via);
722 if ((memcmp(&addr_myself, &addr_via, sizeof(addr_myself))==0) &&
723 (port_via == port_ua)) {
724 type=RESTYP_INCOMING;
732 ticket->direction=type;
735 * ok, we got a response that we are allowed to process.
739 * from an external host to the internal masqueraded host
741 case RESTYP_INCOMING:
742 DEBUGC(DBCLASS_PROXY,"incoming response for %s@%s from outbound",
743 response->from->url->username? response->from->url->username:"*NULL*",
744 response->from->url->host? response->from->url->host : "*NULL*");
747 * Response for INVITE - deal with RTP data in body and
748 * start RTP proxy stream(s). In case
749 * of a negative answer, stop RTP stream
751 if (MSG_IS_RESPONSE_FOR(response,"INVITE")) {
752 /* positive response, start RTP stream */
753 if ((MSG_IS_STATUS_1XX(response)) ||
754 (MSG_IS_STATUS_2XX(response))) {
755 if (configuration.rtp_proxy_enable == 1) {
756 DEBUGC(DBCLASS_PROXY,"here");
757 sts = proxy_rewrite_invitation_body(response, DIR_INCOMING, NULL);
759 /* negative - stop a possibly started RTP stream */
760 } else if ((MSG_IS_STATUS_4XX(response)) ||
761 (MSG_IS_STATUS_5XX(response)) ||
762 (MSG_IS_STATUS_6XX(response))) {
763 rtp_stop_fwd(osip_message_get_call_id(response), DIR_INCOMING);
764 rtp_stop_fwd(osip_message_get_call_id(response), DIR_OUTGOING);
769 * Response for REGISTER - special handling of Contact header
771 if (MSG_IS_RESPONSE_FOR(response,"REGISTER")) {
773 * REGISTER returns *my* Contact header information.
774 * Rewrite Contact header back to represent the true address.
775 * Other responses do return the Contact header of the sender.
777 sip_rewrite_contact(ticket, DIR_INCOMING, NULL);
781 * Response for SUBSCRIBE
783 * HACK for Grandstream SIP phones (with newer firmware like 1.0.4.40):
784 * They send a SUBSCRIBE request to the registration server. In
785 * case of beeing registering directly to siproxd, this request of
786 * course will eventually be forwarded back to the same UA.
787 * Grandstream then does reply with an '202' response (A 202
788 * response merely indicates that the subscription has been
789 * understood, and that authorization may or may not have been
790 * granted), which then of course is forwarded back to the phone.
791 * Ans it seems that the Grandstream can *not* *handle* this
792 * response, as it immediately sends another SUBSCRIBE request.
793 * And this games goes on and on and on...
795 * As a workaround we will transform any 202 response to a
796 * '404 unknown destination'
800 osip_header_t *ua_hdr=NULL;
801 osip_message_get_user_agent(response, 0, &ua_hdr);
802 if (ua_hdr && ua_hdr->hvalue &&
803 (osip_strncasecmp(ua_hdr->hvalue,"grandstream", 11)==0) &&
804 (MSG_IS_RESPONSE_FOR(response,"SUBSCRIBE")) &&
805 (MSG_TEST_CODE(response, 202))) {
806 DEBUGC(DBCLASS_PROXY, "proxy_response: Grandstream hack 202->404");
807 response->status_code=404;
813 * from the internal masqueraded host to an external host
815 case RESTYP_OUTGOING:
816 DEBUGC(DBCLASS_PROXY,"outgoing response for %s@%s from inbound",
817 response->from->url->username ?
818 response->from->url->username : "*NULL*",
819 response->from->url->host ?
820 response->from->url->host : "*NULL*");
822 /* rewrite Contact header to represent the masqued address */
823 sip_rewrite_contact(ticket, DIR_OUTGOING, NULL);
826 * If an 2xx OK or 1xx response, answer to an INVITE request,
829 * In case of a negative answer, stop RTP stream
831 if (MSG_IS_RESPONSE_FOR(response,"INVITE")) {
832 /* positive response, start RTP stream */
833 if ((MSG_IS_STATUS_1XX(response)) ||
834 (MSG_IS_STATUS_2XX(response))) {
835 /* This is an outgoing response, therefore an outgoing stream */
836 sts = proxy_rewrite_invitation_body(response, DIR_OUTGOING, NULL);
837 /* megative - stop a possibly started RTP stream */
838 } else if ((MSG_IS_STATUS_4XX(response)) ||
839 (MSG_IS_STATUS_5XX(response)) ||
840 (MSG_IS_STATUS_6XX(response))) {
841 rtp_stop_fwd(osip_message_get_call_id(response), DIR_INCOMING);
842 rtp_stop_fwd(osip_message_get_call_id(response), DIR_OUTGOING);
849 DEBUGC(DBCLASS_PROXY, "response from/to unregistered UA (%s@%s)",
850 response->from->url->username? response->from->url->username:"*NULL*",
851 response->from->url->host? response->from->url->host : "*NULL*");
856 * for ALL incoming response include my Record-Route header.
857 * The local UA will probably send its answer to the topmost
858 * Route Header (8.1.2 of RFC3261)
860 if (type == RESTYP_INCOMING) {
861 DEBUGC(DBCLASS_PROXY,"Adding my Record-Route");
862 route_add_recordroute(ticket);
865 * outgoing packets must not have my record route header, as
866 * this likely will contain a private IP address (my inbound).
868 DEBUGC(DBCLASS_PROXY,"Purging Record-Routes (outgoing packet)");
869 route_purge_recordroute(ticket);
873 * Determine Next-Hop Address
875 /*&&&& priority probably should be:
877 * 2) fixed outbound proxy
881 * check if we need to send to an outbound proxy
883 if ((type == RESTYP_OUTGOING) &&
884 (sip_find_outbound_proxy(ticket, &sendto_addr, &port) == STS_SUCCESS)) {
885 DEBUGC(DBCLASS_PROXY, "proxy_response: have outbound proxy %s:%i",
886 utils_inet_ntoa(sendto_addr), port);
889 * If so, fetch address from topmost Route: header and remove it.
891 } else if ((type == RESTYP_OUTGOING) &&
892 (response->routes && !osip_list_eol(response->routes, 0))) {
893 sts=route_determine_nexthop(ticket, &sendto_addr, &port);
894 if (sts == STS_FAILURE) {
895 DEBUGC(DBCLASS_PROXY, "proxy_response: route_determine_nexthop failed");
898 DEBUGC(DBCLASS_PROXY, "proxy_response: have Route header to %s:%i",
899 utils_inet_ntoa(sendto_addr), port);
901 /* get target address and port from VIA header */
902 via = (osip_via_t *) osip_list_get (response->vias, 0);
904 ERROR("proxy_response: list_get via failed");
908 sts = get_ip_by_host(via->host, &sendto_addr);
909 if (sts == STS_FAILURE) {
910 DEBUGC(DBCLASS_PROXY, "proxy_response: cannot resolve VIA [%s]",
916 port=atoi(via->port);
922 sts = osip_message_to_str(response, &buffer);
924 ERROR("proxy_response: osip_message_to_str failed");
928 sipsock_send(sendto_addr, port, ticket->protocol,
929 buffer, strlen(buffer));
936 * PROXY_REWRITE_INVITATION_BODY
938 * rewrites the outgoing INVITATION request or response packet
941 * STS_SUCCESS on success
942 * STS_FAILURE on error
944 int proxy_rewrite_invitation_body(osip_message_t *mymsg, int direction,
945 struct in_addr *local_ip){
948 struct in_addr map_addr, addr_sess, addr_media, outside_addr, inside_addr;
951 char clen[8]; /* content length: probably never more than 7 digits !*/
952 int map_port, msg_port;
954 sdp_connection_t *sdp_conn;
955 sdp_media_t *sdp_med;
959 if (configuration.rtp_proxy_enable == 0) return STS_SUCCESS;
964 sts = osip_message_get_body(mymsg, 0, &body);
966 if ((MSG_IS_RESPONSE_FOR(mymsg,"INVITE")) &&
967 (MSG_IS_STATUS_1XX(mymsg))) {
968 /* 1xx responses *MAY* contain SDP data */
969 DEBUGC(DBCLASS_PROXY, "rewrite_invitation_body: "
970 "no body found in message");
973 /* INVITE request and 200 response *MUST* contain SDP data */
974 ERROR("rewrite_invitation_body: no body found in message");
979 sts = osip_body_to_str(body, &bodybuff);
981 ERROR("rewrite_invitation_body: unable to sip_body_to_str");
983 sts = sdp_message_init(&sdp);
984 sts = sdp_message_parse (sdp, bodybuff);
987 ERROR("rewrite_invitation_body: unable to sdp_message_parse body");
988 sdp_message_free(sdp);
993 if (configuration.debuglevel)
994 { /* just dump the buffer */
996 sts = osip_message_get_body(mymsg, 0, &body);
997 sts = osip_body_to_str(body, &tmp);
998 osip_content_length_to_str(mymsg->content_length, &tmp2);
999 DEBUG("Body before rewrite (clen=%s, strlen=%i):\n%s\n----",
1000 tmp2, strlen(tmp), tmp);
1006 * RTP proxy: get ready and start forwarding
1007 * start forwarding for each media stream ('m=' item in SIP message)
1010 /* get outbound address */
1011 if (get_ip_by_ifname(configuration.outbound_if, &outside_addr) !=
1013 ERROR("can't find outbound interface %s - configuration error?",
1014 configuration.outbound_if);
1015 sdp_message_free(sdp);
1019 if (local_ip == NULL) {
1020 /* get inbound address */
1021 if (get_ip_by_ifname(configuration.inbound_if, &inside_addr) !=
1023 ERROR("can't find inbound interface %s - configuration error?",
1024 configuration.inbound_if);
1025 sdp_message_free(sdp);
1029 outside_addr = *local_ip;
1032 /* figure out what address to use for RTP masquerading */
1033 if (MSG_IS_REQUEST(mymsg)) {
1034 if (direction == DIR_INCOMING) {
1035 memcpy(&map_addr, &inside_addr, sizeof (map_addr));
1036 rtp_direction = DIR_OUTGOING;
1038 memcpy(&map_addr, &outside_addr, sizeof (map_addr));
1039 rtp_direction = DIR_INCOMING;
1041 } else /* MSG_IS_REPONSE(mymsg) */ {
1042 if (direction == DIR_INCOMING) {
1043 memcpy(&map_addr, &inside_addr, sizeof (map_addr));
1044 rtp_direction = DIR_OUTGOING;
1046 memcpy(&map_addr, &outside_addr, sizeof (map_addr));
1047 rtp_direction = DIR_INCOMING;
1051 DEBUGC(DBCLASS_PROXY, "proxy_rewrite_invitation_body: SIP[%s %s] RTP[%s %s]",
1052 MSG_IS_REQUEST(mymsg)? "RQ" : "RS",
1053 (direction==DIR_INCOMING)? "IN" : "OUT",
1054 (rtp_direction==DIR_INCOMING)? "IN" : "OUT",
1055 utils_inet_ntoa(map_addr));
1059 * first, check presence of a 'c=' item on session level
1061 if (sdp->c_connection==NULL || sdp->c_connection->c_addr==NULL) {
1063 * No 'c=' on session level, search on media level now
1065 * According to RFC2327, ALL media description must
1066 * include a 'c=' item now:
1069 while (!sdp_message_endof_media(sdp, media_stream_no)) {
1070 /* check if n'th media stream is present */
1071 if (sdp_message_c_addr_get(sdp, media_stream_no, 0) == NULL) {
1072 ERROR("SDP: have no 'c=' on session level and neither "
1073 "on media level (media=%i)",media_stream_no);
1074 sdp_message_free(sdp);
1081 /* Required 'c=' items ARE present */
1085 * rewrite 'c=' item on session level if present and not yet done.
1086 * remember the original address in addr_sess
1088 memset(&addr_sess, 0, sizeof(addr_sess));
1089 if (sdp->c_connection && sdp->c_connection->c_addr) {
1090 sts = get_ip_by_host(sdp->c_connection->c_addr, &addr_sess);
1091 if (sts == STS_FAILURE) {
1092 ERROR("SDP: cannot resolve session 'c=' host [%s]",
1093 sdp->c_connection->c_addr);
1094 sdp_message_free(sdp);
1099 * an IP address of 0.0.0.0 means *MUTE*, don't rewrite such
1101 /*&&&& should use gethostbyname here */
1102 if (strcmp(sdp->c_connection->c_addr, "0.0.0.0") != 0) {
1103 osip_free(sdp->c_connection->c_addr);
1104 sdp->c_connection->c_addr=osip_malloc(HOSTNAME_SIZE);
1105 sprintf(sdp->c_connection->c_addr, "%s", utils_inet_ntoa(map_addr));
1107 /* 0.0.0.0 - don't rewrite */
1108 DEBUGC(DBCLASS_PROXY, "proxy_rewrite_invitation_body: "
1109 "got a MUTE c= record (on session level - legal?)");
1115 * rewrite 'o=' item (originator) on session level if present.
1117 if (sdp->o_addrtype && sdp->o_addr) {
1118 if (strcmp(sdp->o_addrtype, "IP4") != 0) {
1119 ERROR("got IP6 in SDP originator - not yet suported by siproxd");
1120 sdp_message_free(sdp);
1124 osip_free(sdp->o_addr);
1125 sdp->o_addr=osip_malloc(HOSTNAME_SIZE);
1126 sprintf(sdp->o_addr, "%s", utils_inet_ntoa(map_addr));
1131 * loop through all media descritions,
1132 * start RTP proxy and rewrite them
1134 for (media_stream_no=0;;media_stream_no++) {
1135 /* check if n'th media stream is present */
1136 if (sdp_message_m_port_get(sdp, media_stream_no) == NULL) break;
1139 * check if a 'c=' item is present in this media description,
1140 * if so -> rewrite it
1142 memset(&addr_media, 0, sizeof(addr_media));
1144 sdp_conn=sdp_message_connection_get(sdp, media_stream_no, 0);
1145 if (sdp_conn && sdp_conn->c_addr) {
1146 /*&&&& should use gethostbyname here as well */
1147 if (strcmp(sdp_conn->c_addr, "0.0.0.0") != 0) {
1148 sts = get_ip_by_host(sdp_conn->c_addr, &addr_media);
1150 /* have a valid address */
1151 osip_free(sdp_conn->c_addr);
1152 sdp_conn->c_addr=osip_malloc(HOSTNAME_SIZE);
1153 sprintf(sdp_conn->c_addr, "%s", utils_inet_ntoa(map_addr));
1155 /* 0.0.0.0 - don't rewrite */
1156 DEBUGC(DBCLASS_PROXY, "proxy_rewrite_invitation_body: got a "
1157 "MUTE c= record (media level)");
1161 /* start an RTP proxying stream */
1162 if (sdp_message_m_port_get(sdp, media_stream_no)) {
1163 msg_port=atoi(sdp_message_m_port_get(sdp, media_stream_no));
1166 osip_uri_t *cont_url = NULL;
1167 char *client_id=NULL;
1168 /* try to get some additional UA specific unique ID.
1170 * 1) User part of Contact header
1171 * 2) Host part of Contact header (will be different
1172 * between internal UA and external UA)
1174 if (!osip_list_eol(mymsg->contacts, 0))
1175 cont_url = ((osip_contact_t*)(mymsg->contacts->node->element))->url;
1177 client_id=cont_url->username;
1178 if (client_id == NULL) client_id=cont_url->host;
1183 * do we have a 'c=' item on media level?
1184 * if not, use the same as on session level
1186 if (have_c_media == 0) {
1187 memcpy(&addr_media, &addr_sess, sizeof(addr_sess));
1190 sts = rtp_start_fwd(osip_message_get_call_id(mymsg),
1194 map_addr, &map_port,
1195 addr_media, msg_port);
1197 if (sts == STS_SUCCESS) {
1198 /* and rewrite the port */
1199 sdp_med=osip_list_get(sdp->m_medias, media_stream_no);
1200 if (sdp_med && sdp_med->m_port) {
1201 osip_free(sdp_med->m_port);
1202 sdp_med->m_port=osip_malloc(8); /* 5 digits, \0 + align */
1203 sprintf(sdp_med->m_port, "%i", map_port);
1204 DEBUGC(DBCLASS_PROXY, "proxy_rewrite_invitation_body: "
1205 "m= rewrote port to [%i]",map_port);
1207 ERROR("rewriting port in m= failed sdp_med=%p, "
1208 "m_number_of_port=%p", sdp_med, sdp_med->m_port);
1210 } /* sts == success */
1211 } /* if msg_port > 0 */
1213 /* no port defined - skip entry */
1214 WARN("no port defined in m=(media) stream_no=%i", media_stream_no);
1217 } /* for media_stream_no */
1219 /* remove old body */
1220 sts = osip_list_remove(mymsg->bodies, 0);
1221 osip_body_free(body);
1224 sdp_message_to_str(sdp, &bodybuff);
1226 /* free sdp structure */
1227 sdp_message_free(sdp);
1229 /* include new body */
1230 osip_message_set_body(mymsg, bodybuff);
1232 /* free content length resource and include new one*/
1233 osip_content_length_free(mymsg->content_length);
1234 mymsg->content_length=NULL;
1235 sprintf(clen,"%i",strlen(bodybuff));
1236 sts = osip_message_set_content_length(mymsg, clen);
1239 osip_free(bodybuff);
1241 if (configuration.debuglevel)
1242 { /* just dump the buffer */
1244 sts = osip_message_get_body(mymsg, 0, &body);
1245 sts = osip_body_to_str(body, &tmp);
1246 osip_content_length_to_str(mymsg->content_length, &tmp2);
1247 DEBUG("Body after rewrite (clen=%s, strlen=%i):\n%s\n----",
1248 tmp2, strlen(tmp), tmp);
1257 * PROXY_REWRITE_REQUEST_URI
1259 * rewrites the incoming Request URI
1262 * STS_SUCCESS on success
1264 int proxy_rewrite_request_uri(osip_message_t *mymsg, int idx){
1269 if ((idx >= URLMAP_SIZE) || (idx < 0)) {
1270 WARN("proxy_rewrite_request_uri: called with invalid index");
1274 DEBUGC(DBCLASS_PROXY,"rewriting incoming Request URI");
1275 url=osip_message_get_uri(mymsg);
1277 /* set the true host */
1278 if (url->host) osip_free(url->host);url->host=NULL;
1279 if (urlmap[idx].true_url->host) {
1280 DEBUGC(DBCLASS_BABBLE,"proxy_rewrite_request_uri: host=%s",
1281 urlmap[idx].true_url->host);
1282 host = (char *)malloc(strlen(urlmap[idx].true_url->host)+1);
1283 memcpy(host, urlmap[idx].true_url->host, strlen(urlmap[idx].true_url->host));
1284 host[strlen(urlmap[idx].true_url->host)]='\0';
1285 osip_uri_set_host(url, host);
1288 /* set the true port */
1289 if (url->port) osip_free(url->port);url->port=NULL;
1290 if (urlmap[idx].true_url->port) {
1291 DEBUGC(DBCLASS_BABBLE,"proxy_rewrite_request_uri: port=%s",
1292 urlmap[idx].true_url->port);
1293 port = (char *)malloc(strlen(urlmap[idx].true_url->port)+1);
1294 memcpy(port, urlmap[idx].true_url->port, strlen(urlmap[idx].true_url->port));
1295 port[strlen(urlmap[idx].true_url->port)]='\0';
1296 osip_uri_set_port(url, port);