luasocket/src/inet.c

353 lines
12 KiB
C
Raw Normal View History

2002-03-21 14:52:38 +01:00
/*=========================================================================*\
* Internet socket methods implementation
* Global Lua fuctions:
* toip(hostname)
* tohostname(dotted-quad)
* Socket table methods:
* getpeername()
* getsockname()
\*=========================================================================*/
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include "lsinet.h"
#include "lssock.h"
#include "lscompat.h"
/*=========================================================================*\
* Internal function prototypes.
\*=========================================================================*/
static int inet_lua_toip(lua_State *L);
static int inet_lua_tohostname(lua_State *L);
static int inet_lua_getpeername(lua_State *L);
static int inet_lua_getsockname(lua_State *L);
static void inet_pushresolved(lua_State *L, struct hostent *hp);
#ifdef COMPAT_INETATON
static int inet_aton(cchar *cp, struct in_addr *inp);
#endif
/*=========================================================================*\
* Exported functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
void inet_open(lua_State *L)
{
lua_pushcfunction(L, inet_lua_toip);
lua_setglobal(L, "toip");
lua_pushcfunction(L, inet_lua_tohostname);
lua_setglobal(L, "tohostname");
priv_newglobalmethod(L, "getsockname");
priv_newglobalmethod(L, "getpeername");
}
/*-------------------------------------------------------------------------*\
* Hook object methods to methods table.
\*-------------------------------------------------------------------------*/
void inet_inherit(lua_State *L, cchar *lsclass)
{
unsigned int i;
static struct luaL_reg funcs[] = {
{"getsockname", inet_lua_getsockname},
{"getpeername", inet_lua_getpeername},
};
sock_inherit(L, lsclass);
for (i = 0; i < sizeof(funcs)/sizeof(funcs[0]); i++) {
lua_pushcfunction(L, funcs[i].func);
priv_setmethod(L, lsclass, funcs[i].name);
}
}
void inet_construct(lua_State *L, p_inet inet)
{
sock_construct(L, (p_sock) inet);
}
/*=========================================================================*\
* Global Lua functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Returns the list of ip addresses associated with a host name
* Lua Input: address
* address: ip address or hostname to dns lookup
* Lua Returns
* On success: first IP address followed by a resolved table
* On error: nil, followed by an error message
\*-------------------------------------------------------------------------*/
static int inet_lua_toip(lua_State *L)
{
cchar *address = luaL_check_string(L, 1);
struct in_addr addr;
struct hostent *hp;
if (inet_aton(address, &addr))
hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
else hp = gethostbyname(address);
if (!hp) {
lua_pushnil(L);
lua_pushstring(L, compat_hoststrerror());
return 2;
}
addr = *((struct in_addr *) hp->h_addr);
lua_pushstring(L, inet_ntoa(addr));
inet_pushresolved(L, hp);
return 2;
}
/*-------------------------------------------------------------------------*\
* Returns the list of host names associated with an ip address
* Lua Input: address
* address: ip address or host name to reverse dns lookup
* Lua Returns
* On success: canonic name followed by a resolved table
* On error: nil, followed by an error message
\*-------------------------------------------------------------------------*/
static int inet_lua_tohostname(lua_State *L)
{
cchar *address = luaL_check_string(L, 1);
struct in_addr addr;
struct hostent *hp;
if (inet_aton(address, &addr))
hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
else hp = gethostbyname(address);
if (!hp) {
lua_pushnil(L);
lua_pushstring(L, compat_hoststrerror());
return 2;
}
lua_pushstring(L, hp->h_name);
inet_pushresolved(L, hp);
return 2;
}
/*=========================================================================*\
* Socket table methods
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Retrieves socket peer name
* Lua Input: sock
* sock: socket
* Lua Returns
* On success: ip address and port of peer
* On error: nil
\*-------------------------------------------------------------------------*/
static int inet_lua_getpeername(lua_State *L)
{
p_sock sock = (p_sock) lua_touserdata(L, 1);
struct sockaddr_in peer;
size_t peer_len = sizeof(peer);
if (getpeername(sock->fd, (SA *) &peer, &peer_len) < 0) {
lua_pushnil(L);
return 1;
}
lua_pushstring(L, inet_ntoa(peer.sin_addr));
lua_pushnumber(L, ntohs(peer.sin_port));
return 2;
}
/*-------------------------------------------------------------------------*\
* Retrieves socket local name
* Lua Input: sock
* sock: socket
* Lua Returns
* On success: local ip address and port
* On error: nil
\*-------------------------------------------------------------------------*/
static int inet_lua_getsockname(lua_State *L)
{
p_sock sock = (p_sock) lua_touserdata(L, 1);
struct sockaddr_in local;
size_t local_len = sizeof(local);
if (getsockname(sock->fd, (SA *) &local, &local_len) < 0) {
lua_pushnil(L);
return 1;
}
lua_pushstring(L, inet_ntoa(local.sin_addr));
lua_pushnumber(L, ntohs(local.sin_port));
return 2;
}
/*=========================================================================*\
* Internal functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Passes all resolver information to Lua as a table
* Input
* hp: hostent structure returned by resolver
\*-------------------------------------------------------------------------*/
static void inet_pushresolved(lua_State *L, struct hostent *hp)
{
char **alias;
struct in_addr **addr;
int i, resolved;
lua_newtable(L); resolved = lua_gettop(L);
lua_pushstring(L, "name");
lua_pushstring(L, hp->h_name);
lua_settable(L, resolved);
lua_pushstring(L, "ip");
lua_pushstring(L, "alias");
i = 1;
alias = hp->h_aliases;
lua_newtable(L);
while (*alias) {
lua_pushnumber(L, i);
lua_pushstring(L, *alias);
lua_settable(L, -3);
i++; alias++;
}
lua_settable(L, resolved);
i = 1;
lua_newtable(L);
addr = (struct in_addr **) hp->h_addr_list;
while (*addr) {
lua_pushnumber(L, i);
lua_pushstring(L, inet_ntoa(**addr));
lua_settable(L, -3);
i++; addr++;
}
lua_settable(L, resolved);
}
/*-------------------------------------------------------------------------*\
* Tries to create a TCP socket and connect to remote address (address, port)
* Input
* client: socket structure to be used
* address: host name or ip address
* port: port number to bind to
* Returns
* NULL in case of success, error message otherwise
\*-------------------------------------------------------------------------*/
cchar *inet_tryconnect(p_inet inet, cchar *address, ushort port,
int type)
{
struct sockaddr_in remote;
cchar *err = NULL;
memset(&remote, 0, sizeof(remote));
if (strlen(address) && inet_aton(address, &remote.sin_addr)) {
remote.sin_family = AF_INET;
remote.sin_port = htons(port);
err = inet_trysocket(inet, type);
if (err) return err;
if (compat_connect(inet->fd, (SA *) &remote, sizeof(remote)) < 0) {
compat_close(inet->fd);
inet->fd = COMPAT_INVALIDFD;
return compat_connectstrerror();
} else return NULL;
/* go ahead and try by hostname resolution */
} else {
struct hostent *hp = gethostbyname(address);
struct in_addr **addr;
if (!hp) return compat_hoststrerror();
addr = (struct in_addr **) hp->h_addr_list;
for (; *addr != NULL; addr++) {
memcpy(&remote.sin_addr, *addr, sizeof(struct in_addr));
remote.sin_family = AF_INET;
remote.sin_port = htons(port);
err = inet_trysocket(inet, type);
if (err) return err;
if (compat_connect(inet->fd, (SA *)&remote, sizeof(remote)) < 0) {
compat_close(inet->fd);
inet->fd = COMPAT_INVALIDFD;
} else return NULL;
memset(&remote, 0, sizeof(remote));
}
return compat_connectstrerror();
}
}
/*-------------------------------------------------------------------------*\
* Tries to create a TCP socket and bind it to (address, port)
* Input
* address: host name or ip address
* port: port number to bind to
* backlog: backlog to set
* Returns
* NULL in case of success, error message otherwise
\*-------------------------------------------------------------------------*/
cchar *inet_trybind(p_inet inet, cchar *address, ushort port,
int type)
{
struct sockaddr_in local;
cchar *err = NULL;
memset(&local, 0, sizeof(local));
/* address is either wildcard or a valid ip address */
local.sin_addr.s_addr = htonl(INADDR_ANY);
if (!strcmp(address, "*") ||
(strlen(address) && inet_aton(address, &local.sin_addr))) {
local.sin_port = htons(port);
local.sin_family = AF_INET;
err = inet_trysocket(inet, type);
if (err) return err;
compat_setreuseaddr(inet->fd);
if (compat_bind(inet->fd, (SA *) &local, sizeof(local)) < 0) {
compat_close(inet->fd);
inet->fd = COMPAT_INVALIDFD;
return compat_bindstrerror();
} else return NULL;
/* otherwise, proceed with domain name resolution */
} else {
struct hostent *hp = gethostbyname(address);
struct in_addr **addr;
if (!hp) return compat_hoststrerror();
addr = (struct in_addr **) hp->h_addr_list;
for (; *addr != NULL; addr++) {
memcpy(&local.sin_addr, *addr, sizeof(struct in_addr));
local.sin_port = htons(port);
local.sin_family = AF_INET;
err = inet_trysocket(inet, type);
if (err) return err;
compat_setreuseaddr(inet->fd);
if (compat_bind(inet->fd, (SA *) &local, sizeof(local)) < 0) {
compat_close(inet->fd);
inet->fd = COMPAT_INVALIDFD;
} else return NULL;
memset(&local, 0, sizeof(local));
}
return compat_bindstrerror();
}
}
/*-------------------------------------------------------------------------*\
* Tries to create a new inet socket
* Input
* udp: udp structure
* Returns
* NULL if successfull, error message on error
\*-------------------------------------------------------------------------*/
cchar *inet_trysocket(p_inet inet, int type)
{
if (inet->fd != COMPAT_INVALIDFD) compat_close(inet->fd);
inet->fd = compat_socket(AF_INET, type, 0);
if (inet->fd == COMPAT_INVALIDFD) return compat_socketstrerror();
else return NULL;
}
/*-------------------------------------------------------------------------*\
* Some systems do not provide this so that we provide our own. It's not
* marvelously fast, but it works just fine.
\*-------------------------------------------------------------------------*/
#ifdef COMPAT_INETATON
static int inet_aton(const char *cp, struct in_addr *inp)
{
unsigned int a = 0, b = 0, c = 0, d = 0;
int n = 0, r;
unsigned long int addr = 0;
r = sscanf(cp, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n);
if (r == 0 || n == 0) return 0;
cp += n;
if (*cp) return 0;
if (a > 255 || b > 255 || c > 255 || d > 255) return 0;
if (inp) {
addr += a; addr <<= 8;
addr += b; addr <<= 8;
addr += c; addr <<= 8;
addr += d;
inp->s_addr = htonl(addr);
}
return 1;
}
#endif