tm/uac.c

00001 /*
00002  * $Id$
00003  *
00004  * simple UAC for things such as SUBSCRIBE or SMS gateway;
00005  * no authentication and other UAC features -- just send
00006  * a message, retransmit and await a reply; forking is not
00007  * supported during client generation, in all other places
00008  * it is -- adding it should be simple
00009  *
00010  * Copyright (C) 2001-2003 FhG Fokus
00011  *
00012  * This file is part of ser, a free SIP server.
00013  *
00014  * ser is free software; you can redistribute it and/or modify
00015  * it under the terms of the GNU General Public License as published by
00016  * the Free Software Foundation; either version 2 of the License, or
00017  * (at your option) any later version
00018  *
00019  * For a license to use the ser software under conditions
00020  * other than those described here, or to purchase support for this
00021  * software, please contact iptel.org by e-mail at the following addresses:
00022  *    info@iptel.org
00023  *
00024  * ser is distributed in the hope that it will be useful,
00025  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00026  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00027  * GNU General Public License for more details.
00028  *
00029  * You should have received a copy of the GNU General Public License 
00030  * along with this program; if not, write to the Free Software 
00031  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00032  *
00033  * History:
00034  * --------
00035  *  2003-01-23  t_uac_dlg now uses get_out_socket (jiri)
00036  *  2003-01-27  fifo:t_uac_dlg completed (jiri)
00037  *  2003-01-29  scratchpad removed (jiri)
00038  *  2003-02-13  t_uac, t _uac_dlg, gethfblock, uri2proxy changed to use 
00039  *               proto & rb->dst (andrei)
00040  *  2003-02-27  FIFO/UAC now dumps reply -- good for CTD (jiri)
00041  *  2003-02-28  scratchpad compatibility abandoned (jiri)
00042  *  2003-03-01  kr set through a function now (jiri)
00043  *  2003-03-19  replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei)
00044  *  2003-04-02  port_no_str does not contain a leading ':' anymore (andrei)
00045  *  2003-07-08  appropriate log messages in check_params(...), 
00046  *               call calculate_hooks if next_hop==NULL in t_uac (dcm) 
00047  *  2003-10-24  updated to the new socket_info lists (andrei)
00048  *  2003-12-03  completion filed removed from transaction and uac callbacks
00049  *              merged in transaction callbacks as LOCAL_COMPLETED (bogdan)
00050  *  2004-02-11  FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri)
00051  *  2004-02-13  t->is_invite, t->local, t->noisy_ctimer replaced (bogdan)
00052  *  2004-08-23  avp support in t_uac (bogdan)
00053  *  2005-12-16  t_uac will set the new_cell timers to the default values,
00054  *               fixes 0 fr_timer bug (andrei)
00055  *  2006-08-11  t_uac uses dns failover until it finds a send socket (andrei)
00056  *  2007-03-15  TMCB_ONSEND callbacks support added (andrei)
00057  *  2007-03-23  TMCB_LOCAL_REQUEST_IN callbacks support (andrei)
00058  *  2007-04-23  per dialog callbacks support (andrei)
00059  *  2007-06-01  support for per transaction different retransmissions intervals
00060  *              (andrei)
00061  */
00062 
00063 #include <string.h>
00064 #include "../../mem/shm_mem.h"
00065 #include "../../dprint.h"
00066 #include "../../globals.h"
00067 #include "../../md5.h"
00068 #include "../../crc.h"
00069 #include "../../ip_addr.h"
00070 #include "../../socket_info.h"
00071 #include "../../compiler_opt.h"
00072 #include "config.h"
00073 #include "ut.h"
00074 #include "h_table.h"
00075 #include "t_hooks.h"
00076 #include "t_funcs.h"
00077 #include "t_msgbuilder.h"
00078 #include "callid.h"
00079 #include "uac.h"
00080 #include "t_stats.h"
00081 #ifdef USE_DNS_FAILOVER
00082 #include "../../dns_cache.h"
00083 #include "../../cfg_core.h" /* cfg_get(core, core_cfg, use_dns_failover) */
00084 #endif
00085 #ifdef WITH_EVENT_LOCAL_REQUEST
00086 #include "../../receive.h"
00087 #include "../../route.h"
00088 #include "../../action.h"
00089 #include "../../onsend.h"
00090 #include "t_lookup.h"
00091 #endif
00092 
00093 #define FROM_TAG_LEN (MD5_LEN + 1 /* - */ + CRC16_LEN) /* length of FROM tags */
00094 
00095 #ifdef WITH_EVENT_LOCAL_REQUEST
00096 /* where to go for the local request route ("tm:local-request") */
00097 int goto_on_local_req=-1; /* default disabled */
00098 #endif /* WITH_EVEN_LOCAL_REQuEST */
00099 
00100 static char from_tag[FROM_TAG_LEN + 1];
00101 
00102 /*
00103  * Initialize UAC
00104  */
00105 int uac_init(void) 
00106 {
00107         str src[3];
00108         struct socket_info *si;
00109 
00110         if (RAND_MAX < TABLE_ENTRIES) {
00111                 LOG(L_WARN, "Warning: uac does not spread "
00112                     "across the whole hash table\n");
00113         }
00114         /* on tcp/tls bind_address is 0 so try to get the first address we listen
00115          * on no matter the protocol */
00116         si=bind_address?bind_address:get_first_socket();
00117         if (si==0){
00118                 LOG(L_CRIT, "BUG: uac_init: null socket list\n");
00119                 return -1;
00120         }
00121 
00122         /* calculate the initial From tag */
00123         src[0].s = "Long live SER server";
00124         src[0].len = strlen(src[0].s);
00125         src[1].s = si->address_str.s;
00126         src[1].len = strlen(src[1].s);
00127         src[2].s = si->port_no_str.s;
00128         src[2].len = strlen(src[2].s);
00129 
00130         MD5StringArray(from_tag, src, 3);
00131         from_tag[MD5_LEN] = '-';
00132         return 1;
00133 }
00134 
00135 
00136 /*
00137  * Generate a From tag
00138  */
00139 void generate_fromtag(str* tag, str* callid)
00140 {
00141              /* calculate from tag from callid */
00142         crcitt_string_array(&from_tag[MD5_LEN + 1], callid, 1);
00143         tag->s = from_tag; 
00144         tag->len = FROM_TAG_LEN;
00145 }
00146 
00147 
00148 /*
00149  * Check value of parameters
00150  */
00151 static inline int check_params(uac_req_t *uac_r, str* to, str* from)
00152 {
00153         if (!uac_r || !uac_r->method || !to || !from) {
00154                 LOG(L_ERR, "check_params(): Invalid parameter value\n");
00155                 return -1;
00156         }
00157 
00158         if (!uac_r->method->s || !uac_r->method->len) {
00159                 LOG(L_ERR, "check_params(): Invalid request method\n");
00160                 return -2;
00161         }
00162 
00163         if (!to->s || !to->len) {
00164                 LOG(L_ERR, "check_params(): Invalid To URI\n");
00165                 return -4;
00166         }
00167 
00168         if (!from->s || !from->len) {
00169                 LOG(L_ERR, "check_params(): Invalid From URI\n");
00170                 return -5;
00171         }
00172         return 0;
00173 }
00174 
00175 static inline unsigned int dlg2hash( dlg_t* dlg )
00176 {
00177         str cseq_nr;
00178         unsigned int hashid;
00179 
00180         cseq_nr.s=int2str(dlg->loc_seq.value, &cseq_nr.len);
00181         hashid=hash(dlg->id.call_id, cseq_nr);
00182         DBG("DEBUG: dlg2hash: %d\n", hashid);
00183         return hashid;
00184 }
00185 
00186 
00187 /* WARNING: - dst_cell contains the created cell, but it is un-referenced
00188  *            (before using it make sure you REF() it first)
00189  *          - if  ACK (method==ACK), a cell will be created but it will not
00190  *            be added in the hash table (should be either deleted by the 
00191  *            caller) 
00192  */
00193 static inline int t_uac_prepare(uac_req_t *uac_r, 
00194                 struct retr_buf **dst_req,
00195                 struct cell **dst_cell)
00196 {
00197         struct dest_info dst;
00198         struct cell *new_cell;
00199         struct retr_buf *request;
00200         char* buf;
00201         int buf_len, ret;
00202         unsigned int hi;
00203         int is_ack;
00204         ticks_t lifetime;
00205 #ifdef USE_DNS_FAILOVER
00206         struct dns_srv_handle dns_h;
00207 #endif
00208         long nhtype;
00209 #ifdef WITH_EVENT_LOCAL_REQUEST
00210         struct cell *backup_t;
00211         int backup_branch;
00212         unsigned int backup_msgid;
00213         static struct sip_msg lreq;
00214         char *buf1;
00215         int buf_len1;
00216         int sflag_bk;
00217         int backup_route_type;
00218 #endif
00219         snd_flags_t snd_flags;
00220         tm_xlinks_t backup_xd;
00221         tm_xdata_t local_xd;
00222 
00223         ret=-1;
00224         hi=0; /* make gcc happy */
00225         /*if (dst_req) *dst_req = NULL;*/
00226         is_ack = (((uac_r->method->len == 3) && (memcmp("ACK", uac_r->method->s, 3)==0)) ? 1 : 0);
00227         
00228         /*** added by dcm 
00229          * - needed by external ua to send a request within a dlg
00230          */
00231         if ((nhtype = w_calculate_hooks(uac_r->dialog)) < 0)
00232                 /* if err's returned, the message is incorrect */
00233                 goto error2;
00234 
00235         if (!uac_r->dialog->loc_seq.is_set) {
00236                 /* this is the first request in the dialog,
00237                 set cseq to default value now - Miklos */
00238                 uac_r->dialog->loc_seq.value = DEFAULT_CSEQ;
00239                 uac_r->dialog->loc_seq.is_set = 1;
00240         }
00241 
00242         DBG("DEBUG:tm:t_uac: next_hop=<%.*s>\n",uac_r->dialog->hooks.next_hop->len,
00243                         uac_r->dialog->hooks.next_hop->s);
00244         /* new message => take the dialog send_socket if set, or the default
00245           send_socket if not*/
00246         SND_FLAGS_INIT(&snd_flags);
00247 #ifdef USE_DNS_FAILOVER
00248         if (cfg_get(core, core_cfg, use_dns_failover)){
00249                 dns_srv_handle_init(&dns_h);
00250                 if ((uri2dst2(&dns_h, &dst, uac_r->dialog->send_sock, snd_flags,
00251                                                         uac_r->dialog->hooks.next_hop, PROTO_NONE)==0)
00252                                 || (dst.send_sock==0)){
00253                         dns_srv_handle_put(&dns_h);
00254                         ser_error = E_NO_SOCKET;
00255                         ret=ser_error;
00256                         LOG(L_ERR, "t_uac: no socket found\n");
00257                         goto error2;
00258                 }
00259                 dns_srv_handle_put(&dns_h); /* not needed anymore */
00260         }else{
00261                 if ((uri2dst2(0, &dst, uac_r->dialog->send_sock, snd_flags,
00262                                                 uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) ||
00263                                 (dst.send_sock==0)){
00264                         ser_error = E_NO_SOCKET;
00265                         ret=ser_error;
00266                         LOG(L_ERR, "t_uac: no socket found\n");
00267                         goto error2;
00268                 }
00269         }
00270 #else /* USE_DNS_FAILOVER */
00271         if ((uri2dst2(&dst, uac_r->dialog->send_sock, snd_flags,
00272                                         uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) ||
00273                         (dst.send_sock==0)){
00274                 ser_error = E_NO_SOCKET;
00275                 ret=ser_error;
00276                 LOG(L_ERR, "t_uac: no socket found\n");
00277                 goto error2;
00278         }
00279 #endif /* USE_DNS_FAILOVER */
00280 
00281         /* build cell sets X/AVP lists to new transaction structure
00282          * => bakup in a tmp struct and restore afterwards */
00283         memset(&local_xd, 0, sizeof(tm_xdata_t));
00284         tm_xdata_replace(&local_xd, &backup_xd);
00285         new_cell = build_cell(0); 
00286         tm_xdata_replace(0, &backup_xd);
00287 
00288         if (!new_cell) {
00289                 ret=E_OUT_OF_MEM;
00290                 LOG(L_ERR, "t_uac: short of cell shmem\n");
00291                 goto error2;
00292         }
00293         if (uac_r->method->len==INVITE_LEN && memcmp(uac_r->method->s, INVITE, INVITE_LEN)==0){
00294                 new_cell->flags |= T_IS_INVITE_FLAG;
00295                 new_cell->flags|=T_AUTO_INV_100 &
00296                                 (!cfg_get(tm, tm_cfg, tm_auto_inv_100) -1);
00297 #ifdef WITH_AS_SUPPORT
00298                 if (uac_r->cb_flags & TMCB_DONT_ACK)
00299                         new_cell->flags |= T_NO_AUTO_ACK;
00300 #endif
00301                 lifetime=cfg_get(tm, tm_cfg, tm_max_inv_lifetime);
00302         }else
00303                 lifetime=cfg_get(tm, tm_cfg, tm_max_noninv_lifetime);
00304         new_cell->flags |= T_IS_LOCAL_FLAG;
00305         /* init timers hack, new_cell->fr_timer and new_cell->fr_inv_timer
00306          * must be set, or else the fr will happen immediately
00307          * we can't call init_new_t() because we don't have a sip msg
00308          * => we'll ignore t_set_fr() or avp timer value and will use directly the
00309          * module params fr_inv_timer and fr_timer -- andrei */
00310         new_cell->fr_timeout=cfg_get(tm, tm_cfg, fr_timeout);
00311         new_cell->fr_inv_timeout=cfg_get(tm, tm_cfg, fr_inv_timeout);
00312         new_cell->end_of_life=get_ticks_raw()+lifetime;
00313 #ifdef TM_DIFF_RT_TIMEOUT
00314         /* same as above for retransmission intervals */
00315         new_cell->rt_t1_timeout_ms = cfg_get(tm, tm_cfg, rt_t1_timeout_ms);
00316         new_cell->rt_t2_timeout_ms = cfg_get(tm, tm_cfg, rt_t2_timeout_ms);
00317 #endif
00318 
00319         set_kr(REQ_FWDED);
00320 
00321         request = &new_cell->uac[0].request;
00322         request->dst = dst;
00323         request->flags |= nhtype;
00324 
00325         if (!is_ack) {
00326 #ifdef TM_DEL_UNREF
00327                 INIT_REF(new_cell, 1); /* ref'ed only from the hash */
00328 #endif
00329                 hi=dlg2hash(uac_r->dialog);
00330                 LOCK_HASH(hi);
00331                 insert_into_hash_table_unsafe(new_cell, hi);
00332                 UNLOCK_HASH(hi);
00333         }
00334 
00335         buf = build_uac_req(uac_r->method, uac_r->headers, uac_r->body, uac_r->dialog, 0, new_cell,
00336                 &buf_len, &dst);
00337         if (!buf) {
00338                 LOG(L_ERR, "t_uac: Error while building message\n");
00339                 ret=E_OUT_OF_MEM;
00340                 goto error1;
00341         }
00342 
00343 #ifdef WITH_EVENT_LOCAL_REQUEST
00344         if (unlikely(goto_on_local_req>=0)) {
00345                 DBG("executing event_route[tm:local-request]\n");
00346                 if(likely(build_sip_msg_from_buf(&lreq, buf, buf_len, inc_msg_no())
00347                                         == 0)) {
00348                         /* fill some field in sip_msg */
00349                         if (unlikely(set_dst_uri(&lreq, uac_r->dialog->hooks.next_hop))) {
00350                                 LM_ERR("failed to set dst_uri");
00351                                 free_sip_msg(&lreq);
00352                         } else {
00353                                 struct onsend_info onsnd_info;
00354 
00355                                 lreq.force_send_socket = uac_r->dialog->send_sock;
00356                                 lreq.rcv.proto = dst.send_sock->proto;
00357                                 lreq.rcv.src_ip = dst.send_sock->address;
00358                                 lreq.rcv.src_port = dst.send_sock->port_no;
00359                                 lreq.rcv.dst_port = su_getport(&dst.to);
00360                                 su2ip_addr(&lreq.rcv.dst_ip, &dst.to);
00361                                 lreq.rcv.src_su=dst.send_sock->su;
00362                                 lreq.rcv.bind_address=dst.send_sock;
00363                         #ifdef USE_COMP
00364                                 lreq.rcv.comp=dst.comp;
00365                         #endif /* USE_COMP */
00366                                 sflag_bk = getsflags();
00367                                 tm_xdata_swap(new_cell, &backup_xd, 0);
00368 
00369                                 onsnd_info.to=&dst.to;
00370                                 onsnd_info.send_sock=dst.send_sock;
00371                                 onsnd_info.buf=buf;
00372                                 onsnd_info.len=buf_len;
00373                                 p_onsend=&onsnd_info;
00374 
00375                                 /* run the route */
00376                                 backup_route_type = get_route_type();
00377                                 set_route_type(LOCAL_ROUTE);
00378                                 /* set T to the current transaction */
00379                                 backup_t=get_t();
00380                                 backup_branch=get_t_branch();
00381                                 backup_msgid=global_msg_id;
00382                                 /* fake transaction and message id */
00383                                 global_msg_id=lreq.id;
00384                                 set_t(new_cell, T_BR_UNDEFINED);
00385                                 run_top_route(event_rt.rlist[goto_on_local_req], &lreq, 0);
00386                                 /* restore original environment */
00387                                 set_t(backup_t, backup_branch);
00388                                 global_msg_id=backup_msgid;
00389                                 set_route_type( backup_route_type );
00390                                 p_onsend=0;
00391 
00392                                 /* restore original environment */
00393                                 tm_xdata_swap(new_cell, &backup_xd, 1);
00394                                 setsflagsval(sflag_bk);
00395 
00396                                 if (unlikely(lreq.new_uri.s))
00397                                 {
00398                                         pkg_free(lreq.new_uri.s);
00399                                         lreq.new_uri.s=0;
00400                                         lreq.new_uri.len=0;
00401                                 }
00402                                 if (unlikely(lreq.dst_uri.s))
00403                                 {
00404                                         pkg_free(lreq.dst_uri.s);
00405                                         lreq.dst_uri.s=0;
00406                                         lreq.dst_uri.len=0;
00407                                 }
00408 
00409                                 if (unlikely(lreq.add_rm || lreq.body_lumps)) {
00410                                         LM_DBG("apply new updates to sip msg\n");
00411                                         buf1 = build_req_buf_from_sip_req(&lreq,
00412                                                         (unsigned int*)&buf_len1,
00413                                                         &dst, BUILD_NO_LOCAL_VIA|BUILD_NO_VIA1_UPDATE|
00414                                                         BUILD_IN_SHM);
00415                                         if (likely(buf1)){
00416                                                 shm_free(buf);
00417                                                 buf = buf1;
00418                                                 buf_len = buf_len1;
00419                                                 /* a possible change of the method is not handled! */
00420                                         }
00421                                 }
00422                                 lreq.buf=0; /* covers the obsolete DYN_BUF */
00423                                 free_sip_msg(&lreq);
00424                         }
00425                 }
00426         }
00427 #endif
00428 
00429         new_cell->method.s = buf;
00430         new_cell->method.len = uac_r->method->len;
00431 
00432         request->buffer = buf;
00433         request->buffer_len = buf_len;
00434         new_cell->nr_of_outgoings++;
00435 
00436         /* Register the callbacks after everything is successful and nothing can fail.
00437         Otherwise the callback parameter would be freed twise, once from TMCB_DESTROY,
00438         and again because of the negative return code. */
00439         if(uac_r->cb && insert_tmcb(&(new_cell->tmcb_hl), uac_r->cb_flags, 
00440                                                                 *(uac_r->cb), uac_r->cbp, NULL)!=1){
00441                 ret=E_OUT_OF_MEM; 
00442                 LOG(L_ERR, "t_uac: short of tmcb shmem\n");
00443                 goto error1;
00444         }
00445         if (has_local_reqin_tmcbs())
00446                         run_local_reqin_callbacks(new_cell, 0, 0);
00447 #ifdef DIALOG_CALLBACKS
00448         run_trans_dlg_callbacks(uac_r->dialog, new_cell, request);
00449 #endif /* DIALOG_CALLBACKS */
00450         if (dst_req) *dst_req = request;
00451         if (dst_cell) *dst_cell = new_cell;
00452         else if(is_ack && dst_req==0){
00453                 free_cell(new_cell);
00454         }
00455         
00456         return 1;
00457 
00458  error1:
00459         if (!is_ack) {
00460                 LOCK_HASH(hi);
00461                 remove_from_hash_table_unsafe(new_cell);
00462                 UNLOCK_HASH(hi);
00463 #ifdef TM_DEL_UNREF
00464                 UNREF_FREE(new_cell);
00465         }else
00466 #else
00467         }
00468 #endif
00469                 free_cell(new_cell);
00470 error2:
00471         return ret;
00472 }
00473 
00474 /*
00475  * Prepare a message within a dialog
00476  */
00477 int prepare_req_within(uac_req_t *uac_r,
00478                 struct retr_buf **dst_req)
00479 {
00480         if (!uac_r || !uac_r->method || !uac_r->dialog) {
00481                 LOG(L_ERR, "req_within: Invalid parameter value\n");
00482                 goto err;
00483         }
00484 
00485         if (uac_r->dialog->state != DLG_CONFIRMED) {
00486                 LOG(L_ERR, "req_within: Dialog is not confirmed yet\n");
00487                 goto err;
00488         }
00489 
00490         if ((uac_r->method->len == 3) && (!memcmp("ACK", uac_r->method->s, 3))) goto send;
00491         if ((uac_r->method->len == 6) && (!memcmp("CANCEL", uac_r->method->s, 6))) goto send;
00492         uac_r->dialog->loc_seq.value++; /* Increment CSeq */
00493  send:
00494         return t_uac_prepare(uac_r, dst_req, 0);
00495 
00496  err:
00497         /* if (cbp) shm_free(cbp); */
00498         /* !! never free cbp here because if t_uac_prepare fails, cbp is not freed
00499          * and thus caller has no chance to discover if it is freed or not !! */
00500         return -1;
00501 }
00502 
00503 static inline void send_prepared_request_impl(struct retr_buf *request, int retransmit)
00504 {
00505         if (SEND_BUFFER(request) == -1) {
00506                 LOG(L_ERR, "t_uac: Attempt to send to precreated request failed\n");
00507         }
00508         else if (unlikely(has_tran_tmcbs(request->my_T, TMCB_REQUEST_SENT)))
00509                 /* we don't know the method here */
00510                         run_trans_callbacks_with_buf(TMCB_REQUEST_SENT, request, 0, 0,
00511                         TMCB_LOCAL_F);
00512         
00513         if (retransmit && (start_retr(request)!=0))
00514                 LOG(L_CRIT, "BUG: t_uac: failed to start retr. for %p\n", request);
00515 }
00516 
00517 void send_prepared_request(struct retr_buf *request)
00518 {
00519         send_prepared_request_impl(request, 1 /* retransmit */);
00520 }
00521 
00522 /*
00523  * Send a request using data from the dialog structure
00524  */
00525 int t_uac(uac_req_t *uac_r)
00526 {
00527         return t_uac_with_ids(uac_r, NULL, NULL);
00528 }
00529 
00530 /*
00531  * Send a request using data from the dialog structure
00532  * ret_index and ret_label will identify the new cell
00533  */
00534 int t_uac_with_ids(uac_req_t *uac_r,
00535         unsigned int *ret_index, unsigned int *ret_label)
00536 {
00537         struct retr_buf *request;
00538         struct cell *cell;
00539         int ret;
00540         int is_ack;
00541 
00542         ret = t_uac_prepare(uac_r, &request, &cell);
00543         if (ret < 0) return ret;
00544         is_ack = (uac_r->method->len == 3) && (memcmp("ACK", uac_r->method->s, 3)==0) ? 1 : 0;
00545         send_prepared_request_impl(request, !is_ack /* retransmit */);
00546         if (is_ack) {
00547                 if (cell) free_cell(cell);
00548                 if (ret_index && ret_label)
00549                         *ret_index = *ret_label = 0;
00550         } else {
00551                 if (ret_index && ret_label) {
00552                         *ret_index = cell->hash_index;
00553                         *ret_label = cell->label;
00554                 }
00555         }
00556         return ret;
00557 }
00558 
00559 #ifdef WITH_AS_SUPPORT
00560 struct retr_buf *local_ack_rb(sip_msg_t *rpl_2xx, struct cell *trans,
00561                                         unsigned int branch, str *hdrs, str *body)
00562 {
00563         struct retr_buf *lack;
00564         unsigned int buf_len;
00565         char *buffer;
00566         struct dest_info dst;
00567 
00568         buf_len = (unsigned)sizeof(struct retr_buf);
00569         if (! (buffer = build_dlg_ack(rpl_2xx, trans, branch, hdrs, body, 
00570                         &buf_len, &dst))) {
00571                 return 0;
00572         } else {
00573                 /* 'buffer' now points into a contiguous chunk of memory with enough
00574                  * room to hold both the retr. buffer and the string raw buffer: it
00575                  * points to the begining of the string buffer; we iterate back to get
00576                  * the begining of the space for the retr. buffer. */
00577                 lack = &((struct retr_buf *)buffer)[-1];
00578                 lack->buffer = buffer;
00579                 lack->buffer_len = buf_len;
00580                 lack->dst = dst;
00581         }
00582 
00583         /* TODO: need next 2? */
00584         lack->activ_type = TYPE_LOCAL_ACK;
00585         lack->my_T = trans;
00586 
00587         return lack;
00588 }
00589 
00590 void free_local_ack(struct retr_buf *lack)
00591 {
00592         shm_free(lack);
00593 }
00594 
00595 void free_local_ack_unsafe(struct retr_buf *lack)
00596 {
00597         shm_free_unsafe(lack);
00598 }
00599 
00606 int ack_local_uac(struct cell *trans, str *hdrs, str *body)
00607 {
00608         struct retr_buf *local_ack, *old_lack;
00609         int ret;
00610         struct tmcb_params onsend_params;
00611 
00612         /* sanity checks */
00613 
00614 #ifdef EXTRA_DEBUG
00615         if (! trans) {
00616                 BUG("no transaction to ACK.\n");
00617                 abort();
00618         }
00619 #endif
00620 
00621 #define RET_INVALID \
00622                 ret = -2; \
00623                 goto fin
00624 
00625         if (! is_local(trans)) {
00626                 ERR("trying to ACK non local transaction (T@%p).\n", trans);
00627                 RET_INVALID;
00628         }
00629         if (! is_invite(trans)) {
00630                 ERR("trying to ACK non INVITE local transaction (T@%p).\n", trans);
00631                 RET_INVALID;
00632         }
00633         if (! trans->uac[0].reply) {
00634                 ERR("trying to ACK un-completed INVITE transaction (T@%p).\n", trans);
00635                 RET_INVALID;
00636         }
00637 
00638         if (! (trans->flags & T_NO_AUTO_ACK)) {
00639                 ERR("trying to ACK an auto-ACK transaction (T@%p).\n", trans);
00640                 RET_INVALID;
00641         }
00642         if (trans->uac[0].local_ack) {
00643                 ERR("trying to rebuild ACK retransmission buffer (T@%p).\n", trans);
00644                 RET_INVALID;
00645         }
00646 
00647         /* looks sane: build the retransmission buffer */
00648 
00649         if (! (local_ack = local_ack_rb(trans->uac[0].reply, trans, /*branch*/0, 
00650                         hdrs, body))) {
00651                 ERR("failed to build ACK retransmission buffer");
00652                 RET_INVALID;
00653         } else {
00654                 /* set the new buffer, but only if not already set (conc. invok.) */
00655                 if ((old_lack = (struct retr_buf *)atomic_cmpxchg_long(
00656                                 (void *)&trans->uac[0].local_ack, 0, (long)local_ack))) {
00657                         /* buffer already set: deny current attempt */
00658                         ERR("concurrent ACKing for local INVITE detected (T@%p).\n",trans);
00659                         free_local_ack(local_ack);
00660                         RET_INVALID;
00661                 }
00662         }
00663 
00664         if (msg_send(&local_ack->dst, local_ack->buffer, local_ack->buffer_len)<0){
00665                 /* hopefully will succeed on next 2xx retransmission */
00666                 ERR("failed to send local ACK (T@%p).\n", trans);
00667                 ret = -1;
00668                 goto fin;
00669         }
00670         else {
00671                 INIT_TMCB_ONSEND_PARAMS(onsend_params, 0, 0, &trans->uac[0].request,
00672                                                                 &local_ack->dst,
00673                                                                 local_ack->buffer, local_ack->buffer_len,
00674                                                                 TMCB_LOCAL_F, 0 /* branch */, TYPE_LOCAL_ACK);
00675                 run_trans_callbacks_off_params(TMCB_REQUEST_SENT, trans, &onsend_params);
00676         }
00677 
00678         ret = 0;
00679 fin:
00680         /* TODO: ugly! */
00681         /* FIXME: the T had been obtain by t_lookup_ident()'ing for it, so, it is
00682          * ref-counted. The t_unref() can not be used, as it requests a valid SIP
00683          * message (all available might be the reply, but if AS goes wrong and
00684          * tries to ACK before the final reply is received, we still have to
00685          * lookup the T to find this out). */
00686         UNREF( trans );
00687         return ret;
00688 
00689 #undef RET_INVALID
00690 }
00691 #endif /* WITH_AS_SUPPORT */
00692 
00693 
00694 /*
00695  * Send a message within a dialog
00696  */
00697 int req_within(uac_req_t *uac_r)
00698 {
00699         if (!uac_r || !uac_r->method || !uac_r->dialog) {
00700                 LOG(L_ERR, "req_within: Invalid parameter value\n");
00701                 goto err;
00702         }
00703 
00704         if ((uac_r->method->len == 3) && (!memcmp("ACK", uac_r->method->s, 3))) goto send;
00705         if ((uac_r->method->len == 6) && (!memcmp("CANCEL", uac_r->method->s, 6))) goto send;
00706         uac_r->dialog->loc_seq.value++; /* Increment CSeq */
00707  send:
00708         return t_uac(uac_r);
00709 
00710  err:
00711         /* callback parameter must be freed outside of tm module
00712         if (cbp) shm_free(cbp); */
00713         return -1;
00714 }
00715 
00716 
00717 /*
00718  * Send an initial request that will start a dialog
00719  * WARNING: writes uac_r->dialog
00720  */
00721 int req_outside(uac_req_t *uac_r, str* ruri, str* to, str* from, str *next_hop)
00722 {
00723         str callid, fromtag;
00724 
00725         if (check_params(uac_r, to, from) < 0) goto err;
00726         
00727         generate_callid(&callid);
00728         generate_fromtag(&fromtag, &callid);
00729 
00730         if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, &uac_r->dialog) < 0) {
00731                 LOG(L_ERR, "req_outside(): Error while creating new dialog\n");
00732                 goto err;
00733         }
00734 
00735         if (ruri) {
00736                 uac_r->dialog->rem_target.s = ruri->s;
00737                 uac_r->dialog->rem_target.len = ruri->len;
00738                 /* hooks will be set from w_calculate_hooks */
00739         }
00740 
00741         if (next_hop) uac_r->dialog->dst_uri = *next_hop;
00742         w_calculate_hooks(uac_r->dialog);
00743 
00744         return t_uac(uac_r);
00745 
00746  err:
00747         /* callback parameter must be freed outside of tm module
00748         if (cbp) shm_free(cbp); */
00749         return -1;
00750 }
00751 
00752 
00753 /*
00754  * Send a transactional request, no dialogs involved
00755  * WARNING: writes uac_r->dialog
00756  */
00757 int request(uac_req_t *uac_r, str* ruri, str* to, str* from, str *next_hop)
00758 {
00759         str callid, fromtag;
00760         dlg_t* dialog;
00761         int res;
00762 
00763         if (check_params(uac_r, to, from) < 0) goto err;
00764 
00765         generate_callid(&callid);
00766         generate_fromtag(&fromtag, &callid);
00767 
00768         if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, &dialog) < 0) {
00769                 LOG(L_ERR, "request(): Error while creating temporary dialog\n");
00770                 goto err;
00771         }
00772 
00773         if (ruri) {
00774                 dialog->rem_target.s = ruri->s;
00775                 dialog->rem_target.len = ruri->len;
00776                 /* hooks will be set from w_calculate_hooks */
00777         }
00778 
00779         if (next_hop) dialog->dst_uri = *next_hop;
00780         w_calculate_hooks(dialog);
00781 
00782         /* WARNING:
00783          * to be clean it should be called 
00784          *   set_dlg_target(dialog, ruri, next_hop);
00785          * which sets both uris if given [but it duplicates them in shm!]
00786          *
00787          * but in this case the _ruri parameter in set_dlg_target
00788          * must be optional (it is needed now) and following hacks
00789          *   dialog->rem_target.s = 0;
00790          *   dialog->dst_uri.s = 0;
00791          * before freeing dialog here must be removed
00792          */
00793         uac_r->dialog = dialog;
00794         res = t_uac(uac_r);
00795         dialog->rem_target.s = 0;
00796         dialog->dst_uri.s = 0;
00797         free_dlg(dialog);
00798         uac_r->dialog = 0;
00799         return res;
00800 
00801  err:
00802         /* callback parameter must be freed outside of tm module
00803         if (cp) shm_free(cp); */
00804         return -1;
00805 }