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); } }
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); } }
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)); }
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); } }
private void TimeoutHandler(AsyncHttpRequest pRequest) { pRequest.SendResponse(ProcessEvents(pRequest, true)); }
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)); } }
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)); } }
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); }