transport: add automatic authentication scheme
[siplcs.git] / src / core / sip-transport.c
blob11eefaee66680c363d7d61d40d73675a04e8ad08
1 /**
2 * @file sip-transport.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2014 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 /**
24 * This module incapsulates SIP (RFC3261) protocol and provides
25 * higher level API (a layer) to XML-based SIPE (SIP with Extensions).
26 * Underlying leyer for this is TCP/SSL layer.
28 * A diagram in pseudographics:
30 * === SIPE (XML-based) layer ======================
31 * === SIP RFC3261 transport layer (This module) ===
32 * === TCP/SSL layer ===============================
34 * Authentication (Kerberos and NTLM) is applicable to this layer only.
35 * The same with message integtity (signing). No sip-sec* code should
36 * be used ourside of this module.
38 * SIP errors as codes(both as a return codes and network conditions) should be
39 * escalated to higher leyer (SIPE). Network conditions include no response
40 * within timeout interval.
42 * This module should support redirect internally. No escalations to higher
43 * layers needed.
45 * NO SIP-messages (headers) composing and processing should be outside of
46 * this module (!) Like headers: Via, Route, Contact, Authorization, etc.
47 * It's all irrelevant to higher layer responsibilities.
51 #ifdef HAVE_CONFIG_H
52 #include "config.h"
53 #endif
55 #include <stdlib.h>
56 #include <string.h>
57 #include <stdio.h>
59 #include <glib.h>
61 #include "sipe-common.h"
62 #include "sipmsg.h"
63 #include "sip-sec.h"
64 #include "sip-sec-digest.h"
65 #include "sip-transport.h"
66 #include "sipe-backend.h"
67 #include "sipe-core.h"
68 #include "sipe-core-private.h"
69 #include "sipe-certificate.h"
70 #include "sipe-dialog.h"
71 #include "sipe-incoming.h"
72 #include "sipe-nls.h"
73 #include "sipe-notify.h"
74 #include "sipe-schedule.h"
75 #include "sipe-sign.h"
76 #include "sipe-subscriptions.h"
77 #include "sipe-utils.h"
79 struct sip_auth {
80 guint type;
81 struct sip_sec_context *gssapi_context;
82 gchar *gssapi_data;
83 gchar *opaque;
84 const gchar *protocol;
85 gchar *realm;
86 gchar *sts_uri;
87 gchar *target;
88 guint version;
89 guint retries;
90 guint ntlm_num;
91 guint expires;
94 /* sip-transport.c private data */
95 struct sip_transport {
96 struct sipe_transport_connection *connection;
98 gchar *server_name;
99 guint server_port;
100 gchar *server_version;
102 gchar *user_agent;
104 GSList *transactions;
106 struct sip_auth registrar;
107 struct sip_auth proxy;
109 guint cseq;
110 guint register_attempt;
112 guint keepalive_timeout;
113 time_t last_message;
115 gboolean processing_input; /* whether full header received */
116 gboolean auth_incomplete; /* whether authentication not completed */
117 gboolean auth_retry; /* whether next authentication should be tried */
118 gboolean reregister_set; /* whether reregister timer set */
119 gboolean reauthenticate_set; /* whether reauthenticate timer set */
120 gboolean subscribed; /* whether subscribed to events, except buddies presence */
121 gboolean deregister; /* whether in deregistration */
124 /* Keep in sync with sipe_transport_type! */
125 static const char *transport_descriptor[] = { "", "tls", "tcp"};
126 #define TRANSPORT_DESCRIPTOR (transport_descriptor[transport->connection->type])
128 static char *genbranch()
130 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
131 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
132 rand() & 0xFFFF, rand() & 0xFFFF);
135 static void sipe_auth_free(struct sip_auth *auth)
137 g_free(auth->opaque);
138 auth->opaque = NULL;
139 auth->protocol = NULL;
140 g_free(auth->realm);
141 auth->realm = NULL;
142 g_free(auth->sts_uri);
143 auth->sts_uri = NULL;
144 g_free(auth->target);
145 auth->target = NULL;
146 auth->version = 0;
147 auth->type = SIPE_AUTHENTICATION_TYPE_UNSET;
148 auth->retries = 0;
149 auth->expires = 0;
150 g_free(auth->gssapi_data);
151 auth->gssapi_data = NULL;
152 sip_sec_destroy_context(auth->gssapi_context);
153 auth->gssapi_context = NULL;
156 static void sipe_make_signature(struct sipe_core_private *sipe_private,
157 struct sipmsg *msg)
159 struct sip_transport *transport = sipe_private->transport;
160 if (sip_sec_context_is_ready(transport->registrar.gssapi_context)) {
161 struct sipmsg_breakdown msgbd;
162 gchar *signature_input_str;
163 msgbd.msg = msg;
164 sipmsg_breakdown_parse(&msgbd, transport->registrar.realm, transport->registrar.target,
165 transport->registrar.protocol);
166 msgbd.rand = g_strdup_printf("%08x", g_random_int());
167 transport->registrar.ntlm_num++;
168 msgbd.num = g_strdup_printf("%d", transport->registrar.ntlm_num);
169 signature_input_str = sipmsg_breakdown_get_string(transport->registrar.version, &msgbd);
170 if (signature_input_str != NULL) {
171 char *signature_hex = sip_sec_make_signature(transport->registrar.gssapi_context, signature_input_str);
172 msg->signature = signature_hex;
173 msg->rand = g_strdup(msgbd.rand);
174 msg->num = g_strdup(msgbd.num);
175 g_free(signature_input_str);
177 sipmsg_breakdown_free(&msgbd);
181 static const gchar *const auth_type_to_protocol[] = {
182 NULL, /* SIPE_AUTHENTICATION_TYPE_UNSET */
183 NULL, /* SIPE_AUTHENTICATION_TYPE_BASIC */
184 "NTLM", /* SIPE_AUTHENTICATION_TYPE_NTLM */
185 "Kerberos", /* SIPE_AUTHENTICATION_TYPE_KERBEROS */
186 NULL, /* SIPE_AUTHENTICATION_TYPE_NEGOTIATE */
187 "TLS-DSK", /* SIPE_AUTHENTICATION_TYPE_TLS_DSK */
188 NULL, /* SIPE_AUTHENTICATION_TYPE_AUTOMATIC */
190 #define AUTH_PROTOCOLS (sizeof(auth_type_to_protocol)/sizeof(gchar *))
192 static gchar *msg_signature_to_auth(struct sip_auth *auth,
193 struct sipmsg *msg)
195 return(g_strdup_printf("%s qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", crand=\"%s\", cnum=\"%s\", response=\"%s\"",
196 auth->protocol,
197 auth->opaque, auth->realm, auth->target,
198 msg->rand, msg->num, msg->signature));
201 static gchar *initialize_auth_context(struct sipe_core_private *sipe_private,
202 struct sip_auth *auth,
203 struct sipmsg *msg)
205 struct sip_transport *transport = sipe_private->transport;
206 gchar *ret;
207 gchar *gssapi_data = NULL;
208 gchar *sign_str;
209 gchar *gssapi_str;
210 gchar *opaque_str;
211 gchar *version_str;
214 * If transport is de-registering when we reach this point then we
215 * are in the middle of the previous authentication context setup
216 * attempt. So we shouldn't try another attempt.
218 if (transport->deregister)
219 return NULL;
221 /* Create security context or handshake continuation? */
222 if (auth->gssapi_context) {
223 /* Perform next step in authentication handshake */
224 gboolean status = sip_sec_init_context_step(auth->gssapi_context,
225 auth->target,
226 auth->gssapi_data,
227 &gssapi_data,
228 &auth->expires);
230 /* If authentication is completed gssapi_data can be NULL */
231 if (!(status &&
232 (sip_sec_context_is_ready(auth->gssapi_context) || gssapi_data))) {
233 SIPE_DEBUG_ERROR_NOFORMAT("initialize_auth_context: security context continuation failed");
234 g_free(gssapi_data);
235 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
236 SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
237 _("Failed to authenticate to server"));
238 return NULL;
241 } else {
242 /* Create security context */
243 gpointer password = sipe_private->password;
245 /* For TLS-DSK the "password" is a certificate */
246 if (auth->type == SIPE_AUTHENTICATION_TYPE_TLS_DSK) {
247 password = sipe_certificate_tls_dsk_find(sipe_private,
248 auth->target);
250 if (!password) {
251 if (auth->sts_uri) {
252 SIPE_DEBUG_INFO("initialize_auth_context: TLS-DSK Certificate Provisioning URI %s",
253 auth->sts_uri);
254 if (!sipe_certificate_tls_dsk_generate(sipe_private,
255 auth->target,
256 auth->sts_uri)) {
257 gchar *tmp = g_strdup_printf(_("Can't request certificate from %s"),
258 auth->sts_uri);
259 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
260 SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
261 tmp);
262 g_free(tmp);
264 } else {
265 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
266 SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
267 _("No URI for certificate provisioning service provided"));
270 /* we can't authenticate the message yet */
271 transport->auth_incomplete = TRUE;
273 return(NULL);
274 } else {
275 SIPE_DEBUG_INFO("initialize_auth_context: TLS-DSK certificate for target '%s' found.",
276 auth->target);
280 auth->gssapi_context = sip_sec_create_context(auth->type,
281 SIPE_CORE_PRIVATE_FLAG_IS(SSO),
282 FALSE, /* connection-less for SIP */
283 sipe_private->authdomain ? sipe_private->authdomain : "",
284 sipe_private->authuser,
285 password);
287 if (auth->gssapi_context) {
288 sip_sec_init_context_step(auth->gssapi_context,
289 auth->target,
290 NULL,
291 &gssapi_data,
292 &(auth->expires));
295 if (!gssapi_data || !auth->gssapi_context) {
296 g_free(gssapi_data);
297 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
298 SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
299 _("Failed to authenticate to server"));
300 return NULL;
304 if ((auth->version > 3) &&
305 sip_sec_context_is_ready(auth->gssapi_context)) {
306 sipe_make_signature(sipe_private, msg);
307 sign_str = g_strdup_printf(", crand=\"%s\", cnum=\"%s\", response=\"%s\"",
308 msg->rand, msg->num, msg->signature);
309 } else {
310 sign_str = g_strdup("");
313 if (gssapi_data) {
314 gssapi_str = g_strdup_printf(", gssapi-data=\"%s\"",
315 gssapi_data);
316 g_free(gssapi_data);
317 } else {
318 gssapi_str = g_strdup("");
321 opaque_str = auth->opaque ? g_strdup_printf(", opaque=\"%s\"", auth->opaque) : g_strdup("");
323 if (auth->version > 2) {
324 version_str = g_strdup_printf(", version=%d", auth->version);
325 } else {
326 version_str = g_strdup("");
329 ret = g_strdup_printf("%s qop=\"auth\"%s, realm=\"%s\", targetname=\"%s\"%s%s%s",
330 auth->protocol, opaque_str,
331 auth->realm, auth->target,
332 gssapi_str, version_str, sign_str);
333 g_free(version_str);
334 g_free(opaque_str);
335 g_free(gssapi_str);
336 g_free(sign_str);
338 return(ret);
341 static gchar *auth_header(struct sipe_core_private *sipe_private,
342 struct sip_auth *auth,
343 struct sipmsg *msg)
345 gchar *ret = NULL;
348 * If the message is already signed then we have an authentication
349 * context, i.e. the authentication handshake is complete. Generate
350 * authentication header from message signature.
352 if (msg->signature) {
353 ret = msg_signature_to_auth(auth, msg);
356 * We should reach this point only when the authentication context
357 * needs to be initialized.
359 } else {
360 ret = initialize_auth_context(sipe_private, auth, msg);
363 return(ret);
366 static void fill_auth(const gchar *hdr, struct sip_auth *auth)
368 const gchar *param;
370 /* skip authentication identifier */
371 hdr = strchr(hdr, ' ');
372 if (!hdr) {
373 SIPE_DEBUG_ERROR_NOFORMAT("fill_auth: corrupted authentication header");
374 return;
376 while (*hdr == ' ')
377 hdr++;
379 /* start of next parameter value */
380 while ((param = strchr(hdr, '=')) != NULL) {
381 const gchar *end;
383 /* parameter value type */
384 param++;
385 if (*param == '"') {
386 /* string: xyz="..."(,) */
387 end = strchr(++param, '"');
388 if (!end) {
389 SIPE_DEBUG_ERROR("fill_auth: corrupted string parameter near '%s'", hdr);
390 break;
392 } else {
393 /* number: xyz=12345(,) */
394 end = strchr(param, ',');
395 if (!end) {
396 /* last parameter */
397 end = param + strlen(param);
401 #if 0
402 SIPE_DEBUG_INFO("fill_auth: hdr '%s'", hdr);
403 SIPE_DEBUG_INFO("fill_auth: param '%s'", param);
404 SIPE_DEBUG_INFO("fill_auth: end '%s'", end);
405 #endif
407 /* parameter type */
408 if (g_str_has_prefix(hdr, "gssapi-data=\"")) {
409 g_free(auth->gssapi_data);
410 auth->gssapi_data = g_strndup(param, end - param);
411 } else if (g_str_has_prefix(hdr, "opaque=\"")) {
412 g_free(auth->opaque);
413 auth->opaque = g_strndup(param, end - param);
414 } else if (g_str_has_prefix(hdr, "realm=\"")) {
415 g_free(auth->realm);
416 auth->realm = g_strndup(param, end - param);
417 } else if (g_str_has_prefix(hdr, "sts-uri=\"")) {
418 /* Only used with SIPE_AUTHENTICATION_TYPE_TLS_DSK */
419 g_free(auth->sts_uri);
420 auth->sts_uri = g_strndup(param, end - param);
421 } else if (g_str_has_prefix(hdr, "targetname=\"")) {
422 g_free(auth->target);
423 auth->target = g_strndup(param, end - param);
424 } else if (g_str_has_prefix(hdr, "version=")) {
425 auth->version = atoi(param);
428 /* skip to next parameter */
429 while ((*end == '"') || (*end == ',') || (*end == ' '))
430 end++;
431 hdr = end;
434 return;
437 static void sign_outgoing_message(struct sipe_core_private *sipe_private,
438 struct sipmsg *msg)
440 struct sip_transport *transport = sipe_private->transport;
441 gchar *buf;
443 if (transport->registrar.type == SIPE_AUTHENTICATION_TYPE_UNSET) {
444 return;
447 sipe_make_signature(sipe_private, msg);
449 buf = auth_header(sipe_private, &transport->registrar, msg);
450 if (buf) {
451 sipmsg_add_header_now(msg, "Authorization", buf);
452 g_free(buf);
456 static const gchar *sip_transport_user_agent(struct sipe_core_private *sipe_private)
458 struct sip_transport *transport = sipe_private->transport;
460 if (!transport->user_agent) {
461 const gchar *useragent = sipe_backend_setting(SIPE_CORE_PUBLIC,
462 SIPE_SETTING_USER_AGENT);
463 if (is_empty(useragent)) {
464 /*@TODO: better approach to define _user_ OS, it's version and host architecture */
465 /* ref: lzodefs.h */
466 #if defined(__linux__) || defined(__linux) || defined(__LINUX__)
467 #define SIPE_TARGET_PLATFORM "linux"
468 #elif defined(__NetBSD__) ||defined( __OpenBSD__) || defined(__FreeBSD__)
469 #define SIPE_TARGET_PLATFORM "bsd"
470 #elif defined(__APPLE__) || defined(__MACOS__)
471 #define SIPE_TARGET_PLATFORM "macosx"
472 #elif defined(_AIX) || defined(__AIX__) || defined(__aix__)
473 #define SIPE_TARGET_PLATFORM "aix"
474 #elif defined(__solaris__) || defined(__sun)
475 #define SIPE_TARGET_PLATFORM "sun"
476 #elif defined(_WIN32)
477 #define SIPE_TARGET_PLATFORM "win"
478 #elif defined(__CYGWIN__)
479 #define SIPE_TARGET_PLATFORM "cygwin"
480 #elif defined(__hpux__)
481 #define SIPE_TARGET_PLATFORM "hpux"
482 #elif defined(__sgi__)
483 #define SIPE_TARGET_PLATFORM "irix"
484 #else
485 #define SIPE_TARGET_PLATFORM "unknown"
486 #endif
488 #if defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)
489 #define SIPE_TARGET_ARCH "x86_64"
490 #elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386)
491 #define SIPE_TARGET_ARCH "i386"
492 #elif defined(__ppc64__)
493 #define SIPE_TARGET_ARCH "ppc64"
494 #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR)
495 #define SIPE_TARGET_ARCH "ppc"
496 #elif defined(__hppa__) || defined(__hppa)
497 #define SIPE_TARGET_ARCH "hppa"
498 #elif defined(__mips__) || defined(__mips) || defined(_MIPS_ARCH) || defined(_M_MRX000)
499 #define SIPE_TARGET_ARCH "mips"
500 #elif defined(__s390__) || defined(__s390) || defined(__s390x__) || defined(__s390x)
501 #define SIPE_TARGET_ARCH "s390"
502 #elif defined(__sparc__) || defined(__sparc) || defined(__sparcv8)
503 #define SIPE_TARGET_ARCH "sparc"
504 #elif defined(__arm__)
505 #define SIPE_TARGET_ARCH "arm"
506 #else
507 #define SIPE_TARGET_ARCH "other"
508 #endif
509 gchar *backend = sipe_backend_version();
510 transport->user_agent = g_strdup_printf("%s Sipe/" PACKAGE_VERSION " (" SIPE_TARGET_PLATFORM "-" SIPE_TARGET_ARCH "; %s)",
511 backend,
512 transport->server_version ? transport->server_version : "");
513 g_free(backend);
514 } else {
515 transport->user_agent = g_strdup(useragent);
518 return(transport->user_agent);
522 * NOTE: Do *NOT* call sipe_backend_transport_message(...) directly!
524 * All SIP messages must pass through this function in order to update
525 * the timestamp for keepalive tracking.
527 static void send_sip_message(struct sip_transport *transport,
528 const gchar *string)
530 sipe_utils_message_debug("SIP", string, NULL, TRUE);
531 transport->last_message = time(NULL);
532 sipe_backend_transport_message(transport->connection, string);
535 static void start_keepalive_timer(struct sipe_core_private *sipe_private,
536 guint seconds);
537 static void keepalive_timeout(struct sipe_core_private *sipe_private,
538 SIPE_UNUSED_PARAMETER gpointer data)
540 struct sip_transport *transport = sipe_private->transport;
541 if (transport) {
542 guint since_last = time(NULL) - transport->last_message;
543 guint restart = transport->keepalive_timeout;
544 if (since_last >= restart) {
545 SIPE_DEBUG_INFO("keepalive_timeout: expired %d", restart);
546 send_sip_message(transport, "\r\n\r\n");
547 } else {
548 /* timeout not reached since last message -> reschedule */
549 restart -= since_last;
551 start_keepalive_timer(sipe_private, restart);
555 static void start_keepalive_timer(struct sipe_core_private *sipe_private,
556 guint seconds)
558 sipe_schedule_seconds(sipe_private,
559 "<+keepalive-timeout>",
560 NULL,
561 seconds,
562 keepalive_timeout,
563 NULL);
566 void sip_transport_response(struct sipe_core_private *sipe_private,
567 struct sipmsg *msg,
568 guint code,
569 const char *text,
570 const char *body)
572 gchar *name;
573 gchar *value;
574 GString *outstr = g_string_new("");
575 gchar *contact;
576 GSList *tmp;
577 static const gchar *keepers[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL };
579 /* Can return NULL! */
580 contact = get_contact(sipe_private);
581 if (contact) {
582 sipmsg_add_header(msg, "Contact", contact);
583 g_free(contact);
586 if (body) {
587 gchar *len = g_strdup_printf("%" G_GSIZE_FORMAT , (gsize) strlen(body));
588 sipmsg_add_header(msg, "Content-Length", len);
589 g_free(len);
590 } else {
591 sipmsg_add_header(msg, "Content-Length", "0");
594 sipmsg_add_header(msg, "User-Agent", sip_transport_user_agent(sipe_private));
596 msg->response = code;
598 sipmsg_strip_headers(msg, keepers);
599 sipmsg_merge_new_headers(msg);
600 sign_outgoing_message(sipe_private, msg);
602 g_string_append_printf(outstr, "SIP/2.0 %d %s\r\n", code, text);
603 tmp = msg->headers;
604 while (tmp) {
605 name = ((struct sipnameval*) (tmp->data))->name;
606 value = ((struct sipnameval*) (tmp->data))->value;
608 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
609 tmp = g_slist_next(tmp);
611 g_string_append_printf(outstr, "\r\n%s", body ? body : "");
612 send_sip_message(sipe_private->transport, outstr->str);
613 g_string_free(outstr, TRUE);
616 static void transactions_remove(struct sipe_core_private *sipe_private,
617 struct transaction *trans)
619 struct sip_transport *transport = sipe_private->transport;
620 if (transport->transactions) {
621 transport->transactions = g_slist_remove(transport->transactions,
622 trans);
623 SIPE_DEBUG_INFO("SIP transactions count:%d after removal", g_slist_length(transport->transactions));
625 if (trans->msg) sipmsg_free(trans->msg);
626 if (trans->payload) {
627 if (trans->payload->destroy)
628 (*trans->payload->destroy)(trans->payload->data);
629 g_free(trans->payload);
631 g_free(trans->key);
632 if (trans->timeout_key) {
633 sipe_schedule_cancel(sipe_private, trans->timeout_key);
634 g_free(trans->timeout_key);
636 g_free(trans);
640 static struct transaction *transactions_find(struct sip_transport *transport,
641 struct sipmsg *msg)
643 GSList *transactions = transport->transactions;
644 const gchar *call_id = sipmsg_find_header(msg, "Call-ID");
645 const gchar *cseq = sipmsg_find_header(msg, "CSeq");
646 gchar *key;
648 if (!call_id || !cseq) {
649 SIPE_DEBUG_ERROR_NOFORMAT("transaction_find: no Call-ID or CSeq!");
650 return NULL;
653 key = g_strdup_printf("<%s><%s>", call_id, cseq);
654 while (transactions) {
655 struct transaction *trans = transactions->data;
656 if (!g_ascii_strcasecmp(trans->key, key)) {
657 g_free(key);
658 return trans;
660 transactions = transactions->next;
662 g_free(key);
664 return NULL;
667 static void transaction_timeout_cb(struct sipe_core_private *sipe_private,
668 gpointer data)
670 struct transaction *trans = data;
671 (trans->timeout_callback)(sipe_private, trans->msg, trans);
672 transactions_remove(sipe_private, trans);
675 struct transaction *sip_transport_request_timeout(struct sipe_core_private *sipe_private,
676 const gchar *method,
677 const gchar *url,
678 const gchar *to,
679 const gchar *addheaders,
680 const gchar *body,
681 struct sip_dialog *dialog,
682 TransCallback callback,
683 guint timeout,
684 TransCallback timeout_callback)
686 struct sip_transport *transport = sipe_private->transport;
687 char *buf;
688 struct sipmsg *msg;
689 gchar *ourtag = dialog && dialog->ourtag ? g_strdup(dialog->ourtag) : NULL;
690 gchar *theirtag = dialog && dialog->theirtag ? g_strdup(dialog->theirtag) : NULL;
691 gchar *theirepid = dialog && dialog->theirepid ? g_strdup(dialog->theirepid) : NULL;
692 gchar *callid = dialog && dialog->callid ? g_strdup(dialog->callid) : gencallid();
693 gchar *branch = dialog && dialog->callid ? NULL : genbranch();
694 gchar *route = g_strdup("");
695 gchar *epid = get_epid(sipe_private);
696 int cseq = dialog ? ++dialog->cseq : 1 /* as Call-Id is new in this case */;
697 struct transaction *trans = NULL;
699 if (dialog && dialog->routes)
701 GSList *iter = dialog->routes;
703 while(iter)
705 char *tmp = route;
706 route = g_strdup_printf("%sRoute: %s\r\n", route, (char *)iter->data);
707 g_free(tmp);
708 iter = g_slist_next(iter);
712 if (!ourtag && !dialog) {
713 ourtag = gentag();
716 if (sipe_strequal(method, "REGISTER")) {
717 if (sipe_private->register_callid) {
718 g_free(callid);
719 callid = g_strdup(sipe_private->register_callid);
720 } else {
721 sipe_private->register_callid = g_strdup(callid);
723 cseq = ++transport->cseq;
726 buf = g_strdup_printf("%s %s SIP/2.0\r\n"
727 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
728 "From: <sip:%s>%s%s;epid=%s\r\n"
729 "To: <%s>%s%s%s%s\r\n"
730 "Max-Forwards: 70\r\n"
731 "CSeq: %d %s\r\n"
732 "User-Agent: %s\r\n"
733 "Call-ID: %s\r\n"
734 "%s%s"
735 "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n%s",
736 method,
737 dialog && dialog->request ? dialog->request : url,
738 TRANSPORT_DESCRIPTOR,
739 sipe_backend_network_ip_address(SIPE_CORE_PUBLIC),
740 transport->connection->client_port,
741 branch ? ";branch=" : "",
742 branch ? branch : "",
743 sipe_private->username,
744 ourtag ? ";tag=" : "",
745 ourtag ? ourtag : "",
746 epid,
748 theirtag ? ";tag=" : "",
749 theirtag ? theirtag : "",
750 theirepid ? ";epid=" : "",
751 theirepid ? theirepid : "",
752 cseq,
753 method,
754 sip_transport_user_agent(sipe_private),
755 callid,
756 route,
757 addheaders ? addheaders : "",
758 body ? (gsize) strlen(body) : 0,
759 body ? body : "");
762 //printf ("parsing msg buf:\n%s\n\n", buf);
763 msg = sipmsg_parse_msg(buf);
765 g_free(buf);
766 g_free(ourtag);
767 g_free(theirtag);
768 g_free(theirepid);
769 g_free(branch);
770 g_free(route);
771 g_free(epid);
773 sign_outgoing_message(sipe_private, msg);
775 /* The authentication scheme is not ready so we can't send the message.
776 This should only happen for REGISTER messages. */
777 if (!transport->auth_incomplete) {
778 buf = sipmsg_to_string(msg);
780 /* add to ongoing transactions */
781 /* ACK isn't supposed to be answered ever. So we do not keep transaction for it. */
782 if (!sipe_strequal(method, "ACK")) {
783 trans = g_new0(struct transaction, 1);
784 trans->callback = callback;
785 trans->msg = msg;
786 trans->key = g_strdup_printf("<%s><%d %s>", callid, cseq, method);
787 if (timeout_callback) {
788 trans->timeout_callback = timeout_callback;
789 trans->timeout_key = g_strdup_printf("<transaction timeout>%s", trans->key);
790 sipe_schedule_seconds(sipe_private,
791 trans->timeout_key,
792 trans,
793 timeout,
794 transaction_timeout_cb,
795 NULL);
797 transport->transactions = g_slist_append(transport->transactions,
798 trans);
799 SIPE_DEBUG_INFO("SIP transactions count:%d after addition", g_slist_length(transport->transactions));
802 send_sip_message(transport, buf);
803 g_free(buf);
806 if (!trans) sipmsg_free(msg);
807 g_free(callid);
808 return trans;
811 struct transaction *sip_transport_request(struct sipe_core_private *sipe_private,
812 const gchar *method,
813 const gchar *url,
814 const gchar *to,
815 const gchar *addheaders,
816 const gchar *body,
817 struct sip_dialog *dialog,
818 TransCallback callback)
820 return sip_transport_request_timeout(sipe_private,
821 method,
822 url,
824 addheaders,
825 body,
826 dialog,
827 callback,
829 NULL);
832 static void sip_transport_simple_request(struct sipe_core_private *sipe_private,
833 const gchar *method,
834 struct sip_dialog *dialog)
836 sip_transport_request(sipe_private,
837 method,
838 dialog->with,
839 dialog->with,
840 NULL,
841 NULL,
842 dialog,
843 NULL);
846 void sip_transport_ack(struct sipe_core_private *sipe_private,
847 struct sip_dialog *dialog)
849 sip_transport_simple_request(sipe_private, "ACK", dialog);
852 void sip_transport_bye(struct sipe_core_private *sipe_private,
853 struct sip_dialog *dialog)
855 sip_transport_simple_request(sipe_private, "BYE", dialog);
858 struct transaction *sip_transport_info(struct sipe_core_private *sipe_private,
859 const gchar *addheaders,
860 const gchar *body,
861 struct sip_dialog *dialog,
862 TransCallback callback)
864 return sip_transport_request(sipe_private,
865 "INFO",
866 dialog->with,
867 dialog->with,
868 addheaders,
869 body,
870 dialog,
871 callback);
874 struct transaction *sip_transport_invite(struct sipe_core_private *sipe_private,
875 const gchar *addheaders,
876 const gchar *body,
877 struct sip_dialog *dialog,
878 TransCallback callback)
880 return sip_transport_request(sipe_private,
881 "INVITE",
882 dialog->with,
883 dialog->with,
884 addheaders,
885 body,
886 dialog,
887 callback);
890 struct transaction *sip_transport_service(struct sipe_core_private *sipe_private,
891 const gchar *uri,
892 const gchar *addheaders,
893 const gchar *body,
894 TransCallback callback)
896 return sip_transport_request(sipe_private,
897 "SERVICE",
898 uri,
899 uri,
900 addheaders,
901 body,
902 NULL,
903 callback);
906 void sip_transport_subscribe(struct sipe_core_private *sipe_private,
907 const gchar *uri,
908 const gchar *addheaders,
909 const gchar *body,
910 struct sip_dialog *dialog,
911 TransCallback callback)
913 sip_transport_request(sipe_private,
914 "SUBSCRIBE",
915 uri,
916 uri,
917 addheaders,
918 body,
919 dialog,
920 callback);
923 void sip_transport_update(struct sipe_core_private *sipe_private,
924 struct sip_dialog *dialog,
925 TransCallback callback)
927 sip_transport_request(sipe_private,
928 "UPDATE",
929 dialog->with,
930 dialog->with,
931 NULL,
932 NULL,
933 dialog,
934 callback);
937 static const gchar *get_auth_header(struct sipe_core_private *sipe_private,
938 guint type,
939 struct sipmsg *msg)
941 struct sip_auth *auth = &sipe_private->transport->registrar;
943 auth->type = type;
944 auth->protocol = auth_type_to_protocol[auth->type];
946 return(sipmsg_find_auth_header(msg, auth->protocol));
949 static void do_register(struct sipe_core_private *sipe_private,
950 gboolean deregister);
952 static void do_reauthenticate_cb(struct sipe_core_private *sipe_private,
953 SIPE_UNUSED_PARAMETER gpointer unused)
955 struct sip_transport *transport = sipe_private->transport;
957 /* register again when security token expires */
958 /* we have to start a new authentication as the security token
959 * is almost expired by sending a not signed REGISTER message */
960 SIPE_DEBUG_INFO_NOFORMAT("do a full reauthentication");
961 sipe_auth_free(&transport->registrar);
962 sipe_auth_free(&transport->proxy);
963 sipe_schedule_cancel(sipe_private, "<registration>");
964 transport->auth_retry = TRUE;
965 transport->reregister_set = FALSE;
966 transport->register_attempt = 0;
967 do_register(sipe_private, FALSE);
968 transport->reauthenticate_set = FALSE;
971 static void sip_transport_default_contact(struct sipe_core_private *sipe_private)
973 struct sip_transport *transport = sipe_private->transport;
974 sipe_private->contact = g_strdup_printf("<sip:%s:%d;maddr=%s;transport=%s>;proxy=replace",
975 sipe_private->username,
976 transport->connection->client_port,
977 sipe_backend_network_ip_address(SIPE_CORE_PUBLIC),
978 TRANSPORT_DESCRIPTOR);
981 static void do_register_cb(struct sipe_core_private *sipe_private,
982 SIPE_UNUSED_PARAMETER void *unused)
984 do_register(sipe_private, FALSE);
987 static void sip_transport_set_reregister(struct sipe_core_private *sipe_private,
988 int expires)
990 sipe_schedule_seconds(sipe_private,
991 "<registration>",
992 NULL,
993 expires,
994 do_register_cb,
995 NULL);
998 static void sipe_server_register(struct sipe_core_private *sipe_private,
999 guint type,
1000 gchar *server_name,
1001 guint server_port);
1003 static gboolean process_register_response(struct sipe_core_private *sipe_private,
1004 struct sipmsg *msg,
1005 SIPE_UNUSED_PARAMETER struct transaction *trans)
1007 struct sip_transport *transport = sipe_private->transport;
1008 const gchar *expires_header;
1009 int expires, i;
1010 GSList *hdr = msg->headers;
1011 struct sipnameval *elem;
1013 expires_header = sipmsg_find_header(msg, "Expires");
1014 expires = expires_header != NULL ? strtol(expires_header, NULL, 10) : 0;
1015 SIPE_DEBUG_INFO("process_register_response: got response to REGISTER; expires = %d", expires);
1017 switch (msg->response) {
1018 case 200:
1019 if (expires) {
1020 const gchar *contact_hdr;
1021 const gchar *auth_hdr;
1022 gchar *gruu = NULL;
1023 gchar *uuid;
1024 gchar *timeout;
1025 const gchar *server_hdr = sipmsg_find_header(msg, "Server");
1027 if (!transport->reregister_set) {
1028 sip_transport_set_reregister(sipe_private,
1029 expires);
1030 transport->reregister_set = TRUE;
1033 if (server_hdr && !transport->server_version) {
1034 transport->server_version = g_strdup(server_hdr);
1035 g_free(transport->user_agent);
1036 transport->user_agent = NULL;
1039 auth_hdr = sipmsg_find_auth_header(msg,
1040 transport->registrar.protocol);
1041 if (auth_hdr) {
1042 SIPE_DEBUG_INFO("process_register_response: Auth header: %s", auth_hdr);
1043 fill_auth(auth_hdr, &transport->registrar);
1046 if (!transport->reauthenticate_set) {
1047 guint reauth_timeout = transport->registrar.expires;
1049 SIPE_DEBUG_INFO_NOFORMAT("process_register_response: authentication handshake completed successfully");
1051 /* Does authentication scheme provide valid expiration time? */
1052 if (reauth_timeout == 0) {
1053 SIPE_DEBUG_INFO_NOFORMAT("process_register_response: no expiration time - using default of 8 hours");
1054 reauth_timeout = 8 * 60 * 60;
1057 /* schedule reauthentication 5 minutes before expiration */
1058 if (reauth_timeout > 5 * 60)
1059 reauth_timeout -= 5 * 60;
1060 sipe_schedule_seconds(sipe_private,
1061 "<+reauthentication>",
1062 NULL,
1063 reauth_timeout,
1064 do_reauthenticate_cb,
1065 NULL);
1066 transport->reauthenticate_set = TRUE;
1069 sipe_backend_connection_completed(SIPE_CORE_PUBLIC);
1071 uuid = get_uuid(sipe_private);
1073 // There can be multiple Contact headers (one per location where the user is logged in) so
1074 // make sure to only get the one for this uuid
1075 for (i = 0; (contact_hdr = sipmsg_find_header_instance (msg, "Contact", i)); i++) {
1076 gchar * valid_contact = sipmsg_find_part_of_header (contact_hdr, uuid, NULL, NULL);
1077 if (valid_contact) {
1078 gruu = sipmsg_find_part_of_header(contact_hdr, "gruu=\"", "\"", NULL);
1079 //SIPE_DEBUG_INFO("process_register_response: got gruu %s from contact hdr w/ right uuid: %s", gruu, contact_hdr);
1080 g_free(valid_contact);
1081 break;
1082 } else {
1083 //SIPE_DEBUG_INFO("process_register_response: ignoring contact hdr b/c not right uuid: %s", contact_hdr);
1086 g_free(uuid);
1088 g_free(sipe_private->contact);
1089 if(gruu) {
1090 sipe_private->contact = g_strdup_printf("<%s>", gruu);
1091 g_free(gruu);
1092 } else {
1093 //SIPE_DEBUG_INFO_NOFORMAT("process_register_response: didn't find gruu in a Contact hdr");
1094 sip_transport_default_contact(sipe_private);
1096 SIPE_CORE_PRIVATE_FLAG_UNSET(OCS2007);
1097 SIPE_CORE_PRIVATE_FLAG_UNSET(REMOTE_USER);
1098 SIPE_CORE_PRIVATE_FLAG_UNSET(BATCHED_SUPPORT);
1100 while(hdr)
1102 elem = hdr->data;
1103 if (sipe_strcase_equal(elem->name, "Supported")) {
1104 if (sipe_strcase_equal(elem->value, "msrtc-event-categories")) {
1105 /* We interpret this as OCS2007+ indicator */
1106 SIPE_CORE_PRIVATE_FLAG_SET(OCS2007);
1107 SIPE_DEBUG_INFO("Supported: %s (indicates OCS2007+)", elem->value);
1109 if (sipe_strcase_equal(elem->value, "adhoclist")) {
1110 SIPE_CORE_PRIVATE_FLAG_SET(BATCHED_SUPPORT);
1111 SIPE_DEBUG_INFO("Supported: %s", elem->value);
1114 if (sipe_strcase_equal(elem->name, "Allow-Events")){
1115 gchar **caps = g_strsplit(elem->value,",",0);
1116 i = 0;
1117 while (caps[i]) {
1118 sipe_private->allowed_events = g_slist_append(sipe_private->allowed_events, g_strdup(caps[i]));
1119 SIPE_DEBUG_INFO("Allow-Events: %s", caps[i]);
1120 i++;
1122 g_strfreev(caps);
1124 if (sipe_strcase_equal(elem->name, "ms-user-logon-data")) {
1125 if (sipe_strcase_equal(elem->value, "RemoteUser")) {
1126 SIPE_CORE_PRIVATE_FLAG_SET(REMOTE_USER);
1127 SIPE_DEBUG_INFO_NOFORMAT("ms-user-logon-data: RemoteUser (connected "
1128 "via Edge Server)");
1131 hdr = g_slist_next(hdr);
1134 /* rejoin open chats to be able to use them by continue to send messages */
1135 sipe_backend_chat_rejoin_all(SIPE_CORE_PUBLIC);
1137 /* subscriptions, done only once */
1138 if (!transport->subscribed) {
1139 sipe_subscription_self_events(sipe_private);
1140 transport->subscribed = TRUE;
1143 timeout = sipmsg_find_part_of_header(sipmsg_find_header(msg, "ms-keep-alive"),
1144 "timeout=", ";", NULL);
1145 if (timeout != NULL) {
1146 sscanf(timeout, "%u", &transport->keepalive_timeout);
1147 SIPE_DEBUG_INFO("process_register_response: server determined keep alive timeout is %u seconds",
1148 transport->keepalive_timeout);
1149 g_free(timeout);
1152 SIPE_DEBUG_INFO("process_register_response: got 200, removing CSeq: %d", transport->cseq);
1154 break;
1155 case 301:
1157 gchar *redirect = parse_from(sipmsg_find_header(msg, "Contact"));
1159 SIPE_DEBUG_INFO_NOFORMAT("process_register_response: authentication handshake completed successfully (with redirect)");
1161 if (redirect && (g_ascii_strncasecmp("sip:", redirect, 4) == 0)) {
1162 gchar **parts = g_strsplit(redirect + 4, ";", 0);
1163 gchar **tmp;
1164 gchar *hostname;
1165 int port = 0;
1166 guint transport_type = SIPE_TRANSPORT_TLS;
1167 int i = 1;
1169 tmp = g_strsplit(parts[0], ":", 0);
1170 hostname = g_strdup(tmp[0]);
1171 if (tmp[1]) port = strtoul(tmp[1], NULL, 10);
1172 g_strfreev(tmp);
1174 while (parts[i]) {
1175 tmp = g_strsplit(parts[i], "=", 0);
1176 if (tmp[1]) {
1177 if (g_ascii_strcasecmp("transport", tmp[0]) == 0) {
1178 if (g_ascii_strcasecmp("tcp", tmp[1]) == 0) {
1179 transport_type = SIPE_TRANSPORT_TCP;
1183 g_strfreev(tmp);
1184 i++;
1186 g_strfreev(parts);
1188 /* Close old connection */
1189 sipe_core_connection_cleanup(sipe_private);
1190 /* transport and sipe_private->transport are invalid after this */
1192 /* Create new connection */
1193 sipe_server_register(sipe_private, transport_type, hostname, port);
1194 /* sipe_private->transport has a new value */
1195 SIPE_DEBUG_INFO("process_register_response: redirected to host %s port %d transport %d",
1196 hostname, port, transport_type);
1198 g_free(redirect);
1200 break;
1201 case 401:
1203 const char *auth_hdr = NULL;
1205 SIPE_DEBUG_INFO("process_register_response: REGISTER retries %d", transport->registrar.retries);
1207 if (transport->reauthenticate_set) {
1208 SIPE_DEBUG_ERROR_NOFORMAT("process_register_response: RE-REGISTER rejected, triggering re-authentication");
1209 do_reauthenticate_cb(sipe_private, NULL);
1210 return TRUE;
1213 if (sip_sec_context_is_ready(transport->registrar.gssapi_context)) {
1214 SIPE_DEBUG_INFO_NOFORMAT("process_register_response: authentication handshake failed - giving up.");
1215 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1216 SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
1217 _("Authentication failed"));
1218 return TRUE;
1221 if (sipe_private->authentication_type == SIPE_AUTHENTICATION_TYPE_AUTOMATIC) {
1222 guint try = transport->registrar.type;
1224 while (!auth_hdr) {
1227 * Determine next authentication
1228 * scheme in priority order
1230 if (transport->auth_retry)
1231 switch (try) {
1232 case SIPE_AUTHENTICATION_TYPE_UNSET:
1233 try = SIPE_AUTHENTICATION_TYPE_TLS_DSK;
1234 break;
1236 case SIPE_AUTHENTICATION_TYPE_TLS_DSK:
1237 try = SIPE_AUTHENTICATION_TYPE_KERBEROS;
1238 break;
1240 case SIPE_AUTHENTICATION_TYPE_KERBEROS:
1241 try = SIPE_AUTHENTICATION_TYPE_NTLM;
1242 break;
1244 default:
1245 try = SIPE_AUTHENTICATION_TYPE_UNSET;
1246 break;
1249 if (try == SIPE_AUTHENTICATION_TYPE_UNSET) {
1250 SIPE_DEBUG_INFO_NOFORMAT("process_register_response: no more authentication schemes to try");
1251 break;
1254 auth_hdr = get_auth_header(sipe_private,
1255 try,
1256 msg);
1259 transport->auth_retry = FALSE;
1261 } else
1262 auth_hdr = get_auth_header(sipe_private,
1263 sipe_private->authentication_type,
1264 msg);
1266 if (!auth_hdr) {
1267 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1268 SIPE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
1269 _("Incompatible authentication scheme chosen"));
1270 return TRUE;
1272 SIPE_DEBUG_INFO("process_register_response: Auth header: %s", auth_hdr);
1273 fill_auth(auth_hdr, &transport->registrar);
1274 transport->reregister_set = FALSE;
1275 transport->register_attempt = 0;
1276 do_register(sipe_private,
1277 sipe_backend_connection_is_disconnecting(SIPE_CORE_PUBLIC));
1279 break;
1280 case 403:
1282 gchar *reason;
1283 gchar *warning;
1284 sipmsg_parse_warning(msg, &reason);
1285 reason = reason ? reason : sipmsg_get_ms_diagnostics_public_reason(msg);
1286 warning = g_strdup_printf(_("You have been rejected by the server: %s"),
1287 reason ? reason : _("no reason given"));
1288 g_free(reason);
1290 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1291 SIPE_CONNECTION_ERROR_INVALID_SETTINGS,
1292 warning);
1293 g_free(warning);
1294 return TRUE;
1296 break;
1297 case 404:
1299 const gchar *diagnostics = sipmsg_find_header(msg, "ms-diagnostics");
1300 gchar *reason = sipmsg_get_ms_diagnostics_reason(msg);
1301 gchar *warning;
1302 warning = g_strdup_printf(_("Not found: %s. Please contact your Administrator"),
1303 diagnostics ? (reason ? reason : _("no reason given")) :
1304 _("SIP is either not enabled for the destination URI or it does not exist"));
1305 g_free(reason);
1307 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1308 SIPE_CONNECTION_ERROR_INVALID_USERNAME,
1309 warning);
1310 g_free(warning);
1311 return TRUE;
1313 break;
1314 case 504: /* Server time-out */
1315 /* first attempt + 5 retries */
1316 if (transport->register_attempt < 6) {
1317 SIPE_DEBUG_INFO("process_register_response: RE-REGISTER timeout on attempt %d, retrying later",
1318 transport->register_attempt);
1319 sip_transport_set_reregister(sipe_private, 60);
1320 return TRUE;
1322 /* FALLTHROUGH */
1323 case 503:
1325 gchar *reason = sipmsg_get_ms_diagnostics_reason(msg);
1326 gchar *warning;
1327 warning = g_strdup_printf(_("Service unavailable: %s"), reason ? reason : _("no reason given"));
1328 g_free(reason);
1330 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1331 SIPE_CONNECTION_ERROR_NETWORK,
1332 warning);
1333 g_free(warning);
1334 return TRUE;
1336 break;
1338 return TRUE;
1341 static gboolean register_response_timeout(struct sipe_core_private *sipe_private,
1342 SIPE_UNUSED_PARAMETER struct sipmsg *msg,
1343 SIPE_UNUSED_PARAMETER struct transaction *trans)
1345 struct sip_transport *transport = sipe_private->transport;
1346 if (transport->register_attempt < 6) {
1347 SIPE_DEBUG_INFO("register_response_timeout: no answer to attempt %d, retrying",
1348 transport->register_attempt);
1349 do_register(sipe_private, FALSE);
1350 } else {
1351 gchar *warning = g_strdup_printf(_("Service unavailable: %s"), _("no reason given"));
1352 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1353 SIPE_CONNECTION_ERROR_NETWORK,
1354 warning);
1355 g_free(warning);
1357 return TRUE;
1360 static void do_register(struct sipe_core_private *sipe_private,
1361 gboolean deregister)
1363 struct sip_transport *transport = sipe_private->transport;
1364 char *uri;
1365 char *to;
1366 char *hdr;
1367 char *uuid;
1369 if (!sipe_private->public.sip_domain) return;
1371 if (!deregister) {
1372 if (transport->reregister_set) {
1373 transport->reregister_set = FALSE;
1374 transport->register_attempt = 1;
1375 } else {
1376 transport->register_attempt++;
1380 transport->deregister = deregister;
1381 transport->auth_incomplete = FALSE;
1383 uuid = get_uuid(sipe_private);
1384 hdr = g_strdup_printf("Contact: <sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY\";proxy=replace;+sip.instance=\"<urn:uuid:%s>\"\r\n"
1385 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1386 "Event: registration\r\n"
1387 "Allow-Events: presence\r\n"
1388 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1389 "%s",
1390 sipe_backend_network_ip_address(SIPE_CORE_PUBLIC),
1391 transport->connection->client_port,
1392 TRANSPORT_DESCRIPTOR,
1393 uuid,
1394 deregister ? "Expires: 0\r\n" : "");
1395 g_free(uuid);
1397 uri = sip_uri_from_name(sipe_private->public.sip_domain);
1398 to = sip_uri_self(sipe_private);
1399 sip_transport_request_timeout(sipe_private,
1400 "REGISTER",
1401 uri,
1403 hdr,
1405 NULL,
1406 process_register_response,
1408 deregister ? NULL : register_response_timeout);
1409 g_free(to);
1410 g_free(uri);
1411 g_free(hdr);
1413 if (deregister) {
1414 /* Make sure that all messages are pushed to the server
1415 before the connection gets shut down */
1416 SIPE_DEBUG_INFO_NOFORMAT("De-register from server. Flushing outstanding messages.");
1417 sipe_backend_transport_flush(transport->connection);
1421 void sip_transport_deregister(struct sipe_core_private *sipe_private)
1423 do_register(sipe_private, TRUE);
1426 void sip_transport_disconnect(struct sipe_core_private *sipe_private)
1428 struct sip_transport *transport = sipe_private->transport;
1430 /* transport can be NULL during connection setup */
1431 if (transport) {
1432 sipe_backend_transport_disconnect(transport->connection);
1434 sipe_auth_free(&transport->registrar);
1435 sipe_auth_free(&transport->proxy);
1437 g_free(transport->server_name);
1438 g_free(transport->server_version);
1439 g_free(transport->user_agent);
1441 while (transport->transactions)
1442 transactions_remove(sipe_private,
1443 transport->transactions->data);
1445 g_free(transport);
1448 sipe_private->transport = NULL;
1449 sipe_private->service_data = NULL;
1450 sipe_private->address_data = NULL;
1452 sipe_schedule_cancel(sipe_private, "<+keepalive-timeout>");
1454 if (sipe_private->dns_query)
1455 sipe_backend_dns_query_cancel(sipe_private->dns_query);
1459 void sip_transport_authentication_completed(struct sipe_core_private *sipe_private)
1461 do_reauthenticate_cb(sipe_private, NULL);
1464 guint sip_transport_port(struct sipe_core_private *sipe_private)
1466 return sipe_private->transport->server_port;
1469 static void process_input_message(struct sipe_core_private *sipe_private,
1470 struct sipmsg *msg)
1472 struct sip_transport *transport = sipe_private->transport;
1473 gboolean notfound = FALSE;
1474 const char *method = msg->method ? msg->method : "NOT FOUND";
1476 SIPE_DEBUG_INFO("process_input_message: msg->response(%d),msg->method(%s)",
1477 msg->response, method);
1479 if (msg->response == 0) { /* request */
1480 if (sipe_strequal(method, "MESSAGE")) {
1481 process_incoming_message(sipe_private, msg);
1482 } else if (sipe_strequal(method, "NOTIFY")) {
1483 SIPE_DEBUG_INFO_NOFORMAT("send->process_incoming_notify");
1484 process_incoming_notify(sipe_private, msg);
1485 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
1486 } else if (sipe_strequal(method, "BENOTIFY")) {
1487 SIPE_DEBUG_INFO_NOFORMAT("send->process_incoming_benotify");
1488 process_incoming_notify(sipe_private, msg);
1489 } else if (sipe_strequal(method, "INVITE")) {
1490 process_incoming_invite(sipe_private, msg);
1491 } else if (sipe_strequal(method, "REFER")) {
1492 process_incoming_refer(sipe_private, msg);
1493 } else if (sipe_strequal(method, "OPTIONS")) {
1494 process_incoming_options(sipe_private, msg);
1495 } else if (sipe_strequal(method, "INFO")) {
1496 process_incoming_info(sipe_private, msg);
1497 } else if (sipe_strequal(method, "ACK")) {
1498 /* ACK's don't need any response */
1499 } else if (sipe_strequal(method, "PRACK")) {
1500 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
1501 } else if (sipe_strequal(method, "SUBSCRIBE")) {
1502 /* LCS 2005 sends us these - just respond 200 OK */
1503 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
1504 } else if (sipe_strequal(method, "CANCEL")) {
1505 process_incoming_cancel(sipe_private, msg);
1506 } else if (sipe_strequal(method, "BYE")) {
1507 process_incoming_bye(sipe_private, msg);
1508 } else {
1509 sip_transport_response(sipe_private, msg, 501, "Not implemented", NULL);
1510 notfound = TRUE;
1513 } else { /* response */
1514 struct transaction *trans = transactions_find(transport, msg);
1515 if (trans) {
1516 if (msg->response < 200) {
1517 /* ignore provisional response */
1518 SIPE_DEBUG_INFO("process_input_message: got provisional (%d) response, ignoring", msg->response);
1520 /* Transaction not yet completed */
1521 trans = NULL;
1523 } else if (msg->response == 401) { /* Unauthorized */
1525 if (sipe_strequal(trans->msg->method, "REGISTER")) {
1526 /* Expected response during authentication handshake */
1527 transport->registrar.retries++;
1528 SIPE_DEBUG_INFO("process_input_message: RE-REGISTER CSeq: %d", transport->cseq);
1529 } else {
1530 gchar *resend;
1532 /* Are we registered? */
1533 if (transport->reregister_set) {
1534 SIPE_DEBUG_INFO_NOFORMAT("process_input_message: 401 response to non-REGISTER message. Retrying with new authentication.");
1535 sipmsg_remove_header_now(trans->msg, "Authorization");
1536 sign_outgoing_message(sipe_private,
1537 trans->msg);
1538 } else {
1540 * We don't have a valid authentication at the moment.
1541 * Resend message unchanged. It will be rejected again
1542 * and hopefully by then we have a valid authentication.
1544 SIPE_DEBUG_INFO_NOFORMAT("process_input_message: 401 response to non-REGISTER message. Bouncing...");
1547 /* Resend request */
1548 resend = sipmsg_to_string(trans->msg);
1549 send_sip_message(sipe_private->transport, resend);
1550 g_free(resend);
1552 /* Transaction not yet completed */
1553 trans = NULL;
1556 } else if (msg->response == 407) { /* Proxy Authentication Required */
1558 if (transport->proxy.retries++ <= 30) {
1559 const gchar *proxy_hdr = sipmsg_find_header(msg, "Proxy-Authenticate");
1561 if (proxy_hdr) {
1562 gchar *auth = NULL;
1564 if (!g_ascii_strncasecmp(proxy_hdr, "Digest", 6)) {
1565 auth = sip_sec_digest_authorization(sipe_private,
1566 proxy_hdr + 7,
1567 msg->method,
1568 msg->target);
1569 } else {
1570 guint i;
1572 transport->proxy.type = SIPE_AUTHENTICATION_TYPE_UNSET;
1573 for (i = 0; i < AUTH_PROTOCOLS; i++) {
1574 const gchar *protocol = auth_type_to_protocol[i];
1575 if (protocol &&
1576 !g_ascii_strncasecmp(proxy_hdr, protocol, strlen(protocol))) {
1577 SIPE_DEBUG_INFO("process_input_message: proxy authentication scheme '%s'", protocol);
1578 transport->proxy.type = i;
1579 transport->proxy.protocol = protocol;
1580 fill_auth(proxy_hdr, &transport->proxy);
1581 auth = auth_header(sipe_private, &transport->proxy, trans->msg);
1582 break;
1587 if (auth) {
1588 gchar *resend;
1590 /* replace old proxy authentication with new one */
1591 sipmsg_remove_header_now(trans->msg, "Proxy-Authorization");
1592 sipmsg_add_header_now(trans->msg, "Proxy-Authorization", auth);
1593 g_free(auth);
1595 /* resend request with proxy authentication */
1596 resend = sipmsg_to_string(trans->msg);
1597 send_sip_message(sipe_private->transport, resend);
1598 g_free(resend);
1600 /* Transaction not yet completed */
1601 trans = NULL;
1603 } else
1604 SIPE_DEBUG_ERROR_NOFORMAT("process_input_message: can't generate proxy authentication. Giving up.");
1605 } else
1606 SIPE_DEBUG_ERROR_NOFORMAT("process_input_message: 407 response without 'Proxy-Authenticate' header. Giving up.");
1607 } else
1608 SIPE_DEBUG_ERROR_NOFORMAT("process_input_message: too many proxy authentication retries. Giving up.");
1610 } else {
1611 transport->registrar.retries = 0;
1612 transport->proxy.retries = 0;
1615 /* Is transaction completed? */
1616 if (trans) {
1617 if (trans->callback) {
1618 SIPE_DEBUG_INFO_NOFORMAT("process_input_message: we have a transaction callback");
1619 /* call the callback to process response */
1620 (trans->callback)(sipe_private, msg, trans);
1621 /* transport && trans no longer valid after redirect */
1625 * Redirect case: sipe_private->transport is
1626 * the new transport with empty queue
1628 if (sipe_private->transport->transactions) {
1629 SIPE_DEBUG_INFO("process_input_message: removing CSeq %d", transport->cseq);
1630 transactions_remove(sipe_private, trans);
1633 } else {
1634 SIPE_DEBUG_INFO_NOFORMAT("process_input_message: received response to unknown transaction");
1635 notfound = TRUE;
1639 if (notfound) {
1640 SIPE_DEBUG_INFO("received a unknown sip message with method %s and response %d", method, msg->response);
1644 static void sip_transport_input(struct sipe_transport_connection *conn)
1646 struct sipe_core_private *sipe_private = conn->user_data;
1647 struct sip_transport *transport = sipe_private->transport;
1648 gchar *cur = conn->buffer;
1650 /* according to the RFC remove CRLF at the beginning */
1651 while (*cur == '\r' || *cur == '\n') {
1652 cur++;
1654 if (cur != conn->buffer)
1655 sipe_utils_shrink_buffer(conn, cur);
1657 /* Received a full Header? */
1658 transport->processing_input = TRUE;
1659 while (transport->processing_input &&
1660 ((cur = strstr(conn->buffer, "\r\n\r\n")) != NULL)) {
1661 struct sipmsg *msg;
1662 guint remainder;
1664 cur += 2;
1665 cur[0] = '\0';
1666 msg = sipmsg_parse_header(conn->buffer);
1668 cur += 2;
1669 remainder = conn->buffer_used - (cur - conn->buffer);
1670 if (msg && remainder >= (guint) msg->bodylen) {
1671 char *dummy = g_malloc(msg->bodylen + 1);
1672 memcpy(dummy, cur, msg->bodylen);
1673 dummy[msg->bodylen] = '\0';
1674 msg->body = dummy;
1675 cur += msg->bodylen;
1676 sipe_utils_message_debug("SIP",
1677 conn->buffer,
1678 msg->body,
1679 FALSE);
1680 sipe_utils_shrink_buffer(conn, cur);
1681 } else {
1682 if (msg) {
1683 SIPE_DEBUG_INFO("sipe_transport_input: body too short (%d < %d, strlen %d) - ignoring message", remainder, msg->bodylen, (int)strlen(conn->buffer));
1684 sipmsg_free(msg);
1687 /* restore header for next try */
1688 cur[-2] = '\r';
1689 return;
1692 /* Fatal header parse error? */
1693 if (msg->response == SIPMSG_RESPONSE_FATAL_ERROR) {
1694 /* can't proceed -> drop connection */
1695 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1696 SIPE_CONNECTION_ERROR_NETWORK,
1697 _("Corrupted message received"));
1698 transport->processing_input = FALSE;
1700 /* Verify the signature before processing it */
1701 } else if (sip_sec_context_is_ready(transport->registrar.gssapi_context)) {
1702 struct sipmsg_breakdown msgbd;
1703 gchar *signature_input_str;
1704 gchar *rspauth;
1705 msgbd.msg = msg;
1706 sipmsg_breakdown_parse(&msgbd, transport->registrar.realm, transport->registrar.target,
1707 transport->registrar.protocol);
1708 signature_input_str = sipmsg_breakdown_get_string(transport->registrar.version, &msgbd);
1710 rspauth = sipmsg_find_part_of_header(sipmsg_find_header(msg, "Authentication-Info"), "rspauth=\"", "\"", NULL);
1712 if (rspauth != NULL) {
1713 if (sip_sec_verify_signature(transport->registrar.gssapi_context, signature_input_str, rspauth)) {
1714 SIPE_DEBUG_INFO_NOFORMAT("sip_transport_input: signature of incoming message validated");
1715 process_input_message(sipe_private, msg);
1716 /* transport is invalid after redirect */
1717 } else {
1718 SIPE_DEBUG_INFO_NOFORMAT("sip_transport_input: signature of incoming message is invalid.");
1719 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1720 SIPE_CONNECTION_ERROR_NETWORK,
1721 _("Invalid message signature received"));
1722 transport->processing_input = FALSE;
1724 } else if ((msg->response == 401) ||
1725 sipe_strequal(msg->method, "REGISTER")) {
1726 /* a) Retry non-REGISTER requests with updated authentication */
1727 /* b) We must always process REGISTER responses */
1728 process_input_message(sipe_private, msg);
1729 } else {
1730 /* OCS sends provisional messages that are *not* signed */
1731 if (msg->response >= 200) {
1732 /* We are not calling process_input_message(),
1733 so we need to drop the transaction here. */
1734 struct transaction *trans = transactions_find(transport, msg);
1735 if (trans) transactions_remove(sipe_private, trans);
1737 SIPE_DEBUG_INFO_NOFORMAT("sip_transport_input: message without authentication data - ignoring");
1739 g_free(signature_input_str);
1741 g_free(rspauth);
1742 sipmsg_breakdown_free(&msgbd);
1743 } else {
1744 process_input_message(sipe_private, msg);
1747 sipmsg_free(msg);
1749 /* Redirect: old content of "transport" & "conn" is no longer valid */
1750 transport = sipe_private->transport;
1751 conn = transport->connection;
1755 static void sip_transport_connected(struct sipe_transport_connection *conn)
1757 struct sipe_core_private *sipe_private = conn->user_data;
1758 struct sip_transport *transport = sipe_private->transport;
1760 sipe_private->service_data = NULL;
1761 sipe_private->address_data = NULL;
1764 * Initial keepalive timeout during REGISTER phase
1766 * NOTE: 60 seconds is a guess. Needs more testing!
1768 transport->keepalive_timeout = 60;
1769 start_keepalive_timer(sipe_private, transport->keepalive_timeout);
1771 do_register(sipe_private, FALSE);
1774 static void resolve_next_service(struct sipe_core_private *sipe_private,
1775 const struct sip_service_data *start);
1776 static void resolve_next_address(struct sipe_core_private *sipe_private,
1777 gboolean initial);
1778 static void sip_transport_error(struct sipe_transport_connection *conn,
1779 const gchar *msg)
1781 struct sipe_core_private *sipe_private = conn->user_data;
1783 /* This failed attempt was based on a DNS SRV record */
1784 if (sipe_private->service_data) {
1785 resolve_next_service(sipe_private, NULL);
1786 /* This failed attempt was based on a DNS A record */
1787 } else if (sipe_private->address_data) {
1788 resolve_next_address(sipe_private, FALSE);
1789 } else {
1790 sipe_backend_connection_error(SIPE_CORE_PUBLIC,
1791 SIPE_CONNECTION_ERROR_NETWORK,
1792 msg);
1796 /* server_name must be g_alloc()'ed */
1797 static void sipe_server_register(struct sipe_core_private *sipe_private,
1798 guint type,
1799 gchar *server_name,
1800 guint server_port)
1802 sipe_connect_setup setup = {
1803 type,
1804 server_name,
1805 (server_port != 0) ? server_port :
1806 (type == SIPE_TRANSPORT_TLS) ? 5061 : 5060,
1807 sipe_private,
1808 sip_transport_connected,
1809 sip_transport_input,
1810 sip_transport_error
1812 struct sip_transport *transport = g_new0(struct sip_transport, 1);
1814 transport->auth_retry = TRUE;
1815 transport->server_name = server_name;
1816 transport->server_port = setup.server_port;
1817 transport->connection = sipe_backend_transport_connect(SIPE_CORE_PUBLIC,
1818 &setup);
1819 sipe_private->transport = transport;
1822 struct sip_service_data {
1823 const char *protocol;
1824 const char *transport;
1825 guint type;
1829 * Autodiscover using DNS SRV records. See RFC2782/3263
1831 * Service list for AUTO
1833 static const struct sip_service_data service_autodetect[] = {
1834 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS }, /* for internal TLS connections */
1835 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP }, /* for internal TCP connections */
1836 { "sip", "tls", SIPE_TRANSPORT_TLS }, /* for external TLS connections */
1837 { "sip", "tcp", SIPE_TRANSPORT_TCP }, /*.for external TCP connections */
1838 { NULL, NULL, 0 }
1841 /* Service list for SSL/TLS */
1842 static const struct sip_service_data service_tls[] = {
1843 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS }, /* for internal TLS connections */
1844 { "sip", "tls", SIPE_TRANSPORT_TLS }, /* for external TLS connections */
1845 { NULL, NULL, 0 }
1848 /* Service list for TCP */
1849 static const struct sip_service_data service_tcp[] = {
1850 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP }, /* for internal TCP connections */
1851 { "sip", "tcp", SIPE_TRANSPORT_TCP }, /*.for external TCP connections */
1852 { NULL, NULL, 0 }
1855 static const struct sip_service_data *services[] = {
1856 service_autodetect, /* SIPE_TRANSPORT_AUTO */
1857 service_tls, /* SIPE_TRANSPORT_TLS */
1858 service_tcp /* SIPE_TRANSPORT_TCP */
1861 struct sip_address_data {
1862 const char *prefix;
1863 guint port;
1867 * Autodiscover using DNS A records. This is an extension addded
1868 * by Microsoft. See http://support.microsoft.com/kb/2619522
1870 static const struct sip_address_data addresses[] = {
1871 { "sipinternal", 5061 },
1872 { "sipexternal", 443 },
1874 * Our implementation supports only one port per host name. If the host name
1875 * resolves OK, we abort the search and try to connect. If we would know if we
1876 * are trying to connect from "Intranet" or "Internet" then we could choose
1877 * between those two ports.
1879 * We drop port 5061 in order to cover the "Internet" case.
1881 * { "sip", 5061 },
1883 { "sip", 443 },
1884 { NULL, 0 }
1887 static void sipe_core_dns_resolved(struct sipe_core_public *sipe_public,
1888 const gchar *hostname, guint port)
1890 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1891 gboolean service = sipe_private->service_data != NULL;
1893 sipe_private->dns_query = NULL;
1895 if (hostname) {
1896 gchar *host;
1897 guint type;
1899 if (service) {
1900 host = g_strdup(hostname);
1901 type = sipe_private->service_data->type;
1902 } else {
1903 /* DNS A resolver returns an IP address */
1904 host = g_strdup_printf("%s.%s",
1905 sipe_private->address_data->prefix,
1906 sipe_private->public.sip_domain);
1907 port = sipe_private->address_data->port;
1908 type = sipe_private->transport_type;
1909 if (type == SIPE_TRANSPORT_AUTO)
1910 type = SIPE_TRANSPORT_TLS;
1913 SIPE_DEBUG_INFO("sipe_core_dns_resolved - %s hostname: %s port: %d",
1914 service ? "SRV" : "A", hostname, port);
1915 sipe_server_register(sipe_private, type, host, port);
1916 } else {
1917 if (service)
1918 resolve_next_service(SIPE_CORE_PRIVATE, NULL);
1919 else
1920 resolve_next_address(SIPE_CORE_PRIVATE, FALSE);
1924 static void resolve_next_service(struct sipe_core_private *sipe_private,
1925 const struct sip_service_data *start)
1927 if (start) {
1928 sipe_private->service_data = start;
1929 } else {
1930 sipe_private->service_data++;
1931 if (sipe_private->service_data->protocol == NULL) {
1933 /* We tried all services */
1934 sipe_private->service_data = NULL;
1936 /* Try A records list next */
1937 SIPE_DEBUG_INFO_NOFORMAT("no SRV records found; trying A records next");
1938 resolve_next_address(sipe_private, TRUE);
1939 return;
1943 /* Try to resolve next service */
1944 sipe_private->dns_query = sipe_backend_dns_query_srv(
1945 SIPE_CORE_PUBLIC,
1946 sipe_private->service_data->protocol,
1947 sipe_private->service_data->transport,
1948 sipe_private->public.sip_domain,
1949 (sipe_dns_resolved_cb) sipe_core_dns_resolved,
1950 SIPE_CORE_PUBLIC);
1953 static void resolve_next_address(struct sipe_core_private *sipe_private,
1954 gboolean initial)
1956 gchar *hostname;
1958 if (initial) {
1959 sipe_private->address_data = addresses;
1960 } else {
1961 sipe_private->address_data++;
1962 if (sipe_private->address_data->prefix == NULL) {
1963 guint type = sipe_private->transport_type;
1965 /* We tried all addresss */
1966 sipe_private->address_data = NULL;
1968 /* Try connecting to the SIP hostname directly */
1969 SIPE_DEBUG_INFO_NOFORMAT("no SRV or A records found; using SIP domain as fallback");
1970 if (type == SIPE_TRANSPORT_AUTO)
1971 type = SIPE_TRANSPORT_TLS;
1973 sipe_server_register(sipe_private, type,
1974 g_strdup(sipe_private->public.sip_domain),
1976 return;
1980 /* Try to resolve next address */
1981 hostname = g_strdup_printf("%s.%s",
1982 sipe_private->address_data->prefix,
1983 sipe_private->public.sip_domain);
1984 sipe_private->dns_query = sipe_backend_dns_query_a(
1985 SIPE_CORE_PUBLIC,
1986 hostname,
1987 sipe_private->address_data->port,
1988 (sipe_dns_resolved_cb) sipe_core_dns_resolved,
1989 SIPE_CORE_PUBLIC);
1990 g_free(hostname);
1994 * NOTE: this function can be called before sipe_core_allocate()!
1996 gboolean sipe_core_transport_sip_requires_password(guint authentication,
1997 gboolean sso)
1999 return(sip_sec_requires_password(authentication, sso));
2002 void sipe_core_transport_sip_connect(struct sipe_core_public *sipe_public,
2003 guint transport,
2004 guint authentication,
2005 const gchar *server,
2006 const gchar *port)
2008 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
2010 /* backend initialization is complete */
2011 sipe_core_backend_initialized(sipe_private, authentication);
2014 * Initializing the certificate sub-system will trigger the generation
2015 * of a cryptographic key pair which takes time. If we do this after we
2016 * have connected to the server then there is a risk that we run into a
2017 * SIP connection timeout. So let's get this out of the way now...
2019 * This is currently only needed if the user has selected TLS-DSK.
2021 if (sipe_private->authentication_type == SIPE_AUTHENTICATION_TYPE_TLS_DSK)
2022 sipe_certificate_init(sipe_private);
2024 if (server) {
2025 /* Use user specified server[:port] */
2026 int port_number = 0;
2028 if (port)
2029 port_number = atoi(port);
2031 SIPE_DEBUG_INFO("sipe_core_connect: user specified SIP server %s:%d",
2032 server, port_number);
2034 sipe_server_register(sipe_private, transport,
2035 g_strdup(server), port_number);
2036 } else {
2037 /* Server auto-discovery */
2039 /* Remember user specified transport type */
2040 sipe_private->transport_type = transport;
2041 resolve_next_service(sipe_private, services[transport]);
2045 const gchar *sipe_core_transport_sip_server_name(struct sipe_core_public *sipe_public)
2047 struct sip_transport *transport = SIPE_CORE_PRIVATE->transport;
2048 return(transport ? transport->server_name : NULL);
2051 int sip_transaction_cseq(struct transaction *trans)
2053 int cseq;
2055 g_return_val_if_fail(trans && trans->key, 0);
2057 sscanf(trans->key, "<%*[a-zA-Z0-9]><%d INVITE>", &cseq);
2058 return cseq;
2062 Local Variables:
2063 mode: c
2064 c-file-style: "bsd"
2065 indent-tabs-mode: t
2066 tab-width: 8
2067 End: