From ceace6fb1143f67f0b266c66e63f391840e0e89d Mon Sep 17 00:00:00 2001 From: kpfleming Date: Wed, 7 Sep 2005 19:00:31 +0000 Subject: factor out INVITE response handling in its own function (issue #5127) git-svn-id: http://svn.digium.com/svn/asterisk/trunk@6532 f38db490-d61c-443f-a65b-d21fe96a405b --- channels/chan_sip.c | 299 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 205 insertions(+), 94 deletions(-) (limited to 'channels') diff --git a/channels/chan_sip.c b/channels/chan_sip.c index a6239c5cc..f14ea507d 100755 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -8893,6 +8893,148 @@ static void check_pendings(struct sip_pvt *p) } } +/*--- handle_response_invite: Handle SIP response in dialogue ---*/ +static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int ignore, int seqno) +{ + int outgoing = ast_test_flag(p, SIP_OUTGOING); + + if (option_debug > 3) { + int reinvite = (p->owner && p->owner->_state == AST_STATE_UP); + if (reinvite) + ast_log(LOG_DEBUG, "SIP response %d to RE-invite on %s call %s\n", resp, outgoing ? "outgoing" : "incoming", p->callid); + else + ast_log(LOG_DEBUG, "SIP response %d to standard invite", resp); + } + + switch (resp) { + case 100: /* Trying */ + sip_cancel_destroy(p); + case 180: /* 180 Ringing */ + sip_cancel_destroy(p); + if (!ignore && p->owner) { + ast_queue_control(p->owner, AST_CONTROL_RINGING); + if (p->owner->_state != AST_STATE_UP) + ast_setstate(p->owner, AST_STATE_RINGING); + } + if (!strcasecmp(get_header(req, "Content-Type"), "application/sdp")) { + process_sdp(p, req); + if (!ignore && p->owner) { + /* Queue a progress frame only if we have SDP in 180 */ + ast_queue_control(p->owner, AST_CONTROL_PROGRESS); + } + } + break; + case 183: /* Session progress */ + sip_cancel_destroy(p); + if (!strcasecmp(get_header(req, "Content-Type"), "application/sdp")) { + process_sdp(p, req); + } + if (!ignore && p->owner) { + /* Queue a progress frame */ + ast_queue_control(p->owner, AST_CONTROL_PROGRESS); + } + break; + case 200: /* 200 OK on invite - someone's answering our call */ + sip_cancel_destroy(p); + p->authtries = 0; + if (!strcasecmp(get_header(req, "Content-Type"), "application/sdp")) { + process_sdp(p, req); + } + + /* Parse contact header for continued conversation */ + /* When we get 200 OK, we know which device (and IP) to contact for this call */ + /* This is important when we have a SIP proxy between us and the phone */ + if (outgoing) { + parse_ok_contact(p, req); + + /* Save Record-Route for any later requests we make on this dialogue */ + build_route(p, req, 1); + } + + if (!ignore && p->owner) { + if (p->owner->_state != AST_STATE_UP) { +#ifdef OSP_SUPPORT + time(&p->ospstart); +#endif + ast_queue_control(p->owner, AST_CONTROL_ANSWER); + } else { /* RE-invite */ + struct ast_frame af = { AST_FRAME_NULL, }; + ast_queue_frame(p->owner, &af); + } + } else { + /* It's possible we're getting an ACK after we've tried to disconnect + by sending CANCEL */ + /* THIS NEEDS TO BE CHECKED: OEJ */ + if (!ignore) + ast_set_flag(p, SIP_PENDINGBYE); + } + /* 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); + break; + case 401: /* Www auth */ + /* First we ACK */ + transmit_request(p, SIP_ACK, seqno, 0, 0); + /* Then we AUTH */ + p->theirtag[0]='\0'; /* forget their old tag, so we don't match tags when getting response */ + if (!ignore) { + if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, "WWW-Authenticate", "Authorization", SIP_INVITE, 1)) { + ast_log(LOG_NOTICE, "Failed to authenticate 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 403: /* Forbidden */ + /* First we ACK */ + transmit_request(p, SIP_ACK, seqno, 0, 0); + ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for INVITE to '%s'\n", get_header(&p->initreq, "From")); + if (!ignore && p->owner) + ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + ast_set_flag(p, SIP_NEEDDESTROY); + ast_set_flag(p, SIP_ALREADYGONE); + break; + case 404: /* Not found */ + transmit_request(p, SIP_ACK, seqno, 0, 0); + if (p->owner && !ignore) + ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + ast_set_flag(p, SIP_ALREADYGONE); + break; + case 407: /* Proxy authentication */ + /* First we ACK */ + transmit_request(p, SIP_ACK, seqno, 0, 0); + /* Then we AUTH */ + /* But only if the packet wasn't marked as ignore in handle_request */ + if (!ignore) { + p->theirtag[0]='\0'; /* forget their old tag, so we don't match tags when getting response */ + if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, "Proxy-Authenticate", "Proxy-Authorization", SIP_INVITE, 1)) { + ast_log(LOG_NOTICE, "Failed to authenticate on INVITE to '%s'\n", get_header(&p->initreq, "From")); + ast_set_flag(p, SIP_NEEDDESTROY); + if (p->owner) + ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + ast_set_flag(p, SIP_ALREADYGONE); + } + } + break; + case 481: /* Call leg does not exist */ + /* Could be REFER or INVITE */ + ast_log(LOG_WARNING, "Re-invite to non-existing call leg on other UA. SIP dialog '%s'. Giving up.\n", p->callid); + transmit_request(p, SIP_ACK, seqno, 0, 0); + break; + case 491: /* Pending */ + /* we have to wait a while, then retransmit */ + /* Transmission is rescheduled, so everything should be taken care of. + We should support the retry-after at some point */ + break; + case 501: /* Not implemented */ + if (p->owner) + ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + break; + } +} + /*--- handle_response_register: Handle responses on REGISTER to services ---*/ static int handle_response_register(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int ignore, int seqno) { @@ -9116,30 +9258,16 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ } switch(resp) { case 100: /* 100 Trying */ - if (sipmethod == SIP_INVITE) { - sip_cancel_destroy(p); - } + if (sipmethod == SIP_INVITE) + handle_response_invite(p, resp, rest, req, ignore, seqno); break; case 183: /* 183 Session Progress */ - if (sipmethod == SIP_INVITE) { - sip_cancel_destroy(p); - if (!ast_strlen_zero(get_header(req, "Content-Type"))) - process_sdp(p, req); - if (p->owner) { - /* Queue a progress frame */ - ast_queue_control(p->owner, AST_CONTROL_PROGRESS); - } - } + if (sipmethod == SIP_INVITE) + handle_response_invite(p, resp, rest, req, ignore, seqno); break; case 180: /* 180 Ringing */ - if (sipmethod == SIP_INVITE) { - sip_cancel_destroy(p); - if (p->owner) { - ast_queue_control(p->owner, AST_CONTROL_RINGING); - if (p->owner->_state != AST_STATE_UP) - ast_setstate(p->owner, AST_STATE_RINGING); - } - } + if (sipmethod == SIP_INVITE) + handle_response_invite(p, resp, rest, req, ignore, seqno); break; case 200: /* 200 OK */ p->authtries = 0; /* Reset authentication counter */ @@ -9157,52 +9285,14 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ } } } else if (sipmethod == SIP_INVITE) { - /* 200 OK on invite - someone's answering our call */ - sip_cancel_destroy(p); - if (!ast_strlen_zero(get_header(req, "Content-Type"))) - process_sdp(p, req); - - /* Parse contact header for continued conversation */ - /* When we get 200 OK, we now which device (and IP) to contact for this call */ - /* This is important when we have a SIP proxy between us and the phone */ - parse_ok_contact(p, req); - /* Save Record-Route for any later requests we make on this dialogue */ - build_route(p, req, 1); - if (p->owner) { - if (p->owner->_state != AST_STATE_UP) { -#ifdef OSP_SUPPORT - time(&p->ospstart); -#endif - ast_queue_control(p->owner, AST_CONTROL_ANSWER); - ast_setstate(p->owner, AST_STATE_UP); - } else { - struct ast_frame af = { AST_FRAME_NULL, }; - ast_queue_frame(p->owner, &af); - } - } else { /* It's possible we're getting an ACK after we've tried to disconnect by sending CANCEL */ - ast_set_flag(p, SIP_PENDINGBYE); - } - ast_device_state_changed("SIP/%s", p->peername); - /* 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); + handle_response_invite(p, resp, rest, req, ignore, seqno); } else if (sipmethod == SIP_REGISTER) { res = handle_response_register(p, resp, rest, req, ignore, seqno); } break; case 401: /* Not www-authorized on SIP method */ if (sipmethod == SIP_INVITE) { - /* First we ACK */ - transmit_request(p, SIP_ACK, seqno, 0, 0); - /* Then we AUTH */ - p->theirtag[0]='\0'; /* forget their old tag, so we don't match tags when getting response */ - if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, "WWW-Authenticate", "Authorization", SIP_INVITE, 1)) { - ast_log(LOG_NOTICE, "Failed to authenticate 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); - } + handle_response_invite(p, resp, rest, req, ignore, seqno); } else if (p->registry && sipmethod == SIP_REGISTER) { res = handle_response_register(p, resp, rest, req, ignore, seqno); } else { @@ -9212,12 +9302,7 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ break; case 403: /* Forbidden - we failed authentication */ if (sipmethod == SIP_INVITE) { - /* First we ACK */ - transmit_request(p, SIP_ACK, seqno, 0, 0); - ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for INVITE to '%s'\n", get_header(&p->initreq, "From")); - if (owner) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - ast_set_flag(p, SIP_NEEDDESTROY); + handle_response_invite(p, resp, rest, req, ignore, seqno); } else if (p->registry && sipmethod == SIP_REGISTER) { res = handle_response_register(p, resp, rest, req, ignore, seqno); } else { @@ -9227,25 +9312,14 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ case 404: /* Not found */ if (p->registry && sipmethod == SIP_REGISTER) { res = handle_response_register(p, resp, rest, req, ignore, seqno); + } else if (sipmethod == SIP_INVITE) { + handle_response_invite(p, resp, rest, req, ignore, seqno); } else if (owner) ast_queue_control(p->owner, AST_CONTROL_CONGESTION); break; case 407: /* Proxy auth required */ if (sipmethod == SIP_INVITE) { - /* First we ACK */ - transmit_request(p, SIP_ACK, seqno, 0, 0); - /* Then we AUTH */ - /* But only if the packet wasn't marked as ignore in handle_request */ - if (!ignore){ - p->theirtag[0]='\0'; /* forget their old tag, so we don't match tags when getting response */ - if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, "Proxy-Authenticate", "Proxy-Authorization", SIP_INVITE, 1)) { - ast_log(LOG_NOTICE, "Failed to authenticate on INVITE to '%s'\n", get_header(&p->initreq, "From")); - ast_set_flag(p, SIP_NEEDDESTROY); - if (p->owner) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - ast_set_flag(p, SIP_ALREADYGONE); - } - } + handle_response_invite(p, resp, rest, req, ignore, seqno); } else if (sipmethod == SIP_BYE || sipmethod == SIP_REFER) { if (ast_strlen_zero(p->authname)) ast_log(LOG_WARNING, "Asked to authenticate %s, to %s:%d but we have no matching peer!\n", @@ -9261,10 +9335,13 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ ast_set_flag(p, SIP_NEEDDESTROY); break; + case 491: /* Pending */ + if (sipmethod == SIP_INVITE) { + handle_response_invite(p, resp, rest, req, ignore, seqno); + } case 501: /* Not Implemented */ if (sipmethod == SIP_INVITE) { - if (p->owner) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + handle_response_invite(p, resp, rest, req, ignore, seqno); } else ast_log(LOG_WARNING, "Host '%s' does not implement '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), msg); break; @@ -9304,11 +9381,9 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ snprintf(p->owner->call_forward, sizeof(p->owner->call_forward), "Local/%s@%s", p->username, p->context); /* Fall through */ case 486: /* Busy here */ + case 488: /* Not acceptable here - codec error */ case 600: /* Busy everywhere */ case 603: /* Decline */ - if (p->owner) - ast_queue_control(p->owner, AST_CONTROL_BUSY); - break; case 480: /* Temporarily Unavailable */ case 404: /* Not Found */ case 410: /* Gone */ @@ -9346,27 +9421,63 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ } else { /* Responses to OUTGOING SIP requests on INCOMING calls get handled here. As well as out-of-call message responses */ - if (sip_debug_test_pvt(p)) - ast_verbose("Response message %s arrived\n", msg); + if (req->debug) + ast_verbose("SIP Response message for INCOMING dialog %s arrived\n", msg); switch(resp) { case 200: - /* Change branch since this is a 200 response */ if (sipmethod == SIP_INVITE) { - transmit_request(p, SIP_ACK, seqno, 0, 1); - p->authtries = 0; + handle_response_invite(p, resp, rest, req, ignore, seqno); + } else if (sipmethod == SIP_CANCEL) { + ast_log(LOG_DEBUG, "Got 200 OK on CANCEL\n"); } else if (sipmethod == SIP_MESSAGE) /* We successfully transmitted a message */ ast_set_flag(p, SIP_NEEDDESTROY); break; + case 401: /* www-auth */ case 407: if (sipmethod == SIP_BYE || sipmethod == SIP_REFER) { - if (ast_strlen_zero(p->authname)) - ast_log(LOG_WARNING, "Asked to authenticate %s, to %s:%d but we have no matching peer!\n", - msg, ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr), ntohs(p->recv.sin_port)); - if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, "Proxy-Authenticate", "Proxy-Authorization", sipmethod, 0)) { + char *auth, *auth2; + if (resp == 407) { + auth = "Proxy-Authenticate"; + auth2 = "Proxy-Authorization"; + } else { + auth = "WWW-Authenticate"; + auth2 = "Authorization"; + } + if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, auth, auth2, sipmethod, 0)) { ast_log(LOG_NOTICE, "Failed to authenticate on %s to '%s'\n", msg, get_header(&p->initreq, "From")); ast_set_flag(p, SIP_NEEDDESTROY); } + } else if (sipmethod == SIP_INVITE) { + handle_response_invite(p, resp, rest, req, ignore, seqno); + } + break; + case 481: /* Call leg does not exist */ + if (sipmethod == SIP_INVITE) { + /* Re-invite failed */ + handle_response_invite(p, resp, rest, req, ignore, seqno); + } + break; + default: /* Errors without handlers */ + if ((resp >= 100) && (resp < 200)) { + if (sipmethod == SIP_INVITE) { /* re-invite */ + sip_cancel_destroy(p); + } + } + if ((resp >= 300) && (resp < 700)) { + if ((option_verbose > 2) && (resp != 487)) + ast_verbose(VERBOSE_PREFIX_3 "Incoming call: Got SIP response %d \"%s\" back from %s\n", resp, rest, ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr)); + switch(resp) { + case 488: /* Not acceptable here - codec error */ + case 603: /* Decline */ + case 500: /* Server error */ + case 503: /* Service Unavailable */ + + if (sipmethod == SIP_INVITE) { /* re-invite failed */ + sip_cancel_destroy(p); + } + break; + } } break; } -- cgit v1.2.3