コード例 #1
0
ファイル: t_msgbuilder.c プロジェクト: lbalaceanu/kamailio
/**
 * Evaluates the routing elements in locally originated request or reply to
 * locally originated request.
 * If original INVITE was in-dialog (had to-tag), it uses the
 * routes present there (b/c the 2xx for it does not have a RR set, normally).
 * Otherwise, use the reply (b/c the INVITE does not have yet the complete
 * route set).
 *
 * @return: negative for failure; out params:
 *  - list: route set;
 *  - ruri: RURI to be used in ACK;
 *  - nexthop: where to first send the ACK.
 *
 *  NOTE: assumes rpl's parsed to EOF!
 *
 */
static int eval_uac_routing(sip_msg_t *rpl, const struct retr_buf *inv_rb,
		str* contact, struct rte **list, str *ruri, str *next_hop)
{
	sip_msg_t orig_inv, *sipmsg; /* reparse original INVITE */
	rte_t *t, *prev_t, *rtset = NULL;
	int is_req;
	struct sip_uri puri;
	static size_t chklen;

	/* parse the retr. buffer */
	memset(&orig_inv, 0, sizeof(struct sip_msg));
	orig_inv.buf = inv_rb->buffer;
	orig_inv.len = inv_rb->buffer_len;
	LM_DBG("reparsing retransmission buffer of original INVITE:\n%.*s\n",
			(int)orig_inv.len, orig_inv.buf);
	if (parse_msg(orig_inv.buf, orig_inv.len, &orig_inv) != 0) {
		LM_ERR("failed to parse retr buffer (weird!): \n%.*s\n",
				(int)orig_inv.len, orig_inv.buf);
		return -1;
	}

	/* check if we need to look at request or reply */
	if ((parse_headers(&orig_inv, HDR_TO_F, 0) < 0) || (! orig_inv.to)) {
		/* the bug is at message assembly */
		LM_BUG("failed to parse INVITE retr. buffer and/or extract 'To' HF:"
				"\n%.*s\n", (int)orig_inv.len, orig_inv.buf);
		goto error;
	}
	if (((struct to_body *)orig_inv.to->parsed)->tag_value.len) {
		LM_DBG("building ACK for in-dialog INVITE (using RS in orig. INV.)\n");
		if (parse_headers(&orig_inv, HDR_EOH_F, 0) < 0) {
			LM_BUG("failed to parse INVITE retr. buffer to EOH:"
					"\n%.*s\n", (int)orig_inv.len, orig_inv.buf);
			goto error;
		}
		sipmsg = &orig_inv;
		is_req = 1;
	} else {
		LM_DBG("building ACK for out-of-dialog INVITE (using RS in RR set).\n");
		sipmsg = rpl;
		is_req = 0;
	}

	/* extract the route set */
	if (get_uac_rs(sipmsg, is_req, &rtset) < 0) {
		LM_ERR("failed to extract route set.\n");
		goto error;
	}

	if (! rtset) { /* No routes */
		*ruri = *contact;
		*next_hop = *contact;
	} else if (! is_req) { /* out of dialog req. */
		if (parse_uri(rtset->ptr->nameaddr.uri.s, rtset->ptr->nameaddr.uri.len,
				&puri) < 0) {
			LM_ERR("failed to parse first route in set.\n");
			goto error;
		}

		if (puri.lr.s) { /* Next hop is loose router */
			*ruri = *contact;
			*next_hop = rtset->ptr->nameaddr.uri;
		} else { /* Next hop is strict router */
			*ruri = rtset->ptr->nameaddr.uri;
			*next_hop = *ruri;
			/* consume first route, b/c it will be put in RURI */
			t = rtset;
			rtset = rtset->next;
			pkg_free(t);
		}
	} else {
		unsigned long route_flags = inv_rb->flags;
		LM_DBG("UAC rb flags: 0x%x.\n", (unsigned int)route_flags);
eval_flags:
		switch (route_flags & (F_RB_NH_LOOSE|F_RB_NH_STRICT)) {
		case 0:
			LM_WARN("calculate_hooks() not called when built the local UAC of "
					"in-dialog request, or called with empty route set.\n");
			/* try to figure out what kind of hop is the next one
			 * (strict/loose) by reading the original invite */
			if ((route_flags = nhop_type(&orig_inv, rtset, &inv_rb->dst,
					contact))) {
				LM_DBG("original request's next hop type evaluated to: 0x%x.\n",
						(unsigned int)route_flags);
				goto eval_flags;
			} else {
				LM_ERR("failed to establish what kind of router the next "
						"hop is.\n");
				goto error;
			}
			break;
		case F_RB_NH_LOOSE:
			*ruri = *contact;
			*next_hop = rtset->ptr->nameaddr.uri;
			break;
		case F_RB_NH_STRICT:
			/* find ptr to last route body that contains the (possibly) old
			 * remote target
			 */
			for (t = rtset, prev_t = NULL; t->next; prev_t = t, t = t->next)
				;
			if ((t->ptr->len == contact->len) &&
					(memcmp(t->ptr->nameaddr.name.s, contact->s,
							contact->len) == 0)){
				/* the remote target didn't update -> keep the whole route set,
				 * including the last entry */
				/* do nothing */
			} else {
				/* trash last entry and replace with new remote target */
				free_rte_list(t);
				/* compact the rr_t struct along with rte. this way, free'ing
				 * it can be done along with rte chunk, independent of Route
				 * header parser's allocator (using pkg/shm) */
				chklen = sizeof(struct rte) + sizeof(rr_t);
				if (! (t = pkg_malloc(chklen))) {
					ERR("out of pkg memory (%d required)\n", (int)chklen);
					/* last element was freed, unlink it */
					if(prev_t == NULL) {
						/* there is only one elem in route set: the remote target */
						rtset = NULL;
					} else {
						prev_t->next = NULL;
					}
					goto error;
				}
				/* this way, .free_rr is also set to 0 (!!!) */
				memset(t, 0, chklen);
				((rr_t *)&t[1])->nameaddr.name = *contact;
				((rr_t *)&t[1])->len = contact->len;
				/* chain the new route elem in set */
				if (prev_t == NULL)
					/* there is only one elem in route set: the remote target */
					rtset = t;
				else
					prev_t->next = t;
			}

			*ruri = *GET_RURI(&orig_inv); /* reuse original RURI */
			*next_hop = *ruri;
			break;
		default:
			/* probably a mem corruption */
			LM_BUG("next hop of original request marked as both loose"
					" and strict router (buffer: %.*s).\n",
					inv_rb->buffer_len, inv_rb->buffer);
#ifdef EXTRA_DEBUG
			abort();
#else
			goto error;
#endif
		}
	}

	*list = rtset;
	free_sip_msg(&orig_inv);
	/* all went well */
	return 0;

error:
	free_sip_msg(&orig_inv);
	if (rtset)
		free_rte_list(rtset);
	return -1;
}
コード例 #2
0
ファイル: t_msgbuilder.c プロジェクト: lbalaceanu/kamailio
/**
 * Extract route set from the message (out of Record-Route, if reply, OR
 * Route, if request).
 * The route set is returned into the "UAC-format" (keep order for Rs, reverse
 * RRs).
 */
static inline int get_uac_rs(sip_msg_t *msg, int is_req, struct rte **rtset)
{
	struct hdr_field* ptr;
	rr_t *p, *new_p;
	struct rte *t, *head, *old_head;

	head = 0;
	for (ptr = is_req ? msg->route : msg->record_route; ptr; ptr = ptr->next) {
		switch (ptr->type) {
			case HDR_RECORDROUTE_T:
				if (is_req)
					continue;
				break;
			case HDR_ROUTE_T:
				if (! is_req)
					continue;
				break;
			default:
				continue;
		}
		if (parse_rr(ptr) < 0) {
			LM_ERR("failed to parse Record-/Route HF (%d).\n", ptr->type);
			goto err;
		}

		p = (rr_t*)ptr->parsed;
		while(p) {
			if (! (t = pkg_malloc(sizeof(struct rte)))) {
				LM_ERR("out of pkg mem (asked for: %d).\n",
						(int)sizeof(struct rte));
				goto err;
			}
			if (is_req) {
				/* in case of requests, the sip_msg structure is free'd before
				 * rte list is evaluated => must do a copy of it */
				if (duplicate_rr(&new_p, p) < 0) {
					pkg_free(t);
					LM_ERR("failed to duplicate RR");
					goto err;
				}
				t->ptr = new_p;
			} else {
				t->ptr = p;
			}
			t->free_rr = is_req;
			t->next = head;
			head = t;
			p = p->next;
		}
	}

	if (is_req) {
		/* harvesting the R/RR HF above inserts at head, which suites RRs (as
		 * they must be reversed, anyway), but not Rs => reverse once more */
		old_head = head;
		head = 0;
		while (old_head) {
			t = old_head;
			old_head = old_head->next;
			t->next = head;
			head = t;
		}
	}

	*rtset = head;
	return 0;
err:
	free_rte_list(head);
	return -1;
}
コード例 #3
0
/*
 * The function creates an ACK for a local INVITE. If 200 OK, route set 
 * will be created and parsed
 */
char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans,
							unsigned int branch, str* to, unsigned int *len)
{
	char *req_buf, *p, *via;
	unsigned int via_len;
	char branch_buf[MAX_BRANCH_PARAM_LEN];
	int branch_len;
	str branch_str;
	struct hostport hp;
	struct rte* list;
	str contact, ruri, *cont;
	struct socket_info* send_sock;
	str next_hop;


	if (rpl->first_line.u.reply.statuscode < 300 ) {
		/* build e2e ack for 2xx reply -> we need the route set */
		if (get_contact_uri(rpl, &contact) < 0) {
			return 0;
		}

		if (process_routeset(rpl, &contact, &list, &ruri, &next_hop) < 0) {
			return 0;
		}

		if ((contact.s != ruri.s) || (contact.len != ruri.len)) {
			/* contact != ruri means that the next
			 * hop is a strict router, cont will be non-zero
			 * and print_routeset will append it at the end
			 * of the route set
			 */
			cont = &contact;
		} else {
			/* Next hop is a loose router, nothing to append */
			cont = 0;
		}
	} else {
		/* build hop-by-hop ack for negative reply ->
		 * ruri is the same as in INVITE; no route set */
		ruri = Trans->uac[branch].uri;
		cont = 0;
		list = 0;
	}

	/* method, separators, version: "ACK sip:[email protected] SIP/2.0" */
	*len = SIP_VERSION_LEN + ACK_LEN + 2 /* spaces */ + CRLF_LEN;
	*len += ruri.len;

	/* use same socket as for INVITE -bogdan */
	send_sock = Trans->uac[branch].request.dst.send_sock;

	if (!t_calc_branch(Trans,  branch, branch_buf, &branch_len)) goto error;
	branch_str.s = branch_buf;
	branch_str.len = branch_len;
	set_hostport(&hp, 0);

	/* build via */
	via = via_builder(&via_len, send_sock, &branch_str, 0, 
			send_sock->proto, &hp);
	if (!via) {
		LM_ERR("no via header got from builder\n");
		goto error;
	}
	*len+= via_len;

	/*headers*/
	*len += Trans->from.len + Trans->callid.len + to->len +
		Trans->cseq_n.len + 1 + ACK_LEN + CRLF_LEN;

	/* copy'n'paste Route headers */
	*len += calc_routeset_len(list, cont);

	/* User Agent */
	if (server_signature)
		*len += user_agent_header.len + CRLF_LEN;

	/* Content Length, EoM */
	*len += CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN;

	req_buf = shm_malloc(*len + 1);
	if (!req_buf) {
		LM_ERR("no more share memory\n");
		goto error01;
	}
	p = req_buf;

	append_string( p, ACK " ", ACK_LEN+1 );
	append_string(p, ruri.s, ruri.len );
	append_string( p, " " SIP_VERSION CRLF, 1 + SIP_VERSION_LEN + CRLF_LEN);

	/* insert our via */
	append_string(p, via, via_len);

	/*other headers*/
	append_string(p, Trans->from.s, Trans->from.len);
	append_string(p, Trans->callid.s, Trans->callid.len);
	append_string(p, to->s, to->len);

	append_string(p, Trans->cseq_n.s, Trans->cseq_n.len);
	*(p++) = ' ';
	append_string(p, ACK CRLF, ACK_LEN+CRLF_LEN);

	/* Routeset */
	p = print_rs(p, list, cont);

	/* User Agent header, Content Length, EoM */
	if (server_signature) {
		append_string(p, user_agent_header.s, user_agent_header.len);
		append_string(p, CRLF CONTENT_LENGTH "0" CRLF CRLF,
			CRLF_LEN+CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN);
	} else {
		append_string(p, CONTENT_LENGTH "0" CRLF CRLF,
			CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN);
	}
	*p = 0;

	pkg_free(via);
	free_rte_list(list);
	return req_buf;
error01:
	pkg_free(via);
error:
	free_rte_list(list);
	return 0;
}
コード例 #4
0
ファイル: t_msgbuilder.c プロジェクト: lbalaceanu/kamailio
/*
 * The function creates an ACK to 200 OK. Route set will be created
 * and parsed and the dst parameter will contain the destination to which
 * the request should be send. The function is used by tm when it
 * generates local ACK to 200 OK (on behalf of applications using uac)
 */
char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans,
					unsigned int branch, str *hdrs, str *body,
					unsigned int *len, struct dest_info* dst)
{
	char *req_buf, *p, *via;
	unsigned int via_len;
	char branch_buf[MAX_BRANCH_PARAM_LEN];
	int branch_len;
	str branch_str;
	struct hostport hp;
	struct rte* list;
	str contact, ruri, *cont;
	str next_hop;
	str body_len;
	str _to, *to = &_to;
#ifdef USE_DNS_FAILOVER
	struct dns_srv_handle dns_h;
#endif
#ifdef WITH_AS_SUPPORT
	/* With AS support, TM allows for external modules to generate building of
	 * the ACK; in this case, the ACK's retransmission buffer is built once
	 * and kept in memory (to help when retransmitted 2xx are received and ACK
	 * must be resent).
	 * Allocation of the string raw buffer that holds the ACK is piggy-backed
	 * with allocation of the retransmission buffer (since both have the same
	 * life-cycle): both the string buffer and retransm. buffer are placed
	 * into the same allocated chunk of memory (retr. buffer first, string
	 * buffer follows).In this case, the 'len' param is used as in-out
	 * parameter: 'in' to give the extra space needed by the retr. buffer,
	 * 'out' to return the lenght of the allocated string buffer.
	 */
	unsigned offset = *len;
#endif

	if (parse_headers(rpl, HDR_EOH_F, 0) == -1 || !rpl->to) {
		LM_ERR("Error while parsing headers.\n");
		return 0;
	} else {
		_to.s = rpl->to->name.s;
		_to.len = rpl->to->len;
	}

	if (get_contact_uri(rpl, &contact) < 0) {
		return 0;
	}

	if (eval_uac_routing(rpl, &Trans->uac[branch].request, &contact,
			&list, &ruri, &next_hop) < 0) {
		LM_ERR("failed to evaluate routing elements.\n");
		return 0;
	}
	LM_DBG("ACK RURI: `%.*s', NH: `%.*s'.\n", STR_FMT(&ruri),
			STR_FMT(&next_hop));

	if ((contact.s != ruri.s) || (contact.len != ruri.len)) {
		/* contact != ruri means that the next
		 * hop is a strict router, cont will be non-zero
		 * and print_routeset will append it at the end
		 * of the route set
		 */
		cont = &contact;
	} else {
		/* Next hop is a loose router, nothing to append */
		cont = 0;
	}

	/* method, separators, version: "ACK sip:[email protected] SIP/2.0" */
	*len = SIP_VERSION_LEN + ACK_LEN + 2 /* spaces */ + CRLF_LEN;
	*len += ruri.len;

	/* dst */
	switch(cfg_get(tm, tm_cfg, local_ack_mode)){
		case 1:
			/* send the local 200 ack to the same dst as the corresp. invite*/
			*dst=Trans->uac[branch].request.dst;
			break;
		case 2:
			/* send the local 200 ack to the same dst as the 200 reply source*/
			init_dst_from_rcv(dst, &rpl->rcv);
			dst->send_flags=rpl->fwd_send_flags;
			break;
		case 0:
		default:
			/* rfc conformant behaviour: use the next_hop determined from the
			 * contact and the route set */
#ifdef USE_DNS_FAILOVER
		if (cfg_get(core, core_cfg, use_dns_failover)){
			dns_srv_handle_init(&dns_h);
			if ((uri2dst(&dns_h , dst, rpl, &next_hop, PROTO_NONE)==0) ||
					(dst->send_sock==0)){
				dns_srv_handle_put(&dns_h);
				LM_ERR("no socket found\n");
				goto error;
			}
			dns_srv_handle_put(&dns_h); /* not needed any more */
		}else{
			if ((uri2dst(0 , dst, rpl, &next_hop, PROTO_NONE)==0) ||
					(dst->send_sock==0)){
				LM_ERR("no socket found\n");
				goto error;
			}
		}
#else /* USE_DNS_FAILOVER */
		if ( (uri2dst( dst, rpl, &next_hop, PROTO_NONE)==0) ||
				(dst->send_sock==0)){
			LM_ERR("no socket found\n");
			goto error;
		}
#endif /* USE_DNS_FAILOVER */
		break;
	}

	/* via */
	if (!t_calc_branch(Trans,  branch, branch_buf, &branch_len)) goto error;
	branch_str.s = branch_buf;
	branch_str.len = branch_len;
	set_hostport(&hp, 0);
	via = via_builder(&via_len, NULL, dst, &branch_str, 0, &hp);
	if (!via) {
		LM_ERR("No via header got from builder\n");
		goto error;
	}
	*len+= via_len;

	/* headers */
	*len += Trans->from.len + Trans->callid.len + to->len + Trans->cseq_n.len + 1 + ACK_LEN + CRLF_LEN;

	/* copy'n'paste Route headers */

	*len += calc_routeset_len(list, cont);

	/* User Agent */
	if (server_signature) *len += user_agent_hdr.len + CRLF_LEN;
	/* extra headers */
	if (hdrs)
		*len += hdrs->len;
	/* body */
	if (body) {
		body_len.s = int2str(body->len, &body_len.len);
		*len += body->len;
	} else {
		body_len.len = 0;
		body_len.s = NULL; /*4gcc*/
		*len += 1; /* for the (Cont-Len:) `0' */
	}
	/* Content Length, EoM */
	*len += CONTENT_LENGTH_LEN + body_len.len + CRLF_LEN + CRLF_LEN;

#if WITH_AS_SUPPORT
	req_buf = shm_malloc(offset + *len + 1);
	req_buf += offset;
#else
	req_buf = shm_malloc(*len + 1);
#endif
	if (!req_buf) {
		LM_ERR("Cannot allocate memory (%u+1)\n", *len);
		goto error01;
	}
	p = req_buf;

	append_str( p, ACK, ACK_LEN );
	append_str( p, " ", 1 );
	append_str(p, ruri.s, ruri.len);
	append_str( p, " " SIP_VERSION CRLF, 1 + SIP_VERSION_LEN + CRLF_LEN);

	/* insert our via */
	append_str(p, via, via_len);

	/*other headers*/
	append_str(p, Trans->from.s, Trans->from.len);
	append_str(p, Trans->callid.s, Trans->callid.len);
	append_str(p, to->s, to->len);

	append_str(p, Trans->cseq_n.s, Trans->cseq_n.len);
	append_str( p, " ", 1 );
	append_str( p, ACK, ACK_LEN);
	append_str(p, CRLF, CRLF_LEN);

	/* Routeset */
	p = print_rs(p, list, cont);

	/* User Agent header */
	if (server_signature) {
		append_str(p, user_agent_hdr.s, user_agent_hdr.len);
		append_str(p, CRLF, CRLF_LEN);
	}

	/* extra headers */
	if (hdrs)
		append_str(p, hdrs->s, hdrs->len);

	/* Content Length, EoH, (body) */
	if (body) {
		append_str(p, CONTENT_LENGTH, CONTENT_LENGTH_LEN);
		append_str(p, body_len.s, body_len.len);
		append_str(p, /*end crr. header*/CRLF /*EoH*/CRLF, CRLF_LEN +
				CRLF_LEN);
		append_str(p, body->s, body->len);
	} else {
		append_str(p, CONTENT_LENGTH "0" CRLF CRLF,
				CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN);
	}

	/* EoM */
	*p = 0;

	pkg_free(via);
	free_rte_list(list);
	return req_buf;

error01:
	pkg_free(via);
error:
	free_rte_list(list);
	return 0;
}
コード例 #5
0
static inline int process_routeset(struct sip_msg* msg, str* contact, struct rte** list, str* ruri, str* next_hop)
{
	struct hdr_field* ptr;
	rr_t* p;
	struct rte* t, *head;
	struct sip_uri puri;
	
	ptr = msg->record_route;
	head = 0;
	while(ptr) {
		if (ptr->type == HDR_RECORDROUTE_T) {
			if (parse_rr(ptr) < 0) {
				LM_ERR("failed to parse Record-Route header\n");
				return -1;
			}
			
			p = (rr_t*)ptr->parsed;
			while(p) {
				t = (struct rte*)pkg_malloc(sizeof(struct rte));
				if (!t) {
					LM_ERR("no more pkg memory\n");
					free_rte_list(head);
					return -1;
				}
				t->ptr = p;
				t->next = head;
				head = t;
				p = p->next;
			}
		}
		ptr = ptr->next;
	}
	
	if (head) {
		if (parse_uri(head->ptr->nameaddr.uri.s, head->ptr->nameaddr.uri.len, &puri) < 0) {
			LM_ERR("failed to parse URI\n");
			free_rte_list(head);
			return -1;
		}
		
		if (puri.lr.s) {
			     /* Next hop is loose router */
			*ruri = *contact;
			*next_hop = head->ptr->nameaddr.uri;
		} else {
			     /* Next hop is strict router */
			*ruri = head->ptr->nameaddr.uri;
			*next_hop = *ruri;
			t = head;
			head = head->next;
			pkg_free(t);
		}
	} else {
		     /* No routes */
		*ruri = *contact;
		*next_hop = *contact;
	}
	
	*list = head;
	return 0;
}