private Boolean UpdateChallenges(TSIP_Response response, Boolean acceptNewVector) { Boolean ret = true; TSIP_HeaderWWWAuthenticate WWW_Authenticate; // TSIP_HeaderProxyAuthenticate Proxy_Authenticate; /* RFC 2617 - HTTP Digest Session * * (A) The client response to a WWW-Authenticate challenge for a protection * space starts an authentication session with that protection space. * The authentication session lasts until the client receives another * WWW-Authenticate challenge from any server in the protection space. * * (B) The server may return a 401 response with a new nonce value, causing the client * to retry the request; by specifying stale=TRUE with this response, * the server tells the client to retry with the new nonce, but without * prompting for a new username and password. */ /* RFC 2617 - 1.2 Access Authentication Framework * The realm directive (case-insensitive) is required for all authentication schemes that issue a challenge. */ /* FIXME: As we perform the same task ==> Use only one loop. */ for (int i = 0; (WWW_Authenticate = (TSIP_HeaderWWWAuthenticate)response.GetHeaderAtIndex(TSIP_Header.tsip_header_type_t.WWW_Authenticate, i)) != null; i++) { Boolean isnew = true; foreach (TSIP_Challenge challenge in mChallenges) { //if(challenge.IProxy) //{ // continue; //} if (String.Equals(challenge.Realm, WWW_Authenticate.Realm, StringComparison.InvariantCultureIgnoreCase) && (WWW_Authenticate.Stale || acceptNewVector)) { /*== (B) ==*/ if (!(ret = challenge.Update(WWW_Authenticate.Scheme, WWW_Authenticate.Realm, WWW_Authenticate.Nonce, WWW_Authenticate.Opaque, WWW_Authenticate.Algorithm, WWW_Authenticate.Qop))) { return(ret); } else { isnew = false; continue; } } else { TSK_Debug.Error("Failed to handle new challenge"); return(false); } } if (isnew) { TSIP_Challenge challenge; if ((challenge = new TSIP_Challenge(this.Stack, false, WWW_Authenticate.Scheme, WWW_Authenticate.Realm, WWW_Authenticate.Nonce, WWW_Authenticate.Opaque, WWW_Authenticate.Algorithm, WWW_Authenticate.Qop)) != null) { mChallenges.Add(challenge); } else { TSK_Debug.Error("Failed to handle new challenge"); return(false); } } } return(ret); }
protected TSIP_Request CreateRequest(String method) { TSIP_Request request = null; TSIP_Uri to_uri, from_uri, request_uri; String call_id; int copy_routes_start = -1; /* NONE */ /* * RFC 3261 - 12.2.1.1 Generating the Request * * The Call-ID of the request MUST be set to the Call-ID of the dialog. */ call_id = mCallId; /* * RFC 3261 - 12.2.1.1 Generating the Request * * Requests within a dialog MUST contain strictly monotonically * increasing and contiguous CSeq sequence numbers (increasing-by-one) * in each direction (excepting ACK and CANCEL of course, whose numbers * equal the requests being acknowledged or cancelled). Therefore, if * the local sequence number is not empty, the value of the local * sequence number MUST be incremented by one, and this value MUST be * placed into the CSeq header field. */ /*if(!tsk_striequals(method, "ACK") && !tsk_striequals(method, "CANCEL")) * { * TSIP_DIALOG(self)->cseq_value +=1; * } * ===> See send method (cseq will be incremented before sending the request) */ /* * RFC 3261 - 12.2.1.1 Generating the Request * * The URI in the To field of the request MUST be set to the remote URI * from the dialog state. The tag in the To header field of the request * MUST be set to the remote tag of the dialog ID. The From URI of the * request MUST be set to the local URI from the dialog state. The tag * in the From header field of the request MUST be set to the local tag * of the dialog ID. If the value of the remote or local tags is null, * the tag parameter MUST be omitted from the To or From header fields, * respectively. */ to_uri = mUriRemote; from_uri = mUriLocal; /* * RFC 3261 - 12.2.1.1 Generating the Request * * If the route set is empty, the UAC MUST place the remote target URI * into the Request-URI. The UAC MUST NOT add a Route header field to * the request. */ if (mRecordRoutes == null || mRecordRoutes.Count == 0) { request_uri = mUriRemoteTarget; } /* * RFC 3261 - 12.2.1.1 Generating the Request * * If the route set is not empty, and the first URI in the route set * contains the lr parameter (see Section 19.1.1), the UAC MUST place * the remote target URI into the Request-URI and MUST include a Route * header field containing the route set values in order, including all * parameters. * * If the route set is not empty, and its first URI does not contain the * lr parameter, the UAC MUST place the first URI from the route set * into the Request-URI, stripping any parameters that are not allowed * in a Request-URI. The UAC MUST add a Route header field containing * the remainder of the route set values in order, including all * parameters. The UAC MUST then place the remote target URI into the * Route header field as the last value. * * For example, if the remote target is sip:user@remoteua and the route * set contains: * * <sip:proxy1>,<sip:proxy2>,<sip:proxy3;lr>,<sip:proxy4> */ else { TSIP_Uri first_route = mRecordRoutes[0].Uri; if (TSK_Param.HasParam(first_route.Params, "lr")) { request_uri = mUriRemoteTarget; copy_routes_start = 0; /* Copy all */ } else { request_uri = first_route; copy_routes_start = 1; /* Copy starting at index 1. */ } } /*===================================================================== */ request = new TSIP_Request(method, request_uri, from_uri, to_uri, call_id, (Int32)mCSeqValue); request.To.Tag = mTagRemote; request.From.Tag = mTagLocal; request.ShouldUpdate = true; /* Now signal that the message should be updated by the transport layer (Contact, SigComp, IPSec, ...) */ /* * RFC 3261 - 12.2.1.1 Generating the Request * * A UAC SHOULD include a Contact header field in any target refresh * requests within a dialog, and unless there is a need to change it, * the URI SHOULD be the same as used in previous requests within the * dialog. If the "secure" flag is true, that URI MUST be a SIPS URI. * As discussed in Section 12.2.2, a Contact header field in a target * refresh request updates the remote target URI. This allows a UA to * provide a new contact address, should its address change during the * duration of the dialog. */ switch (request.RequestType) { case TSIP_Message.tsip_request_type_t.MESSAGE: case TSIP_Message.tsip_request_type_t.PUBLISH: case TSIP_Message.tsip_request_type_t.BYE: { if (request.RequestType == TSIP_Message.tsip_request_type_t.PUBLISH) { request.AddHeader(new TSIP_HeaderExpires(mExpires)); } /* add caps in Accept-Contact headers */ foreach (TSK_Param param in mSipSession.Caps) { request.AddHeader(new TSIP_HeaderDummy("Accept-Contact", String.Format("*;{0}{1}{2}", param.Name, !String.IsNullOrEmpty(param.Value) ? "=" : String.Empty, !String.IsNullOrEmpty(param.Value) ? param.Value : String.Empty) )); } break; } default: { String contact = null; List <TSIP_HeaderContact> hdr_contacts = null; if (request.RequestType == TSIP_Message.tsip_request_type_t.OPTIONS || request.RequestType == TSIP_Message.tsip_request_type_t.PUBLISH || request.RequestType == TSIP_Message.tsip_request_type_t.REGISTER) { /**** with expires */ contact = String.Format("m: <{0}:{1}@{2}:{3}>;expires={4}\r\n", "sip", from_uri.UserName, "127.0.0.1", 5060, TSK_Time.Milliseconds2Seconds(mExpires)); } else { /**** without expires */ if (request.RequestType == TSIP_Message.tsip_request_type_t.SUBSCRIBE) { /* RFC 3265 - 3.1.1. Subscription Duration * An "expires" parameter on the "Contact" header has no semantics for SUBSCRIBE and is explicitly * not equivalent to an "Expires" header in a SUBSCRIBE request or response. */ request.AddHeader(new TSIP_HeaderExpires(TSK_Time.Milliseconds2Seconds(mExpires))); } contact = String.Format("m: <{0}:{1}@{2}:{3}>\r\n", "sip", from_uri.UserName, "127.0.0.1", 5060); } hdr_contacts = TSIP_HeaderContact.Parse(contact); if (hdr_contacts != null && hdr_contacts.Count > 0) { request.Contact = hdr_contacts[0]; } /* Add capabilities as per RFC 3840 */ if (request.Contact != null) { foreach (TSK_Param param in mSipSession.Caps) { request.Contact.Params = TSK_Param.AddParam(request.Contact.Params, param.Name, param.Value); } } break; } } /* Update authorizations */ if (mState == tsip_dialog_state_t.Initial && mChallenges == null || mChallenges.Count == 0) { /* 3GPP TS 33.978 6.2.3.1 Procedures at the UE * On sending a REGISTER request in order to indicate support for early IMS security procedures, the UE shall not * include an Authorization header field and not include header fields or header field values as required by RFC3329. */ if (request.IsREGISTER && !this.Stack.EarlyIMS) { /* 3GPP TS 24.229 - 5.1.1.2.2 Initial registration using IMS AKA * On sending a REGISTER request, the UE shall populate the header fields as follows: * a) an Authorization header field, with: * - the "username" header field parameter, set to the value of the private user identity; * - the "realm" header field parameter, set to the domain name of the home network; * - the "uri" header field parameter, set to the SIP URI of the domain name of the home network; * - the "nonce" header field parameter, set to an empty value; and * - the "response" header field parameter, set to an empty value; */ String realm = this.Stack.Realm != null ? this.Stack.Realm.Host : "(null)"; String request_uri_ = TSIP_Uri.ToString(request.Uri, false, false); TSIP_Header auth_hdr = TSIP_Challenge.CreateEmptyAuthorization(this.Stack.PrivateIdentity, realm, request_uri_); if (auth_hdr != null) { request.AddHeader(auth_hdr); } } } else if (mChallenges != null && mChallenges.Count > 0) { TSIP_Header auth_hdr; foreach (TSIP_Challenge challenge in mChallenges) { auth_hdr = challenge.CreateHeaderAuthorization(request); if (auth_hdr != null) { request.AddHeader(auth_hdr); } } } /* Update CSeq */ /* RFC 3261 - 13.2.2.4 2xx Responses * Generating ACK: The sequence number of the CSeq header field MUST be * the same as the INVITE being acknowledged, but the CSeq method MUST * be ACK. The ACK MUST contain the same credentials as the INVITE. If * the 2xx contains an offer (based on the rules above), the ACK MUST * carry an answer in its body. * ==> CSeq number will be added/updated by the caller of this function, * credentials were added above. */ if (!request.IsACK && !request.IsCANCEL) { request.CSeq.CSeq = ++mCSeqValue; } /* Route generation * ==> http://betelco.blogspot.com/2008/11/proxy-and-service-route-discovery-in.html * The dialog Routes have been copied above. * * 3GPP TS 24.229 - 5.1.2A.1 UE-originating case * * The UE shall build a proper preloaded Route header field value for all new dialogs and standalone transactions. The UE * shall build a list of Route header field values made out of the following, in this order: * a) the P-CSCF URI containing the IP address or the FQDN learnt through the P-CSCF discovery procedures; and * b) the P-CSCF port based on the security mechanism in use: * * - if IMS AKA or SIP digest with TLS is in use as a security mechanism, the protected server port learnt during * the registration procedure; * - if SIP digest without TLS, NASS-IMS bundled authentciation or GPRS-IMS-Bundled authentication is in * use as a security mechanism, the unprotected server port used during the registration procedure; * c) and the values received in the Service-Route header field saved from the 200 (OK) response to the last * registration or re-registration of the public user identity with associated contact address. */ if (!request.IsREGISTER) { // According to the above link ==> Initial/Re/De registration do not have routes. if (copy_routes_start != -1) { /* The dialog already have routes ==> copy them. */ if (mState == tsip_dialog_state_t.Early || mState == tsip_dialog_state_t.Established) { Int32 index = -1; foreach (TSIP_HeaderRecordRoute record_route in mRecordRoutes) { TSIP_Uri uri = record_route.Uri; if (++index < copy_routes_start || uri == null) { continue; } TSIP_HeaderRoute route = new TSIP_HeaderRoute(uri); // copy parameters: see http://code.google.com/p/imsdroid/issues/detail?id=52 route.Params.AddRange(record_route.Params); request.AddHeader(route); } } } } else {/* No routes associated to this dialog. */ if (mState == tsip_dialog_state_t.Initial || mState == tsip_dialog_state_t.Early) { /* GPP TS 24.229 section 5.1.2A [Generic procedures applicable to all methods excluding the REGISTER method]: * The UE shall build a proper preloaded Route header field value for all new dialogs and standalone transactions. The UE * shall build a list of Route header field values made out of the following, in this order: * a) the P-CSCF URI containing the IP address or the FQDN learnt through the P-CSCF discovery procedures; and * b) the P-CSCF port based on the security mechanism in use: * - if IMS AKA or SIP digest with TLS is in use as a security mechanism, the protected server port learnt during * the registration procedure; * - if SIP digest without TLS, NASS-IMS bundled authentciation or GPRS-IMS-Bundled authentication is in * use as a security mechanism, the unprotected server port used during the registration procedure; * c) and the values received in the Service-Route header field saved from the 200 (OK) response to the last * registration or re-registration of the public user identity with associated contact address. */ #if _DEBUG && SDS_HACK /* Ericsson SDS hack (INVITE with Proxy-CSCF as First route fail) */ #else TSIP_Uri uri = this.Stack.GetProxyCSCFUri(true); // Proxy-CSCF as first route if (uri != null) { request.AddHeader(new TSIP_HeaderRoute(uri)); } #endif // Service routes foreach (TSIP_Uri uriServiceRoute in mServiceRoutes) { request.AddHeader(new TSIP_HeaderRoute(uriServiceRoute)); } } } /* Add headers associated to the dialog's session */ foreach (TSK_Param param in mSipSession.Headers) { request.AddHeader(new TSIP_HeaderDummy(param.Name, param.Value)); } /* Add headers associated to the dialog's stack */ foreach (TSK_Param param in this.Stack.Headers) { request.AddHeader(new TSIP_HeaderDummy(param.Name, param.Value)); } /* Add common headers */ this.AddCommonHeaders(request); /* SigComp */ return(request); }