예제 #1
0
파일: uac.c 프로젝트: tsudot/kamailio
/* WARNING: - dst_cell contains the created cell, but it is un-referenced
 *            (before using it make sure you REF() it first)
 *          - if  ACK (method==ACK), a cell will be created but it will not
 *            be added in the hash table (should be either deleted by the 
 *            caller) 
 */
static inline int t_uac_prepare(uac_req_t *uac_r, 
		struct retr_buf **dst_req,
		struct cell **dst_cell)
{
	struct dest_info dst;
	struct cell *new_cell;
	struct retr_buf *request;
	char* buf;
	int buf_len, ret;
	unsigned int hi;
	int is_ack;
	ticks_t lifetime;
#ifdef USE_DNS_FAILOVER
	struct dns_srv_handle dns_h;
#endif
	long nhtype;
#ifdef WITH_EVENT_LOCAL_REQUEST
	struct cell *backup_t;
	int backup_branch;
	unsigned int backup_msgid;
	static struct sip_msg lreq;
	char *buf1;
	int buf_len1;
	int sflag_bk;
	int backup_route_type;
#endif
	snd_flags_t snd_flags;
	tm_xlinks_t backup_xd;
	tm_xdata_t local_xd;

	ret=-1;
	hi=0; /* make gcc happy */
	/*if (dst_req) *dst_req = NULL;*/
	is_ack = (((uac_r->method->len == 3) && (memcmp("ACK", uac_r->method->s, 3)==0)) ? 1 : 0);
	
	/*** added by dcm 
	 * - needed by external ua to send a request within a dlg
	 */
	if ((nhtype = w_calculate_hooks(uac_r->dialog)) < 0)
		/* if err's returned, the message is incorrect */
		goto error2;

	if (!uac_r->dialog->loc_seq.is_set) {
		/* this is the first request in the dialog,
		set cseq to default value now - Miklos */
		uac_r->dialog->loc_seq.value = DEFAULT_CSEQ;
		uac_r->dialog->loc_seq.is_set = 1;
	}

	DBG("DEBUG:tm:t_uac: next_hop=<%.*s>\n",uac_r->dialog->hooks.next_hop->len,
			uac_r->dialog->hooks.next_hop->s);
	/* new message => take the dialog send_socket if set, or the default
	  send_socket if not*/
	SND_FLAGS_INIT(&snd_flags);
#ifdef USE_DNS_FAILOVER
	if (cfg_get(core, core_cfg, use_dns_failover)){
		dns_srv_handle_init(&dns_h);
		if ((uri2dst2(&dns_h, &dst, uac_r->dialog->send_sock, snd_flags,
							uac_r->dialog->hooks.next_hop, PROTO_NONE)==0)
				|| (dst.send_sock==0)){
			dns_srv_handle_put(&dns_h);
			ser_error = E_NO_SOCKET;
			ret=ser_error;
			LOG(L_ERR, "t_uac: no socket found\n");
			goto error2;
		}
		dns_srv_handle_put(&dns_h); /* not needed anymore */
	}else{
		if ((uri2dst2(0, &dst, uac_r->dialog->send_sock, snd_flags,
						uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) ||
				(dst.send_sock==0)){
			ser_error = E_NO_SOCKET;
			ret=ser_error;
			LOG(L_ERR, "t_uac: no socket found\n");
			goto error2;
		}
	}
#else /* USE_DNS_FAILOVER */
	if ((uri2dst2(&dst, uac_r->dialog->send_sock, snd_flags,
					uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) ||
			(dst.send_sock==0)){
		ser_error = E_NO_SOCKET;
		ret=ser_error;
		LOG(L_ERR, "t_uac: no socket found\n");
		goto error2;
	}
#endif /* USE_DNS_FAILOVER */

	/* build cell sets X/AVP lists to new transaction structure
	 * => bakup in a tmp struct and restore afterwards */
	memset(&local_xd, 0, sizeof(tm_xdata_t));
	tm_xdata_replace(&local_xd, &backup_xd);
	new_cell = build_cell(0); 
	tm_xdata_replace(0, &backup_xd);

	if (!new_cell) {
		ret=E_OUT_OF_MEM;
		LOG(L_ERR, "t_uac: short of cell shmem\n");
		goto error2;
	}
	if (uac_r->method->len==INVITE_LEN && memcmp(uac_r->method->s, INVITE, INVITE_LEN)==0){
		new_cell->flags |= T_IS_INVITE_FLAG;
		new_cell->flags|=T_AUTO_INV_100 &
				(!cfg_get(tm, tm_cfg, tm_auto_inv_100) -1);
#ifdef WITH_AS_SUPPORT
		if (uac_r->cb_flags & TMCB_DONT_ACK)
			new_cell->flags |= T_NO_AUTO_ACK;
#endif
		lifetime=cfg_get(tm, tm_cfg, tm_max_inv_lifetime);
	}else
		lifetime=cfg_get(tm, tm_cfg, tm_max_noninv_lifetime);
	new_cell->flags |= T_IS_LOCAL_FLAG;
	/* init timers hack, new_cell->fr_timer and new_cell->fr_inv_timer
	 * must be set, or else the fr will happen immediately
	 * we can't call init_new_t() because we don't have a sip msg
	 * => we'll ignore t_set_fr() or avp timer value and will use directly the
	 * module params fr_inv_timer and fr_timer -- andrei */
	new_cell->fr_timeout=cfg_get(tm, tm_cfg, fr_timeout);
	new_cell->fr_inv_timeout=cfg_get(tm, tm_cfg, fr_inv_timeout);
	new_cell->end_of_life=get_ticks_raw()+lifetime;
#ifdef TM_DIFF_RT_TIMEOUT
	/* same as above for retransmission intervals */
	new_cell->rt_t1_timeout_ms = cfg_get(tm, tm_cfg, rt_t1_timeout_ms);
	new_cell->rt_t2_timeout_ms = cfg_get(tm, tm_cfg, rt_t2_timeout_ms);
#endif

	set_kr(REQ_FWDED);

	request = &new_cell->uac[0].request;
	request->dst = dst;
	request->flags |= nhtype;

	if (!is_ack) {
#ifdef TM_DEL_UNREF
		INIT_REF(new_cell, 1); /* ref'ed only from the hash */
#endif
		hi=dlg2hash(uac_r->dialog);
		LOCK_HASH(hi);
		insert_into_hash_table_unsafe(new_cell, hi);
		UNLOCK_HASH(hi);
	}

	buf = build_uac_req(uac_r->method, uac_r->headers, uac_r->body, uac_r->dialog, 0, new_cell,
		&buf_len, &dst);
	if (!buf) {
		LOG(L_ERR, "t_uac: Error while building message\n");
		ret=E_OUT_OF_MEM;
		goto error1;
	}

#ifdef WITH_EVENT_LOCAL_REQUEST
	if (unlikely(goto_on_local_req>=0)) {
		DBG("executing event_route[tm:local-request]\n");
		if(likely(build_sip_msg_from_buf(&lreq, buf, buf_len, inc_msg_no())
					== 0)) {
			/* fill some field in sip_msg */
			if (unlikely(set_dst_uri(&lreq, uac_r->dialog->hooks.next_hop))) {
				LM_ERR("failed to set dst_uri");
				free_sip_msg(&lreq);
			} else {
				struct onsend_info onsnd_info;

				lreq.force_send_socket = uac_r->dialog->send_sock;
				lreq.rcv.proto = dst.send_sock->proto;
				lreq.rcv.src_ip = dst.send_sock->address;
				lreq.rcv.src_port = dst.send_sock->port_no;
				lreq.rcv.dst_port = su_getport(&dst.to);
				su2ip_addr(&lreq.rcv.dst_ip, &dst.to);
				lreq.rcv.src_su=dst.send_sock->su;
				lreq.rcv.bind_address=dst.send_sock;
			#ifdef USE_COMP
				lreq.rcv.comp=dst.comp;
			#endif /* USE_COMP */
				sflag_bk = getsflags();
				tm_xdata_swap(new_cell, &backup_xd, 0);

				onsnd_info.to=&dst.to;
				onsnd_info.send_sock=dst.send_sock;
				onsnd_info.buf=buf;
				onsnd_info.len=buf_len;
				p_onsend=&onsnd_info;

				/* run the route */
				backup_route_type = get_route_type();
				set_route_type(LOCAL_ROUTE);
				/* set T to the current transaction */
				backup_t=get_t();
				backup_branch=get_t_branch();
				backup_msgid=global_msg_id;
				/* fake transaction and message id */
				global_msg_id=lreq.id;
				set_t(new_cell, T_BR_UNDEFINED);
				run_top_route(event_rt.rlist[goto_on_local_req], &lreq, 0);
				/* restore original environment */
				set_t(backup_t, backup_branch);
				global_msg_id=backup_msgid;
				set_route_type( backup_route_type );
				p_onsend=0;

				/* restore original environment */
				tm_xdata_swap(new_cell, &backup_xd, 1);
				setsflagsval(sflag_bk);

				if (unlikely(lreq.new_uri.s))
				{
					pkg_free(lreq.new_uri.s);
					lreq.new_uri.s=0;
					lreq.new_uri.len=0;
				}
				if (unlikely(lreq.dst_uri.s))
				{
					pkg_free(lreq.dst_uri.s);
					lreq.dst_uri.s=0;
					lreq.dst_uri.len=0;
				}

				if (unlikely(lreq.add_rm || lreq.body_lumps)) {
					LM_DBG("apply new updates to sip msg\n");
					buf1 = build_req_buf_from_sip_req(&lreq,
							(unsigned int*)&buf_len1,
							&dst, BUILD_NO_LOCAL_VIA|BUILD_NO_VIA1_UPDATE|
							BUILD_IN_SHM);
					if (likely(buf1)){
						shm_free(buf);
						buf = buf1;
						buf_len = buf_len1;
						/* a possible change of the method is not handled! */
					}
				}
				lreq.buf=0; /* covers the obsolete DYN_BUF */
				free_sip_msg(&lreq);
			}
		}
	}
#endif

	new_cell->method.s = buf;
	new_cell->method.len = uac_r->method->len;

	request->buffer = buf;
	request->buffer_len = buf_len;
	new_cell->nr_of_outgoings++;

	/* Register the callbacks after everything is successful and nothing can fail.
	Otherwise the callback parameter would be freed twise, once from TMCB_DESTROY,
	and again because of the negative return code. */
	if(uac_r->cb && insert_tmcb(&(new_cell->tmcb_hl), uac_r->cb_flags, 
								*(uac_r->cb), uac_r->cbp, NULL)!=1){
		ret=E_OUT_OF_MEM; 
		LOG(L_ERR, "t_uac: short of tmcb shmem\n");
		goto error1;
	}
	if (has_local_reqin_tmcbs())
			run_local_reqin_callbacks(new_cell, 0, 0);
#ifdef DIALOG_CALLBACKS
	run_trans_dlg_callbacks(uac_r->dialog, new_cell, request);
#endif /* DIALOG_CALLBACKS */
	if (dst_req) *dst_req = request;
	if (dst_cell) *dst_cell = new_cell;
	else if(is_ack && dst_req==0){
		free_cell(new_cell);
	}
	
	return 1;

 error1:
 	if (!is_ack) {
		LOCK_HASH(hi);
		remove_from_hash_table_unsafe(new_cell);
		UNLOCK_HASH(hi);
#ifdef TM_DEL_UNREF
		UNREF_FREE(new_cell);
	}else
#else
	}
#endif
		free_cell(new_cell);
error2:
	return ret;
}
예제 #2
0
/*!
 * helper function for stateless reply
 */
int sl_reply_helper(struct sip_msg *msg, int code, char *reason, str *tag)
{
    str buf = {0, 0};
    str dset = {0, 0};
    struct dest_info dst;
    struct bookmark dummy_bm;
    int backup_mhomed, ret;
    str text;

    int rt, backup_rt;
    struct run_act_ctx ctx;
    struct sip_msg pmsg;

    if (msg->first_line.u.request.method_value==METHOD_ACK)
        goto error;

    init_dest_info(&dst);
    if (reply_to_via) {
        if (update_sock_struct_from_via(&dst.to, msg, msg->via1 )==-1)
        {
            LOG(L_ERR, "ERROR: sl_reply_helper: cannot lookup reply dst: %s\n",
                msg->via1->host.s);
            goto error;
        }
    } else update_sock_struct_from_ip(&dst.to, msg);

    /* if that is a redirection message, dump current message set to it */
    if (code>=300 && code<400) {
        dset.s=print_dset(msg, &dset.len);
        if (dset.s) {
            add_lump_rpl(msg, dset.s, dset.len, LUMP_RPL_HDR);
        }
    }

    text.s = reason;
    text.len = strlen(reason);

    /* add a to-tag if there is a To header field without it */
    if ( 	/* since RFC3261, we append to-tags anywhere we can, except
		 * 100 replies */
        /* msg->first_line.u.request.method_value==METHOD_INVITE && */
        code>=180 &&
        (msg->to || (parse_headers(msg,HDR_TO_F, 0)!=-1 && msg->to))
        && (get_to(msg)->tag_value.s==0 || get_to(msg)->tag_value.len==0) )
    {
        if(tag!=NULL && tag->s!=NULL) {
            buf.s = build_res_buf_from_sip_req(code, &text, tag,
                                               msg, (unsigned int*)&buf.len, &dummy_bm);
        } else {
            calc_crc_suffix( msg, tag_suffix );
            buf.s = build_res_buf_from_sip_req(code, &text, &sl_tag, msg,
                                               (unsigned int*)&buf.len, &dummy_bm);
        }
    } else {
        buf.s = build_res_buf_from_sip_req(code, &text, 0, msg,
                                           (unsigned int*)&buf.len, &dummy_bm);
    }
    if (!buf.s)
    {
        DBG("DEBUG: sl_reply_helper: response building failed\n");
        goto error;
    }

    sl_run_callbacks(SLCB_REPLY_READY, msg, code, reason, &buf, &dst);

    /* supress multhoming support when sending a reply back -- that makes sure
       that replies will come from where requests came in; good for NATs
       (there is no known use for mhomed for locally generated replies;
        note: forwarded cross-interface replies do benefit of mhomed!
    */
    backup_mhomed=mhomed;
    mhomed=0;
    /* use for sending the received interface -bogdan*/
    dst.proto=msg->rcv.proto;
    dst.send_sock=msg->rcv.bind_address;
    dst.id=msg->rcv.proto_reserved1;
#ifdef USE_COMP
    dst.comp=msg->via1->comp_no;
#endif
    dst.send_flags=msg->rpl_send_flags;
    ret = msg_send(&dst, buf.s, buf.len);
    mhomed=backup_mhomed;

    rt = route_lookup(&event_rt, "sl:local-response");
    if (unlikely(rt >= 0 && event_rt.rlist[rt] != NULL))
    {
        if (likely(build_sip_msg_from_buf(&pmsg, buf.s, buf.len,
                                          inc_msg_no()) == 0))
        {
            char *tmp = NULL;
            struct onsend_info onsnd_info;

            onsnd_info.to=&dst.to;
            onsnd_info.send_sock=dst.send_sock;
            onsnd_info.buf=buf.s;
            onsnd_info.len=buf.len;
            p_onsend=&onsnd_info;

            if (unlikely(!IS_SIP(msg)))
            {
                /* This is an HTTP reply...  So fudge in a CSeq into the parsed message
                   message structure so that $rm will work in the route */
                struct hdr_field *hf;
                struct cseq_body *cseqb;
                char *tmp2;
                int len;

                if ((hf = (struct hdr_field *) pkg_malloc(sizeof(struct hdr_field))) == NULL)
                {
                    LM_ERR("out of package memory\n");
                    goto event_route_error;
                }

                if ((cseqb = (struct cseq_body *) pkg_malloc(sizeof(struct cseq_body))) == NULL)
                {
                    LM_ERR("out of package memory\n");
                    pkg_free(hf);
                    goto event_route_error;
                }

                if ((tmp = (char *) pkg_malloc(sizeof(char) * (msg->first_line.u.request.method.len + 5))) == NULL)
                {
                    LM_ERR("out of package memory\n");
                    pkg_free(cseqb);
                    pkg_free(hf);
                    goto event_route_error;
                }

                memset(hf, 0, sizeof(struct hdr_field));
                memset(cseqb, 0, sizeof(struct cseq_body));

                len = sprintf(tmp, "0 %.*s\r\n", msg->first_line.u.request.method.len, msg->first_line.u.request.method.s);
                tmp2 = parse_cseq(tmp, &tmp[len], cseqb);

                hf->type = HDR_CSEQ_T;
                hf->body.s = tmp;
                hf->body.len = tmp2 - tmp;
                hf->parsed = cseqb;

                pmsg.parsed_flag|=HDR_CSEQ_F;
                pmsg.cseq = hf;
                if (pmsg.last_header==0) {
                    pmsg.headers=hf;
                    pmsg.last_header=hf;
                } else {
                    pmsg.last_header->next=hf;
                    pmsg.last_header=hf;
                }
            }

            backup_rt = get_route_type();
            set_route_type(LOCAL_ROUTE);
            init_run_actions_ctx(&ctx);
            run_top_route(event_rt.rlist[rt], &pmsg, 0);
            set_route_type(backup_rt);
            p_onsend=0;

            if (tmp != NULL)
                pkg_free(tmp);

event_route_error:
            free_sip_msg(&pmsg);
        }
    }

    pkg_free(buf.s);

    if (ret<0) {
        goto error;
    }

    *(sl_timeout) = get_ticks() + SL_RPL_WAIT_TIME;

    update_sl_stats(code);
    return 1;

error:
    update_sl_failures();
    return -1;
}