2 * asynchronous DNS services
4 * (C) 1996,1997 Alex Korobka.
6 * TODO: Fork dns lookup helper during the startup (with a pipe
7 * for communication) and make it fork for a database request
8 * instead of forking the main process (i.e. something like
15 #include <sys/ioctl.h>
16 #include <sys/types.h>
22 #include <sys/so_ioctl.h>
23 #include <sys/param.h>
27 #include <sys/filio.h>
41 #define FASYNC FIOASYNC
44 #define __WS_ASYNC_DEBUG 0
46 typedef struct /* async DNS op control struct */
60 extern HANDLE16 __ws_gethandle( void* ptr );
61 extern void* __ws_memalloc( int size );
62 extern void __ws_memfree( void* ptr );
64 /* NOTE: ws_async_op list is traversed inside the SIGIO handler! */
66 static int __async_io_max_fd = 0;
67 static fd_set __async_io_fdset;
68 static ws_async_op* __async_op_list = NULL;
70 static void fixup_wshe(struct ws_hostent* p_wshe, void* base);
71 static void fixup_wspe(struct ws_protoent* p_wspe, void* base);
72 static void fixup_wsse(struct ws_servent* p_wsse, void* base);
74 extern void EVENT_AddIO( int fd, unsigned flag );
75 extern void EVENT_DeleteIO( int fd, unsigned flag );
77 /* ----------------------------------- async/non-blocking I/O */
79 int WINSOCK_async_io(int fd, int async)
84 fcntl(fd, F_SETOWN, getpid());
87 fd_flags = fcntl(fd, F_GETFL, 0);
88 if (fcntl(fd, F_SETFL, (async)? fd_flags | FASYNC
89 : fd_flags & ~FASYNC ) != -1) return 0;
93 int WINSOCK_unblock_io(int fd, int noblock)
97 fd_flags = fcntl(fd, F_GETFL, 0);
98 if (fcntl(fd, F_SETFL, (noblock)? fd_flags | O_NONBLOCK
99 : fd_flags & ~O_NONBLOCK ) != -1) return 0;
103 int WINSOCK_check_async_op(ws_async_op* p_aop)
105 ws_async_op* p = __async_op_list;
106 while( p ) if( p == p_aop ) return 1;
111 int WINSOCK_cancel_async_op(ws_async_op* p_aop)
115 if( WINSOCK_check_async_op(p_aop) )
117 if( !(p_aop->flags & WSMSG_DEAD_AOP) )
119 kill(p_aop->pid, SIGKILL);
120 waitpid(p_aop->pid, NULL, 0); /* just in case */
123 WINSOCK_unlink_async_op(p_aop);
124 EVENT_DeleteIO( p_aop->fd[0], EVENT_IO_READ );
126 p_aop->hWnd = p_aop->uMsg = 0;
132 void WINSOCK_cancel_task_aops(HTASK16 hTask, void (*__opfree)(void*))
134 /* SIGIO safe, hTask == 0 cancels all outstanding async ops */
137 ws_async_op* p, *next;
139 dprintf_info(winsock," cancelling async DNS requests... \n");
141 SIGNAL_MaskAsyncEvents( TRUE );
142 next = __async_op_list;
145 HTASK16 hWndTask = GetWindowTask16(p->hWnd);
148 if(!hTask || !hWndTask || (hTask == hWndTask))
150 WINSOCK_cancel_async_op(p);
151 if( __opfree ) __opfree(p);
155 SIGNAL_MaskAsyncEvents( FALSE );
156 dprintf_info(winsock," -> %i total\n", num );
159 void WINSOCK_link_async_op(ws_async_op* p_aop)
164 SIGNAL_MaskAsyncEvents( TRUE );
165 if( __async_op_list )
167 ws_async_op* p = __async_op_list;
168 __async_op_list->prev = p_aop;
170 /* traverse the list and retire dead ops created
171 * by the signal handler (see below). */
175 if( p->flags & WSMSG_DEAD_AOP )
177 ws_async_op* dead = p;
179 dprintf_info(winsock,"\treaping dead aop [%08x]\n", (unsigned)p );
182 WINSOCK_unlink_async_op( dead );
183 __ws_memfree( dead );
189 else FD_ZERO(&__async_io_fdset);
190 p_aop->next = __async_op_list;
191 __async_op_list = p_aop;
192 SIGNAL_MaskAsyncEvents( FALSE );
194 FD_SET(p_aop->fd[0], &__async_io_fdset);
195 if( p_aop->fd[0] > __async_io_max_fd )
196 __async_io_max_fd = p_aop->fd[0];
199 void WINSOCK_unlink_async_op(ws_async_op* p_aop)
203 if( p_aop == __async_op_list ) __async_op_list = p_aop->next;
205 p_aop->prev->next = p_aop->next;
206 if( p_aop->next ) p_aop->next->prev = p_aop->prev;
208 FD_CLR(p_aop->fd[0], &__async_io_fdset);
209 if( p_aop->fd[0] == __async_io_max_fd )
213 /* ----------------------------------- SIGIO handler -
215 * link_async_op/unlink_async_op allow to install generic
216 * async IO handlers (provided that aop_control function is defined).
218 * Note: pipe-based handlers must raise explicit SIGIO with kill(2).
221 void WINSOCK_sigio(int signal)
223 struct timeval timeout;
227 check_set = __async_io_fdset;
228 memset(&timeout, 0, sizeof(timeout));
230 while( select(__async_io_max_fd + 1,
231 &check_set, NULL, NULL, &timeout) > 0)
233 for( p_aop = __async_op_list;
234 p_aop ; p_aop = p_aop->next )
235 if( FD_ISSET(p_aop->fd[0], &check_set) )
236 if( p_aop->aop_control(p_aop, AOP_IO) == AOP_CONTROL_REMOVE )
238 /* NOTE: memory management is signal-unsafe, therefore
239 * we can only set a flag to remove this p_aop later on.
242 p_aop->flags = WSMSG_DEAD_AOP;
244 FD_CLR(p_aop->fd[0],&__async_io_fdset);
245 if( p_aop->fd[0] == __async_io_max_fd )
246 __async_io_max_fd = p_aop->fd[0];
249 kill(p_aop->pid, SIGKILL);
250 waitpid(p_aop->pid, NULL, WNOHANG);
254 check_set = __async_io_fdset;
258 /* ----------------------------------- getXbyY requests */
260 static ws_async_ctl async_ctl; /* child process control struct */
262 static int aop_control(ws_async_op* p_aop, int flag )
266 /* success: LOWORD(lLength) has the length of the struct
268 * failure: LOWORD(lLength) is zero, HIWORD(lLength) contains
272 read(p_aop->fd[0], &lLength, sizeof(unsigned));
273 if( LOWORD(lLength) )
275 if( (int)LOWORD(lLength) <= p_aop->buflen )
277 char* buffer = (p_aop->flags & WSMSG_ASYNC_WIN32)
278 ? p_aop->b.lin_base : (char*)PTR_SEG_TO_LIN(p_aop->b.seg_base);
280 read(p_aop->fd[0], buffer, LOWORD(lLength));
281 switch( p_aop->flags )
283 case WSMSG_ASYNC_HOSTBYNAME:
284 case WSMSG_ASYNC_HOSTBYADDR:
285 fixup_wshe((struct ws_hostent*)buffer, p_aop->b.ptr_base); break;
286 case WSMSG_ASYNC_PROTOBYNAME:
287 case WSMSG_ASYNC_PROTOBYNUM:
288 fixup_wspe((struct ws_protoent*)buffer, p_aop->b.ptr_base); break;
289 case WSMSG_ASYNC_SERVBYNAME:
290 case WSMSG_ASYNC_SERVBYPORT:
291 fixup_wsse((struct ws_servent*)buffer, p_aop->b.ptr_base); break;
293 if( p_aop->flags ) fprintf(stderr,"Received unknown async request!\n");
294 return AOP_CONTROL_REMOVE;
297 else lLength = ((UINT32)LOWORD(lLength)) | ((unsigned)WSAENOBUFS << 16);
301 printf("DNS aop completed: hWnd [%04x], uMsg [%04x], aop [%04x], event [%08x]\n",
302 p_aop->hWnd, p_aop->uMsg, __ws_gethandle(p_aop), (LPARAM)lLength);
305 /* FIXME: update num_async_rq */
306 EVENT_DeleteIO( p_aop->fd[0], EVENT_IO_READ );
307 PostMessage32A( p_aop->hWnd, p_aop->uMsg, __ws_gethandle(p_aop), (LPARAM)lLength );
309 return AOP_CONTROL_REMOVE; /* one-shot request */
313 HANDLE16 __WSAsyncDBQuery(LPWSINFO pwsi, HWND32 hWnd, UINT32 uMsg, INT32 type, LPSTR init,
314 INT32 len, LPSTR proto, void* sbuf, INT32 buflen, UINT32 flag)
316 /* queue 'flag' request and fork off its handler */
318 async_ctl.ws_aop = (ws_async_op*)__ws_memalloc(sizeof(ws_async_op));
320 if( async_ctl.ws_aop )
322 HANDLE16 handle = __ws_gethandle(async_ctl.ws_aop);
324 if( pipe(async_ctl.ws_aop->fd) == 0 )
326 async_ctl.rq.init = (char*)init;
327 async_ctl.ilength = len;
328 async_ctl.buffer = proto;
329 async_ctl.type = type;
331 async_ctl.ws_aop->hWnd = hWnd;
332 async_ctl.ws_aop->uMsg = uMsg;
333 async_ctl.ws_aop->b.ptr_base = sbuf;
334 async_ctl.ws_aop->buflen = buflen;
335 async_ctl.ws_aop->flags = flag;
336 async_ctl.ws_aop->aop_control = &aop_control;
338 WINSOCK_link_async_op( async_ctl.ws_aop );
340 EVENT_AddIO( async_ctl.ws_aop->fd[0], EVENT_IO_READ );
341 pwsi->num_async_rq++;
343 async_ctl.ws_aop->pid = fork();
344 if( async_ctl.ws_aop->pid )
346 dprintf_info(winsock, "\tasync_op = %04x (child %i)\n",
347 handle, async_ctl.ws_aop->pid);
349 close(async_ctl.ws_aop->fd[1]); /* write endpoint */
350 if( async_ctl.ws_aop->pid > 0 )
351 return __ws_gethandle(async_ctl.ws_aop);
355 pwsi->num_async_rq--;
356 EVENT_DeleteIO( async_ctl.ws_aop->fd[0], EVENT_IO_READ );
357 close(async_ctl.ws_aop->fd[0]);
358 pwsi->err = WSAEWOULDBLOCK;
364 close(async_ctl.ws_aop->fd[0]); /* read endpoint */
367 case WSMSG_ASYNC_HOSTBYADDR:
368 case WSMSG_ASYNC_HOSTBYNAME:
369 WS_do_async_gethost(pwsi, flag);
371 case WSMSG_ASYNC_PROTOBYNUM:
372 case WSMSG_ASYNC_PROTOBYNAME:
373 WS_do_async_getproto(pwsi, flag);
375 case WSMSG_ASYNC_SERVBYPORT:
376 case WSMSG_ASYNC_SERVBYNAME:
377 WS_do_async_getserv(pwsi, flag);
380 _exit(0); /* skip atexit()'ed cleanup */
383 else pwsi->err = wsaErrno(); /* failed to create pipe */
385 __ws_memfree((void*)async_ctl.ws_aop);
386 } else pwsi->err = WSAEWOULDBLOCK;
390 static int _async_notify()
392 /* use half-duplex pipe to send variable length packets
393 * to the parent process */
395 write(async_ctl.ws_aop->fd[1], &async_ctl.ilength, sizeof(unsigned));
396 write(async_ctl.ws_aop->fd[1], async_ctl.buffer, async_ctl.ilength );
399 kill(getppid(), SIGIO); /* simulate async I/O */
403 printf("handler - notify aop [%d, buf %d]\n", async_ctl.ilength, async_ctl.ws_aop->buflen);
408 static void _async_fail()
410 /* write a DWORD with error code (low word is zero) */
413 (h_errno < 0) ? (unsigned)WSAMAKEASYNCREPLY( 0, wsaErrno() )
414 : (unsigned)WSAMAKEASYNCREPLY( 0, wsaHerrno() );
415 write(async_ctl.ws_aop->fd[1], &async_ctl.ilength, sizeof(unsigned) );
417 kill(getppid(), SIGIO); /* simulate async I/O */
421 printf("handler - failed aop [%d, buf %d]\n", async_ctl.ilength, async_ctl.ws_aop->buflen);
425 void dump_ws_hostent_offset(struct ws_hostent* wshe)
428 char* base = (char*)wshe;
431 printf("h_name = %08x\t[%s]\n", (unsigned)wshe->h_name, base + (unsigned)wshe->h_name);
432 printf("h_aliases = %08x\t[%08x]\n", (unsigned)wshe->h_aliases,
433 (unsigned)(base + (unsigned)wshe->h_aliases));
434 ptr = (unsigned*)(base + (unsigned)wshe->h_aliases);
435 for(i = 0; ptr[i]; i++ )
437 printf("%i - %08x ", i + 1, ptr[i]);
438 printf(" [%s]\n", ((char*)base) + ptr[i]);
440 printf("h_length = %i\n", wshe->h_length);
443 void WS_do_async_gethost(LPWSINFO pwsi, unsigned flag )
446 struct hostent* p_he;
448 close(async_ctl.ws_aop->fd[0]);
450 p_he = (flag & WSMSG_ASYNC_HOSTBYNAME)
451 ? gethostbyname(async_ctl.rq.name)
452 : gethostbyaddr(async_ctl.rq.name,
453 async_ctl.ilength, async_ctl.type);
455 dprintf_info(winsock,"DNS: got hostent for [%s]\n", async_ctl.rq.name );
457 if( p_he ) /* convert to the Winsock format with internal pointers as offsets */
458 size = WS_dup_he(pwsi, p_he, WS_DUP_OFFSET |
459 ((flag & WSMSG_ASYNC_WIN32) ? WS_DUP_LINEAR : WS_DUP_SEGPTR) );
462 async_ctl.buffer = pwsi->buffer;
463 async_ctl.ilength = (unsigned)WSAMAKEASYNCREPLY( (UINT16)size, 0 );
464 _async_notify( flag );
469 void WS_do_async_getproto(LPWSINFO pwsi, unsigned flag )
472 struct protoent* p_pe;
474 close(async_ctl.ws_aop->fd[0]);
475 p_pe = (flag & WSMSG_ASYNC_PROTOBYNAME)
476 ? getprotobyname(async_ctl.rq.name)
477 : getprotobynumber(async_ctl.type);
479 dprintf_info(winsock,"DNS: got protoent for [%s]\n", async_ctl.rq.name );
481 if( p_pe ) /* convert to the Winsock format with internal pointers as offsets */
482 size = WS_dup_pe(pwsi, p_pe, WS_DUP_OFFSET |
483 ((flag & WSMSG_ASYNC_WIN32) ? WS_DUP_LINEAR : WS_DUP_SEGPTR) );
486 async_ctl.buffer = pwsi->buffer;
487 async_ctl.ilength = (unsigned)WSAMAKEASYNCREPLY( (UINT16)size, 0 );
488 _async_notify( flag );
493 void WS_do_async_getserv(LPWSINFO pwsi, unsigned flag )
496 struct servent* p_se;
498 close(async_ctl.ws_aop->fd[0]);
499 p_se = (flag & WSMSG_ASYNC_SERVBYNAME)
500 ? getservbyname(async_ctl.rq.name, async_ctl.buffer)
501 : getservbyport(async_ctl.type, async_ctl.buffer);
503 if( p_se ) /* convert to the Winsock format with internal pointers as offsets */
504 size = WS_dup_se(pwsi, p_se, WS_DUP_OFFSET |
505 ((flag & WSMSG_ASYNC_WIN32) ? WS_DUP_LINEAR : WS_DUP_SEGPTR) );
508 async_ctl.buffer = pwsi->buffer;
509 async_ctl.ilength = (unsigned)WSAMAKEASYNCREPLY( (UINT16)size, 0 );
510 _async_notify( flag );
515 /* ----------------------------------- helper functions -
517 * Raw results from pipe contain internal pointers stored as
518 * offsets relative to the beginning of the buffer and we need
519 * to apply a fixup before passing them to applications.
521 * NOTE: It is possible to exploit the fact that fork() doesn't
522 * change the buffer address by storing fixed up pointers right
523 * in the handler. However, this will get in the way if we ever
524 * get around to implementing DNS helper daemon a-la Netscape.
527 void fixup_wshe(struct ws_hostent* p_wshe, void* base)
529 /* add 'base' to ws_hostent pointers to convert them from offsets */
532 unsigned* p_aliases,*p_addr;
534 p_aliases = (unsigned*)((char*)p_wshe + (unsigned)p_wshe->h_aliases);
535 p_addr = (unsigned*)((char*)p_wshe + (unsigned)p_wshe->h_addr_list);
536 ((unsigned)(p_wshe->h_name)) += (unsigned)base;
537 ((unsigned)(p_wshe->h_aliases)) += (unsigned)base;
538 ((unsigned)(p_wshe->h_addr_list)) += (unsigned)base;
539 for(i=0;p_aliases[i];i++) p_aliases[i] += (unsigned)base;
540 for(i=0;p_addr[i];i++) p_addr[i] += (unsigned)base;
543 void fixup_wspe(struct ws_protoent* p_wspe, void* base)
546 unsigned* p_aliases = (unsigned*)((char*)p_wspe + (unsigned)p_wspe->p_aliases);
547 ((unsigned)(p_wspe->p_name)) += (unsigned)base;
548 ((unsigned)(p_wspe->p_aliases)) += (unsigned)base;
549 for(i=0;p_aliases[i];i++) p_aliases[i] += (unsigned)base;
552 void fixup_wsse(struct ws_servent* p_wsse, void* base)
555 unsigned* p_aliases = (unsigned*)((char*)p_wsse + (unsigned)p_wsse->s_aliases);
556 ((unsigned)(p_wsse->s_name)) += (unsigned)base;
557 ((p_wsse->s_proto)) += (unsigned)base;
558 ((p_wsse->s_aliases)) += (unsigned)base;
559 for(i=0;p_aliases[i];i++) p_aliases[i] += (unsigned)base;