/* * Arguments: sd_udata, new_sd_udata, [sock_addr_udata] * Returns: [new_sd_udata | false (EAGAIN)] */ static int sock_accept (lua_State *L) { sd_t sd = (sd_t) lua_unboxinteger(L, 1, SD_TYPENAME); sd_t *sdp = checkudata(L, 2, SD_TYPENAME); struct sock_addr *from = lua_isnoneornil(L, 3) ? NULL : checkudata(L, 3, SA_TYPENAME); struct sockaddr *sap = NULL; socklen_t *slp = NULL; if (from) { sap = &from->u.addr; slp = &from->addrlen; } #ifndef _WIN32 do sd = accept(sd, sap, slp); while (sd == -1 && SYS_ERRNO == EINTR); #else sd = accept(sd, sap, slp); #endif if (sd != (sd_t) -1) { *sdp = sd; lua_settop(L, 2); return 1; } else if (SYS_EAGAIN(SYS_ERRNO)) { lua_pushboolean(L, 0); return 1; } return sys_seterror(L, 0); }
/* * Arguments: sd_udata, nonblocking (boolean) * Returns: [sd_udata] */ static int sock_nonblocking (lua_State *L) { sd_t sd = (sd_t) lua_unboxinteger(L, 1, SD_TYPENAME); unsigned long opt = lua_toboolean(L, 2); lua_settop(L, 1); return !ioctlsocket(sd, FIONBIO, &opt) ? 1 : sys_seterror(L, 0); }
/* * Arguments: sd_udata * Returns: string */ static int sock_tostring (lua_State *L) { sd_t sd = (sd_t) lua_unboxinteger(L, 1, SD_TYPENAME); if (sd != (sd_t) -1) lua_pushfstring(L, SD_TYPENAME " (%d)", (int) sd); else lua_pushliteral(L, SD_TYPENAME " (closed)"); return 1; }
/* * Arguments: sd_udata, [backlog (number)] * Returns: [sd_udata] */ static int sock_listen (lua_State *L) { sd_t sd = (sd_t) lua_unboxinteger(L, 1, SD_TYPENAME); const int backlog = luaL_optinteger(L, 2, SOMAXCONN); if (!listen(sd, backlog)) { lua_settop(L, 1); return 1; } return sys_seterror(L, 0); }
/* * Arguments: sd_udata, sock_addr_udata * Returns: [sd_udata] */ static int sock_bind (lua_State *L) { sd_t sd = (sd_t) lua_unboxinteger(L, 1, SD_TYPENAME); struct sock_addr *sap = checkudata(L, 2, SA_TYPENAME); if (!bind(sd, &sap->u.addr, sap->addrlen)) { lua_settop(L, 1); return 1; } return sys_seterror(L, 0); }
/* * Arguments: sd_udata * Returns: [sd_udata] */ static int sock_shutdown (lua_State *L) { sd_t sd = (sd_t) lua_unboxinteger(L, 1, SD_TYPENAME); /* SHUT_RD (SD_RECEIVE) has different behavior in unix and win32 */ if (!shutdown(sd, SHUT_WR)) { lua_settop(L, 1); return 1; } return sys_seterror(L, 0); }
/* * Arguments: sd_udata, option (string), * [value_lo (number), value_hi (number)] * Returns: [sd_udata | value_lo (number), value_hi (number)] */ static int sock_sockopt (lua_State *L) { static const int opt_flags[] = { SO_REUSEADDR, SO_TYPE, SO_ERROR, SO_DONTROUTE, SO_SNDBUF, SO_RCVBUF, SO_SNDLOWAT, SO_RCVLOWAT, SO_BROADCAST, SO_KEEPALIVE, SO_OOBINLINE, SO_LINGER, #define OPTNAMES_TCP 12 TCP_NODELAY, #define OPTNAMES_IP 13 IP_MULTICAST_TTL, IP_MULTICAST_IF, IP_MULTICAST_LOOP }; static const char *const opt_names[] = { "reuseaddr", "type", "error", "dontroute", "sndbuf", "rcvbuf", "sndlowat", "rcvlowat", "broadcast", "keepalive", "oobinline", "linger", "tcp_nodelay", "multicast_ttl", "multicast_if", "multicast_loop", NULL }; #undef OPT_START #define OPT_START 2 sd_t sd = (sd_t) lua_unboxinteger(L, 1, SD_TYPENAME); const int optname = luaL_checkoption(L, OPT_START, NULL, opt_names); const int level = (optname < OPTNAMES_TCP) ? SOL_SOCKET : (optname < OPTNAMES_IP ? IPPROTO_TCP : IPPROTO_IP); const int optflag = opt_flags[optname]; int optval[4]; socklen_t optlen = sizeof(int); const int nargs = lua_gettop(L); if (nargs > OPT_START) { optval[0] = lua_tointeger(L, OPT_START + 1); if (nargs > OPT_START + 1) { optval[1] = lua_tointeger(L, OPT_START + 2); optlen *= 2; } if (!setsockopt(sd, level, optflag, (char *) &optval, optlen)) { lua_settop(L, 1); return 1; } } else if (!getsockopt(sd, level, optflag, (char *) &optval, &optlen)) { lua_pushinteger(L, optval[0]); if (optlen <= sizeof(int)) return 1; lua_pushinteger(L, optval[1]); return 2; } return sys_seterror(L, 0); }
/* * Arguments: sd_udata, binary_address (multiaddr), * [binary_address_ipv4 (interface) | interface_ipv6 (number), * add/drop (boolean)] * Returns: [sd_udata] */ static int sock_membership (lua_State *L) { sd_t sd = (sd_t) lua_unboxinteger(L, 1, SD_TYPENAME); int len, af; const char *maddrp = sock_checkladdr(L, 2, &len, &af); const int optflag = !lua_isboolean(L, -1) || lua_toboolean(L, -1) ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; union { struct ip_mreq ip; #ifdef IPPROTO_IPV6 struct ipv6_mreq ip6; #endif } mr; int level, mr_len; memset(&mr, 0, sizeof(mr)); if (af == AF_INET) { const char *ifacep = (lua_type(L, 3) == LUA_TSTRING) ? sock_checkladdr(L, 3, &len, &af) : NULL; if (ifacep && af != AF_INET) luaL_argerror(L, 3, "invalid interface"); memcpy(&mr.ip.imr_multiaddr, maddrp, len); if (ifacep) memcpy(&mr.ip.imr_interface, ifacep, len); level = IPPROTO_IP; mr_len = sizeof(struct ip_mreq); } else { #ifdef IPPROTO_IPV6 memcpy(&mr.ip6.ipv6mr_multiaddr, maddrp, len); mr.ip6.ipv6mr_interface = lua_tointeger(L, 3); level = IPPROTO_IPV6; mr_len = sizeof(struct ipv6_mreq); #else luaL_argerror(L, 2, "invalid family"); #endif } if (!setsockopt(sd, level, optflag, (char *) &mr, mr_len)) { lua_settop(L, 1); return 1; } return sys_seterror(L, 0); }
/* * Arguments: rand_udata, [upper_bound (number) * | buffer (ludata), buffer_length (number)] * Returns: number */ static int rand_next (lua_State *L) { const int is_udata = lua_isuserdata(L, 2); const unsigned int ub = is_udata ? 0 : lua_tointeger(L, 2); unsigned int num; unsigned char *buf = is_udata ? lua_touserdata(L, 2) : # const int len = is_udata ? luaL_checkinteger(L, 3) : (int) sizeof(num); #ifndef _WIN32 fd_t fd = (fd_t) lua_unboxinteger(L, 1, RAND_TYPENAME); int nr; sys_vm_leave(L); do nr = read(fd, (char *) buf, len); while (nr == -1 && sys_eintr()); sys_vm_enter(L); if (nr == len) { #else HCRYPTPROV prov = (HCRYPTPROV) lua_unboxpointer(L, 1, RAND_TYPENAME); int res; sys_vm_leave(L); res = CryptGenRandom(prov, len, buf); sys_vm_enter(L); if (res) { #endif lua_pushinteger(L, is_udata ? 1 : (ub ? num % ub : num)); return 1; } return sys_seterror(L, 0); } #define RAND_METHODS \ {"random", sys_random} static luaL_Reg rand_meth[] = { {"__call", rand_next}, {"__gc", rand_close}, {NULL, NULL} };
/* * Arguments: sd_udata, [membuf_udata, count (number)] * Returns: [string | false (EAGAIN)] */ static int sock_read (lua_State *L) { sd_t sd = (sd_t) lua_unboxinteger(L, 1, SD_TYPENAME); size_t n = !lua_isnumber(L, -1) ? ~((size_t) 0) : (size_t) lua_tointeger(L, -1); const size_t len = n; /* how much total to read */ size_t rlen; /* how much to read */ int nr; /* number of bytes actually read */ struct sys_buffer sb; char buf[SYS_BUFSIZE]; sys_buffer_write_init(L, 2, &sb, buf, sizeof(buf)); do { rlen = (n <= sb.size) ? n : sb.size; sys_vm_leave(); #ifndef _WIN32 do nr = read(sd, sb.ptr.w, rlen); while (nr == -1 && SYS_ERRNO == EINTR); #else { WSABUF buf = {rlen, sb.ptr.w}; DWORD l, flags = 0; nr = !WSARecv(sd, &buf, 1, &l, &flags, NULL, NULL) ? l : -1; } #endif sys_vm_enter(); if (nr == -1) break; n -= nr; /* still have to read `n' bytes */ } while ((n != 0L && nr == (int) rlen) /* until end of count or eof */ && sys_buffer_write_next(L, &sb, buf, 0)); if (nr <= 0 && len == n) { if (!nr || !SYS_EAGAIN(SYS_ERRNO)) goto err; lua_pushboolean(L, 0); } else { if (!sys_buffer_write_done(L, &sb, buf, nr)) lua_pushinteger(L, len - n); } return 1; err: return sys_seterror(L, 0); }
/* * Arguments: sd_udata, sock_addr_udata * Returns: [sd_udata | false (EINPROGRESS)] */ static int sock_connect (lua_State *L) { sd_t sd = (sd_t) lua_unboxinteger(L, 1, SD_TYPENAME); struct sock_addr *sap = checkudata(L, 2, SA_TYPENAME); int res; sys_vm_leave(); do res = connect(sd, &sap->u.addr, sap->addrlen); while (res == -1 && SYS_ERRNO == EINTR); sys_vm_enter(); if (!res || SYS_ERRNO == EINPROGRESS || SYS_ERRNO == EALREADY #if defined(__FreeBSD__) || SYS_ERRNO == EADDRINUSE #endif || SYS_EAGAIN(SYS_ERRNO)) { if (res) lua_pushboolean(L, 0); else lua_settop(L, 1); return 1; } return sys_seterror(L, 0); }
/* * Arguments: sd_udata, {string | membuf_udata}, * [to (sock_addr_udata), options (string) ...] * Returns: [success/partial (boolean), count (number)] */ static int sock_send (lua_State *L) { static const int o_flags[] = { MSG_OOB, MSG_DONTROUTE, }; static const char *const o_names[] = { "oob", "dontroute", NULL }; sd_t sd = (sd_t) lua_unboxinteger(L, 1, SD_TYPENAME); const struct sock_addr *to = !lua_isuserdata(L, 3) ? NULL : checkudata(L, 3, SA_TYPENAME); struct sys_buffer sb; int nw; /* number of chars actually send */ unsigned int i, flags = 0; if (!sys_buffer_read_init(L, 2, &sb)) luaL_argerror(L, 2, "buffer expected"); for (i = lua_gettop(L); i > 3; --i) { flags |= o_flags[luaL_checkoption(L, i, NULL, o_names)]; } sys_vm_leave(); do nw = !to ? send(sd, sb.ptr.r, sb.size, flags) : sendto(sd, sb.ptr.r, sb.size, flags, &to->u.addr, to->addrlen); while (nw == -1 && SYS_ERRNO == EINTR); sys_vm_enter(); if (nw == -1) { if (!SYS_EAGAIN(SYS_ERRNO)) return sys_seterror(L, 0); nw = 0; } else { sys_buffer_read_next(&sb, nw); } lua_pushboolean(L, ((size_t) nw == sb.size)); lua_pushinteger(L, nw); return 2; }
/* * Returns: [interfaces (table)] */ static int sock_getifaddrs (lua_State *L) { struct sock_addr *sap; int i, res; #ifndef _WIN32 struct ifaddrs *result, *rp; sys_vm_leave(L); res = getifaddrs(&result); sys_vm_enter(L); #else INTERFACE_INFO result[8192], *rp; SOCKET sd = WSASocketW(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAGS); DWORD n; sys_vm_leave(L); res = WSAIoctl(sd, SIO_GET_INTERFACE_LIST, NULL, 0, result, sizeof(result), &n, NULL, NULL); closesocket(sd); sys_vm_enter(L); #endif if (res == -1) return sys_seterror(L, 0); lua_createtable(L, 8, 0); rp = result; #ifndef _WIN32 for (i = 0; rp; rp = rp->ifa_next) { #else for (i = 0; n--; ++rp) { #endif #ifndef _WIN32 sap = (struct sock_addr *) rp->ifa_addr; #else sap = (struct sock_addr *) &rp->iiAddress; #endif if (!sap || sap->u.addr.sa_family == AF_UNSPEC) continue; lua_newtable(L); { const int af = sap->u.addr.sa_family; if (af == AF_INET #ifdef AF_INET6 || af == AF_INET6 #endif ) { sock_pushaddr(L, sap); lua_setfield(L, -2, "addr"); } { const char *s = NULL; switch (af) { case AF_INET: s = "INET"; break; #ifdef AF_INET6 case AF_INET6: s = "INET6"; break; #endif #ifdef AF_LOCAL case AF_LOCAL: s = "LOCAL"; break; #endif #ifdef AF_AX25 case AF_AX25: s = "AX25"; break; #endif #ifdef AF_IPX case AF_IPX: s = "IPX"; break; #endif #ifdef AF_APPLETALK case AF_APPLETALK: s = "APPLETALK"; break; #endif #ifdef AF_NETROM case AF_NETROM: s = "NETROM"; break; #endif #ifdef AF_BRIDGE case AF_BRIDGE: s = "BRIDGE"; break; #endif #ifdef AF_ATMPVC case AF_ATMPVC: s = "ATMPVC"; break; #endif #ifdef AF_X25 case AF_X25: s = "X25"; break; #endif #ifdef AF_ROSE case AF_ROSE: s = "ROSE"; break; #endif #ifdef AF_DECnet case AF_DECnet: s = "DECnet"; break; #endif #ifdef AF_NETBEUI case AF_NETBEUI: s = "NETBEUI"; break; #endif #ifdef AF_SECURITY case AF_SECURITY: s = "SECURITY"; break; #endif #ifdef AF_KEY case AF_KEY: s = "KEY"; break; #endif #ifdef AF_NETLINK case AF_NETLINK: s = "NETLINK"; break; #endif #ifdef AF_PACKET case AF_PACKET: s = "PACKET"; break; #endif #ifdef AF_ASH case AF_ASH: s = "ASH"; break; #endif #ifdef AF_ECONET case AF_ECONET: s = "ECONET"; break; #endif #ifdef AF_ATMSVC case AF_ATMSVC: s = "ATMSVC"; break; #endif #ifdef AF_RDS case AF_RDS: s = "RDS"; break; #endif #ifdef AF_SNA case AF_SNA: s = "SNA"; break; #endif #ifdef AF_IRDA case AF_IRDA: s = "IRDA"; break; #endif #ifdef AF_PPPOX case AF_PPPOX: s = "PPPOX"; break; #endif #ifdef AF_WANPIPE case AF_WANPIPE: s = "WANPIPE"; break; #endif #ifdef AF_LLC case AF_LLC: s = "LLC"; break; #endif #ifdef AF_CAN case AF_CAN: s = "CAN"; break; #endif #ifdef AF_TIPC case AF_TIPC: s = "TIPC"; break; #endif #ifdef AF_BLUETOOTH case AF_BLUETOOTH: s = "BLUETOOTH"; break; #endif #ifdef AF_IUCV case AF_IUCV: s = "IUCV"; break; #endif #ifdef AF_RXRPC case AF_RXRPC: s = "RXRPC"; break; #endif #ifdef AF_ISDN case AF_ISDN: s = "ISDN"; break; #endif #ifdef AF_PHONET case AF_PHONET: s = "PHONET"; break; #endif #ifdef AF_IEEE802154 case AF_IEEE802154: s = "IEEE802154"; break; #endif default: s = "UNKNOWN"; } if (s) { lua_pushstring(L, s); lua_setfield(L, -2, "family"); } } #ifndef _WIN32 sap = (struct sock_addr *) rp->ifa_netmask; #else sap = (struct sock_addr *) &rp->iiNetmask; #endif if (sap) { sock_pushaddr(L, sap); lua_setfield(L, -2, "netmask"); } #ifndef _WIN32 sap = (struct sock_addr *) rp->ifa_broadaddr; #else sap = (struct sock_addr *) &rp->iiBroadcastAddress; #endif if (sap) { sock_pushaddr(L, sap); lua_setfield(L, -2, "broadaddr"); } lua_createtable(L, 0, 5); { #ifndef _WIN32 const int flags = rp->ifa_flags; #else const int flags = rp->iiFlags; #endif lua_pushboolean(L, flags & IFF_UP); lua_setfield(L, -2, "up"); lua_pushboolean(L, flags & IFF_BROADCAST); lua_setfield(L, -2, "broadcast"); lua_pushboolean(L, flags & IFF_LOOPBACK); lua_setfield(L, -2, "loopback"); lua_pushboolean(L, flags & IFF_POINTOPOINT); lua_setfield(L, -2, "pointtopoint"); lua_pushboolean(L, flags & IFF_MULTICAST); lua_setfield(L, -2, "multicast"); } lua_setfield(L, -2, "flags"); } lua_rawseti(L, -2, ++i); } #ifndef _WIN32 freeifaddrs(result); #endif return 1; } /* * Arguments: text_address (string), [ip4_tonumber (true)] * Returns: [binary_address (string | number)] */ static int sock_inet_pton (lua_State *L) { const char *src = luaL_checkstring(L, 1); const int to_ip4 = lua_toboolean(L, 2); const int af = (!to_ip4 && strchr(src, ':')) ? AF_INET6 : AF_INET; struct sock_addr sa; void *inp = sock_addr_get_inp(&sa, af); const int in_len = sock_addr_get_inlen(af); #ifdef _WIN32 union sys_rwptr src_ptr; /* to avoid "const cast" warning */ #endif memset(&sa, 0, sizeof(struct sock_addr)); if (*src == '*') goto end; #ifndef _WIN32 if (inet_pton(af, src, inp) == 1) { #else sa.addrlen = sizeof(sa); src_ptr.r = src; if (!WSAStringToAddressA(src_ptr.w, af, NULL, &sa.u.addr, &sa.addrlen)) { #endif end: if (to_ip4) lua_pushnumber(L, ntohl(*((unsigned long *) inp))); else lua_pushlstring(L, inp, in_len); return 1; } return sys_seterror(L, 0); } /* * Arguments: binary_address (string | number) * Returns: [text_address (string)] */ static int sock_inet_ntop (lua_State *L) { const int is_ip4 = (lua_type(L, 1) == LUA_TNUMBER); unsigned long ip4; int in_len, af; const char *src; char buf[48]; if (is_ip4) { const lua_Number num = lua_tonumber(L, 1); in_len = 4; af = AF_INET; ip4 = htonl((unsigned long) num); src = (const char *) &ip4; } else { src = sock_checkladdr(L, 1, &in_len, &af); } #ifndef _WIN32 if (inet_ntop(af, src, buf, sizeof(buf)) == NULL) goto err; #else { struct sock_addr sa; void *inp = sock_addr_get_inp(&sa, af); const int sl = (af == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); DWORD buflen = sizeof(buf); memset(&sa, 0, sizeof(struct sock_addr)); memcpy(inp, src, in_len); sa.u.addr.sa_family = (short) af; if (WSAAddressToStringA(&sa.u.addr, sl, NULL, buf, &buflen) || buflen >= sizeof(buf)) goto err; } #endif lua_pushstring(L, buf); return 1; err: return sys_seterror(L, 0); } /* * Returns: sock_addr_udata */ static int sock_addr_new (lua_State *L) { lua_newuserdata(L, sizeof(struct sock_addr)); luaL_getmetatable(L, SA_TYPENAME); lua_setmetatable(L, -2); return 1; } /* * Arguments: sock_addr_udata, [port (number), binary_address (string)] * Returns: sock_addr_udata | port (number), binary_address (string) */ static int sock_addr_inet (lua_State *L) { struct sock_addr *sap = checkudata(L, 1, SA_TYPENAME); if (lua_gettop(L) == 1) { const int af = sap->u.addr.sa_family; if (af == AF_INET) { lua_pushinteger(L, ntohs(sap->u.in.sin_port)); lua_pushlstring(L, (char *) &sap->u.in.sin_addr, sizeof(struct in_addr)); } else if (af == AF_INET6) { lua_pushinteger(L, ntohs(sap->u.in6.sin6_port)); lua_pushlstring(L, (char *) &sap->u.in6.sin6_addr, sizeof(struct in6_addr)); } else return 0; return 2; } else { const int port = (int) lua_tointeger(L, 2); int in_len = SOCK_ADDR_LEN, af = AF_INET; const char *addr = lua_isnoneornil(L, 3) ? NULL : sock_checkladdr(L, 3, &in_len, &af); memset(sap, 0, sizeof(struct sock_addr)); sap->u.addr.sa_family = (short) af; if (af == AF_INET) { sap->u.in.sin_port = htons((unsigned short) port); if (addr) memcpy(&sap->u.in.sin_addr, addr, in_len); sap->addrlen = sizeof(struct sockaddr_in); } else { sap->u.in6.sin6_port = htons((unsigned short) port); if (addr) memcpy(&sap->u.in6.sin6_addr, addr, in_len); sap->addrlen = sizeof(struct sockaddr_in6); } lua_settop(L, 1); return 1; }; } /* * Arguments: sock_addr_udata, [path (string)] * Returns: sock_addr_udata | path (string) */ static int sock_addr_file (lua_State *L) { struct sock_addr *sap = checkudata(L, 1, SA_TYPENAME); #ifndef _WIN32 if (lua_gettop(L) == 1) { if (sap->u.addr.sa_family == AF_LOCAL) { lua_pushstring(L, sap->u.un.sun_path); return 1; } } else { size_t len; const char *path = luaL_checklstring(L, 2, &len); if (len < sizeof(sap->u.un.sun_path)) { sap->u.un.sun_family = AF_LOCAL; sap->addrlen = ++len; memcpy(sap->u.un.sun_path, path, len); lua_settop(L, 1); return 1; } }; #else (void) sap; #endif return 0; } /* * Arguments: sock_addr_udata, sd_udata * Returns: [sock_addr_udata] */ static int sock_addr_getsockname (lua_State *L) { struct sock_addr *sap = checkudata(L, 1, SA_TYPENAME); sd_t sd = (sd_t) lua_unboxinteger(L, 2, SD_TYPENAME); sap->addrlen = SOCK_ADDR_LEN; if (!getsockname(sd, &sap->u.addr, &sap->addrlen)) { lua_settop(L, 1); return 1; } return sys_seterror(L, 0); } /* * Arguments: sock_addr_udata, sd_udata * Returns: [sock_addr_udata] */ static int sock_addr_getpeername (lua_State *L) { struct sock_addr *sap = checkudata(L, 1, SA_TYPENAME); sd_t sd = (sd_t) lua_unboxinteger(L, 2, SD_TYPENAME); sap->addrlen = SOCK_ADDR_LEN; if (!getpeername(sd, &sap->u.addr, &sap->addrlen)) { lua_settop(L, 1); return 1; } return sys_seterror(L, 0); } /* * Arguments: sock_addr_udata * Returns: string */ static int sock_addr_tostring (lua_State *L) { struct sock_addr *sap = checkudata(L, 1, SA_TYPENAME); lua_pushfstring(L, SA_TYPENAME " (%p)", sap); return 1; } #define ADDR_METHODS \ {"getaddrinfo", sock_getaddrinfo}, \ {"getnameinfo", sock_getnameinfo}, \ {"getifaddrs", sock_getifaddrs}, \ {"inet_pton", sock_inet_pton}, \ {"inet_ntop", sock_inet_ntop}, \ {"addr", sock_addr_new} static luaL_Reg addr_meth[] = { {"inet", sock_addr_inet}, {"file", sock_addr_file}, {"getsockname", sock_addr_getsockname}, {"getpeername", sock_addr_getpeername}, {"__tostring", sock_addr_tostring}, {NULL, NULL} };
/* * Arguments: fd_udata, [options ...: * reset (string: "reset"), * baud_rate (number), * character_size (string: "cs5".."cs8"), * parity (string: "parno", "parodd", "pareven"), * stop_bits (string: "sb1", "sb2"), * flow_controls (string: "foff", "frtscts", "fxio")] * Returns: [fd_udata] */ static int comm_init (lua_State *L) { const fd_t fd = (fd_t) lua_unboxinteger(L, 1, FD_TYPENAME); const int narg = lua_gettop(L); int i; #ifndef _WIN32 struct termios tio; if (tcgetattr(fd, &tio) == -1) goto err; #else DCB dcb; dcb.DCBlength = sizeof(DCB); if (!GetCommState(fd, &dcb)) goto err; #endif for (i = 2; i <= narg; ++i) { #ifndef _WIN32 tcflag_t mask = 0, flag = 0; #endif if (lua_isnumber(L, i)) { const int baud_rate = lua_tointeger(L, i); #ifndef _WIN32 switch (baud_rate) { case 9600: flag = B9600; break; case 19200: flag = B19200; break; case 38400: flag = B38400; break; case 57600: flag = B57600; break; case 115200: flag = B115200; break; } if (cfsetispeed(&tio, flag) == -1 || cfsetospeed(&tio, flag) == -1) goto err; #else dcb.BaudRate = baud_rate; #endif } else { const char *opt = lua_tostring(L, i); const char *endp = opt + lua_rawlen(L, i) - 1; if (!opt) continue; switch (*opt) { case 'r': /* reset */ #ifndef _WIN32 memset(&tio, 0, sizeof(struct termios)); #else memset(&dcb, 0, sizeof(DCB)); #endif continue; case 'c': /* character size */ #ifndef _WIN32 switch (*endp) { case '5': flag = CS5; break; case '6': flag = CS6; break; case '7': flag = CS7; break; default: flag = CS8; } mask = CSIZE; #else dcb.ByteSize = (char) (*endp - '0'); #endif break; case 'p': /* parity */ #ifndef _WIN32 switch (*endp) { case 'd': flag = PARODD; case 'n': flag |= PARENB; break; default: flag = 0; /* no parity */ } mask = PARENB | PARODD; #else { int parity; switch (*endp) { case 'd': parity = ODDPARITY; break; case 'n': parity = EVENPARITY; break; default: parity = 0; /* no parity */ } dcb.Parity = (char) parity; dcb.fParity = (parity != 0); } #endif break; case 's': /* stop bits */ #ifndef _WIN32 if (*endp == '2') flag = CSTOPB; /* else: one stop bit */ mask = CSTOPB; #else dcb.StopBits = (char) (*endp == '2' ? TWOSTOPBITS : ONESTOPBIT); #endif break; case 'f': /* flow controls */ /* off */ #ifndef _WIN32 mask = CRTSCTS; tio.c_iflag &= ~(IXON | IXOFF | IXANY); #else dcb.fOutX = dcb.fInX = 0; dcb.fRtsControl = RTS_CONTROL_DISABLE; dcb.fOutxCtsFlow = 0; #endif switch (opt[1]) { case 'x': /* XON/XOFF */ #ifndef _WIN32 tio.c_iflag |= (IXON | IXOFF | IXANY); #else dcb.fOutX = dcb.fInX = 1; #endif break; case 'r': /* RTS/CTS */ #ifndef _WIN32 flag = CRTSCTS; #else dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; dcb.fOutxCtsFlow = 1; #endif break; } break; } } #ifndef _WIN32 tio.c_cflag &= ~mask; tio.c_cflag |= flag; #endif } #ifndef _WIN32 tio.c_cflag |= CLOCAL | CREAD; if (!tcsetattr(fd, TCSANOW, &tio)) { #else if (SetCommState(fd, &dcb)) { #endif lua_settop(L, 1); return 1; } err: return sys_seterror(L, 0); } /* * Arguments: fd_udata, [controls (string: "dtr", "dsr", "rts", "cts") ...] * Returns: [fd_udata] */ static int comm_control (lua_State *L) { const fd_t fd = (fd_t) lua_unboxinteger(L, 1, FD_TYPENAME); const int narg = lua_gettop(L); int i; #ifndef _WIN32 int flags; if (ioctl(fd, TIOCMGET, &flags) == -1) goto err; flags &= ~(TIOCM_DTR | TIOCM_DSR | TIOCM_RTS | TIOCM_CTS | TIOCM_ST | TIOCM_SR | TIOCM_CAR | TIOCM_RNG); #else DCB dcb; dcb.DCBlength = sizeof(DCB); if (!GetCommState(fd, &dcb)) goto err; dcb.fDtrControl = dcb.fOutxDsrFlow = dcb.fRtsControl = dcb.fOutxCtsFlow = 0; #endif for (i = 2; i <= narg; ++i) { const char *s = lua_tostring(L, i); if (!s) continue; switch (*s) { case 'd': /* DTR/DSR */ #ifndef _WIN32 flags |= (s[1] == 't') ? TIOCM_DTR : TIOCM_DSR; #else if (s[1] == 't') dcb.fDtrControl = 1; else dcb.fOutxDsrFlow = 1; #endif break; case 'r': /* RTS */ #ifndef _WIN32 flags |= TIOCM_RTS; #else dcb.fRtsControl = 1; #endif break; case 'c': /* CTS */ #ifndef _WIN32 flags |= TIOCM_CTS; #else dcb.fOutxCtsFlow = 1; #endif break; } } #ifndef _WIN32 if (!ioctl(fd, TIOCMSET, &flags)) { #else if (SetCommState(fd, &dcb)) { #endif lua_settop(L, 1); return 1; } err: return sys_seterror(L, 0); } /* * Arguments: fd_udata, [read_timeout (milliseconds)] * Returns: [fd_udata] */ static int comm_timeout (lua_State *L) { const fd_t fd = (fd_t) lua_unboxinteger(L, 1, FD_TYPENAME); const int rtime = lua_tointeger(L, 2); #ifndef _WIN32 struct termios tio; if (tcgetattr(fd, &tio) == -1) goto err; tio.c_cc[VTIME] = rtime / 100; tio.c_cc[VMIN] = 0; if (!tcsetattr(fd, TCSANOW, &tio)) { #else COMMTIMEOUTS timeouts; memset(&timeouts, 0, sizeof(COMMTIMEOUTS)); timeouts.ReadIntervalTimeout = rtime ? (DWORD) rtime : MAXDWORD; timeouts.ReadTotalTimeoutMultiplier = timeouts.ReadTotalTimeoutConstant = rtime; if (SetCommTimeouts(fd, &timeouts)) { #endif lua_settop(L, 1); return 1; } #ifndef _WIN32 err: #endif return sys_seterror(L, 0); } /* * Arguments: fd_udata, in_buffer_size (number), out_buffer_size (number) * Returns: [fd_udata] */ static int comm_queues (lua_State *L) { #ifndef _WIN32 if (1) { #else const fd_t fd = (fd_t) lua_unboxinteger(L, 1, FD_TYPENAME); const int rqueue = lua_tointeger(L, 2); const int wqueue = lua_tointeger(L, 3); if (SetupComm(fd, rqueue, wqueue)) { #endif lua_settop(L, 1); return 1; } #ifdef _WIN32 return sys_seterror(L, 0); #endif } /* * Arguments: fd_udata, [mode (string: "rw", "r", "w")] * Returns: [fd_udata] */ static int comm_purge (lua_State *L) { const fd_t fd = (fd_t) lua_unboxinteger(L, 1, FD_TYPENAME); const char *mode = lua_tostring(L, 2); int flags; flags = TCIOFLUSH; if (mode) switch (mode[0]) { case 'w': flags = TCOFLUSH; break; case 'r': if (!mode[1]) flags = TCIFLUSH; } #ifndef _WIN32 if (!tcflush(fd, flags)) { #else if (PurgeComm(fd, flags)) { #endif lua_settop(L, 1); return 1; } return sys_seterror(L, 0); } /* * Arguments: fd_udata, options (string: "car", "cts", "dsr", "ring") ... * Returns: [boolean ...] */ static int comm_wait (lua_State *L) { const fd_t fd = (fd_t) lua_unboxinteger(L, 1, FD_TYPENAME); const int narg = lua_gettop(L); unsigned long status = 0; int flags = 0; int i, res; for (i = 2; i <= narg; ++i) { const char *s = lua_tostring(L, i); if (!s) continue; switch (*s) { case 'c': /* CAR/CTS */ flags |= (s[1] == 'a') ? TIOCM_CAR : TIOCM_CTS; break; case 'd': /* DSR */ flags |= TIOCM_DSR; break; case 'r': /* RING */ flags |= TIOCM_RNG; break; } } sys_vm_leave(); #ifndef _WIN32 do { #ifdef TIOCMIWAIT res = !(ioctl(fd, TIOCMIWAIT, flags) || ioctl(fd, TIOCMGET, &status)); #else while ((res = !ioctl(fd, TIOCMGET, &status)) && !(status & flags)) usleep(10000); /* 10 msec polling */ #endif } while (!res && sys_eintr()); #else res = SetCommMask(fd, flags) && WaitCommEvent(fd, &status, NULL); #endif sys_vm_enter(); if (res) { if (flags & TIOCM_CAR) lua_pushboolean(L, status & TIOCM_CAR); if (flags & TIOCM_CTS) lua_pushboolean(L, status & TIOCM_CTS); if (flags & TIOCM_DSR) lua_pushboolean(L, status & TIOCM_DSR); if (flags & TIOCM_RNG) lua_pushboolean(L, status & TIOCM_RNG); return narg - 1; } return sys_seterror(L, 0); }
/* * Arguments: sd_udata, fd_udata, [count (number)] * Returns: [count (number) | false (EAGAIN)] */ static int sock_sendfile (lua_State *L) { sd_t sd = (sd_t) lua_unboxinteger(L, 1, SD_TYPENAME); fd_t fd = (fd_t) lua_unboxinteger(L, 2, FD_TYPENAME); size_t n = (size_t) lua_tointeger(L, 3); ssize_t res; sys_vm_leave(); #ifndef _WIN32 #if defined(__linux__) do res = sendfile(sd, fd, NULL, n ? n : ~((size_t) 0)); while (res == -1 && SYS_ERRNO == EINTR); #else { off_t nw, off = lseek(fd, 0, SEEK_CUR); #if defined(__APPLE__) && defined(__MACH__) nw = n; do res = sendfile(fd, sd, off, &nw, NULL, 0); #else do res = sendfile(fd, sd, off, n, NULL, &nw, 0); #endif while (res == -1 && SYS_ERRNO == EINTR); if (res != -1) { res = (size_t) nw; lseek(fd, nw, SEEK_CUR); } } #endif sys_vm_enter(); if (res != -1 || SYS_EAGAIN(SYS_ERRNO)) { if (res == -1) { lua_pushboolean(L, 0); return 1; } #else res = TransmitFileMap(sd, fd, n); sys_vm_enter(); if (res != 0L) { #endif lua_pushinteger(L, res); return 1; } return sys_seterror(L, 0); } /* * Arguments: sd_udata, {string | membuf_udata} ... * Returns: [success/partial (boolean), count (number)] */ static int sock_write (lua_State *L) { sd_t sd = (sd_t) lua_unboxinteger(L, 1, SD_TYPENAME); ssize_t n = 0; /* number of chars actually write */ int i, nargs = lua_gettop(L); for (i = 2; i <= nargs; ++i) { struct sys_buffer sb; int nw; if (!sys_buffer_read_init(L, i, &sb)) continue; sys_vm_leave(); #ifndef _WIN32 do nw = write(sd, sb.ptr.r, sb.size); while (nw == -1 && SYS_ERRNO == EINTR); #else { WSABUF buf = {sb.size, sb.ptr.w}; DWORD l; nw = !WSASend(sd, &buf, 1, &l, 0, NULL, NULL) ? l : -1; } #endif sys_vm_enter(); if (nw == -1) { if (n > 0 || SYS_EAGAIN(SYS_ERRNO)) break; return sys_seterror(L, 0); } n += nw; sys_buffer_read_next(&sb, nw); if ((size_t) nw < sb.size) break; } lua_pushboolean(L, (i > nargs)); lua_pushinteger(L, n); return 2; }
/* * Arguments: sd_udata, [count (number) | membuf_udata, * from (sock_addr_udata), options (string) ...] * Returns: [string | count (number) | false (EAGAIN)] */ static int sock_recv (lua_State *L) { static const int o_flags[] = { MSG_OOB, MSG_PEEK, #ifndef _WIN32 MSG_WAITALL #endif }; static const char *const o_names[] = { "oob", "peek", #ifndef _WIN32 "waitall", #endif NULL }; sd_t sd = (sd_t) lua_unboxinteger(L, 1, SD_TYPENAME); size_t n = !lua_isnumber(L, 2) ? ~((size_t) 0) : (size_t) lua_tointeger(L, 2); struct sock_addr *from = !lua_isuserdata(L, 3) ? NULL : checkudata(L, 3, SA_TYPENAME); struct sockaddr *sap = NULL; socklen_t *slp = NULL; const size_t len = n; /* how much total to read */ size_t rlen; /* how much to read */ int nr; /* number of bytes actually read */ struct sys_buffer sb; char buf[SYS_BUFSIZE]; unsigned int i, flags = 0; sys_buffer_write_init(L, 2, &sb, buf, sizeof(buf)); for (i = lua_gettop(L); i > 3; --i) { flags |= o_flags[luaL_checkoption(L, i, NULL, o_names)]; } if (from) { sap = &from->u.addr; slp = &from->addrlen; } do { rlen = (n <= sb.size) ? n : sb.size; sys_vm_leave(); #ifndef _WIN32 do nr = recvfrom(sd, sb.ptr.w, rlen, flags, sap, slp); while (nr == -1 && SYS_ERRNO == EINTR); #else nr = recvfrom(sd, sb.ptr.w, rlen, flags, sap, slp); #endif sys_vm_enter(); if (nr == -1) break; n -= nr; /* still have to read `n' bytes */ } while ((n != 0L && nr == (int) rlen) /* until end of count or eof */ && sys_buffer_write_next(L, &sb, buf, 0)); if (nr <= 0 && len == n) { if (!nr || !SYS_EAGAIN(SYS_ERRNO)) goto err; lua_pushboolean(L, 0); } else { if (!sys_buffer_write_done(L, &sb, buf, nr)) lua_pushinteger(L, len - n); } return 1; err: return sys_seterror(L, 0); }