--- chan_sip.c.bak 2008-01-05 16:06:12.000000000 +0900 +++ chan_sip.c 2008-01-05 21:39:59.000000000 +0900 @@ -709,6 +709,9 @@ struct sip_auth *next; /*!< Next auth structure in list */ }; +/*! \brief se refresher s.nakamura */ +enum se_refresher { REFRESHER_UAC, REFRESHER_UAS }; + /*--- Various flags for the flags field in the pvt structure */ #define SIP_ALREADYGONE (1 << 0) /*!< Whether or not we've already been destroyed by our peer */ #define SIP_NEEDDESTROY (1 << 1) /*!< if we need to be destroyed by the monitor thread */ @@ -1014,6 +1017,10 @@ struct sip_pvt *next; /*!< Next dialog in chain */ struct sip_invite_param *options; /*!< Options for INVITE */ int autoframing; + int sip_reinvite_timer; /*!< SE timer id s.nakamura */ + int timer_se; /*!< SE timer valuer s.nakamura */ + enum se_refresher refresher; /*!< SE refresher s.nakamura */ + int min_se; /*!< Min-SE value s.nakamura */ } *iflist = NULL; /*! Max entires in the history list for a sip_pvt */ @@ -1129,6 +1136,9 @@ struct sip_pvt *mwipvt; /*!< Subscription for MWI */ int lastmsg; int autoframing; + int session_expires; /*!< Session-expires (SE timer) value s.nakamura */ + enum se_refresher refresher; /*!< refresher s.nakamura */ + int min_se; /*!< Min-SE value s.nakamura */ }; @@ -1478,6 +1488,7 @@ static int get_msg_text(char *buf, int len, struct sip_request *req); static void free_old_route(struct sip_route *route); static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout); +static int sip_session_timeout(const void *data); /* s.nakamura */ /*--- Constructing requests and responses */ static void initialize_initreq(struct sip_pvt *p, struct sip_request *req); @@ -2651,6 +2662,17 @@ return p; } +/*! \brief Locate peer by username s.nakamura */ +static struct sip_peer *find_peer_by_username(const char *peer) +{ + struct sip_peer *p = NULL; + ASTOBJ_CONTAINER_TRAVERSE(&peerl, !p, do { + if (!(strcasecmp(iterator->name, peer))) + p = ASTOBJ_REF(iterator); + } while (0)); + return p; +} + /*! \brief Remove user object from in-memory storage */ static void sip_destroy_user(struct sip_user *user) { @@ -5820,7 +5842,7 @@ if (!ast_strlen_zero(global_useragent)) add_header(resp, "User-Agent", global_useragent); add_header(resp, "Allow", ALLOWED_METHODS); - add_header(resp, "Supported", SUPPORTED_EXTENSIONS); +/* add_header(resp, "Supported", SUPPORTED_EXTENSIONS); s.nakamura */ if (msg[0] == '2' && (p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER)) { /* For registration responses, we also need expiry and contact info */ @@ -5836,6 +5858,13 @@ } else if (msg[0] != '4' && !ast_strlen_zero(p->our_contact)) { add_header(resp, "Contact", p->our_contact); } + /* add Min-SE header s.nakamura */ + if (strncmp(msg, "422", 3) == 0) { + char str[32]; + memset(str, 0, sizeof(str)); + snprintf(str, sizeof(str), "%d", p->min_se); + add_header(resp, "Min-SE", str); + } return 0; } @@ -6651,6 +6680,24 @@ return -1; } respprep(&resp, p, msg, req); + + /* s.nakamura */ + if (req->method == SIP_INVITE && strstr(msg, "200") != NULL && p->timer_se > 0) { + if (p->sip_reinvite_timer != 0) + ast_sched_del(sched, p->sip_reinvite_timer); + char str[32]; + memset(str, 0, sizeof(str)); + if (p->refresher == REFRESHER_UAS) { /* add a waiting timer for re-INVITE request s.nakamura */ + snprintf(str, sizeof(str), "%d;refresher=uac", p->timer_se); + p->sip_reinvite_timer = ast_sched_add(sched, p->timer_se * 1000, sip_session_timeout, p); + } else { /* in case of transferring refresher */ + snprintf(str, sizeof(str), "%d;refresher=uas", p->timer_se); + p->sip_reinvite_timer = ast_sched_add(sched, p->timer_se / 2 * 1000, sip_session_timeout, p); + } + add_header(&resp, "Require", "timer"); + add_header(&resp, "Session-Expires", str); + } + if (p->rtp) { if (!p->autoframing && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { if (option_debug) @@ -6707,6 +6754,24 @@ return 1; } +/*! \brief SE timer s.nakamura */ +static int sip_session_timeout(const void *data) +{ + struct sip_pvt *p = (struct sip_pvt *) data; + p->sip_reinvite_timer = 0; + if (p->refresher == REFRESHER_UAC) { + return transmit_reinvite_with_sdp(p); + } else { + /* say BYE to the destination */ + ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); + + /* say BYE to my phone */ + if (p->owner) + ast_queue_hangup(p->owner); + return 0; + } +} + /*! \brief Transmit reinvite with SDP \note A re-invite is basically a new INVITE with the same CALL-ID and TAG as the INVITE that opened the SIP dialogue @@ -6718,9 +6783,18 @@ struct sip_request req; reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1); + + /* if necessary, add se-timer headers s.nakamura */ + if (p->timer_se > 0 && p->refresher == REFRESHER_UAC) { + char expstr[64]; + memset(expstr, 0, sizeof(expstr)); + snprintf(expstr, sizeof(expstr), "%d;refresher=uac", p->timer_se); + add_header(&req, "Session-Expires", expstr); + add_header(&req, "Supported", "timer"); + } add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); +/* add_header(&req, "Supported", SUPPORTED_EXTENSIONS); s.nakamura */ if (sipdebug) add_header(&req, "X-asterisk-Info", "SIP re-invite (External RTP bridge)"); if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) @@ -6744,7 +6818,7 @@ reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1); add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); +/* add_header(&req, "Supported", SUPPORTED_EXTENSIONS); s.nakamura */ if (sipdebug) add_header(&req, "X-asterisk-info", "SIP re-invite (T38 switchover)"); ast_udptl_offered_from_local(p->udptl, 1); @@ -7023,6 +7097,24 @@ } } } + /* add se-timer headers when "session-expires" is specified in sip.conf s.nakamura */ + char expstr[64]; + struct sip_peer *peer = NULL; + peer = find_peer_by_username(p->peername); + if (peer != NULL && peer->session_expires > 0) { + p->sip_reinvite_timer = 0; + p->timer_se = p->min_se > peer->session_expires ? p->min_se : peer->session_expires; + p->refresher = peer->refresher; + memset(expstr, 0, sizeof(expstr)); + snprintf(expstr, sizeof(expstr), "%d;refresher=%s", p->timer_se, peer->refresher == REFRESHER_UAC ? "uac" : "uas"); + add_header(&req, "Session-Expires", expstr); + add_header(&req, "Supported", "timer"); + if (p->min_se > 0) { + memset(expstr, 0, sizeof(expstr)); + snprintf(expstr, sizeof(expstr), "%d", p->min_se); + add_header(&req, "Min-SE", expstr); + } + } /* This new INVITE is part of an attended transfer. Make sure that the other end knows and replace the current call with this new call */ if (p->options && p->options->replaces && !ast_strlen_zero(p->options->replaces)) { @@ -7031,7 +7123,7 @@ } add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); +/* add_header(&req, "Supported", SUPPORTED_EXTENSIONS); s.nakamura */ if (p->options && p->options->addsipheaders && p->owner) { struct ast_channel *chan = p->owner; /* The owner channel */ struct varshead *headp; @@ -7335,7 +7427,7 @@ add_header(&req, "Subscription-state", terminate ? "terminated;reason=noresource" : "active"); add_header(&req, "Content-Type", "message/sipfrag;version=2.0"); add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); +/* add_header(&req, "Supported", SUPPORTED_EXTENSIONS); s.nakamura */ snprintf(tmp, sizeof(tmp), "SIP/2.0 %s\r\n", message); add_header_contentLength(&req, strlen(tmp)); @@ -7716,7 +7808,7 @@ add_header(&req, "Refer-To", referto); add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); +/* add_header(&req, "Supported", SUPPORTED_EXTENSIONS); s.nakamura */ if (!ast_strlen_zero(p->our_contact)) add_header(&req, "Referred-By", p->our_contact); @@ -7757,6 +7849,11 @@ */ static int transmit_request(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch) { + /* remove se-timer callback s.nakamura */ + if (sipmethod == SIP_BYE && p->sip_reinvite_timer != 0) { + ast_sched_del(sched, p->sip_reinvite_timer); + } + struct sip_request resp; if (sipmethod == SIP_ACK) @@ -7770,6 +7867,11 @@ /*! \brief Transmit SIP request, auth added */ static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch) { + /* remove se-timer callback s.nakamura */ + if (sipmethod == SIP_BYE && p->sip_reinvite_timer != 0) { + ast_sched_del(sched, p->sip_reinvite_timer); + } + struct sip_request resp; reqprep(&resp, p, sipmethod, seqno, newbranch); @@ -12001,6 +12103,8 @@ if (resp >= 300 && (p->invitestate == INV_CALLING || p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA )) p->invitestate = INV_COMPLETED; + char *pr; /* s.nakamura */ + const char *ph, *minse; /* s.nakamura */ switch (resp) { case 100: /* Trying */ @@ -12031,6 +12135,34 @@ check_pendings(p); break; + case 422: /* Session-timer negotiation s.nakamura */ + minse = get_header(req, "Min-SE"); + if (strlen(minse) > 0) { + sscanf(minse, "%d", &(p->min_se)); + p->timer_se = p->min_se; + /* First we ACK */ + transmit_request(p, SIP_ACK, seqno, 0, 0); + + /* Then we negotiate */ + if (!ast_test_flag(req, SIP_PKT_IGNORE)) { + if (p->authtries == MAX_AUTHTRIES || transmit_invite(p, SIP_INVITE, 1, 1)) { + ast_log(LOG_NOTICE, "Failed to negotiate session-timer on INVITE to '%s'\n", get_header(&p->initreq, "From")); + ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); + ast_set_flag(&p->flags[0], SIP_ALREADYGONE); + if (p->owner) + ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + } + p->authtries++; + } + } else { + ast_log(LOG_NOTICE, "Failed to negotiate session-timer on INVITE to '%s'\n", get_header(&p->initreq, "From")); + ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); + ast_set_flag(&p->flags[0], SIP_ALREADYGONE); + if (p->owner) + ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + } + break; + case 183: /* Session progress */ if (!ast_test_flag(req, SIP_PKT_IGNORE)) sip_cancel_destroy(p); @@ -12146,6 +12278,18 @@ p->invitestate = INV_TERMINATED; xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, TRUE); check_pendings(p); + + /* add se-timer callback s.nakamura */ + ph = get_header(req, "Session-Expires"); + pr = strstr(ph, ";refresher=uac"); + if (pr) { + char timestr[16]; + memset(timestr, 0, sizeof(timestr)); + strncpy(timestr, ph, pr-ph); + sscanf(timestr, "%d", &(p->timer_se)); + if (p->timer_se > 0) + p->sip_reinvite_timer = ast_sched_add(sched, p->timer_se / 2 * 1000, sip_session_timeout, p); + } break; case 407: /* Proxy authentication */ case 401: /* Www auth */ @@ -12707,6 +12851,14 @@ ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); } break; + case 422: /* session-timer negotiation s.nakamura */ + if (sipmethod == SIP_INVITE) { + handle_response_invite(p, resp, rest, req, seqno); + } else { + ast_log(LOG_WARNING, "Got negotiation request (422) on unknown %s to '%s'\n", sip_methods[sipmethod].text, get_header(req, "To")); + ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); + } + break; case 481: /* Call leg does not exist */ if (sipmethod == SIP_INVITE) { handle_response_invite(p, resp, rest, req, seqno); @@ -13779,6 +13931,49 @@ ast_verbose("Ignoring this INVITE request\n"); + /* session-timer for incoming invite s.nakamura */ + char timestr[32]; + const char *ph= get_header(req, "Session-Expires"); + char *pr = strstr(ph, ";refresher"); + struct sip_peer *peer = NULL; + if (strlen(ph) > 0) { + peer = find_peer(NULL, &p->recv, 1); + if (pr) { + memset(timestr, 0, sizeof(timestr)); + strncpy(timestr, ph, pr-ph); + sscanf(timestr, "%d", &(p->timer_se)); + if (!strcasecmp(pr, ";refresher=uac")) { + p->refresher = REFRESHER_UAS; + } else if (!strcasecmp(pr, ";refresher=uas")) { + p->refresher = REFRESHER_UAC; + } + } else { + sscanf(ph, "%d", &(p->timer_se)); + p->refresher = REFRESHER_UAS; + } + if (peer && p->timer_se < peer->min_se) { + p->min_se = peer->min_se; + if (ast_test_flag(req, SIP_PKT_IGNORE)) + transmit_response(p, "422 Session Timer Too Small", req); + else + transmit_response_reliable(p, "422 Session Timer Too Small", req); + return 0; + } + } else { + p->timer_se = 0; + } + const char *pt = get_header(req, "Supported"); + if (p->timer_se > 0 && !strstr(pt, "timer")) { + p->timer_se = 0; + ast_log(LOG_NOTICE, "Failed to authenticate due to lack of timer support\n"); + if (ast_test_flag(req, SIP_PKT_IGNORE)) + transmit_response(p, "403 Forbidden", req); + else + transmit_response_reliable(p, "403 Forbidden", req); + ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); + return 0; + } + if (!p->lastinvite && !ast_test_flag(req, SIP_PKT_IGNORE) && !p->owner) { /* This is a new invite */ /* Handle authentication if this is our first invite */ @@ -14647,6 +14842,11 @@ int res; struct ast_channel *bridged_to; + /* remove se timer callback ... s.nakamura */ + if (p->sip_reinvite_timer != 0) { + ast_sched_del(sched, p->sip_reinvite_timer); + } + /* If we have an INCOMING invite that we haven't answered, terminate that transaction */ if (p->pendinginvite && !ast_test_flag(&p->flags[0], SIP_OUTGOING) && !ast_test_flag(req, SIP_PKT_IGNORE) && !p->owner) transmit_response_reliable(p, "487 Request Terminated", &p->initreq); @@ -16645,6 +16845,26 @@ if (peer->maxcallbitrate < 0) peer->maxcallbitrate = default_maxcallbitrate; } + /* add session-expires (se timer value) ... s.nakamura */ + else if (!strcasecmp(v->name, "session-expires")) { + if ((sscanf(v->value, "%d", &peer->session_expires) !=1) || (peer->session_expires < 0)) { + ast_log(LOG_WARNING, "'%s' is not a valid Session-Expires time value at line %d. Using default.\n", v->value, v->lineno); + peer->session_expires = 0; + } + } else if (!strcasecmp(v->name, "refresher")) { + if (!strcasecmp(v->value, "uac")) { + peer->refresher = REFRESHER_UAC; + } else if (!strcasecmp(v->value, "uas")) { + peer->refresher = REFRESHER_UAS; + } else { + peer->refresher = REFRESHER_UAC; + } + } else if (!strcasecmp(v->name, "min-se")) { + if ((sscanf(v->value, "%d", &peer->min_se) !=1) || (peer->min_se < 0)) { + ast_log(LOG_WARNING, "'%s' is not a valid Min-SE value at line %d. Using default.\n", v->value, v->lineno); + peer->min_se = 0; + } + } } if (!ast_test_flag(&global_flags[1], SIP_PAGE2_IGNOREREGEXPIRE) && ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC) && realtime) { time_t nowtime = time(NULL);