536 lines
15 KiB
C
536 lines
15 KiB
C
/*
|
|
* 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 <assert.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 HAVE_SYS_FILE_H
|
|
# include <sys/file.h>
|
|
#endif
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#ifdef HAVE_RESOLV_H
|
|
# include <resolv.h>
|
|
#endif
|
|
|
|
#include "winsock.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;
|
|
}
|