[0.1.1-1] raw UDP support \:D/

This commit is contained in:
Ben Russell (300178622) 2013-07-21 14:46:42 +12:00
parent 5091a86bdc
commit f59063c97c
7 changed files with 245 additions and 2 deletions

View File

@ -582,6 +582,25 @@ remain = common.tcp_send(tcpsockfd, data) @
common.tcp_close(tcpsockfd) @
closes a TCP socket
udpsockfd = common.udp_open() @
creates a raw UDP socket
returns nil on socket creation failure
throws an error in other weird situations
data, host, port = common.udp_recvfrom(udpsockfd) @
receives from a UDP socket
returns an empty string + nils if nothing arrived
returns false on error, such as disconnection
remain = common.udp_sendto(udpsockfd, data, host, port) @
sends data to a host using a UDP socket
returns the data that wasn't quite sent just yet, which can be an empty string
returns false on error, such as disconnection
common.udp_close(udpsockfd) @
closes a UDP socket
wav = common.wav_load(fname) @
loads a sound with filename "fname"
remember to free it when you're done

View File

@ -19,7 +19,7 @@
#define VERSION_X 1
#define VERSION_Y 1
#define VERSION_A 0
#define VERSION_Z 0
#define VERSION_Z 1
// Remember to bump "Z" basically every time you change the engine!
// Remember to bump the version in Lua too!
// Remember to document API changes in a new version!

View File

@ -21,6 +21,21 @@ if common.version.num < 4227072 then
-- but I *do* want the server to stay up to date.
end
-- UDP port test.
if false then
local sk = common.udp_open()
print("sk:", sk)
m = common.udp_sendto(sk, "Hello World!", "127.0.0.1", 9999)
print("M:", m)
local ctime = common.time()
while common.time() <= ctime do end
local m2, h, p
m2, h, p = common.udp_recvfrom(sk)
print("recv:", m2, h, p)
common.udp_sendto(sk, "pang", h, p)
common.udp_close(sk)
end
dofile("pkg/base/preconf.lua")
dofile("pkg/base/lib_util.lua")

View File

@ -100,5 +100,7 @@ VERSION_BUGS = {
{intro=4194304+3, fix=nil, msg="[OpenGL] Frustum culling still screws up on occasion"},
{intro=nil, fix=4194304+8, msg="Occasional crash when music is stopped"},
{intro=4194304+1, fix=4194304+9, msg="Raw TCP still throws a lua error if it can't connect"},
{intro=nil, fix=4227072+1, msg="Arbitrary UDP connections not supported"},
{intro=4227072+1, fix=nil, msg="Raw UDP support might be a bit flaky - if you find bugs, please tell us!"},
}

View File

@ -87,8 +87,9 @@ int icelua_force_get_integer(lua_State *L, int table, char *name)
#include "lua_model.h"
#include "lua_net.h"
#include "lua_tcp.h"
#include "lua_wav.h"
#include "lua_udp.h"
#include "lua_util.h"
#include "lua_wav.h"
// common functions
@ -172,6 +173,10 @@ struct icelua_entry icelua_common[] = {
{icelua_fn_common_tcp_send, "tcp_send"},
{icelua_fn_common_tcp_recv, "tcp_recv"},
{icelua_fn_common_tcp_close, "tcp_close"},
{icelua_fn_common_udp_open, "udp_open"},
{icelua_fn_common_udp_sendto, "udp_sendto"},
{icelua_fn_common_udp_recvfrom, "udp_recvfrom"},
{icelua_fn_common_udp_close, "udp_close"},
{icelua_fn_common_wav_load, "wav_load"},
{icelua_fn_common_wav_free, "wav_free"},
{icelua_fn_common_mus_load_it, "mus_load_it"},

View File

@ -51,6 +51,7 @@ int icelua_fn_common_tcp_connect(lua_State *L) {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
ret = connect(sockfd, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
if(ret == -1)
return 0;
#ifdef WIN32

201
src/lua_udp.h Normal file
View File

@ -0,0 +1,201 @@
/*
This file is part of Iceball.
Iceball is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Iceball is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Iceball. If not, see <http://www.gnu.org/licenses/>.
*/
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
int whitelist_validate(const char *name, int port);
int icelua_fn_common_udp_open(lua_State *L) {
int top = icelua_assert_stack(L, 0, 0);
int ret, sockfd;
const char *host;
char port_ch[18];
int port = 0;
// TODO: support IPv6
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd == -1)
return 0;
#ifdef WIN32
int yes = 1;
if (ioctlsocket(sockfd, FIONBIO, (u_long *)&yes) == -1) {
#else
if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK) == -1) {
#endif
return luaL_error(L, "Could not set up a nonblocking connection!");
}
lua_pushnumber(L, sockfd);
return 1;
}
int icelua_fn_common_udp_recvfrom(lua_State *L) {
int top = icelua_assert_stack(L, 1, 1);
int sockfd;
int n = 0;
struct sockaddr_in saddr;
char buf[4096];
memset(buf, '\0', sizeof(buf));
if (lua_isnumber(L, 1)) {
sockfd = lua_tonumber(L, 1);
} else {
luaL_error(L, "not a number");
return 0;
}
socklen_t sadlen = sizeof(saddr);
n = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&saddr, &sadlen);
if (n == -1) {
#ifdef WIN32
int err = WSAGetLastError();
if (err != WSAEWOULDBLOCK) {
#else
int err = errno;
if (err != EAGAIN && err != EWOULDBLOCK) {
#endif
lua_pushboolean(L, 0);
return 1;
#ifdef WIN32
} else if (err == WSAEWOULDBLOCK) {
#else
} else if (err == EWOULDBLOCK || err == EAGAIN) {
#endif
lua_pushstring(L, "");
return 1;
}
}
lua_pushlstring(L, buf, n);
// TODO: support IPv6
char dst_buf[50];
const char *astr = inet_ntop(AF_INET, &(saddr.sin_addr.s_addr), dst_buf, sizeof(dst_buf)-1);
if(astr == NULL)
lua_pushnil(L);
else
lua_pushstring(L, astr);
lua_pushinteger(L, ntohs(saddr.sin_port));
return 3;
}
int icelua_fn_common_udp_sendto(lua_State *L) {
int top = icelua_assert_stack(L, 4, 4);
int sockfd;
struct addrinfo hints, *res;
char port_ch[18];
const char *data;
const char *host;
int port = 0;
int length;
int sent;
if (lua_isnumber(L, 1)) {
sockfd = lua_tonumber(L, 1);
} else {
luaL_error(L, "not a number");
return 0;
}
if (lua_isstring(L, 2)) {
data = lua_tostring(L, 2);
} else {
luaL_error(L, "not a string");
return 0;
}
if (lua_isstring(L, 3)) {
host = lua_tostring(L, 3);
} else {
return luaL_error(L, "not a string");
}
if (lua_isnumber(L, 4)) {
port = lua_tonumber(L, 4);
} else {
return luaL_error(L, "not a number");
}
if(L == lstate_client && !whitelist_validate(host, port))
return luaL_error(L, "address/port not on whitelist!");
// FIXME: the host lookup result should ideally be cached
// FIXME: make note of the address family used / socktype
// TODO: support IPv6
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
snprintf(port_ch, 17, "%u", port);
getaddrinfo(host, port_ch, &hints, &res);
length = lua_strlen(L, 2);
sent = sendto(sockfd, data, length, 0, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
if (sent <= 0) {
#ifdef WIN32
int err = WSAGetLastError();
if (err != WSAEWOULDBLOCK) {
#else
int err = errno;
if (err != EAGAIN && err != EWOULDBLOCK) {
#endif
lua_pushboolean(L, 0);
return 1;
#ifdef WIN32
} else if (err == WSAEWOULDBLOCK) {
#else
} else if (err == EWOULDBLOCK || err == EAGAIN) {
#endif
lua_pushlstring(L, data, length);
return 1;
}
}
if (sent < length)
lua_pushlstring(L, data + sent, length - sent);
else
lua_pushstring(L, "");
return 1;
}
int icelua_fn_common_udp_close(lua_State *L) {
int top = icelua_assert_stack(L, 1, 1);
int sockfd;
if (lua_isnumber(L, 1)) {
sockfd = lua_tonumber(L, 1);
} else {
luaL_error(L, "not a number");
return 0;
}
close(sockfd);
return 0;
}