tport.c 117 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
{
  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 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
  tport_primary_t *pri;

  if (tport_is_master(self)) {
280
    for (pri = self->tp_master->mr_primaries; pri; pri = pri->pri_next)
281 282 283 284 285 286 287 288
      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
/** Test if transport can be used to send message. @NEW_1_12_7. */
322 323 324
int tport_is_clear_to_send(tport_t const *self)
{
  return
325 326
    tport_is_master(self) ||
    tport_is_primary(self) ||
327 328 329 330 331 332 333
    (tport_is_secondary(self) &&
     tport_is_registered(self) &&
     self->tp_reusable &&
     !self->tp_closed &&
     !self->tp_send_close);
}

334
/** Return true if transport has message in send queue. @NEW_1_12_7. */
335 336 337 338 339
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
/** Set IP TOS for socket */
void tport_set_tos(su_socket_t socket, su_addrinfo_t *ai, int tos)
{
355 356
  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())));
  }
}

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

367
static int bind6only_check(tport_master_t *mr);
368 369 370 371 372

static
int tport_server_addrinfo(tport_master_t *mr,
			  char const *canon,
			  int family,
373
			  char const *host,
374 375 376 377 378
			  char const *service,
			  char const *protocol,
			  char const * const transports[],
			  su_addrinfo_t **res);

379
static int tport_get_local_addrinfo(tport_master_t *mr,
Pekka Pessi's avatar
Pekka Pessi committed
380 381 382 383
				    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 409
  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
410
  tport_queue(tport_t *self, msg_t *msg),
411
  tport_queue_rest(tport_t *self, msg_t *msg, msg_iovec_t iov[], size_t iovused),
Pekka Pessi's avatar
Pekka Pessi committed
412
  tport_pending_error(tport_t *self, su_sockaddr_t const *dst, int error),
413
  tport_pending_errmsg(tport_t *self, msg_t *msg, int error);
Pekka Pessi's avatar
Pekka Pessi committed
414

415 416 417 418
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
419 420 421 422 423 424 425
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
426

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

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

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


/* Stack class used when transports are being destroyed */
static
448 449
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
450 451 452 453 454 455
			su_time_t received)
{
  msg_destroy(msg);
}

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

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

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

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

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

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

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

536
#if HAVE_SOFIA_STUN
537
  tport_init_stun_server(mr, ta_args(ta));
538
#endif
539

Pekka Pessi's avatar
Pekka Pessi committed
540 541 542 543 544 545 546 547 548 549
  ta_end(ta);

  return mr->mr_master;
}

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

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

559
  SU_DEBUG_7(("%s(%p)\n", __func__, (void *)self));
Pekka Pessi's avatar
Pekka Pessi committed
560 561 562

  if (self == NULL)
    return;
563

Pekka Pessi's avatar
Pekka Pessi committed
564 565 566 567 568 569 570 571 572 573
  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);

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

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

587

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

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

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

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

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

    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
621
    if (!pri->pri_public)
622
      tp->tp_addrinfo->ai_addr = &tp->tp_addr->su_sa;
623

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

628
  *next = pri;
629
  tp = pri->pri_primary;
630

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

646
  save_errno = su_errno();
647
  tport_zap_primary(pri);
648
  su_seterrno(save_errno);
Pekka Pessi's avatar
Pekka Pessi committed
649

650 651
  return NULL;
}
652

653 654

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

668 669 670 671
  while (pri->pri_open)
    tport_zap_secondary(pri->pri_open);
  while (pri->pri_closed)
    tport_zap_secondary(pri->pri_closed);
672 673 674 675 676 677 678 679 680

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

681
  tport_zap_secondary((tport_t *)pri);
682
}
683

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

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

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

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

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

Pekka Pessi's avatar
Pekka Pessi committed
748 749
    tp->tp_index = index;
  }
Pekka Pessi's avatar
Pekka Pessi committed
750

751
  pri->pri_primary->tp_has_connection = 0;
752

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

  return pri;
}

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

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

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

775 776
  ai->ai_addrlen = sulen;

777 778 779 780 781 782
#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];
783

784
      memcpy(su0, su, sizeof su0);
785

786 787 788
      memset(su, 0, ai->ai_addrlen = sizeof su->su_sin);
      su->su_family = ai->ai_family = AF_INET;
      su->su_port = su0->su_port;
789

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

799
  return 0;
800 801
}

802 803 804 805 806 807 808 809 810 811 812

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


813 814
static
int tport_set_events(tport_t *self, int set, int clear)
Pekka Pessi's avatar
Pekka Pessi committed
815
{
816
  int events;
Pekka Pessi's avatar
Pekka Pessi committed
817

818 819
  if (self == NULL)
    return -1;
Pekka Pessi's avatar
Pekka Pessi committed
820

821 822
  events = (self->tp_events | set) & ~clear;
  self->tp_events = events;
Pekka Pessi's avatar
Pekka Pessi committed
823

824 825
  if (self->tp_pri->pri_vtable->vtp_set_events)
    return self->tp_pri->pri_vtable->vtp_set_events(self);
826

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

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

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

Pekka Pessi's avatar
Pekka Pessi committed
862
  self = su_home_clone(mr->mr_home, pri->pri_vtable->vtp_secondary_size);
Pekka Pessi's avatar
Pekka Pessi committed
863 864

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

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

    self->tp_magic = pri->pri_primary->tp_magic;
876 877

    self->tp_addrinfo->ai_addr = (void *)self->tp_addr;
878 879

    self->tp_socket = socket;
880 881 882 883

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

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

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

  return self;
}

906

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

924 925 926
  if (ai == NULL || ai->ai_addrlen > sizeof (pri->pri_primary->tp_addr))
    return NULL;

927 928
  if (pri->pri_vtable->vtp_connect)
    return pri->pri_vtable->vtp_connect(pri, ai, tpn);
929 930 931 932 933

  tp = tport_base_connect(pri, ai, ai, tpn);
  if (tp)
    tport_set_secondary_timer(tp);
  return tp;
934 935 936 937 938 939 940 941 942
}

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

955
  su_socket_t s, server_socket;
Pekka Pessi's avatar
Pekka Pessi committed
956
  su_wait_t wait[1] = { SU_WAIT_INIT };
957
  su_wakeup_f wakeup = tport_wakeup;
958
  int index = 0;
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 972
	    su_wait_destroy(wait),				     \
	    (SU_LOG_LEVEL >= errlevel ?				     \
	     su_llog(tport_log, errlevel,			     \
973 974
		     "%s(%p): %s(pf=%d %s/%s): %s\n",			\
				 __func__, (void *)pri, #what, ai->ai_family,	\
Pekka Pessi's avatar
Pekka Pessi committed
975
		     tpn->tpn_proto,				     \
976 977
		     tport_hostport(buf, sizeof(buf),		     \
				    (void *)ai->ai_addr, 2),	     \
Pekka Pessi's avatar
Pekka Pessi committed
978 979 980 981 982 983
		     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);
984
  if (s == INVALID_SOCKET)
985
    TPORT_CONNECT_ERROR(su_errno(), "socket");
Pekka Pessi's avatar
Pekka Pessi committed
986

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

  self->tp_conn_orient = 1;

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

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

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

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

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

1032
  if (su_wait_create(wait, s, self->tp_events = events) == -1)
Pekka Pessi's avatar
Pekka Pessi committed
1033 1034 1035
    TPORT_CONNECT_ERROR(su_errno(), su_wait_create);

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

1039
  self->tp_index = index;
Pekka Pessi's avatar
Pekka Pessi committed
1040

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

  tprb_append(&pri->pri_open, self);
Pekka Pessi's avatar
Pekka Pessi committed
1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065

  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 */
1066 1067 1068 1069 1070 1071 1072
  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
1073

1074 1075 1076
  /* Do not deinit primary as secondary! */
  if (tport_is_secondary(self) &&
      self->tp_pri->pri_vtable->vtp_deinit_secondary)
1077 1078
    self->tp_pri->pri_vtable->vtp_deinit_secondary(self);

Pekka Pessi's avatar
Pekka Pessi committed
1079 1080
  if (self->tp_msg) {
    msg_destroy(self->tp_msg), self->tp_msg = NULL;
1081
    SU_DEBUG_3(("%s(%p): zapped partially received message\n",
1082
		__func__, (void *)self));
Pekka Pessi's avatar
Pekka Pessi committed
1083 1084
  }

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

  if (self->tp_pused) {
1096
    SU_DEBUG_3(("%s(%p): zapped while pending\n",
1097
		__func__, (void *)self));
Pekka Pessi's avatar
Pekka Pessi committed
1098 1099 1100 1101
  }

  mr = self->tp_master;

1102
#if HAVE_SOFIA_STUN
1103
  tport_stun_server_remove_socket(self);
1104
#endif
1105

Pekka Pessi's avatar
Pekka Pessi committed
1106 1107 1108
  if (self->tp_index)
    su_root_deregister(mr->mr_root, self->tp_index);
  self->tp_index = 0;
1109
  if (self->tp_socket != INVALID_SOCKET)
Pekka Pessi's avatar
Pekka Pessi committed
1110
    su_close(self->tp_socket);
1111
  self->tp_socket = INVALID_SOCKET;
Pekka Pessi's avatar
Pekka Pessi committed
1112 1113 1114 1115

  su_home_zap(self->tp_home);
}

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

1128 1129 1130
/** Destroy reference to a transport object. */
void tport_unref(tport_t *tp)
{
1131 1132 1133 1134
  if (tp == NULL || tp->tp_refs <= 0)
    return;
  if (--tp->tp_refs > 0)
    return;
1135

1136 1137 1138 1139 1140 1141 1142
  if (!tport_is_secondary(tp))
    return;

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

  tport_set_secondary_timer(tp);
1143 1144 1145 1146 1147 1148 1149 1150
}

/** 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
1151 1152 1153 1154 1155 1156
/** Destroy a transport reference. */
void tport_decref(tport_t **ttp)
{
  assert(ttp);

  if (*ttp) {
1157
    tport_unref(*ttp);
Pekka Pessi's avatar
Pekka Pessi committed
1158 1159 1160 1161 1162 1163 1164 1165
    *ttp = NULL;
  }
}

/** Get transport parameters.
 *
 * @param self          pointer to a transport object
 * @param tag,value,... list of tags
1166 1167 1168 1169 1170 1171 1172 1173
 *
 * @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
1174 1175 1176 1177 1178 1179 1180
 */
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;
1181
  int connect;
Pekka Pessi's avatar
Pekka Pessi committed
1182 1183

  if (self == NULL)
1184
    return su_seterrno(EINVAL);
Pekka Pessi's avatar
Pekka Pessi committed
1185 1186 1187 1188

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

1189
  connect = tpp->tpp_conn_orient
1190 1191 1192
    /* Only dgram primary is *not* connection-oriented */
    || !tport_is_primary(self) || !tport_is_dgram(self);

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

  ta_end(ta);

  return n;
}

/** Set transport parameters.
 *
 * @param self          pointer to a transport object
 * @param tag,value,... list of tags
1224 1225 1226
 *
 * @TAGS
 * TPTAG_MTU(), TPTAG_QUEUESIZE(), TPTAG_IDLE(), TPTAG_TIMEOUT(),
1227
 * TPTAG_KEEPALIVE(), TPTAG_PINGPONG(), TPTAG_PONG2PING(),
1228 1229 1230
 * 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
1231 1232 1233 1234 1235
 */
int tport_set_params(tport_t *self,
		     tag_type_t tag, tag_value_t value, ...)
{
  ta_list ta;
1236
  int n, m = 0;
Pekka Pessi's avatar
Pekka Pessi committed
1237
  tport_params_t tpp[1], *tpp0;
1238

1239
  usize_t mtu;
1240
  int connect, sdwn_error, reusable, stun_server, pong2ping;
1241

Pekka Pessi's avatar
Pekka Pessi committed
1242
  if (self == NULL)
1243
    return su_seterrno(EINVAL);
Pekka Pessi's avatar
Pekka Pessi committed
1244

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

1247
  mtu = tpp->tpp_mtu;
Pekka Pessi's avatar
Pekka Pessi committed
1248 1249 1250
  connect = tpp->tpp_conn_orient;
  sdwn_error = tpp->tpp_sdwn_error;
  reusable = self->tp_reusable;
1251
  stun_server = tpp->tpp_stun_server;
1252
  pong2ping = tpp->tpp_pong2ping;
Pekka Pessi's avatar
Pekka Pessi committed
1253 1254 1255 1256

  ta_start(ta, tag, value);

  n = tl_gets(ta_args(ta),
1257
	      TPTAG_MTU_REF(mtu),
Pekka Pessi's avatar
Pekka Pessi committed
1258 1259 1260
	      TAG_IF(!self->tp_queue, TPTAG_QUEUESIZE_REF(tpp->tpp_qsize)),
	      TPTAG_IDLE_REF(tpp->tpp_idle),
	      TPTAG_TIMEOUT_REF(tpp->tpp_timeout),
1261 1262 1263
	      TPTAG_KEEPALIVE_REF(tpp->tpp_keepalive),
	      TPTAG_PINGPONG_REF(tpp->tpp_pingpong),
	      TPTAG_PONG2PING_REF(pong2ping),
Pekka Pessi's avatar
Pekka Pessi committed
1264 1265 1266 1267 1268 1269 1270
	      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),
1271
	      TPTAG_STUN_SERVER_REF(stun_server),
1272
	      TPTAG_TOS_REF(tpp->tpp_tos),
Pekka Pessi's avatar
Pekka Pessi committed
1273 1274
	      TAG_END());

1275 1276 1277
  if (self == (tport_t *)self->tp_master)
    m = tport_open_log(self->tp_master, ta_args