/* * 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 #include #include #include #include #include #include #include #include #include #ifdef __EMX__ # include #endif #ifdef HAVE_SYS_PARAM_H # include #endif #ifdef HAVE_SYS_FILIO_H # include #endif #ifdef HAVE_SYS_FILE_H # include #endif #include "winsock.h" #include "windows.h" #include "heap.h" #include "ldt.h" #include "message.h" #include "miscemu.h" #include "async.h" #include "debug.h" static void WINSOCK_async_handler(int unixfd,void *private); /* 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 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_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; } } p_aop->next = __async_op_list; __async_op_list = p_aop; SIGNAL_MaskAsyncEvents( FALSE ); ASYNC_RegisterFD(p_aop->fd[0],WINSOCK_async_handler,p_aop); } 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; ASYNC_UnregisterFD(p_aop->fd[0],WINSOCK_async_handler); } /* ----------------------------------- 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). */ static void WINSOCK_async_handler(int unixfd,void *private) { ws_async_op* p_aop = (ws_async_op*)private; 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]); if( p_aop->pid ) { kill(p_aop->pid, SIGKILL); waitpid(p_aop->pid, NULL, WNOHANG); p_aop->pid = 0; } } } /* ----------------------------------- 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; }