| 
									
										
										
										
											2023-03-30 15:55:38 +02:00
										 |  |  | ----------------------------------------------------------------------------- | 
					
						
							|  |  |  | -- LuaSocket helper module | 
					
						
							|  |  |  | -- Author: Diego Nehab | 
					
						
							|  |  |  | -- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $ | 
					
						
							|  |  |  | ----------------------------------------------------------------------------- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ----------------------------------------------------------------------------- | 
					
						
							|  |  |  | -- Declare module and import dependencies | 
					
						
							|  |  |  | ----------------------------------------------------------------------------- | 
					
						
							|  |  |  | local base = _G | 
					
						
							|  |  |  | local string = require("string") | 
					
						
							|  |  |  | local math = require("math") | 
					
						
							| 
									
										
										
										
											2023-04-15 00:17:33 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | function get_lua_version() | 
					
						
							|  |  |  |     local major, minor = _VERSION:match("Lua (%d+)%.(%d+)") | 
					
						
							|  |  |  |     assert(tonumber(major) == 5) | 
					
						
							|  |  |  |     if tonumber(minor) >= 4 then | 
					
						
							|  |  |  |         return "5-4" | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |     return "5-1" | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function get_os() | 
					
						
							|  |  |  |     local the_os, ext, arch | 
					
						
							|  |  |  |     if package.config:sub(1,1) == "\\" then | 
					
						
							|  |  |  |         the_os, ext = "windows", "dll" | 
					
						
							|  |  |  |         arch = os.getenv"PROCESSOR_ARCHITECTURE" | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         -- TODO: macos? | 
					
						
							|  |  |  |         the_os, ext = "linux", "so" | 
					
						
							|  |  |  |         arch = "x86_64" -- TODO: read ELF header from /proc/$PID/exe to get arch | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if arch:find("64") ~= nil then | 
					
						
							|  |  |  |         arch = "x64" | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         arch = "x86" | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return the_os, ext, arch | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function get_socket_path() | 
					
						
							|  |  |  |     local the_os, ext, arch = get_os() | 
					
						
							|  |  |  |     -- for some reason ./ isn't working, so use a horrible hack to get the pwd | 
					
						
							|  |  |  |     local pwd = (io.popen and io.popen("cd"):read'*l') or "." | 
					
						
							|  |  |  | 	return pwd .. "/" .. arch .. "/socket-" .. the_os .. "-" .. get_lua_version() .. "." .. ext | 
					
						
							|  |  |  | end | 
					
						
							| 
									
										
										
										
											2023-07-09 06:17:24 -07:00
										 |  |  | local lua_version = get_lua_version() | 
					
						
							| 
									
										
										
										
											2023-04-15 00:17:33 -07:00
										 |  |  | local socket_path = get_socket_path() | 
					
						
							|  |  |  | local socket = assert(package.loadlib(socket_path, "luaopen_socket_core"))() | 
					
						
							| 
									
										
										
										
											2023-07-09 06:17:24 -07:00
										 |  |  | local event = event | 
					
						
							| 
									
										
										
										
											2023-04-15 00:17:33 -07:00
										 |  |  | -- http://lua-users.org/wiki/ModulesTutorial | 
					
						
							|  |  |  | local M = {} | 
					
						
							|  |  |  | if setfenv then | 
					
						
							|  |  |  | 	setfenv(1, M) -- for 5.1 | 
					
						
							|  |  |  | else | 
					
						
							|  |  |  | 	_ENV = M -- for 5.2 | 
					
						
							|  |  |  | end | 
					
						
							| 
									
										
										
										
											2023-03-30 15:55:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-03 14:35:14 -07:00
										 |  |  | M.socket = socket | 
					
						
							| 
									
										
										
										
											2023-07-09 06:17:24 -07:00
										 |  |  | -- Bizhawk <= 2.8 has an issue where resetting the lua doesn't close the socket | 
					
						
							|  |  |  | -- ...to get around this, we register an exit handler to close the socket first | 
					
						
							|  |  |  | if lua_version == '5-1' then | 
					
						
							|  |  |  |     local old_udp = socket.udp | 
					
						
							|  |  |  |     function udp(self) | 
					
						
							|  |  |  |         s = old_udp(self) | 
					
						
							|  |  |  |         function close_socket(self) | 
					
						
							|  |  |  |             s:close() | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         event.onexit(close_socket) | 
					
						
							|  |  |  |         return s | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |     socket.udp = udp | 
					
						
							|  |  |  | end | 
					
						
							| 
									
										
										
										
											2023-05-03 14:35:14 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-30 15:55:38 +02:00
										 |  |  | ----------------------------------------------------------------------------- | 
					
						
							|  |  |  | -- Exported auxiliar functions | 
					
						
							|  |  |  | ----------------------------------------------------------------------------- | 
					
						
							|  |  |  | function connect(address, port, laddress, lport) | 
					
						
							|  |  |  |     local sock, err = socket.tcp() | 
					
						
							|  |  |  |     if not sock then return nil, err end | 
					
						
							|  |  |  |     if laddress then | 
					
						
							|  |  |  |         local res, err = sock:bind(laddress, lport, -1) | 
					
						
							|  |  |  |         if not res then return nil, err end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |     local res, err = sock:connect(address, port) | 
					
						
							|  |  |  |     if not res then return nil, err end | 
					
						
							|  |  |  |     return sock | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function bind(host, port, backlog) | 
					
						
							|  |  |  |     local sock, err = socket.tcp() | 
					
						
							|  |  |  |     if not sock then return nil, err end | 
					
						
							|  |  |  |     sock:setoption("reuseaddr", true) | 
					
						
							|  |  |  |     local res, err = sock:bind(host, port) | 
					
						
							|  |  |  |     if not res then return nil, err end | 
					
						
							|  |  |  |     res, err = sock:listen(backlog) | 
					
						
							|  |  |  |     if not res then return nil, err end | 
					
						
							|  |  |  |     return sock | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-15 00:17:33 -07:00
										 |  |  | try = socket.newtry() | 
					
						
							| 
									
										
										
										
											2023-03-30 15:55:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | function choose(table) | 
					
						
							|  |  |  |     return function(name, opt1, opt2) | 
					
						
							|  |  |  |         if base.type(name) ~= "string" then | 
					
						
							|  |  |  |             name, opt1, opt2 = "default", name, opt1 | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         local f = table[name or "nil"] | 
					
						
							|  |  |  |         if not f then base.error("unknown key (".. base.tostring(name) ..")", 3) | 
					
						
							|  |  |  |         else return f(opt1, opt2) end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ----------------------------------------------------------------------------- | 
					
						
							|  |  |  | -- Socket sources and sinks, conforming to LTN12 | 
					
						
							|  |  |  | ----------------------------------------------------------------------------- | 
					
						
							|  |  |  | -- create namespaces inside LuaSocket namespace | 
					
						
							|  |  |  | sourcet = {} | 
					
						
							|  |  |  | sinkt = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | BLOCKSIZE = 2048 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | sinkt["close-when-done"] = function(sock) | 
					
						
							|  |  |  |     return base.setmetatable({ | 
					
						
							|  |  |  |         getfd = function() return sock:getfd() end, | 
					
						
							|  |  |  |         dirty = function() return sock:dirty() end | 
					
						
							|  |  |  |     }, { | 
					
						
							|  |  |  |         __call = function(self, chunk, err) | 
					
						
							|  |  |  |             if not chunk then | 
					
						
							|  |  |  |                 sock:close() | 
					
						
							|  |  |  |                 return 1 | 
					
						
							|  |  |  |             else return sock:send(chunk) end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | sinkt["keep-open"] = function(sock) | 
					
						
							|  |  |  |     return base.setmetatable({ | 
					
						
							|  |  |  |         getfd = function() return sock:getfd() end, | 
					
						
							|  |  |  |         dirty = function() return sock:dirty() end | 
					
						
							|  |  |  |     }, { | 
					
						
							|  |  |  |         __call = function(self, chunk, err) | 
					
						
							|  |  |  |             if chunk then return sock:send(chunk) | 
					
						
							|  |  |  |             else return 1 end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | sinkt["default"] = sinkt["keep-open"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | sink = choose(sinkt) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | sourcet["by-length"] = function(sock, length) | 
					
						
							|  |  |  |     return base.setmetatable({ | 
					
						
							|  |  |  |         getfd = function() return sock:getfd() end, | 
					
						
							|  |  |  |         dirty = function() return sock:dirty() end | 
					
						
							|  |  |  |     }, { | 
					
						
							|  |  |  |         __call = function() | 
					
						
							|  |  |  |             if length <= 0 then return nil end | 
					
						
							|  |  |  |             local size = math.min(socket.BLOCKSIZE, length) | 
					
						
							|  |  |  |             local chunk, err = sock:receive(size) | 
					
						
							|  |  |  |             if err then return nil, err end | 
					
						
							|  |  |  |             length = length - string.len(chunk) | 
					
						
							|  |  |  |             return chunk | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | sourcet["until-closed"] = function(sock) | 
					
						
							|  |  |  |     local done | 
					
						
							|  |  |  |     return base.setmetatable({ | 
					
						
							|  |  |  |         getfd = function() return sock:getfd() end, | 
					
						
							|  |  |  |         dirty = function() return sock:dirty() end | 
					
						
							|  |  |  |     }, { | 
					
						
							|  |  |  |         __call = function() | 
					
						
							|  |  |  |             if done then return nil end | 
					
						
							|  |  |  |             local chunk, err, partial = sock:receive(socket.BLOCKSIZE) | 
					
						
							|  |  |  |             if not err then return chunk | 
					
						
							|  |  |  |             elseif err == "closed" then | 
					
						
							|  |  |  |                 sock:close() | 
					
						
							|  |  |  |                 done = 1 | 
					
						
							|  |  |  |                 return partial | 
					
						
							|  |  |  |             else return nil, err end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | sourcet["default"] = sourcet["until-closed"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | source = choose(sourcet) | 
					
						
							| 
									
										
										
										
											2023-04-15 00:17:33 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | return M |