/**@internal Set refresh in range min..max seconds in the future. */ void nua_dialog_usage_set_refresh_range(nua_dialog_usage_t *du, unsigned min, unsigned max) { sip_time_t now = sip_now(), target; unsigned delta; if (max < min) max = min; if (min != max) delta = su_randint(min, max); else delta = min; if (now + delta >= now) target = now + delta; else target = SIP_TIME_MAX; SU_DEBUG_7(("nua(): refresh %s after %lu seconds (in [%u..%u])\n", nua_dialog_usage_name(du), target - now, min, max)); du->du_refquested = now; du->du_refresh = target; }
/** Set absolute refresh time */ void nua_dialog_usage_set_refresh_at(nua_dialog_usage_t *du, sip_time_t target) { SU_DEBUG_7(("nua(): refresh %s after %lu seconds\n", nua_dialog_usage_name(du), target - sip_now())); du->du_refresh = target; }
/**@internal Set refresh in range min..max seconds in the future. */ void nua_dialog_usage_set_refresh_range(nua_dialog_usage_t *du, unsigned min, unsigned max) { su_duration_t max_defer = nua_dialog_usage_get_max_defer(du); unsigned delta; int make_deferrable = 0; if (max <= min) { max = min; delta = min * 1000; } else if (max_defer > 0 && (int)(max - min) >= max_defer / 1000) { delta = su_randint(min * 1000, max * 1000 - (max_defer + 999)); make_deferrable = 1; } else delta = su_randint(min * 1000, max * 1000); SU_DEBUG_7(("nua(): refresh %s in %.3f seconds (in [%u..%u]%s)\n", nua_dialog_usage_name(du), 1e-3 * delta, min, max, make_deferrable? ", deferrable" : "")); delta += delta == 0; nua_dialog_usage_set_refresh_timer(du, delta, make_deferrable); }
void nw_changed_cb(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) { su_network_changed_t *snc = (su_network_changed_t *) info; su_network_changed_t *snc2; su_msg_r rmsg = SU_MSG_R_INIT; SU_DEBUG_7(("nw_changed_cb: entering.\n")); if (su_msg_create(rmsg, su_root_task(snc->su_root), su_root_task(snc->su_root), su_nw_changed_msg_recv, sizeof *snc) == SU_FAILURE) { return; } snc2 = su_msg_data(rmsg); assert(snc2); snc2->su_root = snc->su_root; snc2->su_home = snc->su_home; memcpy(snc2->su_storeRef, snc->su_storeRef, sizeof(SCDynamicStoreRef)); memcpy(snc2->su_sourceRef, snc->su_sourceRef, sizeof(CFRunLoopSourceRef)); snc2->su_os_thread = snc->su_os_thread; snc2->su_network_changed_cb = snc->su_network_changed_cb; snc2->su_network_changed_magic = snc->su_network_changed_magic; if (su_msg_send(rmsg) == SU_FAILURE) { su_msg_destroy(rmsg); return; } return; }
/*# Send a request to the protocol thread */ int nua_signal(nua_t *nua, nua_handle_t *nh, msg_t *msg, nua_event_t event, int status, char const *phrase, tag_type_t tag, tag_value_t value, ...) { su_msg_r sumsg = SU_MSG_R_INIT; size_t len, xtra, ee_len, l_len = 0, l_xtra = 0; ta_list ta; int retval = -1; if (nua == NULL) return -1; if (nua->nua_shutdown_started && event != nua_r_shutdown) return -1; ta_start(ta, tag, value); ee_len = offsetof(nua_ee_data_t, ee_data[0].e_tags); len = tl_len(ta_args(ta)); xtra = tl_xtra(ta_args(ta), len); if (su_msg_new(sumsg, ee_len + len + l_len + xtra + l_xtra) == 0) { nua_ee_data_t *ee = su_msg_data(sumsg); nua_event_data_t *e = ee->ee_data; tagi_t *t = e->e_tags; void *b = (char *)t + len + l_len; tagi_t *tend = (tagi_t *)b; char *bend = (char *)b + xtra + l_xtra; t = tl_dup(t, ta_args(ta), &b); assert(tend == t); (void)tend; assert(b == bend); (void)bend; e->e_always = event == nua_r_destroy || event == nua_r_shutdown; e->e_event = event; e->e_nh = nh ? nua_handle_ref(nh) : NULL; e->e_status = status; e->e_phrase = phrase; su_msg_deinitializer(sumsg, nua_event_deinit); retval = su_msg_send_to(sumsg, nua->nua_server, nua_stack_signal); if (retval == 0){ SU_DEBUG_7(("nua(%p): %s signal %s\n", (void *)nh, "sent", nua_event_name(event) + 4)); } else { SU_DEBUG_0(("nua(%p): %s signal %s\n", (void *)nh, "FAILED TO SEND", nua_event_name(event) + 4)); } } ta_end(ta); return retval; }
/** Internal deinitialization */ static void _su_home_deinit(su_home_t *home) { if (home->suh_blocks) { size_t i; su_block_t *b; void *suh_lock = home->suh_lock; home->suh_lock = NULL; if (home->suh_blocks->sub_destructor) { void (*destructor)(void *) = home->suh_blocks->sub_destructor; home->suh_blocks->sub_destructor = NULL; destructor(home); } b = home->suh_blocks; #ifdef DEBUG SU_DEBUG_9(("%s: block %p sub_used is %ld sub_n %ld used %d\n", __func__, b, b->sub_used, b->sub_n, su_get_used_count(b))) ; #endif su_home_check_blocks(b); for (i = 0; i < b->sub_n; i++) { if (b->sub_nodes[i].sua_data) { if (b->sub_nodes[i].sua_home) { su_home_t *subhome = b->sub_nodes[i].sua_data; su_block_t *subb = MEMLOCK(subhome); assert(subb); assert(subb->sub_ref >= 1); #if 0 if (subb->sub_ref > 0) SU_DEBUG_7(("su_home_unref: subhome %p with destructor %p has still %u refs\n", subhome, subb->sub_destructor, subb->sub_ref)); #endif subb->sub_ref = 0; /* zap them all */ _su_home_deinit(subhome); } else if (su_is_preloaded(b, b->sub_nodes[i].sua_data)) continue; safefree(b->sub_nodes[i].sua_data); } } if (b->sub_preload && !b->sub_preauto) free(b->sub_preload); if (b->sub_stats) free(b->sub_stats); if (!b->sub_auto) free(b); home->suh_blocks = NULL; if (suh_lock) { /* Unlock, or risk assert() or leak handles on Windows */ _su_home_unlocker(suh_lock); _su_home_destroy_mutexes(suh_lock); } } }
/*# Receive event from protocol machine and hand it over to application */ static void nua_application_event(nua_t *dummy, su_msg_r sumsg, nua_ee_data_t *ee) { nua_t *nua = ee->ee_nua; nua_event_data_t *e = ee->ee_data; nua_handle_t *nh = e->e_nh; enter; ee->ee_nua = NULL; e->e_nh = NULL; if (nh == NULL) { /* Xyzzy */ } else if (nh->nh_valid) { if (!nh->nh_ref_by_user) { /* Application must now call nua_handle_destroy() */ nh->nh_ref_by_user = 1; nua_handle_ref(nh); } } else if (!nh->nh_valid) { /* Handle has been destroyed */ if (nua_log->log_level >= 7) { char const *name = nua_event_name((enum nua_event_e)e->e_event) + 4; SU_DEBUG_7(("nua(%p): event %s dropped\n", (void *)nh, name)); } nua_handle_unref(nh); nua_stack_unref(nua); return; } if (e->e_event == nua_r_shutdown && e->e_status >= 200) nua->nua_shutdown_final = 1; if (nua->nua_callback) { nua_event_frame_t frame[1]; su_msg_save(frame->nf_saved, sumsg); frame->nf_next = nua->nua_current, nua->nua_current = frame; nua->nua_callback((enum nua_event_e)e->e_event, e->e_status, e->e_phrase, nua, nua->nua_magic, nh, nh ? nh->nh_magic : NULL, e->e_msg ? sip_object(e->e_msg) : NULL, e->e_tags); su_msg_destroy(frame->nf_saved); nua->nua_current = frame->nf_next; } nua_handle_unref(nh); nua_stack_unref(nua); }
static void nua_notify_usage_refresh(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du) { struct notifier_usage *nu = nua_dialog_usage_private(du); nua_client_request_t *cr = du->du_cr; nua_event_t e = nua_r_notify; if (cr) { int terminating = 0; sip_time_t now = sip_now(); SU_DEBUG_7(("%s(%p, %p, %p): using existing cr=%p\n", "nua_notify_usage_refresh", (void *)nh, (void *)ds, (void *)du, (void *)cr)); if (nu->nu_expires && nu->nu_expires <= now) terminating = 1; else if (nu->nu_requested && nu->nu_requested <= now) terminating = 1; if (nua_client_resend_request(cr, terminating) >= 0) return; } else { SU_DEBUG_7(("%s(%p, %p, %p): new NOTIFY cr for %s\n", "nua_notify_usage_refresh", (void *)nh, (void *)ds, (void *)du, du->du_event ? du->du_event->o_type : "<implicit>")); if (nua_client_create(nh, e, &nua_notify_client_methods, NULL) >= 0) return; } nua_stack_tevent(nh->nh_nua, nh, NULL, e, NUA_ERROR_AT(__FILE__, __LINE__), NUTAG_SUBSTATE(nua_substate_terminated), TAG_END()); nua_dialog_usage_remove(nh, ds, du, NULL, NULL); }
/** Send pong */ int tport_ws_pong(tport_t *self) { self->tp_ping = 0; if (tport_has_queued(self) || !self->tp_params->tpp_pong2ping) return 0; SU_DEBUG_7(("%s(%p): %s to " TPN_FORMAT "%s\n", __func__, (void *)self, "sending PONG", TPN_ARGS(self->tp_name), "")); return send(self->tp_socket, "\r\n", 2, 0); }
/**Get digest-response parameters. * * The function auth_response_get() searches for the digest authentication * parameters in @a params. The parameters are assigned to the appropriate * fields in @a ar structure. * * @return * * The function auth_response_get() returns number of parameters * found, or -1 upon an error. */ issize_t auth_digest_response_get(su_home_t *home, auth_response_t *ar0, char const *const params[]) { ssize_t n; auth_response_t ar[1] = {{ 0 }}; char const *md5 = NULL, *md5sess = NULL, *sha1 = NULL, *qop_auth = NULL, *qop_auth_int = NULL; ar->ar_size = sizeof(ar); assert(ar0); assert(params); assert(ar0->ar_size >= (int) sizeof(ar)); if (ar0 == NULL || params == NULL) return -1; n = auth_get_params(home, params, "username="******"realm=", &ar->ar_realm, "nonce=", &ar->ar_nonce, "uri=", &ar->ar_uri, "response=", &ar->ar_response, "algorithm=", &ar->ar_algorithm, "opaque=", &ar->ar_opaque, "cnonce=", &ar->ar_cnonce, "qop=", &ar->ar_qop, "nc=", &ar->ar_nc, "algorithm=md5", &md5, "algorithm=md5-sess", &md5sess, "algorithm=sha1", &sha1, "qop=auth", &qop_auth, "qop=auth-int", &qop_auth_int, NULL); if (n < 0) return n; ar->ar_md5 = md5 != NULL || ar->ar_algorithm == NULL; ar->ar_md5sess = md5sess != NULL; ar->ar_sha1 = sha1 != NULL; ar->ar_auth = qop_auth != NULL; ar->ar_auth_int = qop_auth_int != NULL; auth_struct_copy(ar0, ar, sizeof(ar)); SU_DEBUG_7(("%s: "MOD_ZD"\n", "auth_digest_response_get", n)); return n; }
/**@internal Set refresh before delta seconds elapse */ void nua_dialog_usage_set_refresh_in(nua_dialog_usage_t *du, unsigned delta) { su_duration_t max_defer = nua_dialog_usage_get_max_defer(du); su_duration_t timeout = delta * 1000L; int make_deferrable = 0; if (max_defer > 0 && timeout >= max_defer) { timeout -= max_defer; make_deferrable = 1; } SU_DEBUG_7(("nua(): refresh %s in %u seconds%s\n", nua_dialog_usage_name(du), delta, make_deferrable? " (deferrable)" : "")); nua_dialog_usage_set_refresh_timer(du, timeout, make_deferrable); }
static int li_name(su_localinfo_t const *hints, int gni_flags, su_sockaddr_t const *su, char **ccanonname) { char name[SU_MAXHOST]; int error; int flags = hints->li_flags; *ccanonname = NULL; if ((flags & LI_CANONNAME) || hints->li_canonname) { if ((flags & LI_NAMEREQD) == LI_NAMEREQD) gni_flags |= NI_NAMEREQD; if (flags & LI_NUMERIC) gni_flags |= NI_NUMERICHOST; error = su_getnameinfo(su, su_sockaddr_size(su), name, sizeof(name), NULL, 0, gni_flags); if (error) { if ((flags & LI_NAMEREQD) == LI_NAMEREQD) return 1; SU_DEBUG_7(("li_name: getnameinfo() failed\n")); if (!su_inet_ntop(su->su_family, SU_ADDR(su), name, sizeof name)) return ELI_RESOLVER; } if (hints->li_canonname && !su_casematch(name, hints->li_canonname)) return 1; if (!(flags & LI_CANONNAME)) return 0; if (!(*ccanonname = strdup(name))) return ELI_MEMORY; } return 0; }
/** Send PING */ int tport_ws_ping(tport_t *self, su_time_t now) { ssize_t n; char *why = ""; if (tport_has_queued(self)) return 0; n = send(self->tp_socket, "\r\n\r\n", 4, 0); if (n > 0) self->tp_ktime = now; if (n == 4) { if (self->tp_ptime.tv_sec == 0) self->tp_ptime = now; } else if (n == -1) { int error = su_errno(); why = " failed"; if (!su_is_blocking(error)) tport_error_report(self, error, NULL); else why = " blocking"; return -1; } SU_DEBUG_7(("%s(%p): %s to " TPN_FORMAT "%s\n", __func__, (void *)self, "sending PING", TPN_ARGS(self->tp_name), why)); return n == -1 ? -1 : 0; }
/** * Updates the modified copy of local SDP based * on application provided local SDP and remote SDP. */ static int offer_answer_step(soa_session_t *ss, enum offer_answer_action action, char const *by) { soa_static_session_t *sss = (soa_static_session_t *)ss; sdp_session_t *local = ss->ss_local->ssd_sdp; sdp_session_t local0[1]; sdp_session_t *user = ss->ss_user->ssd_sdp; unsigned user_version = ss->ss_user_version; sdp_session_t *remote = ss->ss_remote->ssd_sdp; unsigned remote_version = ss->ss_remote_version; int fresh = 0; sdp_origin_t o[1] = {{ sizeof(o) }}; sdp_connection_t *c, c0[1] = {{ sizeof(c0) }}; char c0_buffer[64]; sdp_time_t t[1] = {{ sizeof(t) }}; int *u2s = NULL, *s2u = NULL, *tbf; sdp_session_t *latest = NULL, *previous = NULL; char const *phrase = "Internal Media Error"; su_home_t tmphome[SU_HOME_AUTO_SIZE(8192)]; su_home_auto(tmphome, sizeof tmphome); SU_DEBUG_7(("soa_static_offer_answer_action(%p, %s): called\n", (void *)ss, by)); if (user == NULL) return soa_set_status(ss, 500, "No session set by user"); if (action == generate_offer) remote = NULL; else if (remote == NULL) return soa_set_status(ss, 500, "No remote SDP"); /* Pre-negotiation Step: Expand truncated remote SDP */ if (local && remote) switch (action) { case generate_answer: case process_answer: if (sdp_media_count(remote, sdp_media_any, "*", (sdp_proto_e)0, (sdp_text_t)0) < sdp_media_count(local, sdp_media_any, "*", (sdp_proto_e)0, (sdp_text_t)0)) { SU_DEBUG_5(("%s: remote %s is truncated: expanding\n", by, action == generate_answer ? "offer" : "answer")); remote = soa_sdp_expand_media(tmphome, remote, local); if (remote == NULL) return soa_set_status(ss, 500, "Cannot expand remote session"); } default: break; } /* Step A: Create local SDP session (based on user-supplied SDP) */ if (local == NULL) switch (action) { case generate_offer: case generate_answer: SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, "generating local description")); fresh = 1; local = local0; *local = *user, local->sdp_media = NULL; o->o_username = "******"; o->o_address = c0; c0->c_address = c0_buffer; if (!local->sdp_origin) local->sdp_origin = o; break; case process_answer: default: goto internal_error; } /* Step B: upgrade local SDP (add m= lines to it) */ switch (action) { case generate_offer: /* Upgrade local SDP based on user SDP */ if (local != local0 && ss->ss_local_user_version == user_version) break; if (local != local0) *local0 = *local, local = local0; SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, "upgrade with local description")); if (soa_sdp_upgrade(ss, tmphome, local, user, NULL, &u2s, &s2u) < 0) goto internal_error; break; case generate_answer: /* Upgrade local SDP based on remote SDP */ if (ss->ss_local_user_version == user_version && ss->ss_local_remote_version == remote_version) break; if (1) { if (local != local0) *local0 = *local, local = local0; SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, "upgrade with remote description")); if (soa_sdp_upgrade(ss, tmphome, local, user, remote, &u2s, &s2u) < 0) goto internal_error; } break; case process_answer: default: break; } /* Step C: reject media */ switch (action) { case generate_offer: /* Local media is marked as rejected already in upgrade phase */ break; case generate_answer: case process_answer: if (ss->ss_local_remote_version == remote_version) break; if (soa_sdp_reject_is_needed(local, remote)) { if (local != local0) { *local0 = *local, local = local0; #define DUP_LOCAL(local) \ do { \ if (!local->sdp_media) break; \ local->sdp_media = \ sdp_media_dup_all(tmphome, local->sdp_media, local); \ if (!local->sdp_media) \ goto internal_error; \ } while (0) DUP_LOCAL(local); } SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, "marking rejected media")); soa_sdp_reject(tmphome, local, remote); } break; default: break; } /* Step D: Set media mode bits */ switch (action) { int const *s2u_; case generate_offer: case generate_answer: case process_answer: s2u_ = s2u; if (!s2u_) s2u_ = sss->sss_s2u; if (soa_sdp_mode_set(user, s2u_, local, remote, ss->ss_hold, 1)) { if (local != local0) { *local0 = *local, local = local0; DUP_LOCAL(local); } soa_sdp_mode_set(user, s2u_, local, remote, ss->ss_hold, 0); } break; default: break; } /* Step E: Upgrade codecs by answer. */ switch (action) { case process_answer: /* Upgrade local SDP based on remote SDP */ if (ss->ss_local_remote_version == remote_version) break; if (1 /* We don't have good test for codecs */) { SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, "upgrade codecs with remote description")); if (local != local0) { *local0 = *local, local = local0; DUP_LOCAL(local); } soa_sdp_session_upgrade_rtpmaps(ss, local, remote); } break; case generate_offer: case generate_answer: default: break; } /* Step F0: Initialize o= line */ if (fresh) { if (user->sdp_origin) { o->o_username = user->sdp_origin->o_username; if (user->sdp_origin->o_address) o->o_address = user->sdp_origin->o_address; if (user->sdp_origin->o_id) o->o_id = user->sdp_origin->o_id; if (user->sdp_origin->o_version && user->sdp_origin->o_version != o->o_version) { o->o_version = user->sdp_origin->o_version; o->o_version--; } } if (soa_init_sdp_origin_with_session(ss, o, c0_buffer, local) < 0) { phrase = "Cannot Get IP Address for Session Description"; goto internal_error; } local->sdp_origin = o; } /* Step F: Update c= line(s) */ switch (action) { sdp_connection_t *user_c, *local_c; case generate_offer: case generate_answer: user_c = user->sdp_connection; if (!soa_check_sdp_connection(user_c)) user_c = NULL; local_c = local->sdp_connection; if (!soa_check_sdp_connection(local_c)) local_c = NULL; if (ss->ss_local_user_version != user_version || local_c == NULL || (user_c != NULL && sdp_connection_cmp(local_c, user_c))) { sdp_media_t *m; if (user_c) c = user_c; else c = local->sdp_origin->o_address; /* Every m= line (even rejected one) must have a c= line * or there must be a c= line at session level */ for (m = local->sdp_media; m; m = m->m_next) if (m->m_connections == NULL) break; if (m) { if (local != local0) { *local0 = *local, local = local0; DUP_LOCAL(local); } local->sdp_connection = c; } } break; default: break; } soa_description_free(ss, ss->ss_previous); su_free(ss->ss_home, sss->sss_previous.u2s), sss->sss_previous.u2s = NULL; su_free(ss->ss_home, sss->sss_previous.s2u), sss->sss_previous.s2u = NULL; if (u2s) { u2s = u2s_alloc(ss->ss_home, u2s); s2u = u2s_alloc(ss->ss_home, s2u); if (!u2s || !s2u) goto internal_error; } if (ss->ss_local->ssd_sdp != local && sdp_session_cmp(ss->ss_local->ssd_sdp, local)) { int bump; switch (action) { case generate_offer: bump = sdp_session_cmp(local, sss->sss_latest); break; case generate_answer: bump = 1; break; case process_answer: default: bump = 0; break; } if (bump) { /* Upgrade the version number */ if (local->sdp_origin != o) *o = *local->sdp_origin, local->sdp_origin = o; o->o_version++; } /* Do sanity checks for the created SDP */ if (!local->sdp_subject) /* s= is mandatory */ local->sdp_subject = "-"; if (!local->sdp_time) /* t= is mandatory */ local->sdp_time = t; if (action == generate_offer) { /* Keep a copy of previous session state */ int *previous_u2s = u2s_alloc(ss->ss_home, sss->sss_u2s); int *previous_s2u = u2s_alloc(ss->ss_home, sss->sss_s2u); if ((sss->sss_u2s && !previous_u2s) || (sss->sss_s2u && !previous_s2u)) goto internal_error; *ss->ss_previous = *ss->ss_local; memset(ss->ss_local, 0, (sizeof *ss->ss_local)); ss->ss_previous_user_version = ss->ss_local_user_version; ss->ss_previous_remote_version = ss->ss_local_remote_version; sss->sss_previous.u2s = previous_u2s; sss->sss_previous.s2u = previous_s2u; } SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, "storing local description")); /* Update the unparsed and pretty-printed descriptions */ if (soa_description_set(ss, ss->ss_local, local, NULL, 0) < 0) { if (action == generate_offer) { /* Remove 2nd reference to local session state */ memset(ss->ss_previous, 0, (sizeof *ss->ss_previous)); ss->ss_previous_user_version = 0; ss->ss_previous_remote_version = 0; su_free(ss->ss_home, sss->sss_previous.u2s), sss->sss_previous.u2s = NULL; su_free(ss->ss_home, sss->sss_previous.s2u), sss->sss_previous.s2u = NULL; } su_free(ss->ss_home, u2s), su_free(ss->ss_home, s2u); goto internal_error; } if (bump) { latest = sdp_session_dup(ss->ss_home, ss->ss_local->ssd_sdp); previous = sss->sss_latest; } } if (u2s) { tbf = sss->sss_u2s, sss->sss_u2s = u2s, su_free(ss->ss_home, tbf); tbf = sss->sss_s2u, sss->sss_s2u = s2u, su_free(ss->ss_home, tbf); } /* Update version numbers */ switch (action) { case generate_offer: ss->ss_local_user_version = user_version; sss->sss_latest = latest; break; case generate_answer: ss->ss_local_user_version = user_version; ss->ss_local_remote_version = remote_version; sss->sss_latest = latest; break; case process_answer: ss->ss_local_remote_version = remote_version; default: break; } if (previous) su_free(ss->ss_home, previous); su_home_deinit(tmphome); return 0; internal_error: su_home_deinit(tmphome); return soa_set_status(ss, 500, phrase); }
/** Update mode within session. * * @sa soatag_hold * * @retval 1 if session was changed (or to be changed, if @a dryrun is nonzero) */ static int soa_sdp_mode_set(sdp_session_t const *user, int const *s2u, sdp_session_t *session, sdp_session_t const *remote, char const *hold, int dryrun) { sdp_media_t *sm; sdp_media_t const *rm, *rm_next, *um; int retval = 0, i, j; int hold_all; int inactive_all; char const *hold_media = NULL; sdp_mode_t send_mode, recv_mode; SU_DEBUG_7(("soa_sdp_mode_set(%p, %p, \"%s\"): called\n", (void *)session, (void *)remote, hold ? hold : "")); if (!session || !session->sdp_media) return 0; rm = remote ? remote->sdp_media : NULL, rm_next = NULL; hold_all = su_strmatch(hold, "*"); inactive_all = su_strmatch(hold, "#"); i = 0; for (sm = session->sdp_media; sm; sm = sm->m_next, rm = rm_next, i++) { rm_next = rm ? rm->m_next : NULL; if (sm->m_rejected) continue; assert(s2u); for (j = 0, um = user->sdp_media; j != s2u[i]; um = um->m_next, j++) { if (!um) break; } if (um == NULL) { if (dryrun) return 1; else retval = 1; sm->m_rejected = 1; sm->m_mode = sdp_inactive; sm->m_port = 0; continue; } send_mode = (sdp_mode_t)(um->m_mode & sdp_sendonly); if (rm) send_mode = (rm->m_mode & sdp_recvonly) ? sdp_sendonly : 0; recv_mode = (sdp_mode_t)(um->m_mode & sdp_recvonly); if (rm && rm->m_mode == sdp_inactive) { send_mode = recv_mode = (sdp_mode_t)0; } else if (inactive_all) { send_mode = recv_mode = (sdp_mode_t)0; } else if (hold_all) { recv_mode = (sdp_mode_t)0; } else if (hold && (hold_media = su_strcasestr(hold, sm->m_type_name))) { recv_mode = (sdp_mode_t)0; hold_media += strlen(sm->m_type_name); hold_media += strspn(hold_media, " \t"); if (hold_media[0] == '=') { hold_media += strspn(hold, " \t"); if (su_casenmatch(hold_media, "inactive", strlen("inactive"))) recv_mode = send_mode = (sdp_mode_t)0; } } if (sm->m_mode != (unsigned)(recv_mode | send_mode)) { if (dryrun) return 1; else retval = 1; sm->m_mode = recv_mode | send_mode; } } return retval; }
int tls_connect(su_root_magic_t *magic, su_wait_t *w, tport_t *self) { tport_master_t *mr = self->tp_master; tport_tls_t *tlstp = (tport_tls_t *)self; tls_t *tls; int events = su_wait_events(w, self->tp_socket); int error; SU_DEBUG_7(("%s(%p): events%s%s%s%s\n", __func__, (void *)self, events & (SU_WAIT_CONNECT) ? " CONNECTING" : "", events & SU_WAIT_IN ? " NEGOTIATING" : "", events & SU_WAIT_ERR ? " ERROR" : "", events & SU_WAIT_HUP ? " HANGUP" : "")); #if HAVE_POLL assert(w->fd == self->tp_socket); #endif if (events & SU_WAIT_ERR) tport_error_event(self); if (events & SU_WAIT_HUP && !self->tp_closed) tport_hup_event(self); if (self->tp_closed) return 0; error = su_soerror(self->tp_socket); if (error) { tport_error_report(self, error, NULL); return 0; } if ((tls = tlstp->tlstp_context) == NULL) { SU_DEBUG_3(("%s(%p): Error: no TLS context data for connected socket.\n", __func__, (void *)tlstp)); tport_close(self); tport_set_secondary_timer(self); return 0; } if (self->tp_is_connected == 0) { int ret, status; ret = self->tp_accepted ? SSL_accept(tls->con) : SSL_connect(tls->con); status = SSL_get_error(tls->con, ret); switch (status) { case SSL_ERROR_WANT_READ: /* OpenSSL is waiting for the peer to send handshake data */ self->tp_events = SU_WAIT_IN | SU_WAIT_ERR | SU_WAIT_HUP; su_root_eventmask(mr->mr_root, self->tp_index, self->tp_socket, self->tp_events); return 0; case SSL_ERROR_WANT_WRITE: /* OpenSSL is waiting for the peer to receive handshake data */ self->tp_events = SU_WAIT_IN | SU_WAIT_ERR | SU_WAIT_HUP | SU_WAIT_OUT; su_root_eventmask(mr->mr_root, self->tp_index, self->tp_socket, self->tp_events); return 0; case SSL_ERROR_NONE: /* TLS Handshake complete */ status = tls_post_connection_check(self, tls); if ( status == X509_V_OK ) { su_wait_t wait[1] = {SU_WAIT_INIT}; tport_master_t *mr = self->tp_master; su_root_deregister(mr->mr_root, self->tp_index); self->tp_index = -1; self->tp_events = SU_WAIT_IN | SU_WAIT_ERR | SU_WAIT_HUP; if ((su_wait_create(wait, self->tp_socket, self->tp_events) == -1) || ((self->tp_index = su_root_register(mr->mr_root, wait, tport_wakeup, self, 0)) == -1)) { tport_close(self); tport_set_secondary_timer(self); return 0; } tls->read_events = SU_WAIT_IN; tls->write_events = 0; self->tp_is_connected = 1; self->tp_verified = tls->x509_verified; self->tp_subjects = tls->subjects; if (tport_has_queued(self)) tport_send_event(self); else tport_set_secondary_timer(self); return 0; } break; default: { char errbuf[64]; ERR_error_string_n(status, errbuf, 64); SU_DEBUG_3(("%s(%p): TLS setup failed (%s)\n", __func__, (void *)self, errbuf)); } break; } } /* TLS Handshake Failed or Peer Certificate did not Verify */ tport_close(self); tport_set_secondary_timer(self); return 0; }
su_inline int tls_post_connection_check(tport_t *self, tls_t *tls) { X509 *cert; int extcount; int i, j, error; if (!tls) return -1; cert = SSL_get_peer_certificate(tls->con); if (!cert) { SU_DEBUG_7(("%s(%p): Peer did not provide X.509 Certificate.\n", __func__, (void *) self)); if (self->tp_accepted && tls->verify_incoming) return X509_V_ERR_CERT_UNTRUSTED; else if (!self->tp_accepted && tls->verify_outgoing) return X509_V_ERR_CERT_UNTRUSTED; else return X509_V_OK; } tls->subjects = su_strlst_create(tls->home); if (!tls->subjects) return X509_V_ERR_OUT_OF_MEM; extcount = X509_get_ext_count(cert); /* Find matching subjectAltName.DNS */ for (i = 0; i < extcount; i++) { X509_EXTENSION *ext; char const *name; #if OPENSSL_VERSION_NUMBER > 0x10000000L const X509V3_EXT_METHOD *vp; #else X509V3_EXT_METHOD *vp; #endif STACK_OF(CONF_VALUE) *values; CONF_VALUE *value; void *d2i; ext = X509_get_ext(cert, i); name = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext))); if (strcmp(name, "subjectAltName") != 0) continue; vp = X509V3_EXT_get(ext); if (!vp) continue; d2i = X509V3_EXT_d2i(ext); values = vp->i2v(vp, d2i, NULL); for (j = 0; j < sk_CONF_VALUE_num(values); j++) { value = sk_CONF_VALUE_value(values, j); if (strcmp(value->name, "DNS") == 0) su_strlst_dup_append(tls->subjects, value->value); if (strcmp(value->name, "IP") == 0) su_strlst_dup_append(tls->subjects, value->value); else if (strcmp(value->name, "URI") == 0) su_strlst_dup_append(tls->subjects, value->value); } } { X509_NAME *subject; char name[256]; subject = X509_get_subject_name(cert); if (subject) { if (X509_NAME_get_text_by_NID(subject, NID_commonName, name, sizeof name) > 0) { usize_t k, N = su_strlst_len(tls->subjects); name[(sizeof name) - 1] = '\0'; for (k = 0; k < N; k++) if (su_casematch(su_strlst_item(tls->subjects, k), name) == 0) break; if (k >= N) su_strlst_dup_append(tls->subjects, name); } } } X509_free(cert); error = SSL_get_verify_result(tls->con); if (cert && error == X509_V_OK) tls->x509_verified = 1; if (tport_log->log_level >= 7) { int i, len = su_strlst_len(tls->subjects); for (i=0; i < len; i++) SU_DEBUG_7(("%s(%p): Peer Certificate Subject %i: %s\n", \ __func__, (void *)self, i, su_strlst_item(tls->subjects, i))); if (i == 0) SU_DEBUG_7(("%s(%p): Peer Certificate provided no usable subjects.\n", __func__, (void *)self)); } /* Verify incoming connections */ if (self->tp_accepted) { if (!tls->verify_incoming) return X509_V_OK; if (!tls->x509_verified) return error; if (tls->verify_subj_in) { su_strlst_t const *subjects = self->tp_pri->pri_primary->tp_subjects; int i, items; items = subjects ? su_strlst_len(subjects) : 0; if (items == 0) return X509_V_OK; for (i=0; i < items; i++) { if (tport_subject_search(su_strlst_item(subjects, i), tls->subjects)) return X509_V_OK; } SU_DEBUG_3(("%s(%p): Peer Subject Mismatch (incoming connection)\n", \ __func__, (void *)self)); return X509_V_ERR_CERT_UNTRUSTED; } } /* Verify outgoing connections */ else { char const *subject = self->tp_canon; if (!tls->verify_outgoing) return X509_V_OK; if (!tls->x509_verified || !subject) return error; if (tls->verify_subj_out) { if (tport_subject_search(subject, tls->subjects)) return X509_V_OK; /* Subject match found in verified certificate chain */ SU_DEBUG_3(("%s(%p): Peer Subject Mismatch (%s)\n", \ __func__, (void *)self, subject)); return X509_V_ERR_CERT_UNTRUSTED; } } return error; }
int nua_base_client_check_restart(nua_client_request_t *cr, int status, char const *phrase, sip_t const *sip) { nua_handle_t *nh = cr->cr_owner; nta_outgoing_t *orq; #if 0 if (status == 302 || status == 305) { sip_route_t r[1]; if (!can_redirect(sip->sip_contact, cr->cr_method)) return 0; switch (status) { case 302: if (nua_dialog_zap(nh, nh->nh_ds) == 0 && nua_client_set_target(cr, sip->sip_contact->m_url) >= 0) return nua_client_restart(cr, 100, "Redirected"); break; case 305: sip_route_init(r); *r->r_url = *sip->sip_contact->m_url; if (nua_dialog_zap(nh, nh->nh_ds) == 0 && sip_add_dup(cr->cr_msg, cr->cr_sip, (sip_header_t *)r) >= 0) return nua_client_restart(cr, 100, "Redirected via a proxy"); break; default: break; } } #endif if (status == 423) { unsigned my_expires = 0; if (cr->cr_sip->sip_expires) my_expires = cr->cr_sip->sip_expires->ex_delta; if (sip->sip_min_expires && sip->sip_min_expires->me_delta > my_expires) { sip_expires_t ex[1]; sip_expires_init(ex); ex->ex_delta = sip->sip_min_expires->me_delta; if (sip_add_dup(cr->cr_msg, NULL, (sip_header_t *)ex) < 0) return 0; return nua_client_restart(cr, 100, "Re-Negotiating Expiration"); } } if ((status == 401 && sip->sip_www_authenticate) || (status == 407 && sip->sip_proxy_authenticate)) { int server = 0, proxy = 0; if (sip->sip_www_authenticate) server = auc_challenge(&nh->nh_auth, nh->nh_home, sip->sip_www_authenticate, sip_authorization_class); if (sip->sip_proxy_authenticate) proxy = auc_challenge(&nh->nh_auth, nh->nh_home, sip->sip_proxy_authenticate, sip_proxy_authorization_class); if (server >= 0 && proxy >= 0) { int invalid = cr->cr_challenged && server + proxy == 0; cr->cr_challenged = 1; if (invalid) { /* Bad username/password */ SU_DEBUG_7(("nua(%p): bad credentials, clearing them\n", (void *)nh)); auc_clear_credentials(&nh->nh_auth, NULL, NULL); } else if (auc_has_authorization(&nh->nh_auth)) return nua_client_restart(cr, 100, "Request Authorized by Cache"); orq = cr->cr_orq, cr->cr_orq = NULL; cr->cr_waiting = cr->cr_wait_for_cred = 1; nua_client_report(cr, status, phrase, NULL, orq, NULL); nta_outgoing_destroy(orq); cr->cr_status = 0, cr->cr_phrase = NULL; nua_client_request_unref(cr); return 1; } } if (0 && 500 <= status && status < 600 && sip->sip_retry_after && sip->sip_retry_after->af_delta < 32) { su_timer_t *timer; char phrase[18]; /* Retry After XXXX\0 */ timer = su_timer_create(su_root_task(nh->nh_nua->nua_root), 0); if (su_timer_set_interval(timer, nua_client_restart_after, cr, sip->sip_retry_after->af_delta * 1000) < 0) { su_timer_destroy(timer); return 0; /* Too bad */ } cr->cr_timer = timer; /* This takes over cr reference from orq */ snprintf(phrase, sizeof phrase, "Retry After %u", (unsigned)sip->sip_retry_after->af_delta); orq = cr->cr_orq, cr->cr_orq = NULL; cr->cr_waiting = 1; nua_client_report(cr, 100, phrase, NULL, orq, NULL); nta_outgoing_destroy(orq); cr->cr_status = 0, cr->cr_phrase = NULL; return 1; } return 0; /* This was a final response that cannot be restarted. */ }
/**Process stun messagee. * * @retval -1 error * @retval 3 stun message received, ignore */ int tport_recv_stun_dgram(tport_t const *self, msg_t **in_out_msg, su_sockaddr_t *from, socklen_t fromlen) { int retval = -1; msg_t *msg; uint8_t *request; size_t n; assert(in_out_msg); assert(*in_out_msg); msg = *in_out_msg; request = msg_buf_committed_data(msg); n = msg_buf_committed(msg); if (n < 20 || request == NULL) { su_seterrno(EBADMSG); retval = -1; } else if (request[0] == 1) { /* This is a response. */ if (self->tp_pri->pri_vtable->vtp_stun_response) { if (self->tp_pri->pri_vtable->vtp_stun_response(self, request, n, from, fromlen) < 0) retval = -1; } else SU_DEBUG_7(("tport(%p): recv_stun_dgram(): " "ignoring request with "MOD_ZU" bytes\n", (void *)self, n)); } else if (request[0] == 0 && self->tp_master->mr_stun_server) { tport_stun_server_vtable_t const *vst = tport_stun_server_vtable; vst->vst_request(self->tp_master->mr_stun_server, self->tp_socket, request, n, (void *)from, fromlen); } else if (request[0] == 0) { /* Respond to stun request with a simple error message. */ int const status = 600; char const *error = "Not Implemented"; size_t unpadded = strlen(error); uint16_t elen; uint8_t dgram[128]; if (unpadded > sizeof(dgram) - 28) unpadded = sizeof(dgram) - 28; elen = (uint16_t)unpadded; elen = (elen + 3) & -4; /* Round up to 4 */ SU_DEBUG_7(("tport(%p): recv_stun_dgram(): " "responding %u %s\n", (void *)self, status, error)); /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | STUN Message Type | Message Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Transaction ID +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define set16(b, offset, value) \ (((b)[(offset) + 0] = ((value) >> 8) & 255), \ ((b)[(offset) + 1] = (value) & 255)) /* Respond to request */ dgram[0] = 1; /* Mark as response */ dgram[1] = request[1] | 0x10; /* Mark as error response */ set16(dgram, 2, elen + 4 + 4); /* TransactionID is there at bytes 4..19 */ memcpy(dgram + 4, request + 4, 16); /* TLV At 20: 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Type | Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ set16(dgram, 20, 0x0009); /* ERROR-CODE */ set16(dgram, 22, elen + 4); /* ERROR-CODE at 24: 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0 |Class| Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Reason Phrase (variable) .. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ dgram[24] = 0, dgram[25] = 0; dgram[26] = status / 100, dgram[27] = status % 100; memcpy(dgram + 28, error, unpadded); memset(dgram + 28 + unpadded, 0, elen - unpadded); sendto(self->tp_socket, (void *)dgram, 28 + elen, 0, (void *)from, fromlen); #undef set16 } else { SU_DEBUG_0(("tport(%p): recv_stun_dgram(): internal error\n", (void *)self)); su_seterrno(EBADMSG); retval = -1; } *in_out_msg = NULL, msg_destroy(msg); return retval; }
/** Receive from stream. * * @retval -1 error * @retval 0 end-of-stream * @retval 1 normal receive * @retval 2 incomplete recv, recv again * */ int tport_recv_stream(tport_t *self) { msg_t *msg; ssize_t n, N, veclen; int err, initial; msg_iovec_t iovec[msg_n_fragments] = {{ 0 }}; N = su_getmsgsize(self->tp_socket); if (N == 0) { if (self->tp_msg) msg_recv_commit(self->tp_msg, 0, 1); return 0; /* End of stream */ } if (N == -1) { err = su_errno(); SU_DEBUG_1(("%s(%p): su_getmsgsize(): %s (%d)\n", __func__, (void *)self, su_strerror(err), err)); return -1; } initial = self->tp_msg == NULL; memset(&self->tp_ptime, 0, sizeof self->tp_ptime); while (initial && N <= 8) { /* Check for whitespace */ char crlf[9]; size_t i; n = su_recv(self->tp_socket, crlf, N, MSG_PEEK); i = ws_span(crlf, n); if (i == 0) break; n = su_recv(self->tp_socket, crlf, i, 0); if (n <= 0) return (int)n; SU_DEBUG_7(("%s(%p): received keepalive (total %u)\n", __func__, (void *)self, self->tp_ping)); N -= n, self->tp_ping += n; tport_recv_bytes(self, n, n); if (N == 0) { /* outbound-10 section 3.5.1 - send pong */ if (self->tp_ping >= 4) tport_tcp_pong(self); return 1; } } veclen = tport_recv_iovec(self, &self->tp_msg, iovec, N, 0); if (veclen == -1) return -1; msg = self->tp_msg; msg_set_address(msg, self->tp_addr, (socklen_t)(self->tp_addrlen)); n = su_vrecv(self->tp_socket, iovec, veclen, 0, NULL, NULL); if (n == SOCKET_ERROR) return tport_recv_error_report(self); assert(n <= N); tport_recv_bytes(self, n, n); /* Check if message contains only whitespace */ /* This can happen if multiple PINGs are received at once */ if (initial) { size_t i = ws_span(iovec->siv_base, iovec->siv_len); if (i + self->tp_ping >= 4) tport_tcp_pong(self); else self->tp_ping += (unsigned short)i; if (i == iovec->siv_len && veclen == 1) { SU_DEBUG_7(("%s(%p): received %u bytes of keepalive\n", __func__, (void *)self, (unsigned)i)); msg_destroy(self->tp_msg), self->tp_msg = NULL; return 1; } } /* Write the received data to the message dump file */ if (self->tp_master->mr_dump_file) tport_dump_iovec(self, msg, n, iovec, veclen, "recv", "from"); if (self->tp_master->mr_capt_sock) tport_capt_msg(self, msg, n, iovec, veclen, "recv"); /* Mark buffer as used */ msg_recv_commit(msg, n, n == 0); if (n > 0) self->tp_ping = 0; return n != 0; }
/* Callback from nea_server asking nua to authorize subscription */ static void authorize_watcher(nea_server_t *nes, nua_handle_t *nh, nea_event_t *ev, nea_subnode_t *sn, sip_t const *sip) { nua_t *nua = nh->nh_nua; msg_t *msg = NULL; nta_incoming_t *irq = NULL; int substate = sn->sn_state; int status; char const *phrase; SET_STATUS1(SIP_200_OK); /* OK. In nhp (nua_handle_preferences_t) structure we have the current default action (or state) for incoming subscriptions. Action can now be modified by the application with NUTAG_SUBSTATE(). */ irq = nea_sub_get_request(sn->sn_subscriber); msg = nta_incoming_getrequest(irq); if (sn->sn_state == nea_embryonic) { char const *what; substate = NH_PGET(nh, substate); if (substate == nua_substate_embryonic) substate = nua_substate_pending; if (substate == nua_substate_terminated) { what = "rejected"; SET_STATUS1(SIP_403_FORBIDDEN); } else if (substate == nua_substate_pending) { what = "pending"; SET_STATUS1(SIP_202_ACCEPTED); } else { what = "active"; } SU_DEBUG_7(("nua(%p): authorize_watcher: %s\n", (void *)nh, what)); nea_sub_auth(sn->sn_subscriber, (nea_state_t)substate, TAG_IF(substate == nua_substate_pending, NEATAG_FAKE(1)), TAG_IF(substate == nua_substate_terminated, NEATAG_REASON("rejected")), TAG_END()); } else if (sn->sn_state == nea_terminated || sn->sn_expires == 0) { substate = nua_substate_terminated; nea_server_flush(nes, NULL); SU_DEBUG_7(("nua(%p): authorize_watcher: %s\n", (void *)nh, "watcher is removed")); } nua_stack_tevent(nua, nh, msg, nua_i_subscription, status, phrase, NUTAG_SUBSTATE(substate), NEATAG_SUB(sn->sn_subscriber), TAG_END()); }
/** Verify digest authentication */ void auth_check_digest(auth_mod_t *am, auth_status_t *as, auth_response_t *ar, auth_challenger_t const *ach) { char const *a1; auth_hexmd5_t a1buf, response; auth_passwd_t *apw; char const *phrase; msg_time_t now = msg_now(); if (am == NULL || as == NULL || ar == NULL || ach == NULL) { if (as) { as->as_status = 500, as->as_phrase = "Internal Server Error"; as->as_response = NULL; } return; } phrase = "Bad authorization"; #define PA "Authorization missing " if ((!ar->ar_username && (phrase = PA "username")) || (!ar->ar_nonce && (phrase = PA "nonce")) || (!ar->ar_uri && (phrase = PA "URI")) || (!ar->ar_response && (phrase = PA "response")) || /* (!ar->ar_opaque && (phrase = PA "opaque")) || */ /* Check for qop */ (ar->ar_qop && ((ar->ar_auth && !su_casematch(ar->ar_qop, "auth") && !su_casematch(ar->ar_qop, "\"auth\"")) || (ar->ar_auth_int && !su_casematch(ar->ar_qop, "auth-int") && !su_casematch(ar->ar_qop, "\"auth-int\""))) && (phrase = PA "has invalid qop"))) { assert(phrase); SU_DEBUG_5(("auth_method_digest: 400 %s\n", phrase)); as->as_status = 400, as->as_phrase = phrase; as->as_response = NULL; return; } if (as->as_nonce_issued == 0 /* Already validated nonce */ && auth_validate_digest_nonce(am, as, ar, now) < 0) { as->as_blacklist = am->am_blacklist; auth_challenge_digest(am, as, ach); return; } if (as->as_stale) { auth_challenge_digest(am, as, ach); return; } apw = auth_mod_getpass(am, ar->ar_username, ar->ar_realm); if (apw && apw->apw_hash) a1 = apw->apw_hash; else if (apw && apw->apw_pass) auth_digest_a1(ar, a1buf, apw->apw_pass), a1 = a1buf; else auth_digest_a1(ar, a1buf, "xyzzy"), a1 = a1buf, apw = NULL; if (ar->ar_md5sess) auth_digest_a1sess(ar, a1buf, a1), a1 = a1buf; auth_digest_response(ar, response, a1, as->as_method, as->as_body, as->as_bodylen); if (!apw || strcmp(response, ar->ar_response)) { if (am->am_forbidden) { as->as_status = 403, as->as_phrase = "Forbidden"; as->as_response = NULL; as->as_blacklist = am->am_blacklist; } else { auth_challenge_digest(am, as, ach); as->as_blacklist = am->am_blacklist; } SU_DEBUG_5(("auth_method_digest: response did not match\n")); return; } assert(apw); as->as_user = apw->apw_user; as->as_anonymous = apw == am->am_anon_user; as->as_ident = apw->apw_ident; if (am->am_nextnonce || am->am_mutual) auth_info_digest(am, as, ach); if (am->am_challenge) auth_challenge_digest(am, as, ach); SU_DEBUG_7(("auth_method_digest: successful authentication\n")); as->as_status = 0; /* Successful authentication! */ as->as_phrase = ""; }