Release 980503
[wine] / misc / winsock_dns.c
1 /*
2  * asynchronous DNS services
3  * 
4  * (C) 1996,1997 Alex Korobka.
5  *
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
9  *       Netscape 4.0).
10  */
11
12 #include "config.h"
13
14 #include <unistd.h>
15 #include <string.h>
16 #include <signal.h>
17 #include <sys/ioctl.h>
18 #include <sys/types.h>
19 #include <sys/ipc.h>
20 #include <sys/msg.h>
21 #include <sys/wait.h>
22 #include <errno.h>
23 #ifdef __EMX__
24 # include <sys/so_ioctl.h>
25 #endif
26 #ifdef HAVE_SYS_PARAM_H
27 # include <sys/param.h>
28 #endif
29 #ifdef HAVE_SYS_FILIO_H
30 # include <sys/filio.h>
31 #endif
32 #ifdef __svr4__
33 # include <sys/file.h>
34 #endif
35
36 extern int h_errno;
37
38 #include "winsock.h"
39 #include "windows.h"
40 #include "heap.h"
41 #include "ldt.h"
42 #include "message.h"
43 #include "selectors.h"
44 #include "miscemu.h"
45 #include "sig_context.h"
46 #include "debug.h"
47
48 #ifndef FASYNC
49 #define FASYNC FIOASYNC
50 #endif
51
52 typedef struct          /* async DNS op control struct */
53 {
54   ws_async_op*  ws_aop;
55   char*         buffer;
56   int           type;
57   union
58   {
59     char*       init;
60     char*       name;
61     char*       addr;
62   } rq;
63   unsigned      ilength;
64 } ws_async_ctl;
65
66 extern HANDLE16 __ws_gethandle( void* ptr );
67 extern void*    __ws_memalloc( int size );
68 extern void     __ws_memfree( void* ptr );
69
70 /* NOTE: ws_async_op list is traversed inside the SIGIO handler! */
71
72 static int              __async_io_max_fd = 0;
73 static fd_set           __async_io_fdset;
74 static ws_async_op*     __async_op_list = NULL;
75
76 static void fixup_wshe(struct ws_hostent* p_wshe, void* base);
77 static void fixup_wspe(struct ws_protoent* p_wspe, void* base);
78 static void fixup_wsse(struct ws_servent* p_wsse, void* base);
79
80 extern void EVENT_AddIO( int fd, unsigned flag );
81 extern void EVENT_DeleteIO( int fd, unsigned flag );
82
83 /* ----------------------------------- async/non-blocking I/O */
84
85 int WINSOCK_async_io(int fd, int async)
86 {
87     int fd_flags;
88
89 #ifndef __EMX__
90     fcntl(fd, F_SETOWN, getpid());
91 #endif
92
93     fd_flags = fcntl(fd, F_GETFL, 0);
94     if (fcntl(fd, F_SETFL, (async)? fd_flags | FASYNC
95                                   : fd_flags & ~FASYNC ) != -1) return 0;
96     return -1;
97 }
98
99 int WINSOCK_unblock_io(int fd, int noblock)
100 {
101     int fd_flags;
102
103     fd_flags = fcntl(fd, F_GETFL, 0);
104     if (fcntl(fd, F_SETFL, (noblock)? fd_flags |  O_NONBLOCK
105                                     : fd_flags & ~O_NONBLOCK ) != -1) return 0;
106     return -1;
107 }
108
109 int WINSOCK_check_async_op(ws_async_op* p_aop)
110 {
111   ws_async_op*   p = __async_op_list;
112   while( p ) if( p == p_aop ) return 1;
113              else p = p->next;
114   return 0;
115 }
116
117 int WINSOCK_cancel_async_op(ws_async_op* p_aop)
118 {
119     /* SIGIO unsafe! */
120
121     if( WINSOCK_check_async_op(p_aop) )
122     {
123         if( !(p_aop->flags & WSMSG_DEAD_AOP) )
124         {
125             kill(p_aop->pid, SIGKILL);
126             waitpid(p_aop->pid, NULL, 0); /* just in case */
127             close(p_aop->fd[0]);
128         }
129         WINSOCK_unlink_async_op(p_aop);
130         EVENT_DeleteIO( p_aop->fd[0], EVENT_IO_READ );
131         p_aop->flags = 0;
132         p_aop->hWnd = p_aop->uMsg = 0;
133         return 1;
134     }
135     return 0;
136 }
137
138 void WINSOCK_cancel_task_aops(HTASK16 hTask, void (*__opfree)(void*))
139 {
140     /* SIGIO safe, hTask == 0 cancels all outstanding async ops */
141
142     int num = 0, num_dead = 0;
143     ws_async_op*   p, *next;
144
145     TRACE(winsock," cancelling async DNS requests... \n");
146
147     SIGNAL_MaskAsyncEvents( TRUE );
148     next = __async_op_list;
149     while( (p = next) ) 
150     {
151         HTASK16 hWndTask = GetWindowTask16(p->hWnd);
152
153         next = p->next;
154         if(!hTask || !hWndTask || (hTask == hWndTask))
155         {
156             num++;
157             if( p->flags & WSMSG_DEAD_AOP )
158                 num_dead++;
159
160             WINSOCK_cancel_async_op(p);
161             if( __opfree ) __opfree(p);
162         }
163     }
164     SIGNAL_MaskAsyncEvents( FALSE );
165     TRACE(winsock," -> %i total (%i active)\n", num, num - num_dead );
166 }
167
168 void WINSOCK_link_async_op(ws_async_op* p_aop)
169 {
170   /* SIGIO safe */
171
172   p_aop->prev = NULL;
173   SIGNAL_MaskAsyncEvents( TRUE );
174   if( __async_op_list )
175   {
176       ws_async_op*      p = __async_op_list;
177       __async_op_list->prev = p_aop;
178
179       /* traverse the list and retire dead ops created
180        * by the signal handler (see below). */
181
182       while( p )
183       {
184           if( p->flags & WSMSG_DEAD_AOP )
185           {
186               ws_async_op* dead = p;
187
188               TRACE(winsock,"\treaping dead aop [%08x]\n", (unsigned)p );
189
190               p = p->next;
191               WINSOCK_unlink_async_op( dead );
192               __ws_memfree( dead );
193               continue;
194           }
195           p = p->next;
196       }
197   }
198   else FD_ZERO(&__async_io_fdset);
199   p_aop->next = __async_op_list;
200   __async_op_list = p_aop;
201   SIGNAL_MaskAsyncEvents( FALSE );
202
203   FD_SET(p_aop->fd[0], &__async_io_fdset);
204   if( p_aop->fd[0] > __async_io_max_fd ) 
205                      __async_io_max_fd = p_aop->fd[0];
206 }
207
208 void WINSOCK_unlink_async_op(ws_async_op* p_aop)
209 {
210   /* SIGIO unsafe! */
211
212   if( p_aop == __async_op_list ) __async_op_list = p_aop->next;
213   else
214       p_aop->prev->next = p_aop->next;
215   if( p_aop->next ) p_aop->next->prev = p_aop->prev;
216
217   FD_CLR(p_aop->fd[0], &__async_io_fdset); 
218   if( p_aop->fd[0] == __async_io_max_fd )
219                       __async_io_max_fd--;
220 }
221
222 /* ----------------------------------- SIGIO handler -
223  *
224  * link_async_op/unlink_async_op allow to install generic
225  * async IO handlers (provided that aop_control function is defined).
226  *
227  * Note: pipe-based handlers must raise explicit SIGIO with kill(2).
228  */
229
230 HANDLER_DEF(WINSOCK_sigio)
231 {
232  struct timeval         timeout;
233  fd_set                 check_set;
234  ws_async_op*           p_aop;
235
236  HANDLER_INIT();
237
238  check_set = __async_io_fdset;
239  memset(&timeout, 0, sizeof(timeout));
240
241  while( select(__async_io_max_fd + 1,
242               &check_set, NULL, NULL, &timeout) > 0)
243  {
244    for( p_aop = __async_op_list;
245         p_aop ; p_aop = p_aop->next )
246       if( FD_ISSET(p_aop->fd[0], &check_set) )
247           if( p_aop->aop_control(p_aop, AOP_IO) == AOP_CONTROL_REMOVE )
248           {
249              /* NOTE: memory management is signal-unsafe, therefore
250               * we can only set a flag to remove this p_aop later on.
251               */
252
253               p_aop->flags = WSMSG_DEAD_AOP;
254               close(p_aop->fd[0]);
255               FD_CLR(p_aop->fd[0],&__async_io_fdset);
256               if( p_aop->fd[0] == __async_io_max_fd )
257                                  __async_io_max_fd = p_aop->fd[0];
258               if( p_aop->pid ) 
259               { 
260                   kill(p_aop->pid, SIGKILL); 
261                   waitpid(p_aop->pid, NULL, WNOHANG);
262                   p_aop->pid = 0;
263               }
264           }
265    check_set = __async_io_fdset;
266   }
267 }
268
269 /* ----------------------------------- getXbyY requests */
270
271 static   ws_async_ctl   async_ctl; /* child process control struct */
272
273 static int aop_control(ws_async_op* p_aop, int flag )
274 {
275     unsigned    lLength;
276
277   /* success:   LOWORD(lLength) has the length of the struct
278    *            to read.
279    * failure:   LOWORD(lLength) is zero, HIWORD(lLength) contains
280    *            the error code.
281    */
282
283     read(p_aop->fd[0], &lLength, sizeof(unsigned));
284     if( LOWORD(lLength) )
285     {
286         if( (int)LOWORD(lLength) <= p_aop->buflen )
287         {
288             char* buffer = (p_aop->flags & WSMSG_WIN32_AOP)
289                   ? p_aop->b.lin_base : (char*)PTR_SEG_TO_LIN(p_aop->b.seg_base);
290
291             read(p_aop->fd[0], buffer, LOWORD(lLength));
292             switch( p_aop->flags & WSMSG_ASYNC_RQMASK )
293             {
294                 case WSMSG_ASYNC_HOSTBYNAME:
295                 case WSMSG_ASYNC_HOSTBYADDR:
296                      fixup_wshe((struct ws_hostent*)buffer, p_aop->b.ptr_base); break;
297                 case WSMSG_ASYNC_PROTOBYNAME:
298                 case WSMSG_ASYNC_PROTOBYNUM:
299                      fixup_wspe((struct ws_protoent*)buffer, p_aop->b.ptr_base); break;
300                 case WSMSG_ASYNC_SERVBYNAME:
301                 case WSMSG_ASYNC_SERVBYPORT:
302                      fixup_wsse((struct ws_servent*)buffer, p_aop->b.ptr_base); break;
303                 default:
304                      if( p_aop->flags ) WARN(winsock,"Received unknown async request!\n");
305                      return AOP_CONTROL_REMOVE;
306             }
307         }
308         else lLength =  ((UINT32)LOWORD(lLength)) | ((unsigned)WSAENOBUFS << 16);
309     } /* failure */
310
311      /* was a __WS_ASYNC_DEBUG statement */
312      TRACE(winsock, "DNS aop completed: hWnd [%04x], uMsg [%04x], "
313           "aop [%04x], event [%08lx]\n",
314           p_aop->hWnd, p_aop->uMsg, __ws_gethandle(p_aop), (LPARAM)lLength);
315
316     /* FIXME: update num_async_rq */
317     EVENT_DeleteIO( p_aop->fd[0], EVENT_IO_READ );
318     PostMessage32A( p_aop->hWnd, p_aop->uMsg, __ws_gethandle(p_aop), (LPARAM)lLength );
319     
320     return AOP_CONTROL_REMOVE;  /* one-shot request */
321 }
322
323
324 HANDLE16 __WSAsyncDBQuery(LPWSINFO pwsi, HWND32 hWnd, UINT32 uMsg, INT32 type, LPSTR init,
325                           INT32 len, LPSTR proto, void* sbuf, INT32 buflen, UINT32 flag)
326 {
327     /* queue 'flag' request and fork off its handler */
328
329     async_ctl.ws_aop = (ws_async_op*)__ws_memalloc(sizeof(ws_async_op));
330
331     if( async_ctl.ws_aop )
332     {
333         HANDLE16        handle = __ws_gethandle(async_ctl.ws_aop);
334
335         if( pipe(async_ctl.ws_aop->fd) == 0 )
336         {
337             async_ctl.rq.init = (char*)init;
338             async_ctl.ilength = len;
339             async_ctl.buffer = proto;
340             async_ctl.type = type;
341
342             async_ctl.ws_aop->hWnd = hWnd;
343             async_ctl.ws_aop->uMsg = uMsg;
344             async_ctl.ws_aop->b.ptr_base = sbuf; 
345             async_ctl.ws_aop->buflen = buflen;
346             async_ctl.ws_aop->flags = flag;
347             async_ctl.ws_aop->aop_control = &aop_control;
348
349             WINSOCK_link_async_op( async_ctl.ws_aop );
350
351             EVENT_AddIO( async_ctl.ws_aop->fd[0], EVENT_IO_READ );
352             pwsi->num_async_rq++;
353
354             async_ctl.ws_aop->pid = fork();
355             if( async_ctl.ws_aop->pid )
356             {
357                 TRACE(winsock, "\tasync_op = %04x (child %i)\n", 
358                                 handle, async_ctl.ws_aop->pid);
359
360                 close(async_ctl.ws_aop->fd[1]);  /* write endpoint */
361                 if( async_ctl.ws_aop->pid > 0 )
362                     return __ws_gethandle(async_ctl.ws_aop);
363
364                 /* fork() failed */
365
366                 pwsi->num_async_rq--;
367                 EVENT_DeleteIO( async_ctl.ws_aop->fd[0], EVENT_IO_READ );
368                 close(async_ctl.ws_aop->fd[0]);
369                 pwsi->err = WSAEWOULDBLOCK;
370             }
371             else
372             {
373                 extern BOOL32 THREAD_InitDone;
374
375                 THREAD_InitDone = FALSE;
376                 /* child process */
377
378                 close(async_ctl.ws_aop->fd[0]);  /* read endpoint */
379                 switch( flag & WSMSG_ASYNC_RQMASK )
380                 {
381                     case WSMSG_ASYNC_HOSTBYADDR:
382                     case WSMSG_ASYNC_HOSTBYNAME:
383                         WS_do_async_gethost(pwsi, flag);
384                         break;
385                     case WSMSG_ASYNC_PROTOBYNUM:
386                     case WSMSG_ASYNC_PROTOBYNAME:
387                         WS_do_async_getproto(pwsi, flag);
388                         break;
389                     case WSMSG_ASYNC_SERVBYPORT:
390                     case WSMSG_ASYNC_SERVBYNAME:
391                         WS_do_async_getserv(pwsi, flag);
392                         break;
393                 }
394                 _exit(0);       /* skip  atexit()'ed cleanup */
395             }
396         }
397         else pwsi->err = wsaErrno(); /* failed to create pipe */
398
399         __ws_memfree((void*)async_ctl.ws_aop);
400     } else pwsi->err = WSAEWOULDBLOCK;
401     return 0;
402 }
403
404 static int _async_notify()
405 {
406     /* use half-duplex pipe to send variable length packets 
407      * to the parent process */
408      
409     write(async_ctl.ws_aop->fd[1], &async_ctl.ilength, sizeof(unsigned));
410     write(async_ctl.ws_aop->fd[1], async_ctl.buffer, async_ctl.ilength );
411
412 #ifndef __EMX__
413     kill(getppid(), SIGIO);    /* simulate async I/O */
414 #endif
415
416     /* was a __WS_ASYNC_DEBUG statement */
417     TRACE(winsock, "handler - notify aop [%d, buf %d]\n", 
418           async_ctl.ilength, async_ctl.ws_aop->buflen);
419     return 1;
420 }
421
422 static void _async_fail()
423 {
424      /* write a DWORD with error code (low word is zero) */
425
426      async_ctl.ilength =
427         (h_errno < 0) ? (unsigned)WSAMAKEASYNCREPLY( 0, wsaErrno() )
428                       : (unsigned)WSAMAKEASYNCREPLY( 0, wsaHerrno() );
429      write(async_ctl.ws_aop->fd[1], &async_ctl.ilength, sizeof(unsigned) );
430 #ifndef __EMX__
431      kill(getppid(), SIGIO);    /* simulate async I/O */
432 #endif
433
434     /* was a __WS_ASYNC_DEBUG statement */
435     TRACE(winsock, "handler - failed aop [%d, buf %d]\n", 
436           async_ctl.ilength, async_ctl.ws_aop->buflen);
437 }
438
439 void dump_ws_hostent_offset(struct ws_hostent* wshe)
440 {
441   int           i;
442   char*         base = (char*)wshe;
443   unsigned*     ptr;
444
445   DUMP("h_name = %08x\t[%s]\n", 
446        (unsigned)wshe->h_name, base + (unsigned)wshe->h_name);
447   DUMP("h_aliases = %08x\t[%08x]\n", 
448        (unsigned)wshe->h_aliases, (unsigned)(base+(unsigned)wshe->h_aliases));
449   ptr = (unsigned*)(base + (unsigned)wshe->h_aliases);
450   for(i = 0; ptr[i]; i++ )
451   {
452         DUMP("%i - %08x [%s]\n", i + 1, ptr[i], ((char*)base) + ptr[i]);
453   }
454   DUMP("h_length = %i\n", wshe->h_length);
455 }
456
457 void WS_do_async_gethost(LPWSINFO pwsi, unsigned flag )
458 {  
459   int                   size = 0;
460   struct hostent*       p_he;
461
462   close(async_ctl.ws_aop->fd[0]);
463
464   p_he = (flag & WSMSG_ASYNC_HOSTBYNAME)
465          ? gethostbyname(async_ctl.rq.name)
466          : gethostbyaddr(async_ctl.rq.name,
467                          async_ctl.ilength, async_ctl.type);
468
469   TRACE(winsock,"DNS: got hostent for [%s]\n", async_ctl.rq.name );
470
471   if( p_he ) /* convert to the Winsock format with internal pointers as offsets */
472       size = WS_dup_he(pwsi, p_he, WS_DUP_OFFSET | 
473                      ((flag & WSMSG_WIN32_AOP) ? WS_DUP_LINEAR : WS_DUP_SEGPTR) );
474   if( size )
475   {
476       async_ctl.buffer = pwsi->buffer;
477       async_ctl.ilength = (unsigned)WSAMAKEASYNCREPLY( (UINT16)size, 0 );
478      _async_notify( flag );
479   }
480   else _async_fail();
481 }
482
483 void WS_do_async_getproto(LPWSINFO pwsi, unsigned flag )
484 {
485   int                   size = 0;
486   struct protoent*      p_pe;
487
488   close(async_ctl.ws_aop->fd[0]);
489   p_pe = (flag & WSMSG_ASYNC_PROTOBYNAME)
490          ? getprotobyname(async_ctl.rq.name)
491          : getprotobynumber(async_ctl.type);
492
493   TRACE(winsock,"DNS: got protoent for [%s]\n", async_ctl.rq.name );
494
495   if( p_pe ) /* convert to the Winsock format with internal pointers as offsets */
496       size = WS_dup_pe(pwsi, p_pe, WS_DUP_OFFSET |
497                      ((flag & WSMSG_WIN32_AOP) ? WS_DUP_LINEAR : WS_DUP_SEGPTR) );
498   if( size )
499   {
500       async_ctl.buffer = pwsi->buffer;
501       async_ctl.ilength = (unsigned)WSAMAKEASYNCREPLY( (UINT16)size, 0 );
502      _async_notify( flag );
503   } 
504   else _async_fail();
505 }
506
507 void WS_do_async_getserv(LPWSINFO pwsi, unsigned flag )
508 {
509   int                   size = 0;
510   struct servent*       p_se;
511
512   close(async_ctl.ws_aop->fd[0]);
513   p_se = (flag & WSMSG_ASYNC_SERVBYNAME)
514          ? getservbyname(async_ctl.rq.name, async_ctl.buffer)
515          : getservbyport(async_ctl.type, async_ctl.buffer);
516
517   if( p_se ) /* convert to the Winsock format with internal pointers as offsets */
518       size = WS_dup_se(pwsi, p_se, WS_DUP_OFFSET |
519                       ((flag & WSMSG_WIN32_AOP) ? WS_DUP_LINEAR : WS_DUP_SEGPTR) );
520   if( size )
521   {
522       async_ctl.buffer = pwsi->buffer;
523       async_ctl.ilength = (unsigned)WSAMAKEASYNCREPLY( (UINT16)size, 0 );
524      _async_notify( flag );
525   }
526   else _async_fail();
527 }
528
529 /* ----------------------------------- helper functions -
530  *
531  * Raw results from the pipe contain internal pointers stored as
532  * offsets relative to the beginning of the buffer and we need
533  * to apply a fixup before passing them to applications.
534  *
535  * NOTE: It is possible to exploit the fact that fork() doesn't 
536  * change the buffer address by storing fixed up pointers right 
537  * in the handler. However, this will get in the way if we ever 
538  * get around to implementing DNS helper daemon a-la Netscape 4.x.
539  */
540
541 void fixup_wshe(struct ws_hostent* p_wshe, void* base)
542 {
543    /* add 'base' to ws_hostent pointers to convert them from offsets */
544
545    int i;
546    unsigned*    p_aliases,*p_addr;
547
548    p_aliases = (unsigned*)((char*)p_wshe + (unsigned)p_wshe->h_aliases);
549    p_addr = (unsigned*)((char*)p_wshe + (unsigned)p_wshe->h_addr_list);
550    ((unsigned)(p_wshe->h_name)) += (unsigned)base;
551    ((unsigned)(p_wshe->h_aliases)) += (unsigned)base;
552    ((unsigned)(p_wshe->h_addr_list)) += (unsigned)base;
553    for(i=0;p_aliases[i];i++) p_aliases[i] += (unsigned)base;
554    for(i=0;p_addr[i];i++) p_addr[i] += (unsigned)base;
555 }
556
557 void fixup_wspe(struct ws_protoent* p_wspe, void* base)
558 {
559    int i;
560    unsigned*       p_aliases = (unsigned*)((char*)p_wspe + (unsigned)p_wspe->p_aliases);
561    ((unsigned)(p_wspe->p_name)) += (unsigned)base;
562    ((unsigned)(p_wspe->p_aliases)) += (unsigned)base;
563    for(i=0;p_aliases[i];i++) p_aliases[i] += (unsigned)base;
564 }
565
566 void fixup_wsse(struct ws_servent* p_wsse, void* base)
567 {
568    int i;
569    unsigned*       p_aliases = (unsigned*)((char*)p_wsse + (unsigned)p_wsse->s_aliases);
570    ((unsigned)(p_wsse->s_name)) += (unsigned)base;
571    ((p_wsse->s_proto)) += (unsigned)base;
572    ((p_wsse->s_aliases)) += (unsigned)base;
573    for(i=0;p_aliases[i];i++) p_aliases[i] += (unsigned)base;
574 }
575