public void DumpQueue()
            {
                AsyncHttpRequest pendingRequest = null;
                Hashtable        responseToSend = null;

                lock (m_eqLock)
                {
                    if (isQueueValid == true)
                    {
                        m_highPriorityItems.Clear();
                        m_highPriorityItems = null;
                        m_Items.Clear();
                        m_Items = null;
                    }

                    // If a request is pending signal it.  The response handler will send a 404
                    if (m_Request != null)
                    {
                        pendingRequest = m_Request;
                        m_Request      = null;
                        responseToSend = GetEvents(pendingRequest);
                    }
                }

                if (responseToSend != null)
                {
                    pendingRequest.SendResponse(responseToSend);
                }
            }
예제 #2
0
        private Hashtable HandleHttpCloseSession(Hashtable request)
        {
            DoExpire();

            Hashtable post  = DecodePostString(request["body"].ToString());
            Hashtable reply = new Hashtable();

            reply["str_response_string"] = "";
            reply["int_response_code"]   = 404;
            reply["content_type"]        = "text/plain";

            if (post["ID"] == null)
            {
                return(reply);
            }

            UUID id;

            if (!UUID.TryParse(post["ID"].ToString(), out id))
            {
                return(reply);
            }

            lock (m_Connections)
            {
                if (m_Connections.ContainsKey(id))
                {
                    ConsoleConnection connection = m_Connections[id];
                    m_Connections.Remove(id);
                    CloseConnection(id);
                    if (connection.request != null)
                    {
                        AsyncHttpRequest req = connection.request;
                        connection.request = null;
                        req.SendResponse(ProcessEvents(req));
                    }
                }
            }

            XmlDocument xmldoc  = new XmlDocument();
            XmlNode     xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, "", "");

            xmldoc.AppendChild(xmlnode);
            XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession", "");

            xmldoc.AppendChild(rootElement);

            XmlElement res = xmldoc.CreateElement("", "Result", "");

            res.AppendChild(xmldoc.CreateTextNode("OK"));

            rootElement.AppendChild(res);

            reply["str_response_string"] = xmldoc.InnerXml;
            reply["int_response_code"]   = 200;
            reply["content_type"]        = "text/xml";
            reply = CheckOrigin(reply);

            return(reply);
        }
            public void Enqueue(OSD ev, bool highPriority)
            {
                AsyncHttpRequest pendingRequest = null;
                Hashtable        responseToSend = null;

                lock (m_eqLock)
                {
                    if (isQueueValid == false)
                    {
                        m_log.ErrorFormat("[EVENTQUEUE]: Unable to enqueue new event. isQueueValid == false");
                        return;
                    }

                    if (highPriority)
                    {
                        m_highPriorityItems.Enqueue(ev);
                    }
                    else
                    {
                        m_Items.Enqueue(ev);
                    }

                    if (m_Request != null)
                    {
                        pendingRequest = m_Request;
                        m_Request      = null;
                        responseToSend = GetEvents(pendingRequest);
                    }
                }

                if (responseToSend != null)
                {
                    pendingRequest.SendResponse(responseToSend);
                }
            }
            public void Handle(IHttpServer server, string path, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
            {
                AsyncHttpRequest pendingRequest    = null;
                AsyncHttpRequest eventQueueRequest =
                    new AsyncHttpRequest(server, httpRequest, httpResponse, m_agentID, TimeoutHandler, m_timeout);
                Hashtable responseToSend = null;

                lock (m_eqLock)
                {
                    if (!isQueueValid)
                    {
                        m_log.ErrorFormat("[EVENTQUEUE]: HandleRequest for {0} failed, isQueueValid == false", m_agentID);
                        m_Request = null;
                        return;
                    }

                    pendingRequest = m_Request;
                    m_Request      = eventQueueRequest;

                    if ((pendingRequest == null) && (m_highPriorityItems.Count > 0 || m_Items.Count > 0))
                    {
                        pendingRequest = m_Request;
                        m_Request      = null;
                        responseToSend = GetEvents(pendingRequest);
                    }
                }

                if (responseToSend != null)
                {
                    pendingRequest.SendResponse(responseToSend);
                }
            }
예제 #5
0
        private void TimeoutHandler(AsyncHttpRequest pRequest)
        {
            UUID sessionid = pRequest.AgentID;

            lock (m_Connections)
            {
                ConsoleConnection connection = null;
                m_Connections.TryGetValue(sessionid, out connection);
                if (connection == null)
                {
                    return;
                }
                if (connection.request == pRequest)
                {
                    connection.request = null;
                }
            }

            pRequest.SendResponse(ProcessEvents(pRequest));
        }
예제 #6
0
        public void AsyncReadResponses(IHttpServer server, string path, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
        {
            int    pos1    = path.IndexOf("/");           // /ReadResponses
            int    pos2    = path.IndexOf("/", pos1 + 1); // /ReadResponses/
            int    pos3    = path.IndexOf("/", pos2 + 1); // /ReadResponses/<UUID>/
            int    len     = pos3 - pos2 - 1;
            string uri_tmp = path.Substring(pos2 + 1, len);

            UUID sessionID;

            if (UUID.TryParse(uri_tmp, out sessionID) == false)
            {
                return;
            }

            // Create the new request
            AsyncHttpRequest newRequest =
                new AsyncHttpRequest(server, httpRequest, httpResponse, sessionID, TimeoutHandler, 60 * 1000);
            AsyncHttpRequest currentRequest = null;

            lock (m_Connections)
            {
                ConsoleConnection connection = null;
                m_Connections.TryGetValue(sessionID, out connection);
                if (connection == null)
                {
                    return;
                }

                currentRequest     = connection.request;
                connection.request = newRequest;
            }

            // If there was a request already posted signal it
            if (currentRequest != null)
            {
                currentRequest.SendResponse(ProcessEvents(currentRequest));
            }
        }
            private void TimeoutHandler(AsyncHttpRequest pRequest)
            {
                UUID      sessionid    = pRequest.AgentID;
                Hashtable eventsToSend = null;

                lock (m_eqLock)
                {
                    if (isQueueValid == false)
                    {
                        return;
                    }

                    if (m_Request == pRequest)
                    {
                        m_Request    = null;
                        eventsToSend = GetEvents(pRequest);
                    }
                }

                if (eventsToSend != null)
                {
                    pRequest.SendResponse(eventsToSend);
                }
            }
예제 #8
0
 private void TimeoutHandler(AsyncHttpRequest pRequest)
 {
     pRequest.SendResponse(ProcessEvents(pRequest, true));
 }
예제 #9
0
        public void AsyncHttpRequest(IHttpServer server, string path, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
        {
            UUID urlcode;

            if (UUID.TryParse(path, out urlcode))
            {
                return;
            }

            AsyncHttpRequest asyncRequest =
                new AsyncHttpRequest(server, httpRequest, httpResponse, urlcode, TimeoutHandler, m_DefaultTimeout);

            UUID      requestID = asyncRequest.RequestID;
            Hashtable request   = asyncRequest.RequestData;

            string uri = request["uri"].ToString();

            try
            {
                Hashtable headers = (Hashtable)request["headers"];
                //HTTP server code doesn't provide us with QueryStrings
                string queryString = String.Empty;

                int    pos1     = uri.IndexOf("/");           // /lslhttp
                int    pos2     = uri.IndexOf("/", pos1 + 1); // /lslhttp/
                int    pos3     = uri.IndexOf("/", pos2 + 1); // /lslhttp/<UUID>/
                string pathInfo = uri.Substring(pos3);

                string  url     = URLFromURI(uri);
                UrlData urlData = null;

                lock (m_UrlMap)
                {
                    m_UrlMap.TryGetValue(url, out urlData);

                    // Returning NULL sends a 404 from the base server
                    if (urlData == null)
                    {
                        asyncRequest.SendResponse(server.SendHTML404(httpResponse));
                    }
                }

                //for llGetHttpHeader support we need to store original URI here
                //to make x-path-info / x-query-string / x-script-url / x-remote-ip headers
                //as per http://wiki.secondlife.com/wiki/LlGetHTTPHeader

                RequestData requestData =
                    new RequestData
                {
                    requestId     = asyncRequest.RequestID,
                    polledRequest = asyncRequest,
                    requestDone   = false,
                    startTime     = Environment.TickCount,
                    uri           = uri
                };

                if (requestData.headers == null)
                {
                    requestData.headers = new Dictionary <string, string>();
                }

                // Copy in the headers, convert keys to lower case: See.
                // http://wiki.secondlife.com/wiki/LlGetHTTPHeader
                foreach (DictionaryEntry header in headers)
                {
                    string key   = (string)header.Key;
                    string value = (string)header.Value;
                    requestData.headers.Add(key.ToLower(), value);
                }

                foreach (DictionaryEntry de in request)
                {
                    if (de.Key.ToString() == "querystringkeys")
                    {
                        String[] keys = (String[])de.Value;
                        foreach (String key in keys)
                        {
                            if (request.ContainsKey(key))
                            {
                                string val = (String)request[key];
                                queryString = queryString + key + "=" + val + "&";
                            }
                        }

                        if (queryString.Length > 1)
                        {
                            queryString = queryString.Substring(0, queryString.Length - 1);
                        }
                    }
                }

                //if this machine is behind DNAT/port forwarding, currently this is being
                //set to address of port forwarding router
                requestData.headers["x-remote-ip"]    = httpRequest.RemoteIPEndPoint.ToString();
                requestData.headers["x-path-info"]    = pathInfo;
                requestData.headers["x-query-string"] = queryString;
                requestData.headers["x-script-url"]   = urlData.url;

                lock (m_RequestMap)
                {
                    m_RequestMap.Add(requestID, requestData);
                }

                lock (m_UrlMap)
                {
                    urlData.requests.Add(requestID);
                }

                urlData.engine.PostScriptEvent(urlData.itemID, "http_request", new Object[] { requestID.ToString(), request["http-method"].ToString(), request["body"].ToString() });
            }
            catch (Exception we)
            {
                m_log.Warn("[HttpRequestHandler]: http-in request failed");
                m_log.Warn(we.Message);
                m_log.Warn(we.StackTrace);

                asyncRequest.SendResponse(server.SendHTML500(httpResponse));
            }
        }
예제 #10
0
        public void AsyncReadResponses(IHttpServer server, string path, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
        {
            int    pos1    = path.IndexOf("/");           // /ReadResponses
            int    pos2    = path.IndexOf("/", pos1 + 1); // /ReadResponses/
            int    pos3    = path.IndexOf("/", pos2 + 1); // /ReadResponses/<UUID>/
            int    len     = pos3 - pos2 - 1;
            string uri_tmp = path.Substring(pos2 + 1, len);

            var authHeader = httpRequest.Headers.Get("Authorization");

            if (authHeader != null)
            {
                if (!authHeader.StartsWith("Bearer ", StringComparison.InvariantCultureIgnoreCase))
                {
                    m_log.Warn($"[REMOTECONSOLE] ReadResponses JWT Authorization header format failure from '{httpRequest.RemoteIPEndPoint}'.");
                    return;
                }

                try
                {
                    var token = new JWToken(authHeader.Substring(7), m_sigUtil);

                    // TODO: Make the scope strings come from some central list that can be registered into?
                    if (!(token.HasValidSignature && token.IsNotExpired && token.Payload.Scope == "remote-console"))
                    {
                        m_log.Warn($"[REMOTECONSOLE] ReadResponses invalid/expired/wrong scope JWToken from '{httpRequest.RemoteIPEndPoint}'.");
                        return;
                    }

                    m_log.Info($"[REMOTECONSOLE] ReadResponses for session '{uri_tmp}' accessed via JWT by '{token.Payload.Username}' from '{httpRequest.RemoteIPEndPoint}'.");
                }
                catch (JWTokenException jte)
                {
                    m_log.Error($"[REMOTECONSOLE] Failure with JWToken in ReadResponses from '{httpRequest.RemoteIPEndPoint}': {jte}");
                    return;
                }
            }
            else
            {
                m_log.Warn($"[REMOTECONSOLE] ReadResponses for session '{uri_tmp}' from '{httpRequest.RemoteIPEndPoint}' being accessed without Authorization header!");
            }
            // BUG: Longstanding issue: if someone gets ahold of, or guesses, the ID of another user they can send comamnds to the console.
            // The only way I can think to close this bug is to associate each session with something the user cannot change. Not sure, but maybe the IP address of the connection would work?

            UUID sessionID;

            if (UUID.TryParse(uri_tmp, out sessionID) == false)
            {
                return;
            }

            // Create the new request
            AsyncHttpRequest newRequest =
                new AsyncHttpRequest(server, httpRequest, httpResponse, sessionID, TimeoutHandler, 60 * 1000);
            AsyncHttpRequest currentRequest = null;

            lock (m_Connections)
            {
                ConsoleConnection connection = null;
                m_Connections.TryGetValue(sessionID, out connection);
                if (connection == null)
                {
                    return;
                }

                currentRequest     = connection.request;
                connection.request = newRequest;
            }

            // If there was a request already posted, signal it.
            if (currentRequest != null)
            {
                currentRequest.SendResponse(ProcessEvents(currentRequest));
            }
        }
예제 #11
0
        private Hashtable HandleHttpCloseSession(Hashtable request)
        {
            DoExpire();

            Hashtable post  = DecodePostString(request["body"].ToString());
            Hashtable reply = new Hashtable();

            reply["str_response_string"] = String.Empty;
            reply["int_response_code"]   = 404;
            reply["content_type"]        = "text/plain";

            JWToken token   = null;
            var     headers = (Hashtable)request["headers"];

            if (headers.ContainsKey("Authorization"))
            {
                var authHeader = headers["Authorization"].ToString();
                if (!authHeader.StartsWith("Bearer ", StringComparison.InvariantCultureIgnoreCase))
                {
                    m_log.Warn($"[REMOTECONSOLE] CloseSession JWT Authorization header format failure from '{headers["remote_addr"]}'.");
                    return(reply);
                }

                try
                {
                    token = new JWToken(authHeader.Substring(7), m_sigUtil);

                    // TODO: Make the scope strings come from some central list that can be registered into?
                    if (!(token.HasValidSignature && token.IsNotExpired && token.Payload.Scope == "remote-console"))
                    {
                        m_log.Warn($"[REMOTECONSOLE] CloseSession invalid/expired/wrong scope JWToken from '{headers["remote_addr"]}'.");
                        return(reply);
                    }

                    m_log.Info($"[REMOTECONSOLE] CloseSession for session '{post["ID"]}' accessed via JWT by '{token.Payload.Username}' from '{headers["remote_addr"]}'.");
                }
                catch (JWTokenException jte)
                {
                    m_log.Error($"[REMOTECONSOLE] Failure with JWToken in CloseSession from '{headers["remote_addr"]}': {jte}");
                    return(reply);
                }
            }
            else
            {
                m_log.Warn($"[REMOTECONSOLE] CloseSession for session '{post["ID"]}' from '{headers["remote_addr"]}' being accessed without Authorization header!");
            }
            // BUG: Longstanding issue: if someone gets ahold of, or guesses, the ID and/or JWT of another user they can close the console.
            // The only way I can think to close this bug is to associate each session with something the user cannot change. Not sure, but maybe the IP address of the connection would work?

            if (post["ID"] == null)
            {
                return(reply);
            }

            UUID id;

            if (!UUID.TryParse(post["ID"].ToString(), out id))
            {
                return(reply);
            }

            lock (m_Connections)
            {
                if (m_Connections.ContainsKey(id))
                {
                    ConsoleConnection connection = m_Connections[id];
                    m_Connections.Remove(id);
                    CloseConnection(id);
                    if (connection.request != null)
                    {
                        AsyncHttpRequest req = connection.request;
                        connection.request = null;
                        req.SendResponse(ProcessEvents(req));
                    }
                }
            }

            XmlDocument xmldoc  = new XmlDocument();
            XmlNode     xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, String.Empty, String.Empty);

            xmldoc.AppendChild(xmlnode);
            XmlElement rootElement = xmldoc.CreateElement(String.Empty, "ConsoleSession", String.Empty);

            xmldoc.AppendChild(rootElement);

            XmlElement res = xmldoc.CreateElement(String.Empty, "Result", String.Empty);

            res.AppendChild(xmldoc.CreateTextNode("OK"));

            rootElement.AppendChild(res);

            reply["str_response_string"] = xmldoc.InnerXml;
            reply["int_response_code"]   = 200;
            reply["content_type"]        = "text/xml";
            reply = CheckOrigin(reply);

            m_log.Info($"[REMOTECONSOLE] CloseSession successful for user '{token?.Payload.Username}' with session '{id}' from '{headers["remote_addr"]}'.");

            return(reply);
        }