Permalink
Fetching contributors…
Cannot retrieve contributors at this time
789 lines (675 sloc) 23.3 KB
/* Network I/O handling module for the Lua/APR binding.
*
* Author: Peter Odding <peter@peterodding.com>
* Last Change: December 7, 2011
* Homepage: http://peterodding.com/code/lua/apr/
* License: MIT
*
* To get started with network programming using the Lua/APR binding, have a
* look at any of the following examples:
*
* - [HTTP client](#example_http_client)
* - [Single threaded webserver](#example_single_threaded_webserver)
* - [Multi threaded webserver](#example_multi_threaded_webserver)
* - [Asynchronous webserver](#example_asynchronous_webserver)
*/
#include "lua_apr.h"
#include <apr_network_io.h>
#include <apr_portable.h>
/* Internal functions {{{1 */
/* family_check(L, i) -- check for address family on Lua stack {{{2 */
#if APR_HAVE_IPV6
const char *family_options[] = { "inet", "inet6", "unspec", NULL };
const int family_values[] = { APR_INET, APR_INET6, APR_UNSPEC };
#else
const char *family_options[] = { "inet", "unspec", NULL };
const int family_values[] = { APR_INET, APR_UNSPEC };
#endif
#define family_check(L, i) \
family_values[luaL_checkoption(L, i, "inet", family_options)]
/* socket_alloc(L) -- allocate and initialize socket object {{{2 */
static apr_status_t socket_alloc(lua_State *L, lua_apr_socket **p)
{
lua_apr_socket *object;
apr_status_t status;
object = new_object(L, &lua_apr_socket_type);
if (object == NULL)
raise_error_memory(L);
status = apr_pool_create(&object->pool, NULL);
*p = object;
return status;
}
/* socket_init(L, object) -- initialize socket buffers {{{2 */
static void socket_init(lua_State *L, lua_apr_socket *object)
{
init_buffers(L, &object->input, &object->output, object->handle, 0,
(lua_apr_buf_rf) apr_socket_recv,
(lua_apr_buf_wf) apr_socket_send,
NULL);
}
/* socket_check(L, i, open) -- get socket object from Lua stack {{{2 */
static lua_apr_socket* socket_check(lua_State *L, int i, int open)
{
lua_apr_socket *object = check_object(L, i, &lua_apr_socket_type);
if (open && object->handle == NULL)
luaL_error(L, "attempt to use a closed socket");
return object;
}
/* option_check(L, i) -- check for socket option on Lua stack {{{2 */
static apr_int32_t option_check(lua_State *L, int i)
{
const char *options[] = { "debug", "keep-alive", "linger", "non-block",
"reuse-addr", "sndbuf", "rcvbuf", "disconnected", NULL };
const apr_int32_t values[] = { APR_SO_DEBUG, APR_SO_KEEPALIVE, APR_SO_LINGER,
APR_SO_NONBLOCK, APR_SO_REUSEADDR, APR_SO_SNDBUF, APR_SO_RCVBUF,
APR_SO_DISCONNECTED };
return values[luaL_checkoption(L, i, NULL, options)];
}
/* socket_close_impl(L, socket) -- destroy socket object {{{2 */
static apr_status_t socket_close_impl(lua_State *L, lua_apr_socket *socket)
{
apr_status_t status = APR_SUCCESS;
if (socket->handle != NULL) {
status = apr_socket_close(socket->handle);
socket->handle = NULL;
}
if (socket->pool != NULL) {
apr_pool_destroy(socket->pool);
socket->pool = NULL;
}
return status;
}
/* apr.socket_create([protocol [, family]]) -> socket {{{1
*
* Create a network socket. On success the new socket object is returned,
* otherwise a nil followed by an error message is returned. Valid values for
* the @protocol argument are:
*
* - `'tcp'` to create a [TCP] [tcp] socket (this is the default)
* - `'udp'` to create a [UDP] [udp] socket
*
* These are the valid values for the @family argument:
*
* - `'inet'` to create a socket using the [IPv4] [ipv4] address family (this
* is the default)
* - `'inet6'` to create a socket using the [IPv6] [ipv6] address family
* - `'unspec'` to pick the system default type
*
* Note that `'inet6'` is only supported when `apr.socket_supports_ipv6` is
* true.
*
* [tcp]: http://en.wikipedia.org/wiki/Transmission_Control_Protocol
* [udp]: http://en.wikipedia.org/wiki/User_Datagram_Protocol
* [ipv4]: http://en.wikipedia.org/wiki/IPv4
* [ipv6]: http://en.wikipedia.org/wiki/IPv6
*/
int lua_apr_socket_create(lua_State *L)
{
/* Socket types */
const char *proto_options[] = { "tcp", "udp", NULL };
const int proto_values[] = { APR_PROTO_TCP, APR_PROTO_UDP };
lua_apr_socket *object;
apr_status_t status;
int family, type, protocol;
protocol = proto_values[luaL_checkoption(L, 1, "tcp", proto_options)];
family = family_check(L, 2);
type = protocol == APR_PROTO_TCP ? SOCK_STREAM : SOCK_DGRAM;
/* Create and initialize the socket and its associated memory pool. */
status = socket_alloc(L, &object);
object->family = family;
object->protocol = protocol;
if (status == APR_SUCCESS)
status = apr_socket_create(&object->handle, family, type, protocol, object->pool);
if (status != APR_SUCCESS)
return push_error_status(L, status);
socket_init(L, object);
return 1;
}
/* apr.hostname_get() -> name {{{1
*
* Get the name of the current machine. On success the host name string is
* returned, otherwise a nil followed by an error message is returned.
*/
int lua_apr_hostname_get(lua_State *L)
{
char hostname[APRMAXHOSTLEN + 1];
apr_status_t status;
apr_pool_t *pool;
pool = to_pool(L);
status = apr_gethostname(hostname, count(hostname), pool);
if (status != APR_SUCCESS)
return push_error_status(L, status);
lua_pushstring(L, hostname);
return 1;
}
/* apr.host_to_addr(hostname [, family]) -> ip_address, ... {{{1
*
* Resolve a host name to one or more IP addresses. On success one or more IP
* addresses are returned as strings, otherwise a nil followed by an error
* message is returned. The optional @family argument is documented under
* `apr.socket_create()`.
*
* > = apr.host_to_addr 'www.lua.org'
* '89.238.129.35'
* > = apr.host_to_addr 'google.com'
* '173.194.65.104' '173.194.65.106' '173.194.65.147' '173.194.65.103' '173.194.65.105' '173.194.65.99'
* > = apr.host_to_addr('ipv6.google.com', 'inet6')
* '2a00:1450:8005::67'
*/
int lua_apr_host_to_addr(lua_State *L)
{
apr_sockaddr_t *address;
apr_pool_t *pool;
const char *host;
char *ip_address;
apr_status_t status;
int family;
pool = to_pool(L);
host = luaL_checkstring(L, 1);
family = family_check(L, 2);
status = apr_sockaddr_info_get(&address, host, family, SOCK_STREAM, 0, pool);
if (status != APR_SUCCESS)
return push_error_status(L, status);
lua_settop(L, 0);
do {
status = apr_sockaddr_ip_get(&ip_address, address);
if (status != APR_SUCCESS)
return push_error_status(L, status);
lua_pushstring(L, ip_address);
address = address->next;
} while (address != NULL);
return lua_gettop(L);
}
/* apr.addr_to_host(ip_address [, family]) -> hostname {{{1
*
* Look up the host name from an IP-address (also known as a reverse [DNS]
* [dns] lookup). On success the host name is returned as a string, otherwise a
* nil followed by an error message is returned. The optional @family argument
* is documented under `apr.socket_create()`.
*
* > = apr.addr_to_host '89.238.129.35'
* 'flounder.pepperfish.net'
*
* [dns]: http://en.wikipedia.org/wiki/Domain_Name_System
*/
int lua_apr_addr_to_host(lua_State *L)
{
apr_sockaddr_t *address;
apr_pool_t *pool;
const char *ip_address;
char *host;
apr_status_t status;
int family;
pool = to_pool(L);
ip_address = luaL_checkstring(L, 1);
family = family_check(L, 2);
status = apr_sockaddr_info_get(&address, ip_address, family, SOCK_STREAM, 0, pool);
if (status == APR_SUCCESS)
status = apr_getnameinfo(&host, address, 0);
if (status != APR_SUCCESS)
return push_error_status(L, status);
lua_pushstring(L, host);
return 1;
}
/* socket:connect(host, port) -> status {{{1
*
* Issue a connection request to a socket either on the same machine or a
* different one, as indicated by the @host string and @port number. On success
* true is returned, otherwise a nil followed by an error message is
* returned.
*/
static int socket_connect(lua_State *L)
{
lua_apr_socket *object;
apr_sockaddr_t *address;
const char *host;
apr_port_t port;
apr_status_t status;
object = socket_check(L, 1, 1);
host = luaL_checkstring(L, 2);
port = luaL_checkinteger(L, 3);
status = apr_sockaddr_info_get(&address, host, object->family, port, 0, object->pool);
if (status == APR_SUCCESS)
status = apr_socket_connect(object->handle, address);
return push_status(L, status);
}
/* socket:bind(host, port) -> status {{{1
*
* Bind the socket to the given @host string and @port number. On success true
* is returned, otherwise a nil followed by an error message is returned. The
* special @host value `'*'` can be used to select the default 'any' address.
* For example if you want to create a web server you can start with the
* following:
*
* -- Basic single threaded server
* server = assert(apr.socket_create())
* assert(server:bind('*', 80))
* assert(server:listen(10))
* while true do
* local client = assert(server:accept())
* -- Here you can receive data from the client by calling client:read()
* -- and send data to the client by calling client:write()
* end
*
* This function can fail if you try to bind a port below 1000 without
* superuser privileges or if another process is already bound to the given
* port number.
*/
static int socket_bind(lua_State *L)
{
lua_apr_socket *object;
apr_sockaddr_t *address;
const char *host;
apr_port_t port;
apr_status_t status;
object = socket_check(L, 1, 1);
host = luaL_checkstring(L, 2);
if (strcmp(host, "*") == 0)
host = APR_ANYADDR;
port = luaL_checkinteger(L, 3);
status = apr_sockaddr_info_get(&address, host, object->family, port, 0, object->pool);
if (status == APR_SUCCESS)
status = apr_socket_bind(object->handle, address);
return push_status(L, status);
}
/* socket:listen(backlog) -> status {{{1
*
* To listen for incoming network connections three steps must be performed:
*
* 1. First a socket is created with `apr.socket_create()`
* 2. Next a willingness to accept incoming connections and a queue limit for
* incoming connections are specified with `socket:listen()` (this call
* doesn't block)
* 3. Finally `socket:accept()` is called to wait for incoming connections
*
* On success true is returned, otherwise a nil followed by an error message is
* returned. The @backlog argument indicates the number of outstanding
* connections allowed in the socket's listen queue. If this value is less than
* zero, the listen queue size is set to zero. As a special case, if you pass
* the string `'max'` as @backlog then a platform specific maximum value is
* chosen based on the compile time constant [SOMAXCONN] [SOMAXCONN].
*
* [SOMAXCONN]: http://www.google.com/search?q=SOMAXCONN
*/
static int socket_listen(lua_State *L)
{
lua_apr_socket *object;
apr_status_t status;
apr_int32_t backlog = SOMAXCONN;
object = socket_check(L, 1, 1);
if (strcmp(lua_tostring(L, 2), "max") != 0)
backlog = luaL_checkinteger(L, 2);
status = apr_socket_listen(object->handle, backlog);
return push_status(L, status);
}
/* socket:recvfrom([bufsize]) -> address, data {{{1
*
* Read data from an [UDP] [udp] socket that has been bound to an interface
* and/or port. On success two values are returned: A table with the address of
* the peer from which the data was sent and a string with the received data.
* If the call fails it returns nil followed by an error message.
*
* By default @bufsize is 1024, this means the resulting @data string will be
* truncated to a maximum of 1024 bytes. If you want to receive larger
* messages, pass a larger @bufsize.
*
* The returned @address table contains the following fields:
*
* - `address` is the IP address (a string)
* - `port` is the port number
* - `family` is one of the strings `'inet'`, `'inet6'` or `'unspec'`
*
* *This function is binary safe.*
*
* [udp]: http://en.wikipedia.org/wiki/User_Datagram_Protocol
*/
static int socket_recvfrom(lua_State *L)
{
lua_apr_socket *object;
apr_status_t status;
apr_sockaddr_t address = { 0 };
apr_size_t buflen;
apr_int32_t flags;
char buffer[1024], ip_addr[APRMAXHOSTLEN];
char *bufptr;
int i;
/* Validate arguments. */
object = socket_check(L, 1, 1);
buflen = luaL_optint(L, 2, sizeof buffer);
/* Use dynamically allocated buffer only when necessary. */
bufptr = (buflen > sizeof buffer) ? lua_newuserdata(L, buflen) : &buffer[0];
flags = 0;
status = apr_socket_recvfrom(&address, object->handle, flags, bufptr, &buflen);
if (status != APR_SUCCESS)
return push_error_status(L, status);
/* Convert the socket address to a Lua table. */
lua_newtable(L);
/* Get the IP address in numeric format. */
status = apr_sockaddr_ip_getbuf(ip_addr, sizeof ip_addr, &address);
if (status == APR_SUCCESS)
lua_pushstring(L, ip_addr), lua_setfield(L, -2, "address");
/* Get the port number. */
lua_pushnumber(L, address.port), lua_setfield(L, -2, "port");
/* Get the address family. */
for (i = 0; i < count(family_values); i++)
if (family_values[i] == address.family) {
lua_pushstring(L, family_options[i]);
lua_setfield(L, -2, "family");
break;
}
/* Push the received data. */
lua_pushlstring(L, bufptr, buflen);
/* Return address table and received data. */
return 2;
}
/* socket:accept() -> client_socket {{{1
*
* Accept a connection request on a server socket. On success a socket is
* returned which forms the connection to the client, otherwise a nil followed
* by an error message is returned. This function blocks until a client
* connects.
*/
static int socket_accept(lua_State *L)
{
lua_apr_socket *server, *client = NULL;
apr_status_t status;
server = socket_check(L, 1, 1);
status = socket_alloc(L, &client);
client->family = server->family;
client->protocol = server->protocol;
if (status == APR_SUCCESS)
status = apr_socket_accept(&client->handle, server->handle, client->pool);
socket_init(L, client);
if (status != APR_SUCCESS)
return push_error_status(L, status);
return 1;
}
/* socket:read([format, ...]) -> mixed value, ... {{{1
*
* This function implements the interface of Lua's `file:read()` function.
*/
static int socket_read(lua_State *L)
{
lua_apr_socket *object = socket_check(L, 1, 1);
return read_buffer(L, &object->input);
}
/* socket:write(value [, ...]) -> status {{{1
*
* This function implements the interface of Lua's `file:write()` function.
*/
static int socket_write(lua_State *L)
{
lua_apr_socket *object = socket_check(L, 1, 1);
int nresults = write_buffer(L, &object->output);
apr_status_t status = flush_buffer(L, &object->output, 1);
if (status != APR_SUCCESS)
return push_error_status(L, status);
return nresults;
}
/* socket:lines() -> iterator {{{1
*
* This function implements the interface of Lua's `file:lines()` function.
*/
static int socket_lines(lua_State *L)
{
lua_apr_socket *object = socket_check(L, 1, 1);
return read_lines(L, &object->input);
}
/* socket:timeout_get() -> timeout {{{1
*
* Get the timeout value or blocking state of @socket. On success the timeout
* value is returned, otherwise a nil followed by an error message is returned.
*
* The @timeout true means wait forever, false means don't wait at all and a
* number is the microseconds to wait.
*/
static int socket_timeout_get(lua_State *L)
{
lua_apr_socket *object;
apr_status_t status;
apr_interval_time_t timeout;
object = socket_check(L, 1, 1);
status = apr_socket_timeout_get(object->handle, &timeout);
if (status != APR_SUCCESS)
return push_error_status(L, status);
else if (timeout <= 0)
lua_pushboolean(L, timeout != 0);
else
lua_pushinteger(L, (lua_Integer) timeout);
return 1;
}
/* socket:timeout_set(timeout) -> status {{{1
*
* Set the timeout value or blocking state of @socket. On success true is
* returned, otherwise a nil followed by an error message is returned.
*
* The @timeout true means wait forever, false means don't wait at all and a
* number is the microseconds to wait.
*/
static int socket_timeout_set(lua_State *L)
{
lua_apr_socket *object;
apr_status_t status;
apr_interval_time_t timeout;
object = socket_check(L, 1, 1);
if (lua_isnumber(L, 2))
timeout = luaL_checkinteger(L, 2);
else
timeout = lua_toboolean(L, 2) ? -1 : 0;
status = apr_socket_timeout_set(object->handle, timeout);
return push_status(L, status);
}
/* socket:opt_get(name) -> value {{{1
*
* Query socket options for the specified socket. Valid values for @name are:
*
* - `'debug'`: turn on debugging information
* - `'keep-alive'`: keep connections active
* - `'linger'`: lingers on close if data is present
* - `'non-block'`: turns blocking on/off for socket
* - `'reuse-addr'`: the rules used in validating addresses supplied to bind
* should allow reuse of local addresses
* - `'sndbuf'`: set the send buffer size
* - `'rcvbuf'`: set the receive buffer size
* - `'disconnected'`: query the disconnected state of the socket (currently only used on Windows)
*
* The `'sndbuf'` and `'rcvbuf'` options have integer values, all other options
* have a boolean value.
*/
static int socket_opt_get(lua_State *L)
{
apr_status_t status;
lua_apr_socket *object;
apr_int32_t option, value;
object = socket_check(L, 1, 1);
option = option_check(L, 2);
status = apr_socket_opt_get(object->handle, option, &value);
if (status != APR_SUCCESS)
return push_error_status(L, status);
else if (option == APR_SO_SNDBUF || option == APR_SO_RCVBUF)
lua_pushinteger(L, value);
else
lua_pushboolean(L, value);
return 1;
}
/* socket:opt_set(name, value) -> status {{{1
*
* Setup socket options for the specified socket. Valid values for @name are
* documented under `socket:opt_get()`, @value should be a boolean or integer
* value. On success true is returned, otherwise a nil followed by an error
* message is returned.
*/
static int socket_opt_set(lua_State *L)
{
apr_status_t status;
lua_apr_socket *object;
apr_int32_t option, value;
object = socket_check(L, 1, 1);
option = option_check(L, 2);
value = lua_isboolean(L, 3) ? lua_toboolean(L, 3) : luaL_checkinteger(L, 3);
status = apr_socket_opt_set(object->handle, option, value);
return push_status(L, status);
}
/* socket:addr_get([type]) -> ip_address, port [, hostname] {{{1
*
* Get one of the IP-address / port pairs associated with @socket, according to
* @type:
*
* - `'local'` to get the address/port to which the socket is bound locally
* - `'remote'` to get the address/port of the peer to which the socket is
* connected (this is the default)
*
* On success the local or remote IP-address (a string) and the port (a number)
* are returned, otherwise a nil followed by an error message is returned. If a
* host name is available that will be returned as the third value.
*/
static int socket_addr_get(lua_State *L)
{
const char *options[] = { "local", "remote", NULL };
const apr_interface_e values[] = { APR_LOCAL, APR_REMOTE };
lua_apr_socket *object;
apr_sockaddr_t *address;
apr_status_t status;
apr_interface_e which;
char *ip_address;
object = socket_check(L, 1, 1);
which = values[luaL_checkoption(L, 2, "remote", options)];
status = apr_socket_addr_get(&address, which, object->handle);
if (status == APR_SUCCESS)
status = apr_sockaddr_ip_get(&ip_address, address);
if (status != APR_SUCCESS)
return push_error_status(L, status);
lua_pushstring(L, ip_address);
lua_pushinteger(L, address->port);
lua_pushstring(L, address->hostname);
return 3;
}
/* socket:fd_get() -> fd {{{1
*
* Get the underlying [file descriptor] [fildes] for this socket. On success a
* number is returned, otherwise a nil followed by an error message is
* returned.
*
* [fildes]: http://en.wikipedia.org/wiki/File_descriptor
*/
int socket_fd_get(lua_State *L)
{
apr_status_t status;
lua_apr_socket *object;
apr_os_sock_t fd;
object = socket_check(L, 1, 1);
status = apr_os_sock_get(&fd, object->handle);
if (status != APR_SUCCESS)
return push_error_status(L, status);
lua_pushinteger(L, fd);
return 1;
}
/* socket:fd_set(fd) -> status {{{1
*
* Set the underlying file descriptor (a number) of an existing socket. On
* success true is returned, otherwise a nil followed by an error message is
* returned.
*/
int socket_fd_set(lua_State *L)
{
lua_apr_socket *object;
apr_status_t status;
apr_os_sock_t fd;
object = socket_check(L, 1, 0);
fd = luaL_checkinteger(L, 2);
status = apr_os_sock_put(&object->handle, &fd, object->pool);
return push_status(L, status);
}
/* socket:shutdown(mode) -> status {{{1
*
* Shutdown either reading, writing, or both sides of a socket. On success true
* is returned, otherwise a nil followed by an error message is returned. Valid
* values for @mode are:
*
* - `'read'`: no longer allow read requests
* - `'write'`: no longer allow write requests
* - `'both'`: no longer allow read or write requests
*
* This does not actually close the socket descriptor, it just controls which
* calls are still valid on the socket. To close sockets see `socket:close()`.
*/
static int socket_shutdown(lua_State *L)
{
const char *options[] = { "read", "write", "both", NULL };
const apr_shutdown_how_e values[] = {
APR_SHUTDOWN_READ, APR_SHUTDOWN_WRITE, APR_SHUTDOWN_READWRITE
};
lua_apr_socket *socket;
apr_status_t status;
apr_shutdown_how_e how;
socket = socket_check(L, 1, 1);
how = values[luaL_checkoption(L, 2, NULL, options)];
status = apr_socket_shutdown(socket->handle, how);
return push_status(L, status);
}
/* socket:close() -> status {{{1
*
* Close @socket. On success true is returned, otherwise a nil followed by an
* error message is returned.
*/
static int socket_close(lua_State *L)
{
return push_status(L, socket_close_impl(L, socket_check(L, 1, 1)));
}
/* socket:__tostring() {{{1 */
static int socket_tostring(lua_State *L)
{
lua_apr_socket *socket;
socket = socket_check(L, 1, 0);
if (socket->handle != NULL)
lua_pushfstring(L, "%s (%p)", lua_apr_socket_type.friendlyname, socket->handle);
else
lua_pushfstring(L, "%s (closed)", lua_apr_socket_type.friendlyname);
return 1;
}
/* socket:__gc() {{{1 */
static int socket_gc(lua_State *L)
{
lua_apr_socket *socket = socket_check(L, 1, 0);
if (object_collectable((lua_apr_refobj*)socket)) {
free_buffer(L, &socket->input.buffer);
free_buffer(L, &socket->output.buffer);
socket_close_impl(L, socket);
}
release_object((lua_apr_refobj*)socket);
return 0;
}
/* }}} */
luaL_reg socket_methods[] = {
{ "bind", socket_bind },
{ "listen", socket_listen },
{ "recvfrom", socket_recvfrom },
{ "accept", socket_accept },
{ "connect", socket_connect },
{ "read", socket_read },
{ "write", socket_write },
{ "lines", socket_lines },
{ "timeout_get", socket_timeout_get },
{ "timeout_set", socket_timeout_set },
{ "opt_get", socket_opt_get },
{ "opt_set", socket_opt_set },
{ "addr_get", socket_addr_get },
{ "fd_get", socket_fd_get },
{ "fd_set", socket_fd_set },
{ "shutdown", socket_shutdown },
{ "close", socket_close },
{ NULL, NULL },
};
luaL_reg socket_metamethods[] = {
{ "__tostring", socket_tostring },
{ "__eq", objects_equal },
{ "__gc", socket_gc },
{ NULL, NULL },
};
lua_apr_objtype lua_apr_socket_type = {
"lua_apr_socket*", /* metatable name in registry */
"socket", /* friendly object name */
sizeof(lua_apr_socket), /* structure size */
socket_methods, /* methods table */
socket_metamethods /* metamethods table */
};