/* §5.2 */ static int answer_REQ_VOTE( struct ticket_config *tk, struct booth_site *sender, struct booth_site *leader, struct boothc_ticket_msg *msg ) { int valid; struct boothc_ticket_msg omsg; cmd_result_t inappr_reason; int reason; inappr_reason = test_reason(tk, sender, leader, msg); if (inappr_reason) return send_reject(sender, tk, inappr_reason, msg); valid = term_time_left(tk); reason = ntohl(msg->header.reason); /* valid tickets are not allowed only if the sender thinks * the ticket got lost */ if (sender != tk->leader && valid && reason != OR_STEPDOWN) { tk_log_warn("election from %s with reason %s rejected " "(we have %s as ticket owner), ticket still valid for %ds", site_string(sender), state_to_string(reason), site_string(tk->leader), valid); return send_reject(sender, tk, RLT_TERM_STILL_VALID, msg); } if (term_too_low(tk, sender, leader, msg)) return 0; /* set this, so that we know not to send status for the * ticket */ tk->in_election = 1; /* reset ticket's leader on not valid tickets */ if (!valid) set_leader(tk, NULL); /* if it's a newer term or ... */ if (newer_term(tk, sender, leader, msg, 1)) { clear_election(tk); goto vote_for_sender; } /* ... we didn't vote yet, then vote for the sender */ /* §5.2, §5.4 */ if (!tk->voted_for) { vote_for_sender: tk->voted_for = sender; record_vote(tk, sender, leader); } init_ticket_msg(&omsg, OP_VOTE_FOR, OP_REQ_VOTE, RLT_SUCCESS, 0, tk); omsg.ticket.leader = htonl(get_node_id(tk->voted_for)); return booth_udp_send_auth(sender, &omsg, sendmsglen(&omsg)); }
static int process_UPDATE ( struct ticket_config *tk, struct booth_site *sender, struct booth_site *leader, struct boothc_ticket_msg *msg ) { if (is_owned(tk) && sender != tk->leader) { tk_log_warn("different leader %s wants to update " "our ticket, sending reject", site_string(leader)); return send_reject(sender, tk, RLT_TERM_OUTDATED, msg); } tk_log_debug("leader %s wants to update our ticket", site_string(leader)); become_follower(tk, msg); set_leader(tk, leader); ticket_write(tk); /* run ticket_cron if the ticket expires */ set_ticket_wakeup(tk); return send_msg(OP_ACK, tk, sender, msg); }
static int term_too_low(struct ticket_config *tk, struct booth_site *sender, struct booth_site *leader, struct boothc_ticket_msg *msg) { uint32_t term; term = ntohl(msg->ticket.term); /* §5.1 */ if (term < tk->current_term) { tk_log_info("sending reject to %s, its term too low " "(%d vs. %d)", site_string(sender), term, tk->current_term ); send_reject(sender, tk, RLT_TERM_OUTDATED, msg); return 1; } return 0; }
/* For follower. */ static int answer_HEARTBEAT ( struct ticket_config *tk, struct booth_site *sender, struct booth_site *leader, struct boothc_ticket_msg *msg ) { uint32_t term; term = ntohl(msg->ticket.term); tk_log_debug("heartbeat from leader: %s, have %s; term %d vs %d", site_string(leader), ticket_leader_string(tk), term, tk->current_term); if (term < tk->current_term) { if (sender == tk->leader) { tk_log_info("trusting leader %s with a lower term (%d vs %d)", site_string(leader), term, tk->current_term); } else if (is_owned(tk)) { tk_log_warn("different leader %s with a lower term " "(%d vs %d), sending reject", site_string(leader), term, tk->current_term); return send_reject(sender, tk, RLT_TERM_OUTDATED, msg); } } /* got heartbeat, no rejects expected anymore */ tk->expect_more_rejects = 0; /* Needed? */ newer_term(tk, sender, leader, msg, 0); become_follower(tk, msg); /* Racy??? */ assert(sender == leader || !leader); set_leader(tk, leader); /* Ack the heartbeat (we comply). */ return send_msg(OP_ACK, tk, sender, msg); }
/** * Every time a new dialog is created (from a new INVITE) the dialog * module will call this callback function. We need to track the * dialogs lifespan from this point forward until it is terminated * with a BYE, CANCEL, etc. In the process, we will see if either or * both ends of the conversation supports SIP Session Timers and setup * the dialog timeout to expire at the session timer expire time. Each * time the new re-INVITE is seen to update the SST, we will reset the * life span of the dialog to match it. * * This function will setup the other types of dialog callbacks * required to track the lifespan of the dialog. It will also start * the state tracking to figure out if and who supports SST. * * As per RFC4028: Request handling: * * - The proxy may insert a SE header if none found. * - The SE value can be anything >= Min-SE (if found) * - The proxy MUST NOT add a refresher parameter to the SE. * * - If SE is already there, the Proxy can reduce its value but no * lower then the Min-SE value if present. * - If the SE value is >= Min-SE the proxy MUST NOT increase it! * - If the SE value is < Min-SE (settable by the proxy) the proxy * MUST increase the SE value to >= the new Min-SE. * - The proxy MUST NOT insert or change the refresher parameter. * * - If the supported=timer is found, the proxy may reject the request * with a 422 if the SE value is smaller then the local policy. The * 422 MUST hold the proxies Min-SE value >= 90. * - If support=timer is NOT indecated, the proxy can't reject with a * 422 but can include/increase the MIN-SE: to be = to local policy. * and increase the SE to match the new Min-SE value. * - the proxy MUST NOT insert/change the Min-SE header if * supported=timer is present. (DoS attacks) * * @param did - The dialog ID * @param type - The trigger event type (CREATED) * @param params - The pointer to nothing. As we did not attach * anything to this callback in the dialog module. */ void sst_dialog_created_CB(struct dlg_cell *did, int type, struct dlg_cb_params * params) { sst_info_t *info = NULL; sst_msg_info_t minfo; struct sip_msg* msg = params->msg; memset(&minfo, 0, sizeof(sst_msg_info_t)); /* * Only deal with messages flaged as SST interested. */ if ((msg->flags & sst_flag) != sst_flag) { LM_DBG("SST flag was not set for this request\n"); return; } /* * look only at INVITE */ if (msg->first_line.type != SIP_REQUEST || msg->first_line.u.request.method_value != METHOD_INVITE) { LM_WARN("dialog create callback called with a non-INVITE request.\n"); return; } /* * Gather all he information about SST for this message */ if (parse_msg_for_sst_info(msg, &minfo)) { LM_ERR("failed to parse sst information\n"); return; } info = (sst_info_t *)shm_malloc(sizeof(sst_info_t)); memset(info, 0, sizeof(sst_info_t)); info->requester = (minfo.se?SST_UAC:SST_UNDF); info->supported = (minfo.supported?SST_UAC:SST_UNDF); info->interval = MAX(sst_interval, 90); /* For now, will set for real * later */ if (minfo.se != 0) { /* * There is a SE already there, this is good, we just need to * check the values out a little before passing it along. */ if (minfo.se < sst_min_se) { /* * Problem, the requested Session-Expires is too small for * our local policy. We need to fix it, or reject it or * ignore it. */ if (!minfo.supported) { /* * Increase the Min-SE: value in the request and * forward it. */ char buf[80]; if (minfo.min_se) { /* We need to update, which means, remove + * insert */ remove_minse_header(msg); } info->interval = MAX(sst_min_se, minfo.min_se); snprintf(buf, 80, "Min-SE: %d\r\n", info->interval); if (append_header(msg, buf)) { LM_ERR("Could not append modified Min-SE: header\n"); } } else if (sst_reject) { /* Make sure that that all are at least 90 */ send_reject(msg, MAX(MAX(sst_min_se, minfo.min_se), 90)); shm_free(info); return; } } /* end of se < sst_min_se */ else { /* Use the INVITE SE: value */ info->interval = minfo.se; } } else { /* * No Session-Expire: stated in request. */ char buf[80]; info->interval = MAX(minfo.min_se, sst_min_se); if (minfo.min_se && minfo.min_se < sst_min_se) { remove_minse_header(msg); snprintf(buf, 80, "Min-SE: %d\r\n", info->interval); if (append_header(msg, buf)) { LM_ERR("failed to append modified Min-SE: header\n"); /* What to do? Let is slide, we can still work */ } } info->interval = MAX(info->interval, sst_interval); info->requester = SST_PXY; snprintf(buf, 80, "Session-Expires: %d\r\n", info->interval); if (append_header(msg, buf)) { LM_ERR("failed to append Session-Expires header to proxy " "requested SST.\n"); shm_free(info); return; /* Nothing we can do! */ } } /* We keep the sst_info in the dialog's vals in case of restarting */ /* No const here because of store_dlg_value's definition */ str raw_info = {(char*)info, sizeof(sst_info_t)}; if (dlg_binds->store_dlg_value(did, &info_val_name, &raw_info) != 0) { LM_ERR("No sst_info can be added to the dialog." "This dialog won't be considered after restart!\n"); } dlg_binds->set_mod_flag(did, SST_DIALOG_FLAG); setup_dialog_callbacks(did, info); /* Early setup of default timeout */ set_dialog_lifetime(did, info->interval); return; }
int raft_answer( struct ticket_config *tk, struct booth_site *sender, struct booth_site *leader, struct boothc_ticket_msg *msg ) { int cmd, req; int rv; rv = 0; cmd = ntohl(msg->header.cmd); req = ntohl(msg->header.request); if (req) tk_log_debug("got %s (req %s) from %s", state_to_string(cmd), state_to_string(req), site_string(sender)); else tk_log_debug("got %s from %s", state_to_string(cmd), site_string(sender)); /* don't process tickets with invalid term */ if (cmd != OP_STATUS && msg_term_invalid(tk, sender, leader, msg)) return 0; switch (cmd) { case OP_REQ_VOTE: rv = answer_REQ_VOTE(tk, sender, leader, msg); break; case OP_VOTE_FOR: rv = process_VOTE_FOR(tk, sender, leader, msg); break; case OP_ACK: if (tk->leader == local && tk->state == ST_LEADER) rv = process_ACK(tk, sender, leader, msg); break; case OP_HEARTBEAT: if ((tk->leader != local || !term_time_left(tk)) && (tk->state == ST_INIT || tk->state == ST_FOLLOWER || tk->state == ST_CANDIDATE)) rv = answer_HEARTBEAT(tk, sender, leader, msg); else { tk_log_warn("unexpected message %s, from %s", state_to_string(cmd), site_string(sender)); if (ticket_seems_ok(tk)) send_reject(sender, tk, RLT_TERM_STILL_VALID, msg); rv = -EINVAL; } break; case OP_UPDATE: if (((tk->leader != local && tk->leader == leader) || !is_owned(tk)) && (tk->state == ST_INIT || tk->state == ST_FOLLOWER || tk->state == ST_CANDIDATE)) { rv = process_UPDATE(tk, sender, leader, msg); } else { tk_log_warn("unexpected message %s, from %s", state_to_string(cmd), site_string(sender)); if (ticket_seems_ok(tk)) send_reject(sender, tk, RLT_TERM_STILL_VALID, msg); rv = -EINVAL; } break; case OP_REJECTED: rv = process_REJECTED(tk, sender, leader, msg); break; case OP_REVOKE: rv = process_REVOKE(tk, sender, leader, msg); break; case OP_MY_INDEX: rv = process_MY_INDEX(tk, sender, leader, msg); break; case OP_STATUS: if (!tk->in_election) rv = send_msg(OP_MY_INDEX, tk, sender, msg); break; default: tk_log_error("unknown message %s, from %s", state_to_string(cmd), site_string(sender)); rv = -EINVAL; } return rv; }
OM_uint32 KRB5_LIB_FUNCTION gss_accept_sec_context_spnego (OM_uint32 * minor_status, gss_ctx_id_t * context_handle, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t * delegated_cred_handle) { NegTokenInit init_token; OM_uint32 major_status; OM_uint32 minor_status2; gss_buffer_desc ibuf, obuf; gss_buffer_t ot = NULL; unsigned char *buf; size_t buf_size; size_t len, taglen, ni_len; int found = 0; int ret, i; memset(&init_token, 0, sizeof(init_token)); ret = gssapi_spnego_decapsulate(minor_status, input_token_buffer, &buf, &buf_size, GSS_SPNEGO_MECH); if (ret) return ret; ret = der_match_tag_and_length(buf, buf_size, KERB_CTXT, CONS, 0, &len, &taglen); if (ret) return ret; ret = decode_NegTokenInit(buf + taglen, len, &init_token, &ni_len); if (ret) { *minor_status = EINVAL; /* XXX */ return GSS_S_DEFECTIVE_TOKEN; } if (init_token.mechTypes == NULL) return send_reject (minor_status, output_token); for (i = 0; !found && i < init_token.mechTypes->len; ++i) { unsigned char mechbuf[17]; size_t mech_len; ret = der_put_oid (mechbuf + sizeof(mechbuf) - 1, sizeof(mechbuf), &init_token.mechTypes->val[i], &mech_len); if (ret) return GSS_S_DEFECTIVE_TOKEN; if (mech_len == GSS_KRB5_MECH->length && memcmp(GSS_KRB5_MECH->elements, mechbuf + sizeof(mechbuf) - mech_len, mech_len) == 0) found = 1; } if (!found) return send_reject (minor_status, output_token); if (init_token.mechToken != NULL) { ibuf.length = init_token.mechToken->length; ibuf.value = init_token.mechToken->data; major_status = gss_accept_sec_context(minor_status, context_handle, acceptor_cred_handle, &ibuf, input_chan_bindings, src_name, mech_type, &obuf, ret_flags, time_rec, delegated_cred_handle); if (GSS_ERROR(major_status)) { send_reject (&minor_status2, output_token); return major_status; } ot = &obuf; } ret = send_accept (&minor_status2, output_token, ot); if (ot != NULL) gss_release_buffer(&minor_status2, ot); return ret; }
static OM_uint32 GSSAPI_CALLCONV acceptor_continue (OM_uint32 * minor_status, gss_ctx_id_t * context_handle, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t *delegated_cred_handle ) { OM_uint32 ret, ret2, minor; NegotiationToken nt; size_t nt_len; NegTokenResp *na; unsigned int negResult = accept_incomplete; gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; gss_buffer_t mech_output_token = GSS_C_NO_BUFFER; gss_buffer_desc mech_buf; gssspnego_ctx ctx; mech_buf.value = NULL; ctx = (gssspnego_ctx)*context_handle; /* * The GSS-API encapsulation is only present on the initial * context token (negTokenInit). */ ret = decode_NegotiationToken(input_token_buffer->value, input_token_buffer->length, &nt, &nt_len); if (ret) { *minor_status = ret; return GSS_S_DEFECTIVE_TOKEN; } if (nt.element != choice_NegotiationToken_negTokenResp) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } na = &nt.u.negTokenResp; if (na->negResult != NULL) { negResult = *(na->negResult); } HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); { gss_buffer_desc ibuf, obuf; int require_mic, get_mic = 0; int require_response; heim_octet_string *mic; if (na->responseToken != NULL) { ibuf.length = na->responseToken->length; ibuf.value = na->responseToken->data; mech_input_token = &ibuf; } else { ibuf.value = NULL; ibuf.length = 0; } if (mech_input_token != GSS_C_NO_BUFFER) { if (ctx->mech_src_name != GSS_C_NO_NAME) gss_release_name(&minor, &ctx->mech_src_name); ret = gss_accept_sec_context(&minor, &ctx->negotiated_ctx_id, acceptor_cred_handle, mech_input_token, input_chan_bindings, &ctx->mech_src_name, &ctx->negotiated_mech_type, &obuf, &ctx->mech_flags, &ctx->mech_time_rec, delegated_cred_handle); if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { mech_output_token = &obuf; } if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) { free_NegotiationToken(&nt); gss_mg_collect_error(ctx->negotiated_mech_type, ret, minor); send_reject (minor_status, output_token); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return ret; } if (ret == GSS_S_COMPLETE) ctx->open = 1; } else ret = GSS_S_COMPLETE; ret2 = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic); if (ret2) goto out; ctx->require_mic = require_mic; mic = na->mechListMIC; if (mic != NULL) require_mic = 1; if (ret == GSS_S_COMPLETE) ret = acceptor_complete(minor_status, ctx, &get_mic, &mech_buf, mech_input_token, mech_output_token, na->mechListMIC, output_token); if (ctx->mech_flags & GSS_C_DCE_STYLE) require_response = (negResult != accept_completed); else require_response = 0; /* * Check whether we need to send a result: there should be only * one accept_completed response sent in the entire negotiation */ if ((mech_output_token != GSS_C_NO_BUFFER && mech_output_token->length != 0) || (ctx->open && negResult == accept_incomplete) || require_response || get_mic) { ret2 = send_accept (minor_status, ctx, mech_output_token, 0, get_mic ? &mech_buf : NULL, output_token); if (ret2) goto out; } out: if (ret2 != GSS_S_COMPLETE) ret = ret2; if (mech_output_token != NULL) gss_release_buffer(&minor, mech_output_token); if (mech_buf.value != NULL) free(mech_buf.value); free_NegotiationToken(&nt); } if (ret == GSS_S_COMPLETE) { if (src_name != NULL && ctx->mech_src_name != NULL) { spnego_name name; name = calloc(1, sizeof(*name)); if (name) { name->mech = ctx->mech_src_name; ctx->mech_src_name = NULL; *src_name = (gss_name_t)name; } } } if (mech_type != NULL) *mech_type = ctx->negotiated_mech_type; if (ret_flags != NULL) *ret_flags = ctx->mech_flags; if (time_rec != NULL) *time_rec = ctx->mech_time_rec; if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return ret; } _gss_spnego_internal_delete_sec_context(&minor, context_handle, GSS_C_NO_BUFFER); return ret; }
static OM_uint32 acceptor_complete(OM_uint32 * minor_status, gssspnego_ctx ctx, int *get_mic, gss_buffer_t mech_buf, gss_buffer_t mech_input_token, gss_buffer_t mech_output_token, heim_octet_string *mic, gss_buffer_t output_token) { OM_uint32 ret; int require_mic, verify_mic; gss_buffer_desc buf; buf.length = 0; buf.value = NULL; ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic); if (ret) return ret; ctx->require_mic = require_mic; if (mic != NULL) require_mic = 1; if (ctx->open && require_mic) { if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */ verify_mic = 1; *get_mic = 0; } else if (mech_output_token != GSS_C_NO_BUFFER && mech_output_token->length == 0) { /* Odd */ *get_mic = verify_mic = 1; } else { /* Even/One */ verify_mic = 0; *get_mic = 1; } if (verify_mic || *get_mic) { int eret; size_t buf_len; ASN1_MALLOC_ENCODE(MechTypeList, mech_buf->value, mech_buf->length, &ctx->initiator_mech_types, &buf_len, eret); if (eret) { *minor_status = eret; return GSS_S_FAILURE; } if (buf.length != buf_len) abort(); } if (verify_mic) { ret = verify_mechlist_mic(minor_status, ctx, mech_buf, mic); if (ret) { if (*get_mic) send_reject (minor_status, output_token); if (buf.value) free(buf.value); return ret; } ctx->verified_mic = 1; } if (buf.value) free(buf.value); } else *get_mic = 0; return GSS_S_COMPLETE; }
/** * Every time a new dialog is created (from a new INVITE) the dialog * module will call this callback function. We need to track the * dialogs lifespan from this point forward until it is terminated * with a BYE, CANCEL, etc. In the process, we will see if either or * both ends of the conversation supports SIP Session Timers and setup * the dialog timeout to expire at the session timer expire time. Each * time the new re-INVITE is seen to update the SST, we will reset the * life span of the dialog to match it. * * This function will setup the other types of dialog callbacks * required to track the lifespan of the dialog. It will also start * the state tracking to figure out if and who supports SST. * * As per RFC4028: Request handling: * * - The proxy may insert a SE header if none found. * - The SE value can be anything >= Min-SE (if found) * - The proxy MUST NOT add a refresher parameter to the SE. * * - If SE is already there, the Proxy can reduce its value but no * lower then the Min-SE value if present. * - If the SE value is >= Min-SE the proxy MUST NOT increase it! * - If the SE value is < Min-SE (settable by the proxy) the proxy * MUST increase the SE value to >= the new Min-SE. * - The proxy MUST NOT insert or change the refresher parameter. * * - If the supported=timer is found, the proxy may reject the request * with a 422 if the SE value is smaller then the local policy. The * 422 MUST hold the proxies Min-SE value >= 90. * - If support=timer is NOT indecated, the proxy can't reject with a * 422 but can include/increase the MIN-SE: to be = to local policy. * and increase the SE to match the new Min-SE value. * - the proxy MUST NOT insert/change the Min-SE header if * supported=timer is present. (DoS attacks) * * @param did - The dialog ID * @param type - The trigger event type (CREATED) * @param params - The pointer to nothing. As we did not attach * anything to this callback in the dialog module. */ void sst_dialog_created_CB(struct dlg_cell *did, int type, struct dlg_cb_params * params) { sst_info_t *info = NULL; sst_msg_info_t minfo; struct sip_msg* msg = params->req; memset(&minfo, 0, sizeof(sst_msg_info_t)); /* * Only deal with messages flaged as SST interested. */ if ((msg->flags & sst_flag) != sst_flag) { LM_DBG("SST flag was not set for this request\n"); return; } /* * look only at INVITE */ if (msg->first_line.type != SIP_REQUEST || msg->first_line.u.request.method_value != METHOD_INVITE) { LM_WARN("dialog create callback called with a non-INVITE request.\n"); return; } /* * Gather all he information about SST for this message */ if (parse_msg_for_sst_info(msg, &minfo)) { LM_ERR("failed to parse sst information\n"); return; } info = (sst_info_t *)shm_malloc(sizeof(sst_info_t)); memset(info, 0, sizeof(sst_info_t)); info->requester = (minfo.se?SST_UAC:SST_UNDF); info->supported = (minfo.supported?SST_UAC:SST_UNDF); info->interval = MAX(sst_min_se, 90); /* For now, will set for real * later */ if (minfo.se != 0) { /* * There is a SE already there, this is good, we just need to * check the values out a little before passing it along. */ if (minfo.se < sst_min_se) { /* * Problem, the requested Session-Expires is too small for * our local policy. We need to fix it, or reject it or * ignore it. */ if (!minfo.supported) { /* * Increase the Min-SE: value in the request and * forward it. */ str msehdr; if (minfo.min_se) { /* We need to update, which means, remove + * insert */ remove_header(msg, "Min-SE"); } info->interval = MAX(sst_min_se, minfo.min_se); sst_build_minse_hdr(info->interval, &msehdr); if (append_header(msg, msehdr.s)) { LM_ERR("Could not append modified Min-SE: header\n"); } } else if (sst_reject) { /* Make sure that that all are at least 90 */ send_reject(msg, MAX(MAX(sst_min_se, minfo.min_se), 90)); shm_free(info); return; } } /* end of se < sst_min_se */ else { /* Use the INVITE SE: value */ info->interval = minfo.se; } } else { /* * No Session-Expire: stated in request. */ str msehdr; info->interval = MAX(minfo.min_se, sst_min_se); if (minfo.min_se && minfo.min_se < sst_min_se) { remove_header(msg, "Min-SE"); sst_build_minse_hdr(info->interval, &msehdr); if (append_header(msg, msehdr.s)) { LM_ERR("failed to append modified Min-SE: header\n"); /* What to do? Let is slide, we can still work */ } } info->requester = SST_PXY; sst_build_se_hdr(info->interval, &msehdr, NULL); if (append_header(msg, msehdr.s)) { LM_ERR("failed to append Session-Expires header to proxy " "requested SST.\n"); shm_free(info); return; /* Nothing we can do! */ } } setup_dialog_callbacks(did, info); set_timeout_avp(msg, info->interval); return; }