Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2001-2003 FhG Fokus |
3 | | * Copyright (C) 2005-2009 Voice Sistem SRL |
4 | | * |
5 | | * This file is part of opensips, a free SIP server. |
6 | | * |
7 | | * opensips is free software; you can redistribute it and/or modify |
8 | | * it under the terms of the GNU General Public License as published by |
9 | | * the Free Software Foundation; either version 2 of the License, or |
10 | | * (at your option) any later version |
11 | | * |
12 | | * opensips is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | | * |
21 | | * History: |
22 | | * ------- |
23 | | * 2001-??-?? created by andrei |
24 | | * ????-??-?? lots of changes by a lot of people |
25 | | * 2003-01-23 support for determination of outbound interface added : |
26 | | * get_out_socket (jiri) |
27 | | * 2003-01-24 reply to rport support added, contributed by |
28 | | * Maxim Sobolev <sobomax@FreeBSD.org> and modified by andrei |
29 | | * 2003-02-11 removed calls to upd_send & tcp_send & replaced them with |
30 | | * calls to msg_send (andrei) |
31 | | * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei) |
32 | | * 2003-04-02 fixed get_send_socket for tcp fwd to udp (andrei) |
33 | | * 2003-04-03 added su_setport (andrei) |
34 | | * 2003-04-04 update_sock_struct_from_via now differentiates between |
35 | | * local replies & "normal" replies (andrei) |
36 | | * 2003-04-12 update_sock_struct_from via uses also FL_FORCE_RPORT for |
37 | | * local replies (andrei) |
38 | | * 2003-08-21 check_self properly handles ipv6 addresses & refs (andrei) |
39 | | * 2003-10-21 check_self updated to handle proto (andrei) |
40 | | * 2003-10-24 converted to the new socket_info lists (andrei) |
41 | | * 2004-10-10 modified check_self to use grep_sock_info (andrei) |
42 | | * 2004-11-08 added force_send_socket support in get_send_socket (andrei) |
43 | | * 2006-09-06 added new algorithm for building VIA branch parameter for |
44 | | * stateless requests - it complies to RFC3261 requirement to be |
45 | | * unique through time and space (bogdan) |
46 | | */ |
47 | | |
48 | | /*! |
49 | | * \file |
50 | | * \brief OpenSIPS Stateless forward support |
51 | | */ |
52 | | |
53 | | |
54 | | #include <string.h> |
55 | | #include <stdio.h> |
56 | | #include <stdlib.h> |
57 | | #include <errno.h> |
58 | | #include <sys/types.h> |
59 | | #include <sys/socket.h> |
60 | | #include <netdb.h> |
61 | | #include <netinet/in.h> |
62 | | #include <arpa/inet.h> |
63 | | |
64 | | #include "forward.h" |
65 | | #include "parser/msg_parser.h" |
66 | | #include "parser/parse_from.h" |
67 | | #include "dprint.h" |
68 | | #include "ut.h" |
69 | | #include "dset.h" |
70 | | #include "mem/mem.h" |
71 | | #include "msg_translator.h" |
72 | | #include "sr_module.h" |
73 | | #include "ip_addr.h" |
74 | | #include "resolve.h" |
75 | | #include "net/trans.h" |
76 | | #include "name_alias.h" |
77 | | #include "socket_info.h" |
78 | | #include "core_stats.h" |
79 | | #include "blacklists.h" |
80 | | #include "msg_callbacks.h" |
81 | | #include "md5utils.h" |
82 | | |
83 | | |
84 | | |
85 | | /*! \brief return a socket_info_pointer to the sending socket |
86 | | * \note As opposed to |
87 | | * get_send_socket(), which returns process's default socket, get_out_socket |
88 | | * attempts to determine the outbound interface which will be used; |
89 | | * it creates a temporary connected socket to determine it; it will |
90 | | * be very likely noticeably slower, but it can deal better with |
91 | | * multihomed hosts |
92 | | */ |
93 | | const struct socket_info* get_out_socket(const union sockaddr_union* to, int proto) |
94 | 0 | { |
95 | 0 | int temp_sock; |
96 | 0 | socklen_t len; |
97 | 0 | union sockaddr_union from; |
98 | 0 | const struct socket_info* si; |
99 | 0 | struct ip_addr ip, ip_dst; |
100 | |
|
101 | 0 | if (proto!=PROTO_UDP) { |
102 | 0 | LM_CRIT("can only be called for UDP\n"); |
103 | 0 | return 0; |
104 | 0 | } |
105 | | |
106 | 0 | temp_sock=socket(to->s.sa_family, SOCK_DGRAM, 0 ); |
107 | 0 | if (temp_sock==-1) { |
108 | 0 | LM_ERR("socket() failed: %s\n", strerror(errno)); |
109 | 0 | return 0; |
110 | 0 | } |
111 | 0 | if (connect(temp_sock, &to->s, sockaddru_len(*to))==-1) { |
112 | 0 | LM_ERR("connect failed: %s\n", strerror(errno)); |
113 | 0 | goto error; |
114 | 0 | } |
115 | 0 | len=sizeof(from); |
116 | 0 | if (getsockname(temp_sock, &from.s, &len)==-1) { |
117 | 0 | LM_ERR("getsockname failed: %s\n", strerror(errno)); |
118 | 0 | goto error; |
119 | 0 | } |
120 | 0 | su2ip_addr(&ip, &from); |
121 | 0 | si=find_si(&ip, 0, proto); |
122 | 0 | if (si==0) { |
123 | 0 | LM_ERR("outbound IP %s not found as listener\n", ip_addr2a(&ip)); |
124 | 0 | goto error; |
125 | 0 | } |
126 | 0 | close(temp_sock); |
127 | 0 | LM_DBG("socket determined: %p\n", si ); |
128 | 0 | return si; |
129 | 0 | error: |
130 | 0 | su2ip_addr( &ip_dst, to); |
131 | 0 | LM_ERR("failed to find route to %s\n", ip_addr2a(&ip_dst)); |
132 | 0 | close(temp_sock); |
133 | 0 | return 0; |
134 | 0 | } |
135 | | |
136 | | |
137 | | |
138 | | /*! \brief returns a socket_info pointer to the sending socket or 0 on error |
139 | | * \param msg SIP message (can be null) |
140 | | * \param to destination socket_union pointer |
141 | | * \param proto protocol |
142 | | * |
143 | | * \note if msg!=null and msg->force_send_socket, the force_send_socket will be used |
144 | | */ |
145 | | const struct socket_info* get_send_socket(struct sip_msg *msg, |
146 | | const union sockaddr_union* to, int proto) |
147 | 0 | { |
148 | 0 | const struct socket_info* send_sock; |
149 | | |
150 | | /* check if send interface is not forced */ |
151 | 0 | if (msg && msg->force_send_socket){ |
152 | 0 | if (msg->force_send_socket->proto!=proto){ |
153 | 0 | LM_DBG("force_send_socket of different proto (%d)!\n", proto); |
154 | 0 | msg->force_send_socket=find_si(&(msg->force_send_socket->address), |
155 | 0 | msg->force_send_socket->port_no, |
156 | 0 | proto); |
157 | 0 | } |
158 | 0 | if (msg->force_send_socket) |
159 | 0 | return msg->force_send_socket; |
160 | 0 | else |
161 | 0 | LM_WARN("protocol/port mismatch\n"); |
162 | 0 | }; |
163 | |
|
164 | 0 | if (mhomed && proto==PROTO_UDP) |
165 | 0 | return get_out_socket(to, proto); |
166 | | |
167 | 0 | send_sock=0; |
168 | | /* check if we need to change the socket (different address families - |
169 | | * eg: ipv4 -> ipv6 or ipv6 -> ipv4) */ |
170 | 0 | switch(proto){ |
171 | 0 | case PROTO_UDP: |
172 | 0 | if (msg && msg->rcv.bind_address && |
173 | 0 | msg->rcv.bind_address->address.af==to->s.sa_family && |
174 | 0 | msg->rcv.bind_address->proto==PROTO_UDP) { |
175 | 0 | send_sock = msg->rcv.bind_address; |
176 | 0 | break; |
177 | 0 | } |
178 | | /* default logic for all protos */ |
179 | 0 | default: |
180 | | /* we don't really know the sending address (we can find it out, |
181 | | * but we'll need also to see if we listen on it, and if yes on |
182 | | * which port -> too complicated*/ |
183 | 0 | send_sock = (to->s.sa_family==AF_INET) ? |
184 | 0 | protos[proto].sendipv4 : protos[proto].sendipv6; |
185 | 0 | } |
186 | 0 | return send_sock; |
187 | 0 | } |
188 | | |
189 | | |
190 | | |
191 | | /*! \brief checks if the proto: host:port is one of the address we listen on |
192 | | * |
193 | | * if port==0, the port number is ignored |
194 | | * if proto==0 (PROTO_NONE) the protocol is ignored |
195 | | * returns 1 if true, 0 if false, -1 on error |
196 | | * WARNING: uses str2ip6 so it will overwrite any previous |
197 | | * unsaved result of this function (static buffer) |
198 | | */ |
199 | | int check_self(str* host, unsigned short port, unsigned short proto) |
200 | 0 | { |
201 | 0 | if (grep_sock_info(host, port, proto)) goto found; |
202 | | /* try to look into the aliases*/ |
203 | 0 | if (grep_aliases(host->s, host->len, port, proto)==0){ |
204 | 0 | LM_DBG("host != me\n"); |
205 | 0 | return 0; |
206 | 0 | } |
207 | 0 | found: |
208 | 0 | return 1; |
209 | 0 | } |
210 | | |
211 | | |
212 | | |
213 | | static inline int set_sl_branch(struct sip_msg* msg) |
214 | 0 | { |
215 | 0 | struct hdr_field *h_via; |
216 | 0 | struct via_body *b_via; |
217 | 0 | str *branch; |
218 | 0 | int via_parsed; |
219 | 0 | char b_md5[MD5_LEN]; |
220 | |
|
221 | 0 | via_parsed = 0; |
222 | 0 | branch = 0; |
223 | | |
224 | | /* first VIA header must be parsed */ |
225 | 0 | for( h_via=msg->h_via1 ; h_via ; h_via=h_via->sibling ) { |
226 | |
|
227 | 0 | b_via = (struct via_body*)h_via->parsed; |
228 | 0 | for( ; b_via ; b_via=b_via->next ) { |
229 | | /* check if there is any valid branch param */ |
230 | 0 | if (b_via->branch==0 || b_via->branch->value.s==0 |
231 | 0 | || b_via->branch->value.len==0 ) |
232 | 0 | continue; |
233 | 0 | branch = &b_via->branch->value; |
234 | | /* check if the branch param has the magic cookie */ |
235 | 0 | if (branch->len <= (int)MCOOKIE_LEN |
236 | 0 | || memcmp( branch->s, MCOOKIE, MCOOKIE_LEN)!=0 ) |
237 | 0 | continue; |
238 | | /* found a statefull branch -> use it */ |
239 | 0 | goto found; |
240 | 0 | } |
241 | | |
242 | 0 | if (!via_parsed) { |
243 | 0 | if ( parse_headers(msg,HDR_EOH_F,0)<0 ) { |
244 | 0 | LM_ERR("failed to parse all hdrs\n"); |
245 | 0 | return -1; |
246 | 0 | } |
247 | 0 | via_parsed = 1; |
248 | 0 | } |
249 | 0 | } |
250 | | |
251 | | /* no statefull branch :(.. -> use the branch from the last via */ |
252 | 0 | found: |
253 | 0 | if (branch==NULL) { |
254 | | /* no branch found :(.. -> try to use the From TAG param as |
255 | | * a value to seed the MD5 - the From TAG is per call, so it gives |
256 | | * a bit of uniqueness; if this is empty, as a last resort, use the |
257 | | * FROM URI (it cannot mis) */ |
258 | 0 | if ( parse_from_header(msg)!=0 ) |
259 | 0 | { |
260 | 0 | LM_ERR("failed to extract FROM header\n"); |
261 | 0 | return -1; |
262 | 0 | } |
263 | 0 | if ( get_from(msg)->tag_value.len ) |
264 | 0 | branch = &get_from(msg)->tag_value; |
265 | 0 | else |
266 | 0 | branch = &get_from(msg)->uri; |
267 | 0 | } |
268 | | |
269 | | /* make an MD5 over the found branch, to ensure a controlable |
270 | | * length of the resulting branch */ |
271 | 0 | MD5StringArray ( b_md5, branch, 1 ); |
272 | | /* and make a hash over transaction-related values */ |
273 | 0 | if ( parse_headers(msg, HDR_CALLID_F|HDR_CSEQ_F,0)==-1 || |
274 | 0 | msg->callid==NULL || msg->cseq==NULL ) |
275 | 0 | { |
276 | 0 | LM_ERR("failed to extract CALLID or CSEQ hdr from SIP msg\n"); |
277 | 0 | return -1; |
278 | 0 | } |
279 | | /* build the new branch */ |
280 | 0 | if (branch_builder( |
281 | 0 | core_hash( &msg->callid->body, &get_cseq(msg)->number, 1<<16 ), |
282 | 0 | 0 /*labled - not used here */, |
283 | 0 | b_md5, |
284 | 0 | 0 /*branch - not used here */, |
285 | 0 | msg->add_to_branch_s, &msg->add_to_branch_len )==0 ) |
286 | 0 | { |
287 | 0 | LM_ERR("branch_builder failed to construct the branch\n"); |
288 | 0 | return -1; |
289 | 0 | } |
290 | | |
291 | 0 | return 0; |
292 | 0 | } |
293 | | |
294 | | |
295 | | |
296 | | int forward_request( struct sip_msg* msg, struct proxy_l * p) |
297 | 0 | { |
298 | 0 | union sockaddr_union to; |
299 | 0 | str buf; |
300 | 0 | const struct socket_info* send_sock; |
301 | 0 | const struct socket_info* last_sock; |
302 | |
|
303 | 0 | buf.s=NULL; |
304 | | |
305 | | /* calculate branch for outbound request - if the branch buffer is already |
306 | | * set (maybe by an upper level as TM), used it; otherwise computes |
307 | | * the branch for stateless fwd. . According to the latest discussions |
308 | | * on the topic, you should reuse the latest statefull branch |
309 | | * --bogdan */ |
310 | 0 | if ( msg->add_to_branch_len==0 ) { |
311 | 0 | if (set_sl_branch(msg)!=0) { |
312 | 0 | LM_ERR("unable to compute and add stateless VIA branch\n"); |
313 | 0 | goto error; |
314 | 0 | } |
315 | 0 | } |
316 | | |
317 | 0 | msg_callback_process(msg, REQ_PRE_FORWARD, (void *)p); |
318 | |
|
319 | 0 | hostent2su( &to, &p->host, p->addr_idx, (p->port)?p->port:SIP_PORT); |
320 | 0 | last_sock = 0; |
321 | |
|
322 | 0 | if (getb0flags(msg) & tcp_no_new_conn_bflag) |
323 | 0 | tcp_no_new_conn = 1; |
324 | |
|
325 | 0 | do { |
326 | 0 | send_sock=get_send_socket( msg, &to, p->proto); |
327 | 0 | if (send_sock==0){ |
328 | 0 | LM_ERR("cannot forward to af %d, proto %d no corresponding" |
329 | 0 | "listening socket\n", to.s.sa_family, p->proto); |
330 | 0 | ser_error=E_NO_SOCKET; |
331 | 0 | continue; |
332 | 0 | } |
333 | | |
334 | 0 | if ( last_sock!=send_sock ) { |
335 | |
|
336 | 0 | if (buf.s) |
337 | 0 | pkg_free(buf.s); |
338 | |
|
339 | 0 | buf.s = build_req_buf_from_sip_req( msg, (unsigned int*)&buf.len, |
340 | 0 | send_sock, p->proto, NULL, 0 /*flags*/); |
341 | 0 | if (!buf.s){ |
342 | 0 | LM_ERR("building req buf failed\n"); |
343 | 0 | tcp_no_new_conn = 0; |
344 | 0 | goto error; |
345 | 0 | } |
346 | | |
347 | 0 | last_sock = send_sock; |
348 | 0 | } |
349 | | |
350 | 0 | if (check_blacklists( p->proto, &to, buf.s, buf.len)) { |
351 | 0 | LM_DBG("blocked by blacklists\n"); |
352 | 0 | ser_error=E_IP_BLOCKED; |
353 | 0 | continue; |
354 | 0 | } |
355 | | |
356 | | /* send it! */ |
357 | 0 | LM_DBG("sending:\n%.*s.\n", buf.len, buf.s); |
358 | 0 | LM_DBG("orig. len=%d, new_len=%d, proto=%d\n", |
359 | 0 | msg->len, buf.len, p->proto ); |
360 | |
|
361 | 0 | if (msg_send(send_sock, p->proto, &to, 0, buf.s, buf.len, msg)<0){ |
362 | 0 | ser_error=E_SEND; |
363 | 0 | continue; |
364 | 0 | } |
365 | | |
366 | 0 | slcb_run_req_out( msg, &buf, &to, send_sock, p->proto); |
367 | |
|
368 | 0 | ser_error = 0; |
369 | 0 | break; |
370 | |
|
371 | 0 | }while( get_next_su( p, &to, (ser_error==E_IP_BLOCKED)?0:1)==0 ); |
372 | | |
373 | 0 | tcp_no_new_conn = 0; |
374 | |
|
375 | 0 | if (ser_error) { |
376 | 0 | update_stat( drp_reqs, 1); |
377 | 0 | goto error; |
378 | 0 | } |
379 | | |
380 | | /* sent requests stats */ |
381 | 0 | update_stat( fwd_reqs, 1); |
382 | |
|
383 | 0 | pkg_free(buf.s); |
384 | | /* received_buf & line_buf will be freed in receive_msg by free_lump_list*/ |
385 | 0 | return 0; |
386 | | |
387 | 0 | error: |
388 | 0 | if (buf.s) pkg_free(buf.s); |
389 | 0 | return -1; |
390 | 0 | } |
391 | | |
392 | | |
393 | | |
394 | | int update_sock_struct_from_via( union sockaddr_union* to, |
395 | | struct sip_msg* msg, |
396 | | struct via_body* via ) |
397 | 0 | { |
398 | 0 | struct hostent* he; |
399 | 0 | str* name; |
400 | 0 | int err; |
401 | 0 | unsigned short port; |
402 | |
|
403 | 0 | port=0; |
404 | 0 | if(via==msg->via1){ |
405 | | /* _local_ reply, we ignore any rport or received value |
406 | | * (but we will send back to the original port if rport is |
407 | | * present) */ |
408 | 0 | if ((msg->msg_flags&FL_FORCE_RPORT)||(via->rport)) |
409 | 0 | port=msg->rcv.src_port; |
410 | 0 | else port=via->port; |
411 | 0 | if(via->maddr) |
412 | 0 | name= &(via->maddr->value); |
413 | 0 | else |
414 | 0 | name=&(via->host); /* received=ip in 1st via is ignored (it's |
415 | | not added by us so it's bad) */ |
416 | 0 | }else{ |
417 | | /* "normal" reply, we use rport's & received value if present */ |
418 | 0 | if (via->rport && via->rport->value.s){ |
419 | 0 | LM_DBG("using 'rport'\n"); |
420 | 0 | port=str2s(via->rport->value.s, via->rport->value.len, &err); |
421 | 0 | if (err){ |
422 | 0 | LM_NOTICE("bad rport value(%.*s)\n", |
423 | 0 | via->rport->value.len,via->rport->value.s); |
424 | 0 | port=0; |
425 | 0 | } |
426 | 0 | } |
427 | |
|
428 | 0 | if (via->maddr){ |
429 | 0 | name= &(via->maddr->value); |
430 | 0 | if (port==0) port=via->port?via->port:SIP_PORT; |
431 | 0 | } else if (via->received){ |
432 | 0 | LM_DBG("using 'received'\n"); |
433 | 0 | name=&(via->received->value); |
434 | | /* making sure that we won't do SRV lookup on "received" */ |
435 | 0 | if (port==0) port=via->port?via->port:SIP_PORT; |
436 | 0 | }else{ |
437 | 0 | LM_DBG("using via host\n"); |
438 | 0 | name=&(via->host); |
439 | 0 | if (port==0) port=via->port; |
440 | 0 | } |
441 | 0 | } |
442 | 0 | LM_DBG("trying SRV lookup\n"); |
443 | 0 | he=sip_resolvehost(name, &port, &via->proto, 0, 0); |
444 | |
|
445 | 0 | if (he==0){ |
446 | 0 | LM_NOTICE("resolve_host(%.*s) failure\n", name->len, name->s); |
447 | 0 | return -1; |
448 | 0 | } |
449 | | |
450 | 0 | hostent2su( to, he, 0, port); |
451 | 0 | return 1; |
452 | 0 | } |
453 | | |
454 | | |
455 | | |
456 | | /*! \brief removes first via & sends msg to the second */ |
457 | | int forward_reply(struct sip_msg* msg) |
458 | 0 | { |
459 | 0 | char* new_buf; |
460 | 0 | union sockaddr_union* to; |
461 | 0 | unsigned int new_len; |
462 | 0 | struct sr_module *mod; |
463 | 0 | int proto; |
464 | 0 | unsigned int id; /* used only by tcp*/ |
465 | 0 | const struct socket_info *send_sock; |
466 | 0 | char* s; |
467 | 0 | int len; |
468 | |
|
469 | 0 | to=0; |
470 | 0 | id=0; |
471 | 0 | new_buf=0; |
472 | | /*check if first via host = us */ |
473 | 0 | if (check_via){ |
474 | 0 | if (check_self(&msg->via1->host, |
475 | 0 | msg->via1->port?msg->via1->port:SIP_PORT, |
476 | 0 | msg->via1->proto)!=1){ |
477 | 0 | LM_ERR("host in first via!=me : %.*s:%d\n", |
478 | 0 | msg->via1->host.len, msg->via1->host.s, msg->via1->port); |
479 | | /* send error msg back? */ |
480 | 0 | goto error; |
481 | 0 | } |
482 | 0 | } |
483 | | /* quick hack, slower for multiple modules*/ |
484 | 0 | for (mod=modules;mod;mod=mod->next){ |
485 | 0 | if ((mod->exports) && (mod->exports->response_f)){ |
486 | 0 | LM_DBG("found module %s, passing reply to it\n", |
487 | 0 | mod->exports->name); |
488 | 0 | if (mod->exports->response_f(msg)==0) goto skip; |
489 | 0 | } |
490 | 0 | } |
491 | | |
492 | | /* if stateless fwd was disabled, we cannot have stateless replies here*/ |
493 | 0 | if (sl_fwd_disabled) |
494 | 0 | goto skip; |
495 | | |
496 | | /* we have to forward the reply stateless, so we need second via -bogdan*/ |
497 | 0 | if (parse_headers( msg, HDR_VIA2_F, 0 )==-1 |
498 | 0 | || (msg->via2==0) || (msg->via2->error!=PARSE_OK)) |
499 | 0 | { |
500 | | /* no second via => error */ |
501 | 0 | LM_ERR("no 2nd via found in [%.*s] [%.*s] reply from [%s] for callid [%.*s]\n", |
502 | 0 | msg->first_line.u.reply.status.len, msg->first_line.u.reply.status.s, |
503 | 0 | msg->cseq->body.len, msg->cseq->body.s, |
504 | 0 | ip_addr2a(&msg->rcv.src_ip), |
505 | 0 | msg->callid->body.len, msg->callid->body.s); |
506 | 0 | goto error; |
507 | 0 | } |
508 | | |
509 | 0 | to=(union sockaddr_union*)pkg_malloc(sizeof(union sockaddr_union)); |
510 | 0 | if (to==0){ |
511 | 0 | LM_ERR("out of pkg memory\n"); |
512 | 0 | goto error; |
513 | 0 | } |
514 | | |
515 | 0 | proto=msg->via2->proto; |
516 | 0 | if (update_sock_struct_from_via( to, msg, msg->via2 )==-1) goto error; |
517 | | |
518 | 0 | if (is_tcp_based_proto(proto)){ |
519 | | /* find id in i param if it exists */ |
520 | 0 | if (msg->via1->i&&msg->via1->i->value.s){ |
521 | 0 | s=msg->via1->i->value.s; |
522 | 0 | len=msg->via1->i->value.len; |
523 | 0 | if (reverse_hex2int(s, len, &id)<0) |
524 | 0 | id = 0; |
525 | 0 | } |
526 | 0 | } |
527 | |
|
528 | 0 | send_sock = get_send_socket(msg, to, proto); |
529 | |
|
530 | 0 | new_buf = build_res_buf_from_sip_res( msg, &new_len, send_sock,0); |
531 | 0 | if (!new_buf){ |
532 | 0 | LM_ERR("failed to build rpl from req failed\n"); |
533 | 0 | goto error; |
534 | 0 | } |
535 | | |
536 | 0 | if (msg->flags & tcp_no_new_conn_rplflag) |
537 | 0 | tcp_no_new_conn = 1; |
538 | |
|
539 | 0 | if (msg_send(send_sock, proto, to, id, new_buf, new_len, msg)<0) { |
540 | 0 | tcp_no_new_conn = 0; |
541 | 0 | update_stat( drp_rpls, 1); |
542 | 0 | goto error0; |
543 | 0 | } |
544 | 0 | tcp_no_new_conn = 0; |
545 | |
|
546 | 0 | update_stat( fwd_rpls, 1); |
547 | | /* |
548 | | * If no port is specified in the second via, then this |
549 | | * message output a wrong port number - zero. Despite that |
550 | | * the correct port is choosen in update_sock_struct_from_via, |
551 | | * as its visible with su_getport(to); . |
552 | | */ |
553 | 0 | LM_DBG("reply forwarded to %.*s:%d\n", msg->via2->host.len, |
554 | 0 | msg->via2->host.s, (unsigned short) msg->via2->port); |
555 | |
|
556 | 0 | pkg_free(new_buf); |
557 | 0 | pkg_free(to); |
558 | 0 | skip: |
559 | 0 | return 0; |
560 | 0 | error: |
561 | 0 | update_stat( err_rpls, 1); |
562 | 0 | error0: |
563 | 0 | if (new_buf) pkg_free(new_buf); |
564 | 0 | if (to) pkg_free(to); |
565 | 0 | return -1; |
566 | 0 | } |