--- chan_sip.c.bak 2007-05-22 23:01:37.000000000 +0900 +++ chan_sip.c 2007-05-22 23:13:32.000000000 +0900 @@ -589,6 +589,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 */ @@ -699,6 +702,10 @@ 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 */ + int min_se; /*!< Min-SE value s.nakamura */ } *iflist = NULL; #define FLAG_RESPONSE (1 << 0) @@ -801,6 +808,9 @@ 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 min_se; /*!< Min-SE value s.nakamura */ int lastmsg; }; @@ -932,6 +942,7 @@ 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, int timeout); static char *gettag(struct sip_request *req, char *header, char *tagbuf, int tagbufsize); +static int sip_session_timeout(void *data); /* s.nakamura */ /*! \brief Definition of this channel for PBX channel registration */ static const struct ast_channel_tech sip_tech = { @@ -1785,6 +1796,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) { @@ -4186,6 +4209,13 @@ } else if (msg[0] != '4' && p->our_contact[0]) { 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; } @@ -4729,6 +4759,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) { ast_rtp_offered_from_local(p->rtp, 0); try_suggested_sip_codec(p); @@ -4788,6 +4836,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 @@ -4801,6 +4868,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) @@ -5093,6 +5169,24 @@ 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) { + 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); + } + } #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); @@ -5797,6 +5891,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); @@ -5807,6 +5905,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); @@ -9741,6 +9844,8 @@ (resp != 183)) resp = 183; + char *ph, *pr, *minse; /* s.nakamura */ + switch (resp) { case 100: /* Trying */ if (!ignore) @@ -9766,6 +9871,33 @@ ast_set_flag(p, SIP_CAN_BYE); 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 (!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, SIP_NEEDDESTROY); + ast_set_flag(p, 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, SIP_NEEDDESTROY); + ast_set_flag(p, SIP_ALREADYGONE); + if (p->owner) + ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + } + break; case 183: /* Session progress */ if (!ignore) sip_cancel_destroy(p); @@ -9819,6 +9951,18 @@ transmit_request(p, SIP_ACK, seqno, 0, 1); ast_set_flag(p, SIP_CAN_BYE); check_pendings(p); + + /* add se-timer callback ... s.nakamura */ + ph = get_header(req, "Session-Expires"); + 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 */ @@ -10189,6 +10333,14 @@ if (sipmethod == SIP_INVITE) handle_response_invite(p, resp, rest, req, ignore, seqno); break; + case 422: /* session-timer negotiation ... s.nakamura */ + if (sipmethod == SIP_INVITE) { + handle_response_invite(p, resp, rest, req, ignore, 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, SIP_NEEDDESTROY); + } + break; case 491: /* Pending */ if (sipmethod == SIP_INVITE) { handle_response_invite(p, resp, rest, req, ignore, seqno); @@ -10600,7 +10752,6 @@ } } - /* Check if this is a loop */ /* This happens since we do not properly support SIP domain handling yet... -oej */ @@ -10640,6 +10791,50 @@ } } 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"); + 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 (ignore) + transmit_response(p, "422 Session Timer Too Small", req); + else + transmit_response_reliable(p, "422 Session Timer Too Small", req, 1); + return 0; + } + } 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); @@ -10924,6 +11119,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 we have an INCOMING invite that we haven't answered, terminate that transaction */ if (p->pendinginvite && !ast_test_flag(p, SIP_OUTGOING) && !ignore && !p->owner) @@ -12644,6 +12844,26 @@ peer->maxms = 0; } } + /* 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; + } + } v = v->next; } if (!ast_test_flag((&global_flags_page2), SIP_PAGE2_IGNOREREGEXPIRE) && ast_test_flag(&peer->flags_page2, SIP_PAGE2_DYNAMIC) && realtime) {