diff --git a/include/coeio.h b/include/coeio.h
index cee7be29284f654c22a0aa0170ae08b8e411d73b..9f19d77cde7e93c696de6af152709e56bd56b003 100644
--- a/include/coeio.h
+++ b/include/coeio.h
@@ -47,6 +47,10 @@
 
 #define ERESOLVE -1
 
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
 /**
  * Asynchronous IO Tasks (libeio wrapper)
  *
@@ -60,4 +64,7 @@ struct addrinfo *
 coeio_resolve(int socktype, const char *host, const char *port,
               ev_tstamp timeout);
 
+#if defined(__cplusplus)
+}
+#endif
 #endif /* TARANTOOL_COEIO_H_INCLUDED */
diff --git a/include/fiber.h b/include/fiber.h
index a89d8e8d63d1837d2007283ce645b5b3f156a3fa..c657b32a7c85724ef5f6bfa6b9e620018346f1ea 100644
--- a/include/fiber.h
+++ b/include/fiber.h
@@ -72,6 +72,8 @@ class FiberCancelException: public Exception {
 		say_debug("FiberCancelException");
 	}
 };
+
+extern "C" {
 #endif /* defined(__cplusplus) */
 
 struct fiber {
@@ -175,4 +177,7 @@ fiber_set_sid(struct fiber *f, uint32_t sid, uint64_t cookie)
 	f->cookie = cookie;
 }
 
+#if defined(__cplusplus)
+}
+#endif
 #endif /* TARANTOOL_FIBER_H_INCLUDED */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 51fd9bcca5d6a73c6e63a30088fd1430d6d3586f..f6bf0d01436d9ff3b9ceff5b9c8df9ba582962de 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -64,6 +64,8 @@ set(lua_sources)
 lua_source(lua_sources lua/uuid.lua)
 lua_source(lua_sources lua/digest.lua)
 lua_source(lua_sources lua/session.lua)
+lua_source(lua_sources lua/bsdsocket.lua)
+lua_source(lua_sources lua/errno.lua)
 
 add_custom_target(generate_lua_sources
     WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src/box
@@ -121,7 +123,9 @@ set (common_sources
      lua/lua_socket.cc
      lua/session.cc
      lua/cjson.cc
+     lua/errno.c
      fiob.c
+     bsdsocket.cc
      ${lua_sources}
 )
 
diff --git a/src/box/box_lua.cc b/src/box/box_lua.cc
index 5571340f1ba190e4491afb3f201683f55890255b..92308ab48672cd92d6583b26e9af087c37f84016 100644
--- a/src/box/box_lua.cc
+++ b/src/box/box_lua.cc
@@ -54,8 +54,15 @@ extern "C" {
 #include "scoped_guard.h"
 
 /* contents of box.lua, misc.lua, box.net.lua respectively */
-extern char box_lua[], box_net_lua[], misc_lua[];
-static const char *lua_sources[] = { box_lua, box_net_lua, misc_lua, NULL };
+extern char box_lua[],
+	box_net_lua[],
+	misc_lua[];
+static const char *lua_sources[] = {
+	box_lua,
+	box_net_lua,
+	misc_lua,
+	NULL
+};
 
 /**
  * All box connections share the same Lua state. We use
diff --git a/src/bsdsocket.cc b/src/bsdsocket.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4766d93c499d93bf2c658ef42ed485396297db92
--- /dev/null
+++ b/src/bsdsocket.cc
@@ -0,0 +1,870 @@
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/types.h>
+#include <string.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+
+extern "C" {
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#include <lj_obj.h>
+#include <lj_ctype.h>
+#include <lj_cdata.h>
+#include <lj_cconv.h>
+}
+
+#include <arpa/inet.h>
+#include <lib/bit/bit.h>
+#include <coeio.h>
+#include <unistd.h>
+#include <fiber.h>
+#include <say.h>
+#include <scoped_guard.h>
+
+#include "bsdsocket.h"
+
+extern char bsdsocket_lua[];
+
+static const struct { char name[32]; int value; } domains[] = {
+	{ "PF_UNIX",		PF_UNIX		},
+	{ "PF_LOCAL",		PF_LOCAL	},
+	{ "PF_INET",		PF_INET		},
+	{ "PF_INET6",		PF_INET6	},
+	{ "PF_IPX",		PF_IPX		},
+	{ "PF_NETLINK",		PF_NETLINK	},
+	{ "PF_X25",		PF_X25		},
+	{ "PF_AX25",		PF_AX25		},
+	{ "PF_ATMPVC",		PF_ATMPVC	},
+	{ "PF_APPLETALK",	PF_APPLETALK	},
+	{ "PF_PACKET",		PF_PACKET	},
+
+	{ "AF_UNIX",		AF_UNIX		},
+	{ "AF_LOCAL",		AF_LOCAL	},
+	{ "AF_INET",		AF_INET		},
+	{ "AF_INET6",		AF_INET6	},
+	{ "AF_IPX",		AF_IPX		},
+	{ "AF_NETLINK",		AF_NETLINK	},
+	{ "AF_X25",		AF_X25		},
+	{ "AF_AX25",		AF_AX25		},
+	{ "AF_ATMPVC",		AF_ATMPVC	},
+	{ "AF_APPLETALK",	AF_APPLETALK	},
+	{ "AF_PACKET",		AF_PACKET	},
+	{ "", 0 }
+};
+
+static const struct { char name[32]; int value; } types[] = {
+	{ "SOCK_STREAM",	SOCK_STREAM	},
+	{ "SOCK_DGRAM",		SOCK_DGRAM	},
+	{ "SOCK_SEQPACKET",	SOCK_SEQPACKET	},
+	{ "SOCK_RAW",		SOCK_RAW	},
+	{ "SOCK_RDM",		SOCK_RDM	},
+	{ "", 0 }
+};
+
+static const struct { char name[32]; int value; } send_flags[] = {
+#ifdef MSG_OOB
+	{"MSG_OOB",		MSG_OOB		},
+#endif
+#ifdef MSG_PEEK
+	{"MSG_PEEK",		MSG_PEEK	},
+#endif
+#ifdef MSG_DONTROUTE
+	{"MSG_DONTROUTE",	MSG_DONTROUTE	},
+#endif
+#ifdef MSG_TRYHARD
+	{"MSG_TRYHARD",		MSG_TRYHARD	},
+#endif
+#ifdef MSG_CTRUNC
+	{"MSG_CTRUNC",		MSG_CTRUNC	},
+#endif
+#ifdef MSG_PROXY
+	{"MSG_PROXY",		MSG_PROXY	},
+#endif
+#ifdef MSG_TRUNC
+	{"MSG_TRUNC",		MSG_TRUNC	},
+#endif
+#ifdef MSG_DONTWAIT
+	{"MSG_DONTWAIT",	MSG_DONTWAIT	},
+#endif
+#ifdef MSG_EOR
+	{"MSG_EOR",		MSG_EOR		},
+#endif
+#ifdef MSG_WAITALL
+	{"MSG_WAITALL",		MSG_WAITALL	},
+#endif
+#ifdef MSG_FIN
+	{"MSG_FIN",		MSG_FIN		},
+#endif
+#ifdef MSG_SYN
+	{"MSG_SYN",		MSG_SYN		},
+#endif
+#ifdef MSG_CONFIRM
+	{"MSG_CONFIRM",		MSG_CONFIRM	},
+#endif
+#ifdef MSG_RST
+	{"MSG_RST",		MSG_RST		},
+#endif
+#ifdef MSG_ERRQUEUE
+	{"MSG_ERRQUEUE",	MSG_ERRQUEUE	},
+#endif
+#ifdef MSG_NOSIGNAL
+	{"MSG_NOSIGNAL",	MSG_NOSIGNAL	},
+#endif
+#ifdef MSG_MORE
+	{"MSG_MORE",		MSG_MORE	},
+#endif
+#ifdef MSG_WAITFORONE
+	{"MSG_WAITFORONE",	MSG_WAITFORONE	},
+#endif
+#ifdef MSG_FASTOPEN
+	{"MSG_FASTOPEN",	MSG_FASTOPEN	},
+#endif
+#ifdef MSG_CMSG_CLOEXEC
+	{"MSG_CMSG_CLOEXEC",	MSG_CMSG_CLOEXEC},
+#endif
+	{ "",			0		}
+};
+
+static const struct { char name[32]; int value, type, rw; } so_opts[] = {
+	{"SO_ACCEPTCONN",	SO_ACCEPTCONN,		1,	0, },
+	{"SO_BINDTODEVICE",	SO_BINDTODEVICE,	2,	1, },
+	{"SO_BROADCAST",	SO_BROADCAST,		1,	1, },
+	{"SO_BSDCOMPAT",	SO_BSDCOMPAT,		1,	1, },
+	{"SO_DEBUG",		SO_DEBUG,		1,	1, },
+	{"SO_DOMAIN",		SO_DOMAIN,		1,	0, },
+	{"SO_ERROR",		SO_ERROR,		1,	0, },
+	{"SO_DONTROUTE",	SO_DONTROUTE,		1,	1, },
+	{"SO_KEEPALIVE",	SO_KEEPALIVE,		1,	1, },
+	{"SO_LINGER",		SO_LINGER,		0,	0, },
+	{"SO_MARK",		SO_MARK,		1,	1, },
+	{"SO_OOBINLINE",	SO_OOBINLINE,		1,	1, },
+	{"SO_PASSCRED",		SO_PASSCRED,		1,	1, },
+	{"SO_PEERCRED",		SO_PEERCRED,		1,	0, },
+	{"SO_PRIORITY",		SO_PRIORITY,		1,	1, },
+	{"SO_PROTOCOL",		SO_PROTOCOL,		1,	0, },
+	{"SO_RCVBUF",		SO_RCVBUF,		1,	1, },
+	{"SO_RCVBUFFORCE",	SO_RCVBUFFORCE,		1,	1, },
+	{"SO_RCVLOWAT",		SO_RCVLOWAT,		1,	1, },
+	{"SO_SNDLOWAT",		SO_SNDLOWAT,		1,	1, },
+	{"SO_RCVTIMEO",		SO_RCVTIMEO,		1,	1, },
+	{"SO_SNDTIMEO",		SO_SNDTIMEO,		1,	1, },
+	{"SO_REUSEADDR",	SO_REUSEADDR,		1,	1, },
+	{"SO_SNDBUF",		SO_SNDBUF,		1,	1, },
+	{"SO_SNDBUFFORCE",	SO_SNDBUFFORCE,		1,	1, },
+	{"SO_TIMESTAMP",	SO_TIMESTAMP,		1,	1, },
+	{"SO_TYPE",		SO_TYPE,		1,	0, },
+
+	{"",			0,			0,	0, }
+};
+
+static const struct { char name[32]; int value; } ai_flags[] = {
+	{"AI_PASSIVE",			AI_PASSIVE			},
+	{"AI_CANONNAME",		AI_CANONNAME			},
+	{"AI_NUMERICHOST",		AI_NUMERICHOST			},
+	{"AI_V4MAPPED",			AI_V4MAPPED			},
+	{"AI_ALL",			AI_ALL				},
+	{"AI_ADDRCONFIG",		AI_ADDRCONFIG			},
+#ifdef AI_IDN
+	{"AI_IDN",			AI_IDN				},
+#endif
+#ifdef AI_CANONIDN
+	{"AI_CANONIDN",			AI_CANONIDN			},
+#endif
+#ifdef AI_IDN_ALLOW_UNASSIGNED
+	{"AI_IDN_ALLOW_UNASSIGNED",	AI_IDN_ALLOW_UNASSIGNED		},
+#endif
+#ifndef AI_IDN_USE_STD3_ASCII_RULES
+	{"AI_IDN_USE_STD3_ASCII_RULES",	AI_IDN_USE_STD3_ASCII_RULES	},
+#endif
+#ifdef AI_NUMERICSERV
+	{"AI_NUMERICSERV",		AI_NUMERICSERV			},
+#endif
+	{"",				0				}
+};
+
+int
+bsdsocket_protocol(const char *proto)
+{
+	struct protoent *p = getprotobyname(proto);
+	if (!p) {
+		errno = EINVAL;
+		return -1;
+	}
+	return p->p_proto;
+}
+
+
+struct dns_res {
+	struct sockaddr *sa;
+	socklen_t salen;
+};
+
+
+
+static struct dns_res *
+local_resolve(const char *host, const char *port)
+{
+	static struct dns_res dns;
+	memset(&dns, 0, sizeof(dns));
+
+	if (strcmp(host, "unix/") == 0) {
+		static struct sockaddr_un uaddr;
+		uaddr.sun_family = AF_UNIX;
+		strncpy(uaddr.sun_path, port, sizeof(uaddr.sun_path));
+
+		dns.sa = (struct sockaddr *)&uaddr;
+		dns.salen = sizeof(uaddr);
+		return &dns;
+	}
+
+	/* IPv4 */
+	in_addr_t iaddr = inet_addr(host);
+	if (iaddr != (in_addr_t)(-1)) {
+		static struct sockaddr_in in;
+		in.sin_family = AF_INET;
+		in.sin_addr.s_addr = iaddr;
+
+		in.sin_port = htons(atoi(port));
+
+		dns.sa = (struct sockaddr *)&in;
+		dns.salen = sizeof(in);
+		return &dns;
+	}
+
+	/* IPv6 */
+	char ipv6[16];
+	if (inet_pton(AF_INET6, host, ipv6) == 1) {
+		struct sockaddr_in6 in6;
+		memset(&in6, 0, sizeof(in6));
+		in6.sin6_family = AF_INET6;
+		in6.sin6_port = htonl(atol(port));
+		memcpy(in6.sin6_addr.s6_addr, ipv6, 16);
+
+		dns.sa = (struct sockaddr *)&in6;
+		dns.salen = sizeof(in6);
+		return &dns;
+	}
+
+	errno = EINVAL;
+	return NULL;
+}
+
+int
+bsdsocket_sysconnect(int fh, const char *host, const char *port)
+{
+	struct dns_res *dns = local_resolve(host, port);
+	if (!dns) {
+		errno = EINVAL;
+		return -1;
+	}
+	return connect(fh, dns->sa, dns->salen);;
+}
+
+int
+bsdsocket_bind(int fh, const char *host, const char *port)
+{
+	struct dns_res *dns = local_resolve(host, port);
+	if (!dns) {
+		errno = EINVAL;
+		return -1;
+	}
+	return bind(fh, dns->sa, dns->salen);;
+}
+
+int
+bsdsocket_nonblock(int fh, int mode)
+{
+	int flags = fcntl(fh, F_GETFL, 0);
+	if (flags < 0)
+		return -1;
+
+	/* GET */
+	if (mode == 0x80) {
+		if (flags & O_NONBLOCK)
+			return 1;
+		else
+			return 0;
+	}
+
+	if (mode) {
+		if (flags & O_NONBLOCK)
+			return 1;
+		flags |= O_NONBLOCK;
+	} else {
+		if ((flags & O_NONBLOCK) == 0)
+			return 0;
+		flags &= ~O_NONBLOCK;
+	}
+	flags = fcntl(fh, F_SETFL, flags);
+	if (flags < 0)
+		return -1;
+
+	return mode ? 1 : 0;
+}
+
+struct bsdsocket_io_wdata {
+	struct fiber *fiber;
+	int io;
+};
+
+static void
+bsdsocket_io(ev_io *watcher, int revents)
+{
+	struct bsdsocket_io_wdata *wdata =
+		(struct bsdsocket_io_wdata *)watcher->data;
+	wdata->io = revents;
+	fiber_wakeup(wdata->fiber);
+}
+
+static int
+lbox_bsdsocket_iowait(struct lua_State *L)
+{
+	int fh = lua_tointeger(L, 1);
+	int events = lua_tointeger(L, 2);
+	ev_tstamp timeout = lua_tonumber(L, 3);
+
+
+	switch(events) {
+		case 0:
+			events = EV_READ;
+			break;
+		case 1:
+			events = EV_WRITE;
+			break;
+		case 2:
+			events = EV_READ | EV_WRITE;
+			break;
+		default:
+			abort();
+	}
+
+	struct ev_io io;
+	ev_io_init(&io, bsdsocket_io, fh, events);
+	struct bsdsocket_io_wdata wdata = { fiber, 0 };
+	io.data = &wdata;
+	ev_set_priority(&io, EV_MAXPRI);
+	ev_io_start(&io);
+
+	fiber_yield_timeout(timeout);
+	ev_io_stop(&io);
+
+	int ret = 0;
+	if (wdata.io & EV_READ)
+		ret |= 1;
+	if (wdata.io & EV_WRITE)
+		ret |= 2;
+
+	lua_pushinteger(L, ret);
+	return 1;
+}
+
+static ssize_t
+bsdsocket_getaddrinfo_cb(va_list ap)
+{
+	const char *host = va_arg(ap, const char *);
+	const char *port = va_arg(ap, const char *);
+	const struct addrinfo *hints = va_arg(ap, const struct addrinfo *);
+	struct addrinfo **res = va_arg(ap, struct addrinfo **);
+	int *res_errno = va_arg(ap, int *);
+
+	int rc = getaddrinfo(host, port, hints, res);
+
+	switch(rc) {
+		case EAI_NONAME:
+			*res = NULL;
+		case 0:
+			return 0;
+
+		case EAI_SYSTEM:
+			*res_errno = errno;
+			return -1;
+
+
+		default:
+			*res_errno = rc;
+			return -1;
+	}
+}
+
+static int
+lbox_bsdsocket_push_family(struct lua_State *L, int family)
+{
+	switch(family) {
+		case AF_UNIX:
+			lua_pushliteral(L, "AF_UNIX");
+			break;
+		case AF_INET:
+			lua_pushliteral(L, "AF_INET");
+			break;
+		case AF_INET6:
+			lua_pushliteral(L, "AF_INET6");
+			break;
+		case AF_IPX:
+			lua_pushliteral(L, "AF_IPX");
+			break;
+		case AF_NETLINK:
+			lua_pushliteral(L, "AF_NETLINK");
+			break;
+		case AF_X25:
+			lua_pushliteral(L, "AF_X25");
+			break;
+		case AF_AX25:
+			lua_pushliteral(L, "AF_AX25");
+			break;
+		case AF_ATMPVC:
+			lua_pushliteral(L, "AF_ATMPVC");
+			break;
+		case AF_APPLETALK:
+			lua_pushliteral(L, "AF_APPLETALK");
+			break;
+		case AF_PACKET:
+			lua_pushliteral(L, "AF_PACKET");
+			break;
+
+		default:
+			lua_pushinteger(L, family);
+			break;
+	}
+	return 1;
+}
+
+static int
+lbox_bsdsocket_push_protocol(struct lua_State *L, int protonumber)
+{
+	struct protoent *p = getprotobynumber(protonumber);
+	if (p) {
+		lua_pushstring(L, p->p_name);
+	} else {
+		lua_pushinteger(L, p->p_proto);
+	}
+	return 1;
+}
+
+static int
+lbox_bsdsocket_push_sotype(struct lua_State *L, int sotype)
+{
+	sotype &= ~(SOCK_NONBLOCK | SOCK_CLOEXEC);
+	switch(sotype) {
+		case SOCK_STREAM:
+			lua_pushliteral(L, "SOCK_STREAM");
+			break;
+		case SOCK_DGRAM:
+			lua_pushliteral(L, "SOCK_DGRAM");
+			break;
+		case SOCK_SEQPACKET:
+			lua_pushliteral(L, "SOCK_SEQPACKET");
+			break;
+		case SOCK_RAW:
+			lua_pushliteral(L, "SOCK_RAW");
+			break;
+		case SOCK_RDM:
+			lua_pushliteral(L, "SOCK_RDM");
+			break;
+		case SOCK_PACKET:
+			lua_pushliteral(L, "SOCK_PACKET");
+			break;
+		default:
+			lua_pushinteger(L, sotype);
+			break;
+	}
+	return 1;
+}
+
+static int
+lbox_bsdsocket_push_addr(struct lua_State *L,
+	const struct sockaddr *addr, socklen_t alen)
+{
+	lua_newtable(L);
+
+	lua_pushliteral(L, "family");
+	lbox_bsdsocket_push_family(L, addr->sa_family);
+	lua_rawset(L, -3);
+
+	switch(addr->sa_family) {
+		case AF_INET:
+		case AF_INET6: {
+			char shost[NI_MAXHOST];
+			char sservice[NI_MAXSERV];
+			int rc = getnameinfo(addr,
+				alen,
+				shost, sizeof(shost),
+				sservice, sizeof(sservice),
+				NI_NUMERICHOST|NI_NUMERICSERV
+			);
+
+			if (rc == 0) {
+				lua_pushliteral(L, "host");
+				lua_pushstring(L, shost);
+				lua_rawset(L, -3);
+
+				lua_pushliteral(L, "port");
+				lua_pushinteger(L, atol(sservice));
+				lua_rawset(L, -3);
+			}
+
+
+			break;
+		}
+
+		case AF_UNIX:
+			lua_pushliteral(L, "host");
+			lua_pushliteral(L, "unix/");
+			lua_rawset(L, -3);
+
+			lua_pushliteral(L, "port");
+			lua_pushstring(L,
+				((struct sockaddr_un *)addr)->sun_path);
+			lua_rawset(L, -3);
+			break;
+
+		default:	/* unknown family */
+			lua_pop(L, 1);
+			lua_pushnil(L);
+			break;
+	}
+
+	return 1;
+}
+
+static int
+lbox_bsdsocket_getaddrinfo(struct lua_State *L)
+{
+	assert(lua_gettop(L) == 4);
+	lua_pushvalue(L, 1);
+	const char *host = lua_tostring(L, -1);
+	lua_pushvalue(L, 2);
+	const char *port = lua_tostring(L, -1);
+
+	ev_tstamp timeout = lua_tonumber(L, 3);
+
+	struct addrinfo hints, *result = NULL;
+	memset(&hints, 0, sizeof(hints));
+
+	if (lua_istable(L, 4)) {
+		lua_getfield(L, 4, "family");
+		if (lua_isnumber(L, -1))
+			hints.ai_family = lua_tointeger(L, -1);
+		lua_pop(L, 1);
+
+		lua_getfield(L, 4, "type");
+		if (lua_isnumber(L, -1))
+			hints.ai_socktype = lua_tointeger(L, -1);
+		lua_pop(L, 1);
+
+		lua_getfield(L, 4, "protocol");
+		if (lua_isnumber(L, -1))
+			hints.ai_protocol = lua_tointeger(L, -1);
+		lua_pop(L, 1);
+
+		lua_getfield(L, 4, "flags");
+		if (lua_isnumber(L, -1))
+			hints.ai_flags = lua_tointeger(L, -1);
+		lua_pop(L, 1);
+	}
+
+	int res_errno = 0;
+	int dns_res = coeio_custom(bsdsocket_getaddrinfo_cb, timeout,
+		host, port, &hints, &result, &res_errno);
+	lua_pop(L, 2);	/* host, port */
+
+	if (dns_res != 0) {
+		errno = res_errno;
+		lua_pushnil(L);
+		return 1;
+	}
+
+	/* no results */
+	if (!result) {
+		lua_newtable(L);
+		return 1;
+	}
+
+        auto scope_guard = make_scoped_guard([&]{
+		freeaddrinfo(result);
+	});
+
+	lua_newtable(L);
+	int i = 1;
+	for (struct addrinfo *rp = result; rp; rp = rp->ai_next, i++) {
+		lua_pushinteger(L, i);
+
+		lbox_bsdsocket_push_addr(L, rp->ai_addr, rp->ai_addrlen);
+
+		if (lua_isnil(L, -1)) {
+			lua_pop(L, 2);
+			i--;
+			continue;
+		}
+
+		lua_pushliteral(L, "protocol");
+		lbox_bsdsocket_push_protocol(L, rp->ai_protocol);
+		lua_rawset(L, -3);
+
+		lua_pushliteral(L, "type");
+		lbox_bsdsocket_push_sotype(L, rp->ai_socktype);
+		lua_rawset(L, -3);
+
+		if (rp->ai_canonname) {
+			lua_pushliteral(L, "canonname");
+			lua_pushstring(L, rp->ai_canonname);
+			lua_rawset(L, -3);
+		}
+
+
+		lua_rawset(L, -3);
+
+	}
+	return 1;
+}
+
+static void
+lbox_bsdsocket_update_proto_type(struct lua_State *L, int fh)
+{
+	if (lua_isnil(L, -1))
+		return;
+
+	int save_errno = errno;
+
+	int value;
+	socklen_t len = sizeof(value);
+
+	if (getsockopt(fh, SOL_SOCKET, SO_PROTOCOL, &value, &len) == 0) {
+		lua_pushliteral(L, "protocol");
+		lbox_bsdsocket_push_protocol(L, value);
+		lua_rawset(L, -3);
+	}
+
+	len = sizeof(value);
+	if (getsockopt(fh, SOL_SOCKET, SO_TYPE, &value, &len) == 0) {
+		lua_pushliteral(L, "type");
+		lbox_bsdsocket_push_sotype(L, value);
+		lua_rawset(L, -3);
+	}
+	errno = save_errno;
+
+}
+
+static int
+lbox_bsdsocket_soname(struct lua_State *L)
+{
+	lua_pushvalue(L, 1);
+	int fh = lua_tointeger(L, -1);
+	lua_pop(L, 1);
+
+
+	struct sockaddr_storage addr;
+	socklen_t len = sizeof(addr);
+	if (getsockname(fh, (struct sockaddr *)&addr, &len) != 0) {
+		lua_pushnil(L);
+		return 1;
+	}
+	lbox_bsdsocket_push_addr(L, (const struct sockaddr *)&addr, len);
+	lbox_bsdsocket_update_proto_type(L, fh);
+	return 1;
+}
+
+
+static int
+lbox_bsdsocket_peername(struct lua_State *L)
+{
+	lua_pushvalue(L, 1);
+	int fh = lua_tointeger(L, -1);
+	lua_pop(L, 1);
+
+	struct sockaddr_storage addr;
+	socklen_t len = sizeof(addr);
+	if (getpeername(fh, (struct sockaddr *)&addr, &len) != 0) {
+		lua_pushnil(L);
+		return 1;
+	}
+	lbox_bsdsocket_push_addr(L, (const struct sockaddr *)&addr, len);
+	lbox_bsdsocket_update_proto_type(L, fh);
+	return 1;
+}
+
+
+static int
+lbox_bsdsocket_recvfrom(struct lua_State *L)
+{
+	int fh = lua_tointeger(L, 1);
+	int size = lua_tointeger(L, 2);
+	int flags = lua_tointeger(L, 3);
+
+	struct sockaddr_storage fa;
+	socklen_t len = sizeof(fa);
+
+	char *buf = (char *)malloc(size);
+	if (!buf) {
+		errno = ENOMEM;
+		lua_pushnil(L);
+		return 1;
+	}
+
+        auto scope_guard = make_scoped_guard([&]{ free(buf); });
+
+	ssize_t res = recvfrom(fh, buf, size, flags,
+		(struct sockaddr*)&fa, &len);
+
+	if (res < 0) {
+		lua_pushnil(L);
+		return 1;
+	}
+	lua_pushlstring(L, buf, res);
+	lbox_bsdsocket_push_addr(L, (struct sockaddr *)&fa, len);
+	return 2;
+}
+
+
+int
+bsdsocket_sendto(int fh, const char *host, const char *port,
+	const void *octets, size_t len, int flags)
+{
+	struct dns_res *dns = local_resolve(host, port);
+	if (!dns) {
+		errno = EINVAL;
+		return -1;
+	}
+	return sendto(fh, octets, len, flags, dns->sa, dns->salen);
+}
+
+void
+tarantool_lua_bsdsocket_init(struct lua_State *L)
+{
+	int top = lua_gettop(L);
+
+	static const struct luaL_Reg internal_methods[] = {
+		{ "iowait",		lbox_bsdsocket_iowait		},
+		{ "getaddrinfo",	lbox_bsdsocket_getaddrinfo	},
+		{ "name",		lbox_bsdsocket_soname		},
+		{ "peer",		lbox_bsdsocket_peername		},
+		{ "recvfrom",		lbox_bsdsocket_recvfrom		},
+		{ NULL,			NULL				}
+	};
+
+
+	if (luaL_dostring(L, bsdsocket_lua))
+		panic("Error loading Lua source (internal)/bsdsocket.lua: %s",
+			      lua_tostring(L, -1));
+
+	lua_getfield(L, LUA_GLOBALSINDEX, "box");
+	lua_getfield(L, -1, "socket");
+	lua_getfield(L, -1, "internal");
+
+
+	luaL_register(L, NULL, internal_methods);
+
+
+	/* domains table */
+	lua_pushliteral(L, "DOMAIN");
+	lua_newtable(L);
+	for (int i = 0; domains[i].name[0]; i++) {
+		lua_pushstring(L, domains[i].name);
+		lua_pushinteger(L, domains[i].value);
+		lua_rawset(L, -3);
+	}
+	lua_rawset(L, -3);
+
+	/* SO_TYPE */
+	lua_pushliteral(L, "SO_TYPE");
+	lua_newtable(L);
+	for (int i = 0; types[i].name[0]; i++) {
+		lua_pushstring(L, types[i].name);
+		lua_pushinteger(L, types[i].value);
+		lua_rawset(L, -3);
+	}
+	lua_rawset(L, -3);
+
+	/* SEND_FLAGS */
+	lua_pushliteral(L, "SEND_FLAGS");
+	lua_newtable(L);
+	for (int i = 0; send_flags[i].name[0]; i++) {
+		lua_pushstring(L, send_flags[i].name);
+		lua_pushinteger(L, send_flags[i].value);
+		lua_rawset(L, -3);
+	}
+	lua_rawset(L, -3);
+
+	/* AI_FLAGS */
+	lua_pushliteral(L, "AI_FLAGS");
+	lua_newtable(L);
+	for (int i = 0; ai_flags[i].name[0]; i++) {
+		lua_pushstring(L, ai_flags[i].name);
+		lua_pushinteger(L, ai_flags[i].value);
+		lua_rawset(L, -3);
+	}
+	lua_rawset(L, -3);
+
+
+	/* SO_OPT */
+	lua_pushliteral(L, "SO_OPT");
+	lua_newtable(L);
+	for (int i = 0; so_opts[i].name[0]; i++) {
+		lua_pushstring(L, so_opts[i].name);
+		lua_newtable(L);
+
+		lua_pushliteral(L, "iname");
+		lua_pushinteger(L, so_opts[i].value);
+		lua_rawset(L, -3);
+
+		lua_pushliteral(L, "type");
+		lua_pushinteger(L, so_opts[i].type);
+		lua_rawset(L, -3);
+
+		lua_pushliteral(L, "rw");
+		lua_pushboolean(L, so_opts[i].rw);
+		lua_rawset(L, -3);
+
+		lua_rawset(L, -3);
+	}
+	lua_rawset(L, -3);
+
+	/* constants */
+	lua_pushliteral(L, "SOL_SOCKET");
+	lua_pushinteger(L, SOL_SOCKET);
+	lua_rawset(L, -3);
+
+	/* constants */
+	lua_pushliteral(L, "INT_SIZE");
+	lua_pushinteger(L, sizeof(int));
+	lua_rawset(L, -3);
+	lua_pushliteral(L, "SIZE_T_SIZE");
+	lua_pushinteger(L, sizeof(size_t));
+	lua_rawset(L, -3);
+
+
+	lua_settop(L, top);
+}
+
diff --git a/src/bsdsocket.h b/src/bsdsocket.h
new file mode 100644
index 0000000000000000000000000000000000000000..05b822a4072c3d232c160790c873fb1453c6cf39
--- /dev/null
+++ b/src/bsdsocket.h
@@ -0,0 +1,53 @@
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef TARANTOOL_BOX_RAWSOCKET_H_INCLUDED
+#define TARANTOOL_BOX_RAWSOCKET_H_INCLUDED
+
+#include <stddef.h>
+
+struct lua_State;
+void tarantool_lua_bsdsocket_init(struct lua_State *L);
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+int bsdsocket_protocol(const char *proto);
+int bsdsocket_sysconnect(int fh, const char *host, const char *port);
+int bsdsocket_bind(int fh, const char *host, const char *port);
+int bsdsocket_nonblock(int fh, int mode);
+int bsdsocket_sendto(int fh, const char *host, const char *port,
+	const void *octets, size_t len, int flags);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* TARANTOOL_BOX_RAWSOCKET_H_INCLUDED */
diff --git a/src/ffisyms.cc b/src/ffisyms.cc
index 4bf53e005c691d653e28807f610db28258aa01b1..e6a7332473688900f5a4d06dde97105eecb5fd7f 100644
--- a/src/ffisyms.cc
+++ b/src/ffisyms.cc
@@ -1,6 +1,11 @@
+#include "bsdsocket.h"
 /*
  * A special hack to cc/ld to keep symbols in an optimized binary.
  * Please add your symbols to this array if you plan to use it from LuaJIT FFI.
  */
 void *ffi_symbols[] = {
+	(void *)bsdsocket_protocol,
+	(void *)bsdsocket_sysconnect,
+	(void *)bsdsocket_bind,
+	(void *)bsdsocket_nonblock
 };
diff --git a/src/lua/bsdsocket.lua b/src/lua/bsdsocket.lua
new file mode 100644
index 0000000000000000000000000000000000000000..cc257a138f0e99aae940d5aa2f75b441b1c474bd
--- /dev/null
+++ b/src/lua/bsdsocket.lua
@@ -0,0 +1,847 @@
+-- bsdsocket.lua (internal file)
+
+do
+
+local TIMEOUT_INFINITY      = 500 * 365 * 86400
+
+local ffi = require 'ffi'
+
+ffi.cdef[[
+    int write(int fh, const char *octets, size_t len);
+    int read(int fh, char *buf, size_t len);
+    int listen(int fh, int backlog);
+    int socket(int domain, int type, int protocol);
+    int close(int s);
+    int shutdown(int s, int how);
+    int send(int s, const void *msg, size_t len, int flags);
+    int recv(int s, void *buf, size_t len, int flags);
+    int accept(int s, void *addr, void *addrlen);
+
+    int bsdsocket_protocol(const char *proto);
+    int bsdsocket_sysconnect(int fh, const char *host, const char *port);
+    int bsdsocket_bind(int fh, const char *host, const char *port);
+    int bsdsocket_nonblock(int fh, int mode);
+    int bsdsocket_sendto(int fh, const char *host, const char *port,
+	const void *octets, size_t len, int flags);
+
+
+    int setsockopt(int s, int level, int iname, const void *opt, size_t optlen);
+    int getsockopt(int s, int level, int iname, void *ptr, size_t *optlen);
+
+
+    typedef struct { int active; int timeout; } linger_t;
+]]
+
+local function sprintf(fmt, ...)
+    return string.format(fmt, ...)
+end
+
+local function printf(fmt, ...)
+    print(sprintf(fmt, ...))
+end
+
+
+local function errno(self)
+    if self['_errno'] == nil then
+        return 0
+    else
+        return self['_errno']
+    end
+end
+
+local function errstr(self)
+    if self['_errno'] == nil then
+        return nil
+    else
+        return box.errno.strerror(self._errno)
+    end
+end
+
+local function get_ivalue(table, key)
+    if type(key) == 'number' then
+        return key
+    end
+    return table[key]
+end
+
+local function get_iflags(table, flags)
+    if flags == nil then
+        return 0
+    end
+    local res = 0
+    if type(flags) ~= 'table' then
+        flags = { flags }
+    end
+    for i, f in pairs(flags) do
+        if table[f] == nil then
+            return nil
+        end
+        res = bit.bor(res, table[f])
+    end
+    return res
+end
+
+local socket_methods  = {}
+socket_methods.errno = errno
+socket_methods.error = errstr
+    
+socket_methods.sysconnect = function(self, host, port)
+    self._errno = nil
+
+    host = tostring(host)
+    port = tostring(port)
+
+    local res = ffi.C.bsdsocket_sysconnect(self.fh, host, port)
+    if res == 0 then
+        return true
+    end
+
+    local errno = box.errno()
+    if errno == box.errno.EINPROGRESS then
+        return true
+    end
+
+    self._errno = errno
+    return false
+end
+
+socket_methods.syswrite = function(self, octets)
+    self._errno = nil
+    local done = ffi.C.write(self.fh, octets, string.len(octets))
+    if done < 0 then
+        self._errno = box.errno()
+        return nil
+    end
+    return done
+end
+
+socket_methods.sysread = function(self, len)
+    self._errno = nil
+    local buf = ffi.new('char[?]', len)
+    local res = ffi.C.read(self.fh, buf, len)
+    
+    if res < 0 then
+        self._errno = box.errno()
+        return nil
+    end
+
+    buf = ffi.string(buf, res)
+    return buf
+end
+
+socket_methods.nonblock = function(self, nb)
+    self._errno = nil
+
+    local res
+
+    if nb == nil then
+        res = ffi.C.bsdsocket_nonblock(self.fh, 0x80)
+    elseif nb then
+        res = ffi.C.bsdsocket_nonblock(self.fh, 1)
+    else
+        res = ffi.C.bsdsocket_nonblock(self.fh, 0)
+    end
+
+    if res < 0 then
+        self._errno = box.errno()
+        return nil
+    end
+
+    if res == 1 then
+        return true
+    else
+        return false
+    end
+end
+
+socket_methods.readable = function(self, timeout)
+    self._errno = nil
+    if timeout == nil then
+        timeout = TIMEOUT_INFINITY
+    end
+
+    local wres = box.socket.internal.iowait(self.fh, 0, timeout)
+
+    if wres == 0 then
+        self._errno = box.errno.ETIMEDOUT
+        return false
+    end
+    return true
+end
+
+socket_methods.wait = function(self, timeout)
+    self._errno = nil
+    if timeout == nil then
+        timeout = TIMEOUT_INFINITY
+    end
+
+    local wres = box.socket.internal.iowait(self.fh, 2, timeout)
+
+    if wres == 0 then
+        self._errno = box.errno.ETIMEDOUT
+        return
+    end
+
+    local res = ''
+    if bit.band(wres, 1) ~= 0 then
+        res = res .. 'R'
+    end
+    if bit.band(wres, 2) ~= 0 then
+        res = res .. 'W'
+    end
+    return res
+end
+    
+socket_methods.writable = function(self, timeout)
+    self._errno = nil
+    if timeout == nil then
+        timeout = TIMEOUT_INFINITY
+    end
+
+    local wres = box.socket.internal.iowait(self.fh, 1, timeout)
+
+    if wres == 0 then
+        self._errno = box.errno.ETIMEDOUT
+        return false
+    end
+    return true
+end
+
+socket_methods.listen = function(self, backlog)
+    self._errno = nil
+    if backlog == nil then
+        backlog = 256
+    end
+    local res = ffi.C.listen(self.fh, backlog)
+    if res < 0 then
+        self._errno = box.errno()
+        return false
+    end
+    return true
+end
+
+socket_methods.bind = function(self, host, port)
+    self._errno = nil
+
+    host = tostring(host)
+    port = tostring(port)
+
+    local res = ffi.C.bsdsocket_bind(self.fh, host, port)
+    if res == 0 then
+        return true
+    end
+    self._errno = box.errno()
+    return false
+end
+
+socket_methods.close = function(self)
+    self._errno = nil
+    if ffi.C.close(self.fh) < 0 then
+        self._errno = box.errno()
+        return false
+    else
+        return true
+    end
+end
+
+
+socket_methods.shutdown = function(self, how)
+    local hvariants = {
+        ['R']           = 0,
+        ['READ']        = 0,
+        ['W']           = 1,
+        ['WRITE']       = 1,
+        ['RW']          = 2,
+        ['READ_WRITE']  = 2,
+
+        [0]             = 0,
+        [1]             = 1,
+        [2]             = 2
+    }
+    local ihow = hvariants[how]
+
+    if ihow == nil then
+        ihow = 3
+    end
+    self._errno = nil
+    if ffi.C.shutdown(self.fh, ihow) < 0 then
+        self._errno = box.errno()
+        return false
+    end
+    return true
+end
+
+socket_methods.setsockopt = function(self, level, name, value)
+    local info = get_ivalue(box.socket.internal.SO_OPT, name)
+
+    if info == nil then
+        error(sprintf("Unknown socket option name: %s", tostring(name)))
+    end
+
+    if not info.rw then
+        error(sprintf("Socket option %s is read only", name))
+    end
+
+    self._errno = nil
+
+    if type(level) == 'string' then
+        if level == 'SOL_SOCKET' then
+            level = box.socket.internal.SOL_SOCKET
+        else
+            level = ffi.C.bsdsocket_protocol(level)
+            if level == -1 then
+                self._errno = box.errno()
+                return false
+            end
+        end
+    else
+        level = tonumber(level)
+    end
+
+    if type(value) == 'boolean' then
+        if value then
+            value = 1
+        else
+            value = 0
+        end
+    end
+
+    if info.type == 1 then
+        local value = ffi.new("int[1]", value)
+        local res = ffi.C.setsockopt(self.fh,
+            level, info.iname, value, box.socket.internal.INT_SIZE)
+
+        if res < 0 then
+            self._errno = box.errno()
+            return false
+        end
+        return true
+    end
+
+    if info.type == 2 then
+        local res = ffi.C.setsockopt(self.fh,
+            level, info.iname, value, box.socket.internal.SIZE_T_SIZE)
+        if res < 0 then
+            self._errno = box.errno()
+            return false
+        end
+        return true
+    end
+    
+    if name == 'SO_LINGER' then
+        error("Use s:linger(active[, timeout])")
+    end
+    error(sprintf("Unsupported socket option: %s", name))
+end
+
+socket_methods.getsockopt = function(self, level, name)
+    local info = get_ivalue(box.socket.internal.SO_OPT, name)
+
+    if info == nil then
+        error(sprintf("Unknown socket option name: %s", tostring(name)))
+    end
+
+    self._errno = nil
+
+    if type(level) == 'string' then
+        if level == 'SOL_SOCKET' then
+            level = box.socket.internal.SOL_SOCKET
+        else
+            level = ffi.C.bsdsocket_protocol(level)
+            if level == -1 then
+                self._errno = box.errno()
+                return nil
+            end
+        end
+    else
+        level = tonumber(level)
+    end
+
+
+    if info.type == 1 then
+        local value = ffi.new("int[1]", 0)
+        local len = ffi.new("size_t[1]", box.socket.internal.INT_SIZE)
+        local res = ffi.C.getsockopt(self.fh, level, info.iname, value, len)
+
+        if res < 0 then
+            self._errno = box.errno()
+            return nil
+        end
+
+        if len[0] ~= 4 then
+            error(sprintf("Internal error: unexpected optlen: %d", len[0]))
+        end
+        return value[0]
+    end
+
+    if info.type == 2 then
+        local value = ffi.new("char[256]", { 0 })
+        local len = ffi.new("size_t[1]", 256)
+        local res = ffi.C.getsockopt(self.fh, level, info.iname, value, len)
+        if res < 0 then
+            self._errno = box.errno()
+            return nil
+        end
+        return ffi.string(value, len[0])
+    end
+
+    if name == 'SO_LINGER' then
+        error("Use s:linger()")
+    end
+    error(sprintf("Unsupported socket option: %s", name))
+end
+
+socket_methods.linger = function(self, active, timeout)
+
+    local info = box.socket.internal.SO_OPT.SO_LINGER
+    self._errno = nil
+    if active == nil then
+        local value = ffi.new("linger_t[1]")
+        local len = ffi.new("size_t[1]", 2 * box.socket.internal.INT_SIZE)
+        local res = ffi.C.getsockopt(self.fh,
+            box.socket.internal.SOL_SOCKET, info.iname, value, len)
+        if res < 0 then
+            self._errno = box.errno()
+            return nil
+        end
+        if value[0].active ~= 0 then
+            active = true
+        else
+            active = false
+        end
+        return active, value[0].timeout
+    end
+
+    if timeout == nil then
+        timeout = 0
+    end
+
+    local iactive
+    if active then
+        iactive = 1
+    else
+        iactive = 0
+    end
+
+    local value = ffi.new("linger_t[1]",
+        { { active = iactive, timeout = timeout } })
+    local len = 2 * box.socket.internal.INT_SIZE
+    local res = ffi.C.setsockopt(self.fh,
+        box.socket.internal.SOL_SOCKET, info.iname, value, len)
+    if res < 0 then
+        self._errno = box.errno()
+        return nil
+    end
+
+    return active, timeout
+end
+
+
+socket_methods.accept = function(self)
+
+    self._errno = nil
+
+    local fh = ffi.C.accept(self.fh, nil, nil)
+
+    if fh < 1 then
+        self._errno = box.errno()
+        return nil
+    end
+
+    local socket = { fh = fh }
+    setmetatable(socket, box.socket.internal.socket_mt)
+    return socket
+end
+
+
+socket_methods.read = function(self, size, timeout)
+    if timeout == nil then
+        timeout = TIMEOUT_INFINITY
+    end
+
+    if self.rbuf == nil then
+        self.rbuf = ''
+    end
+    
+    if size == nil then
+        size = 4294967295
+    end
+
+    if string.len(self.rbuf) >= size then
+        self._errno = nil
+        local data = string.sub(self.rbuf, 1, size)
+        self.rbuf = string.sub(self.rbuf, size + 1)
+        return data
+    end
+
+    while timeout > 0 do
+        local started = box.time()
+        
+        if not self:readable(timeout) then
+            self._errno = box.errno()
+            return nil
+        end
+        
+        timeout = timeout - ( box.time() - started )
+
+        local data = self:sysread(4096)
+        if data ~= nil then
+            self.rbuf = self.rbuf .. data
+            if string.len(self.rbuf) >= size then
+               data = string.sub(self.rbuf, 1, size)
+               self.rbuf = string.sub(self.rbuf, size + 1)
+               return data
+            end
+
+            if string.len(data) == 0 then   -- eof
+                data = self.rbuf
+                self.rbuf = nil
+                return data
+            end
+        elseif self:errno() ~= box.errno.EAGAIN then
+            self._errno = box.errno()
+            return nil
+        end
+    end
+    self._errno = box.errno.ETIMEDOUT
+    return nil
+end
+
+
+local function readline_check(self, eols, limit)
+    local rbuf = self.rbuf
+    if rbuf == nil then
+        return nil
+    end
+    if string.len(rbuf) == 0 then
+        return nil
+    end
+    for i, eol in pairs(eols) do
+        if string.len(rbuf) >= string.len(eol) then
+            local data = string.match(rbuf, "^(.*" .. eol .. ")")
+            if data ~= nil then
+                if string.len(data) > limit then
+                    return string.sub(data, 1, limit)
+                end
+                return data
+            end
+        end
+    end
+    return nil
+end
+
+socket_methods.readline = function(self, limit, eol, timeout)
+
+    if type(limit) == 'table' then -- :readline({eol}[, timeout ])
+        if eol ~= nil then
+            timeout = eol
+            eol = limit
+            limit = nil
+        else
+            eol = limit
+            limit = nil
+        end
+    elseif eol ~= nil then
+        if type(eol) ~= 'table' then
+            eol = { eol }
+        end
+    end
+    
+    if timeout == nil then
+        timeout = TIMEOUT_INFINITY
+    end
+
+    if self.rbuf == nil then
+        self.rbuf = ''
+    end
+
+    if limit == nil then
+        limit = 4294967295
+    end
+
+    self._errno = nil
+    local data = readline_check(self, eol, limit)
+    if data ~= nil then
+        self.rbuf = string.sub(self.rbuf, string.len(data) + 1)
+        return data
+    end
+
+    local started = box.time()
+    while timeout > 0 do
+        local started = box.time()
+        
+        if not self:readable(timeout) then
+            self._errno = box.errno()
+            return nil
+        end
+        
+        timeout = timeout - ( box.time() - started )
+
+        local data = self:sysread(4096)
+        if data ~= nil then
+            self.rbuf = self.rbuf .. data
+            if string.len(self.rbuf) >= limit then
+               data = string.sub(self.rbuf, 1, limit)
+               self.rbuf = string.sub(self.rbuf, limit + 1)
+               return data
+            end
+
+            if string.len(data) == 0 then
+                data = self.rbuf
+                self.rbuf = nil
+                return data
+            end
+
+            data = readline_check(self, eol, limit)
+            if data ~= nil then
+                self.rbuf = string.sub(self.rbuf, string.len(data) + 1)
+                return data
+            end
+        elseif self:errno() ~= box.errno.EAGAIN then
+            self._errno = box.errno()
+            return nil
+        end
+    end
+end
+
+socket_methods.write = function(self, octets, timeout)
+    if timeout == nil then
+        timeout = TIMEOUT_INFINITY
+    end
+
+    local started = box.time()
+    while timeout > 0 and self:writable(timeout) do
+        timeout = timeout - ( box.time() - started )
+
+        local written = self:syswrite(octets)
+        if written == nil then
+            if self:errno() ~= box.errno.EAGAIN then
+                return false
+            end
+        end
+        
+        if written == string.len(octets) then
+            return true
+        end
+        if written > 0 then
+            octets = string.sub(octets, written + 1)
+        end
+    end
+end
+
+socket_methods.send = function(self, octets, flags)
+    local iflags = get_iflags(box.socket.internal.SEND_FLAGS, flags)
+
+    self._errno = nil
+    local res = ffi.C.send(self.fh, octets, string.len(octets), iflags)
+    if res == -1 then
+        self._errno = box.errno()
+        return false
+    end
+    return true
+end
+
+socket_methods.recv = function(self, size, flags)
+    local iflags = get_iflags(box.socket.internal.SEND_FLAGS, flags)
+    if iflags == nil then
+        self._errno = box.errno.EINVAL
+        return nil
+    end
+
+    if size == nil then
+        size = 512
+    end
+    self._errno = nil
+    local buf = ffi.new("char[?]", size)
+
+    local res = ffi.C.recv(self.fh, buf, size, iflags)
+
+    if res == -1 then
+        self._errno = box.errno()
+        return nil
+    end
+    return ffi.string(buf, res)
+end
+
+
+socket_methods.recvfrom = function(self, size, flags)
+    local iflags = get_iflags(box.socket.internal.SEND_FLAGS, flags)
+    if iflags == nil then
+        self._errno = box.errno.EINVAL
+        return nil
+    end
+    self._errno = nil
+    local res, from = box.socket.internal.recvfrom(self.fh, size, iflags)
+    if res == nil then
+        self._errno = box.errno()
+        return nil
+    end
+    return res, from
+end
+
+socket_methods.sendto = function(self, host, port, octets, flags)
+    local iflags = get_iflags(box.socket.internal.SEND_FLAGS, flags)
+
+    if iflags == nil then
+        self._errno = box.errno.EINVAL
+        return nil
+    end
+
+    self._errno = nil
+    if octets == nil or octets == '' then
+        return true
+    end
+
+    host = tostring(host)
+    port = tostring(port)
+    octets = tostring(octets)
+
+    local res = ffi.C.bsdsocket_sendto(self.fh, host, port,
+        octets, string.len(octets), iflags)
+
+    if res < 0 then
+        self._errno = box.errno()
+        return false
+    end
+    return true
+end
+
+
+local function create_socket(self, domain, stype, proto)
+
+    self._errno = nil
+
+    local idomain = get_ivalue(box.socket.internal.DOMAIN, domain)
+    if idomain == nil then
+        self._errno = box.errno.EINVAL
+        return nil
+    end
+
+    local itype = get_ivalue(box.socket.internal.SO_TYPE, stype)
+    if itype == nil then
+        self._errno = box.errno.EINVAL
+        return nil
+    end
+
+    local iproto = ffi.C.bsdsocket_protocol(proto)
+    if iproto == -1 then
+        self._errno = box.errno()
+        return nil
+    end
+
+
+    local fh = ffi.C.socket(idomain, itype, iproto)
+    if fh < 0 then
+        self._errno = box.errno()
+        return nil
+    end
+
+    local socket = { fh = fh }
+    setmetatable(socket, box.socket.internal.socket_mt)
+    return socket
+end
+
+
+local function getaddrinfo(host, port, timeout, opts)
+    local self = box.socket
+
+    if type(timeout) == 'table' and opts == nil then
+        opts = timeout
+        timeout = TIMEOUT_INFINITY
+    elseif timeout == nil then
+        timeout = TIMEOUT_INFINITY
+    end
+    if port == nil then
+        port = 0
+    end
+    local ga_opts = {}
+    if opts ~= nil then
+        if opts.type ~= nil then
+            local itype = get_ivalue(box.socket.internal.SO_TYPE, opts.type)
+            if itype == nil then
+                self._errno = box.errno.EINVAL
+                return nil
+            end
+            ga_opts.type = itype
+        end
+
+        if opts.family ~= nil then
+            local ifamily = get_ivalue(box.socket.internal.DOMAIN, opts.family)
+            if ifamily == nil then
+                self._errno = box.errno.EINVAL
+                return nil
+            end
+            ga_opts.family = ifamily
+        end
+
+        if opts.protocol ~= nil then
+            local iproto = ffi.C.bsdsocket_protocol(opts.protocol)
+            if iproto == -1 then
+                self._errno = box.errno()
+                return nil
+            end
+            ga_opts.protocol = iproto
+        end
+
+        if opts.flags ~= nil then
+            ga_opts.flags =
+                get_iflags(box.socket.internal.AI_FLAGS, opts.flags)
+            if ga_opts.flags == nil then
+                self._errno = box.errno()
+                return nil
+            end
+        end
+
+    end
+    
+    local r = box.socket.internal.getaddrinfo(host, port, timeout, ga_opts)
+    if r == nil then
+        self._errno = box.errno()
+    else
+        self._errno = nil
+    end
+    return r
+end
+
+socket_methods.name = function(self)
+    local aka = box.socket.internal.name(self.fh)
+    if aka == nil then
+        self._errno = box.errno()
+        return nil
+    end
+    return aka
+end
+
+if type(box.socket) == nil then
+    box.socket = {}
+end
+
+box.socket.internal = {
+    socket_mt   = {
+        __index     = socket_methods,
+        __tostring  = function(self)
+            local name = sprintf("fd %d", self.fh)
+            local aka = box.socket.internal.name(self.fh)
+            if aka ~= nil then
+                name = sprintf("%s, aka %s:%s", name, aka.host, aka.port)
+            end
+            local peer = box.socket.internal.peer(self.fh)
+            if peer ~= nil then
+                name = sprintf("%s, peer %s:%s", name, peer.host, peer.port)
+            end
+            return name
+        end
+    },
+    
+}
+
+setmetatable(box.socket, {
+    __call = create_socket,
+    __index = {
+        errno = errno,
+        error = errstr,
+        getaddrinfo = getaddrinfo,
+    }
+})
+
+end
diff --git a/src/lua/errno.c b/src/lua/errno.c
new file mode 100644
index 0000000000000000000000000000000000000000..fe60812f5f684ebae011763f10ed7be70ef06ec0
--- /dev/null
+++ b/src/lua/errno.c
@@ -0,0 +1,155 @@
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "errno.h"
+#include <errno.h>
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#include <say.h>
+
+extern char errno_lua[];
+
+
+void
+tarantool_lua_errno_init(struct lua_State *L)
+{
+	static const struct { char name[32]; int value; } elist[] = {
+		{ "E2BIG",		E2BIG		},
+		{ "EACCES",		EACCES		},
+		{ "EADDRINUSE",		EADDRINUSE	},
+		{ "EADDRNOTAVAIL",	EADDRNOTAVAIL	},
+		{ "EAFNOSUPPORT",	EAFNOSUPPORT	},
+		{ "EAGAIN",		EAGAIN		},
+		{ "EALREADY",		EALREADY	},
+		{ "EBADF",		EBADF		},
+		{ "EBADMSG",		EBADMSG		},
+		{ "EBUSY",		EBUSY		},
+		{ "ECANCELED",		ECANCELED	},
+		{ "ECHILD",		ECHILD		},
+		{ "ECONNABORTED",	ECONNABORTED	},
+		{ "ECONNREFUSED",	ECONNREFUSED	},
+		{ "ECONNRESET",		ECONNRESET	},
+		{ "EDEADLK",		EDEADLK		},
+		{ "EDESTADDRREQ",	EDESTADDRREQ	},
+		{ "EDOM",		EDOM		},
+		{ "EDQUOT",		EDQUOT		},
+		{ "EEXIST",		EEXIST		},
+		{ "EFAULT",		EFAULT		},
+		{ "EFBIG",		EFBIG		},
+		{ "EHOSTUNREACH",	EHOSTUNREACH	},
+		{ "EIDRM",		EIDRM		},
+		{ "EILSEQ",		EILSEQ		},
+		{ "EINPROGRESS",	EINPROGRESS	},
+		{ "EINTR",		EINTR		},
+		{ "EINVAL",		EINVAL		},
+		{ "EIO",		EIO		},
+		{ "EISCONN",		EISCONN		},
+		{ "EISDIR",		EISDIR		},
+		{ "ELOOP",		ELOOP		},
+		{ "EMFILE",		EMFILE		},
+		{ "EMLINK",		EMLINK		},
+		{ "EMSGSIZE",		EMSGSIZE	},
+		{ "EMULTIHOP",		EMULTIHOP	},
+		{ "ENAMETOOLONG",	ENAMETOOLONG	},
+		{ "ENETDOWN",		ENETDOWN	},
+		{ "ENETRESET",		ENETRESET	},
+		{ "ENETUNREACH",	ENETUNREACH	},
+		{ "ENFILE",		ENFILE		},
+		{ "ENOBUFS",		ENOBUFS		},
+		{ "ENODATA",		ENODATA		},
+		{ "ENODEV",		ENODEV		},
+		{ "ENOENT",		ENOENT		},
+		{ "ENOEXEC",		ENOEXEC		},
+		{ "ENOLCK",		ENOLCK		},
+		{ "ENOLINK",		ENOLINK		},
+		{ "ENOMEM",		ENOMEM		},
+		{ "ENOMSG",		ENOMSG		},
+		{ "ENOPROTOOPT",	ENOPROTOOPT	},
+		{ "ENOSPC",		ENOSPC		},
+		{ "ENOSR",		ENOSR		},
+		{ "ENOSTR",		ENOSTR		},
+		{ "ENOSYS",		ENOSYS		},
+		{ "ENOTCONN",		ENOTCONN	},
+		{ "ENOTDIR",		ENOTDIR		},
+		{ "ENOTEMPTY",		ENOTEMPTY	},
+		{ "ENOTSOCK",		ENOTSOCK	},
+		{ "ENOTSUP",		ENOTSUP		},
+		{ "ENOTTY",		ENOTTY		},
+		{ "ENXIO",		ENXIO		},
+		{ "EOPNOTSUPP",		EOPNOTSUPP	},
+		{ "EOVERFLOW",		EOVERFLOW	},
+		{ "EPERM",		EPERM		},
+		{ "EPIPE",		EPIPE		},
+		{ "EPROTO",		EPROTO		},
+		{ "EPROTONOSUPPORT",	EPROTONOSUPPORT	},
+		{ "EPROTOTYPE",		EPROTOTYPE	},
+		{ "ERANGE",		ERANGE		},
+		{ "EROFS",		EROFS		},
+		{ "ESPIPE",		ESPIPE		},
+		{ "ESRCH",		ESRCH		},
+		{ "ESTALE",		ESTALE		},
+		{ "ETIME",		ETIME		},
+		{ "ETIMEDOUT",		ETIMEDOUT	},
+		{ "ETXTBSY",		ETXTBSY		},
+		{ "EWOULDBLOCK",	EWOULDBLOCK	},
+		{ "EXDEV",		EXDEV		},
+		{ "",			0		}
+	};
+
+	int top = lua_gettop(L);
+	lua_getfield(L, LUA_GLOBALSINDEX, "box");
+	lua_pushliteral(L, "errno");
+	lua_newtable(L);
+	for (int i = 0; elist[i].name[0]; i++) {
+		lua_pushstring(L, elist[i].name);
+		lua_pushinteger(L, elist[i].value);
+		lua_rawset(L, -3);
+	}
+	lua_rawset(L, -3);
+
+	if (luaL_dostring(L, errno_lua))
+		panic("Error loading Lua source (internal)/errno.lua: %s",
+			      lua_tostring(L, -1));
+
+	lua_settop(L, top);
+}
+
+int
+errno_get(void)
+{
+	return errno;
+}
+
+int
+errno_set(int new_errno)
+{
+	errno = new_errno;
+	return errno;
+}
diff --git a/src/lua/errno.h b/src/lua/errno.h
new file mode 100644
index 0000000000000000000000000000000000000000..1bfade6e0f32a7aa317250de011ba00c55cb9ea8
--- /dev/null
+++ b/src/lua/errno.h
@@ -0,0 +1,44 @@
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef TARANTOOL_LUA_ERRNO_H_INCLUDED
+#define TARANTOOL_LUA_ERRNO_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+struct lua_State;
+void tarantool_lua_errno_init(struct lua_State *L);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* TARANTOOL_LUA_ERRNO_H_INCLUDED */
+
diff --git a/src/lua/errno.lua b/src/lua/errno.lua
new file mode 100644
index 0000000000000000000000000000000000000000..c12da271c07e112296b0d5c6c206fdae0eaffc20
--- /dev/null
+++ b/src/lua/errno.lua
@@ -0,0 +1,28 @@
+-- errno.lua (internal file)
+
+do
+
+local ffi = require 'ffi'
+
+ffi.cdef[[
+    char *strerror(int errnum);
+    int errno_get();
+    int errno_set(int new_errno);
+]]
+
+box.errno.strerror = function(errno)
+    return ffi.string(ffi.C.strerror(tonumber(errno)))
+end
+
+setmetatable(box.errno, {
+    __newindex  = function() error("Can't create new errno constants") end,
+    __call = function(self, new_errno)
+        local res
+        if new_errno then
+            return ffi.C.errno_set(new_errno)
+        end
+        return ffi.C.errno_get()
+    end
+})
+
+end
diff --git a/src/lua/init.cc b/src/lua/init.cc
index f50877eaf63d02f86889ca5d86044a5c4e896119..aacb9de91ad2e2db42442ce36691ff90fccbb5fd 100644
--- a/src/lua/init.cc
+++ b/src/lua/init.cc
@@ -61,6 +61,8 @@ extern "C" {
 #include <dirent.h>
 #include <stdio.h>
 #include "scoped_guard.h"
+#include "errno.h"
+#include "../bsdsocket.h"
 
 extern "C" {
 #include <cfg/tarantool_box_cfg.h>
@@ -76,8 +78,11 @@ struct lua_State *tarantool_L;
 /* contents of src/lua/ files */
 extern char uuid_lua[];
 extern char session_lua[];
-extern char digest_lua[];
-static const char *lua_sources[] = { uuid_lua, digest_lua, session_lua, NULL };
+static const char *lua_sources[] = {
+	uuid_lua,
+	session_lua,
+	NULL
+};
 
 /**
  * Remember the output of the administrative console in the
@@ -1230,12 +1235,14 @@ tarantool_lua_init()
 	lua_register(L, "pcall", lbox_pcall);
 	lua_register(L, "tonumber64", lbox_tonumber64);
 
+	tarantool_lua_errno_init(L);
 	tarantool_lua_cjson_init(L);
 	tarantool_lua_info_init(L);
 	tarantool_lua_slab_init(L);
 	tarantool_lua_stat_init(L);
 	tarantool_lua_ipc_init(L);
 	tarantool_lua_socket_init(L);
+	tarantool_lua_bsdsocket_init(L);
 	tarantool_lua_session_init(L);
 	tarantool_lua_error_init(L);
 
@@ -1246,6 +1253,7 @@ tarantool_lua_init()
 			      *s, lua_tostring(L, -1));
 	}
 
+
 	mod_lua_init(L);
 
 	/* clear possible left-overs of init */
diff --git a/test/box/bsdsocket.result b/test/box/bsdsocket.result
new file mode 100644
index 0000000000000000000000000000000000000000..8eebd881aecd5f6b3287b2d3371a8e05a70895a4
--- /dev/null
+++ b/test/box/bsdsocket.result
@@ -0,0 +1,719 @@
+bsdsocket tests
+lua type(box.socket)
+---
+ - table
+...
+lua box.socket:errno()
+---
+ - 0
+...
+lua type(box.socket:error())
+---
+ - nil
+...
+lua box.socket('PF_INET', 'SOCK_STREAM', 'tcp121222')
+---
+ - nil
+...
+lua box.socket:errno() ~= 0
+---
+ - true
+...
+lua type(box.socket:error())
+---
+ - string
+...
+lua s = box.socket('PF_INET', 'SOCK_STREAM', 'tcp')
+---
+...
+lua s:wait(.01)
+---
+ - RW
+...
+lua type(s)
+---
+ - table
+...
+lua s
+---
+ - fd 10, aka 0.0.0.0:0
+...
+lua s:errno()
+---
+ - 0
+...
+lua type(s:error())
+---
+ - nil
+...
+lua s:sysconnect('127.0.0.1', box.cfg.primary_port)
+---
+ - true
+...
+lua s:nonblock(true)
+---
+ - true
+...
+lua s:nonblock()
+---
+ - true
+...
+lua s:nonblock(false)
+---
+ - false
+...
+lua s:nonblock()
+---
+ - false
+...
+lua s:nonblock(true)
+---
+ - true
+...
+lua s:readable(.01)
+---
+ - false
+...
+lua s:wait(.01)
+---
+ - W
+...
+lua s:readable(0)
+---
+ - false
+...
+lua s:errno() > 0
+---
+ - true
+...
+lua s:error()
+---
+ - Connection timed out
+...
+lua s:writable(.00000000000001)
+---
+ - true
+...
+lua s:writable(0)
+---
+ - true
+...
+lua s:wait(.01)
+---
+ - W
+...
+lua s:syswrite(box.pack('iii', 65280, 0, 12334))
+---
+ - 12
+...
+lua s:readable(1)
+---
+ - true
+...
+lua s:wait(.01)
+---
+ - RW
+...
+lua box.unpack('iii', s:sysread(4096))
+---
+ - 65280
+ - 0
+ - 12334
+...
+lua s:syswrite(box.pack('iii', 65280, 0, 12335))
+---
+ - 12
+...
+lua s:readable(1)
+---
+ - true
+...
+lua string.len(s:sysread(4096))
+---
+ - 12
+...
+lua s:close()
+---
+ - true
+...
+lua s = box.socket('PF_INET', 'SOCK_STREAM', 'tcp')
+---
+...
+lua s:setsockopt('SOL_SOCKET', 'SO_REUSEADDR', true)
+---
+ - true
+...
+lua s:error()
+---
+ - nil
+...
+lua s:nonblock(true)
+---
+ - true
+...
+lua s:bind('127.0.0.1', 3457)
+---
+ - true
+...
+lua s:error()
+---
+ - nil
+...
+lua s:listen(128)
+---
+ - true
+...
+lua sevres = {}
+---
+...
+lua type(box.fiber.wrap(function() s:readable() do local sc = s:accept() table.insert(sevres, sc) sc:syswrite('ok') sc:close() end end))
+---
+ - userdata
+...
+lua #sevres
+---
+ - 0
+...
+lua sc = box.socket('PF_INET', 'SOCK_STREAM', 'tcp')
+---
+...
+lua sc:sysconnect('127.0.0.1', 3457)
+---
+ - true
+...
+lua sc:nonblock(true)
+---
+ - true
+...
+lua sc:readable(.5)
+---
+ - true
+...
+lua sc:sysread(4096)
+---
+ - ok
+...
+lua string.match(tostring(sc), ', peer') ~= nil
+---
+ - true
+...
+lua #sevres
+---
+ - 1
+...
+lua sevres[1].host
+---
+ - nil
+...
+lua s:setsockopt('SOL_SOCKET', 'SO_BROADCAST', false)
+---
+ - true
+...
+lua s:getsockopt('SOL_SOCKET', 'SO_TYPE')
+---
+ - 1
+...
+lua s:error()
+---
+ - nil
+...
+lua s:setsockopt('SOL_SOCKET', 'SO_BSDCOMPAT', false)
+---
+ - true
+...
+lua s:setsockopt('SOL_SOCKET', 'SO_DEBUG', false)
+---
+ - true
+...
+lua s:getsockopt('SOL_SOCKET', 'SO_DEBUG')
+---
+ - 0
+...
+lua s:setsockopt('SOL_SOCKET', 'SO_ACCEPTCONN', 1)
+---
+error: '[string "-- bsdsocket.lua (internal file)..."]:282: Socket option SO_ACCEPTCONN is read only'
+...
+lua s:getsockopt('SOL_SOCKET', 'SO_RCVBUF') > 32
+---
+ - true
+...
+lua s:error()
+---
+ - nil
+...
+lua s:linger()
+---
+ - false
+ - 0
+...
+lua s:linger(true, 1)
+---
+ - true
+ - 1
+...
+lua s:linger()
+---
+ - true
+ - 1
+...
+lua s:linger(false, 1)
+---
+ - false
+ - 1
+...
+lua s:linger()
+---
+ - false
+ - 1
+...
+lua s:shutdown('R')
+---
+ - true
+...
+lua s:close()
+---
+ - true
+...
+lua s = box.socket('PF_INET', 'SOCK_STREAM', 'tcp')
+---
+...
+lua s:nonblock(true)
+---
+ - true
+...
+lua s:setsockopt('SOL_SOCKET', 'SO_REUSEADDR', true)
+---
+ - true
+...
+lua s:bind('127.0.0.1', 3457)
+---
+ - true
+...
+lua s:listen(128)
+---
+ - true
+...
+lua sc = box.socket('PF_INET', 'SOCK_STREAM', 'tcp')
+---
+...
+lua sc:nonblock(true)
+---
+ - true
+...
+lua sc:writable()
+---
+ - true
+...
+lua sc:readable()
+---
+ - true
+...
+lua sc:sysconnect('127.0.0.1', 3457)
+---
+ - true
+...
+lua sc:writable(10)
+---
+ - true
+...
+lua sc:write('Hello, world')
+---
+ - true
+...
+lua sa = s:accept()
+---
+...
+lua sa:nonblock(1)
+---
+ - true
+...
+lua sa:read(8)
+---
+ - Hello, w
+...
+lua sa:read(3)
+---
+ - orl
+...
+lua sc:writable()
+---
+ - true
+...
+lua sc:write(', again')
+---
+ - true
+...
+lua sa:read(8)
+---
+ - d, again
+...
+lua sa:error()
+---
+ - nil
+...
+lua string.len(sa:read(0))
+---
+ - 0
+...
+lua type(sa:read(0))
+---
+ - string
+...
+lua sa:read(1, .01)
+---
+ - nil
+...
+lua sc:writable()
+---
+ - true
+...
+lua sc:send('abc')
+---
+ - true
+...
+lua sa:read(3)
+---
+ - abc
+...
+lua sc:send('Hello')
+---
+ - true
+...
+lua sa:readable()
+---
+ - true
+...
+lua sa:recv()
+---
+ - Hello
+...
+lua sa:recv()
+---
+ - nil
+...
+lua sc:send('Hello')
+---
+ - true
+...
+lua sc:send(', world')
+---
+ - true
+...
+lua sc:send("\nnew line")
+---
+ - true
+...
+lua sa:readline({'\n'}, 1)
+---
+ - Hello, world
+
+...
+lua sa:readline(1, {'ine'}, 1)
+---
+ - n
+...
+lua sa:readline({'ine'}, 1)
+---
+ - ew line
+...
+lua sa:readline({'ine'}, 0.1)
+---
+ - nil
+...
+lua sc:send('Hello, world')
+---
+ - true
+...
+lua sa:readline({','}, 1)
+---
+ - Hello,
+...
+lua sc:shutdown('W')
+---
+ - true
+...
+lua sa:read(100, 1)
+---
+ -  world
+...
+lua sa:read(100, 1)
+---
+ - 
+...
+lua sa:close()
+---
+ - true
+...
+lua sc:close()
+---
+ - true
+...
+lua s = box.socket('PF_UNIX', 'SOCK_STREAM', 'ip')
+---
+...
+lua s:setsockopt('SOL_SOCKET', 'SO_REUSEADDR', true)
+---
+ - true
+...
+lua s ~= nil
+---
+ - true
+...
+lua s:nonblock()
+---
+ - false
+...
+lua s:nonblock(true)
+---
+ - true
+...
+lua s:nonblock()
+---
+ - true
+...
+lua s:bind('unix/', '/tmp/tarantool-test-socket')
+---
+ - true
+...
+lua tostring(sc)
+---
+ - fd 12, aka unix/:/tmp/tarantool-test-socket
+...
+lua s:listen(1234)
+---
+ - true
+...
+lua sc = box.socket('PF_UNIX', 'SOCK_STREAM', 'ip')
+---
+...
+lua sc:nonblock(true)
+---
+ - true
+...
+lua sc:sysconnect('unix/', '/tmp/tarantool-test-socket')
+---
+ - true
+...
+lua sc:error()
+---
+ - nil
+...
+lua s:readable()
+---
+ - true
+...
+lua sa = s:accept()
+---
+...
+lua sa:nonblock(true)
+---
+ - true
+...
+lua sa:send('Hello, world')
+---
+ - true
+...
+lua sc:recv()
+---
+ - Hello, world
+...
+lua sc:close()
+---
+ - true
+...
+lua sa:close()
+---
+ - true
+...
+lua s:close()
+---
+ - true
+...
+lua function aexitst(ai, host, port) for i, a in pairs(ai) do if a.host == host and a.port == port then return true end end return false end
+---
+...
+lua aexitst( box.socket.getaddrinfo('localhost', 'http', {  protocol = 'tcp', type = 'SOCK_STREAM'}), '127.0.0.1', 80 )
+---
+ - true
+...
+lua #(box.socket.getaddrinfo('mail.ru', 'http', {})) > 0
+---
+ - true
+...
+lua #(box.socket.getaddrinfo('mail12211alklkl.ru', 'http', {})) == 0
+---
+ - true
+...
+lua sc = box.socket('PF_INET', 'SOCK_STREAM', 'tcp')
+---
+...
+lua tostring(sc)
+---
+ - fd 12, aka 0.0.0.0:0
+...
+lua sc:getsockopt('SOL_SOCKET', 'SO_ERROR')
+---
+ - 0
+...
+lua sc:nonblock(true)
+---
+ - true
+...
+lua sc:readable()
+---
+ - true
+...
+lua sc:sysconnect('127.0.0.1', 3458)
+---
+ - true
+...
+lua string.match(tostring(sc), ', peer') == nil
+---
+ - true
+...
+lua sc:writable()
+---
+ - true
+...
+lua string.match(tostring(sc), ', peer') == nil
+---
+ - true
+...
+lua box.errno.strerror(sc:getsockopt('SOL_SOCKET', 'SO_ERROR'))
+---
+ - Connection refused
+...
+lua box.cjson.encode(box.socket.getaddrinfo('ya.ru', '80', { flags = { 'AI_NUMERICSERV', 'AI_NUMERICHOST', } }))
+---
+ - {}
+...
+lua sc = box.socket('AF_INET', 'SOCK_STREAM', 'tcp')
+---
+...
+lua box.cjson.encode(sc:name())
+---
+ - {"host":"0.0.0.0","family":"AF_INET","type":"SOCK_STREAM","protocol":"tcp","port":0}
+...
+lua sc:nonblock(true)
+---
+ - true
+...
+lua sc:close()
+---
+ - true
+...
+lua s = box.socket('AF_INET', 'SOCK_DGRAM', 'udp')
+---
+...
+lua s:bind('127.0.0.1', 3548)
+---
+ - true
+...
+lua sc = box.socket('AF_INET', 'SOCK_DGRAM', 'udp')
+---
+...
+lua sc:sendto('127.0.0.1', 3548, 'Hello, world')
+---
+ - true
+...
+lua s:readable(10)
+---
+ - true
+...
+lua s:recv(4096)
+---
+ - Hello, world
+...
+lua sc:sendto('127.0.0.1', 3548, 'Hello, world, 2')
+---
+ - true
+...
+lua s:readable(10)
+---
+ - true
+...
+lua local d, from = s:recvfrom(4096) print(' - ', from.port > 0) from.port = 'Random port' return box.cjson.encode{d, from}
+---
+ - true
+ - ["Hello, world, 2",{"host":"127.0.0.1","family":"AF_INET","port":"Random port"}]
+...
+lua s:close()
+---
+ - true
+...
+lua sc:close()
+---
+ - true
+...
+lua s = box.socket('AF_INET', 'SOCK_DGRAM', 'udp')
+---
+...
+lua s:nonblock(true)
+---
+ - true
+...
+lua s:bind('127.0.0.1')
+---
+ - true
+...
+lua s:name().port > 0
+---
+ - true
+...
+lua sc = box.socket('AF_INET', 'SOCK_DGRAM', 'udp')
+---
+...
+lua sc:nonblock(true)
+---
+ - true
+...
+lua sc:sendto('127.0.0.1', s:name().port)
+---
+ - true
+...
+lua sc:sendto('127.0.0.1', s:name().port, 'Hello, World!')
+---
+ - true
+...
+lua s:readable(1)
+---
+ - true
+...
+lua data, from = s:recvfrom(10)
+---
+...
+lua data
+---
+ - Hello, Wor
+...
+lua s:sendto(from.host, from.port, 'Hello, hello!')
+---
+ - true
+...
+lua sc:readable(1)
+---
+ - true
+...
+lua data_r, from_r = sc:recvfrom(4096)
+---
+...
+lua data_r
+---
+ - Hello, hello!
+...
+lua from_r.host
+---
+ - 127.0.0.1
+...
+lua from_r.port == s:name().port
+---
+ - true
+...
+lua s:close()
+---
+ - true
+...
+lua sc:close()
+---
+ - true
+...
diff --git a/test/box/bsdsocket.test b/test/box/bsdsocket.test
new file mode 100644
index 0000000000000000000000000000000000000000..93287d3514550ffb9e904211b8a24d480c51c25c
--- /dev/null
+++ b/test/box/bsdsocket.test
@@ -0,0 +1,245 @@
+# encoding: tarantool
+# 
+
+import os
+import os.path
+
+print("bsdsocket tests")
+exec admin "lua type(box.socket)"
+exec admin "lua box.socket:errno()"
+exec admin "lua type(box.socket:error())"
+exec admin "lua box.socket('PF_INET', 'SOCK_STREAM', 'tcp121222')"
+exec admin "lua box.socket:errno() ~= 0"
+exec admin "lua type(box.socket:error())"
+
+exec admin "lua s = box.socket('PF_INET', 'SOCK_STREAM', 'tcp')"
+exec admin "lua s:wait(.01)"
+exec admin "lua type(s)"
+exec admin "lua s"
+exec admin "lua s:errno()"
+exec admin "lua type(s:error())"
+
+
+exec admin "lua s:sysconnect('127.0.0.1', box.cfg.primary_port)"
+exec admin "lua s:nonblock(true)"
+exec admin "lua s:nonblock()"
+exec admin "lua s:nonblock(false)"
+exec admin "lua s:nonblock()"
+exec admin "lua s:nonblock(true)"
+
+exec admin "lua s:readable(.01)"
+exec admin "lua s:wait(.01)"
+exec admin "lua s:readable(0)"
+exec admin "lua s:errno() > 0"
+exec admin "lua s:error()"
+exec admin "lua s:writable(.00000000000001)"
+exec admin "lua s:writable(0)"
+exec admin "lua s:wait(.01)"
+
+exec admin "lua s:syswrite(box.pack('iii', 65280, 0, 12334))"
+exec admin "lua s:readable(1)"
+exec admin "lua s:wait(.01)"
+exec admin "lua box.unpack('iii', s:sysread(4096))"
+
+exec admin "lua s:syswrite(box.pack('iii', 65280, 0, 12335))"
+exec admin "lua s:readable(1)"
+exec admin "lua string.len(s:sysread(4096))"
+exec admin "lua s:close()"
+
+
+exec admin "lua s = box.socket('PF_INET', 'SOCK_STREAM', 'tcp')"
+exec admin "lua s:setsockopt('SOL_SOCKET', 'SO_REUSEADDR', true)"
+exec admin "lua s:error()"
+exec admin "lua s:nonblock(true)"
+exec admin "lua s:bind('127.0.0.1', 3457)"
+exec admin "lua s:error()"
+exec admin "lua s:listen(128)"
+exec admin "lua sevres = {}"
+exec admin "lua type(box.fiber.wrap(function() s:readable() do local sc = s:accept() table.insert(sevres, sc) sc:syswrite('ok') sc:close() end end))"
+exec admin "lua #sevres"
+
+
+exec admin "lua sc = box.socket('PF_INET', 'SOCK_STREAM', 'tcp')"
+exec admin "lua sc:sysconnect('127.0.0.1', 3457)"
+exec admin "lua sc:nonblock(true)"
+exec admin "lua sc:readable(.5)"
+exec admin "lua sc:sysread(4096)"
+exec admin "lua string.match(tostring(sc), ', peer') ~= nil"
+exec admin "lua #sevres"
+exec admin "lua sevres[1].host"
+
+exec admin "lua s:setsockopt('SOL_SOCKET', 'SO_BROADCAST', false)"
+exec admin "lua s:getsockopt('SOL_SOCKET', 'SO_TYPE')"
+exec admin "lua s:error()"
+exec admin "lua s:setsockopt('SOL_SOCKET', 'SO_BSDCOMPAT', false)"
+exec admin "lua s:setsockopt('SOL_SOCKET', 'SO_DEBUG', false)"
+exec admin "lua s:getsockopt('SOL_SOCKET', 'SO_DEBUG')"
+exec admin "lua s:setsockopt('SOL_SOCKET', 'SO_ACCEPTCONN', 1)"
+exec admin "lua s:getsockopt('SOL_SOCKET', 'SO_RCVBUF') > 32"
+exec admin "lua s:error()"
+
+exec admin "lua s:linger()"
+exec admin "lua s:linger(true, 1)"
+exec admin "lua s:linger()"
+exec admin "lua s:linger(false, 1)"
+exec admin "lua s:linger()"
+exec admin "lua s:shutdown('R')"
+exec admin "lua s:close()"
+
+
+exec admin "lua s = box.socket('PF_INET', 'SOCK_STREAM', 'tcp')"
+exec admin "lua s:nonblock(true)"
+exec admin "lua s:setsockopt('SOL_SOCKET', 'SO_REUSEADDR', true)"
+exec admin "lua s:bind('127.0.0.1', 3457)"
+exec admin "lua s:listen(128)"
+
+exec admin "lua sc = box.socket('PF_INET', 'SOCK_STREAM', 'tcp')"
+exec admin "lua sc:nonblock(true)"
+
+exec admin "lua sc:writable()"
+exec admin "lua sc:readable()"
+exec admin "lua sc:sysconnect('127.0.0.1', 3457)"
+exec admin "lua sc:writable(10)"
+exec admin "lua sc:write('Hello, world')"
+
+exec admin "lua sa = s:accept()"
+exec admin "lua sa:nonblock(1)"
+exec admin "lua sa:read(8)"
+exec admin "lua sa:read(3)"
+exec admin "lua sc:writable()"
+exec admin "lua sc:write(', again')"
+exec admin "lua sa:read(8)"
+exec admin "lua sa:error()"
+exec admin "lua string.len(sa:read(0))"
+exec admin "lua type(sa:read(0))"
+exec admin "lua sa:read(1, .01)"
+exec admin "lua sc:writable()"
+
+exec admin "lua sc:send('abc')"
+exec admin "lua sa:read(3)"
+
+exec admin "lua sc:send('Hello')"
+exec admin "lua sa:readable()"
+exec admin "lua sa:recv()"
+exec admin "lua sa:recv()"
+
+
+exec admin "lua sc:send('Hello')"
+exec admin "lua sc:send(', world')"
+exec admin "lua sc:send(\"\\nnew line\")"
+exec admin "lua sa:readline({'\\n'}, 1)"
+exec admin "lua sa:readline(1, {'ine'}, 1)"
+exec admin "lua sa:readline({'ine'}, 1)"
+exec admin "lua sa:readline({'ine'}, 0.1)"
+
+exec admin "lua sc:send('Hello, world')"
+exec admin "lua sa:readline({','}, 1)"
+exec admin "lua sc:shutdown('W')"
+exec admin "lua sa:read(100, 1)"
+exec admin "lua sa:read(100, 1)"
+exec admin "lua sa:close()"
+exec admin "lua sc:close()"
+
+
+exec admin "lua s = box.socket('PF_UNIX', 'SOCK_STREAM', 'ip')"
+exec admin "lua s:setsockopt('SOL_SOCKET', 'SO_REUSEADDR', true)"
+exec admin "lua s ~= nil"
+exec admin "lua s:nonblock()"
+exec admin "lua s:nonblock(true)"
+exec admin "lua s:nonblock()"
+if os.path.exists('/tmp/tarantool-test-socket'):
+        os.unlink('/tmp/tarantool-test-socket')
+exec admin "lua s:bind('unix/', '/tmp/tarantool-test-socket')"
+exec admin "lua tostring(sc)"
+exec admin "lua s:listen(1234)"
+
+exec admin "lua sc = box.socket('PF_UNIX', 'SOCK_STREAM', 'ip')"
+exec admin "lua sc:nonblock(true)"
+exec admin "lua sc:sysconnect('unix/', '/tmp/tarantool-test-socket')"
+exec admin "lua sc:error()"
+
+
+exec admin "lua s:readable()"
+exec admin "lua sa = s:accept()"
+exec admin "lua sa:nonblock(true)"
+exec admin "lua sa:send('Hello, world')"
+exec admin "lua sc:recv()"
+
+
+
+exec admin "lua sc:close()"
+exec admin "lua sa:close()"
+exec admin "lua s:close()"
+
+if os.path.exists('/tmp/tarantool-test-socket'):
+        os.unlink('/tmp/tarantool-test-socket')
+
+exec admin "lua function aexitst(ai, host, port) for i, a in pairs(ai) do if a.host == host and a.port == port then return true end end return false end"
+
+exec admin "lua aexitst( box.socket.getaddrinfo('localhost', 'http', {  protocol = 'tcp', type = 'SOCK_STREAM'}), '127.0.0.1', 80 )"
+exec admin "lua #(box.socket.getaddrinfo('mail.ru', 'http', {})) > 0"
+exec admin "lua #(box.socket.getaddrinfo('mail12211alklkl.ru', 'http', {})) == 0"
+
+
+
+exec admin "lua sc = box.socket('PF_INET', 'SOCK_STREAM', 'tcp')"
+exec admin "lua tostring(sc)"
+exec admin "lua sc:getsockopt('SOL_SOCKET', 'SO_ERROR')"
+exec admin "lua sc:nonblock(true)"
+exec admin "lua sc:readable()"
+exec admin "lua sc:sysconnect('127.0.0.1', 3458)"
+exec admin "lua string.match(tostring(sc), ', peer') == nil"
+exec admin "lua sc:writable()"
+exec admin "lua string.match(tostring(sc), ', peer') == nil"
+exec admin "lua box.errno.strerror(sc:getsockopt('SOL_SOCKET', 'SO_ERROR'))"
+
+exec admin "lua box.cjson.encode(box.socket.getaddrinfo('ya.ru', '80', { flags = { 'AI_NUMERICSERV', 'AI_NUMERICHOST', } }))"
+
+
+exec admin "lua sc = box.socket('AF_INET', 'SOCK_STREAM', 'tcp')"
+exec admin "lua box.cjson.encode(sc:name())"
+exec admin "lua sc:nonblock(true)"
+exec admin "lua sc:close()"
+
+exec admin "lua s = box.socket('AF_INET', 'SOCK_DGRAM', 'udp')"
+exec admin "lua s:bind('127.0.0.1', 3548)"
+exec admin "lua sc = box.socket('AF_INET', 'SOCK_DGRAM', 'udp')"
+exec admin "lua sc:sendto('127.0.0.1', 3548, 'Hello, world')"
+exec admin "lua s:readable(10)"
+exec admin "lua s:recv(4096)"
+
+exec admin "lua sc:sendto('127.0.0.1', 3548, 'Hello, world, 2')"
+exec admin "lua s:readable(10)"
+exec admin "lua local d, from = s:recvfrom(4096) print(' - ', from.port > 0) from.port = 'Random port' return box.cjson.encode{d, from}"
+
+
+exec admin "lua s:close()"
+exec admin "lua sc:close()"
+
+
+
+
+
+exec admin "lua s = box.socket('AF_INET', 'SOCK_DGRAM', 'udp')"
+exec admin "lua s:nonblock(true)"
+exec admin "lua s:bind('127.0.0.1')"
+exec admin "lua s:name().port > 0"
+exec admin "lua sc = box.socket('AF_INET', 'SOCK_DGRAM', 'udp')"
+exec admin "lua sc:nonblock(true)"
+exec admin "lua sc:sendto('127.0.0.1', s:name().port)"
+exec admin "lua sc:sendto('127.0.0.1', s:name().port, 'Hello, World!')"
+exec admin "lua s:readable(1)"
+exec admin "lua data, from = s:recvfrom(10)"
+exec admin "lua data"
+exec admin "lua s:sendto(from.host, from.port, 'Hello, hello!')"
+exec admin "lua sc:readable(1)"
+exec admin "lua data_r, from_r = sc:recvfrom(4096)"
+exec admin "lua data_r"
+exec admin "lua from_r.host"
+exec admin "lua from_r.port == s:name().port"
+exec admin "lua s:close()"
+exec admin "lua sc:close()"
+
+
+
+
diff --git a/test/box/errno.result b/test/box/errno.result
new file mode 100644
index 0000000000000000000000000000000000000000..cb5d65d5b94f540f2e155c2492b2bdd4d0e7d9e6
--- /dev/null
+++ b/test/box/errno.result
@@ -0,0 +1,26 @@
+lua type(box.errno)
+---
+ - table
+...
+lua box.errno.EINVAL > 0
+---
+ - true
+...
+lua box.errno.EBADF > 0
+---
+ - true
+...
+lua box.errno(box.errno.EINVAL) == box.errno.EINVAL, box.errno() == box.errno.EINVAL
+---
+ - true
+ - true
+...
+lua box.errno(box.errno.EBADF) ~= box.errno.EINVAL, box.errno() == box.errno.EBADF
+---
+ - true
+ - true
+...
+lua box.errno.strerror(box.errno.EINVAL)
+---
+ - Invalid argument
+...
diff --git a/test/box/errno.test b/test/box/errno.test
new file mode 100644
index 0000000000000000000000000000000000000000..8d73fe372d0bcfdb9e7d09ade68052488f11a0ec
--- /dev/null
+++ b/test/box/errno.test
@@ -0,0 +1,9 @@
+# encoding: tarantool
+# 
+
+exec admin "lua type(box.errno)"
+exec admin "lua box.errno.EINVAL > 0"
+exec admin "lua box.errno.EBADF > 0"
+exec admin "lua box.errno(box.errno.EINVAL) == box.errno.EINVAL, box.errno() == box.errno.EINVAL"
+exec admin "lua box.errno(box.errno.EBADF) ~= box.errno.EINVAL, box.errno() == box.errno.EBADF"
+exec admin "lua box.errno.strerror(box.errno.EINVAL)"