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