tport.c 119 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
#include <sofia-sip/su_string.h>
41
#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
{
  if (a == b)
    return 0;
111

Pekka Pessi's avatar
Pekka Pessi committed
112
  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
  return
    self &&
Pekka Pessi's avatar
Pekka Pessi committed
176 177 178 179
    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
  return
    self &&
Pekka Pessi's avatar
Pekka Pessi committed
184 185 186 187
    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
  return
    self &&
    self->tp_master->mr_master != self &&
Pekka Pessi's avatar
Pekka Pessi committed
193 194 195
    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
  return self && self->tp_index != 0;
Pekka Pessi's avatar
Pekka Pessi committed
200 201 202
}

/** 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 && self->tp_addrinfo->ai_socktype == SOCK_STREAM;
Pekka Pessi's avatar
Pekka Pessi committed
206
}
207

Pekka Pessi's avatar
Pekka Pessi committed
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 && self->tp_addrinfo->ai_socktype == SOCK_DGRAM;
Pekka Pessi's avatar
Pekka Pessi committed
212
}
213

Pekka Pessi's avatar
Pekka Pessi committed
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 && self->tp_addrinfo->ai_protocol == IPPROTO_UDP;
Pekka Pessi's avatar
Pekka Pessi committed
218
}
219

Pekka Pessi's avatar
Pekka Pessi committed
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 && self->tp_addrinfo->ai_protocol == IPPROTO_TCP;
Pekka Pessi's avatar
Pekka Pessi committed
224
}
225

Pekka Pessi's avatar
Pekka Pessi committed
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 233
  return self != NULL &&
    (self->tp_addrinfo->ai_socktype == SOCK_STREAM ||
234
     self->tp_addrinfo->ai_socktype == SOCK_SEQPACKET);
Pekka Pessi's avatar
Pekka Pessi committed
235 236
}

237 238
/** Return 0 if self is local, nonzero otherwise.
 *
239
 * The return valu is the tport_via enum.
240
 *
241
 * @sa TPTAG_PUBLIC(), enum tport_via.
242
 */
243
int tport_is_public(tport_t const *self)
244
{
245
  return self && 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 252
  return self &&
    (self->tp_addrinfo->ai_family == 0 ||
253
     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 262
  return self &&
    (self->tp_addrinfo->ai_family == 0 ||
263
     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
  return self && self->tp_pri->pri_has_tls;
271
}
272

273 274 275
/** Return true if transport certificate verified successfully */
int tport_is_verified(tport_t const *self)
{
276
  return tport_has_tls(self) && self->tp_is_connected && self->tp_verified;
Pekka Pessi's avatar
Pekka Pessi committed
277 278
}

279 280 281
/** Return true if transport is being updated. */
int tport_is_updating(tport_t const *self)
{
282 283 284
  tport_primary_t *pri;

  if (tport_is_master(self)) {
285
    for (pri = self->tp_master->mr_primaries; pri; pri = pri->pri_next)
286 287 288 289 290 291 292 293
      if (pri->pri_updating)
	return 1;
  }
  else if (tport_is_primary(self)) {
    return self->tp_pri->pri_updating;
  }

  return 0;
294 295
}

296 297 298 299
/** Test if transport has been closed.
 *
 * @since New in @VERSION_1_12_4
 */
300
inline int tport_is_closed(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
301 302 303 304
{
  return self->tp_closed;
}

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

/** Test if transport is bound */
315
su_inline int tport_is_bound(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
316 317 318 319
{
  return self->tp_protoname != NULL;
}

320
/** Test if transport connection has been established. @NEW_1_12_5. */
321
int tport_is_connected(tport_t const *self)
322 323 324 325
{
  return self->tp_is_connected;
}

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

339
/** Return true if transport has message in send queue. @NEW_1_12_7. */
340 341 342 343 344
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
345
/** MTU for transport  */
346
su_inline unsigned tport_mtu(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
347 348 349 350
{
  return self->tp_params->tpp_mtu;
}

351
su_inline
Pekka Pessi's avatar
Pekka Pessi committed
352 353 354 355 356
int tport_has_sigcomp(tport_t const *self)
{
  return self->tp_name->tpn_comp != NULL;
}

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

368 369
static
tport_t *tport_connect(tport_primary_t *pri, su_addrinfo_t *ai,
Pekka Pessi's avatar
Pekka Pessi committed
370 371
		       tp_name_t const *tpn);

372
static int bind6only_check(tport_master_t *mr);
373 374 375 376 377

static
int tport_server_addrinfo(tport_master_t *mr,
			  char const *canon,
			  int family,
378
			  char const *host,
379 380 381 382 383
			  char const *service,
			  char const *protocol,
			  char const * const transports[],
			  su_addrinfo_t **res);

384
static int tport_get_local_addrinfo(tport_master_t *mr,
Pekka Pessi's avatar
Pekka Pessi committed
385 386 387 388
				    char const *port,
				    su_addrinfo_t const *hints,
				    su_addrinfo_t **return_ai);

389 390 391 392 393
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
394

Pekka Pessi's avatar
Pekka Pessi committed
395 396 397 398
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
399
static int
400 401 402 403 404 405
  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
406

407 408
  tport_wakeup_pri(su_root_magic_t *m, su_wait_t *w, tport_t *self),
  tport_base_wakeup(tport_t *self, int events),
409
  tport_connected(su_root_magic_t *m, su_wait_t *w, tport_t *self),
Pekka Pessi's avatar
Pekka Pessi committed
410
  tport_resolve(tport_t *self, msg_t *msg, tp_name_t const *tpn),
411 412
  tport_send_error(tport_t *, msg_t *, tp_name_t const *, char const *who),
  tport_send_fatal(tport_t *, msg_t *, tp_name_t const *, char const *who),
Pekka Pessi's avatar
Pekka Pessi committed
413
  tport_queue(tport_t *self, msg_t *msg),
414
  tport_queue_rest(tport_t *self, msg_t *msg, msg_iovec_t iov[], size_t iovused),
Pekka Pessi's avatar
Pekka Pessi committed
415
  tport_pending_error(tport_t *self, su_sockaddr_t const *dst, int error),
416
  tport_pending_errmsg(tport_t *self, msg_t *msg, int error);
Pekka Pessi's avatar
Pekka Pessi committed
417

418 419 420 421
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
422 423 424 425 426 427 428
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
429

430 431
static tport_primary_t *tport_alloc_primary(tport_master_t *mr,
					    tport_vtable_t const *vtable,
432
					    tp_name_t tpn[1],
433
					    su_addrinfo_t *ai,
434 435 436 437 438
					    tagi_t const *tags,
					    char const **return_culprit);

static tport_primary_t *tport_listen(tport_master_t *mr,
				     tport_vtable_t const *vtable,
439
				     tp_name_t tpn[1],
440
				     su_addrinfo_t *ai,
Pekka Pessi's avatar
Pekka Pessi committed
441 442 443
				     tagi_t *tags);
static void tport_zap_primary(tport_primary_t *);

444
static char *localipname(int pf, char *buf, size_t bufsiz);
Pekka Pessi's avatar
Pekka Pessi committed
445 446 447 448 449 450
static int getprotohints(su_addrinfo_t *hints,
			 char const *proto, int flags);


/* Stack class used when transports are being destroyed */
static
451 452
void tport_destroy_recv(tp_stack_t *stack, tport_t *tp,
			msg_t *msg, tp_magic_t *magic,
Pekka Pessi's avatar
Pekka Pessi committed
453 454 455 456 457 458
			su_time_t received)
{
  msg_destroy(msg);
}

static
459
void tport_destroy_error(tp_stack_t *stack, tport_t *tp,
Pekka Pessi's avatar
Pekka Pessi committed
460 461 462 463 464
			 int errcode, char const *remote)
{
}

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

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

476 477 478
/** Create the master transport.
 *
 * Master transport object is used to bind the protocol using transport with
479
 * actual transport objects corresponding to TCP, UDP, etc.
480 481
 *
 * @sa tport_tbind()
482 483 484 485 486
 *
 * @TAGS
 * TPTAG_LOG(), TPTAG_DUMP(), tags used with tport_set_params(), especially
 * TPTAG_QUEUESIZE().
 */
Pekka Pessi's avatar
Pekka Pessi committed
487 488
tport_t *tport_tcreate(tp_stack_t *stack,
		       tp_stack_class_t const *tpac,
489
		       su_root_t *root,
Pekka Pessi's avatar
Pekka Pessi committed
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
		       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;

506
  SU_DEBUG_7(("%s(): %p\n", "tport_create", (void *)mr));
Pekka Pessi's avatar
Pekka Pessi committed
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522

  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;
523 524 525
  tpp->tpp_keepalive = 0;
  tpp->tpp_pingpong = 0;
  tpp->tpp_pong2ping = 0;
526
  tpp->tpp_stun_server = 1;
527
  tpp->tpp_tos = -1;                  /* set invalid, valid values are 0-255 */
Pekka Pessi's avatar
Pekka Pessi committed
528 529 530 531 532 533 534 535 536 537 538

  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));

539
#if HAVE_SOFIA_STUN
540
  tport_init_stun_server(mr, ta_args(ta));
541
#endif
542

Pekka Pessi's avatar
Pekka Pessi committed
543 544 545 546 547 548 549 550 551 552
  ta_end(ta);

  return mr->mr_master;
}

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

553
  static tp_stack_class_t tport_destroy_tpac[1] =
Pekka Pessi's avatar
Pekka Pessi committed
554 555 556 557 558
    {{
	sizeof tport_destroy_tpac,
	/* tpac_recv */ tport_destroy_recv,
	/* tpac_error */ tport_destroy_error,
	/* tpac_alloc */ tport_destroy_alloc,
559
	/* tpac_address */ NULL
Pekka Pessi's avatar
Pekka Pessi committed
560 561
      }};

562
  SU_DEBUG_7(("%s(%p)\n", __func__, (void *)self));
Pekka Pessi's avatar
Pekka Pessi committed
563 564 565

  if (self == NULL)
    return;
566

Pekka Pessi's avatar
Pekka Pessi committed
567 568 569 570 571 572 573 574 575 576
  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);

577
#if HAVE_SOFIA_STUN
578
  tport_deinit_stun_server(mr);
579
#endif
Pekka Pessi's avatar
Pekka Pessi committed
580 581 582 583 584 585 586 587 588 589

  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);
}

590

Pekka Pessi's avatar
Pekka Pessi committed
591
/** Allocate a primary transport */
592
static
593 594
tport_primary_t *tport_alloc_primary(tport_master_t *mr,
				     tport_vtable_t const *vtable,
595
				     tp_name_t tpn[1],
596 597 598
				     su_addrinfo_t *ai,
				     tagi_t const *tags,
				     char const **return_culprit)
Pekka Pessi's avatar
Pekka Pessi committed
599 600
{
  tport_primary_t *pri, **next;
601
  tport_t *tp;
602
  int save_errno;
Pekka Pessi's avatar
Pekka Pessi committed
603 604 605

  for (next = &mr->mr_primaries; *next; next = &(*next)->pri_next)
    ;
606

607
  assert(vtable->vtp_pri_size >= sizeof *pri);
Pekka Pessi's avatar
Pekka Pessi committed
608

609
  if ((pri = su_home_clone(mr->mr_home, vtable->vtp_pri_size))) {
Pekka Pessi's avatar
Pekka Pessi committed
610
    tport_t *tp = pri->pri_primary;
611 612 613
    pri->pri_vtable = vtable;
    pri->pri_public = vtable->vtp_public;

Pekka Pessi's avatar
Pekka Pessi committed
614 615
    tp->tp_master = mr;
    tp->tp_pri = pri;
616
    tp->tp_socket = INVALID_SOCKET;
Pekka Pessi's avatar
Pekka Pessi committed
617 618 619 620 621 622 623

    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
624
    if (!pri->pri_public)
625
      tp->tp_addrinfo->ai_addr = &tp->tp_addr->su_sa;
626

627 628
    SU_DEBUG_5(("%s(%p): new primary tport %p\n", __func__, (void *)mr,
		(void *)pri));
Pekka Pessi's avatar
Pekka Pessi committed
629 630
  }

631
  *next = pri;
632
  tp = pri->pri_primary;
633

634 635 636 637
  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
638 639
  else if (vtable->vtp_init_primary &&
	   vtable->vtp_init_primary(pri, tpn, ai, tags, return_culprit) < 0)
640
    ;
641
  else if (tport_setname(tp, vtable->vtp_name, ai, tpn->tpn_canon) == -1)
642
    *return_culprit = "tport_setname";
643
  else if (tpn->tpn_ident &&
644 645 646 647
	   !(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
648

649
  save_errno = su_errno();
650
  tport_zap_primary(pri);
651
  su_seterrno(save_errno);
Pekka Pessi's avatar
Pekka Pessi committed
652

653 654
  return NULL;
}
655

656 657

/** Destroy a primary transport and its secondary transports. @internal */
658
static
659 660 661 662 663 664 665 666 667 668 669 670
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);

671 672 673 674
  while (pri->pri_open)
    tport_zap_secondary(pri->pri_open);
  while (pri->pri_closed)
    tport_zap_secondary(pri->pri_closed);
675 676 677 678 679 680 681 682 683

  /* 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;

684
  tport_zap_secondary((tport_t *)pri);
685
}
686

Pekka Pessi's avatar
Pekka Pessi committed
687 688 689 690 691 692
/**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
693
 * @param ai    pointer to addrinfo structure
Pekka Pessi's avatar
Pekka Pessi committed
694 695 696 697
 * @param canon canonical name of node
 * @param protoname name of the protocol
 */
static
698 699
tport_primary_t *tport_listen(tport_master_t *mr,
			      tport_vtable_t const *vtable,
700
			      tp_name_t tpn[1],
701
			      su_addrinfo_t *ai,
Pekka Pessi's avatar
Pekka Pessi committed
702 703 704 705 706 707 708 709
			      tagi_t *tags)
{
  tport_primary_t *pri = NULL;

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

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

731
  /* Create a primary transport object for another transport. */
Pekka Pessi's avatar
Pekka Pessi committed
732
  pri = tport_alloc_primary(mr, vtable, tpn, ai, tags, &culprit);
733
  if (pri == NULL)
734
    return TPORT_LISTEN_ERROR(su_errno(), culprit);
Pekka Pessi's avatar
Pekka Pessi committed
735

736
  if (pri->pri_primary->tp_socket != INVALID_SOCKET) {
Pekka Pessi's avatar
Pekka Pessi committed
737 738 739 740 741 742 743 744
    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 */
745
    index = su_root_register(mr->mr_root, wait, tport_wakeup_pri, tp, 0);
Pekka Pessi's avatar
Pekka Pessi committed
746 747 748 749
    if (index == -1) {
      su_wait_destroy(wait);
      return TPORT_LISTEN_ERROR(su_errno(), "su_root_register");
    }
Pekka Pessi's avatar
Pekka Pessi committed
750

Pekka Pessi's avatar
Pekka Pessi committed
751 752
    tp->tp_index = index;
  }
Pekka Pessi's avatar
Pekka Pessi committed
753

754
  pri->pri_primary->tp_has_connection = 0;
755

756
  SU_DEBUG_5(("%s(%p): %s " TPN_FORMAT "\n",
757
	      __func__, (void *)pri, "listening at",
Pekka Pessi's avatar
Pekka Pessi committed
758 759 760 761 762
	      TPN_ARGS(pri->pri_primary->tp_name)));

  return pri;
}

Pekka Pessi's avatar
Pekka Pessi committed
763 764 765
int tport_bind_socket(int socket,
		      su_addrinfo_t *ai,
		      char const **return_culprit)
766
{
767
  su_sockaddr_t *su = (su_sockaddr_t *)ai->ai_addr;
768
  socklen_t sulen = (socklen_t)(ai->ai_addrlen);
769

770
  if (bind(socket, ai->ai_addr, sulen) == -1) {
771
    return *return_culprit = "bind", -1;
Pekka Pessi's avatar
Pekka Pessi committed
772
  }
773

774
  if (getsockname(socket, &su->su_sa, &sulen) == SOCKET_ERROR) {
775
    return *return_culprit = "getsockname", -1;
Pekka Pessi's avatar
Pekka Pessi committed
776
  }
777

778 779
  ai->ai_addrlen = sulen;

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];
786

787
      memcpy(su0, su, sizeof su0);
788

789 790 791
      memset(su, 0, ai->ai_addrlen = sizeof su->su_sin);
      su->su_family = ai->ai_family = AF_INET;
      su->su_port = su0->su_port;
792

793
#ifndef IN6_V4MAPPED_TO_INADDR
794
#define IN6_V4MAPPED_TO_INADDR(in6, in4) \
795 796 797 798 799 800 801
      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

802
  return 0;
803 804
}

805 806 807 808 809 810 811 812 813 814 815

/** 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);
}


816 817
static
int tport_set_events(tport_t *self, int set, int clear)
Pekka Pessi's avatar
Pekka Pessi committed
818
{
819
  int events;
Pekka Pessi's avatar
Pekka Pessi committed
820

821 822
  if (self == NULL)
    return -1;
Pekka Pessi's avatar
Pekka Pessi committed
823

824 825
  events = (self->tp_events | set) & ~clear;
  self->tp_events = events;
Pekka Pessi's avatar
Pekka Pessi committed
826

827 828
  if (self->tp_pri->pri_vtable->vtp_set_events)
    return self->tp_pri->pri_vtable->vtp_set_events(self);
829

830
  SU_DEBUG_7(("tport_set_events(%p): events%s%s%s\n", (void *)self,
831 832 833 834
	      (events & SU_WAIT_IN) ? " IN" : "",
	      (events & SU_WAIT_OUT) ? " OUT" : "",
	      SU_WAIT_CONNECT != SU_WAIT_OUT &&
	      (events & SU_WAIT_CONNECT) ? " CONNECT" : ""));
835 836 837 838 839

  return
    su_root_eventmask(self->tp_master->mr_root,
		      self->tp_index,
		      self->tp_socket,
840
		      self->tp_events = events);
Pekka Pessi's avatar
Pekka Pessi committed
841 842 843 844
}

/**Allocate a secondary transport. @internal
 *
845 846
 * Create a secondary transport object. The new transport initally shares
 * parameters structure with the original transport.
Pekka Pessi's avatar
Pekka Pessi committed
847
 *
848 849 850
 * @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
851 852
 *
 * @return
853 854 855
 * 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
856
 */
857
tport_t *tport_alloc_secondary(tport_primary_t *pri,
858 859 860
			       int socket,
			       int accepted,
			       char const **return_reason)
Pekka Pessi's avatar
Pekka Pessi committed
861 862 863 864
{
  tport_master_t *mr = pri->pri_master;
  tport_t *self;

Pekka Pessi's avatar
Pekka Pessi committed
865
  self = su_home_clone(mr->mr_home, pri->pri_vtable->vtp_secondary_size);
Pekka Pessi's avatar
Pekka Pessi committed
866 867

  if (self) {
868
    SU_DEBUG_7(("%s(%p): new secondary tport %p\n",
869
		__func__, (void *)pri, (void *)self));
Pekka Pessi's avatar
Pekka Pessi committed
870 871 872 873 874

    self->tp_refs = -1;			/* Freshly allocated  */
    self->tp_master = mr;
    self->tp_pri = pri;
    self->tp_params = pri->pri_params;
875
    self->tp_accepted = accepted != 0;
Pekka Pessi's avatar
Pekka Pessi committed
876 877 878
    self->tp_reusable = pri->pri_primary->tp_reusable;

    self->tp_magic = pri->pri_primary->tp_magic;
879 880

    self->tp_addrinfo->ai_addr = (void *)self->tp_addr;
881 882

    self->tp_socket = socket;
883 884 885 886

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

887
    if (pri->pri_vtable->vtp_init_secondary &&
888 889
	pri->pri_vtable->vtp_init_secondary(self, socket, accepted,
					    return_reason) < 0) {
890 891 892 893 894
      if (pri->pri_vtable->vtp_deinit_secondary)
	pri->pri_vtable->vtp_deinit_secondary(self);
      su_home_zap(self->tp_home);
      return NULL;
    }
895 896 897 898 899

    /* 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
900
  }
901 902 903 904
  else {
    su_close(socket);
    *return_reason = "malloc";
  }
Pekka Pessi's avatar
Pekka Pessi committed
905 906 907 908

  return self;
}

909

Pekka Pessi's avatar
Pekka Pessi committed
910 911 912 913 914 915 916 917
/** 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
918
 * @param tpn   canonical name of node
Pekka Pessi's avatar
Pekka Pessi committed
919 920
 */
static
921
tport_t *tport_connect(tport_primary_t *pri,
Pekka Pessi's avatar
Pekka Pessi committed
922 923
		       su_addrinfo_t *ai,
		       tp_name_t const *tpn)
924
{
925 926
  tport_t *tp;

927 928 929
  if (ai == NULL || ai->ai_addrlen > sizeof (pri->pri_primary->tp_addr))
    return NULL;

930 931
  if (pri->pri_vtable->vtp_connect)
    return pri->pri_vtable->vtp_connect(pri, ai, tpn);
932 933 934 935 936

  tp = tport_base_connect(pri, ai, ai, tpn);
  if (tp)
    tport_set_secondary_timer(tp);
  return tp;
937 938 939 940 941 942 943 944 945
}

/**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
946 947
 * @param ai    pointer to addrinfo structure describing socket
 * @param real_ai  pointer to addrinfo structure describing real target
948 949
 * @param tpn   canonical name of node
 */
950
tport_t *tport_base_connect(tport_primary_t *pri,
951
			    su_addrinfo_t *ai,
952
			    su_addrinfo_t *real_ai,
953
			    tp_name_t const *tpn)
Pekka Pessi's avatar
Pekka Pessi committed
954 955 956
{
  tport_t *self = NULL;

957
  su_socket_t s, server_socket;
958
  su_wakeup_f wakeup = tport_wakeup;
959
  int events = SU_WAIT_IN | SU_WAIT_ERR;
Pekka Pessi's avatar
Pekka Pessi committed
960

961 962
  int err;
  unsigned errlevel = 3;
Pekka Pessi's avatar
Pekka Pessi committed
963
  char buf[TPORT_HOSTPORTSIZE];
964
  char const *what;
Pekka Pessi's avatar
Pekka Pessi committed
965 966

  /* Log an error, return error */
967
#define TPORT_CONNECT_ERROR(errno, what)			     \
Pekka Pessi's avatar
Pekka Pessi committed
968
  return							     \
969
    ((void)(err = errno,					     \
Pekka Pessi's avatar
Pekka Pessi committed
970 971
	    (SU_LOG_LEVEL >= errlevel ?				     \
	     su_llog(tport_log, errlevel,			     \
972 973
		     "%s(%p): %s(pf=%d %s/%s): %s\n",			\
				 __func__, (void *)pri, #what, ai->ai_family,	\
Pekka Pessi's avatar
Pekka Pessi committed
974
		     tpn->tpn_proto,				     \
975 976
		     tport_hostport(buf, sizeof(buf),		     \
				    (void *)ai->ai_addr, 2),	     \
Pekka Pessi's avatar
Pekka Pessi committed
977 978 979 980 981 982
		     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);
983
  if (s == INVALID_SOCKET)
984
    TPORT_CONNECT_ERROR(su_errno(), "socket");
Pekka Pessi's avatar
Pekka Pessi committed
985

986 987
  what = "tport_alloc_secondary";
  if ((self = tport_alloc_secondary(pri, s, 0, &what)) == NULL)
988
    TPORT_CONNECT_ERROR(su_errno(), what);
989 990 991

  self->tp_conn_orient = 1;

992
  if ((server_socket = pri->pri_primary->tp_socket) != INVALID_SOCKET) {
Pekka Pessi's avatar
Pekka Pessi committed
993 994 995
    su_sockaddr_t susa;
    socklen_t susalen = sizeof(susa);

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

1010
  /* Set sockname for the tport */
1011
  if (tport_setname(self, tpn->tpn_proto, real_ai, tpn->tpn_canon) == -1)
1012
    TPORT_CONNECT_ERROR(su_errno(), tport_setname);
Pekka Pessi's avatar
Pekka Pessi committed
1013

1014
  /* Try to have a non-blocking connect().
1015
   * The tport_register_secondary() below makes the socket non-blocking anyway. */
1016 1017
  su_setblocking(s, 0);

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

1031 1032
  if (tport_register_secondary(self, wakeup, events) == -1)
    TPORT_CONNECT_ERROR(su_errno(), tport_register_secondary);
Pekka Pessi's avatar
Pekka Pessi committed
1033

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

Pekka Pessi's avatar
Pekka Pessi committed
1045 1046 1047
  return self;
}

1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
/** Register a new secondary transport. @internal */
int tport_register_secondary(tport_t *self, su_wakeup_f wakeup, int events)
{
  int i;
  su_root_t *root = tport_is_secondary(self) ? self->tp_master->mr_root : NULL;
  su_wait_t wait[1] = { SU_WAIT_INIT };

  if (root != NULL
      /* Create wait object with appropriate events. */
      &&
      su_wait_create(wait, self->tp_socket, events) != -1
      /* Register socket to root */
      &&
      (i = su_root_register(root, wait, wakeup, self, 0)) != -1) {

    self->tp_index = i;
    self->tp_events = events;

    tprb_append(&self->tp_pri->pri_open, self);
    return 0;
  }

  su_wait_destroy(wait);
  return -1;
}

Pekka Pessi's avatar
Pekka Pessi committed
1074 1075 1076 1077 1078 1079 1080 1081 1082
/** Destroy a secondary transport. @internal */
void tport_zap_secondary(tport_t *self)
{
  tport_master_t *mr;

  if (self == NULL)
    return;

  /* Remove from rbtree */
1083 1084 1085 1086 1087 1088 1089
  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
1090

1091 1092 1093
  /* Do not deinit primary as secondary! */
  if (tport_is_secondary(self) &&
      self->tp_pri->pri_vtable->vtp_deinit_secondary)
1094 1095
    self->tp_pri->pri_vtable->vtp_deinit_secondary(self);

Pekka Pessi's avatar
Pekka Pessi committed
1096 1097
  if (self->tp_msg) {
    msg_destroy(self->tp_msg), self->tp_msg = NULL;
1098
    SU_DEBUG_3(("%s(%p): zapped partially received message\n",
1099
		__func__, (void *)self));
Pekka Pessi's avatar
Pekka Pessi committed
1100 1101
  }

1102
  if (tport_has_queued(self)) {
Pekka Pessi's avatar
Pekka Pessi committed
1103
    size_t n = 0, i, N = self->tp_params->tpp_qsize;
Pekka Pessi's avatar
Pekka Pessi committed
1104
    for (i = self->tp_qhead; self->tp_queue[i]; i = (i + 1) % N) {
Pekka Pessi's avatar
Pekka Pessi committed
1105 1106 1107
      msg_destroy(self->tp_queue[i]), self->tp_queue[i] = NULL;
      n++;
    }
1108
    SU_DEBUG_3(("%s(%p): zapped %lu queued messages\n",
1109
		__func__, (void *)self, (LU)n));
Pekka Pessi's avatar
Pekka Pessi committed
1110 1111 1112
  }

  if (self->tp_pused) {
1113
    SU_DEBUG_3(("%s(%p): zapped while pending\n",
1114
		__func__, (void *)self));
Pekka Pessi's avatar
Pekka Pessi committed
1115 1116 1117 1118
  }

  mr = self->tp_master;

1119
#if HAVE_SOFIA_STUN
1120
  tport_stun_server_remove_socket(self);
1121
#endif
1122

Pekka Pessi's avatar
Pekka Pessi committed
1123 1124 1125
  if (self->tp_index)
    su_root_deregister(mr->mr_root, self->tp_index);
  self->tp_index = 0;
1126
  if (self->tp_socket != INVALID_SOCKET)
Pekka Pessi's avatar
Pekka Pessi committed
1127
    su_close(self->tp_socket);
1128
  self->tp_socket = INVALID_SOCKET;
Pekka Pessi's avatar
Pekka Pessi committed
1129 1130 1131 1132

  su_home_zap(self->tp_home);
}

1133 1134
/** Create a new reference to a transport object. */
tport_t *tport_ref(tport_t *tp)
Pekka Pessi's avatar
Pekka Pessi committed
1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
{
  if (tp) {
    if (tp->tp_refs >= 0)
      tp->tp_refs++;
    else if (tp->tp_refs == -1)
      tp->tp_refs = 1;
  }
  return tp;
}

1145 1146 1147
/** Destroy reference to a transport object. */
void tport_unref(tport_t *tp)
{
1148 1149 1150 1151
  if (tp == NULL || tp->tp_refs <= 0)
    return;
  if (--tp->tp_refs > 0)
    return;
1152

1153 1154 1155 1156 1157 1158 1159
  if (!tport_is_secondary(tp))
    return;

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

  tport_set_secondary_timer(tp);
1160 1161 1162 1163 1164 1165 1166 1167
}

/** 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
1168 1169 1170 1171 1172 1173
/** Destroy a transport reference. */
void tport_decref(tport_t **ttp)
{
  assert(ttp);

  if (*ttp) {
1174
    tport_unref(*ttp);
Pekka Pessi's avatar
Pekka Pessi committed
1175 1176 1177 1178 1179 1180 1181 1182
    *ttp = NULL;
  }
}

/** Get transport parameters.
 *
 * @param self          pointer to a transport object
 * @param tag,value,... list of tags
1183 1184 1185 1186 1187 1188 1189 1190
 *
 * @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
1191 1192 1193 1194 1195 1196 1197
 */
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;
1198
  int connect;
1199
  tport_master_t *mr;
Pekka Pessi's avatar
Pekka Pessi committed
1200 1201

  if (self == NULL)
1202
    return su_seterrno(EINVAL);
Pekka Pessi's avatar
Pekka Pessi committed
1203

1204
  mr = self->tp_master;
Pekka Pessi's avatar
Pekka Pessi committed
1205 1206 1207
  tpp = self->tp_params;
  ta_start(ta, tag, value);

1208
  connect = tpp->tpp_conn_orient
1209 1210 1211
    /* Only dgram primary is *not* connection-oriented */
    || !tport_is_primary(self) || !tport_is_dgram(self);

Pekka Pessi's avatar
Pekka Pessi committed
1212
  n = tl_tgets(ta_args(ta),
1213
	       TPTAG_MTU((usize_t)tpp->tpp_mtu),
Pekka Pessi's avatar
Pekka Pessi committed
1214
	       TPTAG_REUSE(self->tp_reusable),
1215
	       TPTAG_CONNECT(connect),
Pekka Pessi's avatar
Pekka Pessi committed
1216 1217 1218
	       TPTAG_QUEUESIZE(tpp->tpp_qsize),
	       TPTAG_IDLE(tpp->tpp_idle),
	       TPTAG_TIMEOUT(tpp->tpp_timeout),
1219 1220 1221
	       TPTAG_KEEPALIVE(tpp->tpp_keepalive),
	       TPTAG_PINGPONG(tpp->tpp_pingpong),
	       TPTAG_PONG2PING(tpp->tpp_pong2ping),
Pekka Pessi's avatar
Pekka Pessi committed
1222 1223 1224 1225
	       TPTAG_SDWN_ERROR(tpp->tpp_sdwn_error),
	       TPTAG_DEBUG_DROP(tpp->tpp_drop),
	       TPTAG_THRPSIZE(tpp->tpp_thrpsize),
	       TPTAG_THRPRQSIZE(tpp->tpp_thrprqsize),
1226 1227
	       TPTAG_SIGCOMP_LIFETIME(tpp->tpp_sigcomp_lifetime),
	       TPTAG_STUN_SERVER(tpp->tpp_stun_server),
1228 1229 1230
	       TAG_IF(self->tp_pri,
		      TPTAG_PUBLIC(self->tp_pri ?
				   self->tp_pri->pri_public : 0)),
1231
	       TPTAG_TOS(tpp->tpp_tos),
1232 1233 1234 1235
	       TAG_IF((void *)self == (void *)mr,
		      TPTAG_LOG(mr->mr_log != 0)),
	       TAG_IF((void *)self == (void *)mr,
		      TPTAG_DUMP(mr->mr_dump)),
Pekka Pessi's avatar
Pekka Pessi committed
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
	       TAG_END());

  ta_end(ta);

  return n;
}

/** Set transport parameters.
 *
 * @param self          pointer to a transport object
 * @param tag,value,... list of tags
1247 1248 1249
 *
 * @TAGS
 * TPTAG_MTU(), TPTAG_QUEUESIZE(), TPTAG_IDLE(), TPTAG_TIMEOUT(),
1250
 * TPTAG_KEEPALIVE(), TPTAG_PINGPONG(), TPTAG_PONG2PING(),
1251 1252 1253
 * 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
1254 1255 1256 1257 1258
 */
int tport_set_params(tport_t *self,
		     tag_type_t tag, tag_value_t value, ...)
{
  ta_list ta;
1259
  int n, m = 0;
Pekka Pessi's avatar
Pekka Pessi committed
1260
  tport_params_t tpp[1], *tpp0;
1261

1262
  usize_t mtu;
1263
  int connect, sdwn_error, reusable, stun_server, pong2ping;
1264

Pekka Pessi's avatar
Pekka Pessi committed
1265
  if (self == NULL)
1266
    return su_seterrno(EINVAL);
Pekka Pessi's avatar
Pekka Pessi committed
1267

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

1270
  mtu = tpp->tpp_mtu;
Pekka Pessi's avatar
Pekka Pessi committed
1271 1272 1273
  connect = tpp->tpp_conn_orient;
  sdwn_error = tpp->tpp_sdwn_error;
  reusable = self->tp_reusable;
1274
  stun_server = tpp->tpp_stun_server;
1275
  pong2ping = tpp->tpp_pong2ping;