/// <summary>deals with methods of the router class</summary> private void AcceptClientThread(IAsyncResult result) { TcpClient client = _listener.EndAcceptTcpClient(result); _processed_connection.Set(); EndPoint?endpoint = client.Client.RemoteEndPoint; // we put it right here to make things easier later on (gwyneth 20220107) string ip = String.Empty; // more stupid checks to deal with the many, many ways this can become null... (gwyneth 20220214) if (endpoint != null) { string?endpointToString = endpoint.ToString(); if (endpointToString != null) { ip = endpointToString.Split(':')[0]; DebugUtilities .WriteInfo($"Processing Connection from {IPAddress.Parse(((IPEndPoint) endpoint).Address.ToString())} (ip: {ip})!"); // new syntax, since this is now nullable (gwyneth 20220207) } else { DebugUtilities.WriteError("No IP address received (?) or couldn't parse IP address for connection"); } } NetworkStream stream = client.GetStream(); DebugUtilities.WriteSpecial("Reading Stream"); string request = ""; byte[] buffer = new byte[512]; int i = 0; do { i = stream.Read(buffer, 0, buffer.Length); //add the next set of data into the buffer.. DebugUtilities.WriteSpecial("Read chunk from the stream"); request += Encoding.UTF8.GetString(buffer, 0, i); //append it to the overall request }while (stream.DataAvailable); //and repeat :) DebugUtilities .WriteInfo($"Got request, totalling {request.Length} characters"); string[] split = request .Split(new string[] { "\r\n\r\n" }, StringSplitOptions.RemoveEmptyEntries); string hostname = "unknown"; // if we can't resolve IP address to a hostname... try { DebugUtilities.WriteDebug("ip: " + ip ?? "nothing"); // may be an empty string (gwyneth 20220214) if (ip != null) { IPHostEntry host = Dns.GetHostEntry(IPAddress.Parse(ip)); hostname = host.HostName; } DebugUtilities.WriteDebug($"ENDPOINT HOSTNAME: {hostname}"); } catch { DebugUtilities .WriteWarning("Could not parse ip address to get the hostname (ipv6?) - hostname is set as 'unknown'"); } /// <value>get request headers into the appropriate class</value> RequestHeaders _request_headers = new RequestHeaders(split[0], hostname); string body = ""; /// <value> /// For some reason, the RESTbot HTTP server takes pleasure in waiting for 100-continue, /// so we set a flag here. /// </value> bool foundExpectContinue = false; foreach (HeaderLine line in _request_headers.HeaderLines) { if (line.ToString() == "Expect: 100-continue") { foundExpectContinue = true; DebugUtilities.WriteSpecial("Found 100 continue!"); } } if (foundExpectContinue) { try { ResponseHeaders continue_response = new ResponseHeaders(100, "Continue"); byte[] byte_continue_response = System.Text.Encoding.UTF8.GetBytes(continue_response.ToString()); //send the 100 continue message and then go back to the above. DebugUtilities.WriteSpecial("Writing 100 continue response"); stream.Write(byte_continue_response, 0, byte_continue_response.Length); DebugUtilities.WriteSpecial($"Finished writing - {byte_continue_response.Length} bytes total sent"); request = ""; buffer = new byte[512]; /// <value>stream chunk counter</value> i = 0; if (stream.DataAvailable) { DebugUtilities.WriteSpecial("DATA AVALIABLE!!"); } else { DebugUtilities.WriteWarning("NO DATA AVALIABLE? Hurr"); } do { i = stream.Read(buffer, 0, buffer.Length); //add the next set of data into the buffer.. DebugUtilities.WriteSpecial("Read continued chunk from the stream"); request += Encoding.UTF8.GetString(buffer, 0, i); //append it to the overall request }while (stream.DataAvailable); //and repeat :) DebugUtilities.WriteInfo($"Got continued request, totalling {request.Length} characters"); DebugUtilities.WriteDebug($"Here's what I got: {request}"); body = request; } catch (Exception e) { DebugUtilities.WriteError("An error occured while trying to talk to the client (100 expectation response): " + e.Message); } } else { if (split.Length > 1) { body = split[1]; } } /// <value>Status message to return to the client that made the request</value> /// <remarks>Likely XML-formatted (there are very few exceptions)</remarks> string to_return = Program.DoProcessing(_request_headers, body); // The next line is DELIBERATELY not using interpolated strings, because there might have // been some issues with those. (gwyneth 20220426) to_return = "<restbot>" + to_return + "</restbot>"; // commented out until I figure out how to write a DebugUtilities.WriteTrace() method. (gwyneth 20220426) // DebugUtilities.WriteDebug($"What I should return to the client: {to_return}"); ResponseHeaders response_headers = new ResponseHeaders(200, "OK"); string response = response_headers.ToString() + to_return; try { /// <value>stream of bytes to send over the HTTP stream as response</value> /// <remarks>TODO(gwyneth): This comes as XML, but we might wish to convert it first to JSON or /// something else. Note: this would require changing the headers, too</remarks> byte[] the_buffer = System.Text.Encoding.UTF8.GetBytes(response); response = ""; //unset for the hell of it stream.Write(the_buffer, 0, the_buffer.Length); } catch (Exception e) { DebugUtilities.WriteError("Could not write to the network stream, error was: " + e.Message); //see below } try { stream.Close(); client.Close(); } catch { DebugUtilities.WriteWarning("An error occured while closing the stream"); // ignore, sometimes the connection was closed by the client // if it's to be ignored, I've downgraded it to a warning. (gwyneth 20220426) } }
private void AcceptClientThread(IAsyncResult result) { TcpClient client = _listener.EndAcceptTcpClient(result); _proccessed_connection.Set(); DebugUtilities.WriteInfo("Processing Connection from " + client.Client.RemoteEndPoint.ToString() + "!"); NetworkStream stream = client.GetStream(); DebugUtilities.WriteSpecial("Reading Stream"); string request = ""; byte[] buffer = new byte[512]; int i = 0; do { i = stream.Read(buffer, 0, buffer.Length); //add the next set of data into the buffer.. DebugUtilities.WriteSpecial("Read chunk from the stream"); request += Encoding.UTF8.GetString(buffer, 0, i); //append it to the overall request } while (stream.DataAvailable); //and repeat :) DebugUtilities.WriteInfo("Got request, totalling " + request.Length + " characters"); string[] split = request.Split(new string[] { "\r\n\r\n" }, StringSplitOptions.RemoveEmptyEntries); string hostname = "unknown"; try { EndPoint endpoint = client.Client.RemoteEndPoint; DebugUtilities.WriteDebug("ip: " + endpoint.ToString()); string ip = (endpoint.ToString().Split(':'))[0]; IPHostEntry host = Dns.GetHostEntry(IPAddress.Parse(ip)); hostname = host.HostName; DebugUtilities.WriteDebug("ENDPOINT HOSTNAME: " + hostname); } catch { DebugUtilities.WriteWarning("Could not parse ip address to get the hostname (ipv6?) - hostname is set as 'unknown'"); } RequestHeaders x = new RequestHeaders(split[0], hostname); string body = ""; bool foundExpectContinue = false; foreach(HeaderLine line in x.HeaderLines) { if(line.ToString() == "Expect: 100-continue") { foundExpectContinue = true; DebugUtilities.WriteSpecial("Found 100 continue!"); } } if(foundExpectContinue) { try { ResponseHeaders continue_response = new ResponseHeaders(100, "Continue"); byte[] byte_continue_response = System.Text.Encoding.UTF8.GetBytes(continue_response.ToString()); //send the 100 continue message and then go back to the above. DebugUtilities.WriteSpecial("Writing 100 continue response"); stream.Write(byte_continue_response,0,byte_continue_response.Length); DebugUtilities.WriteSpecial("Finished writing - " + byte_continue_response.Length + " bytes total sent"); request = ""; buffer = new byte[512]; i = 0; if (stream.DataAvailable) DebugUtilities.WriteSpecial("DATA AVALIABLE!!"); else DebugUtilities.WriteWarning("NO DATA AVALIABLE? Hurr"); do { i = stream.Read(buffer, 0, buffer.Length); //add the next set of data into the buffer.. DebugUtilities.WriteSpecial("Read continued chunk from the stream"); request += Encoding.UTF8.GetString(buffer, 0, i); //append it to the overall request } while (stream.DataAvailable); //and repeat :) DebugUtilities.WriteInfo("Got continued request, totalling " + request.Length + " characters"); DebugUtilities.WriteDebug("Heres what I got: " + request); body = request; } catch { DebugUtilities.WriteError("An error occured while trying to talk to the client (100 expectation response)"); } } else if (split.Length > 1) body = split[1]; string to_return = Program.DoProcessing(x, body); to_return = "<restbot>" + to_return + "</restbot>"; DebugUtilities.WriteDebug("What I should return to the client: " + to_return); ResponseHeaders response_headers = new ResponseHeaders(200, "OK"); string response = response_headers.ToString() + to_return; try { byte[] the_buffer = System.Text.Encoding.UTF8.GetBytes(response); response = ""; //unset for the hell of it stream.Write(the_buffer, 0, the_buffer.Length); } catch { DebugUtilities.WriteError("Could not write to the network stream!"); //see below } try { stream.Close(); client.Close(); } catch { DebugUtilities.WriteError("An error occured while closing the stream"); //ignore, sometimes the connection was closed by the client } }
public static string DoProcessing(RequestHeaders headers, string body) { //Setup variables DebugUtilities.WriteDebug("New request - " + headers.RequestLine.Path); //Split the URL string[] parts = headers.RequestLine.Path.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); if (parts.Length < 1) { return ("<error>invalidmethod</error>"); } string Method = parts[0]; //Process the request params from POST, URL Dictionary<string, string> Parameters = RestBot.HandleDataFromRequest(headers, body); string debugparams = null; string debugparts = null; foreach (KeyValuePair<string, string> kvp in Parameters) { debugparams = debugparams + "[" + kvp.Key + "=" + kvp.Value + "] "; } foreach (string s in parts) { debugparts = debugparts + "[ " + s + " ]"; } DebugUtilities.WriteDebug("Parameters - " + debugparams); if (Method == "establish_session") { DebugUtilities.WriteDebug("We have an establish_session method."); //Alright, we're going to try to establish a session if (parts.Length >= 2 && parts[1] == Program.config.security.serverPass && Parameters.ContainsKey("first") && Parameters.ContainsKey("last") && Parameters.ContainsKey("pass")) { DebugUtilities.WriteDebug("Found required parameters for establish_session"); foreach (KeyValuePair<UUID, Session> ss in Sessions) { DebugUtilities.WriteSpecial("Avatar check: [" + ss.Value.Bot.First.ToLower() + "/" + ss.Value.Bot.Last.ToLower() + "] = [" + Parameters["first"].ToLower() + "/" + Parameters["last"].ToLower() + "]"); if (Parameters["first"].ToLower() == ss.Value.Bot.First.ToLower() && Parameters["last"].ToLower() == ss.Value.Bot.Last.ToLower() ) { DebugUtilities.WriteWarning("Already running avatar " + Parameters["first"] + " " + Parameters["last"]); return ("<existing_session>true</existing_session>\n<session_id>" + ss.Key.ToString() + "</session_id>"); } } UUID id = UUID.Random(); Session s = new Session(); s.ID = id; s.Hostname = headers.Hostname; s.LastAccessed = DateTime.Now; //Needs the $1$ for the md5 on the login for libsl if (!Parameters["pass"].StartsWith("$1$")) Parameters["pass"] = "******" + Parameters["pass"]; s.Bot = new RestBot(s.ID, Parameters["first"], Parameters["last"], Parameters["pass"]); lock (Sessions) { Sessions.Add(id, s); } RestBot.LoginReply reply = s.Bot.Login(); if (reply.wasFatal) { lock (Sessions) { if (Sessions.ContainsKey(id)) { Sessions.Remove(id); } } } return (reply.xmlReply); } else { String result = null; if (parts.Length < 2) { result = "Missing a part."; } if (!Parameters.ContainsKey("first")) { result = result + " Missing 'first' arg."; } if (!Parameters.ContainsKey("last")) { result = result + " Missing 'last' arg."; } if (!Parameters.ContainsKey("pass")) { result = result + " Missing 'last' arg."; } return ("<error>arguments: "+result+"</error>"); } } else if (Method == "server_quit") { if (parts[1] == Program.config.security.serverPass ) { foreach (KeyValuePair<UUID, Session> s in Sessions) { lock (Sessions) DisposeSession(s.Key); } StillRunning = false; return ("<status>success</status>\n"); } } //Only a method? pssh. if (parts.Length == 1) { return ("<error>nosession</error>"); } UUID sess = new UUID(); try { sess = new UUID(parts[1]); } catch (FormatException) { return ("<error>parsesessionkey</error>"); } catch (Exception e) { DebugUtilities.WriteError(e.Message); } //Session checking if (!ValidSession(sess, headers.Hostname)) { return ("<error>invalidsession</error>"); } //YEY PROCESSING RestBot r = Sessions[sess].Bot; //Last accessed for plugins Sessions[sess].LastAccessed = DateTime.Now; //Pre-error checking if (r.myStatus != RestBot.Status.Connected) //Still logging in? { return ("<error>" + r.myStatus.ToString() + "</error>"); } else if (!r.Client.Network.Connected) //Disconnected? { return ("<error>clientdisconnected</error>"); } else if (Method == "exit") { DisposeSession(sess); return ("<disposed>true</disposed>"); } else if (Method == "stats") { string response = "<bots>" + Sessions.Count.ToString() + "<bots>\n"; response += "<uptime>" + (DateTime.Now - uptime) + "</uptime>\n"; return (response); } return r.DoProcessing(Parameters, parts); }