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