| /* | |
| * asynchronous DNS services | |
| * | |
| * (C) 1996,1997 Alex Korobka. | |
| * | |
| * TODO: Fork dns lookup helper during the startup (with a pipe | |
| * for communication) and make it fork for a database request | |
| * instead of forking the main process (i.e. something like | |
| * Netscape 4.0). | |
| */ | |
| #include "config.h" | |
| #include <unistd.h> | |
| #include <string.h> | |
| #include <signal.h> | |
| #include <sys/ioctl.h> | |
| #include <sys/types.h> | |
| #include <sys/ipc.h> | |
| #include <sys/msg.h> | |
| #include <sys/wait.h> | |
| #include <errno.h> | |
| #ifdef __EMX__ | |
| # include <sys/so_ioctl.h> | |
| #endif | |
| #ifdef HAVE_SYS_PARAM_H | |
| # include <sys/param.h> | |
| #endif | |
| #ifdef HAVE_SYS_FILIO_H | |
| # include <sys/filio.h> | |
| #endif | |
| #ifdef __svr4__ | |
| # include <sys/file.h> | |
| #endif | |
| #include "winsock.h" | |
| #include "windows.h" | |
| #include "heap.h" | |
| #include "ldt.h" | |
| #include "message.h" | |
| #include "selectors.h" | |
| #include "miscemu.h" | |
| #include "sig_context.h" | |
| #include "debug.h" | |
| #ifndef FASYNC | |
| #define FASYNC FIOASYNC | |
| #endif | |
| /* async DNS op control struct */ | |
| typedef struct | |
| { | |
| ws_async_op* ws_aop; | |
| char* buffer; | |
| int type; | |
| union | |
| { | |
| char* init; | |
| char* name; | |
| char* addr; | |
| } rq; | |
| unsigned ilength; | |
| } ws_async_ctl; | |
| extern HANDLE16 __ws_gethandle( void* ptr ); | |
| extern void* __ws_memalloc( int size ); | |
| extern void __ws_memfree( void* ptr ); | |
| /* NOTE: ws_async_op list is traversed inside the SIGIO handler! */ | |
| static int __async_io_max_fd = 0; | |
| static fd_set __async_io_fdset; | |
| static ws_async_op* __async_op_list = NULL; | |
| static void fixup_wshe(struct ws_hostent* p_wshe, void* base); | |
| static void fixup_wspe(struct ws_protoent* p_wspe, void* base); | |
| static void fixup_wsse(struct ws_servent* p_wsse, void* base); | |
| /* ----------------------------------- async/non-blocking I/O */ | |
| int WINSOCK_async_io(int fd, int async) | |
| { | |
| int fd_flags; | |
| #ifndef __EMX__ | |
| fcntl(fd, F_SETOWN, getpid()); | |
| #endif | |
| fd_flags = fcntl(fd, F_GETFL, 0); | |
| if (fcntl(fd, F_SETFL, (async)? fd_flags | FASYNC | |
| : fd_flags & ~FASYNC ) != -1) return 0; | |
| return -1; | |
| } | |
| int WINSOCK_unblock_io(int fd, int noblock) | |
| { | |
| int fd_flags; | |
| fd_flags = fcntl(fd, F_GETFL, 0); | |
| if (fcntl(fd, F_SETFL, (noblock)? fd_flags | O_NONBLOCK | |
| : fd_flags & ~O_NONBLOCK ) != -1) return 0; | |
| return -1; | |
| } | |
| int WINSOCK_check_async_op(ws_async_op* p_aop) | |
| { | |
| ws_async_op* p = __async_op_list; | |
| while( p ) if( p == p_aop ) return 1; | |
| else p = p->next; | |
| return 0; | |
| } | |
| int WINSOCK_cancel_async_op(ws_async_op* p_aop) | |
| { | |
| /* SIGIO unsafe! */ | |
| if( WINSOCK_check_async_op(p_aop) ) | |
| { | |
| if( !(p_aop->flags & WSMSG_DEAD_AOP) ) | |
| { | |
| kill(p_aop->pid, SIGKILL); | |
| waitpid(p_aop->pid, NULL, 0); /* just in case */ | |
| close(p_aop->fd[0]); | |
| } | |
| WINSOCK_unlink_async_op(p_aop); | |
| EVENT_DeleteIO( p_aop->fd[0], EVENT_IO_READ ); | |
| p_aop->flags = 0; | |
| p_aop->hWnd = p_aop->uMsg = 0; | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| void WINSOCK_cancel_task_aops(HTASK16 hTask, void (*__opfree)(void*)) | |
| { | |
| /* SIGIO safe, hTask == 0 cancels all outstanding async ops */ | |
| int num = 0, num_dead = 0; | |
| ws_async_op* p, *next; | |
| TRACE(winsock," cancelling async DNS requests... \n"); | |
| SIGNAL_MaskAsyncEvents( TRUE ); | |
| next = __async_op_list; | |
| while( (p = next) ) | |
| { | |
| HTASK16 hWndTask = GetWindowTask16(p->hWnd); | |
| next = p->next; | |
| if(!hTask || !hWndTask || (hTask == hWndTask)) | |
| { | |
| num++; | |
| if( p->flags & WSMSG_DEAD_AOP ) | |
| num_dead++; | |
| WINSOCK_cancel_async_op(p); | |
| if( __opfree ) __opfree(p); | |
| } | |
| } | |
| SIGNAL_MaskAsyncEvents( FALSE ); | |
| TRACE(winsock," -> %i total (%i active)\n", num, num - num_dead ); | |
| } | |
| void WINSOCK_link_async_op(ws_async_op* p_aop) | |
| { | |
| /* SIGIO safe */ | |
| p_aop->prev = NULL; | |
| SIGNAL_MaskAsyncEvents( TRUE ); | |
| if( __async_op_list ) | |
| { | |
| ws_async_op* p = __async_op_list; | |
| __async_op_list->prev = p_aop; | |
| /* traverse the list and retire dead ops created | |
| * by the signal handler (see below). */ | |
| while( p ) | |
| { | |
| if( p->flags & WSMSG_DEAD_AOP ) | |
| { | |
| ws_async_op* dead = p; | |
| TRACE(winsock,"\treaping dead aop [%08x]\n", (unsigned)p ); | |
| p = p->next; | |
| WINSOCK_unlink_async_op( dead ); | |
| __ws_memfree( dead ); | |
| continue; | |
| } | |
| p = p->next; | |
| } | |
| } | |
| else FD_ZERO(&__async_io_fdset); | |
| p_aop->next = __async_op_list; | |
| __async_op_list = p_aop; | |
| SIGNAL_MaskAsyncEvents( FALSE ); | |
| FD_SET(p_aop->fd[0], &__async_io_fdset); | |
| if( p_aop->fd[0] > __async_io_max_fd ) | |
| __async_io_max_fd = p_aop->fd[0]; | |
| } | |
| void WINSOCK_unlink_async_op(ws_async_op* p_aop) | |
| { | |
| /* SIGIO unsafe! */ | |
| if( p_aop == __async_op_list ) __async_op_list = p_aop->next; | |
| else | |
| p_aop->prev->next = p_aop->next; | |
| if( p_aop->next ) p_aop->next->prev = p_aop->prev; | |
| FD_CLR(p_aop->fd[0], &__async_io_fdset); | |
| if( p_aop->fd[0] == __async_io_max_fd ) | |
| __async_io_max_fd--; | |
| } | |
| /* ----------------------------------- SIGIO handler - | |
| * | |
| * link_async_op/unlink_async_op allow to install generic | |
| * async IO handlers (provided that aop_control function is defined). | |
| * | |
| * Note: pipe-based handlers must raise explicit SIGIO with kill(2). | |
| */ | |
| HANDLER_DEF(WINSOCK_sigio) | |
| { | |
| struct timeval timeout; | |
| fd_set check_set; | |
| ws_async_op* p_aop; | |
| HANDLER_INIT(); | |
| check_set = __async_io_fdset; | |
| memset(&timeout, 0, sizeof(timeout)); | |
| while( select(__async_io_max_fd + 1, | |
| &check_set, NULL, NULL, &timeout) > 0) | |
| { | |
| for( p_aop = __async_op_list; | |
| p_aop ; p_aop = p_aop->next ) | |
| if( FD_ISSET(p_aop->fd[0], &check_set) ) | |
| if( p_aop->aop_control(p_aop, AOP_IO) == AOP_CONTROL_REMOVE ) | |
| { | |
| /* NOTE: memory management is signal-unsafe, therefore | |
| * we can only set a flag to remove this p_aop later on. | |
| */ | |
| p_aop->flags = WSMSG_DEAD_AOP; | |
| close(p_aop->fd[0]); | |
| FD_CLR(p_aop->fd[0],&__async_io_fdset); | |
| if( p_aop->fd[0] == __async_io_max_fd ) | |
| __async_io_max_fd = p_aop->fd[0]; | |
| if( p_aop->pid ) | |
| { | |
| kill(p_aop->pid, SIGKILL); | |
| waitpid(p_aop->pid, NULL, WNOHANG); | |
| p_aop->pid = 0; | |
| } | |
| } | |
| check_set = __async_io_fdset; | |
| } | |
| } | |
| /* ----------------------------------- getXbyY requests */ | |
| /* child process control struct */ | |
| static ws_async_ctl async_ctl; | |
| static int aop_control(ws_async_op* p_aop, int flag ) | |
| { | |
| unsigned lLength; | |
| /* success: LOWORD(lLength) has the length of the struct | |
| * to read. | |
| * failure: LOWORD(lLength) is zero, HIWORD(lLength) contains | |
| * the error code. | |
| */ | |
| read(p_aop->fd[0], &lLength, sizeof(unsigned)); | |
| if( LOWORD(lLength) ) | |
| { | |
| if( (int)LOWORD(lLength) <= p_aop->buflen ) | |
| { | |
| char* buffer = (p_aop->flags & WSMSG_WIN32_AOP) | |
| ? p_aop->b.lin_base : (char*)PTR_SEG_TO_LIN(p_aop->b.seg_base); | |
| read(p_aop->fd[0], buffer, LOWORD(lLength)); | |
| switch( p_aop->flags & WSMSG_ASYNC_RQMASK ) | |
| { | |
| case WSMSG_ASYNC_HOSTBYNAME: | |
| case WSMSG_ASYNC_HOSTBYADDR: | |
| fixup_wshe((struct ws_hostent*)buffer, p_aop->b.ptr_base); break; | |
| case WSMSG_ASYNC_PROTOBYNAME: | |
| case WSMSG_ASYNC_PROTOBYNUM: | |
| fixup_wspe((struct ws_protoent*)buffer, p_aop->b.ptr_base); break; | |
| case WSMSG_ASYNC_SERVBYNAME: | |
| case WSMSG_ASYNC_SERVBYPORT: | |
| fixup_wsse((struct ws_servent*)buffer, p_aop->b.ptr_base); break; | |
| default: | |
| if( p_aop->flags ) WARN(winsock,"Received unknown async request!\n"); | |
| return AOP_CONTROL_REMOVE; | |
| } | |
| } | |
| else lLength = ((UINT32)LOWORD(lLength)) | ((unsigned)WSAENOBUFS << 16); | |
| } /* failure */ | |
| /* was a __WS_ASYNC_DEBUG statement */ | |
| TRACE(winsock, "DNS aop completed: hWnd [%04x], uMsg [%04x], " | |
| "aop [%04x], event [%08lx]\n", | |
| p_aop->hWnd, p_aop->uMsg, __ws_gethandle(p_aop), (LPARAM)lLength); | |
| /* FIXME: update num_async_rq */ | |
| EVENT_DeleteIO( p_aop->fd[0], EVENT_IO_READ ); | |
| PostMessage32A( p_aop->hWnd, p_aop->uMsg, __ws_gethandle(p_aop), (LPARAM)lLength ); | |
| return AOP_CONTROL_REMOVE; /* one-shot request */ | |
| } | |
| HANDLE16 __WSAsyncDBQuery(LPWSINFO pwsi, HWND32 hWnd, UINT32 uMsg, INT32 type, | |
| LPCSTR init, INT32 len, LPCSTR proto, void* sbuf, INT32 buflen, UINT32 flag) | |
| { | |
| /* queue 'flag' request and fork off its handler */ | |
| async_ctl.ws_aop = (ws_async_op*)__ws_memalloc(sizeof(ws_async_op)); | |
| if( async_ctl.ws_aop ) | |
| { | |
| HANDLE16 handle = __ws_gethandle(async_ctl.ws_aop); | |
| if( pipe(async_ctl.ws_aop->fd) == 0 ) | |
| { | |
| async_ctl.rq.init = (char*)init; | |
| async_ctl.ilength = len; | |
| async_ctl.buffer = (char*)proto; | |
| async_ctl.type = type; | |
| async_ctl.ws_aop->hWnd = hWnd; | |
| async_ctl.ws_aop->uMsg = uMsg; | |
| async_ctl.ws_aop->b.ptr_base = sbuf; | |
| async_ctl.ws_aop->buflen = buflen; | |
| async_ctl.ws_aop->flags = flag; | |
| async_ctl.ws_aop->aop_control = &aop_control; | |
| WINSOCK_link_async_op( async_ctl.ws_aop ); | |
| EVENT_AddIO( async_ctl.ws_aop->fd[0], EVENT_IO_READ ); | |
| pwsi->num_async_rq++; | |
| async_ctl.ws_aop->pid = fork(); | |
| if( async_ctl.ws_aop->pid ) | |
| { | |
| TRACE(winsock, "\tasync_op = %04x (child %i)\n", | |
| handle, async_ctl.ws_aop->pid); | |
| close(async_ctl.ws_aop->fd[1]); /* write endpoint */ | |
| if( async_ctl.ws_aop->pid > 0 ) | |
| return __ws_gethandle(async_ctl.ws_aop); | |
| /* fork() failed */ | |
| pwsi->num_async_rq--; | |
| EVENT_DeleteIO( async_ctl.ws_aop->fd[0], EVENT_IO_READ ); | |
| close(async_ctl.ws_aop->fd[0]); | |
| pwsi->err = WSAEWOULDBLOCK; | |
| } | |
| else | |
| { | |
| extern BOOL32 THREAD_InitDone; | |
| THREAD_InitDone = FALSE; | |
| /* child process */ | |
| close(async_ctl.ws_aop->fd[0]); /* read endpoint */ | |
| switch( flag & WSMSG_ASYNC_RQMASK ) | |
| { | |
| case WSMSG_ASYNC_HOSTBYADDR: | |
| case WSMSG_ASYNC_HOSTBYNAME: | |
| WS_do_async_gethost(pwsi, flag); | |
| break; | |
| case WSMSG_ASYNC_PROTOBYNUM: | |
| case WSMSG_ASYNC_PROTOBYNAME: | |
| WS_do_async_getproto(pwsi, flag); | |
| break; | |
| case WSMSG_ASYNC_SERVBYPORT: | |
| case WSMSG_ASYNC_SERVBYNAME: | |
| WS_do_async_getserv(pwsi, flag); | |
| break; | |
| } | |
| _exit(0); /* skip atexit()'ed cleanup */ | |
| } | |
| } | |
| else pwsi->err = wsaErrno(); /* failed to create pipe */ | |
| __ws_memfree((void*)async_ctl.ws_aop); | |
| } else pwsi->err = WSAEWOULDBLOCK; | |
| return 0; | |
| } | |
| static int _async_notify() | |
| { | |
| /* use half-duplex pipe to send variable length packets | |
| * to the parent process */ | |
| write(async_ctl.ws_aop->fd[1], &async_ctl.ilength, sizeof(unsigned)); | |
| write(async_ctl.ws_aop->fd[1], async_ctl.buffer, async_ctl.ilength ); | |
| #ifndef __EMX__ | |
| kill(getppid(), SIGIO); /* simulate async I/O */ | |
| #endif | |
| /* was a __WS_ASYNC_DEBUG statement */ | |
| TRACE(winsock, "handler - notify aop [%d, buf %d]\n", | |
| async_ctl.ilength, async_ctl.ws_aop->buflen); | |
| return 1; | |
| } | |
| static void _async_fail() | |
| { | |
| /* write a DWORD with error code (low word is zero) */ | |
| async_ctl.ilength = | |
| (h_errno < 0) ? (unsigned)WSAMAKEASYNCREPLY( 0, wsaErrno() ) | |
| : (unsigned)WSAMAKEASYNCREPLY( 0, wsaHerrno() ); | |
| write(async_ctl.ws_aop->fd[1], &async_ctl.ilength, sizeof(unsigned) ); | |
| #ifndef __EMX__ | |
| kill(getppid(), SIGIO); /* simulate async I/O */ | |
| #endif | |
| /* was a __WS_ASYNC_DEBUG statement */ | |
| TRACE(winsock, "handler - failed aop [%d, buf %d]\n", | |
| async_ctl.ilength, async_ctl.ws_aop->buflen); | |
| } | |
| void dump_ws_hostent_offset(struct ws_hostent* wshe) | |
| { | |
| int i; | |
| char* base = (char*)wshe; | |
| unsigned* ptr; | |
| DUMP("h_name = %08x\t[%s]\n", | |
| (unsigned)wshe->h_name, base + (unsigned)wshe->h_name); | |
| DUMP("h_aliases = %08x\t[%08x]\n", | |
| (unsigned)wshe->h_aliases, (unsigned)(base+(unsigned)wshe->h_aliases)); | |
| ptr = (unsigned*)(base + (unsigned)wshe->h_aliases); | |
| for(i = 0; ptr[i]; i++ ) | |
| { | |
| DUMP("%i - %08x [%s]\n", i + 1, ptr[i], ((char*)base) + ptr[i]); | |
| } | |
| DUMP("h_length = %i\n", wshe->h_length); | |
| } | |
| void WS_do_async_gethost(LPWSINFO pwsi, unsigned flag ) | |
| { | |
| int size = 0; | |
| struct hostent* p_he; | |
| close(async_ctl.ws_aop->fd[0]); | |
| p_he = (flag & WSMSG_ASYNC_HOSTBYNAME) | |
| ? gethostbyname(async_ctl.rq.name) | |
| : gethostbyaddr(async_ctl.rq.name, | |
| async_ctl.ilength, async_ctl.type); | |
| TRACE(winsock,"DNS: got hostent for [%s]\n", async_ctl.rq.name ); | |
| if( p_he ) /* convert to the Winsock format with internal pointers as offsets */ | |
| size = WS_dup_he(pwsi, p_he, WS_DUP_OFFSET | | |
| ((flag & WSMSG_WIN32_AOP) ? WS_DUP_LINEAR : WS_DUP_SEGPTR) ); | |
| if( size ) | |
| { | |
| async_ctl.buffer = (char*)pwsi->he; | |
| async_ctl.ilength = (unsigned)WSAMAKEASYNCREPLY( (UINT16)size, 0 ); | |
| _async_notify( flag ); | |
| } | |
| else _async_fail(); | |
| } | |
| void WS_do_async_getproto(LPWSINFO pwsi, unsigned flag ) | |
| { | |
| int size = 0; | |
| struct protoent* p_pe; | |
| close(async_ctl.ws_aop->fd[0]); | |
| p_pe = (flag & WSMSG_ASYNC_PROTOBYNAME) | |
| ? getprotobyname(async_ctl.rq.name) | |
| : getprotobynumber(async_ctl.type); | |
| TRACE(winsock,"DNS: got protoent for [%s]\n", async_ctl.rq.name ); | |
| if( p_pe ) /* convert to the Winsock format with internal pointers as offsets */ | |
| size = WS_dup_pe(pwsi, p_pe, WS_DUP_OFFSET | | |
| ((flag & WSMSG_WIN32_AOP) ? WS_DUP_LINEAR : WS_DUP_SEGPTR) ); | |
| if( size ) | |
| { | |
| async_ctl.buffer = (char*)pwsi->pe; | |
| async_ctl.ilength = (unsigned)WSAMAKEASYNCREPLY( (UINT16)size, 0 ); | |
| _async_notify( flag ); | |
| } | |
| else _async_fail(); | |
| } | |
| void WS_do_async_getserv(LPWSINFO pwsi, unsigned flag ) | |
| { | |
| int size = 0; | |
| struct servent* p_se; | |
| close(async_ctl.ws_aop->fd[0]); | |
| p_se = (flag & WSMSG_ASYNC_SERVBYNAME) | |
| ? getservbyname(async_ctl.rq.name, async_ctl.buffer) | |
| : getservbyport(async_ctl.type, async_ctl.buffer); | |
| if( p_se ) /* convert to the Winsock format with internal pointers as offsets */ | |
| size = WS_dup_se(pwsi, p_se, WS_DUP_OFFSET | | |
| ((flag & WSMSG_WIN32_AOP) ? WS_DUP_LINEAR : WS_DUP_SEGPTR) ); | |
| if( size ) | |
| { | |
| async_ctl.buffer = (char*)pwsi->se; | |
| async_ctl.ilength = (unsigned)WSAMAKEASYNCREPLY( (UINT16)size, 0 ); | |
| _async_notify( flag ); | |
| } | |
| else _async_fail(); | |
| } | |
| /* ----------------------------------- helper functions - | |
| * | |
| * Raw results from the pipe contain internal pointers stored as | |
| * offsets relative to the beginning of the buffer and we need | |
| * to apply a fixup before passing them to applications. | |
| * | |
| * NOTE: It is possible to exploit the fact that fork() doesn't | |
| * change the buffer address by storing fixed up pointers right | |
| * in the handler. However, this will get in the way if we ever | |
| * get around to implementing DNS helper daemon a-la Netscape 4.x. | |
| */ | |
| void fixup_wshe(struct ws_hostent* p_wshe, void* base) | |
| { | |
| /* add 'base' to ws_hostent pointers to convert them from offsets */ | |
| int i; | |
| unsigned* p_aliases,*p_addr; | |
| p_aliases = (unsigned*)((char*)p_wshe + (unsigned)p_wshe->h_aliases); | |
| p_addr = (unsigned*)((char*)p_wshe + (unsigned)p_wshe->h_addr_list); | |
| ((unsigned)(p_wshe->h_name)) += (unsigned)base; | |
| ((unsigned)(p_wshe->h_aliases)) += (unsigned)base; | |
| ((unsigned)(p_wshe->h_addr_list)) += (unsigned)base; | |
| for(i=0;p_aliases[i];i++) p_aliases[i] += (unsigned)base; | |
| for(i=0;p_addr[i];i++) p_addr[i] += (unsigned)base; | |
| } | |
| void fixup_wspe(struct ws_protoent* p_wspe, void* base) | |
| { | |
| int i; | |
| unsigned* p_aliases = (unsigned*)((char*)p_wspe + (unsigned)p_wspe->p_aliases); | |
| ((unsigned)(p_wspe->p_name)) += (unsigned)base; | |
| ((unsigned)(p_wspe->p_aliases)) += (unsigned)base; | |
| for(i=0;p_aliases[i];i++) p_aliases[i] += (unsigned)base; | |
| } | |
| void fixup_wsse(struct ws_servent* p_wsse, void* base) | |
| { | |
| int i; | |
| unsigned* p_aliases = (unsigned*)((char*)p_wsse + (unsigned)p_wsse->s_aliases); | |
| ((unsigned)(p_wsse->s_name)) += (unsigned)base; | |
| ((p_wsse->s_proto)) += (unsigned)base; | |
| ((p_wsse->s_aliases)) += (unsigned)base; | |
| for(i=0;p_aliases[i];i++) p_aliases[i] += (unsigned)base; | |
| } |