Created
October 18, 2018 15:07
-
-
Save CapsAdmin/bc45cc45c11cbfd1eff8c43c8eb5071b to your computer and use it in GitHub Desktop.
luajit ffi tcp udp unix, luasocket-like
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local ffi = require("ffi") | |
ffi.cdef[[ | |
typedef int SOCKET; | |
typedef unsigned int socklen_t; | |
typedef uint16_t u_short; | |
typedef uint32_t u_int; | |
typedef unsigned long u_long; | |
typedef unsigned char byte; | |
typedef unsigned long size_t; | |
ssize_t read (int , void *, size_t); | |
ssize_t write (int , const void *, size_t); | |
struct sockaddr { | |
unsigned short sa_family; | |
char sa_data[14]; | |
}; | |
struct in_addr { | |
uint32_t s_addr; | |
}; | |
struct sockaddr_in { | |
short sin_family; | |
u_short sin_port; | |
struct in_addr sin_addr; | |
char sin_zero[8]; | |
}; | |
struct addrinfo { | |
int ai_flags; | |
int ai_family; | |
int ai_socktype; | |
int ai_protocol; | |
socklen_t ai_addrlen; | |
struct sockaddr *ai_addr; | |
char *ai_canonname; | |
struct addrinfo *ai_next; | |
}; | |
typedef struct hostent { | |
char *h_name; | |
char **h_aliases; | |
short h_addrtype; | |
short h_length; | |
byte **h_addr_list; | |
}; | |
typedef struct timeval { | |
long int tv_sec; | |
long int tv_usec; | |
}; | |
typedef struct fd_set { | |
u_int fd_count; | |
SOCKET fd_array[64]; | |
} fd_set; | |
u_long htonl(u_long hostlong); | |
u_short htons(u_short hostshort); | |
u_short ntohs(u_short netshort); | |
u_long ntohl(u_long netlong); | |
unsigned long inet_addr(const char *cp); | |
char *inet_ntoa(struct in_addr in); | |
SOCKET socket(int af, int type, int protocol); | |
SOCKET accept(SOCKET s,struct sockaddr *addr,int *addrlen); | |
int bind(SOCKET s, const struct sockaddr *name, int namelen); | |
int close(SOCKET s); | |
int connect(SOCKET s, const struct sockaddr *name, int namelen); | |
int getsockname(SOCKET s, struct sockaddr *addr, int *namelen); | |
int getpeername(SOCKET s, struct sockaddr *addr, int *namelen); | |
int ioctl(SOCKET s, long cmd, u_long *argp); | |
int listen(SOCKET s, int backlog); | |
int recv(SOCKET s, char *buf, int len, int flags); | |
int recvfrom(SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen); | |
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout); | |
int send(SOCKET s, const char *buf, int len, int flags); | |
int sendto(SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen); | |
int shutdown(SOCKET s, int how); | |
struct hostent *gethostbyname(const char *name); | |
struct hostent *gethostbyaddr(const void *addr, int len, int type); | |
int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); | |
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); | |
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); | |
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags); | |
const char *gai_strerror(int errcode); | |
char * strerror (int errnum); | |
const char *hstrerror(int err); | |
extern int __h_errno; | |
]] | |
local SOMAXCONN = 128 | |
local INVALID_SOCKET = -1 | |
local INADDR_ANY = 0 | |
local INADDR_NONE = 0XFFFFFFFF | |
local SOL_SOCKET = 1 | |
local AF_INET = 2 | |
local SOCK_STREAM = 1 | |
local SOCK_DGRAM = 2 | |
local SOCKET_ERROR = -1 | |
local SO_REUSEADDR = 2 | |
local SD_RECEIVE = 0 | |
local SD_SEND = 1 | |
local SD_BOTH = 2 | |
local SO_RCVTIMEO = 20 | |
local SO_SNDTIMEO = 21 | |
local FIONBIO = 0x5421 | |
local function strerr() | |
return ffi.string(ffi.C.strerror(ffi.errno())) .. " ( " .. ffi.errno() .. " ) " | |
end | |
local function resolve_dns(address) | |
local results = ffi.new("struct addrinfo*[1]") | |
if ffi.C.getaddrinfo(address, nil, hints, ffi.cast("struct addrinfo **", results)) ~= 0 then | |
return nil, ffi.string(ffi.C.gai_strerror(ffi.errno())) | |
end | |
local host = ffi.new("char[256]") | |
if ffi.C.getnameinfo(results[0].ai_addr, results[0].ai_addrlen, host, ffi.sizeof(host), nil, 0, 1) < 0 then | |
return nil, strerr() | |
end | |
return ffi.C.inet_addr(ffi.string(host)) | |
end | |
local sockets = {} | |
do | |
local META = {} | |
META.__index = META | |
function META:Connect(address, port) | |
local server_address = ffi.new("struct sockaddr_in[1]", {{ | |
sin_family = AF_INET, | |
sin_addr = { | |
s_addr = resolve_dns(address), | |
}, | |
sin_port = ffi.C.htons(port), | |
}}) | |
local ret = ffi.C.connect(self.fd, ffi.cast("const struct sockaddr *", server_address), ffi.sizeof(server_address[0])) | |
if ret < 0 then | |
return nil, strerr() | |
end | |
self.address = server_address[0] | |
return true | |
end | |
function META:Bind(address, port) | |
local address_num | |
if address == "*" then | |
address_num = ffi.C.htonl(INADDR_ANY) | |
else | |
address_num = resolve_dns(address) | |
end | |
local server_address = ffi.new("struct sockaddr_in[1]", {{ | |
sin_family = AF_INET, | |
sin_addr = { | |
s_addr = address_num, | |
}, | |
sin_port = ffi.C.htons(port), | |
}}) | |
self.address = server_address[0] | |
if ffi.C.bind(self.fd, ffi.cast("const struct sockaddr *", server_address), ffi.sizeof(server_address[0])) < 0 then | |
return nil, strerr() | |
end | |
return true | |
end | |
function META:Read(max_length) | |
max_length = max_length or 1024 | |
local buffer = ffi.new("char[?]", max_length) | |
local length = ffi.C.read(self.fd, buffer, ffi.sizeof(buffer)) | |
if length ~= -1 then | |
return ffi.string(buffer, length) | |
end | |
end | |
function META:Write(str) | |
if ffi.C.write(self.fd, str, #str) < 0 then | |
return nil, strerr() | |
end | |
end | |
function META:Listen(backlog) | |
if ffi.C.listen(self.fd, backlog or 0) < 0 then | |
return nil, strerr() | |
end | |
return true | |
end | |
function META:Close() | |
if ffi.C.close(self.fd) < 0 then | |
return nil, strerr() | |
end | |
return true | |
end | |
function META:SetReuseAddress(b) | |
if ffi.C.setsockopt(self.fd, SOL_SOCKET, SO_REUSEADDR, ffi.cast("void *", ffi.new("int[1]", b and 1 or 0)), ffi.sizeof(ffi.typeof("int"))) < 0 then | |
return nil, strerr() | |
end | |
return true | |
end | |
function META:Accept() | |
local client_address = ffi.new("struct sockaddr_in[1]") | |
local fd = ffi.C.accept(self.fd, ffi.cast("struct sockaddr *", client_address), ffi.new("socklen_t[1]", ffi.sizeof("struct sockaddr"))) | |
if fd ~= -1 then | |
local self = {} | |
self.fd = fd | |
self.address = client_address[0] | |
setmetatable(self, META) | |
ffi.C.ioctl(self.fd, FIONBIO, ffi.new("uint64_t[1]", 1)) | |
return self | |
end | |
end | |
function META:GetIP() | |
if self.address then | |
local hostaddrp = ffi.C.inet_ntoa(self.address.sin_addr) | |
if hostaddrp ~= nil then | |
return ffi.string(hostaddrp) | |
end | |
end | |
return nil, "Bind or Connect not called from lua" | |
end | |
function META:GetHostName() | |
if self.address then | |
local hostp = ffi.C.gethostbyaddr(ffi.cast("const void *", self.address.sin_addr), ffi.sizeof("uint32_t"), AF_INET) | |
if hostp ~= nil then | |
return ffi.string(hostp.h_name) | |
end | |
local err = ffi.string(ffi.C.hstrerror(ffi.C.__h_errno)) | |
if err == "Unknown host" then | |
return err | |
end | |
return nil, err | |
end | |
return nil, "Bind or Connect not called from lua" | |
end | |
function META:GetPort() | |
if self.address then | |
local hostp = ffi.C.gethostbyaddr(ffi.cast("const void *", self.address.sin_addr), ffi.sizeof("uint32_t"), AF_INET) | |
if hostp ~= nil then | |
return ffi.string(hostp.h_name) | |
end | |
end | |
return nil, "Bind or Connect not called from lua" | |
end | |
function sockets.TCP() | |
local fd = ffi.C.socket(AF_INET, SOCK_STREAM, 0) | |
if fd < 0 then | |
return nil, strerr() | |
end | |
local self = {} | |
self.fd = fd | |
setmetatable(self, META) | |
ffi.C.ioctl(self.fd, FIONBIO, ffi.new("uint64_t[1]", 1)); | |
return self | |
end | |
end | |
do | |
local META = {} | |
META.__index = META | |
function META:Connect(address, port) | |
local server_address = ffi.new("struct sockaddr_in[1]", {{ | |
sin_family = AF_INET, | |
sin_addr = { | |
s_addr = resolve_dns(address), | |
}, | |
sin_port = ffi.C.htons(port), | |
}}) | |
local ret = ffi.C.connect(self.fd, ffi.cast("const struct sockaddr *", server_address), ffi.sizeof(server_address[0])) | |
if ret < 0 then | |
return nil, strerr() | |
end | |
self.address = server_address[0] | |
return true | |
end | |
function META:Bind(address, port) | |
local address_num | |
if address == "*" then | |
address_num = ffi.C.htonl(INADDR_ANY) | |
else | |
address_num = resolve_dns(address) | |
end | |
local server_address = ffi.new("struct sockaddr_in[1]", {{ | |
sin_family = AF_INET, | |
sin_addr = { | |
s_addr = address_num, | |
}, | |
sin_port = ffi.C.htons(port), | |
}}) | |
self.address = server_address[0] | |
if ffi.C.bind(self.fd, ffi.cast("const struct sockaddr *", server_address), ffi.sizeof(server_address[0])) < 0 then | |
return nil, strerr() | |
end | |
return true | |
end | |
function META:Read(max_length) | |
max_length = max_length or 1024 | |
local buffer = ffi.new("char[?]", max_length) | |
local from = ffi.new("struct sockaddr[1]") | |
local length = ffi.C.recvfrom(self.fd, buffer, ffi.sizeof(buffer), 0, from, ffi.new("int[1]", ffi.sizeof(from))) | |
if length < 0 then | |
return nil, strerr() | |
end | |
local sockaddr = ffi.cast("struct sockaddr_in *", from[0]) | |
local hostaddrp = ffi.C.inet_ntoa(sockaddr.sin_addr) | |
return ffi.string(buffer, length), ffi.string(hostaddrp), sockaddr.sin_port | |
end | |
function META:Write(str, address, port) | |
local client_address = ffi.new("struct sockaddr_in[1]", {{ | |
sin_family = AF_INET, | |
sin_addr = { | |
s_addr = resolve_dns(address), | |
}, | |
sin_port = ffi.C.htons(port), | |
}}) | |
local length = ffi.C.sendto(self.fd, str, #str, 0, ffi.cast("const struct sockaddr *", client_address), ffi.sizeof(client_address[0])) | |
if length < 0 then | |
return nil, strerr() | |
end | |
return true | |
end | |
function META:Close() | |
if ffi.C.close(self.fd) < 0 then | |
return nil, strerr() | |
end | |
return true | |
end | |
function META:SetReuseAddress(b) | |
if ffi.C.setsockopt(self.fd, SOL_SOCKET, SO_REUSEADDR, ffi.cast("void *", ffi.new("int[1]", b and 1 or 0)), ffi.sizeof(ffi.typeof("int"))) < 0 then | |
return nil, strerr() | |
end | |
return true | |
end | |
function META:GetIP() | |
if self.address then | |
local hostaddrp = ffi.C.inet_ntoa(self.address.sin_addr) | |
if hostaddrp ~= nil then | |
return ffi.string(hostaddrp) | |
end | |
end | |
return nil, "Bind or Connect not called from lua" | |
end | |
function META:GetHostName() | |
if self.address then | |
local hostp = ffi.C.gethostbyaddr(ffi.cast("const void *", self.address.sin_addr), ffi.sizeof("uint32_t"), AF_INET) | |
if hostp ~= nil then | |
return ffi.string(hostp.h_name) | |
end | |
local err = ffi.string(ffi.C.hstrerror(ffi.C.__h_errno)) | |
if err == "Unknown host" then | |
return err | |
end | |
return nil, err | |
end | |
return nil, "Bind or Connect not called from lua" | |
end | |
function META:GetPort() | |
if self.address then | |
local hostp = ffi.C.gethostbyaddr(ffi.cast("const void *", self.address.sin_addr), ffi.sizeof("uint32_t"), AF_INET) | |
if hostp ~= nil then | |
return ffi.string(hostp.h_name) | |
end | |
end | |
return nil, "Bind or Connect not called from lua" | |
end | |
function sockets.UDP() | |
local fd = ffi.C.socket(AF_INET, SOCK_DGRAM, 0) | |
if fd < 0 then | |
return nil, strerr() | |
end | |
local self = {} | |
self.fd = fd | |
setmetatable(self, META) | |
ffi.C.ioctl(self.fd, FIONBIO, ffi.new("uint64_t[1]", 1)); | |
return self | |
end | |
end | |
local function printf(fmt, ...) return io.write(fmt:format(...)) end | |
if false then -- tcp server | |
local server = assert(sockets.TCP()) | |
assert(server:SetReuseAddress(true)) | |
assert(server:Bind("*", 3000)) | |
assert(server:Listen()) | |
while true do | |
local client = server:Accept() | |
if client then | |
printf("server established connection with %s (%s)\n", assert(client:GetHostName()), assert(client:GetIP())) | |
local str = client:Read() | |
if str then | |
printf("server received %i bytes:%s\n", #str, str) | |
printf("sending buffer back to client\n") | |
client:Write(str) | |
client:Close() | |
break | |
end | |
end | |
end | |
server:Close() | |
end | |
if false then -- tcp client | |
local client = assert(sockets.TCP()) | |
while true do | |
if client:Connect("aliexpress.com", 80) then | |
print("connected to " .. assert(client:GetHostName()) .. " (" .. assert(client:GetIP()) .. ")") | |
break | |
end | |
end | |
client:Write("GET / HTTP/1.1\r\nhost: http://www.aliexpress.com/\r\nConnection: close\r\n\r\n") | |
while true do | |
local str = client:Read() | |
if str then | |
print(str) | |
break | |
end | |
end | |
end | |
do | |
local client = assert(sockets.UDP()) | |
local server = assert(sockets.UDP()) | |
assert(server:Bind("localhost", 3001)) | |
while true do | |
local str, ip, port = server:Read() | |
if str then | |
print(ip .. ":" .. port .. ": " .. str) | |
break | |
end | |
assert(client:Write("hello world", "localhost", 3001)) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment