예제 #1
0
파일: su_os_nw.c 프로젝트: PeterXu/sipstack
/** Register a callback for the network change event.
 *
 * @since New in @VERSION_1_12_2.
 */
su_network_changed_t
*su_root_add_network_changed(su_home_t *home, su_root_t *root,
			     su_network_changed_f *network_changed_cb,
			     su_network_changed_magic_t *magic)
{
  su_network_changed_t *snc = NULL;

  assert(home && root && network_changed_cb && magic);

#if defined (SU_HAVE_NW_CHANGE)
  snc = su_zalloc(home, sizeof *snc);

  if (!snc)
    return NULL;

  snc->su_home = home;
  snc->su_root = root;
  snc->su_network_changed_cb = network_changed_cb;
  snc->su_network_changed_magic = magic;

# if defined (SU_NW_CHANGE_PTHREAD)
  if ((pthread_create(&(snc->su_os_thread), NULL,
		      su_start_nw_os_thread,
		      (void *) snc)) != 0) {
    return NULL;
  }
# endif
#endif
  return snc;
}
예제 #2
0
/**
 * Creates an operation handle and binds it to
 * an existing handle 'nh' (does not create a new nua
 * handle with nua_handle()).
 */
ssc_oper_t *ssc_oper_create_with_handle(ssc_t *ssc,
                                        sip_method_t method,
                                        char const *name,
                                        nua_handle_t *nh,
                                        sip_from_t const *from)
{
    ssc_oper_t *op;

    enter;

    if ((op = su_zalloc(ssc->ssc_home, sizeof(*op)))) {
        op->op_next = ssc->ssc_operations;
        ssc->ssc_operations = op;

        ssc_oper_assign(op, method, name);
        nua_handle_bind(op->op_handle = nh, op);
        op->op_ident = sip_header_as_string(ssc->ssc_home, (sip_header_t*)from);
        op->op_ssc = ssc;
    }
    else {
        printf("%s: cannot create operation object for %s\n",
               ssc->ssc_name, name);
    }

    return op;
}
예제 #3
0
/** @internal Add dialog usage */
nua_dialog_usage_t *nua_dialog_usage_add(nua_owner_t *own,
					 struct nua_dialog_state *ds,
					 nua_usage_class const *uclass,
					 sip_event_t const *event)
{
  if (ds) {
    sip_event_t *o;
    nua_dialog_usage_t *du, **prev_du;

    prev_du = nua_dialog_usage_at(ds, uclass, event);
    du = *prev_du;
    if (du) {		/* Already exists */
      SU_DEBUG_5(("nua(%p): adding already existing %s usage%s%s\n",
		  (void *)own, nua_dialog_usage_name(du),
		  event ? "  with event " : "", event ? event->o_type : ""));

      if (prev_du != &ds->ds_usage) {
	/* Move as a first usage in the list */
	*prev_du = du->du_next;
	du->du_next = ds->ds_usage;
	ds->ds_usage = du;
      }
      return du;
    }

    o = event ? sip_event_dup(own, event) : NULL;

    if (o != NULL || event == NULL)
      du = su_zalloc(own, sizeof *du + uclass->usage_size);

    if (du) {
      su_home_ref(own);

      du->du_dialog = ds;
      du->du_class = uclass;
      du->du_event = o;

      if (uclass->usage_add(own, ds, du) < 0) {
	su_home_unref(own);
	su_free(own, o);
	su_free(own, du);
	return NULL;
      }

      SU_DEBUG_5(("nua(%p): adding %s usage%s%s\n",
		  (void *)own, nua_dialog_usage_name(du),
		  o ? " with event " : "", o ? o->o_type :""));

      du->du_next = ds->ds_usage, ds->ds_usage = du;

      return du;
    }

    su_free(own, o);
  }

  return NULL;
}
예제 #4
0
/**
 * Allocate a su message of given size.
 *
 * Allocate a su message with given data size.
 *
 * @param  rmsg   handle to the new message (may be uninitialized prior calling)
 * @param  size   size of the message data
 *
 * @retval  0 if successful,
 * @retval -1 if message allocation fails.
 *
 * @NEW_1_12_8
 */
int su_msg_new(su_msg_r rmsg, size_t size)
{
    su_msg_t *msg;
    size_t total = sizeof(*msg) + (size_t)size;

    *rmsg = msg = su_zalloc(NULL, (isize_t)total);
    if (!*rmsg)
        return -1;

    msg->sum_size = total;
    return 0;
}
예제 #5
0
/**Create a resolver.
 *
 * The function sres_resolver_create() is used to allocate and initialize
 * the resolver object using the Sofia asynchronous reactor #su_root_t.
 */
sres_resolver_t *
sres_resolver_create(su_root_t *root,
		     char const *conf_file_path,
		     tag_type_t tag, tag_value_t value, ...)
{
  sres_resolver_t *res;
  sres_sofia_t *srs;
  sres_cache_t *cache = NULL;
  ta_list ta;

  if (root == NULL)
    return su_seterrno(EFAULT), (void *)NULL;

  ta_start(ta, tag, value);
  tl_gets(ta_args(ta),
	  SRESTAG_RESOLV_CONF_REF(conf_file_path),
	  SRESTAG_CACHE_REF(cache),
	  TAG_END());
  ta_end(ta);

  res = sres_resolver_new_with_cache(conf_file_path, cache, NULL);
  srs = res ? su_zalloc(0, sizeof *srs) : NULL;

  if (res && srs) {
    su_timer_t *t;

    srs->srs_resolver = res;
    srs->srs_root = root;
    srs->srs_socket = INVALID_SOCKET;

    sres_resolver_set_async(res, sres_sofia_update, srs, 0);

    t = su_timer_create(su_root_task(root), SRES_RETRANSMIT_INTERVAL);
    srs->srs_timer = t;

    if (!srs->srs_timer)
      SU_DEBUG_3(("sres: cannot create timer\n"));
#if nomore
    else if (su_timer_set_for_ever(t, sres_sofia_timer, srs) < 0)
      SU_DEBUG_3(("sres: cannot set timer\n"));
#else
    else if (sres_resolver_set_timer_cb(res, sres_sofia_set_timer, srs) < 0)
      SU_DEBUG_3(("sres: cannot set timer cb\n"));
#endif
    else
      return res;		/* Success! */

    sres_resolver_destroy(res), res = NULL;
  }

  return res;
}
예제 #6
0
/** Decode (parse) Set-Cookie header */
issize_t http_set_cookie_d(su_home_t *home, msg_header_t *h, char *s, isize_t slen)
{
  msg_header_t **hh = &h->sh_succ, *h0 = h;
  http_set_cookie_t *sc = (http_set_cookie_t *)h;
  msg_param_t *params;

  assert(h); assert(sizeof(*h));

  for (;*s;) {
    /* Ignore empty entries (comma-whitespace) */
    if (*s == ',') { *s++ = '\0'; skip_lws(&s); continue; }

    if (!h) {      /* Allocate next header structure */
      if (!(h = msg_header_alloc(home, h0->sh_class, 0)))
	return -1;
      *hh = h; h->sh_prev = hh; hh = &h->sh_succ;
      sc = sc->sc_next = (http_set_cookie_t *)h;
    }

    /* "Set-Cookie:" 1#(NAME "=" VALUE *(";" cookie-av))) */
    params = su_zalloc(home, MSG_PARAMS_NUM(1) * sizeof(msg_param_t));
    if (!params)
      return -1;

    params[0] = s, sc->sc_params = params;
    s += strcspn(s, ",;" LWS);

    if (*s) {
      *s++ = '\0';
      skip_lws(&s);
      if (*s && msg_any_list_d(home, &s, (msg_param_t **)&sc->sc_params,
			       set_cookie_scanner, ';') == -1)
	return -1;
    }

    if (*s != '\0' && *s != ',')
      return -1;

    if (sc->sc_params)
      http_set_cookie_update(sc);

    h = NULL;
  }

  return 0;
}
예제 #7
0
forwarder_t *forwarder_create(proxy_t *pr)
{
  forwarder_t *f;

  assert(pr);

  f = su_zalloc(pr->pr_home, sizeof (*f));

  if (f) {
    f->f_socket = INVALID_SOCKET;
    su_wait_init(f->f_wait);
    su_wait_init(f->f_wait + 1);
    f->f_pr = pr;
    if ((f->f_next = pr->pr_forwarders))
      f->f_next->f_prev = &f->f_next;
    f->f_prev = &pr->pr_forwarders;
    pr->pr_forwarders = f;
  }

  return f;
}
예제 #8
0
/** Create a port using /dev/poll or poll().
 */
su_port_t *su_devpoll_port_create(void)
{
  su_port_t *self;
  int devpoll = open("/dev/poll", O_RDWR);

  if (devpoll == -1) {
    /* Fallback to poll() */
    SU_DEBUG_3(("%s(): open(\"%s\") => %u: %s\n",
		"su_devpoll_port_create", "/dev/poll",
		errno, strerror(errno)));
    return su_poll_port_create();
  }

  self = su_home_new(sizeof *self);
  if (!self) {
    close(devpoll);
    return self;
  }

  if (su_home_destructor(su_port_home(self), su_devpoll_port_deinit) < 0 ||
      !(self->sup_indices =
	su_zalloc(su_port_home(self),
		  (sizeof self->sup_indices[0]) *
		  (self->sup_size_indices = 64)))) {
    su_home_unref(su_port_home(self));
    close(devpoll);
    return NULL;
  }

  self->sup_devpoll = devpoll;
  self->sup_multishot = SU_ENABLE_MULTISHOT_POLL;

  if (su_socket_port_init(self->sup_base, su_devpoll_port_vtable) < 0)
    return su_home_unref(su_port_home(self)), NULL;

  SU_DEBUG_9(("%s(%p): devpoll_create() => %u: %s\n",
	      "su_port_create", (void *)self, self->sup_devpoll, "OK"));

  return self;
}
예제 #9
0
/** Upgrade m= lines within session */
static
int soa_sdp_upgrade(soa_session_t *ss,
		    su_home_t *home,
		    sdp_session_t *session,
		    sdp_session_t const *user,
		    sdp_session_t const *remote,
		    int **return_u2s,
		    int **return_s2u)
{
  soa_static_session_t *sss = (soa_static_session_t *)ss;

  int Ns, Nu, Nr, Nmax, n, i, j;
  sdp_media_t *m, **mm, *um;
  sdp_media_t **s_media, **o_media, **u_media;
  sdp_media_t const *rm, **r_media;
  int *u2s = NULL, *s2u = NULL;

  if (session == NULL || user == NULL)
    return (errno = EFAULT), -1;

  Ns = sdp_media_count(session, sdp_media_any, (sdp_text_t)0, (sdp_proto_e)0, (sdp_text_t)0);
  Nu = sdp_media_count(user, sdp_media_any, (sdp_text_t)0, (sdp_proto_e)0, (sdp_text_t)0);
  Nr = sdp_media_count(remote, sdp_media_any, (sdp_text_t)0, (sdp_proto_e)0, (sdp_text_t)0);

  if (remote == NULL)
    Nmax = Ns + Nu;
  else if (Ns < Nr)
    Nmax = Nr;
  else
    Nmax = Ns;

  s_media = su_zalloc(home, (Nmax + 1) * (sizeof *s_media));
  o_media = su_zalloc(home, (Ns + 1) * (sizeof *o_media));
  u_media = su_zalloc(home, (Nu + 1) * (sizeof *u_media));
  r_media = su_zalloc(home, (Nr + 1) * (sizeof *r_media));
  if (!s_media || !o_media || !u_media || !r_media)
    return -1;

  um = sdp_media_dup_all(home, user->sdp_media, session);
  if (!um && user->sdp_media)
    return -1;

  u2s = su_alloc(home, (Nu + 1) * sizeof(*u2s));
  s2u = su_alloc(home, (Nmax + 1) * sizeof(*s2u));
  if (!u2s || !s2u)
    return -1;

  for (i = 0; i < Nu; i++)
    u2s[i] = U2S_NOT_USED;
  u2s[Nu] = U2S_SENTINEL;

  for (i = 0; i < Nmax; i++)
    s2u[i] = U2S_NOT_USED;
  s2u[Nmax] = U2S_SENTINEL;

  for (i = 0, m = session->sdp_media; m && i < Ns; m = m->m_next)
    o_media[i++] = m;
  assert(i == Ns);
  for (i = 0, m = um; m && i < Nu; m = m->m_next)
    u_media[i++] = m;
  assert(i == Nu);
  m = remote ? remote->sdp_media : NULL;
  for (i = 0; m && i < Nr; m = m->m_next)
      r_media[i++] = m;
  assert(i == Nr);

  if (sss->sss_ordered_user && sss->sss_u2s) {     /* User SDP is ordered */
    for (j = 0; sss->sss_u2s[j] != U2S_SENTINEL; j++) {
      i = sss->sss_u2s[j];
      if (i == U2S_NOT_USED)
	continue;
      if (j >= Nu) /* lines removed from user SDP */
	continue;
	  if (i >= Ns) /* I should never be called but somehow i and Ns are 0 here sometimes */
    continue;
      s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE;
      u2s[j] = i, s2u[i] = j;
    }
  }

  if (remote) {
    /* Update session according to remote */
    for (i = 0; i < Nr; i++) {
      rm = r_media[i];
      m = s_media[i];

      if (!m) {
	int codec_mismatch = 0;

	if (!rm->m_rejected)
	  j = soa_sdp_matching_mindex(ss, u_media, rm, &codec_mismatch);
	else
	  j = -1;

	if (j == -1) {
	  s_media[i] = soa_sdp_make_rejected_media(home, rm, session, 0);
	  continue;
	}
	else if (codec_mismatch && !ss->ss_rtp_mismatch) {
	  m = soa_sdp_make_rejected_media(home, u_media[j], session, 1);
	  soa_sdp_set_rtpmap_pt(s_media[i] = m, rm);
	  continue;
	}

	s_media[i] = m = u_media[j]; u_media[j] = SDP_MEDIA_NONE;
	u2s[j] = i, s2u[i] = j;
      }

      if (sdp_media_uses_rtp(rm))
	soa_sdp_media_upgrade_rtpmaps(ss, m, rm);
    }
  }
  else {

    if (sss->sss_ordered_user) {
      /* Update session with unused media in u_media */

      if (!sss->sss_reuse_rejected) {
	/* Mark previously used slots */
	for (i = 0; i < Ns; i++) {
	  if (s_media[i])
	    continue;
	  s_media[i] =
	    soa_sdp_make_rejected_media(home, o_media[i], session, 0);
	}
      }

      for (j = 0; j < Nu; j++) {
	if (u_media[j] == SDP_MEDIA_NONE)
	  continue;

	for (i = 0; i < Nmax; i++) {
	  if (s_media[i] == NULL) {
	    s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE;
	    u2s[j] = i, s2u[i] = j;
	    break;
	  }
	}

	assert(i != Nmax);
      }
    }

    /* Match unused user media by media types with the existing session */
    for (i = 0; i < Ns; i++) {
      if (s_media[i])
	continue;

      j = soa_sdp_matching_mindex(ss, u_media, o_media[i], NULL);
      if (j == -1) {
	s_media[i] = soa_sdp_make_rejected_media(home, o_media[i], session, 0);
	continue;
      }

      s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE;
      u2s[j] = i, s2u[i] = j;
    }

    /* Here we just append new media at the end */
    for (j = 0; j < Nu; j++) {
      if (u_media[j] != SDP_MEDIA_NONE) {
	s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE;
	u2s[j] = i, s2u[i] = j;
	i++;
      }
    }
    assert(i <= Nmax);
  }

  mm = &session->sdp_media;
  for (i = 0; s_media[i]; i++) {
    m = s_media[i]; *mm = m; mm = &m->m_next;
  }
  *mm = NULL;

  s2u[n = i] = U2S_SENTINEL;
  *return_u2s = u2s;
  *return_s2u = s2u;

#ifndef NDEBUG			/* X check */
  for (j = 0; j < Nu; j++) {
    i = u2s[j];
    assert(i == U2S_NOT_USED || s2u[i] == j);
  }
  for (i = 0; i < n; i++) {
    j = s2u[i];
    assert(j == U2S_NOT_USED || u2s[j] == i);
  }
#endif

  return 0;
}
예제 #10
0
/** Get a list of matching records from cache. */
int sres_cache_get(sres_cache_t *cache,
		   uint16_t type,
		   char const *domain,
		   sres_record_t ***return_cached)
{
  sres_record_t **result = NULL;
  sres_rr_hash_entry_t **slot;
  int result_size, i, j;
  unsigned hash;
  time_t now;
  char b[8];

  if (!domain || !return_cached)
    return -1;

  *return_cached = NULL;

  SU_DEBUG_9(("%s(%p, %s, \"%s\") called\n", "sres_cache_get",
	      (void *)cache, sres_record_type(type, b), domain));

  hash = sres_hash_key(domain);

  if (!LOCK(cache))
    return -1;

  time(&now);

  /* First pass: just count the number of rr:s for array allocation */
  slot = sres_htable_hash(cache->cache_hash, hash);

  i = sres_cache_get0(cache->cache_hash, slot, type, domain, now,
		      NULL, 0, NULL);
  if (i <= 0) {
    UNLOCK(cache);
    return 0;
  }

  result_size = (sizeof *result) * (i + 1);
  result = su_zalloc(cache->cache_home, result_size);
  if (result == NULL) {
    UNLOCK(cache);
    return -1;
  }

  /* Second pass: add the rr pointers to the allocated array */
  j = sres_cache_get0(cache->cache_hash, slot, type, domain, now,
		      result, i, NULL);
  if (i != j) {
    /* Uh-oh. */
    SU_DEBUG_9(("%s(%p, %s, \"%s\") got %d != %d\n", "sres_cache_get",
		(void *)cache, sres_record_type(type, b), domain, i, j));
    for (i = 0; i < result_size; i++) {
      if (result[i])
	result[i]->sr_refcount--;
    }
    su_free(cache->cache_home, result);
    return 0;
  }

  result[i] = NULL;

  UNLOCK(cache);

  SU_DEBUG_9(("%s(%p, %s, \"%s\") returned %d entries\n", "sres_cache_get",
	      (void *)cache, sres_record_type(type, b), domain, i));

  *return_cached = result;

  return i;
}
예제 #11
0
/**
 * Creates a new operation object and stores it the list of
 * active operations for 'cli'.
 */
ssc_oper_t *ssc_oper_create(ssc_t *ssc,
                            sip_method_t method,
                            char const *name,
                            char const *address,
                            tag_type_t tag, tag_value_t value, ...)
{
    ssc_oper_t *op, *old;

    ta_list ta;

    enter;

    for (old = ssc->ssc_operations; old; old = old->op_next)
        if (!old->op_persistent)
            break;

    if (address) {
        int have_url = 1;
        sip_to_t *to;

        to = sip_to_make(ssc->ssc_home, address);

        if (to == NULL) {
            printf("%s: %s: invalid address: %s\n", ssc->ssc_name, name, address);
            return NULL;
        }

        /* Try to make sense out of the URL */
        if (url_sanitize(to->a_url) < 0) {
            printf("%s: %s: invalid address\n", ssc->ssc_name, name);
            return NULL;
        }

        if (!(op = su_zalloc(ssc->ssc_home, sizeof(*op)))) {
            printf("%s: %s: cannot create handle\n", ssc->ssc_name, name);
            return NULL;
        }

        op->op_next = ssc->ssc_operations;
        op->op_prev_state = -1;
        op->op_ssc = ssc;
        ssc->ssc_operations = op;

        if (method == sip_method_register)
            have_url = 0;

        ta_start(ta, tag, value);

        op->op_handle = nua_handle(ssc->ssc_nua, op,
                                   TAG_IF(have_url, NUTAG_URL(to->a_url)),
                                   SIPTAG_TO(to),
                                   ta_tags(ta));

        ta_end(ta);

        op->op_ident = sip_header_as_string(ssc->ssc_home, (sip_header_t *)to);

        ssc_oper_assign(op, method, name);

        if (!op->op_persistent) {
            ssc_oper_t *old_next;
            for (; old; old = old_next) {      /* Clean old handles */
                old_next = old->op_next;
                if (!old->op_persistent && !old->op_callstate)
                    ssc_oper_destroy(ssc, old);
            }
        }

        su_free(ssc->ssc_home, to);
    }
    else if (method || name)
        ssc_oper_assign(op = old, method, name);
    else
        return old;

    if (!op) {
        if (address)
            printf("%s: %s: invalid destination\n", ssc->ssc_name, name);
        else
            printf("%s: %s: no destination\n", ssc->ssc_name, name);
        return NULL;
    }

    return op;
}
예제 #12
0
/**Create a client request.
 *
 * @retval 0 if request is pending
 * @retval > 0 if error event has been sent
 * @retval < 0 upon an error
 */
int nua_client_create(nua_handle_t *nh,
		      int event,
		      nua_client_methods_t const *methods,
		      tagi_t const * const tags)
{
  su_home_t *home = nh->nh_home;
  nua_client_request_t *cr;
  sip_method_t method;
  char const *name;

  method = methods->crm_method, name = methods->crm_method_name;
  if (!name) {
    tagi_t const *t = tl_find_last(tags, nutag_method);
    if (t)
      name = (char const *)t->t_value;
  }

  cr = su_zalloc(home, sizeof *cr + methods->crm_extra);
  if (!cr) {
    return nua_stack_event(nh->nh_nua, nh,
			   NULL,
			   (enum nua_event_e)event,
			   NUA_ERROR_AT(__FILE__, __LINE__),
			   NULL);
  }

  cr->cr_methods = methods;
  cr->cr_event = event;
  cr->cr_method = method;
  cr->cr_method_name = name;
  cr->cr_contactize = methods->crm_flags.target_refresh;
  cr->cr_dialog = methods->crm_flags.create_dialog;
  cr->cr_auto = 1;

  if (su_msg_is_non_null(nh->nh_nua->nua_signal)) {
    nua_event_data_t *e = su_msg_data(nh->nh_nua->nua_signal)->ee_data;

    if (tags == e->e_tags && event == e->e_event) {
      cr->cr_auto = 0;

      if (tags) {
	nua_move_signal(cr->cr_signal, nh->nh_nua->nua_signal);
	if (cr->cr_signal) {
	  /* Steal reference from signal */
	  cr->cr_owner = e->e_nh, e->e_nh = NULL;
	  cr->cr_tags = tags;
	}
      }
    }
  }

  if (cr->cr_owner == NULL)
    cr->cr_owner = nua_handle_ref(nh);

  if (tags && cr->cr_tags == NULL)
    cr->cr_tags = tl_tlist(nh->nh_home, TAG_NEXT(tags));

#if HAVE_MEMLEAK_LOG
  SU_DEBUG_0(("%p %s() for %s\n", cr, __func__, cr->cr_methods->crm_method_name));
#endif

  if (nua_client_request_queue(cr))
    return 0;

  return nua_client_init_request(cr);
}
예제 #13
0
/** Test basic memory home operations  */
static int test_alloc(void)
{
  exhome_t *h0, *h1, *h2, *h3;
  su_home_t home[1] = { SU_HOME_INIT(home) };
  su_home_t home0[1];
  enum { N = 40 };
  void *m0[N], *m1[N], *m;
  char *c, *c0, *p0, *p1;
  int i;
  enum { destructed_once = 1 };
  int d0, d1a, d1, d2, d3;

  BEGIN();

  /* su_home_init() was not initializing suh_locks */
  memset(home0, 0xff, sizeof home0);
  TEST(su_home_init(home0), 0);
  TEST_VOID(su_home_deinit(home0));

  TEST_1(h0 = su_home_new(sizeof(*h0)));
  TEST_1(h1 = su_home_clone(h0->home, sizeof(*h1)));

  d0 = d1a = d1 = d2 = d3 = 0;
  h0->p = &d0; h1->p = &d1a;
  TEST(su_home_destructor(h0->home, exdestructor), 0);
  TEST(su_home_destructor(h1->home, exdestructor), 0);

  TEST_1(h2 = su_home_ref(h0->home));
  su_home_unref(h0->home);
  TEST(d0, 0);

  for (i = 0; i < 128; i++)
    TEST_1(su_alloc(h0->home, 16));

  for (i = 0; i < 128; i++)
    TEST_1(su_alloc(h1->home, 16));

  su_home_unref(h1->home);
  TEST(d1a, destructed_once);

  TEST_1(h1 = su_home_clone(h0->home, sizeof(*h1)));
  TEST(su_home_destructor(h1->home, exdestructor), 0);
  h1->p = &d1;

  for (i = 0; i < 128; i++)
    TEST_1(su_alloc(h1->home, 16));

  for (i = 0; i < 128; i++)
    TEST_1(su_alloc(h2->home, 16));

  su_home_unref(h2->home); /* Should call destructor of cloned home, too */

  TEST(d0, destructed_once);
  TEST(d1, destructed_once);

  TEST_1(h0 = su_home_new(sizeof(*h0)));
  TEST_1(h1 = su_home_clone(h0->home, sizeof(*h1)));
  TEST_1(h2 = su_home_clone(h1->home, sizeof(*h2)));
  TEST_1(h3 = su_home_clone(h2->home, sizeof(*h3)));

  TEST(su_home_threadsafe(h0->home), 0);

  for (i = 0; i < N; i++) {
    TEST_1(m0[i] = su_zalloc(h3->home, 20));
    TEST_1(m1[i] = su_zalloc(h2->home, 20));
  }

  TEST_1(m = su_zalloc(h2->home, 20));

  TEST_1(su_in_home(h2->home, m));
  TEST_1(!su_in_home(h2->home, (char *)m + 1));
  TEST_1(!su_in_home(h2->home, (void *)(intptr_t)su_in_home));
  TEST_1(!su_in_home(h3->home, m));
  TEST_1(!su_in_home(NULL, m));
  TEST_1(!su_in_home(h3->home, NULL));

  TEST(su_home_move(home, NULL), 0);
  TEST(su_home_move(NULL, home), 0);
  TEST(su_home_move(home, h3->home), 0);
  TEST(su_home_move(h2->home, h3->home), 0);
  TEST(su_home_move(h1->home, h2->home), 0);

  su_home_preload(home, 1, 1024 + 2 * 8);

  TEST_1(c = su_zalloc(home, 64)); p0 = c; p1 = c + 1024;
  c0 = c;
  TEST_P(c = su_realloc(home, c0, 127), c0);

  TEST_1(c = c0 = su_zalloc(home, 1024 - 128));
  TEST_1(p0 <= c); TEST_1(c < p1);
  TEST_P(c = su_realloc(home, c, 128), c0);
  TEST_P(c = su_realloc(home, c, 1023 - 128), c0);
  TEST_P(c = su_realloc(home, c, 1024 - 128), c0);
  TEST_1(c = su_realloc(home, c, 1024));
  TEST_1(c = su_realloc(home, c, 2 * 1024));

  TEST_P(c = su_realloc(home, p0, 126), p0);
  TEST_1(c = su_realloc(home, p0, 1024));
  TEST_P(c = su_realloc(home, c, 0), NULL);

  su_home_check(home);
  su_home_deinit(home);

  su_home_check(h2->home);
  su_home_zap(h2->home);
  su_home_check(h0->home);
  su_home_zap(h0->home);

  {
    su_home_t h1[1];

    memset(h1, 0, sizeof h1);

    TEST(su_home_init(h1), 0);
    TEST(su_home_threadsafe(h1), 0);

    TEST_1(su_home_ref(h1));
    TEST_1(su_home_ref(h1));

    TEST(su_home_destructor(h1, test_destructor), 0);

    TEST_1(!su_home_unref(h1));
    TEST_1(!su_home_unref(h1));
    TEST_1(su_home_unref(h1));
    TEST(h1->suh_size, 13);
  }

  /* Test su_home_parent() */
  TEST_1(h0 = su_home_new(sizeof *h0));
  TEST_1(h1 = su_home_clone(h0->home, sizeof *h1));
  TEST_1(h2 = su_home_clone(h1->home, sizeof *h2));
  TEST_1(h3 = su_home_clone(h2->home, sizeof *h3));

  TEST_P(su_home_parent(h0->home), NULL);
  TEST_P(su_home_parent(h1->home), h0);
  TEST_P(su_home_parent(h2->home), h1);
  TEST_P(su_home_parent(h3->home), h2);
  TEST(su_home_move(h0->home, h1->home), 0);
  TEST_P(su_home_parent(h2->home), h0);
  TEST_P(su_home_parent(h3->home), h2);
  TEST(su_home_move(h0->home, h2->home), 0);
  TEST_P(su_home_parent(h3->home), h0);

  su_home_move(NULL, h0->home);

  TEST_P(su_home_parent(h0->home), NULL);
  TEST_P(su_home_parent(h1->home), NULL);
  TEST_P(su_home_parent(h2->home), NULL);
  TEST_P(su_home_parent(h3->home), NULL);

  su_home_unref(h0->home);
  su_home_unref(h1->home);
  su_home_unref(h2->home);
  su_home_unref(h3->home);

  END();
}
예제 #14
0
/** @internal
 *
 *  Register a #su_wait_t object. The wait object, a callback function and
 *  an argument pointer is stored in the port object.  The callback function
 *  will be called when the wait object is signaled.
 *
 *  Please note if identical wait objects are inserted, only first one is
 *  ever signalled.
 *
 * @param self	     pointer to port
 * @param root	     pointer to root object
 * @param waits	     pointer to wait object
 * @param callback   callback function pointer
 * @param arg	     argument given to callback function when it is invoked
 * @param priority   relative priority of the wait object
 *              (0 is normal, 1 important, 2 realtime)
 *
 * @return
 *   Positive index of the wait object,
 *   or -1 upon an error.
 */
int su_devpoll_port_register(su_port_t *self,
			     su_root_t *root,
			     su_wait_t *wait,
			     su_wakeup_f callback,
			     su_wakeup_arg_t *arg,
			     int priority)
{
  int i, j, n;
  struct su_devpoll *ser;
  struct su_devpoll **indices = self->sup_indices;
  struct su_devpoll **devpoll_by_socket = self->sup_devpoll_by_socket;
  su_home_t *h = su_port_home(self);
  struct pollfd pollfd[1];

  assert(su_port_own_thread(self));

  if (wait->fd < 0)
    return su_seterrno(EINVAL);

  n = self->sup_size_indices;

  if (n >= SU_WAIT_MAX)
    return su_seterrno(ENOMEM);

  ser = indices[0];

  if (!ser) {
    i = self->sup_max_index, j = i == 0 ? 15 : i + 16;

    if (j >= self->sup_size_indices) {
      /* Reallocate index table */
      n = n < 1024 ? 2 * n : n + 1024;
      indices = su_realloc(h, indices, n * sizeof(indices[0]));
      if (!indices)
	return -1;
      self->sup_indices = indices;
      self->sup_size_indices = n;
    }

    /* Allocate registrations */
    ser = su_zalloc(h, (j - i) * (sizeof *ser));
    if (!ser)
      return -1;

    indices[0] = ser;

    for (i++; i <= j; i++) {
      ser->ser_id = i;
      ser->ser_next = i < j ? ser + 1 : NULL;
      indices[i] = ser++;
    }

    self->sup_max_index = j;

    ser = indices[0];
  }

  if ((size_t)wait->fd >= self->sup_n_devpoll_by_socket) {
    size_t n_devpoll_by_socket = ((size_t)wait->fd + 32) / 32 * 32;

    devpoll_by_socket = su_realloc(h, devpoll_by_socket,
				   n_devpoll_by_socket *
				   (sizeof devpoll_by_socket[0]));
    if (devpoll_by_socket == NULL)
      return -1;

    memset(&devpoll_by_socket[self->sup_n_devpoll_by_socket],
	   0,
	   (char *)&devpoll_by_socket[n_devpoll_by_socket] -
	   (char *)&devpoll_by_socket[self->sup_n_devpoll_by_socket]);

    self->sup_devpoll_by_socket = devpoll_by_socket;
    self->sup_n_devpoll_by_socket = n_devpoll_by_socket;
  }


  if (devpoll_by_socket[wait->fd])
    /* XXX - we should lift this limitation with epoll, too */
    return errno = EEXIST, -1;

  i = ser->ser_id;

  pollfd->fd = wait->fd;
  pollfd->events = wait->events & ~POLLREMOVE;
  pollfd->revents = 0;

  if (write(self->sup_devpoll, pollfd, (sizeof pollfd)) != (sizeof pollfd)) {
    return errno = EIO, -1;
  }

  indices[0] = ser->ser_next;
  devpoll_by_socket[wait->fd] = ser;

  ser->ser_next = NULL;
  *ser->ser_wait = *wait;
  ser->ser_cb = callback;
  ser->ser_arg = arg;
  ser->ser_root = root;

  self->sup_registers++;
  self->sup_n_registrations++;

  return i;			/* return index */
}
예제 #15
0
/** Read authentication database */
static
int auth_readdb_internal(auth_mod_t *am, int always)
{
    FILE *f;
    char *data, *s;
    size_t len, i, n, N;
    ssize_t slen;
    auth_passwd_t *apw;

    if (!am->am_stat)
        am->am_stat = su_zalloc(am->am_home, sizeof (*am->am_stat));

    f = fopen(am->am_db, "rb");

    if (f) {
        void *buffer = NULL;
        auth_passwd_t *fresh = NULL;

#if HAVE_FLOCK
        int locked;

        /* Obtain shared lock on the database file */
        if (flock(fileno(f), LOCK_SH | (always ? 0 : LOCK_NB)) == 0) {
            locked = 1;
        } else {
            locked = 0;

            if (errno == ENOLCK) {
                ;
            }
            else if (errno == EWOULDBLOCK) {
                SU_DEBUG_3(("auth(%s): user file \"%s\" is busy, trying again later\n",
                            am->am_scheme->asch_method, am->am_db));
                fclose(f);
                return always ? -1 : 0;
            }
            else {
                SU_DEBUG_3(("auth(%s): flock(\"%s\"): %s (%u)\n",
                            am->am_scheme->asch_method, am->am_db,
                            strerror(errno), errno));
                fclose(f);
                return always ? -1 : 0;
            }
        }
#endif
        if (am->am_stat)
            stat(am->am_db, am->am_stat); /* too bad if this fails */

        slen = readfile(am->am_home, f, &buffer, 1);

#if HAVE_FLOCK
        /* Release shared lock on the database file */
        if (locked && flock(fileno(f), LOCK_UN) == -1) {
            SU_DEBUG_0(("auth(%s): un-flock(\"%s\"): %s (%u)\n",
                        am->am_scheme->asch_method, am->am_db, strerror(errno), errno));
            fclose(f);
            return -1;
        }
#endif

        fclose(f);

        if (slen < 0)
            return -1;
        len = (size_t)slen;

        /* Count number of entries in new buffer */
        for (i = am->am_anonymous, s = data = buffer;
                s < data + len;
                s += n + strspn(s + n, "\r\n")) {
            n = strcspn(s, "\r\n");
            if (*s != '#' && *s != '\n' && *s != '\r')
                i++;
        }

        N = i, i = 0;

        if (N > 0) {
            size_t size = (N * 5 + 3) / 4;
            if (auth_htable_resize(am->am_home, am->am_users, size) < 0 ||
                    !(fresh = su_zalloc(am->am_home, sizeof(*fresh) * N))) {
                su_free(am->am_home, buffer);
                return -1;
            }
        }

        if (am->am_anonymous) {
            assert(i < N);

            apw = fresh + i++;

            apw->apw_index = msg_hash_string("anonymous");
            apw->apw_user = "******";
            apw->apw_pass = "";
            apw->apw_realm = "";

            am->am_anon_user = apw;

            if (auth_htable_is_full(am->am_users))
                auth_htable_resize(am->am_home, am->am_users, 0);

            auth_htable_append_local(am->am_users, apw);
        }

        apw = NULL;

        for (data = buffer, s = data;
                s < data + len && i < N;
                s += n + strspn(s + n, "\r\n")) {
            char *user, *pass, *realm, *ident;

            n = strcspn(s, "\r\n");
            if (*s == '#')
                continue;

            user = s;
            s[n++] = '\0';
            if (!(pass = strchr(user, ':')))
                continue;

            *pass++ = '\0';
            if (!*pass || !*user)
                continue;

            if ((realm = strchr(pass, ':')))
                *realm++ = '\0';
            else
                realm = "";

            if ((ident = strchr(realm, ':')))
                *ident++ = '\0';
            else
                ident = "";

            apw = fresh + i++;

            apw->apw_index = msg_hash_string(user);
            apw->apw_user = user;
            apw->apw_ident = ident;

            /* Check for htdigest format */
            if (span_hexdigit(realm) == 32 && realm[32] == '\0') {
                apw->apw_realm = pass;
                apw->apw_hash = realm;
            } else {
                apw->apw_pass = pass;
                apw->apw_realm = realm;
            }

            if (auth_htable_is_full(am->am_users))
                auth_htable_resize(am->am_home, am->am_users, 0);

            auth_htable_append_local(am->am_users, apw);
        }

        assert(i <= N);
        N = i;

        /* Remove from hash those entries that were read from old passwd file */
        for (i = 0; i < am->am_local_count; i++) {
            if (am->am_locals[i].apw_type == auth_apw_local)
                auth_htable_remove(am->am_users, &am->am_locals[i]);
        }

        if (am->am_locals)
            su_free(am->am_home, am->am_locals); /* Free old entries */
        if (am->am_buffer)
            su_free(am->am_home, am->am_buffer); /* Free old passwd file contents */

        SU_DEBUG_5(("auth(%s): read %u entries from \"%s\"\n",
                    am->am_scheme->asch_method, (unsigned)N, am->am_db));

        am->am_locals = fresh;
        am->am_local_count = N;
        am->am_buffer = buffer;

        return 0;
    }

    return -1;
}
예제 #16
0
static
void nth_site_request(server_t *srv,
		      nth_site_t *site,
		      tport_t *tport,
		      msg_t *request,
		      http_t *http,
		      char const *path,
		      msg_t *response)
{
  auth_mod_t *am = site->site_auth;
  nth_request_t *req;
  auth_status_t *as;
  struct auth_info *ai;
  size_t size = (am ? (sizeof *as) + (sizeof *ai) : 0) + (sizeof *req);
  int status;

  req = su_zalloc(srv->srv_home, size);

  if (req == NULL) {
    server_reply(srv, tport, request, response, HTTP_500_INTERNAL_SERVER);
    return;
  }

  if (am)
    as = auth_status_init(req + 1, sizeof *as), ai = (void *)(as + 1);
  else
    as = NULL, ai = NULL;

  req->req_server = srv;
  req->req_method = http->http_request->rq_method;
  req->req_method_name = http->http_request->rq_method_name;
  req->req_url = http->http_request->rq_url;
  req->req_version = http->http_request->rq_version;

  req->req_tport = tport_incref(tport);
  req->req_request = request;
  req->req_response = response;

  req->req_status = 100;
  req->req_close =
    !srv->srv_persistent
    || http->http_request->rq_version != http_version_1_1
    || (http->http_connection &&
	msg_params_find(http->http_connection->k_items, "close"));

  if (am) {
    static auth_challenger_t const http_server_challenger[] =
      {{ HTTP_401_UNAUTHORIZED, http_www_authenticate_class }};

    req->req_as = as;

    as->as_method = http->http_request->rq_method_name;
    as->as_uri = path;

    if (http->http_payload) {
      as->as_body = http->http_payload->pl_data;
      as->as_bodylen = http->http_payload->pl_len;
    }

    auth_mod_check_client(am, as,
			  http->http_authorization,
			  http_server_challenger);

    if (as->as_status == 100) {
      /* Stall transport - do not read more requests */
      if (tport_queuelen(tport) * 2 >= srv->srv_queuesize)
	tport_stall(tport);

      as->as_callback = nth_authentication_result;
      as->as_magic = ai;
      ai->site = site;
      ai->req = req;
      ai->http = http;
      ai->path = path;
      return;
    }
    else if (as->as_status) {
      assert(as->as_status >= 200);
      nth_request_treply(req, as->as_status, as->as_phrase,
			 HTTPTAG_HEADER((http_header_t *)as->as_response),
			 HTTPTAG_HEADER((http_header_t *)as->as_info),
			 TAG_END());
      nth_request_destroy(req);
      return;
    }
  }

  req->req_in_callback = 1;
  status = site->site_callback(site->site_magic, site, req, http, path);
  req->req_in_callback = 0;

  if (status != 0 && (status < 100 || status >= 600))
    status = 500;

  if (status != 0 && req->req_status < 200) {
    nth_request_treply(req, status, NULL, TAG_END());
  }

  if (req->req_status < 100) {
    /* Stall transport - do not read more requests */
    if (tport_queuelen(tport) * 2 >= srv->srv_queuesize)
      tport_stall(tport);
  }

  if (status >= 200 || req->req_destroyed)
    nth_request_destroy(req);
}
예제 #17
0
/** Create a http site object.
 *
 * The function nth_site_create() allocates and initializes a web site
 * object. A web site object can be either
 * - a primary http server (@a parent is NULL),
 * - a virtual http server (@a address contains hostpart), or
 * - a site within a server
 *   (@a address does not have hostpart, only path part).
 *
 * @param parent pointer to parent site
 *               (NULL when creating a primary server object)
 * @param callback pointer to callback function called when
 *                 a request is received
 * @param magic    application context included in callback parameters
 * @param address  absolute or relative URI specifying the address of
 *                 site
 * @param tag, value, ... list of tagged parameters
 *
 * @TAGS
 * If the @a parent is NULL, the list of tagged parameters must contain
 * NTHTAG_ROOT() used to create the server engine. Tags supported when @a
 * parent is NULL are NTHTAG_ROOT(), NTHTAG_MCLASS(), TPTAG_REUSE(),
 * HTTPTAG_SERVER(), and HTTPTAG_SERVER_STR(). All the tags are passed to
 * tport_tcreate() and tport_tbind(), too.
 *
 * @since Support for multiple sites was added to @VERSION_1_12_4
 */
nth_site_t *nth_site_create(nth_site_t *parent,
			    nth_request_f *callback,
			    nth_site_magic_t *magic,
			    url_string_t const *address,
			    tag_type_t tag, tag_value_t value,
			    ...)
{
  nth_site_t *site = NULL, **prev = NULL;
  su_home_t home[SU_HOME_AUTO_SIZE(256)];
  url_t *url, url0[1];
  server_t *srv = NULL;
  ta_list ta;
  char *path = NULL;
  size_t usize;
  int is_host, is_path, wildcard = 0;

  su_home_auto(home, sizeof home);

  if (parent && url_is_string(address)) {
    char const *s = (char const *)address;
    size_t sep = strcspn(s, "/:");

    if (parent->site_path) {
      /* subpath */
      url_init(url0, (enum url_type_e)parent->site_url->url_type);
      url0->url_path = s;
      address = (url_string_t*)url0;
    }
    else if (s[sep] == ':')
      /* absolute URL with scheme */;
    else if (s[sep] == '\0' && strchr(s, '.') && host_is_valid(s)) {
      /* looks like a domain name */;
      url_init(url0, (enum url_type_e)parent->site_url->url_type);
      url0->url_host = s;
      address = (url_string_t*)url0;
    }
    else {
      /* looks like a path */
      url_init(url0, (enum url_type_e)parent->site_url->url_type);
      url0->url_path = s;
      address = (url_string_t*)url0;
    }
  }

  url = url_hdup(home, address->us_url);

  if (!url || !callback)
    return NULL;

  is_host = url->url_host != NULL;
  is_path = url->url_path != NULL;

  if (is_host && is_path) {
    SU_DEBUG_3(("nth_site_create(): virtual host and path simultanously\n"));
    errno = EINVAL;
    goto error;
  }

  if (!parent && !is_host) {
    SU_DEBUG_3(("nth_site_create(): host is required\n"));
    errno = EINVAL;
    goto error;
  }

  if (parent) {
    if (!parent->site_isdir) {
      SU_DEBUG_3(("nth_site_create(): invalid parent resource \n"));
      errno = EINVAL;
      goto error;
    }

    srv = parent->site_server; assert(srv);
    if (is_host) {
      prev = site_get_host(&srv->srv_sites, url->url_host, url->url_port);

      if (prev == NULL) {
	SU_DEBUG_3(("nth_site_create(): host %s:%s already exists\n",
		    url->url_host, url->url_port ? url->url_port : ""));
	errno = EEXIST;
	goto error;
      }
    }
    else {
      size_t i, j;

      path = (char *)url->url_path;
      while (path[0] == '/')
	path++;

      /* Remove duplicate // */
      for (i = j = 0; path[i];) {
	while (path[i] == '/' && path[i + 1] == '/')
	  i++;
	path[j++] = path[i++];
      }
      path[j] = path[i];

      url = url0, *url = *parent->site_url;

      if (url->url_path) {
	url->url_path = su_strcat(home, url->url_path, path);
	if (!url->url_path)
	  goto error;
	path = (char *)url->url_path + strlen(parent->site_url->url_path);
      }
      else
	url->url_path = path;

      prev = site_get_rslot(parent, path, &path);

      if (!prev || path[0] == '\0') {
	SU_DEBUG_3(("nth_site_create(): directory \"%s\" already exists\n",
		    url->url_path));
	errno = EEXIST;
	goto error;
      }
    }
  }

  if (!parent) {
    if (strcmp(url->url_host, "*") == 0 ||
	host_cmp(url->url_host, "0.0.0.0") == 0 ||
	host_cmp(url->url_host, "::") == 0)
      wildcard = 1, url->url_host = "*";
  }

  usize = sizeof(*url) + url_xtra(url);

  ta_start(ta, tag, value);

  if (!parent) {
    srv = server_create(url, ta_tags(ta));
    prev = &srv->srv_sites;
  }

  if (srv && (site = su_zalloc(srv->srv_home, (sizeof *site) + usize))) {
    site->site_url = (url_t *)(site + 1);
    url_dup((void *)(site->site_url + 1), usize - sizeof(*url),
	    site->site_url, url);

    assert(prev);
    if ((site->site_next = *prev))
      site->site_next->site_prev = &site->site_next;
    *prev = site, site->site_prev = prev;
    site->site_server = srv;

    if (path) {
      size_t path_len;

      site->site_path = site->site_url->url_path + (path - url->url_path);
      path_len = strlen(site->site_path); assert(path_len > 0);
      if (path_len > 0 && site->site_path[path_len - 1] == '/')
	path_len--, site->site_isdir = 1;
      site->site_path_len = path_len;
    }
    else {
      site->site_isdir = is_host;
      site->site_path = "";
      site->site_path_len = 0;
    }

    site->site_wildcard = wildcard;
    site->site_callback = callback;
    site->site_magic = magic;

    if (parent)
      site->site_auth = parent->site_auth;

    nth_site_set_params(site, ta_tags(ta));
  }

  ta_end(ta);

 error:
  su_home_deinit(home);
  return site;
}
예제 #18
0
tagi_t* luasofia_tags_table_to_taglist(lua_State *L, int index, su_home_t *home)
{
    int i = 0;
    int maxtags = TAGS_LIST_SIZE;
    tagi_t* tags = su_zalloc(home, sizeof(tagi_t) * maxtags);

    if(!lua_istable(L, index)) {
        tags[0].t_tag = NULL;
        tags[0].t_value = 0;
        return tags;
    }

    /* put the tag table at the stack */
    lua_rawgeti(L, LUA_REGISTRYINDEX, tag_table_ref);
    if (lua_isnil(L, -1)) {
        su_free(home, tags);
        luaL_error(L, "Failed to get tag table!");
    }

    if (index < 0)
        index--;

    /* first key */
    lua_pushnil(L);
    while(lua_next(L, index) != 0) {
        /* 'key' at index -2 and 'value' at index -1 */
        tag_type_t t_tag = NULL;
        tag_value_t return_value;
        char const *s = NULL;

        /* if 'value' is nil not use this tag */
        if(lua_isnil(L, -1)) {
            /* remove 'value' and 'key' is used on the next iteration */
            lua_pop(L, 1);
            continue;
        }
        s = lua_tostring(L, -1);

        /* lookup key in the tag table and push tag_type_t */
        lua_pushvalue(L, -2);
        lua_rawget(L, -4);
        t_tag = lua_touserdata(L, -1);
        lua_pop(L, 1);

        if(t_scan(t_tag, home, s, &return_value) == -1) {
            su_free(home, tags);
            luaL_error(L, "Tag '%s' doesn't exist!", lua_tostring(L, -2));
        }

        tags[i].t_tag = t_tag;
        tags[i++].t_value = return_value;

        if(i == maxtags - 1) {
            maxtags *= 2;
            tags = su_realloc(home, tags, sizeof(tagi_t) * maxtags);
        }

        /* remove 'value' and 'key' is used on the next iteration */
        lua_pop(L, 1);
    }
    /* remove tag table from stack */
    lua_pop(L, 1);

    tags[i].t_tag = NULL;
    tags[i].t_value = 0;
    return tags;
}