/// <summary> /// Authenticates user. /// </summary> /// <param name="userName">User login name.</param> /// <param name="password">Password.</param> /// <param name="tryApop"> If true and POP3 server supports APOP, then APOP is used, otherwise normal login used.</param> /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception> /// <exception cref="InvalidOperationException">Is raised when POP3 client is not connected or is already authenticated.</exception> /// <exception cref="POP3_ClientException">Is raised when POP3 server returns error.</exception> public void Authenticate(string userName, string password, bool tryApop) { if (this.IsDisposed) { throw new ObjectDisposedException(this.GetType().Name); } if (!this.IsConnected) { throw new InvalidOperationException("You must connect first."); } if (this.IsAuthenticated) { throw new InvalidOperationException("Session is already authenticated."); } // Supports APOP, use it. if (tryApop && m_ApopHashKey.Length > 0) { string hexHash = Net_Utils.ComputeMd5(m_ApopHashKey + password, true); int countWritten = this.TcpStream.WriteLine("APOP " + userName + " " + hexHash); LogAddWrite(countWritten, "APOP " + userName + " " + hexHash); string line = this.ReadLine(); if (line.StartsWith("+OK")) { m_pAuthdUserIdentity = new GenericIdentity(userName, "apop"); } else { throw new POP3_ClientException(line); } } // Use normal LOGIN, don't support APOP. else { int countWritten = this.TcpStream.WriteLine("USER " + userName); LogAddWrite(countWritten, "USER " + userName); string line = this.ReadLine(); if (line.StartsWith("+OK")) { countWritten = this.TcpStream.WriteLine("PASS " + password); LogAddWrite(countWritten, "PASS <***REMOVED***>"); line = this.ReadLine(); if (line.StartsWith("+OK")) { m_pAuthdUserIdentity = new GenericIdentity(userName, "pop3-user/pass"); } else { throw new POP3_ClientException(line); } } else { throw new POP3_ClientException(line); } } if (this.IsAuthenticated) { FillMessages(); } }
private string H(string value) { return(Net_Utils.ComputeMd5(value, true)); }
/// <summary> /// Forwards specified request to target recipient. /// </summary> /// <param name="statefull">Specifies if request is sent statefully or statelessly.</param> /// <param name="e">Request event arguments.</param> /// <param name="addRecordRoute">If true Record-Route header field is added.</param> internal void ForwardRequest(bool statefull, SIP_RequestReceivedEventArgs e, bool addRecordRoute) { SIP_RequestContext requestContext = new SIP_RequestContext(this, e.Request, e.Flow); SIP_Request request = e.Request; SIP_Uri route = null; /* RFC 3261 16. * 1. Validate the request (Section 16.3) * 1. Reasonable Syntax * 2. URI scheme (NOTE: We do it later) * 3. Max-Forwards * 4. (Optional) Loop Detection * 5. Proxy-Require * 6. Proxy-Authorization * 2. Preprocess routing information (Section 16.4) * 3. Determine target(s) for the request (Section 16.5) * x. Custom handling (non-RFC) * 1. Process custom request handlers. * 2. URI scheme * 4. Forward the request (Section 16.6) */ // 1.1 Reasonable Syntax. // SIP_Message parsing have done it. // 1.2 URI scheme check. // We do it later. // 1.3 Max-Forwards. if (request.MaxForwards <= 0) { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x483_Too_Many_Hops, request)); return; } // 1.4 (Optional) Loop Detection. // Skip. // 1.5 Proxy-Require. // TODO: // 1.6 Proxy-Authorization. /* If an element requires credentials before forwarding a request, * the request MUST be inspected as described in Section 22.3. That * section also defines what the element must do if the inspection * fails. */ // We need to auth all foreign calls. if (!SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString()) || !this.OnIsLocalUri(((SIP_Uri)request.RequestLine.Uri).Host)) { // If To: field is registrar AOR and request-URI is local registration contact, skip authentication. bool skipAuth = false; if (request.To.Address.IsSipOrSipsUri) { SIP_Registration registration = m_pRegistrar.GetRegistration(((SIP_Uri)request.To.Address.Uri).Address); if (registration != null) { if (registration.GetBinding(request.RequestLine.Uri) != null) { skipAuth = true; } } } if (!skipAuth) { string userName = null; // We need to pass-through ACK. if (request.RequestLine.Method == SIP_Methods.ACK) { } else if (!AuthenticateRequest(e, out userName)) { return; } requestContext.SetUser(userName); } } /* * The proxy MUST inspect the Request-URI of the request. If the * Request-URI of the request contains a value this proxy previously * placed into a Record-Route header field (see Section 16.6 item 4), * the proxy MUST replace the Request-URI in the request with the last * value from the Route header field, and remove that value from the * Route header field. The proxy MUST then proceed as if it received * this modified request. * * This will only happen when the element sending the request to the * proxy (which may have been an endpoint) is a strict router. This * rewrite on receive is necessary to enable backwards compatibility * with those elements. It also allows elements following this * specification to preserve the Request-URI through strict-routing * proxies (see Section 12.2.1.1). * * This requirement does not obligate a proxy to keep state in order * to detect URIs it previously placed in Record-Route header fields. * Instead, a proxy need only place enough information in those URIs * to recognize them as values it provided when they later appear. * * If the Request-URI contains a maddr parameter, the proxy MUST check * to see if its value is in the set of addresses or domains the proxy * is configured to be responsible for. If the Request-URI has a maddr * parameter with a value the proxy is responsible for, and the request * was received using the port and transport indicated (explicitly or by * default) in the Request-URI, the proxy MUST strip the maddr and any * non-default port or transport parameter and continue processing as if * those values had not been present in the request. * * A request may arrive with a maddr matching the proxy, but on a * port or transport different from that indicated in the URI. Such * a request needs to be forwarded to the proxy using the indicated * port and transport. * * If the first value in the Route header field indicates this proxy, * the proxy MUST remove that value from the request. */ // Strict route - handle it. if ((request.RequestLine.Uri is SIP_Uri) && IsRecordRoute(((SIP_Uri)request.RequestLine.Uri)) && request.Route.GetAllValues().Length > 0) { request.RequestLine.Uri = request.Route.GetAllValues()[request.Route.GetAllValues().Length - 1].Address.Uri; SIP_t_AddressParam[] routes = request.Route.GetAllValues(); route = (SIP_Uri)routes[routes.Length - 1].Address.Uri; request.Route.RemoveLastValue(); } // Check if Route header field indicates this proxy. if (request.Route.GetAllValues().Length > 0) { route = (SIP_Uri)request.Route.GetTopMostValue().Address.Uri; // We consider loose-route always ours, because otherwise this message never reach here. if (route.Param_Lr) { request.Route.RemoveTopMostValue(); } // It's our route, remove it. else if (IsLocalRoute(route)) { request.Route.RemoveTopMostValue(); } } if (e.Request.RequestLine.Method == SIP_Methods.REGISTER) { SIP_Uri requestUri = (SIP_Uri)e.Request.RequestLine.Uri; // REGISTER is meant for us. if (this.OnIsLocalUri(requestUri.Host)) { if ((m_ProxyMode & SIP_ProxyMode.Registrar) != 0) { m_pRegistrar.Register(e); return; } else { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x405_Method_Not_Allowed, e.Request)); return; } } // Forward REGISTER. // else{ } /* 3. Determine target(s) for the request (Section 16.5) * Next, the proxy calculates the target(s) of the request. The set of * targets will either be predetermined by the contents of the request * or will be obtained from an abstract location service. Each target * in the set is represented as a URI. * * If the domain of the Request-URI indicates a domain this element is * not responsible for, the Request-URI MUST be placed into the target * set as the only target, and the element MUST proceed to the task of * Request Forwarding (Section 16.6). * * If the target set for the request has not been predetermined as * described above, this implies that the element is responsible for the * domain in the Request-URI, and the element MAY use whatever mechanism * it desires to determine where to send the request. Any of these * mechanisms can be modeled as accessing an abstract Location Service. * This may consist of obtaining information from a location service * created by a SIP Registrar, reading a database, consulting a presence * server, utilizing other protocols, or simply performing an * algorithmic substitution on the Request-URI. When accessing the * location service constructed by a registrar, the Request-URI MUST * first be canonicalized as described in Section 10.3 before being used * as an index. The output of these mechanisms is used to construct the * target set. */ // Non-SIP // Foreign SIP // Local SIP if (e.Request.RequestLine.Uri is SIP_Uri) { SIP_Uri requestUri = (SIP_Uri)e.Request.RequestLine.Uri; // Proxy is not responsible for the domain in the Request-URI. if (!this.OnIsLocalUri(requestUri.Host)) { /* NAT traversal. * When we do record routing, store request sender flow info and request target flow info. * Now the tricky part, how proxy later which flow is target (because both sides can send requests). * Sender-flow will store from-tag to flow and target-flow will store flowID only (Because we don't know to-tag). * Later if request to-tag matches(incoming request), use that flow, otherwise(outgoing request) other flow. * * flowInfo: sender-flow "/" target-flow * sender-flow = from-tag ":" flowID * target-flow = flowID */ SIP_Flow targetFlow = null; string flowInfo = (route != null && route.Parameters["flowInfo"] != null) ? route.Parameters["flowInfo"].Value : null; if (flowInfo != null && request.To.Tag != null) { string flow1Tag = flowInfo.Substring(0, flowInfo.IndexOf(':')); string flow1ID = flowInfo.Substring(flowInfo.IndexOf(':') + 1, flowInfo.IndexOf('/') - flowInfo.IndexOf(':') - 1); string flow2ID = flowInfo.Substring(flowInfo.IndexOf('/') + 1); if (flow1Tag == request.To.Tag) { targetFlow = m_pStack.TransportLayer.GetFlow(flow1ID); } else { ; targetFlow = m_pStack.TransportLayer.GetFlow(flow2ID); } } requestContext.Targets.Add(new SIP_ProxyTarget(requestUri, targetFlow)); } // Proxy is responsible for the domain in the Request-URI. else { // Try to get AOR from registrar. SIP_Registration registration = m_pRegistrar.GetRegistration(requestUri.Address); // We have AOR specified in request-URI in registrar server. if (registration != null) { // Add all AOR SIP contacts to target set. foreach (SIP_RegistrationBinding binding in registration.Bindings) { if (binding.ContactURI is SIP_Uri && binding.TTL > 0) { requestContext.Targets.Add(new SIP_ProxyTarget((SIP_Uri)binding.ContactURI, binding.Flow)); } } } // We don't have AOR specified in request-URI in registrar server. else { // If the Request-URI indicates a resource at this proxy that does not // exist, the proxy MUST return a 404 (Not Found) response. if (!this.OnAddressExists(requestUri.Address)) { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x404_Not_Found, e.Request)); return; } } // If the target set remains empty after applying all of the above, the proxy MUST return an error response, // which SHOULD be the 480 (Temporarily Unavailable) response. if (requestContext.Targets.Count == 0) { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x480_Temporarily_Unavailable, e.Request)); return; } } } // x.1 Process custom request handlers, if any. foreach (SIP_ProxyHandler handler in this.Handlers) { try{ SIP_ProxyHandler h = handler; // Reusing existing handler not allowed, create new instance of handler. if (!handler.IsReusable) { h = (SIP_ProxyHandler)System.Activator.CreateInstance(handler.GetType()); } if (h.ProcessRequest(requestContext)) { // Handler processed request, we are done. return; } } catch (Exception x) { m_pStack.OnError(x); } } // x.2 URI scheme. // If no targets and request-URI non-SIP, reject request because custom handlers should have been handled it. if (requestContext.Targets.Count == 0 && !SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString())) { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x416_Unsupported_URI_Scheme, e.Request)); return; } if (statefull) { SIP_ProxyContext proxyContext = this.CreateProxyContext(requestContext, e.ServerTransaction, request, addRecordRoute); proxyContext.Start(); } else { /* RFC 3261 16.6 Request Forwarding. * For each target, the proxy forwards the request following these steps: * 1. Make a copy of the received request * 2. Update the Request-URI * 3. Update the Max-Forwards header field * 4. Optionally add a Record-route header field value * 5. Optionally add additional header fields * 6. Postprocess routing information * 7. Determine the next-hop address, port, and transport * 8. Add a Via header field value * 9. Add a Content-Length header field if necessary * 10. Forward the new request */ /* RFC 3261 16.11 Stateless Proxy. * o A stateless proxy MUST choose one and only one target from the target set. This choice * MUST only rely on fields in the message and time-invariant properties of the server. In * particular, a retransmitted request MUST be forwarded to the same destination each time * it is processed. Furthermore, CANCEL and non-Routed ACK requests MUST generate the same * choice as their associated INVITE. * * However, a stateless proxy cannot simply use a random number generator to compute * the first component of the branch ID, as described in Section 16.6 bullet 8. * This is because retransmissions of a request need to have the same value, and * a stateless proxy cannot tell a retransmission from the original request. * * We just use: "z9hG4bK-" + md5(topmost branch) */ bool isStrictRoute = false; SIP_Hop[] hops = null; SIP_Request forwardRequest = request.Copy(); forwardRequest.RequestLine.Uri = requestContext.Targets[0].TargetUri; forwardRequest.MaxForwards--; /* 6. Postprocess routing information. * * If the copy contains a Route header field, the proxy MUST inspect the URI in its first value. * If that URI does not contain an lr parameter, the proxy MUST modify the copy as follows: * - The proxy MUST place the Request-URI into the Route header * field as the last value. * * - The proxy MUST then place the first Route header field value * into the Request-URI and remove that value from the Route header field. */ if (forwardRequest.Route.GetAllValues().Length > 0 && !forwardRequest.Route.GetTopMostValue().Parameters.Contains("lr")) { forwardRequest.Route.Add(forwardRequest.RequestLine.Uri.ToString()); forwardRequest.RequestLine.Uri = SIP_Utils.UriToRequestUri(forwardRequest.Route.GetTopMostValue().Address.Uri); forwardRequest.Route.RemoveTopMostValue(); isStrictRoute = true; } /* 7. Determine the next-hop address, port, and transport. * The proxy MAY have a local policy to send the request to a * specific IP address, port, and transport, independent of the * values of the Route and Request-URI. Such a policy MUST NOT be * used if the proxy is not certain that the IP address, port, and * transport correspond to a server that is a loose router. * However, this mechanism for sending the request through a * specific next hop is NOT RECOMMENDED; instead a Route header * field should be used for that purpose as described above. * * In the absence of such an overriding mechanism, the proxy * applies the procedures listed in [4] as follows to determine * where to send the request. If the proxy has reformatted the * request to send to a strict-routing element as described in * step 6 above, the proxy MUST apply those procedures to the * Request-URI of the request. Otherwise, the proxy MUST apply * the procedures to the first value in the Route header field, if * present, else the Request-URI. The procedures will produce an * ordered set of (address, port, transport) tuples. * Independently of which URI is being used as input to the * procedures of [4], if the Request-URI specifies a SIPS * resource, the proxy MUST follow the procedures of [4] as if the * input URI were a SIPS URI. * * As described in [4], the proxy MUST attempt to deliver the * message to the first tuple in that set, and proceed through the * set in order until the delivery attempt succeeds. * * For each tuple attempted, the proxy MUST format the message as * appropriate for the tuple and send the request using a new * client transaction as detailed in steps 8 through 10. * * Since each attempt uses a new client transaction, it represents * a new branch. Thus, the branch parameter provided with the Via * header field inserted in step 8 MUST be different for each * attempt. * * If the client transaction reports failure to send the request * or a timeout from its state machine, the proxy continues to the * next address in that ordered set. If the ordered set is * exhausted, the request cannot be forwarded to this element in * the target set. The proxy does not need to place anything in * the response context, but otherwise acts as if this element of * the target set returned a 408 (Request Timeout) final response. */ SIP_Uri uri = null; if (isStrictRoute) { uri = (SIP_Uri)forwardRequest.RequestLine.Uri; } else if (forwardRequest.Route.GetTopMostValue() != null) { uri = (SIP_Uri)forwardRequest.Route.GetTopMostValue().Address.Uri; } else { uri = (SIP_Uri)forwardRequest.RequestLine.Uri; } hops = m_pStack.GetHops(uri, forwardRequest.ToByteData().Length, ((SIP_Uri)forwardRequest.RequestLine.Uri).IsSecure); if (hops.Length == 0) { if (forwardRequest.RequestLine.Method != SIP_Methods.ACK) { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x503_Service_Unavailable + ": No hop(s) for target.", forwardRequest)); } return; } forwardRequest.Via.AddToTop("SIP/2.0/transport-tl-addign sentBy-tl-assign-it;branch=z9hG4bK-" + Net_Utils.ComputeMd5(request.Via.GetTopMostValue().Branch, true)); // Add 'flowID' what received request, you should use the same flow to send response back. // For more info see RFC 3261 18.2.2. forwardRequest.Via.GetTopMostValue().Parameters.Add("flowID", request.Flow.ID); // Skip, our SIP_Message class is smart and do it when ever it's needed. try{ try{ if (requestContext.Targets[0].Flow != null) { m_pStack.TransportLayer.SendRequest(requestContext.Targets[0].Flow, request); return; } } catch { m_pStack.TransportLayer.SendRequest(request, null, hops[0]); } } catch (SIP_TransportException x) { string dummy = x.Message; if (forwardRequest.RequestLine.Method != SIP_Methods.ACK) { /* RFC 3261 16.9 Handling Transport Errors * If the transport layer notifies a proxy of an error when it tries to * forward a request (see Section 18.4), the proxy MUST behave as if the * forwarded request received a 503 (Service Unavailable) response. */ e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x503_Service_Unavailable + ": Transport error.", forwardRequest)); } } } }
/// <summary> /// Calculates 'rspauth' value. /// </summary> /// <param name="userName">User name.</param> /// <param name="password">Password.</param> /// <returns>Returns 'rspauth' value.</returns> public string CalculateRspAuth(string userName, string password) { /* RFC 2617 3.2.3. * The optional response digest in the "response-auth" directive * supports mutual authentication -- the server proves that it knows the * user's secret, and with qop=auth-int also provides limited integrity * protection of the response. The "response-digest" value is calculated * as for the "request-digest" in the Authorization header, except that * if "qop=auth" or is not specified in the Authorization header for the * request, A2 is * * A2 = ":" digest-uri-value * * and if "qop=auth-int", then A2 is * * A2 = ":" digest-uri-value ":" H(entity-body) * * where "digest-uri-value" is the value of the "uri" directive on the * Authorization header in the request. The "cnonce-value" and "nc- * value" MUST be the ones for the client request to which this message * is the response. The "response-auth", "cnonce", and "nonce-count" * directives MUST BE present if "qop=auth" or "qop=auth-int" is * specified. */ string a1 = ""; string a2 = ""; // Create A1 if (this.Algorithm == "" || this.Algorithm.ToLower() == "md5") { a1 = userName + ":" + this.Realm + ":" + password; } else if (this.Algorithm.ToLower() == "md5-sess") { a1 = Net_Utils.ComputeMd5(userName + ":" + this.Realm + ":" + password, false) + ":" + this.Nonce + ":" + this.CNonce; } else { throw new ArgumentException("Invalid Algorithm value '" + this.Algorithm + "' !"); } // Create A2 if (this.Qop == "" || this.Qop.ToLower() == "auth") { a2 = ":" + this.Uri; } else { throw new ArgumentException("Invalid qop value '" + this.Qop + "' !"); } // Calculate response value. // qop present if (!string.IsNullOrEmpty(this.Qop)) { return(Net_Utils.ComputeMd5(Net_Utils.ComputeMd5(a1, true) + ":" + this.Nonce + ":" + this.NonceCount.ToString("x8") + ":" + this.CNonce + ":" + this.Qop + ":" + Net_Utils.ComputeMd5(a2, true), true)); } // qop not present else { return(Net_Utils.ComputeMd5(Net_Utils.ComputeMd5(a1, true) + ":" + this.Nonce + ":" + Net_Utils.ComputeMd5(a2, true), true)); } }