/** Check that we allow the request method. * * The request-method is compared with the list of supported methods in @a * allow. If match is found, 0 is is returned. Otherwise, if the * request-method is well-known, 405 is returned. If the request-method is * unknown, 501 is returned. If @a irq is non-NULL, the 405 or 501 response * message is sent to the client, too. * * @param irq incoming transaction object (may be NULL). * @param sip contents of the SIP message * @param allow list of allowed methods * @param tag, value, ... optional list of tagged arguments used * when responding to the transaction * * @return 0 if successful, 405 is request-method is not allowed, 501 if * request-method is unknown. */ int nta_check_method(nta_incoming_t *irq, sip_t const *sip, sip_allow_t const *allow, tag_type_t tag, tag_value_t value, ...) { /* Check extensions */ sip_method_t method = sip->sip_request->rq_method; char const *name = sip->sip_request->rq_method_name; if (sip_is_allowed(allow, method, name)) return 0; if (irq) { ta_list ta; ta_start(ta, tag, value); if (method != sip_method_unknown) /* Well-known method */ nta_incoming_treply(irq, SIP_405_METHOD_NOT_ALLOWED, SIPTAG_ALLOW(allow), ta_tags(ta)); else /* Completeley unknown method */ nta_incoming_treply(irq, SIP_501_NOT_IMPLEMENTED, SIPTAG_ALLOW(allow), ta_tags(ta)); ta_end(ta); } return method != sip_method_unknown ? 405 : 501; }
/**Create an authentication plugin module. * * The function auth_mod_create() creates a module used to authenticate the * requests. * * @param root pointer to a su_root_t object * @param tag,value,... tagged argument list * * @TAGS * AUTHTAG_METHOD(), AUTHTAG_REALM(), AUTHTAG_DB(), AUTHTAG_ALLOW(), * AUTHTAG_QOP(), AUTHTAG_ALGORITHM(), AUTHTAG_EXPIRES(), * AUTHTAG_BLACKLIST(), AUTHTAG_FORBIDDEN(), AUTHTAG_ANONYMOUS(), * AUTHTAG_REMOTE(). */ auth_mod_t *auth_mod_create(su_root_t *root, tag_type_t tag, tag_value_t value, ...) { auth_mod_t *am = NULL; ta_list ta; char const *method = NULL; ta_start(ta, tag, value); tl_gets(ta_args(ta), AUTHTAG_METHOD_REF(method), TAG_NULL()); if (method) { auth_scheme_t *bscheme = NULL; char const *base; size_t len; base = strrchr(method, '+'); if (base) len = base++ - method; else len = strlen(method); if (base == NULL) ; else if (su_casematch(base, "Basic")) bscheme = auth_scheme_basic; else if (su_casematch(base, "Digest")) bscheme = auth_scheme_digest; if (base == NULL || bscheme) { int i; for (i = 0; schemes[i] && i < N; i++) { if (su_casenmatch(schemes[i]->asch_method, method, len) && schemes[i]->asch_method[len] == 0) { am = auth_mod_alloc(schemes[i], ta_tags(ta)); if (schemes[i]->asch_init(am, bscheme, root, ta_tags(ta)) == -1) { auth_mod_destroy(am), am = NULL; } break; } } } } ta_end(ta); return am; }
static struct event * subscription_by_nua(nua_handle_t *nh, enum nua_substate current, tag_type_t tag, tag_value_t value, ...) { struct message *subscribe; struct event *notify, *event; ta_list ta; enum nua_substate substate = nua_substate_active; char const *substate_str = subscription_state; char const *expires = "600"; subscribe = s2_sip_wait_for_request(SIP_METHOD_SUBSCRIBE); if (event_type) fail_if(!subscribe->sip->sip_event || strcmp(event_type, subscribe->sip->sip_event->o_type)); if (subscribe->sip->sip_expires && subscribe->sip->sip_expires->ex_delta == 0) { substate = nua_substate_terminated; substate_str = "terminated;reason=timeout"; expires = "0"; } ta_start(ta, tag, value); if (send_notify_before_response) { s2_sip_save_uas_dialog(dialog, subscribe->sip); notify = notify_to_nua(substate, SIPTAG_EVENT(subscribe->sip->sip_event), SIPTAG_SUBSCRIPTION_STATE_STR(substate_str), ta_tags(ta)); event = respond_to_subscribe(subscribe, nua_r_subscribe, substate, SIP_200_OK, SIPTAG_EXPIRES_STR(expires), TAG_END()); s2_free_event(event); } else { event = respond_to_subscribe(subscribe, nua_r_subscribe, current, SIP_202_ACCEPTED, SIPTAG_EXPIRES_STR(expires), TAG_END()); s2_free_event(event); notify = notify_to_nua(substate, SIPTAG_EVENT(subscribe->sip->sip_event), SIPTAG_SUBSCRIPTION_STATE_STR(substate_str), ta_tags(ta)); } s2_sip_free_message(subscribe); return notify; }
nua_handle_t * subscribe_to_nua(char const *event, tag_type_t tag, tag_value_t value, ...) { ta_list ta; struct event *subscribe; struct message *response; nua_handle_t *nh; nua_set_params(nua, NUTAG_APPL_METHOD("SUBSCRIBE"), SIPTAG_ALLOW_EVENTS_STR(event), TAG_END()); fail_unless_event(nua_r_set_params, 200); ta_start(ta, tag, value); s2_sip_request_to(dialog, SIP_METHOD_SUBSCRIBE, NULL, SIPTAG_EVENT_STR(event), ta_tags(ta)); ta_end(ta); subscribe = s2_wait_for_event(nua_i_subscribe, 100); nh = subscribe->nh; nua_respond(nh, SIP_202_ACCEPTED, NUTAG_WITH_SAVED(subscribe->event), TAG_END()); s2_free_event(subscribe); response = s2_sip_wait_for_response(202, SIP_METHOD_SUBSCRIBE); s2_sip_update_dialog(dialog, response); fail_unless(response->sip->sip_expires != NULL); s2_sip_free_message(response); return nh; }
/**Check that we support all features which UAC requires. * * The list of supported features is compared with the list of features * required by the UAC. If some features are not listed as supported, return * 420. If @a irq is non-NULL, the 420 response message is sent to the * client along with list of unsupported features in the @Unsupported * header, too. * * @param irq incoming transaction object (may be NULL). * @param sip contents of the SIP message * @param supported list of protocol features supported * @param tag, value, ... optional list of tagged arguments used * when responding to the transaction * * @return 0 if successful. * 420 if any of the required features is not supported. */ int nta_check_required(nta_incoming_t *irq, sip_t const *sip, sip_supported_t const *supported, tag_type_t tag, tag_value_t value, ...) { int status = 0; if (sip->sip_require) { su_home_t home[SU_HOME_AUTO_SIZE(512)]; sip_unsupported_t *us; su_home_auto(home, sizeof home); us = sip_has_unsupported(home, supported, sip->sip_require); if (us) { status = 420; if (irq) { ta_list ta; ta_start(ta, tag, value); nta_incoming_treply(irq, SIP_420_BAD_EXTENSION, SIPTAG_UNSUPPORTED(us), SIPTAG_SUPPORTED(supported), ta_tags(ta)); ta_end(ta); } } su_home_deinit(home); } return status; }
/**Check @SessionExpires header. * * If the proposed session-expiration time is smaller than @MinSE or our * minimal session expiration time, respond with 422 containing shortest * acceptable session expiration time in @MinSE header. * * @param irq incoming transaction object (may be NULL). * @param sip contents of the SIP message * @param my_min_se minimal session expiration time in seconds * @param tag, value, ... optional list of tagged arguments used * when responding to the transaction * * @return 422 if session expiration time is too small, 0 when successful. */ int nta_check_session_expires(nta_incoming_t *irq, sip_t const *sip, sip_time_t my_min_se, tag_type_t tag, tag_value_t value, ...) { unsigned long min_se = my_min_se; if (sip->sip_min_se && min_se < sip->sip_min_se->min_delta) min_se = sip->sip_min_se->min_delta; if (sip->sip_session_expires->x_delta >= min_se) return 0; if (irq) { ta_list ta; sip_min_se_t min_se0[1]; ta_start(ta, tag, value); sip_min_se_init(min_se0)->min_delta = min_se; nta_incoming_treply(irq, SIP_422_SESSION_TIMER_TOO_SMALL, SIPTAG_MIN_SE(min_se0), ta_tags(ta)); ta_end(ta); } return 422; }
nth_engine_t *nth_engine_create(su_root_t *root, tag_type_t tag, tag_value_t value, ...) { nth_engine_t *he; ta_list ta; if ((he = su_home_new(sizeof(*he)))) { he->he_root = root; he->he_mflags = MSG_DO_CANONIC; he->he_mclass = http_default_mclass(); he->he_expires = 32000; ta_start(ta, tag, value); if (hc_htable_resize(he->he_home, he->he_clients, 0) < 0 || he_create_tports(he, ta_args(ta)) < 0 || he_timer_init(he) < 0 || nth_engine_set_params(he, ta_tags(ta)) < 0) { nth_engine_destroy(he), he = NULL; } ta_end(ta); } return he; }
/**Forward a request. * * @deprecated * Use nta_outgoing_mcreate() instead. */ nta_outgoing_t *nta_outgoing_tclone(nta_agent_t *agent, nta_response_f *callback, nta_outgoing_magic_t *magic, url_string_t const *route_url, msg_t *parent, tag_type_t tag, tag_value_t value, ...) { ta_list ta; msg_t *msg; nta_outgoing_t *orq = NULL; if (parent == NULL) return NULL; if ((msg = nta_msg_create(agent, 0)) == NULL) return NULL; ta_start(ta, tag, value); msg_clone(msg, parent); if (parent && sip_copy_all(msg, sip_object(msg), sip_object(parent)) < 0) ; else if (sip_add_tl(msg, sip_object(msg), ta_tags(ta)) < 0) ; else orq = nta_outgoing_mcreate(agent, callback, magic, route_url, msg); ta_end(ta); if (!orq) msg_destroy(msg); return orq; }
static struct event * notify_to_nua(enum nua_substate expect_substate, tag_type_t tag, tag_value_t value, ...) { struct event *event; struct message *response; ta_list ta; int status; ta_start(ta, tag, value); fail_if(s2_sip_request_to(dialog, SIP_METHOD_NOTIFY, NULL, SIPTAG_CONTENT_TYPE_STR(event_mime_type), SIPTAG_PAYLOAD_STR(event_state), ta_tags(ta))); ta_end(ta); response = s2_sip_wait_for_response(0, SIP_METHOD_NOTIFY); fail_if(!response); status = response->sip->sip_status->st_status; fail_unless(status == 200); s2_sip_free_message(response); event = s2_wait_for_event(nua_i_notify, 200); fail_if(!event); fail_unless(s2_check_substate(event, expect_substate)); return event; }
/** Check that we understand session content in the request. * * If there is no @ContentDisposition header or the @ContentDisposition * header indicated "session", the message body and content-type is compared * with the acceptable session content-types listed in @a session_accepts. * (typically, @c "application/sdp"). If no match is found, a 415 is * returned. If @a irq is non-NULL, the 415 response message is sent to the * client, too. * * If the @ContentDisposition header indicates something else but "session", * and it does not contain "handling=optional" parameter, a 415 response is * returned, too. * * Also, the @ContentEncoding header is checked. If it is not empty * (indicating no content-encoding), a 415 response is returned, too. * * @param irq incoming (server) transaction object (may be NULL). * @param sip contents of the SIP message * @param session_accepts list of acceptable content-types for "session" * content disposition * @param tag, value, ... optional list of tagged arguments used * when responding to the transaction * * @return 0 if successful, 415 if content-type is not acceptable. */ int nta_check_session_content(nta_incoming_t *irq, sip_t const *sip, sip_accept_t const *session_accepts, tag_type_t tag, tag_value_t value, ...) { sip_content_type_t const *c = sip->sip_content_type; sip_content_disposition_t const *cd = sip->sip_content_disposition; int acceptable_type = 0, acceptable_encoding = 0; if (sip->sip_payload == NULL) return 0; if (cd == NULL || su_casematch(cd->cd_type, "session")) { sip_accept_t const *ab = session_accepts; char const *c_type; if (c) c_type = c->c_type; else if (sip->sip_payload->pl_len > 3 && su_casenmatch(sip->sip_payload->pl_data, "v=0", 3)) /* Missing Content-Type, but it looks like SDP */ c_type = application_sdp; else /* No chance */ ab = NULL, c_type = NULL; for (; ab; ab = ab->ac_next) { if (su_casematch(c_type, ab->ac_type)) break; } if (ab) acceptable_type = 1; } else if (cd->cd_optional) acceptable_type = 1; /* Empty or missing Content-Encoding */ if (!sip->sip_content_encoding || !sip->sip_content_encoding->k_items || !sip->sip_content_encoding->k_items[0] || !sip->sip_content_encoding->k_items[0][0]) acceptable_encoding = 1; if (acceptable_type && acceptable_encoding) return 0; if (irq) { ta_list ta; ta_start(ta, tag, value); nta_incoming_treply(irq, SIP_415_UNSUPPORTED_MEDIA, SIPTAG_ACCEPT(session_accepts), ta_tags(ta)); ta_end(ta); } return 415; }
/** @internal * Post signal to stack itself */ void nua_stack_post_signal(nua_handle_t *nh, nua_event_t event, tag_type_t tag, tag_value_t value, ...) { ta_list ta; ta_start(ta, tag, value); nua_signal((nh)->nh_nua, nh, NULL, event, 0, NULL, ta_tags(ta)); ta_end(ta); }
/**Check that UAC accepts one of listed MIME content-types. * * The list of acceptable content-types are compared with the acceptable * content-types. If match is found, it is returned in @a return_acceptable. * If no match is found, a 406 is returned. If @a irq is non-NULL, the 406 * response message is sent to the client, too. * * @param irq incoming transaction object (may be NULL). * @param sip contents of the SIP message * @param acceptable list of acceptable content types * @param return_acceptable optional return-value parameter for * matched content-type * @param tag, value, ... optional list of tagged arguments used * when responding to the transaction * * @return 406 if no content-type is acceptable by client, 0 if successful. */ int nta_check_accept(nta_incoming_t *irq, sip_t const *sip, sip_accept_t const *acceptable, sip_accept_t const **return_acceptable, tag_type_t tag, tag_value_t value, ...) { ta_list ta; sip_accept_t const *ac, *ab; sip_method_t method; if (!acceptable) return 0; if (sip->sip_request) method = sip->sip_request->rq_method; else /* if (sip->sip_cseq) */ method = sip->sip_cseq->cs_method; /* Missing Accept header implies support for SDP in INVITE and OPTIONS * (and PRACK and UPDATE?) */ if (!sip->sip_accept && (method == sip_method_invite || method == sip_method_options || method == sip_method_prack || method == sip_method_update)) { for (ab = acceptable; ab; ab = ab->ac_next) if (su_casematch(application_sdp, ab->ac_type)) { if (return_acceptable) *return_acceptable = ab; return 0; } } for (ac = sip->sip_accept; ac; ac = ac->ac_next) { if (sip_q_value(ac->ac_q) == 0 || !ac->ac_type) continue; for (ab = acceptable; ab; ab = ab->ac_next) if (su_casematch(ac->ac_type, ab->ac_type)) { if (return_acceptable) *return_acceptable = ab; return 0; } } if (irq) { ta_start(ta, tag, value); nta_incoming_treply(irq, SIP_406_NOT_ACCEPTABLE, SIPTAG_ACCEPT(acceptable), ta_tags(ta)); ta_end(ta); } return 406; }
END_TEST static nua_handle_t *make_auth_natted_register( nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...) { struct message *m; ta_list ta; ta_start(ta, tag, value); nua_register(nh, ta_tags(ta)); ta_end(ta); m = s2_sip_wait_for_request(SIP_METHOD_REGISTER); fail_if(!m); s2_sip_respond_to(m, NULL, SIP_401_UNAUTHORIZED, SIPTAG_WWW_AUTHENTICATE_STR(s2_auth_digest_str), SIPTAG_VIA(natted_via(m, receive_natted)), TAG_END()); s2_sip_free_message(m); fail_unless_event(nua_r_register, 401); /* NAT detected event */ fail_unless_event(nua_i_outbound, 101); nua_authenticate(nh, NUTAG_AUTH(s2_auth_credentials), TAG_END()); m = s2_sip_wait_for_request(SIP_METHOD_REGISTER); fail_if(!m); fail_if(!m->sip->sip_authorization); /* should not unregister the previous contact * as it has not been successfully registered */ fail_if(!m->sip->sip_contact); fail_if(m->sip->sip_contact->m_next); s2_save_register(m); s2_sip_respond_to(m, NULL, SIP_200_OK, SIPTAG_CONTACT(s2->registration->contact), SIPTAG_VIA(natted_via(m, receive_natted)), TAG_END()); s2_sip_free_message(m); assert(s2->registration->contact != NULL); fail_unless_event(nua_r_register, 200); return nh; }
static struct message * invite_sent_by_nua(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...) { ta_list ta; ta_start(ta, tag, value); nua_invite(nh, SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"), ta_tags(ta)); ta_end(ta); fail_unless(s2_check_callstate(nua_callstate_calling)); return s2_sip_wait_for_request(SIP_METHOD_INVITE); }
static void bye_by_nua(struct dialog *dialog, nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...) { ta_list ta; struct message *bye; ta_start(ta, tag, value); nua_bye(nh, ta_tags(ta)); ta_end(ta); bye = s2_sip_wait_for_request(SIP_METHOD_BYE); fail_if(!bye); s2_sip_respond_to(bye, dialog, SIP_200_OK, TAG_END()); s2_sip_free_message(bye); fail_unless_event(nua_r_bye, 200); fail_unless(s2_check_callstate(nua_callstate_terminated)); }
nua_t *s2_nua_setup(char const *label, tag_type_t tag, tag_value_t value, ...) { ta_list ta; s2_setup(label); s2 = su_home_new(sizeof *s2); s2_dns_setup(s2base->root); s2_setup_logs(0); s2_sip_setup("example.org", NULL, TAG_END()); assert(s2sip->contact); s2_dns_domain("example.org", 1, "s2", 1, s2sip->udp.contact->m_url, "s2", 1, s2sip->tcp.contact->m_url, NULL); /* enable/disable multithreading */ su_root_threading(s2base->root, s2_nua_thread); ta_start(ta, tag, value); s2->nua = nua_create(s2base->root, s2_nua_callback, s2, SIPTAG_FROM_STR("Alice <sip:[email protected]>"), /* NUTAG_PROXY((url_string_t *)s2sip->contact->m_url), */ /* Use internal DNS server */ NUTAG_PROXY("sip:example.org"), /* Force sresolv to use localhost and s2dns as DNS server */ #if HAVE_WIN32 SRESTAG_RESOLV_CONF("NUL"), #else SRESTAG_RESOLV_CONF("/dev/null"), #endif ta_tags(ta)); ta_end(ta); return s2->nua; }
/** Check that UAC supports all the required features. * * The list of required features is compared with the features supported by * the UAC. If some features are not supported, return 421. If @a irq is * non-NULL, the 421 response message is sent to the client, too. * * @param irq incoming transaction object (may be NULL). * @param sip contents of the SIP message * @param require list of required protocol features * @param tag, value, ... optional list of tagged arguments used * when responding to the transaction * * @return 0 if successful. * 421 if any of the required features is not supported. */ int nta_check_supported(nta_incoming_t *irq, sip_t const *sip, sip_require_t *require, tag_type_t tag, tag_value_t value, ...) { if (!sip_has_unsupported(NULL, sip->sip_supported, require)) return 0; if (irq) { ta_list ta; ta_start(ta, tag, value); nta_incoming_treply(irq, SIP_421_EXTENSION_REQUIRED, SIPTAG_REQUIRE(require), ta_tags(ta)); ta_end(ta); } return 421; }
static struct event * respond_to_subscribe(struct message *subscribe, nua_event_t expect_event, enum nua_substate expect_substate, int status, char const *phrase, tag_type_t tag, tag_value_t value, ...) { struct event *event; ta_list ta; ta_start(ta, tag, value); s2_sip_respond_to(subscribe, dialog, status, phrase, ta_tags(ta)); ta_end(ta); event = s2_wait_for_event(expect_event, status); fail_if(!event); fail_unless(s2_check_substate(event, expect_substate)); return event; }
static void respond_with_sdp(struct message *request, struct dialog *dialog, int status, char const *phrase, tag_type_t tag, tag_value_t value, ...) { ta_list ta; char const *body; isize_t bodylen; fail_if(soa_get_local_sdp(soa, NULL, &body, &bodylen) != 1); ta_start(ta, tag, value); s2_sip_respond_to(request, dialog, status, phrase, SIPTAG_CONTENT_TYPE_STR("application/sdp"), SIPTAG_PAYLOAD_STR(body), SIPTAG_CONTENT_DISPOSITION_STR("session"), ta_tags(ta)); ta_end(ta); }
static int delayed_auth_init(auth_mod_t *am, auth_scheme_t *base, su_root_t *root, tag_type_t tag, tag_value_t value, ...) { auth_plugin_t *ap = AUTH_PLUGIN(am); int retval = -1; ta_list ta; ta_start(ta, tag, value); if (root && base && auth_init_default(am, base, root, ta_tags(ta)) != -1) { ap->ap_root = root; ap->ap_base = base; ap->ap_tail = &ap->ap_list; retval = 0; } ta_end(ta); return retval; }
/** @internal Shutdown notifier object */ int nh_notifier_shutdown(nua_handle_t *nh, nea_event_t *ev, tag_type_t t, tag_value_t v, ...) { nea_server_t *nes = nh->nh_notifier; nea_subnode_t const **subs; int busy = 0; if (nes == NULL) return 0; subs = nea_server_get_subscribers(nes, ev); if (subs) { int i; ta_list ta; ta_start(ta, t, v); for (i = 0; subs[i]; i++) nea_sub_auth(subs[i]->sn_subscriber, nea_terminated, ta_tags(ta)); ta_end(ta); busy++; } nea_server_free_subscribers(nes, subs); nea_server_flush(nh->nh_notifier, NULL); if (ev == NULL) nea_server_destroy(nh->nh_notifier), nh->nh_notifier = NULL; return busy; }
server_t *server_create(url_t const *url, tag_type_t tag, tag_value_t value, ...) { server_t *srv; msg_mclass_t const *mclass = NULL; tp_name_t tpn[1] = {{ NULL }}; su_root_t *root = NULL; http_server_t const *server = NULL; int persistent = 0; char const *server_str = SERVER_VERSION; ta_list ta; ta_start(ta, tag, value); tl_gets(ta_args(ta), NTHTAG_ROOT_REF(root), NTHTAG_MCLASS_REF(mclass), TPTAG_REUSE_REF(persistent), HTTPTAG_SERVER_REF(server), HTTPTAG_SERVER_STR_REF(server_str), TAG_END()); if (!root || !url || (url->url_type != url_http && url->url_type != url_https) || !(srv = su_home_new(sizeof(*srv)))) { ta_end(ta); return NULL; } tpn->tpn_proto = url_tport_default((enum url_type_e)url->url_type); tpn->tpn_canon = url->url_host; tpn->tpn_host = url->url_host; tpn->tpn_port = url_port(url); srv->srv_tports = tport_tcreate(srv, nth_server_class, root, TPTAG_IDLE(600000), TPTAG_TIMEOUT(300000), ta_tags(ta)); srv->srv_persistent = persistent; srv->srv_max_bodylen = 1 << 30; /* 1 GB */ if (tport_tbind(srv->srv_tports, tpn, http_tports, TAG_END()) >= 0 || tport_tbind(srv->srv_tports, tpn, http_no_tls_tports, TAG_END()) >= 0) { srv->srv_root = root; srv->srv_mclass = mclass ? mclass : http_default_mclass(); srv->srv_mflags = MSG_DO_CANONIC; if (server) srv->srv_server = http_server_dup(srv->srv_home, server); else srv->srv_server = http_server_make(srv->srv_home, server_str); tport_get_params(srv->srv_tports, TPTAG_QUEUESIZE_REF(srv->srv_queuesize), TAG_END()); } else { SU_DEBUG_1(("nth_server_create: cannot bind transports " URL_FORMAT_STRING "\n", URL_PRINT_ARGS(url))); server_destroy(srv), srv = NULL; } ta_end(ta); return srv; }
/** 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; }
int nth_request_treply(nth_request_t *req, int status, char const *phrase, tag_type_t tag, tag_value_t value, ...) { msg_t *response, *next = NULL; http_t *http; int retval = -1; int req_close, close; ta_list ta; http_header_t const *as_info = NULL; if (req == NULL || status < 100 || status >= 600) { return -1; } response = req->req_response; http = http_object(response); if (status >= 200 && req->req_as) as_info = (http_header_t const *)req->req_as->as_info; ta_start(ta, tag, value); http_add_tl(response, http, HTTPTAG_SERVER(req->req_server->srv_server), HTTPTAG_HEADER(as_info), ta_tags(ta)); if (http->http_payload && !http->http_content_length) { http_content_length_t *l; http_payload_t *pl; size_t len = 0; for (pl = http->http_payload; pl; pl = pl->pl_next) len += pl->pl_len; if (len > UINT32_MAX) goto fail; l = http_content_length_create(msg_home(response), (uint32_t)len); msg_header_insert(response, (msg_pub_t *)http, (msg_header_t *)l); } if (req->req_method == http_method_head && http->http_payload) { http_payload_t *pl; for (pl = http->http_payload; pl; pl = pl->pl_next) msg_header_remove(response, (msg_pub_t *)http, (msg_header_t *)pl); } http_complete_response(response, status, phrase, http_object(req->req_request)); if (!http->http_date) { http_date_t date[1]; http_date_init(date)->d_time = msg_now(); msg_header_add_dup(response, (msg_pub_t *)http, (msg_header_t*)date); } if (status < 200) { close = 0; next = server_msg_create(req->req_server, 0, NULL, 0, NULL, NULL); } else { req_close = req->req_close; close = (http->http_connection && msg_params_find(http->http_connection->k_items, "close")); if (req_close && !close && status >= 200) { close = 1; http_add_tl(response, http, HTTPTAG_CONNECTION_STR("close"), TAG_END()); } } msg_serialize(response, (msg_pub_t *)http); retval = tport_tqsend(req->req_tport, response, next, TAG_IF(close, TPTAG_CLOSE_AFTER(1)), ta_tags(ta)); fail: ta_end(ta); if (retval == 0) req->req_status = status; return retval; }
/** * 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; }