Quellcode durchsuchen

raw dialog support added (untested !!)

Jan Janak vor 22 Jahren
Ursprung
Commit
cbac555e63
2 geänderte Dateien mit 815 neuen und 0 gelöschten Zeilen
  1. 715 0
      modules/tm/dlg.c
  2. 100 0
      modules/tm/dlg.h

+ 715 - 0
modules/tm/dlg.c

@@ -0,0 +1,715 @@
+/*
+ * $Id$
+ *
+ * History:
+ * -------
+ * 2003-03-29 Created by janakj
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "../../parser/contact/parse_contact.h"
+#include "../../parser/parse_to.h"
+#include "../../parser/parse_from.h"
+#include "../../trim.h"
+#include "../../ut.h"
+#include "dlg.h"
+
+
+#define NORMAL_ORDER 0  /* Create route set in normal order - UAS */
+#define REVERSE_ORDER 1 /* Create route set in reverse order - UAC */
+
+
+/*
+ * Make a copy of a str structure using pkg_malloc
+ */
+static inline int str_duplicate(str* _d, str* _s)
+{
+	_d->s = pkg_malloc(_s->len);
+	if (!_d->s) {
+		LOG(L_ERR, "str_duplicate(): No memory left\n");
+		return -1;
+	}
+	
+	memcpy(_d->s, _s->s, _s->len);
+	_d->len = _s->len;
+	return 0;
+}
+
+
+/*
+ * Create a new dialog
+ */
+int new_dlg_uac(str* _cid, str* _ltag, unsigned int _lseq, str* _luri, str* _ruri, dlg_t** _d)
+{
+	dlg_t* res;
+
+	if (!_cid || !_ltag || !_luri || !_ruri || !_d) {
+		LOG(L_ERR, "new_dlg_uac(): Invalid parameter value\n");
+		return -1;
+	}
+
+	res = (dlg_t*)pkg_malloc(sizeof(dlg_t));
+	if (res == 0) {
+		LOG(L_ERR, "new_dlg_uac(): No memory left\n");
+		return -2;
+	}
+	     /* Clear everything */
+	memset(res, 0, sizeof(dlg_t));
+	
+	     /* Make a copy of Call-ID */
+	if (str_duplicate(&res->id.call_id, _cid) < 0) return -3;
+	     /* Make a copy of local tag (usually From tag) */
+	if (str_duplicate(&res->id.loc_tag, _ltag) < 0) return -4;
+	     /* Make a copy of local URI (usually From) */
+	if (str_duplicate(&res->loc_uri, _luri) < 0) return -5;
+	     /* Make a copy of remote URI (usually To) */
+	if (str_duplicate(&res->rem_uri, _ruri) < 0) return -6;
+	     /* Make a copy of local sequence (usually CSeq) */
+	res->loc_seq.value = _lseq;
+	     /* And mark it as set */
+	res->loc_seq.is_set = 1;
+
+	*_d = res;
+	return 0;
+}
+
+
+/*
+ * Parse Contact header field body and extract URI
+ * Does not parse headers !!
+ */
+static inline int get_contact_uri(struct sip_msg* _m, str* _uri)
+{
+	contact_t* c;
+
+	_uri->len = 0;
+
+	if (!_m->contact) return 0;
+
+	if (parse_contact(_m->contact) < 0) {
+		LOG(L_ERR, "get_contact_uri(): Error while parsing Contact body\n");
+		return -2;
+	}
+
+	c = ((contact_body_t*)_m->contact->parsed)->contacts;
+
+	if (!c) {
+		LOG(L_ERR, "get_contact_uri(): Empty body or * contact\n");
+		return -3;
+	}
+
+	     /* FIXME: c->uri may contain name-addr !! */
+	_uri->s = c->uri.s;
+	_uri->len = c->uri.len;
+	return 0;
+}
+
+
+/*
+ * Extract tag from To header field of a response
+ * Doesn't parse message headers !!
+ */
+static inline int get_to_tag(struct sip_msg* _m, str* _tag)
+{
+	if (!_m->to) {
+		LOG(L_ERR, "get_to_tag(): To header field missing\n");
+		return -1;
+	}
+
+	if (get_to(_m)->tag_value.len) {
+		_tag->s = get_to(_m)->tag_value.s;
+		_tag->len = get_to(_m)->tag_value.len;
+	} else {
+		_tag->len = 0;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Extract tag from From header field of a request
+ */
+static inline int get_from_tag(struct sip_msg* _m, str* _tag)
+{
+	if (parse_from_header(_m) == -1) {
+		LOG(L_ERR, "get_from_tag(): Error while parsing From header\n");
+		return -1;
+	}
+
+	if (get_from(_m)->tag_value.len) {
+		_tag->s = get_from(_m)->tag_value.s;
+		_tag->len = get_from(_m)->tag_value.len;
+	} else {
+		_tag->len = 0;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Extract Call-ID value
+ * Doesn't parse headers !!
+ */
+static inline int get_callid(struct sip_msg* _m, str* _cid)
+{
+	if (_m->callid == 0) {
+		LOG(L_ERR, "get_callid(): Call-ID not found\n");
+		return -1;
+	}
+
+	_cid->s = _m->callid->body.s;
+	_cid->len = _m->callid->body.len;
+	trim(_cid);
+	return 0;
+}
+
+
+/*
+ * Create a copy of route set either in normal or reverse order
+ */
+static inline int get_route_set(struct sip_msg* _m, rr_t** _rs, unsigned char _order)
+{
+	struct hdr_field* ptr;
+	rr_t* last, *p, *t;
+	
+	last = 0;
+
+	ptr = _m->record_route;
+	while(ptr) {
+		if (ptr->type == HDR_RECORDROUTE) {
+			if (parse_rr(ptr) < 0) {
+				LOG(L_ERR, "get_route_set(): Error while parsing Record-Route body\n");
+				goto error;
+			}
+
+			p = (rr_t*)ptr->parsed;
+			while(p) {
+				if (duplicate_rr(_m, &t, p) < 0) {
+					LOG(L_ERR, "get_route_set(): Error while duplicating rr_t\n");
+					goto error;
+				}
+				
+				if (_order == NORMAL_ORDER) {
+					if (!*_rs) *_rs = t;
+					if (last) last->next = t;
+					last = t;
+				} else {
+					t->next = *_rs;
+					*_rs = t;
+				}
+
+				p = p->next;
+			}
+			
+		}
+		ptr = ptr->next;
+	}
+	
+	return 0;
+
+ error:
+        free_rr(_rs);
+	return -1;
+}
+
+
+/*
+ * Extract all necessarry information from a response and put it
+ * in a dialog structure
+ */
+static inline int response2dlg(struct sip_msg* _m, dlg_t* _d)
+{
+	str contact, rtag;
+
+	     /* Parse the whole message, we will need all Record-Route headers */
+	if (parse_headers(_m, HDR_EOH, 0) == -1) {
+		LOG(L_ERR, "dlg_new_resp_uac(): Error while parsing headers\n");
+		return -1;
+	}
+	
+	if (get_contact_uri(_m, &contact) < 0) return -2;
+	if (contact.len && str_duplicate(&_d->rem_target, &contact) < 0) return -3;
+	
+	if (get_to_tag(_m, &rtag) < 0) goto err1;
+	if (rtag.len && str_duplicate(&_d->id.rem_tag, &rtag) < 0) goto err1;
+	
+	if (get_route_set(_m, &_d->route_set, REVERSE_ORDER) < 0) goto err2;
+
+	return 0;
+ err2:
+	if (_d->id.rem_tag.s) pkg_free(_d->id.rem_tag.s);
+	_d->id.rem_tag.s = 0;
+	_d->id.rem_tag.len = 0;
+
+ err1:
+	if (_d->rem_target.s) pkg_free(_d->rem_target.s);
+	_d->rem_target.s = 0;
+	_d->rem_target.len = 0;
+	return -4;
+}
+
+
+/*
+ * Handle dialog in DLG_NEW state, we will be processing the
+ * first response
+ */
+static inline int dlg_new_resp_uac(dlg_t* _d, struct sip_msg* _m)
+{
+	int code;
+	     /*
+	      * Dialog is in DLG_NEW state, we will copy remote
+	      * target URI, remote tag if present, and route-set 
+	      * if present. And we will transit into DLG_CONFIRMED 
+	      * if the response was 2xx and to DLG_DESTROYED if the 
+	      * request was a negative final response.
+	      */
+
+	code = _m->first_line.u.reply.statuscode;
+
+	if (code < 200) {
+		     /* A provisional response, do nothing, we could
+		      * update remote tag and route set but we will do that
+		      * for a positive final response anyway and I don't want
+		      * bet on presence of these fields in provisional responses
+		      *
+		      * Send a request to [email protected] if you need to update
+		      * the structures here
+		      */
+	} else if ((code >= 200) && (code < 299)) {
+		     /* A final response, update the structures and transit
+		      * into DLG_CONFIRMED
+		      */
+		if (response2dlg(_m, _d) < 0) return -1;
+		_d->state = DLG_CONFIRMED;
+	} else {
+		     /* 
+		      * A negative final response, mark the dialog as destroyed
+		      * Again, I do not update the structures here becuase it
+		      * makes no sense to me, a dialog shouldn't be used after
+		      * it is destroyed
+		      */
+		_d->state = DLG_DESTROYED;
+		     /* Signalize the termination with positive return value */
+		return 1;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Handle dialog in DLG_EARLY state, we will be processing either
+ * next provisional response or a final response
+ */
+static inline int dlg_early_resp_uac(dlg_t* _d, struct sip_msg* _m)
+{
+	int code;
+	code = _m->first_line.u.reply.statuscode;	
+
+	if (code < 200) {
+		     /* We are in early state already, do nothing
+		      */
+	} else if ((code >= 200) && (code <= 299)) {
+		     /* Same as in dlg_new_resp_uac */
+		     /* A final response, update the structures and transit
+		      * into DLG_CONFIRMED
+		      */
+		if (response2dlg(_m, _d) < 0) return -1;
+		_d->state = DLG_CONFIRMED;
+	} else {
+		     /* Else terminate the dialog */
+		_d->state = DLG_DESTROYED;
+		     /* Signalize the termination with positive return value */
+		return 1;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Extract method from CSeq header field
+ */
+static inline int get_cseq_method(struct sip_msg* _m, str* _method)
+{
+	if (!_m->cseq && ((parse_headers(_m, HDR_CSEQ, 0) == -1) || !_m->cseq)) {
+		LOG(L_ERR, "get_cseq_method(): Error while parsing CSeq\n");
+		return -1;
+	}
+
+	_method->s = get_cseq(_m)->method.s;
+	_method->len = get_cseq(_m)->method.len;
+	return 0;
+}
+
+
+/*
+ * Handle dialog in DLG_CONFIRMED state, we will be processing
+ * a response to a request sent within a dialog
+ */
+static inline int dlg_confirmed_resp_uac(dlg_t* _d, struct sip_msg* _m)
+{
+	int code;
+	str method, contact;
+
+	code = _m->first_line.u.reply.statuscode;	
+
+	     /* Dialog has been already confirmed, that means we received
+	      * a response to a request sent within the dialog. We will
+	      * update remote target URI if and only if the message sent was
+	      * a target refresher. 
+	      */
+
+	     /* FIXME: Currently we support only INVITEs as target refreshers,
+	      * this should be generalized
+	      */
+
+	     /* IF we receive a 481 response, terminate the dialog because
+	      * the remote peer indicated that it didn't have the dialog
+	      * state anymore, signal this termination with a positive return
+	      * value
+	      */
+	if (code == 481) {
+		_d->state = DLG_DESTROYED;
+		return 1;
+	}
+
+	     /* Do nothing if not 2xx */
+	if ((code < 200) || (code >= 300)) return 0;
+	
+	if (get_cseq_method(_m, &method) < 0) return -1;
+	if ((method.len == 6) && !memcmp("INVITE", method.s, 6)) {
+		     /* Get contact if any and update remote target */
+		if (parse_headers(_m, HDR_CONTACT, 0) == -1) {
+			LOG(L_ERR, "dlg_confirmed_resp_uac(): Error while parsing headers\n");
+			return -2;
+		}
+
+		     /* Try to extract contact URI */
+		if (get_contact_uri(_m, &contact) < 0) return -3;
+		     /* If there is a contact URI */
+		if (contact.len) {
+			     /* Free old remote target if any */
+			if (_d->rem_target.s) pkg_free(_d->rem_target.s);
+			     /* Duplicate new remote target */
+			if (str_duplicate(&_d->rem_target, &contact) < 0) return -4;
+		}
+	}
+
+	return 0;
+}
+
+
+/*
+ * A response arrived, update dialog
+ */
+int dlg_response_uac(dlg_t* _d, struct sip_msg* _m)
+{
+	if (!_d || !_m) {
+		LOG(L_ERR, "dlg_response_uac(): Invalid parameter value\n");
+		return -1;
+	}
+
+	     /* The main dispatcher */
+	switch(_d->state) {
+	case DLG_NEW:       
+		return dlg_new_resp_uac(_d, _m);
+
+	case DLG_EARLY:     
+		return dlg_early_resp_uac(_d, _m);
+
+	case DLG_CONFIRMED: 
+		return dlg_confirmed_resp_uac(_d, _m);
+
+	case DLG_DESTROYED:
+		LOG(L_ERR, "dlg_response_uac(): Cannot handle destroyed dialog\n");
+		return -2;
+	}
+
+	LOG(L_ERR, "dlg_response_uac(): Error in switch statement\n");
+	return -3;
+}
+
+
+/*
+ * Get CSeq number
+ * Does not parse headers !!
+ */
+static inline int get_cseq_value(struct sip_msg* _m, unsigned int* _cs)
+{
+	str num;
+
+	if (_m->cseq == 0) {
+		LOG(L_ERR, "get_cseq_value(): CSeq header not found\n");
+		return -1;
+	}
+
+	num.s = get_cseq(_m)->number.s;
+	num.len = get_cseq(_m)->number.len;
+
+	trim_leading(&num);
+	if (str2int(&num, _cs) < 0) {
+		LOG(L_ERR, "get_cseq_value(): Error while converting cseq number\n");
+		return -2;
+	}
+	return 0;
+}
+
+
+/*
+ * Copy To or From URI without tag parameter
+ */
+static inline int get_dlg_uri(struct hdr_field* _h, str* _s)
+{
+	struct to_param* ptr;
+	struct to_body* body;
+	char* tag = 0; /* Makes gcc happy */
+	int tag_len = 0, len;
+
+	if (!_h) {
+		LOG(L_ERR, "get_dlg_uri(): Header field not found\n");
+		return -1;
+	}
+
+	     /* From was already parsed when extracting tag
+	      * and To is parsed by default
+	      */
+	
+	body = (struct to_body*)_h->parsed;
+
+	ptr = body->param_lst;
+	while(ptr) {
+		if (ptr->type == TAG_PARAM) break;
+		ptr = ptr->next;
+	}
+
+	if (ptr) {
+	     /* Tag param found */
+		tag = ptr->name.s;
+		if (ptr->next) {
+			tag_len = ptr->next->name.s - tag;
+		} else {
+			tag_len = body->body.s + body->body.len - tag;
+		}
+	}
+
+	_s->s = pkg_malloc(body->body.len - tag_len);
+	if (!_s->s) {
+		LOG(L_ERR, "get_dlg_uri(): No memory left\n");
+		return -1;
+	}
+
+	if (tag_len) {
+		len = tag - body->body.s;
+		memcpy(_s->s, body->body.s, len);
+		memcpy(_s->s + len, tag + tag_len, body->body.len - len - tag_len);
+		_s->len = body->body.len - tag_len;
+	} else {
+		memcpy(_s->s, body->body.s, body->body.len);
+		_s->len = body->body.len;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Extract all information from a request 
+ * and update a dialog structure
+ */
+static inline int request2dlg(struct sip_msg* _m, dlg_t* _d)
+{
+	str contact, rtag, callid;
+
+	if (parse_headers(_m, HDR_EOH, 0) == -1) {
+		LOG(L_ERR, "request2dlg(): Error while parsing headers");
+		return -1;
+	}
+
+	if (get_contact_uri(_m, &contact) < 0) return -2;
+	if (contact.len && str_duplicate(&_d->rem_target, &contact) < 0) return -3;
+	
+	if (get_from_tag(_m, &rtag) < 0) goto err1;
+	if (rtag.len && str_duplicate(&_d->id.rem_tag, &rtag) < 0) goto err1;
+
+	if (get_callid(_m, &callid) < 0) goto err2;
+	if (callid.len && str_duplicate(&_d->id.call_id, &callid) < 0) goto err2;
+
+	if (get_cseq_value(_m, &_d->rem_seq.value) < 0) goto err3;
+	_d->rem_seq.is_set = 1;
+
+	if (get_dlg_uri(_m->from, &_d->rem_uri) < 0) goto err3;
+	if (get_dlg_uri(_m->to, &_d->loc_uri) < 0) goto err4;
+
+	if (get_route_set(_m, &_d->route_set, NORMAL_ORDER) < 0) goto err5;	
+
+	return 0;
+ err5:
+	if (_d->loc_uri.s) pkg_free(_d->loc_uri.s);
+	_d->loc_uri.s = 0;
+	_d->loc_uri.len = 0;
+ err4:
+	if (_d->rem_uri.s) pkg_free(_d->rem_uri.s);
+	_d->rem_uri.s = 0;
+	_d->rem_uri.len = 0;
+ err3:
+	if (_d->id.call_id.s) pkg_free(_d->id.call_id.s);
+	_d->id.call_id.s = 0;
+	_d->id.call_id.len = 0;
+ err2:
+	if (_d->id.rem_tag.s) pkg_free(_d->id.rem_tag.s);
+	_d->id.rem_tag.s = 0;
+	_d->id.rem_tag.len = 0;
+ err1:
+	if (_d->rem_target.s) pkg_free(_d->rem_target.s);
+	_d->rem_target.s = 0;
+	_d->rem_target.len = 0;
+	return -4;
+}
+
+
+/*
+ * Establishing a new dialog, UAS side
+ */
+int new_dlg_uas(struct sip_msg* _req, int _code, str* _tag, dlg_t** _d)
+{
+	dlg_t* res;
+
+	if (!_req || !_tag || !_d) {
+		LOG(L_ERR, "new_dlg_uas(): Invalid parameter value\n");
+		return -1;
+	}
+
+	if ((_code < 200) || (_code > 299)) {
+		DBG("new_dlg_uas(): Not a 2xx, no dialog created\n");
+		return -2;
+	}
+
+	res = (dlg_t*)pkg_malloc(sizeof(dlg_t));
+	if (res == 0) {
+		LOG(L_ERR, "new_dlg_uac(): No memory left\n");
+		return -3;
+	}
+	     /* Clear everything */
+	memset(res, 0, sizeof(dlg_t));	
+
+	if (request2dlg(_req, res) < 0) {
+		LOG(L_ERR, "new_dlg_uas(): Error while converting request to dialog\n");
+		return -4;
+	}
+
+	if (_tag->len) {
+		if (str_duplicate(&res->id.loc_tag, _tag) < 0) {
+			free_dlg(res);
+			return -5;
+		}
+	}
+
+	*_d = res;
+	return 0;
+}
+
+
+/*
+ * UAS side - update a dialog from a request
+ */
+int dlg_request_uas(dlg_t* _d, struct sip_msg* _m)
+{
+	str contact;
+	int cseq;
+
+	if (!_d || !_m) {
+		LOG(L_ERR, "dlg_request_uas(): Invalid parameter value\n");
+		return -1;
+	}
+
+	     /* We must check if the request is not out of order or retransmission
+	      * first, if so then we will not update anything
+	      */
+	if (parse_headers(_m, HDR_CSEQ, 0) == -1) {
+		LOG(L_ERR, "dlg_request_uas(): Error while parsing headers\n");
+		return -2;
+	}
+	if (get_cseq_value(_m, &cseq) < 0) return -3;
+	if (_d->rem_seq.is_set && (cseq <= _d->rem_seq.value)) return 0;
+
+	     /* Neither out of order nor retransmission -> update */
+	_d->rem_seq.value = cseq;
+	_d->rem_seq.is_set = 1;
+	
+	     /* We will als update remote target URI if the message 
+	      * is target refresher
+	      */
+	if (_m->first_line.u.request.method_value == METHOD_INVITE) {
+		     /* target refresher */
+		if (parse_headers(_m, HDR_CONTACT, 0) == -1) {
+			LOG(L_ERR, "dlg_request_uas(): Error while parsing headers\n");
+			return -4;
+		}
+		
+		if (get_contact_uri(_m, &contact) < 0) return -5;
+		if (contact.len) {
+			if (_d->rem_target.s) pkg_free(_d->rem_target.s);
+			if (str_duplicate(&_d->rem_target, &contact) < 0) return -6;
+		}
+	}
+
+	return 0;
+}
+
+
+/*
+ * Destroy a dialog state
+ */
+void free_dlg(dlg_t* _d)
+{
+        if (!_d) return;
+
+	if (_d->id.call_id.s) pkg_free(_d->id.call_id.s);
+	if (_d->id.rem_tag.s) pkg_free(_d->id.rem_tag.s);
+	if (_d->id.loc_tag.s) pkg_free(_d->id.loc_tag.s);
+
+	if (_d->loc_uri.s) pkg_free(_d->loc_uri.s);
+	if (_d->rem_uri.s) pkg_free(_d->rem_uri.s);
+	if (_d->rem_target.s) pkg_free(_d->rem_target.s);
+
+	     /* Free all routes in the route set */
+	free_rr(&_d->route_set);
+	pkg_free(_d);
+}
+
+
+/*
+ * Print a dialog structure, just for debugging
+ */
+void print_dlg(dlg_t* _d)
+{
+	printf("====dlg_t===\n");
+	printf("id.call_id    : '%.*s'\n", _d->id.call_id.len, _d->id.call_id.s);
+	printf("id.rem_tag    : '%.*s'\n", _d->id.rem_tag.len, _d->id.rem_tag.s);
+	printf("id.loc_tag    : '%.*s'\n", _d->id.loc_tag.len, _d->id.loc_tag.s);
+	printf("loc_seq.value : %d\n", _d->loc_seq.value);
+	printf("loc_seq.is_set: %s\n", _d->loc_seq.is_set ? "YES" : "NO");
+	printf("rem_seq.value : %d\n", _d->rem_seq.value);
+	printf("rem_seq.is_set: %s\n", _d->rem_seq.is_set ? "YES" : "NO");
+	printf("loc_uri       : '%.*s'\n", _d->loc_uri.len, _d->loc_uri.s);
+	printf("rem_uri       : '%.*s'\n", _d->rem_uri.len, _d->rem_uri.s);
+	printf("rem_target    : '%.*s'\n", _d->rem_target.len, _d->rem_target.s);
+	printf("secure:       : %d\n", _d->secure);
+	printf("state         : ");
+	switch(_d->state) {
+	case DLG_NEW:       printf("DLG_NEW");       break;
+	case DLG_EARLY:     printf("DLG_EARLY");     break;
+	case DLG_CONFIRMED: printf("DLG_CONFIRMED"); break;
+	case DLG_DESTROYED: printf("DLG_DESTROYED"); break;
+	}
+	
+	print_rr(_d->route_set);
+	printf("====dlg_t====\n");
+}

+ 100 - 0
modules/tm/dlg.h

@@ -0,0 +1,100 @@
+/*
+ * $Id$
+ *
+ * History:
+ * -------
+ * 2003-03-29 Created by janakj
+ */
+
+#ifndef DLG_H
+#define DLG_H
+
+
+#include "../../str.h"
+#include "../../parser/parse_rr.h"
+#include "../../parser/msg_parser.h"
+
+
+/*
+ * Dialog sequence
+ */
+typedef struct dlg_seq {
+	unsigned int value;    /* Sequence value */
+	unsigned char is_set;  /* is_set flag */
+} dlg_seq_t;
+
+
+/*
+ * Dialog state
+ */
+typedef enum dlg_state {
+	DLG_NEW = 0,   /* New dialog, no reply received yet */
+	DLG_EARLY,     /* Early dialog, provisional response received */
+	DLG_CONFIRMED, /* Confirmed dialog, 2xx received */
+	DLG_DESTROYED  /* Destroyed dialog */
+} dlg_state_t;
+
+
+/*
+ * Structure describing a dialog identifier
+ */
+typedef struct dlg_id {
+	str call_id;    /* Call-ID */
+	str rem_tag;    /* Remote tag of the dialog */
+	str loc_tag;    /* Local tag of the dialog */
+} dlg_id_t;
+
+
+/*
+ * Structure representing dialog state
+ */
+typedef struct dlg {
+	dlg_id_t id;            /* Dialog identifier */
+	dlg_seq_t loc_seq;      /* Local sequence number */
+	dlg_seq_t rem_seq;      /* Remote sequence number */
+	str loc_uri;            /* Local URI */
+	str rem_uri;            /* Remote URI */
+	str rem_target;         /* Remote target URI */
+	unsigned char secure;   /* Secure flag -- currently not used */
+	dlg_state_t state;      /* State of the dialog */
+	rr_t* route_set;        /* Route set */
+} dlg_t;
+
+
+/*
+ * Create a new dialog
+ */
+int new_dlg_uac(str* _cid, str* _ltag, unsigned int _lseq, str* _luri, str* _ruri, dlg_t** _d);
+
+
+/*
+ * A response arrived, update dialog
+ */
+int dlg_response_uac(dlg_t* _d, struct sip_msg* _m);
+
+
+/*
+ * Establishing a new dialog, UAS side
+ */
+int new_dlg_uas(struct sip_msg* _req, int _code, str* _tag, dlg_t** _d);
+
+
+/*
+ * UAS side - update a dialog from a request
+ */
+int dlg_request_uas(dlg_t* _d, struct sip_msg* _m);
+
+
+/*
+ * Destroy a dialog state
+ */
+void free_dlg(dlg_t* _d);
+
+
+/*
+ * Print a dialog structure, just for debugging
+ */
+void print_dlg(dlg_t* _d);
+
+
+#endif /* DLG_H */