tport.c 115 KB
Newer Older
Pekka Pessi's avatar
Pekka Pessi committed
1 2 3 4 5 6 7
/*
 * This file is part of the Sofia-SIP package
 *
 * Copyright (C) 2005 Nokia Corporation.
 *
 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
 *
8
 * This library is free software; you can redistribute it and/or
Pekka Pessi's avatar
Pekka Pessi committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

/**@CFILE tport.c Transport interface implementation.
 *
 * See tport.docs for more detailed description of tport interface.
 *
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 * @author Ismo Puustinen <Ismo.H.Puustinen@nokia.com>
 * @author Tat Chan <Tat.Chan@nokia.com>
 * @author Kai Vehmanen <kai.vehmanen@nokia.com>
Martti Mela's avatar
Martti Mela committed
33
 * @author Martti Mela <Martti.Mela@nokia.com>
Pekka Pessi's avatar
Pekka Pessi committed
34 35 36 37 38 39
 *
 * @date Created: Thu Jul 20 12:54:32 2000 ppessi
 */

#include "config.h"

40 41
#include <sofia-sip/string0.h>
#include <sofia-sip/su.h>
42
#include <sofia-sip/su_errno.h>
43 44 45
#include <sofia-sip/su_alloc.h>
#include <sofia-sip/su_tagarg.h>
#include <sofia-sip/su_localinfo.h>
Pekka Pessi's avatar
Pekka Pessi committed
46 47 48

typedef struct tport_nat_s tport_nat_t;

49
#define SU_WAKEUP_ARG_T         struct tport_s
50
#define SU_TIMER_ARG_T          struct tport_s
51
#define SU_MSG_ARG_T            union tport_su_msg_arg
52

53
#include <sofia-sip/su_wait.h>
Pekka Pessi's avatar
Pekka Pessi committed
54

55 56
#include <sofia-sip/msg.h>
#include <sofia-sip/msg_addr.h>
57
#include <sofia-sip/hostdomain.h>
Pekka Pessi's avatar
Pekka Pessi committed
58

59 60 61 62 63
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <limits.h>
Pekka Pessi's avatar
Pekka Pessi committed
64 65 66 67 68

#ifndef IPPROTO_SCTP
#define IPPROTO_SCTP (132)
#endif

69 70 71
#include "sofia-sip/tport.h"
#include "sofia-sip/su_uniqueid.h"
#include <sofia-sip/rbtree.h>
Pekka Pessi's avatar
Pekka Pessi committed
72

73 74
#include "tport_internal.h"

Pekka Pessi's avatar
Pekka Pessi committed
75 76 77 78
#if HAVE_FUNC
#elif HAVE_FUNCTION
#define __func__ __FUNCTION__
#else
79
static char const __func__[] = "tport";
Pekka Pessi's avatar
Pekka Pessi committed
80 81 82 83 84 85 86 87 88 89
#endif

#define STACK_RECV(tp, msg, now)		       \
  (tp)->tp_master->mr_tpac->tpac_recv((tp)->tp_master->mr_stack, (tp), \
				      (msg), (tp)->tp_magic, (now))

#define STACK_ERROR(tp, errcode, dstname) \
  (tp)->tp_master->mr_tpac->tpac_error((tp)->tp_master->mr_stack, (tp), \
				       (errcode), (dstname))

90 91 92
#define STACK_ADDRESS(tp)		       \
  (tp)->tp_master->mr_tpac->tpac_address((tp)->tp_master->mr_stack, (tp))

Pekka Pessi's avatar
Pekka Pessi committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106
#define TP_STACK   tp_master->mr_stack

/* Define macros for rbtree implementation */
#define TP_LEFT(tp) ((tp)->tp_left)
#define TP_RIGHT(tp) ((tp)->tp_right)
#define TP_PARENT(tp) ((tp)->tp_dad)
#define TP_SET_RED(tp) ((tp)->tp_black = 0)
#define TP_SET_BLACK(tp) ((tp)->tp_black = 1)
#define TP_IS_RED(tp) ((tp) && (tp)->tp_black == 0)
#define TP_IS_BLACK(tp) (!(tp) || (tp)->tp_black == 1)
#define TP_COPY_COLOR(dst, src) ((dst)->tp_black = (src)->tp_black)
#define TP_INSERT(tp) ((void)0)
#define TP_REMOVE(tp) ((tp)->tp_left = (tp)->tp_right = (tp)->tp_dad = NULL)

107
su_inline int tp_cmp(tport_t const *a, tport_t const *b)
Pekka Pessi's avatar
Pekka Pessi committed
108 109 110 111 112
{
  if (a == b)
    return 0;
  
  if (a->tp_addrlen != b->tp_addrlen)
113
    return (int)(a->tp_addrlen - b->tp_addrlen);
Pekka Pessi's avatar
Pekka Pessi committed
114

115
  return memcmp(a->tp_addr, b->tp_addr, a->tp_addrlen);
Pekka Pessi's avatar
Pekka Pessi committed
116 117
}

118
su_inline int tprb_is_inserted(tport_t const *a)
Pekka Pessi's avatar
Pekka Pessi committed
119 120 121 122
{
  return a->tp_dad != 0 || a->tp_left != 0 || a->tp_right != 0;
}

123
RBTREE_PROTOS(su_inline, tprb, tport_t);
Pekka Pessi's avatar
Pekka Pessi committed
124

125
RBTREE_BODIES(su_inline, tprb, tport_t, 
Pekka Pessi's avatar
Pekka Pessi committed
126 127 128 129
	      TP_LEFT, TP_RIGHT, TP_PARENT,
	      TP_IS_RED, TP_SET_RED, TP_IS_BLACK, TP_SET_BLACK, TP_COPY_COLOR,
	      tp_cmp, TP_INSERT, TP_REMOVE);

130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
static void tplist_insert(tport_t **list, tport_t *tp)
{
  if (*list == NULL)
    *list = tp;
  else
    tp->tp_right = *list, (*list)->tp_left = tp, *list = tp;

  for (tp = *list; tp; tp = tp->tp_right) {
    assert(tp->tp_left == NULL || tp == tp->tp_left->tp_right);
    assert(tp->tp_right == NULL || tp == tp->tp_right->tp_left);
  }
}

static void tplist_remove(tport_t **list, tport_t *tp)
{
  if (*list == tp) {
    *list = tp->tp_right; assert(tp->tp_left == NULL);
  }
  else if (tp->tp_left) {
    tp->tp_left->tp_right = tp->tp_right;
  }
  if (tp->tp_right) {
    tp->tp_right->tp_left = tp->tp_left;
  }
  TP_REMOVE(tp);
}

Pekka Pessi's avatar
Pekka Pessi committed
157 158 159 160 161 162
enum {
  /** Default per-thread read queue length */
  THRP_PENDING = 8
};

struct tport_pending_s {
Pekka Pessi's avatar
Pekka Pessi committed
163
  /* tport_pending_t       *p_left, *p_right, *p_parent; */
Pekka Pessi's avatar
Pekka Pessi committed
164 165 166
  void               *p_client;
  tport_pending_error_f *p_callback;
  msg_t              *p_msg;
167 168
  unsigned short      p_reported;
  unsigned short      p_on_success;
Pekka Pessi's avatar
Pekka Pessi committed
169 170 171
};

/** Return true if transport is master. */
172
int tport_is_master(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
173 174 175 176 177 178 179
{
  return 
    self && 
    self->tp_master->mr_master == self;
}

/** Return true if transport is primary. */
180
int tport_is_primary(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
181 182 183 184 185 186 187
{
  return 
    self && 
    self->tp_pri->pri_primary == self;
}

/** Return true if transport is secondary. */
188
int tport_is_secondary(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
189 190 191 192 193 194 195
{
  return 
    self && 
    self->tp_master->mr_master != self && 
    self->tp_pri->pri_primary != self;
}

196
/** Test if transport has been registered to su_root_t */
197
int tport_is_registered(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
198 199 200 201 202
{
  return self->tp_index != 0;
}

/** Test if transport is stream. */
203
int tport_is_stream(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
204
{
205
  return self->tp_addrinfo->ai_socktype == SOCK_STREAM;
Pekka Pessi's avatar
Pekka Pessi committed
206 207 208
}
 
/** Test if transport is dgram. */
209
int tport_is_dgram(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
210
{
211
  return self->tp_addrinfo->ai_socktype == SOCK_DGRAM;
Pekka Pessi's avatar
Pekka Pessi committed
212 213 214
}
 
/** Test if transport is udp. */
215
int tport_is_udp(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
216
{
217
  return self->tp_addrinfo->ai_protocol == IPPROTO_UDP;
Pekka Pessi's avatar
Pekka Pessi committed
218 219 220
}
 
/** Test if transport is tcp. */
221
int tport_is_tcp(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
222
{
223
  return self->tp_addrinfo->ai_protocol == IPPROTO_TCP;
Pekka Pessi's avatar
Pekka Pessi committed
224 225 226 227 228 229
}
 
/** Return 1 if transport is reliable, 0 otherwise.
 *
 * (Note that this is part of external API).
 */
230
int tport_is_reliable(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
231 232
{
  return self != NULL && 
233 234
    (self->tp_addrinfo->ai_socktype == SOCK_STREAM || 
     self->tp_addrinfo->ai_socktype == SOCK_SEQPACKET);
Pekka Pessi's avatar
Pekka Pessi committed
235 236
}

237 238 239
/** Return 0 if self is local, nonzero otherwise.
 *
 * The return valu is the 
240
 *
241
 * @sa TPTAG_PUBLIC(), enum #tport_via.
242
 */
243
int tport_is_public(tport_t const *self)
244
{
245
  return self->tp_pri->pri_public;
246
}
Pekka Pessi's avatar
Pekka Pessi committed
247 248

/** Return true if transport supports IPv4 */
249
int tport_has_ip4(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
250 251
{
  return self && 
252 253
    (self->tp_addrinfo->ai_family == 0 || 
     self->tp_addrinfo->ai_family == AF_INET);
Pekka Pessi's avatar
Pekka Pessi committed
254 255
}

256 257

#if SU_HAVE_IN6
Pekka Pessi's avatar
Pekka Pessi committed
258
/** Return true if transport supports IPv6 */
259
int tport_has_ip6(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
260 261
{
  return self && 
262 263
    (self->tp_addrinfo->ai_family == 0 || 
     self->tp_addrinfo->ai_family == AF_INET6);
Pekka Pessi's avatar
Pekka Pessi committed
264
}
265
#endif
Pekka Pessi's avatar
Pekka Pessi committed
266 267

/** Return true if transport supports TLS. */
268
int tport_has_tls(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
269
{
270 271
  return self && self->tp_pri->pri_has_tls;

Pekka Pessi's avatar
Pekka Pessi committed
272 273
}

274 275 276
/** Return true if transport is being updated. */
int tport_is_updating(tport_t const *self)
{
277 278 279 280 281 282 283 284 285 286 287 288
  tport_primary_t *pri;

  if (tport_is_master(self)) {
    for (pri = self->tp_master->mr_primaries; pri; pri = pri->pri_next) 
      if (pri->pri_updating)
	return 1;
  }
  else if (tport_is_primary(self)) {
    return self->tp_pri->pri_updating;
  }

  return 0;
289 290
}

291 292 293 294
/** Test if transport has been closed.
 *
 * @since New in @VERSION_1_12_4
 */
295
inline int tport_is_closed(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
296 297 298 299
{
  return self->tp_closed;
}

300 301 302 303
/** Test if transport has been shut down.
 *
 * @since New in @VERSION_1_12_4
 */
304
inline int tport_is_shutdown(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
305 306 307 308 309
{
  return self->tp_closed || self->tp_send_close || self->tp_recv_close;
}

/** Test if transport is bound */
310
su_inline int tport_is_bound(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
311 312 313 314
{
  return self->tp_protoname != NULL;
}

315
/** Test if transport connection has been established. @NEW_1_12_5 */
316
int tport_is_connected(tport_t const *self)
317 318 319 320
{
  return self->tp_is_connected;
}

321 322 323 324 325 326 327 328 329 330 331 332 333
/** Test if transport can be used to send message. @NEW_1_12_7 */
int tport_is_clear_to_send(tport_t const *self)
{
  return
    tport_is_master(self) || 
    tport_is_primary(self) || 
    (tport_is_secondary(self) &&
     tport_is_registered(self) &&
     self->tp_reusable &&
     !self->tp_closed &&
     !self->tp_send_close);
}

334 335 336 337 338 339
/** Return true if transport has message in send queue. @NEW_1_12_7  */
int tport_has_queued(tport_t const *self)
{
  return self && self->tp_queue && self->tp_queue[self->tp_qhead];
}

Pekka Pessi's avatar
Pekka Pessi committed
340
/** MTU for transport  */
341
su_inline unsigned tport_mtu(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
342 343 344 345
{
  return self->tp_params->tpp_mtu;
}

346
su_inline
Pekka Pessi's avatar
Pekka Pessi committed
347 348 349 350 351
int tport_has_sigcomp(tport_t const *self)
{
  return self->tp_name->tpn_comp != NULL;
}

352 353 354 355 356
/** Set IP TOS for socket */
void tport_set_tos(su_socket_t socket, su_addrinfo_t *ai, int tos)
{
  if (tos >= 0 && 
      ai->ai_family == AF_INET && 
357
      setsockopt(socket, IPPROTO_IP, IP_TOS, (const void*)&tos, sizeof(tos)) < 0) {
358 359 360 361 362
    SU_DEBUG_3(("tport: setsockopt(IP_TOS): %s\n",
		su_strerror(su_errno())));
  }
}

Pekka Pessi's avatar
Pekka Pessi committed
363 364 365 366
static 
tport_t *tport_connect(tport_primary_t *pri, su_addrinfo_t *ai, 
		       tp_name_t const *tpn);

367
static int bind6only_check(tport_master_t *mr);
368 369 370 371 372 373 374 375 376 377 378

static
int tport_server_addrinfo(tport_master_t *mr,
			  char const *canon,
			  int family,
			  char const *host, 
			  char const *service,
			  char const *protocol,
			  char const * const transports[],
			  su_addrinfo_t **res);

Pekka Pessi's avatar
Pekka Pessi committed
379 380 381 382 383
static int tport_get_local_addrinfo(tport_master_t *mr, 
				    char const *port,
				    su_addrinfo_t const *hints,
				    su_addrinfo_t **return_ai);

384 385 386 387 388
int tport_getaddrinfo(char const *node, char const *service,
		      su_addrinfo_t const *hints,
		      su_addrinfo_t **res);

static void tport_freeaddrinfo(su_addrinfo_t *ai);
Pekka Pessi's avatar
Pekka Pessi committed
389

Pekka Pessi's avatar
Pekka Pessi committed
390 391 392 393
static
int tport_addrinfo_copy(su_addrinfo_t *dst, void *addr, socklen_t addrlen,
			su_addrinfo_t const *src);

Pekka Pessi's avatar
Pekka Pessi committed
394
static int
395 396 397 398 399 400
  tport_bind_client(tport_master_t *self, tp_name_t const *tpn,
		    char const * const transports[], enum tport_via public,
		    tagi_t *tags),
  tport_bind_server(tport_master_t *, tp_name_t const *tpn,
		    char const * const transports[],  enum tport_via public,
		    tagi_t *tags),
Pekka Pessi's avatar
Pekka Pessi committed
401

402
  tport_setname(tport_t *, char const *, su_addrinfo_t const *, char const *),
403 404 405
  tport_wakeup_pri(su_root_magic_t *m, su_wait_t *w, tport_t *self),
  tport_wakeup(su_root_magic_t *m, su_wait_t *w, tport_t *self),
  tport_base_wakeup(tport_t *self, int events),
406
  tport_connected(su_root_magic_t *m, su_wait_t *w, tport_t *self),
Pekka Pessi's avatar
Pekka Pessi committed
407
  tport_resolve(tport_t *self, msg_t *msg, tp_name_t const *tpn),
408
  tport_send_error(tport_t *, msg_t *, tp_name_t const *),
Pekka Pessi's avatar
Pekka Pessi committed
409
  tport_queue(tport_t *self, msg_t *msg),
410
  tport_queue_rest(tport_t *self, msg_t *msg, msg_iovec_t iov[], size_t iovused),
Pekka Pessi's avatar
Pekka Pessi committed
411
  tport_pending_error(tport_t *self, su_sockaddr_t const *dst, int error),
412
  tport_pending_errmsg(tport_t *self, msg_t *msg, int error);
Pekka Pessi's avatar
Pekka Pessi committed
413

414 415 416 417
static ssize_t tport_vsend(tport_t *self, msg_t *msg, tp_name_t const *tpn,
			   msg_iovec_t iov[], size_t iovused,
			   struct sigcomp_compartment *cc);

Pekka Pessi's avatar
Pekka Pessi committed
418 419 420 421 422 423 424
tport_t *tport_by_addrinfo(tport_primary_t const *pri,
			   su_addrinfo_t const *ai,
			   tp_name_t const *tpn);

void tport_peer_address(tport_t *self, msg_t *msg);

static void tport_parse(tport_t *self, int complete, su_time_t now);
Pekka Pessi's avatar
Pekka Pessi committed
425

426 427
static tport_primary_t *tport_alloc_primary(tport_master_t *mr,
					    tport_vtable_t const *vtable,
428
					    tp_name_t tpn[1],
429 430 431 432 433 434
					    su_addrinfo_t *ai, 
					    tagi_t const *tags,
					    char const **return_culprit);

static tport_primary_t *tport_listen(tport_master_t *mr,
				     tport_vtable_t const *vtable,
435
				     tp_name_t tpn[1],
436
				     su_addrinfo_t *ai,
Pekka Pessi's avatar
Pekka Pessi committed
437 438 439
				     tagi_t *tags);
static void tport_zap_primary(tport_primary_t *);

440
static char *localipname(int pf, char *buf, size_t bufsiz);
Pekka Pessi's avatar
Pekka Pessi committed
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
static int getprotohints(su_addrinfo_t *hints,
			 char const *proto, int flags);


/* Stack class used when transports are being destroyed */
static
void tport_destroy_recv(tp_stack_t *stack, tport_t *tp, 
			msg_t *msg, tp_magic_t *magic, 
			su_time_t received)
{
  msg_destroy(msg);
}

static
void tport_destroy_error(tp_stack_t *stack, tport_t *tp, 
			 int errcode, char const *remote)
{
}

static
msg_t *tport_destroy_alloc(tp_stack_t *stack, int flags, 
462
			   char const data[], usize_t len,
Pekka Pessi's avatar
Pekka Pessi committed
463 464 465 466 467 468 469 470 471
			   tport_t const *tp,
			   tp_client_t *tpc)
{
  return NULL;
}

/** Name for "any" transport. @internal */
static char const tpn_any[] = "*";

472 473 474 475 476 477
/** Create the master transport. 
 *
 * @TAGS
 * TPTAG_LOG(), TPTAG_DUMP(), tags used with tport_set_params(), especially
 * TPTAG_QUEUESIZE().
 */
Pekka Pessi's avatar
Pekka Pessi committed
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
tport_t *tport_tcreate(tp_stack_t *stack,
		       tp_stack_class_t const *tpac,
		       su_root_t *root, 
		       tag_type_t tag, tag_value_t value, ...)
{
  tport_master_t *mr;
  tp_name_t *tpn;
  tport_params_t *tpp;
  ta_list ta;

  if (!stack || !tpac || !root) {
    su_seterrno(EINVAL);
    return NULL;
  }

  mr = su_home_clone(NULL, sizeof *mr);
  if (!mr)
    return NULL;

497
  SU_DEBUG_7(("%s(): %p\n", "tport_create", (void *)mr));
Pekka Pessi's avatar
Pekka Pessi committed
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513

  mr->mr_stack = stack;
  mr->mr_tpac = tpac;
  mr->mr_root = root;

  mr->mr_master->tp_master = mr;
  mr->mr_master->tp_params = tpp = mr->mr_params;

  mr->mr_master->tp_reusable = 1;
  tpp->tpp_mtu = UINT_MAX;
  tpp->tpp_thrprqsize = THRP_PENDING;
  tpp->tpp_qsize = TPORT_QUEUESIZE;
  tpp->tpp_sdwn_error = 1;
  tpp->tpp_idle = UINT_MAX;
  tpp->tpp_timeout = UINT_MAX;
  tpp->tpp_sigcomp_lifetime = UINT_MAX;
514 515 516
  tpp->tpp_keepalive = 0;
  tpp->tpp_pingpong = 0;
  tpp->tpp_pong2ping = 0;
517
  tpp->tpp_stun_server = 1;
518
  tpp->tpp_tos = -1;                  /* set invalid, valid values are 0-255 */
Pekka Pessi's avatar
Pekka Pessi committed
519 520 521 522 523 524 525 526 527 528 529 530

  tpn = mr->mr_master->tp_name;
  tpn->tpn_proto = "*";
  tpn->tpn_host = "*";
  tpn->tpn_canon = "*";
  tpn->tpn_port = "*";

  ta_start(ta, tag, value);

  tport_set_params(mr->mr_master, ta_tags(ta));
  tport_open_log(mr, ta_args(ta));

531
#if HAVE_SOFIA_STUN
532
  tport_init_stun_server(mr, ta_args(ta));
533
#endif
534

Pekka Pessi's avatar
Pekka Pessi committed
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
  ta_end(ta);

  return mr->mr_master;
}

/** Destroy the master transport. */
void tport_destroy(tport_t *self)
{
  tport_master_t *mr;

  static tp_stack_class_t tport_destroy_tpac[1] = 
    {{
	sizeof tport_destroy_tpac,
	/* tpac_recv */ tport_destroy_recv,
	/* tpac_error */ tport_destroy_error,
	/* tpac_alloc */ tport_destroy_alloc,
551
	/* tpac_address */ NULL
Pekka Pessi's avatar
Pekka Pessi committed
552 553
      }};

554
  SU_DEBUG_7(("%s(%p)\n", __func__, (void *)self));
Pekka Pessi's avatar
Pekka Pessi committed
555 556 557 558 559 560 561 562 563 564 565 566 567 568

  if (self == NULL)
    return;
  
  assert(tport_is_master(self));
  if (!tport_is_master(self))
    return;

  mr = (tport_master_t *)self;
  mr->mr_tpac = tport_destroy_tpac;

  while (mr->mr_primaries)
    tport_zap_primary(mr->mr_primaries);

569
#if HAVE_SOFIA_STUN
570
  tport_deinit_stun_server(mr);
571
#endif
Pekka Pessi's avatar
Pekka Pessi committed
572 573 574 575 576 577 578 579 580 581

  if (mr->mr_dump_file)
    fclose(mr->mr_dump_file), mr->mr_dump_file = NULL;

  if (mr->mr_timer)
    su_timer_destroy(mr->mr_timer), mr->mr_timer = NULL;

  su_home_zap(mr->mr_home);
}

582

Pekka Pessi's avatar
Pekka Pessi committed
583 584
/** Allocate a primary transport */
static 
585 586
tport_primary_t *tport_alloc_primary(tport_master_t *mr,
				     tport_vtable_t const *vtable,
587
				     tp_name_t tpn[1],
588 589 590
				     su_addrinfo_t *ai,
				     tagi_t const *tags,
				     char const **return_culprit)
Pekka Pessi's avatar
Pekka Pessi committed
591 592
{
  tport_primary_t *pri, **next;
593
  tport_t *tp;
594
  int save_errno;
Pekka Pessi's avatar
Pekka Pessi committed
595 596 597

  for (next = &mr->mr_primaries; *next; next = &(*next)->pri_next)
    ;
598 599
  
  assert(vtable->vtp_pri_size >= sizeof *pri);
Pekka Pessi's avatar
Pekka Pessi committed
600

601
  if ((pri = su_home_clone(mr->mr_home, vtable->vtp_pri_size))) {
Pekka Pessi's avatar
Pekka Pessi committed
602
    tport_t *tp = pri->pri_primary;
603 604 605
    pri->pri_vtable = vtable;
    pri->pri_public = vtable->vtp_public;

Pekka Pessi's avatar
Pekka Pessi committed
606 607
    tp->tp_master = mr;
    tp->tp_pri = pri;
608
    tp->tp_socket = INVALID_SOCKET;
Pekka Pessi's avatar
Pekka Pessi committed
609 610 611 612 613 614 615

    tp->tp_magic = mr->mr_master->tp_magic;

    tp->tp_params = pri->pri_params;
    memcpy(tp->tp_params, mr->mr_params, sizeof (*tp->tp_params));
    tp->tp_reusable = mr->mr_master->tp_reusable;

Pekka Pessi's avatar
Pekka Pessi committed
616
    if (!pri->pri_public)
617
      tp->tp_addrinfo->ai_addr = &tp->tp_addr->su_sa;
618

619 620
    SU_DEBUG_5(("%s(%p): new primary tport %p\n", __func__, (void *)mr,
		(void *)pri));
Pekka Pessi's avatar
Pekka Pessi committed
621 622
  }

623 624 625 626 627 628 629
  *next = pri; 
  tp = pri->pri_primary;
  
  if (!tp)
    *return_culprit = "alloc";
  else if (tport_set_params(tp, TAG_NEXT(tags)) < 0)
    *return_culprit = "tport_set_params";
Pekka Pessi's avatar
Pekka Pessi committed
630 631
  else if (vtable->vtp_init_primary &&
	   vtable->vtp_init_primary(pri, tpn, ai, tags, return_culprit) < 0)
632 633 634 635 636 637 638 639
    ;
  else if (tport_setname(tp, vtable->vtp_name, ai, tpn->tpn_canon) == -1) 
    *return_culprit = "tport_setname";
  else if (tpn->tpn_ident && 
	   !(tp->tp_name->tpn_ident = su_strdup(tp->tp_home, tpn->tpn_ident)))
    *return_culprit = "alloc ident";
  else
    return pri;			/* Success */
Pekka Pessi's avatar
Pekka Pessi committed
640

641
  save_errno = su_errno();
642
  tport_zap_primary(pri);
643
  su_seterrno(save_errno);
Pekka Pessi's avatar
Pekka Pessi committed
644

645 646
  return NULL;
}
647

648 649 650 651 652 653 654 655 656 657 658 659 660 661 662

/** Destroy a primary transport and its secondary transports. @internal */
static 
void tport_zap_primary(tport_primary_t *pri)
{
  tport_primary_t **prip;

  if (pri == NULL)
    return;

  assert(tport_is_primary(pri->pri_primary));

  if (pri->pri_vtable->vtp_deinit_primary)
    pri->pri_vtable->vtp_deinit_primary(pri);

663 664 665 666
  while (pri->pri_open)
    tport_zap_secondary(pri->pri_open);
  while (pri->pri_closed)
    tport_zap_secondary(pri->pri_closed);
667 668 669 670 671 672 673 674 675

  /* We have just a single-linked list for primary transports */
  for (prip = &pri->pri_master->mr_primaries;
       *prip != pri;
       prip = &(*prip)->pri_next)
    assert(*prip);

  *prip = pri->pri_next;

676
  tport_zap_secondary((tport_t *)pri);
677
}
678

Pekka Pessi's avatar
Pekka Pessi committed
679 680 681 682 683 684
/**Create a primary transport object with socket.
 *
 * Creates a primary transport object with a server socket, and then
 * registers the socket with suitable events to the root.
 *
 * @param dad   parent (master or primary) transport object
Pekka Pessi's avatar
Pekka Pessi committed
685
 * @param ai    pointer to addrinfo structure
Pekka Pessi's avatar
Pekka Pessi committed
686 687 688 689
 * @param canon canonical name of node
 * @param protoname name of the protocol
 */
static
690 691
tport_primary_t *tport_listen(tport_master_t *mr,
			      tport_vtable_t const *vtable,
692
			      tp_name_t tpn[1],
693
			      su_addrinfo_t *ai, 
Pekka Pessi's avatar
Pekka Pessi committed
694 695 696 697 698 699 700 701
			      tagi_t *tags)
{
  tport_primary_t *pri = NULL;

  int err;
  int errlevel = 3;
  char buf[TPORT_HOSTPORTSIZE];

702 703 704 705 706
  char const *protoname = vtable->vtp_name;
  char const *culprit = "unknown";

  su_sockaddr_t *su = (void *)ai->ai_addr;

Pekka Pessi's avatar
Pekka Pessi committed
707
  /* Log an error, return error */
Pekka Pessi's avatar
Pekka Pessi committed
708 709
#define TPORT_LISTEN_ERROR(errno, what)				     \
  ((void)(err = errno,						     \
710 711 712
	  ((err == EADDRINUSE || err == EAFNOSUPPORT ||		     \
	    err == ESOCKTNOSUPPORT || err == EPROTONOSUPPORT ||	     \
	    err == ENOPROTOOPT ? 7 : 3) < SU_LOG_LEVEL ?	     \
Pekka Pessi's avatar
Pekka Pessi committed
713 714
	     su_llog(tport_log, errlevel,			     \
		     "%s(%p): %s(pf=%d %s/%s): %s\n",		     \
715
		     __func__, pri ? (void *)pri : (void *)mr, what, \
716
		     ai->ai_family, protoname,			     \
Pekka Pessi's avatar
Pekka Pessi committed
717 718 719 720 721 722
		     tport_hostport(buf, sizeof(buf), su, 2),	     \
		     su_strerror(err)) : (void)0),		     \
	    tport_zap_primary(pri),		                     \
	    su_seterrno(err)),					     \
     (void *)NULL)

723
  /* Create a primary transport object for another transport. */
Pekka Pessi's avatar
Pekka Pessi committed
724
  pri = tport_alloc_primary(mr, vtable, tpn, ai, tags, &culprit);
725 726
  if (pri == NULL)
    return TPORT_LISTEN_ERROR(errno, culprit);
Pekka Pessi's avatar
Pekka Pessi committed
727

728
  if (pri->pri_primary->tp_socket != INVALID_SOCKET) {
Pekka Pessi's avatar
Pekka Pessi committed
729 730 731 732 733 734 735 736 737 738 739 740 741
    int index = 0;
    tport_t *tp = pri->pri_primary;
    su_wait_t wait[1] = { SU_WAIT_INIT };

    if (su_wait_create(wait, tp->tp_socket, tp->tp_events) == -1)
      return TPORT_LISTEN_ERROR(su_errno(), "su_wait_create");

    /* Register receiving or accepting function with events specified above */
    index = su_root_register(mr->mr_root, wait, tport_wakeup_pri, tp, 0); 
    if (index == -1) {
      su_wait_destroy(wait);
      return TPORT_LISTEN_ERROR(su_errno(), "su_root_register");
    }
Pekka Pessi's avatar
Pekka Pessi committed
742

Pekka Pessi's avatar
Pekka Pessi committed
743 744
    tp->tp_index = index;
  }
Pekka Pessi's avatar
Pekka Pessi committed
745

746
  pri->pri_primary->tp_has_connection = 0;
747

Pekka Pessi's avatar
Pekka Pessi committed
748
  SU_DEBUG_5(("%s(%p): %s " TPN_FORMAT "\n", 
749
	      __func__, (void *)pri, "listening at",
Pekka Pessi's avatar
Pekka Pessi committed
750 751 752 753 754
	      TPN_ARGS(pri->pri_primary->tp_name)));

  return pri;
}

Pekka Pessi's avatar
Pekka Pessi committed
755 756 757
int tport_bind_socket(int socket,
		      su_addrinfo_t *ai,
		      char const **return_culprit)
758
{
759
  su_sockaddr_t *su = (su_sockaddr_t *)ai->ai_addr;
760
  socklen_t sulen = (socklen_t)(ai->ai_addrlen);
761

762
  if (bind(socket, ai->ai_addr, sulen) == -1) {
763
    return *return_culprit = "bind", -1;
Pekka Pessi's avatar
Pekka Pessi committed
764
  }
765

766
  if (getsockname(socket, &su->su_sa, &sulen) == SOCKET_ERROR) {
767
    return *return_culprit = "getsockname", -1;
Pekka Pessi's avatar
Pekka Pessi committed
768
  }
769

770 771
  ai->ai_addrlen = sulen;

772 773 774 775 776 777 778 779 780 781 782 783 784 785
#if defined (__linux__) && defined (SU_HAVE_IN6)
  if (ai->ai_family == AF_INET6) {
    if (!SU_SOCKADDR_INADDR_ANY(su) &&
	(IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr) ||
	 IN6_IS_ADDR_V4COMPAT(&su->su_sin6.sin6_addr))) {
      su_sockaddr_t su0[1];
      
      memcpy(su0, su, sizeof su0);
      
      memset(su, 0, ai->ai_addrlen = sizeof su->su_sin);
      su->su_family = ai->ai_family = AF_INET;
      su->su_port = su0->su_port;
      
#ifndef IN6_V4MAPPED_TO_INADDR
786
#define IN6_V4MAPPED_TO_INADDR(in6, in4) \
787 788 789 790 791 792 793
      memcpy((in4), 12 + (uint8_t *)(in6), sizeof(struct in_addr))
#endif
      IN6_V4MAPPED_TO_INADDR(&su0->su_sin6.sin6_addr, &su->su_sin.sin_addr);
    }
  }
#endif

794
  return 0;
795 796
}

797 798 799 800 801 802 803 804 805 806 807

/** Indicate stack that a transport has been updated */
void tport_has_been_updated(tport_t *self)
{
  self->tp_pri->pri_updating = 0;

  if (self->tp_master->mr_tpac->tpac_address)
    self->tp_master->mr_tpac->tpac_address(self->tp_master->mr_stack, self);
}


808 809
static
int tport_set_events(tport_t *self, int set, int clear)
Pekka Pessi's avatar
Pekka Pessi committed
810
{
811
  int events;
Pekka Pessi's avatar
Pekka Pessi committed
812

813 814
  if (self == NULL)
    return -1;
Pekka Pessi's avatar
Pekka Pessi committed
815

816 817
  events = (self->tp_events | set) & ~clear;
  self->tp_events = events;
Pekka Pessi's avatar
Pekka Pessi committed
818

819 820
  if (self->tp_pri->pri_vtable->vtp_set_events)
    return self->tp_pri->pri_vtable->vtp_set_events(self);
821 822
  
  SU_DEBUG_7(("tport_set_events(%p): events%s%s%s\n", (void *)self,
823 824 825 826 827 828 829 830 831 832
	      (events & SU_WAIT_IN) ? " IN" : "",
	      (events & SU_WAIT_OUT) ? " OUT" : "",
	      SU_WAIT_CONNECT != SU_WAIT_OUT &&
	      (events & SU_WAIT_CONNECT) ? " CONNECT" : ""));
  
  return 
    su_root_eventmask(self->tp_master->mr_root, 
		      self->tp_index, 
		      self->tp_socket, 
		      self->tp_events = events);
Pekka Pessi's avatar
Pekka Pessi committed
833 834 835 836
}

/**Allocate a secondary transport. @internal
 *
837 838
 * Create a secondary transport object. The new transport initally shares
 * parameters structure with the original transport.
Pekka Pessi's avatar
Pekka Pessi committed
839
 *
840 841 842
 * @param pri    primary transport
 * @param socket socket for transport
 * @parma accepted true if the socket was accepted from server socket
Pekka Pessi's avatar
Pekka Pessi committed
843 844
 *
 * @return
845 846 847
 * Pointer to the newly created transport, or NULL upon an error.
 *
 * @note The socket is always closed upon error.
Pekka Pessi's avatar
Pekka Pessi committed
848
 */
849 850 851 852
tport_t *tport_alloc_secondary(tport_primary_t *pri, 
			       int socket,
			       int accepted,
			       char const **return_reason)
Pekka Pessi's avatar
Pekka Pessi committed
853 854 855 856
{
  tport_master_t *mr = pri->pri_master;
  tport_t *self;

Pekka Pessi's avatar
Pekka Pessi committed
857
  self = su_home_clone(mr->mr_home, pri->pri_vtable->vtp_secondary_size);
Pekka Pessi's avatar
Pekka Pessi committed
858 859

  if (self) {
860 861
    SU_DEBUG_7(("%s(%p): new secondary tport %p\n", 
		__func__, (void *)pri, (void *)self));
Pekka Pessi's avatar
Pekka Pessi committed
862 863 864 865 866

    self->tp_refs = -1;			/* Freshly allocated  */
    self->tp_master = mr;
    self->tp_pri = pri;
    self->tp_params = pri->pri_params;
867
    self->tp_accepted = accepted != 0;
Pekka Pessi's avatar
Pekka Pessi committed
868 869 870
    self->tp_reusable = pri->pri_primary->tp_reusable;

    self->tp_magic = pri->pri_primary->tp_magic;
871 872

    self->tp_addrinfo->ai_addr = (void *)self->tp_addr;
873 874

    self->tp_socket = socket;
875 876 877 878

    self->tp_timer = su_timer_create(su_root_task(mr->mr_root), 0);
    self->tp_stime = self->tp_ktime = self->tp_rtime = su_now();

879
    if (pri->pri_vtable->vtp_init_secondary &&
880 881
	pri->pri_vtable->vtp_init_secondary(self, socket, accepted,
					    return_reason) < 0) {
882 883 884 885 886
      if (pri->pri_vtable->vtp_deinit_secondary)
	pri->pri_vtable->vtp_deinit_secondary(self);
      su_home_zap(self->tp_home);
      return NULL;
    }
887 888 889 890 891

    /* Set IP TOS if it is set in primary */
    tport_set_tos(socket,
		  pri->pri_primary->tp_addrinfo,
		  pri->pri_params->tpp_tos);
Pekka Pessi's avatar
Pekka Pessi committed
892
  }
893 894 895 896
  else {
    su_close(socket);
    *return_reason = "malloc";
  }
Pekka Pessi's avatar
Pekka Pessi committed
897 898 899 900

  return self;
}

901

Pekka Pessi's avatar
Pekka Pessi committed
902 903 904 905 906 907 908 909
/** Create a connected transport object with socket.
 *
 * The function tport_connect() creates a secondary transport with a
 * connected socket. It registers the socket with suitable events to the
 * root.
 *
 * @param pri   primary transport object
 * @param ai    pointer to addrinfo structure
910
 * @param tpn   canonical name of node
Pekka Pessi's avatar
Pekka Pessi committed
911 912 913 914 915
 */
static
tport_t *tport_connect(tport_primary_t *pri, 
		       su_addrinfo_t *ai,
		       tp_name_t const *tpn)
916
{
917 918
  tport_t *tp;

919 920 921
  if (ai == NULL || ai->ai_addrlen > sizeof (pri->pri_primary->tp_addr))
    return NULL;

922 923
  if (pri->pri_vtable->vtp_connect)
    return pri->pri_vtable->vtp_connect(pri, ai, tpn);
924 925 926 927 928

  tp = tport_base_connect(pri, ai, ai, tpn);
  if (tp)
    tport_set_secondary_timer(tp);
  return tp;
929 930 931 932 933 934 935 936 937
}

/**Create a connected transport object with socket.
 *
 * The function tport_connect() creates a secondary transport with a
 * connected socket. It registers the socket with suitable events to the
 * root.
 *
 * @param pri   primary transport object
938 939
 * @param ai    pointer to addrinfo structure describing socket
 * @param real_ai  pointer to addrinfo structure describing real target
940 941
 * @param tpn   canonical name of node
 */
942
tport_t *tport_base_connect(tport_primary_t *pri,
943
			    su_addrinfo_t *ai,
944
			    su_addrinfo_t *real_ai,
945
			    tp_name_t const *tpn)
Pekka Pessi's avatar
Pekka Pessi committed
946 947 948 949
{
  tport_master_t *mr = pri->pri_master;
  tport_t *self = NULL;

950
  su_socket_t s, server_socket;
Pekka Pessi's avatar
Pekka Pessi committed
951
  su_wait_t wait[1] = { SU_WAIT_INIT };
952
  su_wakeup_f wakeup = tport_wakeup;
953
  int index = 0;
954
  int events = SU_WAIT_IN | SU_WAIT_ERR;
Pekka Pessi's avatar
Pekka Pessi committed
955

956 957
  int err;
  unsigned errlevel = 3;
Pekka Pessi's avatar
Pekka Pessi committed
958
  char buf[TPORT_HOSTPORTSIZE];
959
  char const *what;
Pekka Pessi's avatar
Pekka Pessi committed
960 961

  /* Log an error, return error */
962
#define TPORT_CONNECT_ERROR(errno, what)			     \
Pekka Pessi's avatar
Pekka Pessi committed
963
  return							     \
964
    ((void)(err = errno,					     \
Pekka Pessi's avatar
Pekka Pessi committed
965 966 967
	    su_wait_destroy(wait),				     \
	    (SU_LOG_LEVEL >= errlevel ?				     \
	     su_llog(tport_log, errlevel,			     \
968 969
		     "%s(%p): %s(pf=%d %s/%s): %s\n",			\
				 __func__, (void *)pri, #what, ai->ai_family,	\
Pekka Pessi's avatar
Pekka Pessi committed
970
		     tpn->tpn_proto,				     \
971 972
		     tport_hostport(buf, sizeof(buf),		     \
				    (void *)ai->ai_addr, 2),	     \
Pekka Pessi's avatar
Pekka Pessi committed
973 974 975 976 977 978
		     su_strerror(err)) : (void)0),		     \
	    tport_zap_secondary(self),				     \
	    su_seterrno(err)),					     \
     (void *)NULL)

  s = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
979
  if (s == INVALID_SOCKET)
980
    TPORT_CONNECT_ERROR(su_errno(), "socket");
Pekka Pessi's avatar
Pekka Pessi committed
981

982 983 984 985 986 987
  what = "tport_alloc_secondary";
  if ((self = tport_alloc_secondary(pri, s, 0, &what)) == NULL)
    TPORT_CONNECT_ERROR(errno, what);

  self->tp_conn_orient = 1;

988
  if ((server_socket = pri->pri_primary->tp_socket) != INVALID_SOCKET) {
Pekka Pessi's avatar
Pekka Pessi committed
989 990 991
    su_sockaddr_t susa;
    socklen_t susalen = sizeof(susa);

992 993
    /* Bind this socket to same IP address as the primary server socket */
    if (getsockname(server_socket, &susa.su_sa, &susalen) < 0) {
994 995
      SU_DEBUG_3(("%s(%p): getsockname(): %s\n", 
		  __func__, (void *)self, su_strerror(su_errno())));
Pekka Pessi's avatar
Pekka Pessi committed
996 997 998 999
    }
    else {
      susa.su_port = 0;
      if (bind(s, &susa.su_sa, susalen) < 0) {
1000 1001
	SU_DEBUG_3(("%s(%p): bind(local-ip): %s\n", 
		    __func__, (void *)self, su_strerror(su_errno())));
Pekka Pessi's avatar
Pekka Pessi committed
1002 1003 1004 1005
      }
    }
  }

1006 1007 1008
  /* Set sockname for the tport */
  if (tport_setname(self, tpn->tpn_proto, real_ai, tpn->tpn_canon) == -1) 
    TPORT_CONNECT_ERROR(su_errno(), tport_setname);
Pekka Pessi's avatar
Pekka Pessi committed
1009

1010 1011 1012 1013
  /* Try to have a non-blocking connect().
   * The su_wait_create() below makes the socket non-blocking anyway. */
  su_setblocking(s, 0);

1014
  if (connect(s, ai->ai_addr, (socklen_t)(ai->ai_addrlen)) == SOCKET_ERROR) {
Pekka Pessi's avatar
Pekka Pessi committed
1015
    err = su_errno();
1016
    if (!su_is_blocking(err))
Pekka Pessi's avatar
Pekka Pessi committed
1017
      TPORT_CONNECT_ERROR(err, connect);
1018 1019
    events = SU_WAIT_CONNECT | SU_WAIT_ERR;
    wakeup = tport_connected;
1020 1021 1022 1023
    what = "connecting";
  }
  else {
    what = "connected";
1024
    self->tp_is_connected = 1;
Pekka Pessi's avatar
Pekka Pessi committed
1025 1026
  }

1027
  if (su_wait_create(wait, s, self->tp_events = events) == -1)
Pekka Pessi's avatar
Pekka Pessi committed
1028 1029 1030
    TPORT_CONNECT_ERROR(su_errno(), su_wait_create);

  /* Register receiving function with events specified above */
1031
  if ((index = su_root_register(mr->mr_root, wait, wakeup, self, 0)) == -1)
Pekka Pessi's avatar
Pekka Pessi committed
1032 1033
    TPORT_CONNECT_ERROR(su_errno(), su_root_register);

1034
  self->tp_index = index;
Pekka Pessi's avatar
Pekka Pessi committed
1035

1036 1037
  if (ai == real_ai) {
    SU_DEBUG_5(("%s(%p): %s to " TPN_FORMAT "\n", 
1038
		__func__, (void *)self, what, TPN_ARGS(self->tp_name)));
1039 1040 1041
  }
  else {
    SU_DEBUG_5(("%s(%p): %s via %s to " TPN_FORMAT "\n",
1042
		__func__, (void *)self, what,
1043 1044 1045
		tport_hostport(buf, sizeof(buf), (void *)ai->ai_addr, 2),
		TPN_ARGS(self->tp_name)));
  }
1046 1047

  tprb_append(&pri->pri_open, self);
Pekka Pessi's avatar
Pekka Pessi committed
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060

  return self;
}

/** Destroy a secondary transport. @internal */
void tport_zap_secondary(tport_t *self)
{
  tport_master_t *mr;

  if (self == NULL)
    return;

  /* Remove from rbtree */
1061 1062 1063 1064 1065 1066 1067
  if (!tport_is_closed(self))
    tprb_remove(&self->tp_pri->pri_open, self);
  else
    tplist_remove(&self->tp_pri->pri_closed, self);

  if (self->tp_timer)
    su_timer_destroy(self->tp_timer), self->tp_timer = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
1068

1069 1070 1071
  /* Do not deinit primary as secondary! */
  if (tport_is_secondary(self) &&
      self->tp_pri->pri_vtable->vtp_deinit_secondary)
1072 1073
    self->tp_pri->pri_vtable->vtp_deinit_secondary(self);

Pekka Pessi's avatar
Pekka Pessi committed
1074 1075 1076
  if (self->tp_msg) {
    msg_destroy(self->tp_msg), self->tp_msg = NULL;
    SU_DEBUG_3(("%s(%p): zapped partially received message\n", 
1077
		__func__, (void *)self));
Pekka Pessi's avatar
Pekka Pessi committed
1078 1079
  }

1080
  if (tport_has_queued(self)) {
Pekka Pessi's avatar
Pekka Pessi committed
1081
    size_t n = 0, i, N = self->tp_params->tpp_qsize;
Pekka Pessi's avatar
Pekka Pessi committed
1082
    for (i = self->tp_qhead; self->tp_queue[i]; i = (i + 1) % N) {
Pekka Pessi's avatar
Pekka Pessi committed
1083 1084 1085 1086
      msg_destroy(self->tp_queue[i]), self->tp_queue[i] = NULL;
      n++;
    }
    SU_DEBUG_3(("%s(%p): zapped %lu queued messages\n", 
1087
		__func__, (void *)self, (LU)n));
Pekka Pessi's avatar
Pekka Pessi committed
1088 1089 1090
  }

  if (self->tp_pused) {
1091
    SU_DEBUG_3(("%s(%p): zapped while pending\n",
1092
		__func__, (void *)self));
Pekka Pessi's avatar
Pekka Pessi committed
1093 1094 1095 1096
  }

  mr = self->tp_master;

1097
#if HAVE_SOFIA_STUN
1098
  tport_stun_server_remove_socket(self);
1099
#endif
1100

Pekka Pessi's avatar
Pekka Pessi committed
1101 1102 1103
  if (self->tp_index)
    su_root_deregister(mr->mr_root, self->tp_index);
  self->tp_index = 0;
1104
  if (self->tp_socket != INVALID_SOCKET)
Pekka Pessi's avatar
Pekka Pessi committed
1105
    su_close(self->tp_socket);
1106
  self->tp_socket = INVALID_SOCKET;
Pekka Pessi's avatar
Pekka Pessi committed
1107 1108 1109 1110

  su_home_zap(self->tp_home);
}

1111 1112
/** Create a new reference to a transport object. */
tport_t *tport_ref(tport_t *tp)
Pekka Pessi's avatar
Pekka Pessi committed
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122
{
  if (tp) {
    if (tp->tp_refs >= 0)
      tp->tp_refs++;
    else if (tp->tp_refs == -1)
      tp->tp_refs = 1;
  }
  return tp;
}

1123 1124 1125
/** Destroy reference to a transport object. */
void tport_unref(tport_t *tp)
{
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
  if (tp == NULL || tp->tp_refs <= 0)
    return;
  if (--tp->tp_refs > 0)
    return;
  
  if (!tport_is_secondary(tp))
    return;

  if (tp->tp_params->tpp_idle == 0)
    tport_close(tp);

  tport_set_secondary_timer(tp);
1138 1139 1140 1141 1142 1143 1144 1145
}

/** Create a new reference to transport object. */
tport_t *tport_incref(tport_t *tp)
{
  return tport_ref(tp);
}

Pekka Pessi's avatar
Pekka Pessi committed
1146 1147 1148 1149 1150 1151
/** Destroy a transport reference. */
void tport_decref(tport_t **ttp)
{
  assert(ttp);

  if (*ttp) {
1152
    tport_unref(*ttp);
Pekka Pessi's avatar
Pekka Pessi committed
1153 1154 1155 1156 1157 1158 1159 1160
    *ttp = NULL;
  }
}

/** Get transport parameters.
 *
 * @param self          pointer to a transport object
 * @param tag,value,... list of tags
1161 1162 1163 1164 1165 1166 1167 1168
 *
 * @TAGS
 * TPTAG_MTU_REF(), TPTAG_QUEUESIZE_REF(), TPTAG_IDLE_REF(),
 * TPTAG_TIMEOUT_REF(), TPTAG_KEEPALIVE_REF(), TPTAG_PINGPONG_REF(),
 * TPTAG_PONG2PING_REF(), TPTAG_DEBUG_DROP_REF(), TPTAG_THRPSIZE_REF(),
 * TPTAG_THRPRQSIZE_REF(), TPTAG_SIGCOMP_LIFETIME_REF(),
 * TPTAG_CONNECT_REF(), TPTAG_SDWN_ERROR_REF(), TPTAG_REUSE_REF(),
 * TPTAG_STUN_SERVER_REF(), TPTAG_PUBLIC_REF() and TPTAG_TOS_REF().
Pekka Pessi's avatar
Pekka Pessi committed
1169 1170 1171 1172 1173 1174 1175
 */
int tport_get_params(tport_t const *self,
		     tag_type_t tag, tag_value_t value, ...)
{
  ta_list ta;
  int n;
  tport_params_t const *tpp;
1176
  tport_primary_t const *pri = self->tp_pri;
1177
  int connect;
Pekka Pessi's avatar
Pekka Pessi committed
1178 1179

  if (self == NULL)
1180
    return su_seterrno(EINVAL);
Pekka Pessi's avatar
Pekka Pessi committed
1181 1182 1183 1184

  tpp = self->tp_params;
  ta_start(ta, tag, value);

1185 1186 1187 1188
  connect = tpp->tpp_conn_orient 
    /* Only dgram primary is *not* connection-oriented */
    || !tport_is_primary(self) || !tport_is_dgram(self);

Pekka Pessi's avatar
Pekka Pessi committed
1189
  n = tl_tgets(ta_args(ta),
1190
	       TPTAG_MTU((usize_t)tpp->tpp_mtu),
Pekka Pessi's avatar
Pekka Pessi committed
1191
	       TPTAG_REUSE(self->tp_reusable),
1192
	       TPTAG_CONNECT(connect),
Pekka Pessi's avatar
Pekka Pessi committed
1193 1194 1195
	       TPTAG_QUEUESIZE(tpp->tpp_qsize),
	       TPTAG_IDLE(tpp->tpp_idle),
	       TPTAG_TIMEOUT(tpp->tpp_timeout),
1196 1197 1198
	       TPTAG_KEEPALIVE(tpp->tpp_keepalive),
	       TPTAG_PINGPONG(tpp->tpp_pingpong),
	       TPTAG_PONG2PING(tpp->tpp_pong2ping),
Pekka Pessi's avatar
Pekka Pessi committed
1199 1200 1201 1202
	       TPTAG_SDWN_ERROR(tpp->tpp_sdwn_error),
	       TPTAG_DEBUG_DROP(tpp->tpp_drop),
	       TPTAG_THRPSIZE(tpp->tpp_thrpsize),
	       TPTAG_THRPRQSIZE(tpp->tpp_thrprqsize),
1203 1204
	       TPTAG_SIGCOMP_LIFETIME(tpp->tpp_sigcomp_lifetime),
	       TPTAG_STUN_SERVER(tpp->tpp_stun_server),
1205
	       TAG_IF(pri, TPTAG_PUBLIC(pri ? pri->pri_public : 0)),
1206
	       TPTAG_TOS(tpp->tpp_tos),
Pekka Pessi's avatar
Pekka Pessi committed
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217
	       TAG_END());

  ta_end(ta);

  return n;
}

/** Set transport parameters.
 *
 * @param self          pointer to a transport object
 * @param tag,value,... list of tags
1218 1219 1220
 *
 * @TAGS
 * TPTAG_MTU(), TPTAG_QUEUESIZE(), TPTAG_IDLE(), TPTAG_TIMEOUT(),
1221
 * TPTAG_KEEPALIVE(), TPTAG_PINGPONG(), TPTAG_PONG2PING(),
1222 1223 1224
 * TPTAG_DEBUG_DROP(), TPTAG_THRPSIZE(), TPTAG_THRPRQSIZE(),
 * TPTAG_SIGCOMP_LIFETIME(), TPTAG_CONNECT(), TPTAG_SDWN_ERROR(),
 * TPTAG_REUSE(), TPTAG_STUN_SERVER(), and TPTAG_TOS().
Pekka Pessi's avatar
Pekka Pessi committed
1225 1226 1227 1228 1229 1230 1231
 */
int tport_set_params(tport_t *self,
		     tag_type_t tag, tag_value_t value, ...)
{
  ta_list ta;
  int n;
  tport_params_t tpp[1], *tpp0;
1232 1233
  
  usize_t mtu;
1234
  int connect, sdwn_error, reusable, stun_server, pong2ping;
1235
  
Pekka Pessi's avatar
Pekka Pessi committed
1236
  if (self == NULL)
1237
    return su_seterrno(EINVAL);
Pekka Pessi's avatar
Pekka Pessi committed
1238

1239
  memcpy(tpp, tpp0 = self->tp_params, sizeof tpp);
Pekka Pessi's avatar
Pekka Pessi committed
1240

1241
  mtu = tpp->tpp_mtu;
Pekka Pessi's avatar
Pekka Pessi committed
1242 1243 1244
  connect = tpp->tpp_conn_orient;
  sdwn_error = tpp->tpp_sdwn_error;
  reusable = self->tp_reusable;
1245
  stun_server = tpp->tpp_stun_server;
1246
  pong2ping = tpp->tpp_pong2ping;
Pekka Pessi's avatar
Pekka Pessi committed
1247 1248 1249 1250

  ta_start(ta, tag, value);

  n = tl_gets(ta_args(ta),
1251
	      TPTAG_MTU_REF(mtu),
Pekka Pessi's avatar
Pekka Pessi committed
1252 1253 1254
	      TAG_IF(!self->tp_queue, TPTAG_QUEUESIZE_REF(tpp->tpp_qsize)),
	      TPTAG_IDLE_REF(tpp->tpp_idle),
	      TPTAG_TIMEOUT_REF(tpp->tpp_timeout),
1255 1256 1257
	      TPTAG_KEEPALIVE_REF(tpp->tpp_keepalive),
	      TPTAG_PINGPONG_REF(tpp->tpp_pingpong),
	      TPTAG_PONG2PING_REF(pong2ping),
Pekka Pessi's avatar
Pekka Pessi committed
1258 1259 1260 1261 1262 1263 1264
	      TPTAG_DEBUG_DROP_REF(tpp->tpp_drop),
	      TPTAG_THRPSIZE_REF(tpp->tpp_thrpsize),
	      TPTAG_THRPRQSIZE_REF(tpp->tpp_thrprqsize),
	      TPTAG_SIGCOMP_LIFETIME_REF(tpp->tpp_sigcomp_lifetime),
	      TPTAG_CONNECT_REF(connect),
	      TPTAG_SDWN_ERROR_REF(sdwn_error),
	      TPTAG_REUSE_REF(reusable),
1265
	      TPTAG_STUN_SERVER_REF(stun_server),
1266
	      TPTAG_TOS_REF(tpp->tpp_tos),
Pekka Pessi's avatar
Pekka Pessi committed
1267 1268 1269 1270 1271 1272 1273
	      TAG_END());

  ta_end(ta);

  if (n == 0)
    return 0;

1274 1275 1276 1277
  if (tpp->tpp_idle > 0 && tpp->tpp_idle < 100)
    tpp->tpp_idle = 100;
  if (tpp->tpp_timeout < 100)
    tpp->tpp_timeout = 100;
Pekka Pessi's avatar
Pekka Pessi committed
1278 1279 1280 1281 1282 1283 1284 1285 1286
  if (tpp->tpp_drop > 1000)
    tpp->tpp_drop = 1000;
  if (tpp->tpp_thrprqsize > 0)
    tpp->tpp_thrprqsize = tpp0->tpp_thrprqsize;
  if (tpp->tpp_sigcomp_lifetime != 0 && tpp->tpp_sigcomp_lifetime < 30)
    tpp->tpp_sigcomp_lifetime = 30;
  if (tpp->tpp_qsize >= 1000)
    tpp->tpp_qsize = 1000;

1287 1288 1289
  if (mtu > UINT_MAX)
    mtu = UINT_MAX;
  tpp->tpp_mtu = (unsigned)mtu;
Pekka Pessi's avatar
Pekka Pessi committed
1290
  /* Currently only primary UDP transport can *not* be connection oriented */ 
1291
  tpp->tpp_conn_orient = connect; 
1292 1293 1294
  tpp->tpp_sdwn_error = sdwn_error;
  self->tp_reusable = reusable;
  tpp->tpp_stun_server = stun_server;
1295 1296 1297 1298
  tpp->tpp_pong2ping = pong2ping;

  if (memcmp(tpp0, tpp, sizeof tpp) == 0)
    return n;
Pekka Pessi's avatar
Pekka Pessi committed
1299 1300 1301 1302

  if (tport_is_secondary(self) && 
      self->tp_params == self->tp_pri->pri_primary->tp_params) {
    tpp0 = su_zalloc(self->tp_home, sizeof *tpp0); if (!tpp0) return -1;
1303
    self->tp_params = tpp0;
Pekka Pessi's avatar
Pekka Pessi committed
1304 1305
  }

1306
  memcpy(tpp0, tpp, sizeof tpp);
Pekka Pessi's avatar
Pekka Pessi committed
1307

1308 1309 1310
  if (tport_is_secondary(self))
    tport_set_secondary_timer(self);

Pekka Pessi's avatar
Pekka Pessi committed
1311 1312 1313
  return n;
}

1314 1315 1316 1317 1318 1319 1320 1321 1322 1323
extern tport_vtable_t const tport_udp_vtable;
extern tport_vtable_t const tport_tcp_vtable;
extern tport_vtable_t const tport_tls_vtable;
extern tport_vtable_t const tport_sctp_vtable;
extern tport_vtable_t const tport_udp_client_vtable;
extern tport_vtable_t const tport_tcp_client_vtable;
extern tport_vtable_t const tport_sctp_client_vtable;
extern tport_vtable_t const tport_tls_client_vtable;
extern tport_vtable_t const tport_http_connect_vtable;
extern tport_vtable_t const tport_threadpool_vtable;
1324 1325 1326 1327 1328

#define TPORT_NUMBER_OF_TYPES 64

tport_vtable_t const *tport_vtables[TPORT_NUMBER_OF_TYPES + 1] =
{
1329
#if HAVE_SOFIA_NTH
1330
  &tport_http_connect_vtable,
1331
#endif
1332
#if HAVE_TLS
1333 1334
  &tport_tls_client_vtable,
  &tport_tls_vtable,
1335
#endif
1336
#if HAVE_SCTP		/* SCTP is broken */
1337 1338
  &tport_sctp_client_vtable,
  &tport_sctp_vtable,
1339
#endif
1340 1341 1342 1343
  &tport_tcp_client_vtable,
  &tport_tcp_vtable,
  &tport_udp_client_vtable,
  &tport_udp_vtable,
1344
#if 0
1345
  &tport_threadpool_vtable,
1346
#endif
1347
#if HAVE_SOFIA_STUN
Pekka Pessi's avatar
Pekka Pessi committed
1348
  &tport_stun_vtable,
1349
#endif
1350 1351
};

1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367
/** Register new transport vtable */
int tport_register_type(tport_vtable_t const *vtp)
{
  int i;

  for (i = TPORT_NUMBER_OF_TYPES; i >= 0; i--) {
    if (tport_vtables[i] == NULL) {
      tport_vtables[i] = vtp;
      return 0;
    }
  }

  su_seterrno(ENOMEM);
  return -1;
}

1368 1369 1370
/**Get a vtable for given protocol */
tport_vtable_t const *tport_vtable_by_name(char const *protoname,
					   enum tport_via public) 
Pekka Pessi's avatar
Pekka Pessi committed
1371
{
1372 1373 1374 1375
  int i;

  for (i = TPORT_NUMBER_OF_TYPES; i >= 0; i--) {
    tport_vtable_t const *vtable = tport_vtables[i];
1376

1377 1378
    if (vtable == NULL)
      continue;
1379 1380 1381 1382
    if (vtable->vtp_public != public)
      continue;
    if (strcasecmp(vtable->vtp_name, protoname))
      continue;
Pekka Pessi's avatar
Pekka Pessi committed
1383 1384 1385 1386

    assert(vtable->vtp_pri_size >= sizeof (tport_primary_t));
    assert(vtable->vtp_secondary_size >= sizeof (tport_t));
    
1387 1388 1389 1390
    return vtable;
  }

  return NULL;
Pekka Pessi's avatar
Pekka Pessi committed
1391 1392
}

1393 1394 1395 1396 1397 1398
#if 0
tport_set_f const *tport_set_methods[TPORT_NUMBER_OF_TYPES + 1] = 
  {
    tport_server_bind_set,
    tport_client_bind_set,
    tport_threadpool_set,
1399
#if HAVE_SOFIA_NTH
1400
    tport_http_connect_set,
1401
#endif
1402 1403 1404 1405 1406
#if HAVE_TLS
    tport_tls_set,
#endif
    NULL
  };
1407

1408 1409 1410 1411 1412 1413
int tport_bind_set(tport_master_t *mr, 
		   tp_name_t const *tpn,
		   char const * const transports[],
		   tagi_t const *taglist,