/* * ircd-ratbox: A slightly useful ircd. * win32.c: select() compatible network routines. * * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center * Copyright (C) 1996-2002 Hybrid Development Team * Copyright (C) 2001 Adrian Chadd * Copyright (C) 2005-2006 Aaron Sethman * Copyright (C) 2002-2006 ircd-ratbox development team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * */ #include #include #include #ifdef _WIN32 static HWND hwnd; #define WM_SOCKET WM_USER /* * having gettimeofday is nice... */ typedef union { unsigned __int64 ft_i64; FILETIME ft_val; } FT_t; #ifdef __GNUC__ #define Const64(x) x##LL #else #define Const64(x) x##i64 #endif /* Number of 100 nanosecond units from 1/1/1601 to 1/1/1970 */ #define EPOCH_BIAS Const64(116444736000000000) pid_t rb_getpid() { return GetCurrentProcessId(); } int rb_gettimeofday(struct timeval *tp, void *not_used) { FT_t ft; /* this returns time in 100-nanosecond units (i.e. tens of usecs) */ GetSystemTimeAsFileTime(&ft.ft_val); /* seconds since epoch */ tp->tv_sec = (long)((ft.ft_i64 - EPOCH_BIAS) / Const64(10000000)); /* microseconds remaining */ tp->tv_usec = (long)((ft.ft_i64 / Const64(10)) % Const64(1000000)); return 0; } pid_t rb_spawn_process(const char *path, const char **argv) { PROCESS_INFORMATION pi; STARTUPINFO si; char cmd[MAX_PATH]; memset(&pi, 0, sizeof(pi)); memset(&si, 0, sizeof(si)); rb_strlcpy(cmd, path, sizeof(cmd)); if(CreateProcess(cmd, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) == FALSE) return -1; return (pi.dwProcessId); } pid_t rb_waitpid(pid_t pid, int *status, int flags) { DWORD timeout = (flags & WNOHANG) ? 0 : INFINITE; HANDLE hProcess; DWORD waitcode; hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid); if(hProcess) { waitcode = WaitForSingleObject(hProcess, timeout); if(waitcode == WAIT_TIMEOUT) { CloseHandle(hProcess); return 0; } else if(waitcode == WAIT_OBJECT_0) { if(GetExitCodeProcess(hProcess, &waitcode)) { *status = (int)((waitcode & 0xff) << 8); CloseHandle(hProcess); return pid; } } CloseHandle(hProcess); } else errno = ECHILD; return -1; } int rb_setenv(const char *name, const char *value, int overwrite) { char *buf; int len; if(!overwrite) { if((buf = getenv(name)) != NULL) { if(strlen(buf) > 0) { return 0; } } } if(name == NULL || value == NULL) return -1; len = strlen(name) + strlen(value) + 5; buf = rb_malloc(len); snprintf(buf, len, "%s=%s", name, value); len = putenv(buf); rb_free(buf); return (len); } int rb_kill(pid_t pid, int sig) { HANDLE hProcess; int ret = -1; hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid); if(hProcess) { switch (sig) { case 0: ret = 0; break; default: if(TerminateProcess(hProcess, sig)) ret = 0; break; } CloseHandle(hProcess); } else errno = EINVAL; return ret; } /* * packet format is uint32_t magic uint8_t protocol count WSAPROTOCOL_INFO * count size_t datasize data */ static int make_wsaprotocol_info(pid_t process, rb_fde_t *F, WSAPROTOCOL_INFO * inf) { WSAPROTOCOL_INFO info; if(!WSADuplicateSocket((SOCKET) rb_get_fd(F), process, &info)) { memcpy(inf, &info, sizeof(WSAPROTOCOL_INFO)); return 1; } return 0; } static rb_fde_t * make_fde_from_wsaprotocol_info(void *data) { WSAPROTOCOL_INFO *info = data; SOCKET t; t = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, info, 0, 0); if(t == INVALID_SOCKET) { rb_get_errno(); return NULL; } return rb_open(t, RB_FD_SOCKET, "remote_socket"); } static uint8_t fd_buf[16384]; #define MAGIC_CONTROL 0xFF0ACAFE int rb_send_fd_buf(rb_fde_t *xF, rb_fde_t **F, int count, void *data, size_t datasize, pid_t pid) { size_t bufsize = sizeof(uint32_t) + sizeof(uint8_t) + (sizeof(WSAPROTOCOL_INFO) * (size_t)count) + sizeof(size_t) + datasize; int i; uint32_t magic = MAGIC_CONTROL; void *ptr; if(count > 4) { errno = EINVAL; return -1; } if(bufsize > sizeof(fd_buf)) { errno = EINVAL; return -1; } memset(fd_buf, 0, sizeof(fd_buf)); ptr = fd_buf; memcpy(ptr, &magic, sizeof(magic)); ptr = (void *)((uintptr_t)ptr + (uintptr_t)sizeof(magic)); *((uint8_t *)ptr) = count; ptr = (void *)((uintptr_t)ptr + (uintptr_t)sizeof(uint8_t)); for(i = 0; i < count; i++) { make_wsaprotocol_info(pid, F[i], (WSAPROTOCOL_INFO *) ptr); ptr = (void *)((uintptr_t)ptr + (uintptr_t)sizeof(WSAPROTOCOL_INFO)); } memcpy(ptr, &datasize, sizeof(size_t)); ptr = (void *)((uintptr_t)ptr + (uintptr_t)sizeof(size_t)); memcpy(ptr, data, datasize); return rb_write(xF, fd_buf, bufsize); } #ifdef MYMIN #undef MYMIN #endif #define MYMIN(a, b) ((a) < (b) ? (a) : (b)) int rb_recv_fd_buf(rb_fde_t *F, void *data, size_t datasize, rb_fde_t **xF, int nfds) { size_t minsize = sizeof(uint32_t) + sizeof(uint8_t) + sizeof(size_t); size_t datalen; ssize_t retlen; uint32_t magic; uint8_t count; unsigned int i; void *ptr; ssize_t ret; memset(fd_buf, 0, sizeof(fd_buf)); /* some paranoia here... */ ret = rb_read(F, fd_buf, sizeof(fd_buf)); if(ret <= 0) { return ret; } if(ret < (ssize_t) minsize) { errno = EINVAL; return -1; } ptr = fd_buf; memcpy(&magic, ptr, sizeof(uint32_t)); if(magic != MAGIC_CONTROL) { errno = EAGAIN; return -1; } ptr = (void *)((uintptr_t)ptr + (uintptr_t)sizeof(uint32_t)); memcpy(&count, ptr, sizeof(uint8_t)); ptr = (void *)((uintptr_t)ptr + (uintptr_t)sizeof(uint8_t)); for(i = 0; i < count && i < (unsigned int)nfds; i++) { rb_fde_t *tF = make_fde_from_wsaprotocol_info(ptr); if(tF == NULL) return -1; xF[i] = tF; ptr = (void *)((uintptr_t)ptr + (uintptr_t)sizeof(WSAPROTOCOL_INFO)); } memcpy(&datalen, ptr, sizeof(size_t)); ptr = (void *)((uintptr_t)ptr + (uintptr_t)sizeof(size_t)); retlen = MYMIN(datalen, datasize); memcpy(data, ptr, datalen); return retlen; } static LRESULT CALLBACK rb_process_events(HWND nhwnd, UINT umsg, WPARAM wparam, LPARAM lparam) { rb_fde_t *F; PF *hdl; void *data; switch (umsg) { case WM_SOCKET: { F = rb_find_fd(wparam); if(F != NULL && IsFDOpen(F)) { switch (WSAGETSELECTEVENT(lparam)) { case FD_ACCEPT: case FD_CLOSE: case FD_READ: { if((hdl = F->read_handler) != NULL) { F->read_handler = NULL; data = F->read_data; F->read_data = NULL; hdl(F, data); } break; } case FD_CONNECT: case FD_WRITE: { if((hdl = F->write_handler) != NULL) { F->write_handler = NULL; data = F->write_data; F->write_data = NULL; hdl(F, data); } } } } return 0; } case WM_DESTROY: { PostQuitMessage(0); return 0; } default: return DefWindowProc(nhwnd, umsg, wparam, lparam); } return 0; } int rb_init_netio_win32(void) { /* this muchly sucks, but i'm too lazy to do overlapped i/o, maybe someday... -androsyn */ WNDCLASS wc; static const char *classname = "charybdis-class"; wc.style = 0; wc.lpfnWndProc = (WNDPROC) rb_process_events; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = classname; wc.hInstance = GetModuleHandle(NULL); if(!RegisterClass(&wc)) rb_lib_die("cannot register window class"); hwnd = CreateWindow(classname, classname, WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL, (HMENU) NULL, wc.hInstance, NULL); if(!hwnd) rb_lib_die("could not create window"); return 0; } void rb_sleep(unsigned int seconds, unsigned int useconds) { DWORD msec = seconds * 1000;; Sleep(msec); } int rb_setup_fd_win32(rb_fde_t *F) { if(F == NULL) return 0; SetHandleInformation((HANDLE) F->fd, HANDLE_FLAG_INHERIT, 0); switch (F->type) { case RB_FD_SOCKET: { unsigned long nonb = 1; if(ioctlsocket((SOCKET) F->fd, FIONBIO, &nonb) == -1) { rb_get_errno(); return 0; } return 1; } default: return 1; } } void rb_setselect_win32(rb_fde_t *F, unsigned int type, PF * handler, void *client_data) { int old_flags = F->pflags; lrb_assert(IsFDOpen(F)); /* Update the list, even though we're not using it .. */ if(type & RB_SELECT_READ) { if(handler != NULL) F->pflags |= FD_CLOSE | FD_READ | FD_ACCEPT; else F->pflags &= ~(FD_CLOSE | FD_READ | FD_ACCEPT); F->read_handler = handler; F->read_data = client_data; } if(type & RB_SELECT_WRITE) { if(handler != NULL) F->pflags |= FD_WRITE | FD_CONNECT; else F->pflags &= ~(FD_WRITE | FD_CONNECT); F->write_handler = handler; F->write_data = client_data; } if(old_flags == 0 && F->pflags == 0) return; if(F->pflags != old_flags) { WSAAsyncSelect(F->fd, hwnd, WM_SOCKET, F->pflags); } } static int has_set_timer = 0; int rb_select_win32(long delay) { MSG msg; if(has_set_timer == 0) { /* XXX should probably have this handle all the events * instead of busy looping */ SetTimer(hwnd, 0, delay, NULL); has_set_timer = 1; } if(GetMessage(&msg, NULL, 0, 0) == FALSE) { rb_lib_die("GetMessage failed..byebye"); } rb_set_time(); DispatchMessage(&msg); return RB_OK; } #ifdef strerror #undef strerror #endif static const char * _rb_strerror(int error) { switch (error) { case 0: return "Success"; case WSAEINTR: return "Interrupted system call"; case WSAEBADF: return "Bad file number"; case WSAEACCES: return "Permission denied"; case WSAEFAULT: return "Bad address"; case WSAEINVAL: return "Invalid argument"; case WSAEMFILE: return "Too many open sockets"; case WSAEWOULDBLOCK: return "Operation would block"; case WSAEINPROGRESS: return "Operation now in progress"; case WSAEALREADY: return "Operation already in progress"; case WSAENOTSOCK: return "Socket operation on non-socket"; case WSAEDESTADDRREQ: return "Destination address required"; case WSAEMSGSIZE: return "Message too long"; case WSAEPROTOTYPE: return "Protocol wrong type for socket"; case WSAENOPROTOOPT: return "Bad protocol option"; case WSAEPROTONOSUPPORT: return "Protocol not supported"; case WSAESOCKTNOSUPPORT: return "Socket type not supported"; case WSAEOPNOTSUPP: return "Operation not supported on socket"; case WSAEPFNOSUPPORT: return "Protocol family not supported"; case WSAEAFNOSUPPORT: return "Address family not supported"; case WSAEADDRINUSE: return "Address already in use"; case WSAEADDRNOTAVAIL: return "Can't assign requested address"; case WSAENETDOWN: return "Network is down"; case WSAENETUNREACH: return "Network is unreachable"; case WSAENETRESET: return "Net connection reset"; case WSAECONNABORTED: return "Software caused connection abort"; case WSAECONNRESET: return "Connection reset by peer"; case WSAENOBUFS: return "No buffer space available"; case WSAEISCONN: return "Socket is already connected"; case WSAENOTCONN: return "Socket is not connected"; case WSAESHUTDOWN: return "Can't send after socket shutdown"; case WSAETOOMANYREFS: return "Too many references, can't splice"; case WSAETIMEDOUT: return "Connection timed out"; case WSAECONNREFUSED: return "Connection refused"; case WSAELOOP: return "Too many levels of symbolic links"; case WSAENAMETOOLONG: return "File name too long"; case WSAEHOSTDOWN: return "Host is down"; case WSAEHOSTUNREACH: return "No route to host"; case WSAENOTEMPTY: return "Directory not empty"; case WSAEPROCLIM: return "Too many processes"; case WSAEUSERS: return "Too many users"; case WSAEDQUOT: return "Disc quota exceeded"; case WSAESTALE: return "Stale NFS file handle"; case WSAEREMOTE: return "Too many levels of remote in path"; case WSASYSNOTREADY: return "Network system is unavailable"; case WSAVERNOTSUPPORTED: return "Winsock version out of range"; case WSANOTINITIALISED: return "WSAStartup not yet called"; case WSAEDISCON: return "Graceful shutdown in progress"; case WSAHOST_NOT_FOUND: return "Host not found"; case WSANO_DATA: return "No host data of that type was found"; default: return strerror(error); } }; char * rb_strerror(int error) { static char buf[128]; rb_strlcpy(buf, _rb_strerror(error), sizeof(buf)); return buf; } const char * rb_path_to_self(void) { static char path_buf[MAX_PATH]; GetModuleFileName(NULL, path_buf, MAX_PATH); return path_buf; } #else /* win32 not supported */ int rb_init_netio_win32(void) { errno = ENOSYS; return -1; } void rb_setselect_win32(rb_fde_t *F, unsigned int type, PF * handler, void *client_data) { errno = ENOSYS; return; } int rb_select_win32(long delay) { errno = ENOSYS; return -1; } int rb_setup_fd_win32(rb_fde_t *F) { errno = ENOSYS; return -1; } #endif /* _WIN32 */