summaryrefslogblamecommitdiffstats
path: root/MCServer/Plugins/NetworkTest/NetworkTest.lua
blob: 21f89c7f96a2a00d4d68f00f2b18646544d406c2 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                              












                                                                       
                                                           





                                                                             
                                                                                 




























                                                                                                                                                                                                                      
                                                                                                                                               


                                   
                                                                                 















                                                                                                                                                                                                                         
                                                                                                











                                                                                                                                                           
                                              






                             

                                                                                   
                                                                             

                                      

           



                                                                       


                                     
















































                                                                                                                                                                                                        
 



















                                                                                             








































                                                                                                                                 



























                                                                                        











































                                                                                                                

-- NetworkTest.lua

-- Implements a few tests for the cNetwork API





--- Map of all servers currently open
-- g_Servers[PortNum] = cServerHandle
local g_Servers = {}

--- List of fortune messages for the fortune server
-- A random message is chosen for each incoming connection
-- The contents are loaded from the splashes.txt file on plugin startup
local g_Fortunes =
{
	"Empty splashes.txt",
}

--- Map of all services that can be run as servers
-- g_Services[ServiceName] = function() -> accept-callbacks
local g_Services =
{
	-- Echo service: each connection echoes back what has been sent to it
	echo = function (a_Port)
		return
		{
			-- A new connection has come, give it new link-callbacks:
			OnIncomingConnection = function (a_RemoteIP, a_RemotePort)
				return
				{
					OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
						LOG("EchoServer(" .. a_Port .. ": Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
					end,
					
					OnReceivedData = function (a_Link, a_Data)
						-- Echo the received data back to the link:
						a_Link:Send(a_Data)
					end,
					
					OnRemoteClosed = function (a_Link)
					end
				}  -- Link callbacks
			end,  -- OnIncomingConnection()
			
			-- Send a welcome message to newly accepted connections:
			OnAccepted = function (a_Link)
				a_Link:Send("Hello, " .. a_Link:GetRemoteIP() .. ", welcome to the echo server @ MCServer-Lua\r\n")
			end,  -- OnAccepted()
			
			-- There was an error listening on the port:
			OnError = function (a_ErrorCode, a_ErrorMsg)
				LOGINFO("EchoServer(" .. a_Port .. ": Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
			end,  -- OnError()
		}  -- Listen callbacks
	end,  -- echo
	
	-- Fortune service: each incoming connection gets a welcome message plus a random fortune text; all communication is ignored afterwards
	fortune = function (a_Port)
		return
		{
			-- A new connection has come, give it new link-callbacks:
			OnIncomingConnection = function (a_RemoteIP, a_RemotePort)
				return
				{
					OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
						LOG("FortuneServer(" .. a_Port .. ": Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
					end,
					
					OnReceivedData = function (a_Link, a_Data)
						-- Ignore any received data
					end,
					
					OnRemoteClosed = function (a_Link)
					end
				}  -- Link callbacks
			end,  -- OnIncomingConnection()
			
			-- Send a welcome message and the fortune to newly accepted connections:
			OnAccepted = function (a_Link)
				a_Link:Send("Hello, " .. a_Link:GetRemoteIP() .. ", welcome to the fortune server @ MCServer-Lua\r\n\r\nYour fortune:\r\n")
				a_Link:Send(g_Fortunes[math.random(#g_Fortunes)] .. "\r\n")
			end,  -- OnAccepted()
			
			-- There was an error listening on the port:
			OnError = function (a_ErrorCode, a_ErrorMsg)
				LOGINFO("FortuneServer(" .. a_Port .. ": Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
			end,  -- OnError()
		}  -- Listen callbacks
	end,  -- fortune
	
	-- TODO: Other services (daytime, ...)
}





function Initialize(a_Plugin)
	-- Load the splashes.txt file into g_Fortunes, overwriting current content:
	local idx = 1
	for line in io.lines(a_Plugin:GetLocalFolder() .. "/splashes.txt") do
		g_Fortunes[idx] = line
		idx = idx + 1
	end
	
	-- Use the InfoReg shared library to process the Info.lua file:
	dofile(cPluginManager:GetPluginsPath() .. "/InfoReg.lua")
	RegisterPluginInfoCommands()
	RegisterPluginInfoConsoleCommands()

	-- Seed the random generator:
	math.randomseed(os.time())
	
	return true
end





function HandleConsoleNetClient(a_Split)
	-- Get the address to connect to:
	local Host = a_Split[3] or "google.com"
	local Port = a_Split[4] or 80
	
	-- Create the callbacks "personalised" for the address:
	local Callbacks =
	{
		OnConnected = function (a_Link)
			LOG("Connected to " .. Host .. ":" .. Port .. ".")
			LOG("Connection stats: Remote address: " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. ", Local address: " .. a_Link:GetLocalIP() .. ":" .. a_Link:GetLocalPort())
			LOG("Sending HTTP request for front page.")
			a_Link:Send("GET / HTTP/1.0\r\nHost: " .. Host .. "\r\n\r\n")
		end,
		
		OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
			LOG("Connection to " .. Host .. ":" .. Port .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
		end,
		
		OnReceivedData = function (a_Link, a_Data)
			LOG("Received data from " .. Host .. ":" .. Port .. ":\r\n" .. a_Data)
		end,
		
		OnRemoteClosed = function (a_Link)
			LOG("Connection to " .. Host .. ":" .. Port .. " was closed by the remote peer.")
		end
	}
	
	-- Queue a connect request:
	local res = cNetwork:Connect(Host, Port, Callbacks)
	if not(res) then
		LOGWARNING("cNetwork:Connect call failed immediately")
		return true
	end
	
	return true, "Client connection request queued."
end





function HandleConsoleNetClose(a_Split)
	-- Get the port to close:
	local Port = tonumber(a_Split[3] or 1024)
	if not(Port) then
		return true, "Bad port number: \"" .. Port .. "\"."
	end

	-- Close the server, if there is one:
	if not(g_Servers[Port]) then
		return true, "There is no server currently listening on port " .. Port .. "."
	end
	g_Servers[Port]:Close()
	g_Servers[Port] = nil
	return true, "Port " .. Port .. " closed."
end





function HandleConsoleNetLookup(a_Split)
	-- Get the name to look up:
	local Addr = a_Split[3] or "google.com"
	
	-- Create the callbacks "personalised" for the host:
	local Callbacks =
	{
		OnNameResolved = function (a_Hostname, a_IP)
			LOG(a_Hostname .. " resolves to " .. a_IP)
		end,
		
		OnError = function (a_Query, a_ErrorCode, a_ErrorMsg)
			LOG("Failed to retrieve information for " .. a_Query .. ": " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
			assert(a_Query == Addr)
		end,
		
		OnFinished = function (a_Query)
			LOG("Resolving " .. a_Query .. " has finished.")
			assert(a_Query == Addr)
		end,
	}
	
	-- Queue both name and IP DNS queries;
	-- we don't distinguish between an IP and a hostname in this command so we don't know which one to use:
	local res = cNetwork:HostnameToIP(Addr, Callbacks)
	if not(res) then
		LOGWARNING("cNetwork:HostnameToIP call failed immediately")
		return true
	end
	res = cNetwork:IPToHostname(Addr, Callbacks)
	if not(res) then
		LOGWARNING("cNetwork:IPToHostname call failed immediately")
		return true
	end
	
	return true, "DNS query has been queued."
end





function HandleConsoleNetListen(a_Split)
	-- Get the params:
	local Port = tonumber(a_Split[3] or 1024)
	if not(Port) then
		return true, "Invalid port: \"" .. Port .. "\"."
	end
	local Service = string.lower(a_Split[4] or "echo")
	
	-- Create the callbacks specific for the service:
	if (g_Services[Service] == nil) then
		return true, "No such service: " .. Service
	end
	local Callbacks = g_Services[Service](Port)
	
	-- Start the server:
	local srv = cNetwork:Listen(Port, Callbacks)
	if not(srv:IsListening()) then
		-- The error message has already been printed in the Callbacks.OnError()
		return true
	end
	g_Servers[Port] = srv
	return true, Service .. " server started on port " .. Port
end





function HandleConsoleNetWasc(a_Split)
	local Callbacks =
	{
		OnConnected = function (a_Link)
			LOG("Connected to webadmin, starting TLS...")
			local res, msg = a_Link:StartTLSClient("", "", "")
			if not(res) then
				LOG("Failed to start TLS client: " .. msg)
				return
			end
			-- We need to send a keep-alive due to #1737
			a_Link:Send("GET / HTTP/1.0\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n")
		end,
		
		OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
			LOG("Connection to webadmin failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
		end,
		
		OnReceivedData = function (a_Link, a_Data)
			LOG("Received data from webadmin:\r\n" .. a_Data)
			
			-- Close the link once all the data is received:
			if (a_Data == "0\r\n\r\n") then  -- Poor man's end-of-data detection; works on localhost
				-- TODO: The Close() method is not yet exported to Lua
				-- a_Link:Close()
			end
		end,
		
		OnRemoteClosed = function (a_Link)
			LOG("Connection to webadmin was closed")
		end,
	}
	
	if not(cNetwork:Connect("localhost", "8080", Callbacks)) then
		LOG("Canot connect to webadmin")
	end
	
	return true
end