tport.c 108 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 50 51
#define SU_WAKEUP_ARG_T         struct tport_s
#define SU_TIMER_ARG_T          struct tport_master
#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 107 108 109 110 111 112
#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)

static inline int tp_cmp(tport_t const *a, tport_t const *b)
{
  if (a == b)
    return 0;
  
  if (a->tp_addrlen != b->tp_addrlen)
113
    return (int)(a->tp_addrlen - b->tp_addrlen);
Pekka Pessi's avatar
Pekka Pessi committed
114

115
  return memcmp(a->tp_addr, b->tp_addr, a->tp_addrlen);
Pekka Pessi's avatar
Pekka Pessi committed
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
}

static inline int tprb_is_inserted(tport_t const *a)
{
  return a->tp_dad != 0 || a->tp_left != 0 || a->tp_right != 0;
}

RBTREE_PROTOS(static inline, tprb, tport_t);

RBTREE_BODIES(static inline, tprb, tport_t, 
	      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);

enum {
  /** Default per-thread read queue length */
  THRP_PENDING = 8
};

struct tport_pending_s {
Pekka Pessi's avatar
Pekka Pessi committed
136
  /* tport_pending_t       *p_left, *p_right, *p_parent; */
Pekka Pessi's avatar
Pekka Pessi committed
137 138 139
  void               *p_client;
  tport_pending_error_f *p_callback;
  msg_t              *p_msg;
140
  unsigned            p_reported;
Pekka Pessi's avatar
Pekka Pessi committed
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
};

/** Return true if transport is master. */
inline int tport_is_master(tport_t const *self)
{
  return 
    self && 
    self->tp_master->mr_master == self;
}

/** Return true if transport is primary. */
inline int tport_is_primary(tport_t const *self)
{
  return 
    self && 
    self->tp_pri->pri_primary == self;
}

/** Return true if transport is secondary. */
inline int tport_is_secondary(tport_t const *self)
{
  return 
    self && 
    self->tp_master->mr_master != self && 
    self->tp_pri->pri_primary != self;
}

/** Test if transport has been registered */
169
inline int tport_is_registered(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
170 171 172 173 174 175 176
{
  return self->tp_index != 0;
}

/** Test if transport is stream. */
inline int tport_is_stream(tport_t const *self)
{
177
  return self->tp_addrinfo->ai_socktype == SOCK_STREAM;
Pekka Pessi's avatar
Pekka Pessi committed
178 179 180
}
 
/** Test if transport is dgram. */
181
inline int tport_is_dgram(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
182
{
183
  return self->tp_addrinfo->ai_socktype == SOCK_DGRAM;
Pekka Pessi's avatar
Pekka Pessi committed
184 185 186 187 188
}
 
/** Test if transport is udp. */
inline int tport_is_udp(tport_t const *self)
{
189
  return self->tp_addrinfo->ai_protocol == IPPROTO_UDP;
Pekka Pessi's avatar
Pekka Pessi committed
190 191 192 193 194
}
 
/** Test if transport is tcp. */
inline int tport_is_tcp(tport_t const *self)
{
195
  return self->tp_addrinfo->ai_protocol == IPPROTO_TCP;
Pekka Pessi's avatar
Pekka Pessi committed
196 197 198 199 200 201
}
 
/** Return 1 if transport is reliable, 0 otherwise.
 *
 * (Note that this is part of external API).
 */
202
int tport_is_reliable(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
203 204
{
  return self != NULL && 
205 206
    (self->tp_addrinfo->ai_socktype == SOCK_STREAM || 
     self->tp_addrinfo->ai_socktype == SOCK_SEQPACKET);
Pekka Pessi's avatar
Pekka Pessi committed
207 208
}

Pekka Pessi's avatar
Pekka Pessi committed
209 210 211
/** Return 0 if self is local, nonzero otherwise.
 *
 * The return valu is the 
212
 *
Pekka Pessi's avatar
Pekka Pessi committed
213
 * @sa TPTAG_PUBLIC(), enum #tport_via.
214
 */
Pekka Pessi's avatar
Pekka Pessi committed
215
int tport_is_public(tport_t const *self)
216
{
Pekka Pessi's avatar
Pekka Pessi committed
217
  return self->tp_pri->pri_public;
218
}
Pekka Pessi's avatar
Pekka Pessi committed
219 220

/** Return true if transport supports IPv4 */
221
int tport_has_ip4(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
222 223
{
  return self && 
224 225
    (self->tp_addrinfo->ai_family == 0 || 
     self->tp_addrinfo->ai_family == AF_INET);
Pekka Pessi's avatar
Pekka Pessi committed
226 227
}

228 229

#if SU_HAVE_IN6
Pekka Pessi's avatar
Pekka Pessi committed
230
/** Return true if transport supports IPv6 */
231
int tport_has_ip6(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
232 233
{
  return self && 
234 235
    (self->tp_addrinfo->ai_family == 0 || 
     self->tp_addrinfo->ai_family == AF_INET6);
Pekka Pessi's avatar
Pekka Pessi committed
236
}
237
#endif
Pekka Pessi's avatar
Pekka Pessi committed
238 239

/** Return true if transport supports TLS. */
240
int tport_has_tls(tport_t const *self)
Pekka Pessi's avatar
Pekka Pessi committed
241
{
242 243
  return self && self->tp_pri->pri_has_tls;

Pekka Pessi's avatar
Pekka Pessi committed
244 245
}

246 247 248
/** Return true if transport is being updated. */
int tport_is_updating(tport_t const *self)
{
249 250 251 252 253 254 255 256 257 258 259 260
  tport_primary_t *pri;

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

  return 0;
261 262
}

Pekka Pessi's avatar
Pekka Pessi committed
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
/** Test if transport has been closed */
static inline int tport_is_closed(tport_t const *self)
{
  return self->tp_closed;
}

/** Test if transport has been shut down */
static inline int tport_is_shutdown(tport_t const *self)
{
  return self->tp_closed || self->tp_send_close || self->tp_recv_close;
}

/** Test if transport is bound */
static inline int tport_is_bound(tport_t const *self)
{
  return self->tp_protoname != NULL;
}

/** MTU for transport  */
static inline unsigned tport_mtu(tport_t const *self)
{
  return self->tp_params->tpp_mtu;
}

static inline
int tport_has_sigcomp(tport_t const *self)
{
  return self->tp_name->tpn_comp != NULL;
}

static 
tport_t *tport_connect(tport_primary_t *pri, su_addrinfo_t *ai, 
		       tp_name_t const *tpn);

297
static int bind6only_check(tport_master_t *mr);
298 299 300 301 302 303 304 305 306 307 308

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

Pekka Pessi's avatar
Pekka Pessi committed
309 310 311 312 313
static int tport_get_local_addrinfo(tport_master_t *mr, 
				    char const *port,
				    su_addrinfo_t const *hints,
				    su_addrinfo_t **return_ai);

314 315 316 317 318
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
319

Pekka Pessi's avatar
Pekka Pessi committed
320 321 322 323
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
324
static int
Pekka Pessi's avatar
Pekka Pessi committed
325 326 327 328 329 330
  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
331

332
  tport_setname(tport_t *, char const *, su_addrinfo_t const *, char const *),
Pekka Pessi's avatar
Pekka Pessi committed
333 334 335
  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),
Pekka Pessi's avatar
Pekka Pessi committed
336
  tport_connected(su_root_magic_t *m, su_wait_t *w, tport_t *self),
Pekka Pessi's avatar
Pekka Pessi committed
337
  tport_resolve(tport_t *self, msg_t *msg, tp_name_t const *tpn),
Pekka Pessi's avatar
Pekka Pessi committed
338
  tport_send_error(tport_t *, msg_t *, tp_name_t const *),
Pekka Pessi's avatar
Pekka Pessi committed
339
  tport_queue(tport_t *self, msg_t *msg),
340
  tport_queue_rest(tport_t *self, msg_t *msg, msg_iovec_t iov[], size_t iovused),
Pekka Pessi's avatar
Pekka Pessi committed
341
  tport_pending_error(tport_t *self, su_sockaddr_t const *dst, int error),
342
  tport_pending_errmsg(tport_t *self, msg_t *msg, int error);
Pekka Pessi's avatar
Pekka Pessi committed
343

344 345 346 347
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
348 349 350 351 352 353 354
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 unsigned long tport_now(void);

355
static void tport_tick(su_root_magic_t *, su_timer_t *, tport_master_t *mr);
Pekka Pessi's avatar
Pekka Pessi committed
356 357

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

Pekka Pessi's avatar
Pekka Pessi committed
359 360
static tport_primary_t *tport_alloc_primary(tport_master_t *mr,
					    tport_vtable_t const *vtable,
361
					    tp_name_t tpn[1],
Pekka Pessi's avatar
Pekka Pessi committed
362 363 364 365 366 367
					    su_addrinfo_t *ai, 
					    tagi_t const *tags,
					    char const **return_culprit);

static tport_primary_t *tport_listen(tport_master_t *mr,
				     tport_vtable_t const *vtable,
368
				     tp_name_t tpn[1],
Pekka Pessi's avatar
Pekka Pessi committed
369
				     su_addrinfo_t *ai,
Pekka Pessi's avatar
Pekka Pessi committed
370 371 372
				     tagi_t *tags);
static void tport_zap_primary(tport_primary_t *);

373
static char *localipname(int pf, char *buf, size_t bufsiz);
Pekka Pessi's avatar
Pekka Pessi committed
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
static int getprotohints(su_addrinfo_t *hints,
			 char const *proto, int flags);
static void tport_send_queue(tport_t *self);


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

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

static
msg_t *tport_destroy_alloc(tp_stack_t *stack, int flags, 
396
			   char const data[], usize_t len,
Pekka Pessi's avatar
Pekka Pessi committed
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
			   tport_t const *tp,
			   tp_client_t *tpc)
{
  return NULL;
}

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

/** Create the master transport. */
tport_t *tport_tcreate(tp_stack_t *stack,
		       tp_stack_class_t const *tpac,
		       su_root_t *root, 
		       tag_type_t tag, tag_value_t value, ...)
{
  tport_master_t *mr;
  tp_name_t *tpn;
  tport_params_t *tpp;
  unsigned tick;
  ta_list ta;

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

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

Pekka Pessi's avatar
Pekka Pessi committed
427
  SU_DEBUG_7(("%s(): %p\n", "tport_create", mr));
Pekka Pessi's avatar
Pekka Pessi committed
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443

  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;
444
  tpp->tpp_stun_server = 1;
Pekka Pessi's avatar
Pekka Pessi committed
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464

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

  ta_start(ta, tag, value);

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

  tick = 5000; /* For testing, usually 30000 is enough */  
  if (tpp->tpp_idle < 4 * tick)
    tick = tpp->tpp_idle / 4;
  if (tpp->tpp_timeout < 4 * tick)
    tick = tpp->tpp_timeout / 4;
  if (tick < 200)
    tick = 200;

465 466
  tport_init_stun_server(mr, ta_args(ta));

Pekka Pessi's avatar
Pekka Pessi committed
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
  mr->mr_timer = su_timer_create(su_root_task(root), tick);
  su_timer_set(mr->mr_timer, tport_tick, mr);

  ta_end(ta);

  return mr->mr_master;
}

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

  static tp_stack_class_t tport_destroy_tpac[1] = 
    {{
	sizeof tport_destroy_tpac,
	/* tpac_recv */ tport_destroy_recv,
	/* tpac_error */ tport_destroy_error,
	/* tpac_alloc */ tport_destroy_alloc,
486
	/* tpac_address */ NULL
Pekka Pessi's avatar
Pekka Pessi committed
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
      }};

  SU_DEBUG_7(("%s(%p)\n", __func__, self));

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

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

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

504
  tport_deinit_stun_server(mr);
Pekka Pessi's avatar
Pekka Pessi committed
505 506 507 508 509 510 511 512 513 514

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

515

Pekka Pessi's avatar
Pekka Pessi committed
516 517
/** Allocate a primary transport */
static 
Pekka Pessi's avatar
Pekka Pessi committed
518 519
tport_primary_t *tport_alloc_primary(tport_master_t *mr,
				     tport_vtable_t const *vtable,
520
				     tp_name_t tpn[1],
Pekka Pessi's avatar
Pekka Pessi committed
521 522 523
				     su_addrinfo_t *ai,
				     tagi_t const *tags,
				     char const **return_culprit)
Pekka Pessi's avatar
Pekka Pessi committed
524 525
{
  tport_primary_t *pri, **next;
Pekka Pessi's avatar
Pekka Pessi committed
526
  tport_t *tp;
527
  int save_errno;
Pekka Pessi's avatar
Pekka Pessi committed
528 529 530

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

Pekka Pessi's avatar
Pekka Pessi committed
534
  if ((pri = su_home_clone(mr->mr_home, vtable->vtp_pri_size))) {
Pekka Pessi's avatar
Pekka Pessi committed
535
    tport_t *tp = pri->pri_primary;
Pekka Pessi's avatar
Pekka Pessi committed
536 537 538
    pri->pri_vtable = vtable;
    pri->pri_public = vtable->vtp_public;

Pekka Pessi's avatar
Pekka Pessi committed
539 540
    tp->tp_master = mr;
    tp->tp_pri = pri;
541
    tp->tp_socket = INVALID_SOCKET;
Pekka Pessi's avatar
Pekka Pessi committed
542 543 544 545 546 547 548

    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
549
    if (!pri->pri_public)
Pekka Pessi's avatar
Pekka Pessi committed
550
      tp->tp_addrinfo->ai_addr = &tp->tp_addr->su_sa;
551

Pekka Pessi's avatar
Pekka Pessi committed
552 553 554
    SU_DEBUG_5(("%s(%p): new primary tport %p\n", __func__, mr, pri));
  }

Pekka Pessi's avatar
Pekka Pessi committed
555 556 557 558 559 560 561
  *next = pri; 
  tp = pri->pri_primary;
  
  if (!tp)
    *return_culprit = "alloc";
  else if (tport_set_params(tp, TAG_NEXT(tags)) < 0)
    *return_culprit = "tport_set_params";
Pekka Pessi's avatar
Pekka Pessi committed
562 563
  else if (vtable->vtp_init_primary &&
	   vtable->vtp_init_primary(pri, tpn, ai, tags, return_culprit) < 0)
Pekka Pessi's avatar
Pekka Pessi committed
564 565 566 567 568 569 570 571
    ;
  else if (tport_setname(tp, vtable->vtp_name, ai, tpn->tpn_canon) == -1) 
    *return_culprit = "tport_setname";
  else if (tpn->tpn_ident && 
	   !(tp->tp_name->tpn_ident = su_strdup(tp->tp_home, tpn->tpn_ident)))
    *return_culprit = "alloc ident";
  else
    return pri;			/* Success */
Pekka Pessi's avatar
Pekka Pessi committed
572

573
  save_errno = su_errno();
Pekka Pessi's avatar
Pekka Pessi committed
574
  tport_zap_primary(pri);
575
  su_seterrno(save_errno);
Pekka Pessi's avatar
Pekka Pessi committed
576

Pekka Pessi's avatar
Pekka Pessi committed
577 578
  return NULL;
}
579

Pekka Pessi's avatar
Pekka Pessi committed
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607

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

  if (pri == NULL)
    return;

  assert(tport_is_primary(pri->pri_primary));

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

  while (pri->pri_secondary)
    tport_zap_secondary(pri->pri_secondary);

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

  tport_zap_secondary(pri->pri_primary);
}
608

Pekka Pessi's avatar
Pekka Pessi committed
609 610 611 612 613 614
/**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
615
 * @param ai    pointer to addrinfo structure
Pekka Pessi's avatar
Pekka Pessi committed
616 617 618 619
 * @param canon canonical name of node
 * @param protoname name of the protocol
 */
static
Pekka Pessi's avatar
Pekka Pessi committed
620 621
tport_primary_t *tport_listen(tport_master_t *mr,
			      tport_vtable_t const *vtable,
622
			      tp_name_t tpn[1],
Pekka Pessi's avatar
Pekka Pessi committed
623
			      su_addrinfo_t *ai, 
Pekka Pessi's avatar
Pekka Pessi committed
624 625 626 627 628 629 630 631
			      tagi_t *tags)
{
  tport_primary_t *pri = NULL;

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

Pekka Pessi's avatar
Pekka Pessi committed
632 633 634 635 636
  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
637
  /* Log an error, return error */
Pekka Pessi's avatar
Pekka Pessi committed
638 639
#define TPORT_LISTEN_ERROR(errno, what)				     \
  ((void)(err = errno,						     \
Pekka Pessi's avatar
Pekka Pessi committed
640 641 642
	  ((err == EADDRINUSE || err == EAFNOSUPPORT ||		     \
	    err == ESOCKTNOSUPPORT || err == EPROTONOSUPPORT ||	     \
	    err == ENOPROTOOPT ? 7 : 3) < SU_LOG_LEVEL ?	     \
Pekka Pessi's avatar
Pekka Pessi committed
643 644
	     su_llog(tport_log, errlevel,			     \
		     "%s(%p): %s(pf=%d %s/%s): %s\n",		     \
Remi Denis-Courmont's avatar
Remi Denis-Courmont committed
645
		     __func__, pri ? (void *)pri : (void *)mr, what, \
646
		     ai->ai_family, protoname,			     \
Pekka Pessi's avatar
Pekka Pessi committed
647 648 649 650 651 652
		     tport_hostport(buf, sizeof(buf), su, 2),	     \
		     su_strerror(err)) : (void)0),		     \
	    tport_zap_primary(pri),		                     \
	    su_seterrno(err)),					     \
     (void *)NULL)

Pekka Pessi's avatar
Pekka Pessi committed
653
  /* Create a primary transport object for another transport. */
Pekka Pessi's avatar
Pekka Pessi committed
654
  pri = tport_alloc_primary(mr, vtable, tpn, ai, tags, &culprit);
Pekka Pessi's avatar
Pekka Pessi committed
655 656
  if (pri == NULL)
    return TPORT_LISTEN_ERROR(errno, culprit);
Pekka Pessi's avatar
Pekka Pessi committed
657

658
  if (pri->pri_primary->tp_socket != INVALID_SOCKET) {
Pekka Pessi's avatar
Pekka Pessi committed
659 660 661 662 663 664 665 666 667 668 669 670 671
    int index = 0;
    tport_t *tp = pri->pri_primary;
    su_wait_t wait[1] = { SU_WAIT_INIT };

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

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

Pekka Pessi's avatar
Pekka Pessi committed
673 674
    tp->tp_index = index;
  }
Pekka Pessi's avatar
Pekka Pessi committed
675 676

  pri->pri_primary->tp_connected = 0;
Pekka Pessi's avatar
Pekka Pessi committed
677

Pekka Pessi's avatar
Pekka Pessi committed
678 679 680 681 682 683 684
  SU_DEBUG_5(("%s(%p): %s " TPN_FORMAT "\n", 
	      __func__, pri, "listening at",
	      TPN_ARGS(pri->pri_primary->tp_name)));

  return pri;
}

Pekka Pessi's avatar
Pekka Pessi committed
685 686 687
int tport_bind_socket(int socket,
		      su_addrinfo_t *ai,
		      char const **return_culprit)
688
{
Pekka Pessi's avatar
Pekka Pessi committed
689
  su_sockaddr_t *su = (su_sockaddr_t *)ai->ai_addr;
690
  socklen_t sulen = (socklen_t)(ai->ai_addrlen);
691

692
  if (bind(socket, ai->ai_addr, sulen) == -1) {
Pekka Pessi's avatar
Pekka Pessi committed
693
    return *return_culprit = "bind", -1;
Pekka Pessi's avatar
Pekka Pessi committed
694
  }
695

696
  if (getsockname(socket, &su->su_sa, &sulen) == SOCKET_ERROR) {
Pekka Pessi's avatar
Pekka Pessi committed
697
    return *return_culprit = "getsockname", -1;
Pekka Pessi's avatar
Pekka Pessi committed
698
  }
699

700 701
  ai->ai_addrlen = sulen;

702 703 704 705 706 707 708 709 710 711 712 713 714 715
#if defined (__linux__) && defined (SU_HAVE_IN6)
  if (ai->ai_family == AF_INET6) {
    if (!SU_SOCKADDR_INADDR_ANY(su) &&
	(IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr) ||
	 IN6_IS_ADDR_V4COMPAT(&su->su_sin6.sin6_addr))) {
      su_sockaddr_t su0[1];
      
      memcpy(su0, su, sizeof su0);
      
      memset(su, 0, ai->ai_addrlen = sizeof su->su_sin);
      su->su_family = ai->ai_family = AF_INET;
      su->su_port = su0->su_port;
      
#ifndef IN6_V4MAPPED_TO_INADDR
Pekka Pessi's avatar
Pekka Pessi committed
716
#define IN6_V4MAPPED_TO_INADDR(in6, in4) \
717 718 719 720 721 722 723
      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

Pekka Pessi's avatar
Pekka Pessi committed
724
  return 0;
725 726
}

727 728 729 730 731 732 733 734 735 736 737

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


Pekka Pessi's avatar
Pekka Pessi committed
738 739
static
int tport_set_events(tport_t *self, int set, int clear)
Pekka Pessi's avatar
Pekka Pessi committed
740
{
Pekka Pessi's avatar
Pekka Pessi committed
741
  int events;
Pekka Pessi's avatar
Pekka Pessi committed
742

Pekka Pessi's avatar
Pekka Pessi committed
743 744
  if (self == NULL)
    return -1;
Pekka Pessi's avatar
Pekka Pessi committed
745

Pekka Pessi's avatar
Pekka Pessi committed
746 747
  events = (self->tp_events | set) & ~clear;
  self->tp_events = events;
Pekka Pessi's avatar
Pekka Pessi committed
748

Pekka Pessi's avatar
Pekka Pessi committed
749 750
  if (self->tp_pri->pri_vtable->vtp_set_events)
    return self->tp_pri->pri_vtable->vtp_set_events(self);
Pekka Pessi's avatar
Pekka Pessi committed
751

Pekka Pessi's avatar
Pekka Pessi committed
752 753 754 755 756 757 758 759 760 761 762
  SU_DEBUG_7(("tport_set_events(%p): events%s%s%s\n", self,
	      (events & SU_WAIT_IN) ? " IN" : "",
	      (events & SU_WAIT_OUT) ? " OUT" : "",
	      SU_WAIT_CONNECT != SU_WAIT_OUT &&
	      (events & SU_WAIT_CONNECT) ? " CONNECT" : ""));
  
  return 
    su_root_eventmask(self->tp_master->mr_root, 
		      self->tp_index, 
		      self->tp_socket, 
		      self->tp_events = events);
Pekka Pessi's avatar
Pekka Pessi committed
763 764 765 766 767 768 769 770
}

/**Allocate a secondary transport. @internal
 *
 * The function tport_alloc_secondary() creates a secondary transport
 * object. The new transport initally shares parameters structure with the
 * original transport.
 *
771 772 773
 * @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
774 775
 *
 * @return
776 777 778
 * 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
779
 */
780 781 782 783
tport_t *tport_alloc_secondary(tport_primary_t *pri, 
			       int socket,
			       int accepted,
			       char const **return_reason)
Pekka Pessi's avatar
Pekka Pessi committed
784 785 786 787
{
  tport_master_t *mr = pri->pri_master;
  tport_t *self;

Pekka Pessi's avatar
Pekka Pessi committed
788
  self = su_home_clone(mr->mr_home, pri->pri_vtable->vtp_secondary_size);
Pekka Pessi's avatar
Pekka Pessi committed
789 790 791 792 793 794 795 796

  if (self) {
    SU_DEBUG_7(("%s(%p): new secondary tport %p\n", __func__, pri, self));

    self->tp_refs = -1;			/* Freshly allocated  */
    self->tp_master = mr;
    self->tp_pri = pri;
    self->tp_params = pri->pri_params;
Pekka Pessi's avatar
Pekka Pessi committed
797
    self->tp_accepted = accepted != 0;
Pekka Pessi's avatar
Pekka Pessi committed
798 799 800
    self->tp_reusable = pri->pri_primary->tp_reusable;

    self->tp_magic = pri->pri_primary->tp_magic;
801 802

    self->tp_addrinfo->ai_addr = (void *)self->tp_addr;
Pekka Pessi's avatar
Pekka Pessi committed
803 804 805 806

    self->tp_socket = socket;
    
    if (pri->pri_vtable->vtp_init_secondary &&
807 808
	pri->pri_vtable->vtp_init_secondary(self, socket, accepted,
					    return_reason) < 0) {
Pekka Pessi's avatar
Pekka Pessi committed
809 810 811 812 813
      if (pri->pri_vtable->vtp_deinit_secondary)
	pri->pri_vtable->vtp_deinit_secondary(self);
      su_home_zap(self->tp_home);
      return NULL;
    }
Pekka Pessi's avatar
Pekka Pessi committed
814
  }
815 816 817 818
  else {
    su_close(socket);
    *return_reason = "malloc";
  }
Pekka Pessi's avatar
Pekka Pessi committed
819 820 821 822

  return self;
}

Pekka Pessi's avatar
Pekka Pessi committed
823

Pekka Pessi's avatar
Pekka Pessi committed
824 825 826 827 828 829 830 831
/** 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
Pekka Pessi's avatar
Pekka Pessi committed
832
 * @param tpn   canonical name of node
Pekka Pessi's avatar
Pekka Pessi committed
833 834 835 836 837
 */
static
tport_t *tport_connect(tport_primary_t *pri, 
		       su_addrinfo_t *ai,
		       tp_name_t const *tpn)
Pekka Pessi's avatar
Pekka Pessi committed
838
{
839 840 841
  if (ai == NULL || ai->ai_addrlen > sizeof (pri->pri_primary->tp_addr))
    return NULL;

Pekka Pessi's avatar
Pekka Pessi committed
842 843 844
  if (pri->pri_vtable->vtp_connect)
    return pri->pri_vtable->vtp_connect(pri, ai, tpn);
  else
Pekka Pessi's avatar
Pekka Pessi committed
845
    return tport_base_connect(pri, ai, ai, tpn);
Pekka Pessi's avatar
Pekka Pessi committed
846 847 848 849 850 851 852 853 854
}

/**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
855 856
 * @param ai    pointer to addrinfo structure describing socket
 * @param real_ai  pointer to addrinfo structure describing real target
Pekka Pessi's avatar
Pekka Pessi committed
857 858
 * @param tpn   canonical name of node
 */
859
tport_t *tport_base_connect(tport_primary_t *pri,
Pekka Pessi's avatar
Pekka Pessi committed
860
			    su_addrinfo_t *ai,
861
			    su_addrinfo_t *real_ai,
Pekka Pessi's avatar
Pekka Pessi committed
862
			    tp_name_t const *tpn)
Pekka Pessi's avatar
Pekka Pessi committed
863 864 865 866
{
  tport_master_t *mr = pri->pri_master;
  tport_t *self = NULL;

867
  su_socket_t s, server_socket;
Pekka Pessi's avatar
Pekka Pessi committed
868
  su_wait_t wait[1] = { SU_WAIT_INIT };
Pekka Pessi's avatar
Pekka Pessi committed
869
  su_wakeup_f wakeup = tport_wakeup;
870
  int index = 0;
Pekka Pessi's avatar
Pekka Pessi committed
871
  int events = SU_WAIT_IN | SU_WAIT_ERR;
Pekka Pessi's avatar
Pekka Pessi committed
872

873 874
  int err;
  unsigned errlevel = 3;
Pekka Pessi's avatar
Pekka Pessi committed
875
  char buf[TPORT_HOSTPORTSIZE];
876
  char const *what;
Pekka Pessi's avatar
Pekka Pessi committed
877 878

  /* Log an error, return error */
879
#define TPORT_CONNECT_ERROR(errno, what)			     \
Pekka Pessi's avatar
Pekka Pessi committed
880
  return							     \
881
    ((void)(err = errno,					     \
Pekka Pessi's avatar
Pekka Pessi committed
882 883 884 885 886 887
	    su_wait_destroy(wait),				     \
	    (SU_LOG_LEVEL >= errlevel ?				     \
	     su_llog(tport_log, errlevel,			     \
		     "%s(%p): %s(pf=%d %s/%s): %s\n",		     \
		     __func__, pri, #what, ai->ai_family,	     \
		     tpn->tpn_proto,				     \
888 889
		     tport_hostport(buf, sizeof(buf),		     \
				    (void *)ai->ai_addr, 2),	     \
Pekka Pessi's avatar
Pekka Pessi committed
890 891 892 893 894 895
		     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);
896
  if (s == INVALID_SOCKET)
897
    TPORT_CONNECT_ERROR(su_errno(), "socket");
Pekka Pessi's avatar
Pekka Pessi committed
898

899 900 901 902 903 904
  what = "tport_alloc_secondary";
  if ((self = tport_alloc_secondary(pri, s, 0, &what)) == NULL)
    TPORT_CONNECT_ERROR(errno, what);

  self->tp_conn_orient = 1;

905
  if ((server_socket = pri->pri_primary->tp_socket) != INVALID_SOCKET) {
Pekka Pessi's avatar
Pekka Pessi committed
906 907 908
    su_sockaddr_t susa;
    socklen_t susalen = sizeof(susa);

909 910
    /* Bind this socket to same IP address as the primary server socket */
    if (getsockname(server_socket, &susa.su_sa, &susalen) < 0) {
Pekka Pessi's avatar
Pekka Pessi committed
911 912 913 914 915 916 917 918 919 920 921 922
      SU_DEBUG_3(("tport_connect: getsockname(): %s\n", 
		  su_strerror(su_errno())));
    }
    else {
      susa.su_port = 0;
      if (bind(s, &susa.su_sa, susalen) < 0) {
	SU_DEBUG_3(("tport_connect: bind(local-ip): %s\n", 
		    su_strerror(su_errno())));
      }
    }
  }

923 924 925
  /* Set sockname for the tport */
  if (tport_setname(self, tpn->tpn_proto, real_ai, tpn->tpn_canon) == -1) 
    TPORT_CONNECT_ERROR(su_errno(), tport_setname);
Pekka Pessi's avatar
Pekka Pessi committed
926

927
  if (connect(s, ai->ai_addr, (socklen_t)(ai->ai_addrlen)) == SOCKET_ERROR) {
Pekka Pessi's avatar
Pekka Pessi committed
928
    err = su_errno();
929
    if (!su_is_blocking(err))
Pekka Pessi's avatar
Pekka Pessi committed
930
      TPORT_CONNECT_ERROR(err, connect);
Pekka Pessi's avatar
Pekka Pessi committed
931 932
    events = SU_WAIT_CONNECT | SU_WAIT_ERR;
    wakeup = tport_connected;
933 934 935 936
    what = "connecting";
  }
  else {
    what = "connected";
Pekka Pessi's avatar
Pekka Pessi committed
937 938
  }

939
  if (su_wait_create(wait, s, self->tp_events = events) == -1)
Pekka Pessi's avatar
Pekka Pessi committed
940 941 942
    TPORT_CONNECT_ERROR(su_errno(), su_wait_create);

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

946
  self->tp_index = index;
Pekka Pessi's avatar
Pekka Pessi committed
947

948 949 950 951 952 953 954 955 956 957 958
  if (ai == real_ai) {
    SU_DEBUG_5(("%s(%p): %s to " TPN_FORMAT "\n", 
		__func__, self, what, TPN_ARGS(self->tp_name)));
  }
  else {
    SU_DEBUG_5(("%s(%p): %s via %s to " TPN_FORMAT "\n",
		__func__, self, what,
		tport_hostport(buf, sizeof(buf), (void *)ai->ai_addr, 2),
		TPN_ARGS(self->tp_name)));
  }
  
Pekka Pessi's avatar
Pekka Pessi committed
959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
  tprb_append(&pri->pri_secondary, self);

  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 */
  tprb_remove(&self->tp_pri->pri_secondary, self);

975 976 977
  /* Do not deinit primary as secondary! */
  if (tport_is_secondary(self) &&
      self->tp_pri->pri_vtable->vtp_deinit_secondary)
Pekka Pessi's avatar
Pekka Pessi committed
978 979
    self->tp_pri->pri_vtable->vtp_deinit_secondary(self);

Pekka Pessi's avatar
Pekka Pessi committed
980 981 982 983 984 985 986 987
  if (self->tp_msg) {
    msg_destroy(self->tp_msg), self->tp_msg = NULL;
    SU_DEBUG_3(("%s(%p): zapped partially received message\n", 
		__func__, self));
  }

  if (self->tp_queue && self->tp_queue[self->tp_qhead]) {
    size_t n = 0, i, N = self->tp_params->tpp_qsize;
Pekka Pessi's avatar
Pekka Pessi committed
988
    for (i = self->tp_qhead; self->tp_queue[i]; i = (i + 1) % N) {
Pekka Pessi's avatar
Pekka Pessi committed
989 990 991 992 993 994 995 996 997 998 999 1000 1001
      msg_destroy(self->tp_queue[i]), self->tp_queue[i] = NULL;
      n++;
    }
    SU_DEBUG_3(("%s(%p): zapped %lu queued messages\n", 
		__func__, self, (LU)n));
  }

  if (self->tp_pused) {
    SU_DEBUG_3(("%s(%p): zapped with pending requests\n", __func__, self));
  }

  mr = self->tp_master;

1002 1003
  tport_stun_server_remove_socket(self);

Pekka Pessi's avatar
Pekka Pessi committed
1004 1005 1006
  if (self->tp_index)
    su_root_deregister(mr->mr_root, self->tp_index);
  self->tp_index = 0;
1007
  if (self->tp_socket != INVALID_SOCKET)
Pekka Pessi's avatar
Pekka Pessi committed
1008
    su_close(self->tp_socket);
1009
  self->tp_socket = INVALID_SOCKET;
Pekka Pessi's avatar
Pekka Pessi committed
1010 1011 1012 1013

  su_home_zap(self->tp_home);
}

1014 1015
/** Create a new reference to a transport object. */
tport_t *tport_ref(tport_t *tp)
Pekka Pessi's avatar
Pekka Pessi committed
1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
{
  if (tp) {
    if (tp->tp_refs >= 0)
      tp->tp_refs++;
    else if (tp->tp_refs == -1)
      tp->tp_refs = 1;
  }
  return tp;
}

1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
/** Destroy reference to a transport object. */
void tport_unref(tport_t *tp)
{
  if (tp && tp->tp_refs > 0)
    if (--tp->tp_refs == 0 && tp->tp_params->tpp_idle == 0)
      if (!tport_is_closed(tp))
	tport_close(tp);
}

/** 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
1041 1042 1043 1044 1045 1046
/** Destroy a transport reference. */
void tport_decref(tport_t **ttp)
{
  assert(ttp);

  if (*ttp) {
1047
    tport_unref(*ttp);
Pekka Pessi's avatar
Pekka Pessi committed
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
    *ttp = NULL;
  }
}

/** Get transport parameters.
 *
 * @param self          pointer to a transport object
 * @param tag,value,... list of tags
 */
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;
Pekka Pessi's avatar
Pekka Pessi committed
1063
  tport_primary_t const *pri = self->tp_pri;
1064
  int connect;
Pekka Pessi's avatar
Pekka Pessi committed
1065 1066

  if (self == NULL)
1067
    return su_seterrno(EINVAL);
Pekka Pessi's avatar
Pekka Pessi committed
1068 1069 1070 1071

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

1072 1073 1074 1075
  connect = tpp->tpp_conn_orient 
    /* Only dgram primary is *not* connection-oriented */
    || !tport_is_primary(self) || !tport_is_dgram(self);

Pekka Pessi's avatar
Pekka Pessi committed
1076 1077 1078
  n = tl_tgets(ta_args(ta),
	       TPTAG_MTU(tpp->tpp_mtu),
	       TPTAG_REUSE(self->tp_reusable),
1079
	       TPTAG_CONNECT(connect),
Pekka Pessi's avatar
Pekka Pessi committed
1080 1081 1082 1083 1084 1085 1086
	       TPTAG_QUEUESIZE(tpp->tpp_qsize),
	       TPTAG_IDLE(tpp->tpp_idle),
	       TPTAG_TIMEOUT(tpp->tpp_timeout),
	       TPTAG_SDWN_ERROR(tpp->tpp_sdwn_error),
	       TPTAG_DEBUG_DROP(tpp->tpp_drop),
	       TPTAG_THRPSIZE(tpp->tpp_thrpsize),
	       TPTAG_THRPRQSIZE(tpp->tpp_thrprqsize),
Pekka Pessi's avatar
Pekka Pessi committed
1087
	       TAG_IF(pri, TPTAG_PUBLIC(pri ? pri->pri_public : 0)),
Pekka Pessi's avatar
Pekka Pessi committed
1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
	       TAG_END());

  ta_end(ta);

  return n;
}

/** Set transport parameters.
 *
 * @param self          pointer to a transport object
 * @param tag,value,... list of tags
 */
int tport_set_params(tport_t *self,
		     tag_type_t tag, tag_value_t value, ...)
{
  ta_list ta;
  int n;
  tport_params_t tpp[1], *tpp0;

1107
  int connect, sdwn_error, reusable, stun_server;
Pekka Pessi's avatar
Pekka Pessi committed
1108 1109 1110 1111

  struct sigcomp_compartment *cc = NONE;

  if (self == NULL)
1112
    return su_seterrno(EINVAL);
Pekka Pessi's avatar
Pekka Pessi committed
1113 1114 1115 1116 1117 1118

  memcpy(tpp, tpp0 = self->tp_params, sizeof *tpp);

  connect = tpp->tpp_conn_orient;
  sdwn_error = tpp->tpp_sdwn_error;
  reusable = self->tp_reusable;
1119
  stun_server = tpp->tpp_stun_server;
Pekka Pessi's avatar
Pekka Pessi committed
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135

  ta_start(ta, tag, value);

  n = tl_gets(ta_args(ta),
	      TPTAG_MTU_REF(tpp->tpp_mtu),
	      TAG_IF(!self->tp_queue, TPTAG_QUEUESIZE_REF(tpp->tpp_qsize)),
	      TPTAG_IDLE_REF(tpp->tpp_idle),
	      TPTAG_TIMEOUT_REF(tpp->tpp_timeout),
	      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),
	      TPTAG_COMPARTMENT_REF(cc),
1136
	      TPTAG_STUN_SERVER_REF(stun_server),
Pekka Pessi's avatar
Pekka Pessi committed
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157
	      TAG_END());

  ta_end(ta);

  if (n == 0)
    return 0;

  if (tpp->tpp_idle > 0 && tpp->tpp_idle < 2000)
    tpp->tpp_idle = 2000;
  if (tpp->tpp_timeout < 1000)
    tpp->tpp_timeout = 1000;
  if (tpp->tpp_drop > 1000)
    tpp->tpp_drop = 1000;
  if (tpp->tpp_thrprqsize > 0)
    tpp->tpp_thrprqsize = tpp0->tpp_thrprqsize;
  if (tpp->tpp_sigcomp_lifetime != 0 && tpp->tpp_sigcomp_lifetime < 30)
    tpp->tpp_sigcomp_lifetime = 30;
  if (tpp->tpp_qsize >= 1000)
    tpp->tpp_qsize = 1000;

  /* Currently only primary UDP transport can *not* be connection oriented */ 
1158
  tpp->tpp_conn_orient = connect; 
1159 1160 1161
  tpp->tpp_sdwn_error = sdwn_error;
  self->tp_reusable = reusable;
  tpp->tpp_stun_server = stun_server;
Pekka Pessi's avatar
Pekka Pessi committed
1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172

  if (tport_is_secondary(self) && 
      self->tp_params == self->tp_pri->pri_primary->tp_params) {
    tpp0 = su_zalloc(self->tp_home, sizeof *tpp0); if (!tpp0) return -1;
  }

  memcpy(tpp0, tpp, sizeof *tpp);

  return n;
}

1173 1174 1175 1176 1177 1178 1179 1180 1181 1182
extern tport_vtable_t const tport_udp_vtable;
extern tport_vtable_t const tport_tcp_vtable;
extern tport_vtable_t const tport_tls_vtable;
extern tport_vtable_t const tport_sctp_vtable;
extern tport_vtable_t const tport_udp_client_vtable;
extern tport_vtable_t const tport_tcp_client_vtable;
extern tport_vtable_t const tport_sctp_client_vtable;
extern tport_vtable_t const tport_tls_client_vtable;
extern tport_vtable_t const tport_http_connect_vtable;
extern tport_vtable_t const tport_threadpool_vtable;
1183 1184 1185 1186 1187

#define TPORT_NUMBER_OF_TYPES 64

tport_vtable_t const *tport_vtables[TPORT_NUMBER_OF_TYPES + 1] =
{
1188
  &tport_http_connect_vtable,
1189
#if HAVE_TLS
1190 1191
  &tport_tls_client_vtable,
  &tport_tls_vtable,
Pekka Pessi's avatar