示例#1
0
        /// <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();
            }
        }
示例#2
0
 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));
                    }
                }
            }
        }
示例#4
0
        /// <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));
            }
        }