--- chan_sip.c.bak 2006-04-15 12:41:34.000000000 +0900 +++ chan_sip.c 2006-04-25 22:59:31.000000000 +0900 @@ -581,6 +581,9 @@ static int global_rtautoclear; +/*! \brief s.nakamura */ +enum se_refresher { REFRESHER_UAC, REFRESHER_UAS }; + /*! \brief sip_pvt: PVT structures are used for each SIP conversation, ie. a call */ static struct sip_pvt { ast_mutex_t lock; /*!< Channel private lock */ @@ -690,6 +693,9 @@ struct ast_variable *chanvars; /*!< Channel variables to set for call */ struct sip_pvt *next; /*!< Next call in chain */ struct sip_invite_param *options; /*!< Options for INVITE */ + int sip_reinvite_timer; /*!< SE timer id s.nakamura (i assume to initialized by zero...) */ + int timer_se; /*!< SE timer value s.nakamura */ + enum se_refresher refresher; /*!< SE refresher s.nakamura */ } *iflist = NULL; #define FLAG_RESPONSE (1 << 0) @@ -792,6 +798,8 @@ struct sockaddr_in defaddr; /*!< Default IP address, used until registration */ struct ast_ha *ha; /*!< Access control list */ struct ast_variable *chanvars; /*!< Variables to set for channel created by user */ + int session_expires; /*!< Session-expires (SE timer) value s.nakamura */ + enum se_refresher refresher; /*!< refresher s.nakamura */ int lastmsg; }; @@ -923,6 +931,8 @@ static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype); static int transmit_state_notify(struct sip_pvt *p, int state, int full, int substate); static char *gettag(struct sip_request *req, char *header, char *tagbuf, int tagbufsize); +static int sip_session_timeout(void *data); /* s.nakamura */ +static int add_header(struct sip_request *req, const char *var, const char *value); /*! \brief Definition of this channel for PBX channel registration */ static const struct ast_channel_tech sip_tech = { @@ -1763,6 +1773,18 @@ return p; } +/*! \brief find_peer_by_username: 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->username, peer))) + p = ASTOBJ_REF(iterator); + } while (0)); + return p; +} + /*! \brief sip_destroy_user: Remove user object from in-memory storage ---*/ static void sip_destroy_user(struct sip_user *user) { @@ -4524,6 +4546,23 @@ 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); + if (p->refresher == REFRESHER_UAS) { /* add a waiting timer for re-INVITE request ... s.nakamura */ + char str[32]; + memset(str, 0, sizeof(str)); + 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); + add_header(&resp, "Require", "timer"); + add_header(&resp, "x", str); + } else { /* in case of transferring refresher */ + p->sip_reinvite_timer = ast_sched_add(sched, p->timer_se / 2 * 1000, sip_session_timeout, p); + } + } + if (p->rtp) { ast_rtp_offered_from_local(p->rtp, 0); try_suggested_sip_codec(p); @@ -4583,6 +4622,25 @@ return 1; } +/*! \brief sip_session_timeout: SE timer + * s.nakamura */ +static int sip_session_timeout(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, SIP_NEEDDESTROY); + + /* say BYE to my phone */ + if (p->owner) + ast_queue_hangup(p->owner); + return 0; + } +} + /*! \brief transmit_reinvite_with_sdp: Transmit reinvite with SDP :-) ---*/ /* A re-invite is basically a new INVITE with the same CALL-ID and TAG as the INVITE that opened the SIP dialogue @@ -4596,6 +4654,15 @@ reqprep(&req, p, SIP_UPDATE, 0, 1); else reqprep(&req, p, 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); if (sipdebug) @@ -4887,6 +4954,19 @@ if (!ast_strlen_zero(p->referred_by)) add_header(&req, "Referred-By", p->referred_by); } + /* 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) { + memset(expstr, 0, sizeof(expstr)); + snprintf(expstr, sizeof(expstr), "%d;refresher=%s", peer->session_expires, peer->refresher == REFRESHER_UAC ? "uac" : "uas"); + p->sip_reinvite_timer = 0; + p->timer_se = peer->session_expires; + p->refresher = peer->refresher; + add_header(&req, "Session-Expires", expstr); + add_header(&req, "Supported", "timer"); + } #ifdef OSP_SUPPORT if ((req.method != SIP_OPTIONS) && p->options && !ast_strlen_zero(p->options->osptoken)) { ast_log(LOG_DEBUG,"Adding OSP Token: %s\n", p->options->osptoken); @@ -5582,6 +5662,10 @@ /*! \brief transmit_request: transmit generic SIP request ---*/ static int transmit_request(struct sip_pvt *p, int sipmethod, int seqno, int 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); add_header_contentLength(&resp, 0); @@ -5592,6 +5676,11 @@ /*! \brief transmit_request_with_auth: Transmit SIP request, auth added ---*/ static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, int 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); @@ -9530,6 +9619,18 @@ /* If I understand this right, the branch is different for a non-200 ACK only */ transmit_request(p, SIP_ACK, seqno, 0, 1); check_pendings(p); + + /* add se-timer callback ... s.nakamura */ + char *ph = get_header(req, "x"); + char *pr = strstr(ph, ";refresher=uac"); + char timestr[16]; + if (pr) { + 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 */ @@ -10278,7 +10379,6 @@ } } - /* Check if this is a loop */ /* This happens since we do not properly support SIP domain handling yet... -oej */ @@ -10317,6 +10417,35 @@ } } else if (debug) ast_verbose("Ignoring this INVITE request\n"); + + /* session-timer for incoming invite ... s.nakamura */ + char timestr[32]; + char *ph= get_header(req, "Session-Expires"); + char *pr = strstr(ph, ";refresher"); + 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 { + p->timer_se = 0; + } + 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 (ignore) + transmit_response(p, "403 Forbidden", req); + else + transmit_response_reliable(p, "403 Forbidden", req, 1); + ast_set_flag(p, SIP_NEEDDESTROY); + return 0; + } + if (!p->lastinvite && !ignore && !p->owner) { /* Handle authentication if this is our first invite */ res = check_user(p, req, SIP_INVITE, e, 1, sin, ignore); @@ -10611,6 +10740,11 @@ int res; struct ast_channel *bridged_to; char iabuf[INET_ADDRSTRLEN]; + + /* remove se timer callback ... s.nakamura */ + if (p->sip_reinvite_timer != 0) { + ast_sched_del(sched, p->sip_reinvite_timer); + } if (p->pendinginvite && !ast_test_flag(p, SIP_OUTGOING) && !ignore) transmit_response_reliable(p, "487 Request Terminated", &p->initreq, 1); @@ -12268,6 +12402,21 @@ /* else if (strcasecmp(v->name,"type")) * ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ + /* 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; + } + } v=v->next; } if (!ast_test_flag((&global_flags_page2), SIP_PAGE2_IGNOREREGEXPIRE) && ast_test_flag(peer, SIP_DYNAMIC) && realtime) {