├── Makefile ├── netout.c ├── simplesocket.h └── winsocket.h /Makefile: -------------------------------------------------------------------------------- 1 | LUAINC = -I/usr/local/include 2 | LUALIB = -L/usr/local/bin -llua53 3 | 4 | CC= gcc 5 | CFLAGS = -O2 -Wall 6 | 7 | all : netout.dll 8 | 9 | netout.dll : netout.c 10 | $(CC) -o $@ --shared $^ $(LUAINC) $(LUALIB) -lws2_32 11 | 12 | clean : 13 | rm netout.dll -------------------------------------------------------------------------------- /netout.c: -------------------------------------------------------------------------------- 1 | #define LUA_LIB 2 | #include 3 | #include 4 | #include 5 | #include "simplesocket.h" 6 | #include 7 | 8 | union sockaddr_all { 9 | struct sockaddr s; 10 | struct sockaddr_in v4; 11 | struct sockaddr_in6 v6; 12 | }; 13 | 14 | static int 15 | string_to_sockaddr(lua_State *L, int addr_idx, int port_idx, union sockaddr_all *sa, socklen_t *retsz) { 16 | size_t sz; 17 | const char * buf = luaL_checklstring(L, addr_idx, &sz); 18 | int port = luaL_checkinteger(L, port_idx); 19 | int family; 20 | void *ptr; 21 | if (memchr(buf, ':', sz)) { 22 | // ipv6 23 | family = AF_INET6; 24 | *retsz = sizeof(sa->v6); 25 | ptr = &sa->v6.sin6_addr; 26 | sa->v6.sin6_port = htons(port); 27 | } else { 28 | // ipv4 29 | family = AF_INET; 30 | *retsz = sizeof(sa->v4); 31 | ptr = &sa->v4.sin_addr; 32 | sa->v4.sin_port = htons(port); 33 | } 34 | if (inet_pton(family, buf, ptr) != 1) { 35 | return -1; 36 | } 37 | sa->s.sa_family = family; 38 | 39 | return family; 40 | } 41 | 42 | #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) 43 | 44 | struct thread_args { 45 | HANDLE readpipe; 46 | SOCKET sock; 47 | }; 48 | 49 | struct pipe_ud { 50 | HANDLE oldstdout; 51 | HANDLE thread; 52 | }; 53 | 54 | static DWORD WINAPI 55 | redirect_thread(LPVOID lpParam) { 56 | struct thread_args *ta = (struct thread_args *)lpParam; 57 | HANDLE rp = ta->readpipe; 58 | SOCKET fd = ta->sock; 59 | free(ta); 60 | 61 | char tmp[1024]; 62 | DWORD sz; 63 | 64 | while (ReadFile(rp, tmp, sizeof(tmp), &sz, NULL)) { 65 | send(fd, tmp, sz, 0); 66 | } 67 | CloseHandle(rp); 68 | closesocket(fd); 69 | return 0; 70 | } 71 | 72 | static int 73 | lclosepipe(lua_State *L) { 74 | struct pipe_ud *closepipe = (struct pipe_ud *)lua_touserdata(L, 1); 75 | if (closepipe) { 76 | HANDLE oso = closepipe->oldstdout; 77 | if (oso != NULL) { 78 | // restore stdout 79 | int fd = _open_osfhandle((intptr_t)closepipe->oldstdout, 0); 80 | _dup2(fd, STDOUT_FILENO); 81 | _close(fd); 82 | SetStdHandle(STD_OUTPUT_HANDLE, closepipe->oldstdout); 83 | closepipe->oldstdout = NULL; 84 | } 85 | WaitForSingleObject(closepipe->thread, INFINITE); 86 | CloseHandle(closepipe->thread); 87 | } 88 | return 0; 89 | } 90 | 91 | static void 92 | redirect(lua_State *L, int fd) { 93 | HANDLE rp, wp; 94 | 95 | BOOL succ = CreatePipe(&rp, &wp, NULL, 0); 96 | if (!succ) { 97 | close(fd); 98 | luaL_error(L, "CreatePipe failed"); 99 | } 100 | 101 | struct thread_args * ta = malloc(sizeof(*ta)); 102 | ta->readpipe = rp; 103 | ta->sock = fd; 104 | // thread don't need large stack 105 | HANDLE thread = CreateThread(NULL, 4096, redirect_thread, (LPVOID)ta, 0, NULL); 106 | 107 | int wpfd = _open_osfhandle((intptr_t)wp, 0); 108 | if (_dup2(wpfd, STDOUT_FILENO) != 0) { 109 | close(fd); 110 | luaL_error(L, "dup2() failed"); 111 | } 112 | _close(wpfd); 113 | struct pipe_ud * closepipe = (struct pipe_ud *)lua_newuserdata(L, sizeof(*closepipe)); 114 | closepipe->oldstdout = NULL; 115 | closepipe->thread = thread; 116 | lua_createtable(L, 0, 1); 117 | lua_pushcfunction(L, lclosepipe); 118 | lua_setfield(L, -2, "__gc"); 119 | lua_setmetatable(L, -2); 120 | lua_setfield(L, LUA_REGISTRYINDEX, "STDOUT_PIPE"); 121 | closepipe->oldstdout = GetStdHandle(STD_OUTPUT_HANDLE); 122 | SetStdHandle(STD_OUTPUT_HANDLE, wp); 123 | } 124 | 125 | 126 | #else 127 | 128 | static void 129 | redirect(lua_State *L, SOCKET fd) { 130 | int r =dup2(fd, STDOUT_FILENO); 131 | close(fd); 132 | if (r != 0) { 133 | luaL_error(L, "dup2() failed"); 134 | } 135 | } 136 | 137 | #endif 138 | 139 | static int 140 | wait(SOCKET fd, int timeout) { 141 | struct timeval tv; 142 | fd_set rfds; 143 | FD_ZERO(&rfds); 144 | FD_SET(fd, &rfds); 145 | tv.tv_sec = timeout; 146 | tv.tv_usec = 0; 147 | return select(fd+1, &rfds, NULL, NULL, &tv); 148 | } 149 | 150 | static int 151 | linit(lua_State *L) { 152 | union sockaddr_all sa; 153 | socklen_t ssz; 154 | int af = string_to_sockaddr(L, 1,2, &sa, &ssz); 155 | SOCKET fd = socket(af, SOCK_STREAM, 0); 156 | if (fd < 0) { 157 | return luaL_error(L, "Can't create socket"); 158 | } 159 | int reuse = 1; 160 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(int)) == -1) { 161 | closesocket(fd); 162 | return luaL_error(L, "Can't set reuse %s %d", lua_tostring(L, 1), lua_tointeger(L, 2)); 163 | } 164 | if (bind(fd, &sa.s, ssz) == -1) { 165 | closesocket(fd); 166 | return luaL_error(L, "Can't bind %s %d", lua_tostring(L, 1), lua_tointeger(L, 2)); 167 | } 168 | if (listen(fd, 1) == -1) { // backlog == 1, only accept one connection 169 | closesocket(fd); 170 | return luaL_error(L, "Can't listen %s %d", lua_tostring(L, 1), lua_tointeger(L, 2)); 171 | } 172 | int timeout = luaL_optinteger(L, 3, 0); 173 | if (timeout > 0) { 174 | int n = wait(fd, timeout); 175 | if (n <= 0) { 176 | closesocket(fd); 177 | return luaL_error(L, "time out"); 178 | } 179 | } 180 | SOCKET sock = accept(fd, NULL, NULL); 181 | closesocket(fd); 182 | if (sock == -1) { 183 | return luaL_error(L, "accept failed %s %d", lua_tostring(L, 1), lua_tointeger(L, 2)); 184 | } 185 | 186 | redirect(L, sock); 187 | 188 | fprintf(stderr, "init %s %d\n", lua_tostring(L, 1), lua_tointeger(L, 2)); 189 | return 0; 190 | } 191 | 192 | LUAMOD_API int 193 | luaopen_netout(lua_State *L) { 194 | luaL_checkversion(L); 195 | luaL_Reg l[] = { 196 | { "init" , linit }, 197 | { NULL, NULL }, 198 | }; 199 | simplesocket_init(); 200 | luaL_newlib(L, l); 201 | return 1; 202 | } 203 | -------------------------------------------------------------------------------- /simplesocket.h: -------------------------------------------------------------------------------- 1 | #ifndef simple_socket_h 2 | #define simple_socket_h 3 | 4 | #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) 5 | 6 | #include "winsocket.h" 7 | 8 | #else 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define SOCK_EAGAIN EAGAIN 18 | #define SOCK_EINPROGRESS EINPROGRESS 19 | #define SOCKET int 20 | #define closesocket close 21 | 22 | static void simplesocket_init() {} 23 | 24 | #endif 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /winsocket.h: -------------------------------------------------------------------------------- 1 | #ifndef windows_socket_h 2 | #define windows_socket_h 3 | 4 | // If you need more connection, change it. 5 | // But we don't sugguest use windows version in production 6 | #define FD_SETSIZE 4096 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define SO_NOSIGPIPE 0 // ignore it, don't support 15 | 16 | #ifdef errno 17 | #undef errno 18 | #endif 19 | #define errno WSAGetLastError() 20 | 21 | #define SOCK_EAGAIN WSATRY_AGAIN 22 | // In windows, connect returns WSAEWOULDBLOCK rather than WSAEINPROGRESS 23 | #define SOCK_EINPROGRESS WSAEWOULDBLOCK 24 | 25 | // only support fcntl(fd, F_SETFL, O_NONBLOCK) 26 | #define F_SETFL 0 27 | #define O_NONBLOCK 0 28 | typedef u_short sa_family_t; 29 | typedef int socklen_t; 30 | 31 | static int 32 | win_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen) { 33 | if (optname == SO_NOSIGPIPE) { 34 | // ignore 35 | return 0; 36 | } 37 | int size = (int)*optlen; 38 | int ret = getsockopt(sockfd, level, optname, (char *)optval, &size); 39 | if (ret == 0) { 40 | *optlen = size; 41 | return 0; 42 | } else { 43 | return -1; 44 | } 45 | } 46 | 47 | static int 48 | win_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen) { 49 | if (optname == SO_NOSIGPIPE) { 50 | // ignore 51 | return 0; 52 | } 53 | int ret = setsockopt(sockfd, level, optname, (const char *)optval, (int)optlen); 54 | if (ret == 0) { 55 | return 0; 56 | } else { 57 | return -1; 58 | } 59 | } 60 | 61 | static int 62 | fcntl(SOCKET fd, int cmd, int value) { 63 | unsigned long on = 1; 64 | return ioctlsocket(fd, FIONBIO, &on); 65 | } 66 | 67 | #define NS_INT16SZ 2 68 | #define NS_IN6ADDRSZ 16 69 | 70 | static const char * 71 | inet_ntop4(const unsigned char *src, char *dst, size_t size) { 72 | char tmp[sizeof "255.255.255.255"]; 73 | size_t len = snprintf(tmp, sizeof(tmp), "%u.%u.%u.%u", src[0], src[1], src[2], src[3]); 74 | if (len >= size) { 75 | return NULL; 76 | } 77 | memcpy(dst, tmp, len + 1); 78 | 79 | return dst; 80 | } 81 | 82 | static const char * 83 | inet_ntop(int af, const void *src, char *dst, socklen_t size) { 84 | switch (af) { 85 | case AF_INET: 86 | return inet_ntop4((const unsigned char*)src, dst, size); 87 | case AF_INET6: 88 | // don't support ipv6 in windows 89 | return NULL; 90 | default: 91 | return NULL; 92 | } 93 | } 94 | 95 | static int 96 | inet_pton(int af, const char *src, void *dst) { 97 | if (af == AF_INET) { 98 | unsigned long ip = inet_addr(src); 99 | if (ip == INADDR_NONE) 100 | return 0; 101 | memcpy(dst,&ip,sizeof(ip)); 102 | return 1; 103 | } else { 104 | // only support ipv4 in windows 105 | // todo: call InetPton in windows 8+ 106 | return 0; 107 | } 108 | } 109 | 110 | static void 111 | simplesocket_init() { 112 | WSADATA wsaData; 113 | WSAStartup(MAKEWORD(2,2), &wsaData); 114 | } 115 | 116 | #define getsockopt win_getsockopt 117 | #define setsockopt win_setsockopt 118 | 119 | #endif --------------------------------------------------------------------------------