moonbridge
view moonbridge_io.c @ 107:06d965df8a0c
Moved local/remote address extraction/formatting to I/O library
| author | jbe | 
|---|---|
| date | Wed Apr 08 23:13:09 2015 +0200 (2015-04-08) | 
| parents | 8fce76ef321f | 
| children | 110493d29f90 | 
 line source
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <stdint.h>
     5 #include <errno.h>
     6 #include <string.h>
     7 #include <sys/socket.h>
     8 #include <sys/select.h>
     9 #include <fcntl.h>
    10 #include <netinet/in.h>
    11 #include <netinet/tcp.h>
    12 #include <arpa/inet.h>
    13 #include <sys/types.h>
    14 #include <netdb.h>
    16 #include <lua.h>
    17 #include <lauxlib.h>
    18 #include <lualib.h>
    20 #define MOONBR_IO_MAXSTRERRORLEN 80
    21 #define MOONBR_IO_READBUFLEN 4096
    22 #define MOONBR_IO_WRITEBUFLEN 4096
    24 #define moonbr_io_errmsg() \
    25   char errmsg[MOONBR_IO_MAXSTRERRORLEN]; \
    26   strerror_r(errno, errmsg, MOONBR_IO_MAXSTRERRORLEN)
    28 #define MOONBR_IO_HANDLE_MT_REGKEY "moonbridge_io_handle"
    29 #define MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY "moonbridge_io_handle_public"
    31 typedef struct {
    32   int fd;
    33   int issock;
    34   sa_family_t addrfam;
    35   int finished;
    36   int closed;
    37   int nonblocking;
    38   int nopush;
    39   int readerr;
    40   int readbufin;
    41   int readbufout;
    42   int writeerr;
    43   size_t writeleft;
    44 #if LUA_VERSION_NUM >= 503
    45   lua_Integer writeqin;
    46   lua_Integer writeqout;
    47 #else
    48   int writeqin;
    49   int writeqout;
    50 #endif
    51   size_t writeqoff;
    52   int writebufin;
    53   int writebufout;
    54   char readbuf[MOONBR_IO_READBUFLEN];
    55   char writebuf[MOONBR_IO_WRITEBUFLEN];
    56 } moonbr_io_handle_t;
    58 static void moonbr_io_handle_set_nonblocking(lua_State *L, moonbr_io_handle_t *handle, int nonblocking) {
    59   int flags;
    60   if (handle->nonblocking == nonblocking) return;
    61   flags = fcntl(handle->fd, F_GETFL, 0);
    62   if (flags == -1) {
    63     moonbr_io_errmsg();
    64     close(handle->fd);
    65     handle->fd = -1;
    66     handle->closed = 1;
    67     luaL_error(L, "Unexpected error in fcntl call: %s", errmsg);
    68   }
    69   if (nonblocking) flags |= O_NONBLOCK;
    70   else flags &= ~O_NONBLOCK;
    71   if (fcntl(handle->fd, F_SETFL, flags) == -1) {
    72     moonbr_io_errmsg();
    73     close(handle->fd);
    74     handle->fd = -1;
    75     handle->closed = 1;
    76     luaL_error(L, "Unexpected error in fcntl call: %s", errmsg);
    77   }
    78   handle->nonblocking = nonblocking;
    79 }
    81 static void moonbr_io_handle_set_linger(lua_State *L, moonbr_io_handle_t *handle, int timeout) {
    82   struct linger lingerval = { 0, };
    83   if (!handle->issock) return;
    84   if (timeout >= 0) {
    85     lingerval.l_onoff = 1;
    86     lingerval.l_linger = timeout;
    87   }
    88   if (setsockopt(handle->fd, SOL_SOCKET, SO_LINGER, &lingerval, sizeof(lingerval))) {
    89     moonbr_io_errmsg();
    90     close(handle->fd);
    91     handle->fd = -1;
    92     handle->closed = 1;
    93     luaL_error(L, "Unexpected error while setting SO_LINGER with setsockopt: %s", errmsg);
    94   }
    95 }
    97 static void moonbr_io_handle_set_nopush(lua_State *L, moonbr_io_handle_t *handle, int nopush) {
    98 #if defined(TCP_NOPUSH) || defined(TCP_CORK)
    99   if (
   100     !(handle->addrfam == AF_INET6 || handle->addrfam == AF_INET) ||
   101     handle->nopush == nopush
   102   ) return;
   103 #if defined(TCP_NOPUSH)
   104   if (setsockopt(handle->fd, IPPROTO_TCP, TCP_NOPUSH, &nopush, sizeof(nopush))) {
   105 #elif defined(TCP_CORK)
   106   if (setsockopt(handle->fd, IPPROTO_TCP, TCP_CORK, &nopush, sizeof(nopush))) {
   107 #endif
   108     moonbr_io_errmsg();
   109     close(handle->fd);
   110     handle->fd = -1;
   111     handle->closed = 1;
   112 #if defined(TCP_NOPUSH)
   113     luaL_error(L, "Unexpected error while setting TCP_NOPUSH with setsockopt: %s", errmsg);
   114 #elif defined(TCP_CORK)
   115     luaL_error(L, "Unexpected error while setting TCP_CORK with setsockopt: %s", errmsg);
   116 #endif
   117   }
   118   handle->nopush = nopush;
   119 #else
   120 #warning Neither TCP_NOPUSH nor TCP_CORK is available
   121 #endif
   122 }
   124 static int moonbr_io_read_impl(lua_State *L, int nonblocking, int drain) {
   125   moonbr_io_handle_t *handle;
   126   lua_Integer maxread;
   127   const char *terminatorstr;
   128   size_t terminatorlen;
   129   char terminator;
   130   luaL_Buffer luabuf;
   131   size_t luabufcnt = 0;
   132   int remaining;
   133   char *terminatorpos;
   134   ssize_t bytesread;
   135   handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
   136   maxread = luaL_optinteger(L, 2, 0);
   137   terminatorstr = luaL_optlstring(L, 3, "", &terminatorlen);
   138   if (terminatorlen) {
   139     luaL_argcheck(L, terminatorlen == 1, 3, "single byte expected");
   140     terminator = terminatorstr[0];
   141   }
   142   lua_settop(L, 1);  /* return handle on drain, terminator string may be garbage collected */
   143   if (handle->closed) luaL_error(L, "Attempt to read from a closed I/O handle");
   144   if (handle->readerr) {
   145     lua_pushnil(L);
   146     lua_pushliteral(L, "Previous read error");
   147     return 2;
   148   }
   149   handle->readerr = 1;
   150   if (handle->fd < 0) goto moonbr_io_read_impl_eof;  /* fake EOF to simulate shutdown */
   151   moonbr_io_handle_set_nonblocking(L, handle, nonblocking);
   152   if (!drain) luaL_buffinit(L, &luabuf);
   153   while (1) {
   154     remaining = -1;
   155     if (
   156       maxread > 0 &&
   157       handle->readbufin - handle->readbufout >= (size_t)maxread - luabufcnt
   158     ) {
   159       remaining = maxread - luabufcnt;
   160     } else if (terminatorlen) {
   161       terminatorpos = memchr(
   162         handle->readbuf + handle->readbufout,
   163         terminator,
   164         handle->readbufin - handle->readbufout
   165       );
   166       if (terminatorpos) remaining = 1 + (
   167         terminatorpos - (handle->readbuf + handle->readbufout)
   168       );
   169     }
   170     if (remaining >= 0) {
   171       if (!drain) {
   172         luaL_addlstring(
   173           &luabuf,
   174           handle->readbuf + handle->readbufout,
   175           remaining
   176         );
   177         luaL_pushresult(&luabuf);
   178       } else {
   179         luaL_pushresult(&luabuf);
   180         lua_pop(L, 1);
   181         lua_pushinteger(L, luabufcnt + remaining);
   182       }
   183       handle->readbufout += remaining;
   184       if (handle->readbufout == handle->readbufin) {
   185         handle->readbufin = 0;
   186         handle->readbufout =0;
   187       }
   188       handle->readerr = 0;
   189       return 1;
   190     }
   191     if (!drain) luaL_addlstring(
   192       &luabuf,
   193       handle->readbuf + handle->readbufout,
   194       handle->readbufin - handle->readbufout
   195     );
   196     luabufcnt += handle->readbufin - handle->readbufout;
   197     do {
   198       bytesread = read(handle->fd, handle->readbuf, MOONBR_IO_READBUFLEN);
   199     } while (bytesread < 0 && (errno == EINTR));
   200     if (
   201       bytesread == 0 || (
   202         nonblocking &&
   203         bytesread < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)
   204       )
   205     ) {
   206       handle->readbufin = 0;
   207       handle->readbufout = 0;
   208       if (!drain) {
   209         luaL_pushresult(&luabuf);
   210         if (!luabufcnt && bytesread == 0) {
   211           lua_pop(L, 1);
   212           moonbr_io_read_impl_eof:
   213           lua_pushboolean(L, 0);
   214           lua_pushliteral(L, "End of file");
   215           handle->readerr = 0;
   216           return 2;
   217         }
   218       } else {
   219         if (!luabufcnt && bytesread == 0) lua_pushboolean(L, 1);
   220         else lua_pushboolean(L, luabufcnt);
   221       }
   222       handle->readerr = 0;
   223       return 1;
   224     }
   225     if (bytesread < 0) {
   226       moonbr_io_errmsg();
   227       lua_pushnil(L);
   228       lua_pushstring(L, errmsg);
   229       return 2;
   230     }
   231     handle->readbufin = bytesread;
   232     handle->readbufout = 0;
   233   }
   234 }
   236 static int moonbr_io_read(lua_State *L) {
   237   return moonbr_io_read_impl(L, 0, 0);
   238 }
   240 static int moonbr_io_read_nb(lua_State *L) {
   241   return moonbr_io_read_impl(L, 1, 0);
   242 }
   244 static int moonbr_io_drain(lua_State *L) {
   245   return moonbr_io_read_impl(L, 0, 1);
   246 }
   248 static int moonbr_io_drain_nb(lua_State *L) {
   249   return moonbr_io_read_impl(L, 1, 1);
   250 }
   252 static int moonbr_io_write_impl(lua_State *L, int nonblocking, int flush) {
   253   moonbr_io_handle_t *handle;
   254   int i, top;
   255   const char *str;
   256   size_t strlen;
   257   ssize_t written;
   258   handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
   259   if (handle->closed) luaL_error(L, "Attempt to write to a closed I/O handle");
   260   if (handle->finished) luaL_error(L, "Attempt to write to a finished I/O handle");
   261   if (handle->writeerr) {
   262     lua_pushnil(L);
   263     lua_pushliteral(L, "Previous write error");
   264     return 2;
   265   }
   266   handle->writeerr = 1;
   267   moonbr_io_handle_set_nonblocking(L, handle, nonblocking);
   268   top = lua_gettop(L);
   269   lua_getuservalue(L, 1);
   270   lua_getfield(L, -1, "writequeue");
   271   for (i=2; i<=top; i++) {
   272     luaL_checklstring(L, i, &strlen);
   273     lua_pushvalue(L, i);
   274     lua_rawseti(L, -2, handle->writeqin++);
   275     handle->writeleft += strlen;
   276   }
   277   while (handle->writeqout != handle->writeqin) {
   278     lua_rawgeti(L, -1, handle->writeqout);
   279     str = lua_tolstring(L, -1, &strlen);
   280     while (handle->writeqoff < strlen) {
   281       if (
   282         strlen - handle->writeqoff <=
   283         MOONBR_IO_WRITEBUFLEN - handle->writebufin
   284       ) {
   285         memcpy(
   286           handle->writebuf + handle->writebufin,
   287           str + handle->writeqoff,
   288           strlen - handle->writeqoff
   289         );
   290         handle->writebufin += strlen - handle->writeqoff;
   291         break;
   292       } else {
   293         moonbr_io_handle_set_nopush(L, handle, 1);
   294         memcpy(
   295           handle->writebuf + handle->writebufin,
   296           str + handle->writeqoff,
   297           MOONBR_IO_WRITEBUFLEN - handle->writebufin
   298         );
   299         handle->writeqoff += MOONBR_IO_WRITEBUFLEN - handle->writebufin;
   300         while (handle->writebufout < MOONBR_IO_WRITEBUFLEN) {
   301           written = write(
   302             handle->fd,
   303             handle->writebuf + handle->writebufout,
   304             MOONBR_IO_WRITEBUFLEN - handle->writebufout
   305           );
   306           if (written < 0) {
   307             if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
   308               goto moonbr_io_write_impl_block;
   309             } else if (errno != EINTR) {
   310               moonbr_io_errmsg();
   311               lua_pushnil(L);
   312               lua_pushstring(L, errmsg);
   313               return 2;
   314             }
   315           } else {
   316             handle->writebufout += written;
   317             handle->writeleft -= written;
   318           }
   319         }
   320         handle->writebufin = 0;
   321         handle->writebufout = 0;
   322       }
   323     }
   324     handle->writeqoff = 0;
   325     lua_pop(L, 1);
   326     lua_pushnil(L);
   327     lua_rawseti(L, -2, handle->writeqout++);
   328   }
   329   if (flush) {
   330     moonbr_io_handle_set_nopush(L, handle, 0);
   331     while (handle->writebufout < handle->writebufin) {
   332       written = write(
   333         handle->fd,
   334         handle->writebuf + handle->writebufout,
   335         handle->writebufin - handle->writebufout
   336       );
   337       if (written < 0) {
   338         if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
   339           goto moonbr_io_write_impl_block;
   340         } else if (errno != EINTR) {
   341           moonbr_io_errmsg();
   342           lua_pushnil(L);
   343           lua_pushstring(L, errmsg);
   344           return 2;
   345         }
   346       } else {
   347         handle->writebufout += written;
   348         handle->writeleft -= written;
   349       }
   350     }
   351     handle->writebufin = 0;
   352     handle->writebufout = 0;
   353   }
   354   if (nonblocking) lua_pushinteger(L, 0);
   355   else lua_pushvalue(L, 1);
   356   handle->writeerr = 0;
   357   return 1;
   358   moonbr_io_write_impl_block:
   359   lua_pushinteger(L, handle->writeleft);
   360   handle->writeerr = 0;
   361   return 1;
   362 }
   364 static int moonbr_io_write(lua_State *L) {
   365   return moonbr_io_write_impl(L, 0, 0);
   366 }
   368 static int moonbr_io_write_nb(lua_State *L) {
   369   return moonbr_io_write_impl(L, 1, 0);
   370 }
   372 static int moonbr_io_flush(lua_State *L) {
   373   return moonbr_io_write_impl(L, 0, 1);
   374 }
   376 static int moonbr_io_flush_nb(lua_State *L) {
   377   return moonbr_io_write_impl(L, 1, 1);
   378 }
   380 static int moonbr_io_finish(lua_State *L) {
   381   moonbr_io_handle_t *handle;
   382   handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
   383   if (handle->closed) luaL_error(L, "Attempt to finish a closed I/O handle");
   384   if (handle->finished) luaL_error(L, "Attempt to finish a finished I/O handle");
   385   if (handle->writeleft) {
   386     lua_pushcfunction(L, moonbr_io_flush);
   387     lua_pushvalue(L, 1);
   388     lua_call(L, 1, 2);
   389     if (!lua_toboolean(L, -2)) {
   390       handle->finished = 1;
   391       return 2;
   392     }
   393   }
   394   handle->finished = 1;
   395   if (handle->addrfam == AF_INET6 || handle->addrfam == AF_INET) {
   396     if (shutdown(handle->fd, SHUT_WR)) {
   397       moonbr_io_errmsg();
   398       lua_pushnil(L);
   399       lua_pushstring(L, errmsg);
   400       return 2;
   401     }
   402   } else {
   403     if (close(handle->fd)) {
   404       moonbr_io_errmsg();
   405       handle->fd = -1;
   406       lua_pushnil(L);
   407       lua_pushstring(L, errmsg);
   408       return 2;
   409     }
   410     handle->fd = -1;  /* fake EOF on read */
   411   }
   412   lua_pushboolean(L, 1);
   413   return 1;
   414 }
   416 static int moonbr_io_close_impl(lua_State *L, int reset) {
   417   moonbr_io_handle_t *handle;
   418   handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
   419   if (handle->closed) luaL_error(L, "Attempt to close a closed I/O handle");
   420   if (!reset) {
   421     if (handle->writeleft) {
   422       lua_pushcfunction(L, moonbr_io_flush);
   423       lua_pushvalue(L, 1);
   424       lua_call(L, 1, 2);
   425       if (!lua_toboolean(L, -2)) {
   426         close(handle->fd);
   427         handle->fd = -1;
   428         return 2;
   429       }
   430     }
   431     moonbr_io_handle_set_linger(L, handle, -1);
   432   }
   433   if (handle->fd >= 0) {
   434     if (close(handle->fd)) {
   435       moonbr_io_errmsg();
   436       handle->fd = -1;
   437       lua_pushnil(L);
   438       lua_pushstring(L, errmsg);
   439       return 2;
   440     }
   441     handle->fd = -1;
   442   }
   443   lua_pushboolean(L, 1);
   444   return 1;
   446 }
   448 static int moonbr_io_close(lua_State *L) {
   449   return moonbr_io_close_impl(L, 0);
   450 }
   452 static int moonbr_io_reset(lua_State *L) {
   453   return moonbr_io_close_impl(L, 1);
   454 }
   456 static int moonbr_io_gc(lua_State *L) {
   457   moonbr_io_handle_t *handle;
   458   handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
   459   if (handle->fd >= 0) {
   460     lua_pushcfunction(L, moonbr_io_close);
   461     lua_pushvalue(L, 1);
   462     lua_pushinteger(L, 0);
   463     lua_call(L, 2, 0);
   464   }
   465   return 0;
   466 }
   468 void moonbr_io_closehandle(lua_State *L, int idx, int reset) {
   469   moonbr_io_handle_t *handle;
   470   handle = luaL_checkudata(L, idx, MOONBR_IO_HANDLE_MT_REGKEY);
   471   if (!handle->closed) {
   472     lua_pushcfunction(L, reset ? moonbr_io_reset : moonbr_io_close);
   473     lua_pushvalue(L, idx < 0 ? idx-1 : idx);
   474     lua_call(L, 1, 0);
   475   }
   476 }
   478 void moonbr_io_pushhandle(lua_State *L, int fd) {
   479   moonbr_io_handle_t *handle;
   480   struct sockaddr addr;
   481   socklen_t addrlen;
   482   handle = lua_newuserdata(L, sizeof(moonbr_io_handle_t));
   483   handle->fd = fd;
   484   addrlen = sizeof(addr);
   485   if (getsockname(fd, &addr, &addrlen)) {
   486     if (errno != ENOTSOCK) {
   487       moonbr_io_errmsg();
   488       luaL_error(L, "Unexpected error when examining socket: %s", errmsg);
   489     }
   490     handle->issock = 0;
   491   } else {
   492     handle->issock = 1;
   493     handle->addrfam = addr.sa_family;
   494   }
   495   handle->finished = 0;
   496   handle->closed = 0;
   497   handle->nonblocking = -1;
   498   handle->nopush = -1;
   499   handle->readerr = 0;
   500   handle->readbufin = 0;
   501   handle->readbufout = 0;
   502   handle->writeerr = 0;
   503   handle->writeleft = 0;
   504   handle->writeqin = 0;
   505   handle->writeqout = 0;
   506   handle->writeqoff = 0;
   507   handle->writebufin = 0;
   508   handle->writebufout = 0;
   509   moonbr_io_handle_set_linger(L, handle, 0);
   510   luaL_getmetatable(L, MOONBR_IO_HANDLE_MT_REGKEY);
   511   lua_setmetatable(L, -2);
   512   lua_newtable(L);  // uservalue
   513   lua_newtable(L);
   514   lua_setfield(L, -2, "writequeue");
   515   lua_newtable(L);  // public
   516   if (handle->addrfam == AF_INET6) {
   517     struct sockaddr_in6 addr_in6;
   518     char addrstrbuf[INET6_ADDRSTRLEN];
   519     const char *addrstr;
   520     addrlen = sizeof(addr_in6);
   521     if (getsockname(fd, (struct sockaddr *)&addr_in6, &addrlen)) {
   522       moonbr_io_errmsg();
   523       luaL_error(L, "Could not determine local IP address/port: %s", errmsg);
   524     }
   525     if (addrlen > sizeof(addr_in6)) {
   526       luaL_error(L, "Could not determine local IP address/port: buffer size exceeded");
   527     }
   528     addrstr = inet_ntop(AF_INET6, addr_in6.sin6_addr.s6_addr, addrstrbuf, sizeof(addrstrbuf));
   529     if (!addrstr) {
   530       moonbr_io_errmsg();
   531       luaL_error(L, "Could not format local IP address: %s", errmsg);
   532     } else {
   533       lua_pushstring(L, addrstr);
   534       lua_setfield(L, -2, "local_ip6");
   535     }
   536     lua_pushinteger(L, ntohs(addr_in6.sin6_port));
   537     lua_setfield(L, -2, "local_tcpport");
   538     if (getpeername(fd, (struct sockaddr *)&addr_in6, &addrlen)) {
   539       moonbr_io_errmsg();
   540       luaL_error(L, "Could not determine remote IP address/port: %s", errmsg);
   541     }
   542     if (addrlen > sizeof(addr_in6)) {
   543       luaL_error(L, "Could not determine remote IP address/port: buffer size exceeded");
   544     }
   545     addrstr = inet_ntop(AF_INET6, addr_in6.sin6_addr.s6_addr, addrstrbuf, sizeof(addrstrbuf));
   546     if (!addrstr) {
   547       moonbr_io_errmsg();
   548       luaL_error(L, "Could not format remote IP address: %s", errmsg);
   549     } else {
   550       lua_pushstring(L, addrstr);
   551       lua_setfield(L, -2, "remote_ip6");
   552     }
   553     lua_pushinteger(L, ntohs(addr_in6.sin6_port));
   554     lua_setfield(L, -2, "remote_tcpport");
   555   } else if (handle->addrfam == AF_INET) {
   556     struct sockaddr_in addr_in;
   557     char addrstrbuf[INET_ADDRSTRLEN];
   558     const char *addrstr;
   559     addrlen = sizeof(addr_in);
   560     if (getsockname(fd, (struct sockaddr *)&addr_in, &addrlen)) {
   561       moonbr_io_errmsg();
   562       luaL_error(L, "Could not determine local IP address/port: %s", errmsg);
   563     }
   564     if (addrlen > sizeof(addr_in)) {
   565       luaL_error(L, "Could not determine local IP address/port: buffer size exceeded");
   566     }
   567     addrstr = inet_ntop(AF_INET, &addr_in.sin_addr.s_addr, addrstrbuf, sizeof(addrstrbuf));
   568     if (!addrstr) {
   569       moonbr_io_errmsg();
   570       luaL_error(L, "Could not format local IP address: %s", errmsg);
   571     } else {
   572       lua_pushstring(L, addrstr);
   573       lua_setfield(L, -2, "local_ip4");
   574     }
   575     lua_pushinteger(L, ntohs(addr_in.sin_port));
   576     lua_setfield(L, -2, "local_tcpport");
   577     if (getpeername(fd, (struct sockaddr *)&addr_in, &addrlen)) {
   578       moonbr_io_errmsg();
   579       luaL_error(L, "Could not determine remote IP address/port: %s", errmsg);
   580     }
   581     if (addrlen > sizeof(addr_in)) {
   582       luaL_error(L, "Could not determine remote IP address/port: buffer size exceeded");
   583     }
   584     addrstr = inet_ntop(AF_INET, &addr_in.sin_addr.s_addr, addrstrbuf, sizeof(addrstrbuf));
   585     if (!addrstr) {
   586       moonbr_io_errmsg();
   587       luaL_error(L, "Could not format remote IP address: %s", errmsg);
   588     } else {
   589       lua_pushstring(L, addrstr);
   590       lua_setfield(L, -2, "remote_ip4");
   591     }
   592     lua_pushinteger(L, ntohs(addr_in.sin_port));
   593     lua_setfield(L, -2, "remote_tcpport");
   594   }
   595   luaL_getmetatable(L, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY);
   596   lua_setmetatable(L, -2);
   597   lua_setfield(L, -2, "public");
   598   lua_setuservalue(L, -2);
   599 }
   601 static int moonbr_io_handleindex(lua_State *L) {
   602   luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
   603   lua_getuservalue(L, 1);
   604   lua_getfield(L, -1, "public");
   605   lua_pushvalue(L, 2);
   606   lua_gettable(L, -2);
   607   return 1;
   608 }
   610 static int moonbr_io_handlenewindex(lua_State *L) {
   611   luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
   612   lua_getuservalue(L, 1);
   613   lua_getfield(L, -1, "public");
   614   lua_pushvalue(L, 2);
   615   lua_pushvalue(L, 3);
   616   lua_settable(L, -3);
   617   return 0;
   618 }
   620 static int moonbr_io_tcpconnect_impl(lua_State *L, int nonblocking) {
   621   const char *host, *port;
   622   struct addrinfo hints = { 0, };
   623   struct addrinfo *res, *addrinfo;
   624   int errcode;
   625   int sock;
   626   host = luaL_checkstring(L, 1);
   627   port = luaL_checkstring(L, 2);
   628   hints.ai_family = AF_UNSPEC;
   629   hints.ai_socktype = SOCK_STREAM;
   630   hints.ai_protocol = IPPROTO_TCP;
   631   hints.ai_flags = AI_ADDRCONFIG;
   632   errcode = getaddrinfo(host, port, &hints, &res);
   633   if (errcode) {
   634     freeaddrinfo(res);
   635     if (errcode == EAI_SYSTEM) {
   636       moonbr_io_errmsg();
   637       lua_pushnil(L);
   638       lua_pushfstring(L, "%s: %s", gai_strerror(errcode), errmsg);
   639     } else {
   640       lua_pushnil(L);
   641       lua_pushstring(L, gai_strerror(errcode));
   642     }
   643     return 2;
   644   }
   645   for (addrinfo=res; addrinfo; addrinfo=addrinfo->ai_next) {
   646     if (addrinfo->ai_family == PF_INET6) goto moonbr_io_tcpconnect_found;
   647   }
   648   for (addrinfo=res; addrinfo; addrinfo=addrinfo->ai_next) {
   649     if (addrinfo->ai_family == PF_INET) goto moonbr_io_tcpconnect_found;
   650   }
   651   addrinfo = res;
   652   moonbr_io_tcpconnect_found:
   653   sock = socket(
   654     addrinfo->ai_family,
   655     addrinfo->ai_socktype | SOCK_CLOEXEC | (nonblocking ? SOCK_NONBLOCK : 0),
   656     addrinfo->ai_protocol
   657   );
   658   freeaddrinfo(res);
   659   if (sock < 0) {
   660     moonbr_io_errmsg();
   661     lua_pushnil(L);
   662     lua_pushstring(L, errmsg);
   663   }
   664   if (connect(sock, addrinfo->ai_addr, addrinfo->ai_addrlen)) {
   665     if (!nonblocking && errno == EINTR) {
   666       moonbr_io_errmsg();
   667       close(sock);
   668       lua_pushnil(L);
   669       lua_pushstring(L, errmsg);
   670       return 2;
   671     } else if (!(nonblocking && (errno == EINPROGRESS || errno == EINTR))) {
   672       moonbr_io_errmsg();
   673       lua_pushnil(L);
   674       lua_pushstring(L, errmsg);
   675       return 2;
   676     }
   677   }
   678   moonbr_io_pushhandle(L, sock);
   679   return 1;
   680 }
   682 static int moonbr_io_tcpconnect(lua_State *L) {
   683   return moonbr_io_tcpconnect_impl(L, 0);
   684 }
   686 static int moonbr_io_tcpconnect_nb(lua_State *L) {
   687   return moonbr_io_tcpconnect_impl(L, 1);
   688 }
   690 static int moonbr_io_poll(lua_State *L) {
   691   moonbr_io_handle_t *handle;
   692   int fd, isnum;
   693   int nfds = 0;
   694   fd_set readfds, writefds, exceptfds;
   695   struct timeval timeout = {0, };
   696   int status;
   697   FD_ZERO(&readfds);
   698   FD_ZERO(&writefds);
   699   FD_ZERO(&exceptfds);
   700   if (!lua_isnoneornil(L, 1)) {
   701     luaL_checktype(L, 1, LUA_TTABLE);
   702     for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
   703       if (lua_toboolean(L, -1)) {
   704         handle = luaL_testudata(L, -2, MOONBR_IO_HANDLE_MT_REGKEY);
   705         if (handle) {
   706           fd = handle->fd;
   707           if (fd < 0) luaL_error(L, "Handle in illegal state");
   708         } else {
   709           fd = lua_tointegerx(L, -2, &isnum);
   710           if (!isnum) luaL_error(L, "File descriptor is not an integer");
   711         }
   712         FD_SET(fd, &readfds);
   713         if (fd+1 > nfds) nfds = fd+1;
   714       }
   715     }
   716   }
   717   if (!lua_isnoneornil(L, 2)) {
   718     luaL_checktype(L, 2, LUA_TTABLE);
   719     for (lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
   720       if (lua_toboolean(L, -1)) {
   721         handle = luaL_testudata(L, -2, MOONBR_IO_HANDLE_MT_REGKEY);
   722         if (handle) {
   723           fd = handle->fd;
   724           if (fd < 0) luaL_error(L, "Handle in illegal state");
   725         } else {
   726           fd = lua_tointegerx(L, -2, &isnum);
   727           if (!isnum) luaL_error(L, "File descriptor is not an integer");
   728         }
   729         FD_SET(fd, &writefds);
   730         if (fd+1 > nfds) nfds = fd+1;
   731       }
   732     }
   733   }
   734   if (!lua_isnoneornil(L, 3)) {
   735     lua_Number n;
   736     n = lua_tonumberx(L, 3, &isnum);
   737     if (isnum && n>=0 && n<100000000) {
   738       timeout.tv_sec = n;
   739       timeout.tv_usec = 1e6 * (n - timeout.tv_sec);
   740     } else {
   741       luaL_argcheck(L, 0, 3, "not a valid timeout");
   742     }
   743     status = select(nfds, &readfds, &writefds, &exceptfds, &timeout);
   744   } else {
   745     status = select(nfds, &readfds, &writefds, &exceptfds, NULL);
   746   }
   747   if (status == -1) {
   748     if (errno == EINTR) {
   749       lua_pushboolean(L, 0);
   750       lua_pushliteral(L, "Signal received while polling file descriptors");
   751       return 2;
   752     } else {
   753       moonbr_io_errmsg();
   754       return luaL_error(L, "Unexpected error during \"select\" system call: %s", errmsg);
   755     }
   756   } else if (status == 0) {
   757     lua_pushboolean(L, 0);
   758     lua_pushliteral(L, "Timeout while polling file descriptors");
   759     return 2;
   760   } else {
   761     lua_pushboolean(L, 1);
   762     return 1;
   763   }
   764 }
   766 static const struct luaL_Reg moonbr_io_handle_methods[] = {
   767   {"read", moonbr_io_read},
   768   {"read_nb", moonbr_io_read_nb},
   769   {"drain", moonbr_io_drain},
   770   {"drain_nb", moonbr_io_drain_nb},
   771   {"write", moonbr_io_write},
   772   {"write_nb", moonbr_io_write_nb},
   773   {"flush", moonbr_io_flush},
   774   {"flush_nb", moonbr_io_flush_nb},
   775   {"finish", moonbr_io_finish},
   776   {"close", moonbr_io_close},
   777   {"reset", moonbr_io_reset},
   778   {NULL, NULL}
   779 };
   781 static const struct luaL_Reg moonbr_io_handle_metamethods[] = {
   782   {"__index", moonbr_io_handleindex},
   783   {"__newindex", moonbr_io_handlenewindex},
   784   {"__gc", moonbr_io_gc},
   785   {NULL, NULL}
   786 };
   788 static const struct luaL_Reg moonbr_io_module_funcs[] = {
   789   {"tcpconnect", moonbr_io_tcpconnect},
   790   {"tcpconnect_nb", moonbr_io_tcpconnect_nb},
   791   {"poll", moonbr_io_poll},
   792   {NULL, NULL}
   793 };
   795 int luaopen_moonbridge_io(lua_State *L) {
   797   lua_newtable(L);  // module
   799   lua_newtable(L);  // public metatable
   800   lua_newtable(L);  // handle methods
   801   luaL_setfuncs(L, moonbr_io_handle_methods, 0);
   802   lua_pushvalue(L, -1);
   803   lua_setfield(L, -4, "handle");
   804   lua_setfield(L, -2, "__index");
   805   lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY);
   807   lua_newtable(L);  // handle metatable
   808   luaL_setfuncs(L, moonbr_io_handle_metamethods, 0);
   809   lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_MT_REGKEY);
   811   luaL_setfuncs(L, moonbr_io_module_funcs, 0);
   812   return 1;
   814 }
