luasocket/src/inet.c

577 lines
19 KiB
C
Raw Normal View History

2002-03-21 14:52:38 +01:00
/*=========================================================================*\
2003-05-25 03:54:13 +02:00
* Internet domain functions
* LuaSocket toolkit
2002-03-21 14:52:38 +01:00
\*=========================================================================*/
#include <stdio.h>
2002-03-21 14:52:38 +01:00
#include <string.h>
2005-09-29 08:11:42 +02:00
#include "lua.h"
#include "lauxlib.h"
2002-03-21 14:52:38 +01:00
2003-05-25 03:54:13 +02:00
#include "inet.h"
2002-03-21 14:52:38 +01:00
/*=========================================================================*\
* Internal function prototypes.
\*=========================================================================*/
2003-05-25 03:54:13 +02:00
static int inet_global_toip(lua_State *L);
static int inet_global_getaddrinfo(lua_State *L);
2003-05-25 03:54:13 +02:00
static int inet_global_tohostname(lua_State *L);
static int inet_global_getnameinfo(lua_State *L);
2002-03-21 14:52:38 +01:00
static void inet_pushresolved(lua_State *L, struct hostent *hp);
static int inet_global_gethostname(lua_State *L);
2002-03-21 14:52:38 +01:00
/* DNS functions */
static luaL_Reg func[] = {
2012-04-11 22:21:25 +02:00
{ "toip", inet_global_toip},
{ "getaddrinfo", inet_global_getaddrinfo},
2012-04-11 22:21:25 +02:00
{ "tohostname", inet_global_tohostname},
{ "getnameinfo", inet_global_getnameinfo},
{ "gethostname", inet_global_gethostname},
2003-05-25 03:54:13 +02:00
{ NULL, NULL}
};
2002-03-21 14:52:38 +01:00
/*=========================================================================*\
* Exported functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int inet_open(lua_State *L)
2002-03-21 14:52:38 +01:00
{
lua_pushstring(L, "dns");
lua_newtable(L);
luaL_openlib(L, NULL, func, 0);
lua_settable(L, -3);
2004-03-26 01:18:41 +01:00
return 0;
2002-03-21 14:52:38 +01:00
}
/*=========================================================================*\
* Global Lua functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
2002-07-03 21:06:55 +02:00
* Returns all information provided by the resolver given a host name
* or ip address
2002-03-21 14:52:38 +01:00
\*-------------------------------------------------------------------------*/
2004-07-15 08:11:53 +02:00
static int inet_gethost(const char *address, struct hostent **hp) {
2002-03-21 14:52:38 +01:00
struct in_addr addr;
if (inet_aton(address, &addr))
2005-10-07 06:40:59 +02:00
return socket_gethostbyaddr((char *) &addr, sizeof(addr), hp);
else
2005-10-07 06:40:59 +02:00
return socket_gethostbyname(address, hp);
2004-07-15 08:11:53 +02:00
}
/*-------------------------------------------------------------------------*\
* Returns all information provided by the resolver given a host name
* or ip address
\*-------------------------------------------------------------------------*/
static int inet_global_tohostname(lua_State *L) {
const char *address = luaL_checkstring(L, 1);
struct hostent *hp = NULL;
2004-07-15 08:11:53 +02:00
int err = inet_gethost(address, &hp);
if (err != IO_DONE) {
2002-03-21 14:52:38 +01:00
lua_pushnil(L);
2005-10-07 06:40:59 +02:00
lua_pushstring(L, socket_hoststrerror(err));
2002-03-21 14:52:38 +01:00
return 2;
}
2004-07-15 08:11:53 +02:00
lua_pushstring(L, hp->h_name);
2002-03-21 14:52:38 +01:00
inet_pushresolved(L, hp);
return 2;
}
static int inet_global_getnameinfo(lua_State *L) {
2013-05-27 15:05:48 +02:00
char hbuf[NI_MAXHOST];
char sbuf[NI_MAXSERV];
int i, ret;
struct addrinfo hints;
struct addrinfo *resolved, *iter;
2013-05-27 15:05:48 +02:00
const char *host = luaL_optstring(L, 1, NULL);
const char *serv = luaL_optstring(L, 2, NULL);
2013-05-27 15:05:48 +02:00
if (!(host || serv))
luaL_error(L, "host and serv cannot be both nil");
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = PF_UNSPEC;
2013-05-27 15:05:48 +02:00
ret = getaddrinfo(host, serv, &hints, &resolved);
if (ret != 0) {
lua_pushnil(L);
lua_pushstring(L, socket_gaistrerror(ret));
return 2;
}
lua_newtable(L);
for (i = 1, iter = resolved; iter; i++, iter = iter->ai_next) {
2013-05-27 15:05:48 +02:00
getnameinfo(iter->ai_addr, (socklen_t) iter->ai_addrlen,
hbuf, host? (socklen_t) sizeof(hbuf): 0,
sbuf, serv? (socklen_t) sizeof(sbuf): 0, 0);
if (host) {
lua_pushnumber(L, i);
2013-05-27 15:05:48 +02:00
lua_pushstring(L, hbuf);
lua_settable(L, -3);
}
}
freeaddrinfo(resolved);
2013-05-27 15:05:48 +02:00
if (serv) {
lua_pushstring(L, sbuf);
return 2;
} else {
return 1;
}
}
2002-03-21 14:52:38 +01:00
/*-------------------------------------------------------------------------*\
2002-07-03 21:06:55 +02:00
* Returns all information provided by the resolver given a host name
* or ip address
2002-03-21 14:52:38 +01:00
\*-------------------------------------------------------------------------*/
2004-07-15 08:11:53 +02:00
static int inet_global_toip(lua_State *L)
2002-03-21 14:52:38 +01:00
{
2003-05-25 03:54:13 +02:00
const char *address = luaL_checkstring(L, 1);
struct hostent *hp = NULL;
2004-07-15 08:11:53 +02:00
int err = inet_gethost(address, &hp);
if (err != IO_DONE) {
2002-03-21 14:52:38 +01:00
lua_pushnil(L);
2005-10-07 06:40:59 +02:00
lua_pushstring(L, socket_hoststrerror(err));
2002-03-21 14:52:38 +01:00
return 2;
}
2004-07-15 08:11:53 +02:00
lua_pushstring(L, inet_ntoa(*((struct in_addr *) hp->h_addr)));
2002-03-21 14:52:38 +01:00
inet_pushresolved(L, hp);
return 2;
}
int inet_optfamily(lua_State* L, int narg, const char* def)
{
static const char* optname[] = { "unspec", "inet", "inet6", NULL };
static int optvalue[] = { PF_UNSPEC, PF_INET, PF_INET6, 0 };
return optvalue[luaL_checkoption(L, narg, def, optname)];
}
int inet_optsocktype(lua_State* L, int narg, const char* def)
{
static const char* optname[] = { "stream", "dgram", NULL };
static int optvalue[] = { SOCK_STREAM, SOCK_DGRAM, 0 };
return optvalue[luaL_checkoption(L, narg, def, optname)];
}
static int inet_global_getaddrinfo(lua_State *L)
2012-04-11 22:21:25 +02:00
{
const char *hostname = luaL_checkstring(L, 1);
struct addrinfo *iterator = NULL, *resolved = NULL;
struct addrinfo hints;
int i = 1, ret = 0;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = PF_UNSPEC;
ret = getaddrinfo(hostname, NULL, &hints, &resolved);
if (ret != 0) {
lua_pushnil(L);
lua_pushstring(L, socket_gaistrerror(ret));
2012-04-11 22:21:25 +02:00
return 2;
}
lua_newtable(L);
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
2013-05-27 15:05:48 +02:00
char hbuf[NI_MAXHOST];
ret = getnameinfo(iterator->ai_addr, (socklen_t) iterator->ai_addrlen,
hbuf, (socklen_t) sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
if (ret){
lua_pushnil(L);
lua_pushstring(L, socket_gaistrerror(ret));
return 2;
}
2012-04-11 22:21:25 +02:00
lua_pushnumber(L, i);
lua_newtable(L);
switch (iterator->ai_family) {
case AF_INET:
lua_pushliteral(L, "family");
lua_pushliteral(L, "inet");
lua_settable(L, -3);
break;
case AF_INET6:
lua_pushliteral(L, "family");
lua_pushliteral(L, "inet6");
lua_settable(L, -3);
break;;
}
lua_pushliteral(L, "addr");
lua_pushstring(L, hbuf);
lua_settable(L, -3);
lua_settable(L, -3);
i++;
}
2012-04-11 22:21:25 +02:00
freeaddrinfo(resolved);
return 1;
}
/*-------------------------------------------------------------------------*\
* Gets the host name
\*-------------------------------------------------------------------------*/
static int inet_global_gethostname(lua_State *L)
{
char name[257];
name[256] = '\0';
if (gethostname(name, 256) < 0) {
lua_pushnil(L);
lua_pushstring(L, socket_strerror(errno));
return 2;
} else {
lua_pushstring(L, name);
return 1;
}
}
2002-03-21 14:52:38 +01:00
/*=========================================================================*\
2002-07-03 21:06:55 +02:00
* Lua methods
2002-03-21 14:52:38 +01:00
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Retrieves socket peer name
\*-------------------------------------------------------------------------*/
int inet_meth_getpeername(lua_State *L, p_socket ps, int family)
2002-03-21 14:52:38 +01:00
{
switch (family) {
case PF_INET: {
struct sockaddr_in peer;
socklen_t peer_len = sizeof(peer);
char name[INET_ADDRSTRLEN];
if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) {
lua_pushnil(L);
lua_pushstring(L, socket_strerror(errno));
return 2;
} else {
inet_ntop(family, &peer.sin_addr, name, sizeof(name));
lua_pushstring(L, name);
lua_pushnumber(L, ntohs(peer.sin_port));
lua_pushliteral(L, "inet");
return 3;
}
}
case PF_INET6: {
struct sockaddr_in6 peer;
socklen_t peer_len = sizeof(peer);
char name[INET6_ADDRSTRLEN];
if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) {
lua_pushnil(L);
lua_pushstring(L, socket_strerror(errno));
return 2;
} else {
inet_ntop(family, &peer.sin6_addr, name, sizeof(name));
lua_pushstring(L, name);
lua_pushnumber(L, ntohs(peer.sin6_port));
lua_pushliteral(L, "inet6");
return 3;
}
}
default:
lua_pushnil(L);
lua_pushfstring(L, "unknown family %d", family);
return 2;
2002-03-21 14:52:38 +01:00
}
}
/*-------------------------------------------------------------------------*\
* Retrieves socket local name
\*-------------------------------------------------------------------------*/
int inet_meth_getsockname(lua_State *L, p_socket ps, int family)
2002-03-21 14:52:38 +01:00
{
switch (family) {
case PF_INET: {
struct sockaddr_in local;
socklen_t local_len = sizeof(local);
char name[INET_ADDRSTRLEN];
if (getsockname(*ps, (SA *) &local, &local_len) < 0) {
lua_pushnil(L);
lua_pushstring(L, socket_strerror(errno));
return 2;
} else {
inet_ntop(family, &local.sin_addr, name, sizeof(name));
lua_pushstring(L, name);
lua_pushnumber(L, ntohs(local.sin_port));
lua_pushliteral(L, "inet");
return 3;
}
}
case PF_INET6: {
struct sockaddr_in6 local;
socklen_t local_len = sizeof(local);
char name[INET6_ADDRSTRLEN];
if (getsockname(*ps, (SA *) &local, &local_len) < 0) {
lua_pushnil(L);
lua_pushstring(L, socket_strerror(errno));
return 2;
} else {
inet_ntop(family, &local.sin6_addr, name, sizeof(name));
lua_pushstring(L, name);
lua_pushnumber(L, ntohs(local.sin6_port));
lua_pushliteral(L, "inet6");
return 3;
}
}
default:
lua_pushnil(L);
lua_pushfstring(L, "unknown family %d", family);
return 2;
2002-03-21 14:52:38 +01:00
}
}
/*=========================================================================*\
* Internal functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Passes all resolver information to Lua as a table
\*-------------------------------------------------------------------------*/
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);
2005-06-14 06:29:23 +02:00
if (alias) {
while (*alias) {
lua_pushnumber(L, i);
lua_pushstring(L, *alias);
lua_settable(L, -3);
i++; alias++;
}
2002-03-21 14:52:38 +01:00
}
lua_settable(L, resolved);
i = 1;
lua_newtable(L);
addr = (struct in_addr **) hp->h_addr_list;
2005-06-14 06:29:23 +02:00
if (addr) {
while (*addr) {
lua_pushnumber(L, i);
lua_pushstring(L, inet_ntoa(**addr));
lua_settable(L, -3);
i++; addr++;
}
2002-03-21 14:52:38 +01:00
}
lua_settable(L, resolved);
}
/*-------------------------------------------------------------------------*\
* Tries to create a new inet socket
\*-------------------------------------------------------------------------*/
const char *inet_trycreate(p_socket ps, int family, int type) {
return socket_strerror(socket_create(ps, family, type, 0));
}
/*-------------------------------------------------------------------------*\
* "Disconnects" a DGRAM socket
\*-------------------------------------------------------------------------*/
const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm)
{
switch (family) {
case PF_INET: {
struct sockaddr_in sin;
memset((char *) &sin, 0, sizeof(sin));
sin.sin_family = AF_UNSPEC;
sin.sin_addr.s_addr = INADDR_ANY;
return socket_strerror(socket_connect(ps, (SA *) &sin,
sizeof(sin), tm));
}
case PF_INET6: {
struct sockaddr_in6 sin6;
struct in6_addr addrany = IN6ADDR_ANY_INIT;
memset((char *) &sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_UNSPEC;
sin6.sin6_addr = addrany;
return socket_strerror(socket_connect(ps, (SA *) &sin6,
sizeof(sin6), tm));
}
}
return NULL;
}
2002-03-21 14:52:38 +01:00
/*-------------------------------------------------------------------------*\
2003-05-25 03:54:13 +02:00
* Tries to connect to remote address (address, port)
2002-03-21 14:52:38 +01:00
\*-------------------------------------------------------------------------*/
const char *inet_tryconnect(p_socket ps, int *family, const char *address,
const char *serv, p_timeout tm, struct addrinfo *connecthints)
2002-03-21 14:52:38 +01:00
{
struct addrinfo *iterator = NULL, *resolved = NULL;
const char *err = NULL;
/* try resolving */
err = socket_gaistrerror(getaddrinfo(address, serv,
connecthints, &resolved));
if (err != NULL) {
if (resolved) freeaddrinfo(resolved);
return err;
}
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
timeout_markstart(tm);
/* create new socket if necessary. if there was no
* bind, we need to create one for every new family
* that shows up while iterating. if there was a
* bind, all families will be the same and we will
* not enter this branch. */
if (*family != iterator->ai_family) {
socket_destroy(ps);
err = socket_strerror(socket_create(ps, iterator->ai_family,
iterator->ai_socktype, iterator->ai_protocol));
if (err != NULL) {
freeaddrinfo(resolved);
return err;
}
*family = iterator->ai_family;
/* all sockets initially non-blocking */
socket_setnonblocking(ps);
}
/* try connecting to remote address */
err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr,
(socklen_t) iterator->ai_addrlen, tm));
/* if success, break out of loop */
if (err == NULL) break;
}
freeaddrinfo(resolved);
/* here, if err is set, we failed */
return err;
2002-03-21 14:52:38 +01:00
}
/*-------------------------------------------------------------------------*\
* Tries to accept a socket
\*-------------------------------------------------------------------------*/
const char *inet_tryaccept(p_socket server, int family, p_socket client,
p_timeout tm)
{
socklen_t len;
t_sockaddr_storage addr;
if (family == PF_INET6) {
len = sizeof(struct sockaddr_in6);
} else {
len = sizeof(struct sockaddr_in);
}
return socket_strerror(socket_accept(server, client, (SA *) &addr, &len, tm));
}
2002-03-21 14:52:38 +01:00
/*-------------------------------------------------------------------------*\
2003-05-25 03:54:13 +02:00
* Tries to bind socket to (address, port)
2002-03-21 14:52:38 +01:00
\*-------------------------------------------------------------------------*/
const char *inet_trybind(p_socket ps, const char *address, const char *serv,
struct addrinfo *bindhints)
2002-03-21 14:52:38 +01:00
{
struct addrinfo *iterator = NULL, *resolved = NULL;
const char *err = NULL;
t_socket sock = *ps;
/* translate luasocket special values to C */
if (strcmp(address, "*") == 0) address = NULL;
if (!serv) serv = "0";
/* try resolving */
err = socket_gaistrerror(getaddrinfo(address, serv, bindhints, &resolved));
if (err) {
if (resolved) freeaddrinfo(resolved);
return err;
2002-07-03 21:06:55 +02:00
}
/* iterate over resolved addresses until one is good */
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
if(sock == SOCKET_INVALID) {
err = socket_strerror(socket_create(&sock, iterator->ai_family,
iterator->ai_socktype, iterator->ai_protocol));
if(err)
continue;
}
/* try binding to local address */
err = socket_strerror(socket_bind(&sock,
(SA *) iterator->ai_addr,
(socklen_t) iterator->ai_addrlen));
/* keep trying unless bind succeeded */
if (err) {
if(sock != *ps)
socket_destroy(&sock);
} else {
/* remember what we connected to, particularly the family */
*bindhints = *iterator;
break;
}
}
/* cleanup and return error */
freeaddrinfo(resolved);
*ps = sock;
return err;
2002-03-21 14:52:38 +01:00
}
/*-------------------------------------------------------------------------*\
* Some systems do not provide this so that we provide our own. It's not
* marvelously fast, but it works just fine.
\*-------------------------------------------------------------------------*/
2013-05-27 14:30:06 +02:00
#ifdef LUASOCKET_INET_ATON
int inet_aton(const char *cp, struct in_addr *inp)
2002-03-21 14:52:38 +01:00
{
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
2013-05-25 12:07:38 +02:00
/*-------------------------------------------------------------------------*\
* inet_ntop/inet_pton for MinGW from
* http://mingw-users.1079350.n2.nabble.com/IPv6-getaddrinfo-amp-inet-ntop-td5891996.html
\*-------------------------------------------------------------------------*/
2013-05-27 14:30:06 +02:00
#ifdef LUASOCKET_INET_PTON
2013-05-25 12:07:38 +02:00
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
{
2013-05-25 12:07:38 +02:00
if (af == AF_INET) {
struct sockaddr_in in;
memset(&in, 0, sizeof(in));
in.sin_family = AF_INET;
memcpy(&in.sin_addr, src, sizeof(struct in_addr));
getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in),
dst, cnt, NULL, 0, NI_NUMERICHOST);
return dst;
} else if (af == AF_INET6) {
struct sockaddr_in6 in;
memset(&in, 0, sizeof(in));
in.sin6_family = AF_INET6;
memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6),
dst, cnt, NULL, 0, NI_NUMERICHOST);
return dst;
}
return NULL;
}
2013-05-25 12:07:38 +02:00
int inet_pton(int af, const char *src, void *dst)
{
2013-05-25 12:07:38 +02:00
struct addrinfo hints, *res, *ressave;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = af;
if (getaddrinfo(src, NULL, &hints, &res) != 0) {
return -1;
}
ressave = res;
while (res) {
memcpy(dst, res->ai_addr, res->ai_addrlen);
res = res->ai_next;
}
freeaddrinfo(ressave);
return 0;
}
#endif